feat(core): support prefix-insensitive DOM schema lookups and compile-time i18n attribute validation#68591
feat(core): support prefix-insensitive DOM schema lookups and compile-time i18n attribute validation#68591alan-agius4 wants to merge 1 commit intoangular:mainfrom
Conversation
72d2688 to
c004f1b
Compare
…-time i18n attribute validation Updates `DomElementSchemaRegistry` to strip `:svg:` and `:math:` namespace prefixes from tag names before querying `SECURITY_SCHEMA` at compile-time. This allows SVG and MathML attributes to correctly match their security contexts during compilation. Also implements dynamic schema-based sanitization and validation for static i18n attributes by wrapping `` expressions directly in their core sanitizers and validators at compile-time via an optimized `switch` statement. Additionally, updates the runtime i18n parser to dynamically resolve sanitizers based on the `SECURITY_SCHEMA` registry and cleans up type assertions inside `ɵɵvalidateAttribute` to safely support evaluation when no `TNode` is selected.
|
|
||
| it('should throw error on static href attributes', () => { | ||
| loadTranslations({[computeMsgId('/safe')]: 'javascript:alert(1)'}); | ||
| const template = `<a href="/safe" i18n-href>Link</a>`; |
There was a problem hiding this comment.
Should this be allowed, IE: static values to be overridden by i18n or disallow translations. (https://github.com/Hexix23/angular/blob/4f048e7de3f01e66d05dcc7f0a53926343d5fb5f/packages/compiler/src/render3/view/i18n/meta.ts#L211)
There was a problem hiding this comment.
I think no? Or else there's an attack vector through the translator right (lower risk, but still possible).
Is this specifically regarding href-like attributes or a generic concern for all static values?
| @@ -140,7 +146,31 @@ export function collectI18nConsts(job: ComponentCompilationJob): void { | |||
| const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext); | |||
| if (attributesForMessage !== undefined) { | |||
| for (const attr of attributesForMessage) { | |||
There was a problem hiding this comment.
Should we run the santizer here or outright reject this in translations for such attributes?
There was a problem hiding this comment.
Thanks Alan. To keep this aligned with the discussion from #68559: I agree the right fix should be centralized and schema-driven, not split into per-attribute patches.
My main concern is preserving the security invariant consistently:
translated static i18n attributes should not bypass the sanitizer/validator path Angular applies to the same security-sensitive tag/attribute pair elsewhere.
So I think the expected behavior should be:
- non-security-sensitive translated attributes remain allowed;
URL/HTML/STYLEcontexts are sanitized;RESOURCE_URLfails closed, because translated strings should not become trusted executable resource URLs;ATTRIBUTE_NO_BINDINGgoes throughvalidateAttribute/ rejection, because those attributes are not safely sanitizable.
The current #68591 direction appears to cover the concrete cases I reported across #68557, #68559, #68560, and #68580. My priority is to help make the broader hardening complete and robust rather than keep fragmented fixes open.
The only extra hardening point I would keep an eye on is namespace normalization anywhere SECURITY_SCHEMA is queried, especially SVG/MathML paths such as :svg:script, :svg:set, and MathML href / xlink:href, plus avoiding drift between the compiler-side and runtime/core schema
copies.
Hope this helps you and the team. Happy to test any extra cases if useful.
| if (tagName.startsWith(':svg:')) { | ||
| tagName = tagName.substring(5); | ||
| } else if (tagName.startsWith(':math:')) { | ||
| tagName = tagName.substring(6); | ||
| } |
There was a problem hiding this comment.
Question: Is there a risk that we're changing the semantics of a tag name by stripping its namespace?
I imagine the different types are mostly compatible, but that feels largely coincidental rather than an intentional constraint of the standard. Is it possible that something like svg:foo is dangerous while regular foo is not and then accidentally skip sanitization? Even if no attributes currently meet that definition, is there a chance future additions to the spec might?
| // property names do not have a security impact. | ||
| tagName = tagName.toLowerCase(); | ||
| if (tagName.startsWith(':svg:')) { | ||
| tagName = tagName.substring(5); |
There was a problem hiding this comment.
Consider: Use .length to be more clear about the intent and meaning behind the magic number.
| tagName = tagName.substring(5); | |
| tagName = tagName.substring(':svg:'.length); |
| ); | ||
| }); | ||
|
|
||
| it('should allow no security senstive attributes', () => { |
There was a problem hiding this comment.
Typo: I think you mean...
| it('should allow no security senstive attributes', () => { | |
| it('should allow non-security sensitive attributes', () => { |
|
|
||
| it('should allow no security senstive attributes', () => { | ||
| loadTranslations({[computeMsgId('50%')]: '100%'}); | ||
| const template = `<iframe width="50%" i18n-width></iframe>`; |
There was a problem hiding this comment.
Nit: title might be the more representative example? Sure, internationalizing width is probably fine from a security perspective, but its also just a bad idea in general.
|
|
||
| it('should throw error on static href attributes', () => { | ||
| loadTranslations({[computeMsgId('/safe')]: 'javascript:alert(1)'}); | ||
| const template = `<a href="/safe" i18n-href>Link</a>`; |
There was a problem hiding this comment.
I think no? Or else there's an attack vector through the translator right (lower risk, but still possible).
Is this specifically regarding href-like attributes or a generic concern for all static values?
There was a problem hiding this comment.
Question: Where did this come from? Don't we have an existing manifest/schema of all the security sensitive attributes?
| if (!ir.isStringLiteral(value)) { | ||
| throw Error('AssertionError: extracted attribute value should be string literal'); | ||
| } | ||
| if (trustedValueFn !== null && ir.isStringLiteral(value)) { |
There was a problem hiding this comment.
Is this change relevant/required?
| * @remarks Keep is a copy of DOM Security Schema. | ||
| * @see [SECURITY_SCHEMA](../../../compiler/src/schema/dom_security_schema.ts) |
There was a problem hiding this comment.
Do we have an easy way to keep this file in sync with the packages/compiler/src/schema/dom_security_schema.ts? Maybe as an extra test in one of the packages?
Updates
DomElementSchemaRegistryto strip:svg:and:math:namespace prefixes from tag names before queryingSECURITY_SCHEMAat compile-time. This allows SVG and MathML attributes to correctly match their security contexts during compilation.Also implements dynamic schema-based sanitization and validation for static i18n attributes by wrapping `` expressions directly in their core sanitizers and validators at compile-time via an optimized
switchstatement.Additionally, updates the runtime i18n parser to dynamically resolve sanitizers based on the
SECURITY_SCHEMAregistry and cleans up type assertions insideɵɵvalidateAttributeto safely support evaluation when noTNodeis selected.