fix: Task 5 review round 1 - restore point inside the kses window, strict map field types (S37)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 15:08:48 +02:00
co-authored by Claude Opus 5.5
parent 7762b76305
commit b8cb01812e
12 changed files with 200 additions and 26 deletions
@@ -431,4 +431,51 @@ final class VariableRefTest extends TestCase
self::assertTrue(VariableRef::contains('a $variable( b'));
self::assertFalse(VariableRef::contains('Preis: 5 $'));
}
/**
* Ruling S37 (M-1/M-3): map field types come from a filter – untrusted. url → url() (a URL the
* sanitizer empties is refused), select_post → a readable numeric post id, select /
* yes_no_button / number → plain values; every other type is refused.
*/
public function testMapFieldTypesAreRoutedStrictly(): void
{
$ref = self::ref([
'evil' => ['link' => 'url', 'color' => 'color', 'blank' => '', 'flag' => 'yes_no_button', 'n' => 'number'],
'post_link_url_page' => ['post_id' => 'select_post'],
]);
$sanitizer = ComposeFixture::sanitizer();
self::assertSame(
self::content('evil', '{"link":"https://kunde.test/a%20b","flag":"on","n":3}'),
$ref->canonical(self::content('evil', '{"link":" https://kunde.test/a b","flag":"on","n":3}'), $sanitizer)
);
self::assertSame(
self::content('post_link_url_page', '{"post_id":"12"}'),
$ref->canonical(self::content('post_link_url_page', '{"post_id":"12"}'), $sanitizer)
);
self::assertSame(
self::content('post_link_url_page', '{"post_id":12}'),
$ref->canonical(self::content('post_link_url_page', '{"post_id":12}'), $sanitizer)
);
$rejected = [
'javascript: in a url setting' => [self::content('evil', '{"link":"javascript:alert(1)"}'), 'URL'],
'url setting not a string' => [self::content('evil', '{"link":5}'), 'nur Text'],
'unknown field type' => [self::content('evil', '{"color":"red"}'), 'Feldtyp'],
'empty field type' => [self::content('evil', '{"blank":"x"}'), 'Feldtyp'],
'post slug' => [self::content('post_link_url_page', '{"post_id":"impressum"}'), 'Beitrags-ID'],
'zero post id' => [self::content('post_link_url_page', '{"post_id":0}'), 'Beitrags-ID'],
'negative post id' => [self::content('post_link_url_page', '{"post_id":"-3"}'), 'Beitrags-ID'],
'unreadable post' => [self::content('post_link_url_page', '{"post_id":"666"}'), 'Beitrags-ID'],
];
foreach ($rejected as $case => [$value, $fragment]) {
try {
$ref->canonical($value, $sanitizer);
self::fail('Accepted: ' . $case);
} catch (InvalidArgumentException $e) {
self::assertStringContainsString($fragment, $e->getMessage(), $case);
}
}
}
}