[ 'innerContent' => ['desktop' => ['value' => 'Alt']], 'decoration' => ['font' => ['font' => [ 'desktop' => ['value' => ['headingLevel' => 'h2', 'color' => '#111111']], 'phone' => ['value' => ['size' => '22px']], ]]], ], 'builderVersion' => '5.2.1', ]; private const TEXT = [ 'content' => ['innerContent' => ['desktop' => ['value' => '
Alt
']]], 'builderVersion' => '5.2.1', ]; public function testMergesShortAndRawBreakpointsAndKeepsTheRest(): void { $result = ComposeFixture::attrWriter()->apply('divi/heading', self::HEADING, [ 'title.decoration.font.font' => [ 'desktop' => ['value' => ['headingLevel' => 'h3'], 'hover' => ['color' => '{{color:primary}}']], 'tablet' => ['size' => '26px'], ], 'title.innerContent' => ['desktop' => 'Neu'], ], 'attrs'); self::assertSame([], $result['errors']); self::assertSame( [ 'desktop' => [ 'value' => ['headingLevel' => 'h3'], 'hover' => ['color' => '$variable({"type":"color","value":{"name":"gcid-primary-color","settings":{}}})$'], ], 'phone' => ['value' => ['size' => '22px']], 'tablet' => ['value' => ['size' => '26px']], ], $result['attrs']['title']['decoration']['font']['font'] ); // Heading title is kind "text" in the catalog (ruling S3): tags are stripped. self::assertSame('Neu', $result['attrs']['title']['innerContent']['desktop']['value']); self::assertSame('5.2.1', $result['attrs']['builderVersion']); } public function testNullRemovesPathsAndStatesAndTopLevelHoverMeansDesktop(): void { $writer = ComposeFixture::attrWriter(); $removed = $writer->apply('divi/heading', self::HEADING, ['title.decoration.font.font' => null], 'attrs'); self::assertArrayNotHasKey('decoration', $removed['attrs']['title']); $phoneGone = $writer->apply('divi/heading', self::HEADING, [ 'title.decoration.font.font' => ['phone' => ['value' => null], 'hover' => ['color' => '#000000']], ], 'attrs'); self::assertSame( ['desktop' => ['value' => ['headingLevel' => 'h2', 'color' => '#111111'], 'hover' => ['color' => '#000000']]], $phoneGone['attrs']['title']['decoration']['font']['font'] ); } public function testUnknownPathsGetSuggestions(): void { $result = ComposeFixture::attrWriter()->apply('divi/heading', self::HEADING, [ 'title.decoration.font' => ['desktop' => ['color' => '#fff']], 'title.innerContent' => ['mobile' => 'x'], ], 'changes[0].attrs'); self::assertSame(['title.decoration.font'], $result['invalid_paths']); self::assertStringContainsString( 'title.decoration.font.font', $result['errors']['changes[0].attrs.title.decoration.font'] ); self::assertStringContainsString('unbekannter Breakpoint "mobile"', $result['errors']['changes[0].attrs.title.innerContent']); self::assertSame(self::HEADING, $result['attrs']); } public function testSanitizesByPlace(): void { $writer = ComposeFixture::attrWriter(); self::assertSame('', $writer->cleanValue('divi/button', 'button.innerContent', 'javascript:alert(1)', 'linkUrl')); self::assertSame('x
', $writer->cleanValue('divi/text', 'content.innerContent', 'x
')); self::assertSame('', $writer->cleanValue('divi/code', 'content.innerContent', '')); self::assertSame('10px', $writer->cleanValue('divi/text', 'module.decoration.spacing', '10px')); self::assertSame( '$variable({"type":"color","value":{"name":"gcid-a1b2","settings":{}}})$', $writer->cleanValue('divi/text', 'module.decoration.background', '$variable({"type":"color","value":{"name":"gcid-a1b2","settings":{}}})$') ); self::assertSame(['desktop' => ['value' => 'x']], AttrWriter::normalize(['desktop' => 'x'])); } /** Ruling S3/S3a: kind from the catalog, per sub-field for composite paths, else key name, else text. */ public function testRoutesByCatalogKindAndKeyNameAtEveryDepth(): void { $writer = ComposeFixture::attrWriter(); // Unknown kind, URL keys deep inside the value. self::assertSame( ['color' => '#fff', 'image' => ['url' => '', 'parallax' => ['enabled' => 'on']]], $writer->cleanValue('divi/text', 'module.decoration.background', [ 'color' => '#fff', 'image' => ['url' => 'javascript:alert(1)', 'parallax' => ['enabled' => 'on']], ]) ); self::assertSame( ['url' => '', 'target' => 'on'], $writer->cleanValue('divi/text', 'module.advanced.link', ['url' => ' JavaScript:alert(1)', 'target' => 'on']) ); self::assertSame(['href' => '', 'src' => '', 'image' => ''], $writer->cleanValue('divi/text', 'module.advanced.link', [ 'href' => 'javascript:a()', 'src' => 'javascript:b()', 'image' => 'javascript:c()', ])); // Rich text keeps markup, text kind strips it, unknown innerContent is text too. self::assertSame('Hallo Welt
', $writer->cleanValue('divi/text', 'content.innerContent', 'Hallo Welt
')); self::assertSame('Titel', $writer->cleanValue('divi/heading', 'title.innerContent', 'Hallo $variable({"type":"content","value":{"name":"post_title","settings":{}}})$
'], self::TEXT, ]; $cases['embedded reference deep in a value'] = [ 'divi/text', 'module.decoration.background', ['desktop' => ['image' => ['url' => 'https://a.test/$variable({"type":"content","value":{"name":"post_title","settings":{}}})$']]], self::TEXT, ]; $cases['javascript via a reference in a URL field'] = [ 'divi/fullwidth-header', 'buttonOne.innerContent', ['desktop' => [ 'text' => 'Los', 'linkUrl' => '$variable({"type":"content","value":{"name":"post_link_url","settings":{"before":"javascript:alert(1)//","after":""}}})$', ]], [], ]; $cases['javascript via a reference in a URL key'] = [ 'divi/text', 'module.advanced.link', ['desktop' => ['url' => '$variable({"type":"content","value":{"name":"custom_meta_ziel","settings":{"before":"javascript:","after":""}}})$']], self::TEXT, ]; return $cases; } /** * @dataProvider malicious * @param array\xC3\x28
"], ], 'attrs'); self::assertStringContainsString('UTF-8', $result['errors']['attrs.content.innerContent']); } public function testReferencesFromAnEarlierReadAreReserialized(): void { $result = ComposeFixture::attrWriter()->apply('divi/text', self::TEXT, [ 'module.decoration.background' => ['desktop' => [ 'color' => '$variable({"value":{"name":"gcid-a1b2","settings":[]},"type":"color"})$', ]], ], 'attrs'); self::assertSame([], $result['errors']); self::assertSame( '$variable({"type":"color","value":{"name":"gcid-a1b2","settings":{}}})$', $result['attrs']['module']['decoration']['background']['desktop']['value']['color'] ); } }