fix: harden WordPress content rendering

This commit is contained in:
bisco
2026-06-26 15:54:57 +02:00
parent 482928a296
commit 0762b98fc6
8 changed files with 43 additions and 18 deletions
+3 -2
View File
@@ -8,8 +8,9 @@ nor MariaDB publishes a host port; automated security checks guard this assumpti
The custom `azionelab` classic theme renders the public single page. Theme modifications The custom `azionelab` classic theme renders the public single page. Theme modifications
store the hero, manifesto, laboratory, teacher, lesson, and contact fields. The store the hero, manifesto, laboratory, teacher, lesson, and contact fields. The
`azionelab-content` must-use plugin registers Shows and Gallery custom post types so `azionelab-content` must-use plugin registers Shows and Gallery custom post types so
structured editorial content survives a theme change. Images use WordPress featured structured editorial content survives a theme change. These custom post types are
images with local SVG fallbacks. editorial data sources for the homepage, not standalone public routes or REST
collections. Images use WordPress featured images with local SVG fallbacks.
WP-CLI is an opt-in tools-profile service. Its idempotent bootstrap installs WordPress, WP-CLI is an opt-in tools-profile service. Its idempotent bootstrap installs WordPress,
activates the theme, configures the site, and creates realistic demo content. Certbot is activates the theme, configures the site, and creates realistic demo content. Certbot is
+3
View File
@@ -6,6 +6,9 @@
ignored and real secrets must come from the deployment secret manager. ignored and real secrets must come from the deployment secret manager.
- WordPress uses its normal capability, nonce, authentication, cookie, and password - WordPress uses its normal capability, nonce, authentication, cookie, and password
controls. Theme settings sanitize input and templates escape output. controls. Theme settings sanitize input and templates escape output.
- Structured Shows and Gallery content is editable in WordPress admin but is consumed
by the homepage only; it is not exposed as standalone public routes or REST
collections.
- File editing is always disabled. Production also disables web-based core, theme, and - File editing is always disabled. Production also disables web-based core, theme, and
plugin changes; patched images are rebuilt and redeployed instead. plugin changes; patched images are rebuilt and redeployed instead.
- XML-RPC and comments are disabled. NGINX blocks PHP execution below uploads, dotfiles, - XML-RPC and comments are disabled. NGINX blocks PHP execution below uploads, dotfiles,
+4 -3
View File
@@ -54,15 +54,15 @@ test("provides usable contact actions", async ({ page }) => {
await expect(page.locator(".social-link-youtube")).toHaveCount(1); await expect(page.locator(".social-link-youtube")).toHaveCount(1);
await expect(page.getByRole("link", { name: "Instagram" })).toHaveAttribute( await expect(page.getByRole("link", { name: "Instagram" })).toHaveAttribute(
"href", "href",
"https://instagram.com/", "https://instagram.com/azionelab",
); );
await expect(page.getByRole("link", { name: "Facebook" })).toHaveAttribute( await expect(page.getByRole("link", { name: "Facebook" })).toHaveAttribute(
"href", "href",
"https://facebook.com/", "https://facebook.com/azionelab",
); );
await expect(page.getByRole("link", { name: "YouTube" })).toHaveAttribute( await expect(page.getByRole("link", { name: "YouTube" })).toHaveAttribute(
"href", "href",
"https://youtube.com/", "https://youtube.com/@azionelab",
); );
await expect(page.locator(".social-links .social-icon")).toHaveCount(3); await expect(page.locator(".social-links .social-icon")).toHaveCount(3);
await expect(page.locator(".contact-actions .contact-icon")).toHaveCount(3); await expect(page.locator(".contact-actions .contact-icon")).toHaveCount(3);
@@ -123,6 +123,7 @@ test("protects the edge and exposes the WordPress admin", async ({ page, request
expect((await request.get("/wp-config.php")).status()).toBe(404); expect((await request.get("/wp-config.php")).status()).toBe(404);
expect((await request.get("/wp-content/uploads/probe.php")).status()).toBe(403); expect((await request.get("/wp-content/uploads/probe.php")).status()).toBe(403);
expect((await request.get("/wp-json/wp/v2/users")).status()).toBe(404); expect((await request.get("/wp-json/wp/v2/users")).status()).toBe(404);
expect((await request.get("/?post_type=azl_show&name=le-cose-che-restano")).status()).toBe(404);
await page.goto("/wp-admin/"); await page.goto("/wp-admin/");
await expect(page).toHaveURL(/\/wp-login\.php/); await expect(page).toHaveURL(/\/wp-login\.php/);
+5
View File
@@ -26,3 +26,8 @@ grep -q 'wp-content/(uploads|files)' /workspace/wordpress/apache-hardening.conf
echo "Apache hardening must block PHP execution below uploads/files." >&2 echo "Apache hardening must block PHP execution below uploads/files." >&2
exit 1 exit 1
} }
grep -q "'publicly_queryable'[[:space:]]*=>[[:space:]]*false" /workspace/wordpress/mu-plugins/azionelab-content.php || {
echo "Structured show content must not be exposed as standalone public routes." >&2
exit 1
}
+4 -1
View File
@@ -19,7 +19,10 @@ function azionelab_register_content_types(): void {
'add_new_item' => 'Aggiungi spettacolo', 'add_new_item' => 'Aggiungi spettacolo',
'edit_item' => 'Modifica spettacolo', 'edit_item' => 'Modifica spettacolo',
), ),
'public' => true, 'public' => false,
'show_ui' => true,
'publicly_queryable' => false,
'exclude_from_search'=> true,
'show_in_rest' => false, 'show_in_rest' => false,
'menu_icon' => 'dashicons-tickets-alt', 'menu_icon' => 'dashicons-tickets-alt',
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'page-attributes' ), 'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'page-attributes' ),
+15 -4
View File
@@ -21,6 +21,11 @@ $social_links = array_values(
static fn ( array $link ): bool => '' !== $link['url'] static fn ( array $link ): bool => '' !== $link['url']
) )
); );
$contact_email = sanitize_email( azionelab_mod( 'contact_email' ) );
$contact_phone = trim( azionelab_mod( 'contact_phone' ) );
$contact_phone_href = azionelab_phone_href( $contact_phone );
$contact_whatsapp = trim( azionelab_mod( 'contact_whatsapp' ) );
$contact_whatsapp_href = azionelab_whatsapp_href( $contact_whatsapp );
?> ?>
<main id="contenuto"> <main id="contenuto">
<section class="hero section" id="inizio" aria-labelledby="hero-title"> <section class="hero section" id="inizio" aria-labelledby="hero-title">
@@ -135,7 +140,7 @@ $social_links = array_values(
?> ?>
<article class="show-card"> <article class="show-card">
<img src="<?php echo esc_url( $image ); ?>" alt="Locandina di <?php the_title_attribute(); ?>" width="700" height="880" loading="lazy"> <img src="<?php echo esc_url( $image ); ?>" alt="Locandina di <?php the_title_attribute(); ?>" width="700" height="880" loading="lazy">
<div class="show-copy"><?php if ( $show_meta ) : ?><p class="show-meta"><?php echo esc_html( $show_meta ); ?></p><?php endif; ?><h3><?php the_title(); ?></h3><div><?php the_excerpt(); ?></div></div> <div class="show-copy"><?php if ( $show_meta ) : ?><p class="show-meta"><?php echo esc_html( $show_meta ); ?></p><?php endif; ?><h3><?php echo esc_html( get_the_title() ); ?></h3><div><?php echo wp_kses_post( wpautop( get_the_excerpt() ) ); ?></div></div>
</article> </article>
<?php <?php
++$show_index; ++$show_index;
@@ -207,9 +212,15 @@ $social_links = array_values(
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="contact-actions"> <div class="contact-actions">
<a class="contact-action" href="mailto:<?php echo esc_attr( sanitize_email( azionelab_mod( 'contact_email' ) ) ); ?>"><?php echo azionelab_contact_icon( 'email' ); ?><span>Scrivi una mail</span><strong><?php echo esc_html( azionelab_mod( 'contact_email' ) ); ?></strong></a> <?php if ( $contact_email ) : ?>
<a class="contact-action" href="tel:<?php echo esc_attr( azionelab_phone_href( azionelab_mod( 'contact_phone' ) ) ); ?>"><?php echo azionelab_contact_icon( 'phone' ); ?><span>Chiama</span><strong><?php echo esc_html( azionelab_mod( 'contact_phone' ) ); ?></strong></a> <a class="contact-action" href="mailto:<?php echo esc_attr( $contact_email ); ?>"><?php echo azionelab_contact_icon( 'email' ); ?><span>Scrivi una mail</span><strong><?php echo esc_html( $contact_email ); ?></strong></a>
<a class="contact-action" href="https://wa.me/<?php echo esc_attr( azionelab_whatsapp_href( azionelab_mod( 'contact_whatsapp' ) ) ); ?>" rel="noopener noreferrer"><?php echo azionelab_contact_icon( 'whatsapp' ); ?><span>WhatsApp</span><strong><?php echo esc_html( azionelab_mod( 'contact_whatsapp' ) ); ?></strong></a> <?php endif; ?>
<?php if ( $contact_phone && $contact_phone_href ) : ?>
<a class="contact-action" href="tel:<?php echo esc_attr( $contact_phone_href ); ?>"><?php echo azionelab_contact_icon( 'phone' ); ?><span>Chiama</span><strong><?php echo esc_html( $contact_phone ); ?></strong></a>
<?php endif; ?>
<?php if ( $contact_whatsapp && $contact_whatsapp_href ) : ?>
<a class="contact-action" href="https://wa.me/<?php echo esc_attr( $contact_whatsapp_href ); ?>" rel="noopener noreferrer"><?php echo azionelab_contact_icon( 'whatsapp' ); ?><span>WhatsApp</span><strong><?php echo esc_html( $contact_whatsapp ); ?></strong></a>
<?php endif; ?>
</div> </div>
</div> </div>
</section> </section>
+3 -3
View File
@@ -29,9 +29,9 @@ function azionelab_defaults(): array {
'contact_email' => 'ciao@azionelab.org', 'contact_email' => 'ciao@azionelab.org',
'contact_phone' => '+39 333 123 4567', 'contact_phone' => '+39 333 123 4567',
'contact_whatsapp' => '+39 333 123 4567', 'contact_whatsapp' => '+39 333 123 4567',
'contact_instagram' => 'https://instagram.com/', 'contact_instagram' => '',
'contact_facebook' => 'https://facebook.com/', 'contact_facebook' => '',
'contact_youtube' => 'https://youtube.com/', 'contact_youtube' => '',
'contact_address' => "Via dell'Epomeo 9999, Napoli", 'contact_address' => "Via dell'Epomeo 9999, Napoli",
'footer_text' => 'Uno spazio aperto a chi desidera incontrare il teatro, insieme.', 'footer_text' => 'Uno spazio aperto a chi desidera incontrare il teatro, insieme.',
); );
+1
View File
@@ -46,6 +46,7 @@ wp option update permalink_structure '/%postname%/'
if ! wp theme is-active azionelab; then if ! wp theme is-active azionelab; then
wp theme activate azionelab wp theme activate azionelab
fi fi
wp eval 'set_theme_mod( "contact_instagram", "https://instagram.com/azionelab" ); set_theme_mod( "contact_facebook", "https://facebook.com/azionelab" ); set_theme_mod( "contact_youtube", "https://youtube.com/@azionelab" );'
for sample_id in 1 2; do for sample_id in 1 2; do
if ! wp post get "$sample_id" --field=ID >/dev/null 2>&1; then if ! wp post get "$sample_id" --field=ID >/dev/null 2>&1; then