Issue #3591663: Fail validation for props that do not exist on a component
Fixes #3591663.
Root cause
AiResponseValidator::convertToComponentTreeData() builds the client model with the component's own propSources as source. JsonSchemaPropsComponentSourceBase::clientModelToInput() iterates only $client_model['source'] keys, so an AI-supplied prop that does not exist on the component is never visited and is silently dropped before ComponentTreeItemList::validate() runs. The existing garbage-input check in validateComponentInput() only sees post-conversion inputs, so it never fires for these props.
Changes
AiResponseValidatorcollects unknown props during conversion (diff of AI prop keys againstpropSources) and merges them into the thrownConstraintViolationException, reusing the wording of the existing garbage-input check:Component `X`: the `Y` prop is not defined.- The check applies to sources exposing
propSources(SDC and code components). Block components keep their settings-form flow. - A scalar
propsvalue (for example a quoted YAML mapping) is now rejected instead of silently dropping every prop. - Five regression cases added to
SetAIGeneratedComponentStructureTest: bogus prop next to valid props, any prop on a zero-prop component, a bogus prop nested in a slot, combined missing-required plus bogus prop, and scalar props. SetAIGeneratedTemplateDataTestsent the non-existenttitleprop to the card component (which definesheading); it only passed because of this bug. Fixed toheading.
Testing
-
SetAIGeneratedComponentStructureTest(13 tests) andSetAIGeneratedTemplateDataTest(5 tests) pass. - Manual: run the issue's reproduce steps; the tool now fails with
components.0.[sdc.mercury.hero-billboard].props.heading_test: Component `sdc.mercury.hero-billboard`: the `heading_test` prop is not defined. -
composer run lintpasses.
Known follow-ups (out of scope here)
- Block-source components: unknown settings keys are still silently stripped by
BlockComponent::clientModelToInput(); nothing downstream rejects them. - Props added to an auto-saved (unpublished) code-component draft are not in the saved entity's
propSources, so replaying them through the AI now fails with a misleading message. A complete fix would layer auto-save data before diffing. - Pre-existing:
CanvasAiPageBuilderHelper::processSdc()filters Attribute-typed props with a strict string comparison on the raw definition, whilepropSourcesuses core's array-aware check. An SDC declaring the array form (type: [Drupal\Core\Template\Attribute]) is advertised in the AI catalog but absent frompropSources, so the AI can be led into sending a prop this validator rejects.
AI usage disclosure
Per the Drupal.org AI policy: an AI assistant helped trace the root cause, draft the change and tests, and run the verification. A human reviewed the diff, the reasoning, and the test results before pushing. Kernel tests were executed locally on Drupal 11.3 (18 passing across the two affected test files); PHPCS and PHPStan level 8 show no new issues against the originals.