fix: Task 3 review round 1 - strip control chars before the sanitizer (S32a), S34 minors
Control characters now go on the raw value in AttrWriter::cleanNode and
VariableRef::setting; after the sanitizer only rejection remains ('<' + VT/FF
no longer turns into a tag). VALUE_KEY anchored with D, exact column-width
fractions, nested rows in columns, specialty columns refused, fullwidth by
catalog category, preset ambiguity in DesignSystem::modulePresetId().
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:
@@ -128,6 +128,10 @@ final class ComposerTest extends TestCase
|
||||
self::assertSame(['24_24'], $widths('1_7,5_7')[0]);
|
||||
self::assertArrayHasKey('n[0].rows[0].columns', $widths('1_7,5_7')[1]);
|
||||
self::assertArrayHasKey('n[0].rows[0].columns', $widths('abc')[1]);
|
||||
// Ruling S34: exact fractions – 1/11 + 11/12 is 133/132, not "about one".
|
||||
self::assertArrayHasKey('n[0].rows[0].columns', $widths('1_11,11_12')[1]);
|
||||
self::assertSame([['1_5', '1_5', '1_5', '1_5', '1_5'], []], $widths('1_5,1_5,1_5,1_5,1_5'));
|
||||
self::assertSame([['8_24', '8_24', '8_24'], []], $widths('1_3,1_3,1_3'));
|
||||
}
|
||||
|
||||
public function testUpdateChangesShortFormPresetAndLabel(): void
|
||||
@@ -191,9 +195,14 @@ final class ComposerTest extends TestCase
|
||||
['type' => 'section', 'label' => $reference],
|
||||
['type' => 'section', 'label' => ['Hero']],
|
||||
['type' => 'section', 'modules' => [['type' => 'heading', 'label' => "Titel\x01\x7f"]]],
|
||||
['type' => 'section', 'label' => '<' . chr(11) . 'img src=x onerror=alert(1)>Hero'],
|
||||
['type' => 'section', 'label' => '<' . chr(12) . 'script>alert(1)</script>Hero'],
|
||||
], null, 'nodes');
|
||||
|
||||
[$clean, $split, $nul, $whole, $list, $control] = $result['blocks'];
|
||||
[$clean, $split, $nul, $whole, $list, $control, $vt, $ff] = $result['blocks'];
|
||||
// Ruling S32a: controls go before the sanitizer, so no tag is built after it.
|
||||
self::assertSame('Hero', NodeSummary::label($vt));
|
||||
self::assertSame('Hero', NodeSummary::label($ff));
|
||||
self::assertSame('Hero', NodeSummary::label($clean));
|
||||
self::assertSame('Hero – Heading', NodeSummary::label($clean->children()[0]->children()[0]->children()[0]));
|
||||
|
||||
@@ -344,6 +353,24 @@ final class ComposerTest extends TestCase
|
||||
self::assertSame([], $readOnly['blocks']);
|
||||
}
|
||||
|
||||
/** Ruling S34: Divi 5 rows are nestable – a row may go into a column. */
|
||||
public function testNestedRowInAColumn(): void
|
||||
{
|
||||
$result = ComposeFixture::composer()->compose([['type' => 'section', 'label' => 'Hero', 'rows' => [[
|
||||
'columns' => '1_2,1_2',
|
||||
'modules' => [
|
||||
[['type' => 'row', 'columns' => '1_2,1_2', 'modules' => [[['type' => 'text', 'content' => '<p>a</p>']], []]]],
|
||||
[['type' => 'text', 'content' => '<p>b</p>']],
|
||||
],
|
||||
]]]], null, 'nodes');
|
||||
|
||||
self::assertSame([], $result['errors']);
|
||||
$nested = $result['blocks'][0]->children()[0]->children()[0]->children()[0];
|
||||
self::assertSame('divi/row', $nested->name());
|
||||
self::assertSame(['12_24', '12_24'], array_map([NodeSummary::class, 'columnWidth'], $nested->children()));
|
||||
self::assertSame('divi/text', $nested->children()[0]->children()[0]->name());
|
||||
}
|
||||
|
||||
public function testRejectsNonObjectsAndMissingTypes(): void
|
||||
{
|
||||
$result = ComposeFixture::composer()->compose(['section', [], ['label' => 'x']], null, 'nodes');
|
||||
|
||||
Reference in New Issue
Block a user