fix(security): Divi-markup refusal sees content as storage will (ruling S52)
Control characters removed by kses (wp_kses_no_null: C0, \\0 sequences) or ControlChars (DEL) could hide a divi/* delimiter or an et_pb shortcode from the raw-input check. The shared DiviMarkup helper checks the raw and the normalized form and now guards every raw-content path: content-create/-update content and media-update description (attachment post_content). MediaImporter logs import failures with scheme, host, port and path only; uninstall.php lists the removed user meta. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -506,4 +506,20 @@ final class MediaImporterTest extends TestCase
|
||||
$this->expectException(\WpOrg\Requests\Exception::class);
|
||||
$listener('http://169.254.169.254/latest/meta-data/');
|
||||
}
|
||||
|
||||
/** Fix round 1: the log names scheme, host and path only – no credentials, no query string. */
|
||||
public function testTheFailureLogOmitsUserinfoAndQuery(): void
|
||||
{
|
||||
Functions\when('wp_http_validate_url')->returnArg();
|
||||
Functions\when('wp_parse_url')->alias('parse_url');
|
||||
Functions\when('wp_safe_remote_get')->justReturn(new WP_Error('http_request_failed', 'timed out'));
|
||||
|
||||
$this->importer()->fromUrl('https://nutzer:geheim@example.com:8443/bilder/a.jpg?token=abc#x', null, []);
|
||||
|
||||
$log = (string) file_get_contents($this->errorLog);
|
||||
self::assertStringContainsString('https://example.com:8443/bilder/a.jpg', $log);
|
||||
self::assertStringNotContainsString('geheim', $log);
|
||||
self::assertStringNotContainsString('nutzer', $log);
|
||||
self::assertStringNotContainsString('token=abc', $log);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,4 +267,19 @@ final class MediaUpdateAbilityTest extends TestCase
|
||||
self::assertSame(50, $ability->inputSchema()['properties']['items']['maxItems']);
|
||||
self::assertStringContainsString('modified', $ability->description());
|
||||
}
|
||||
|
||||
/** Ruling S52: description is the attachment's post_content – no Divi markup there either. */
|
||||
public function testDescriptionRefusesDiviMarkupEvenWithControlCharacters(): void
|
||||
{
|
||||
foreach (["<!-- wp:divi/text /-->", "<!--\0 wp:divi/text /-->", "[et_\x01pb_text]x[/et_pb_text]"] as $description) {
|
||||
$result = (new MediaUpdateAbility($this->log()))->execute(['items' => [['id' => 33, 'description' => $description]]]);
|
||||
|
||||
self::assertToolError($result, 'validation_failed', $description);
|
||||
self::assertStringContainsString('Divi', $result->get_error_data()['failures'][0]['error']);
|
||||
}
|
||||
self::assertSame([], $this->postUpdates);
|
||||
|
||||
(new MediaUpdateAbility(FakeChangeLog::any(9)))->execute(['items' => [['id' => 33, 'description' => '<em>Team</em>']]]);
|
||||
self::assertSame([['post_content' => '<em>Team</em>', 'ID' => 33]], $this->postUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,12 @@ final class PostWriterTest extends TestCase
|
||||
"<p>x</p><!--\twp:divi/text {\"a\":1} /-->",
|
||||
"<!--\n\n/wp:divi/row -->",
|
||||
'[et_pb_section][/et_pb_section]',
|
||||
// Ruling S52: control characters are removed before storage, so they cannot hide the markup.
|
||||
"<!--\0 wp:divi/text {\"a\":1} /-->",
|
||||
"<!-- wp:di\0vi/text /-->",
|
||||
"<!-- wp:divi\x01/text /-->",
|
||||
"[et_\0pb_section][/et_pb_section]",
|
||||
'<!-- wp:di\\0vi/text /-->',
|
||||
];
|
||||
foreach ($refused as $content) {
|
||||
foreach ([true, false] as $isCreate) {
|
||||
|
||||
Reference in New Issue
Block a user