Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
3ac5a9e
refactor(core): align namespaced attribute validation and security sc…
alan-agius4 May 18, 2026
c939775
fix(core): support prefix-insensitive DOM schema lookups and compile-…
alan-agius4 May 19, 2026
3b2a9f0
fix(compiler): strip namespaced SVG script elements during template c…
alan-agius4 May 19, 2026
547e5f8
test(core): remove obsolete SVG script sanitization translation test
leonsenft May 20, 2026
0446654
fix(compiler): sanitize dynamic href and xlink:href bindings on SVG a…
alan-agius4 May 21, 2026
b15ef46
fix(compiler): normalize tag names with custom namespaces in DomEleme…
alan-agius4 May 22, 2026
da5855c
fix(core): normalize tag names in runtime i18n attribute security con…
alan-agius4 May 22, 2026
f1825bb
fixup! fix(core): normalize tag names in runtime i18n attribute secur…
alan-agius4 May 26, 2026
c90eec0
fix(core): synchronize core sanitization schema with compiler
alan-agius4 May 26, 2026
e1526f4
refactor(core): resolve merge conflicts in sanitization.ts
alan-agius4 May 26, 2026
af7d3a0
test(compiler): remove obsolete schema_extractor import
alan-agius4 May 26, 2026
bb69eb3
test(core): update spec files to match 20.3.x limits and actual contexts
alan-agius4 May 26, 2026
d96a322
test(core): remove obsolete blockquote cite host binding tests
alan-agius4 May 26, 2026
117b0eb
fix(core): reject script element as a dynamic component host
alan-agius4 May 13, 2026
79a7dbe
test(core): update golden symbols and host bindings sanitization spec
alan-agius4 May 26, 2026
7c5837f
test(compiler-cli): align ngtsc sanitization expectations with modern…
alan-agius4 May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 2 goldens/public-api/core/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,8 @@ export interface SchemaMetadata {

// @public
export enum SecurityContext {
// (undocumented)
ATTRIBUTE_NO_BINDING = 6,
// (undocumented)
HTML = 1,
// (undocumented)
Expand Down
2 changes: 1 addition & 1 deletion 2 integration/cli-hello-world-ivy-i18n/size.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"dist/main.js": 135813,
"dist/main.js": 144843,
"dist/polyfills.js": 35883
}
42 changes: 4 additions & 38 deletions 42 packages/compiler-cli/test/ngtsc/ngtsc_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8432,9 +8432,6 @@ runInEachFileSystem((os: string) => {
@HostBinding('attr.action')
attrAction: string;

@HostBinding('attr.profile')
attrProfile: string;

@HostBinding('attr.innerHTML')
attrInnerHTML: string;

Expand All @@ -8461,10 +8458,10 @@ runInEachFileSystem((os: string) => {
env.driveMain();
const jsContents = env.getContents('test.js');
const hostBindingsFn = `
hostVars: 6,
hostVars: 5,
hostBindings: function UnsafeAttrsDirective_HostBindings(rf, ctx) {
if (rf & 2) {
i0.ɵɵattribute("href", ctx.attrHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.attrSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.attrAction, i0.ɵɵsanitizeUrl)("profile", ctx.attrProfile, i0.ɵɵsanitizeResourceUrl)("innerHTML", ctx.attrInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.attrSafeTitle);
i0.ɵɵattribute("href", ctx.attrHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.attrSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.attrAction, i0.ɵɵsanitizeUrl)("innerHTML", ctx.attrInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.attrSafeTitle);
}
}
`;
Expand All @@ -8491,9 +8488,6 @@ runInEachFileSystem((os: string) => {
@HostBinding('action')
propAction: string;

@HostBinding('profile')
propProfile: string;

@HostBinding('innerHTML')
propInnerHTML: string;

Expand All @@ -8520,44 +8514,16 @@ runInEachFileSystem((os: string) => {
env.driveMain();
const jsContents = env.getContents('test.js');
const hostBindingsFn = `
hostVars: 6,
hostVars: 5,
hostBindings: function UnsafePropsDirective_HostBindings(rf, ctx) {
if (rf & 2) {
i0.ɵɵdomProperty("href", ctx.propHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.propSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.propAction, i0.ɵɵsanitizeUrl)("profile", ctx.propProfile, i0.ɵɵsanitizeResourceUrl)("innerHTML", ctx.propInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.propSafeTitle);
i0.ɵɵdomProperty("href", ctx.propHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.propSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.propAction, i0.ɵɵsanitizeUrl)("innerHTML", ctx.propInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.propSafeTitle);
}
}
`;
expect(trim(jsContents)).toContain(trim(hostBindingsFn));
});

it('should generate sanitizers for URL properties in SVG script fn in Component', () => {
env.write(
'test.ts',
`
import {Component} from '@angular/core';

@Component({
selector: 'test-cmp',
template: \`
<svg>
<script [attr.xlink:href]="attr" [attr.href]="attr"></script>
</svg>
\`,
})
export class TestCmp {
attr = './script.js';
}
`,
);

env.driveMain();

const jsContents = env.getContents('test.js');
expect(jsContents).toContain(
'i0.ɵɵattribute("href", ctx.attr, i0.ɵɵsanitizeResourceUrl, "xlink")("href", ctx.attr, i0.ɵɵsanitizeResourceUrl);',
);
});

it('should not generate sanitizers for URL properties in hostBindings fn in Component', () => {
env.write(
`test.ts`,
Expand Down
1 change: 0 additions & 1 deletion 1 packages/compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {publishFacade} from './jit_compiler_facade';
import * as outputAst from './output/output_ast';
import {global} from './util';

export {SECURITY_SCHEMA} from './schema/dom_security_schema';
export {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from './core';
export {core};

Expand Down
12 changes: 2 additions & 10 deletions 12 packages/compiler/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,6 @@ export interface Type extends Function {
}
export const Type = Function;

export enum SecurityContext {
NONE = 0,
HTML = 1,
STYLE = 2,
SCRIPT = 3,
URL = 4,
RESOURCE_URL = 5,
ATTRIBUTE_NO_BINDING = 6,
}

/**
* Injection flags for DI.
*/
Expand Down Expand Up @@ -328,3 +318,5 @@ export const enum AttributeMarker {
*/
I18n = 6,
}

export {SecurityContext} from './schema/dom_security_schema';
50 changes: 30 additions & 20 deletions 50 packages/compiler/src/schema/dom_element_schema_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
*/

import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata, SecurityContext} from '../core';
import {isNgContainer, isNgContent} from '../ml_parser/tags';
import {isNgContainer, isNgContent, splitNsName} from '../ml_parser/tags';
import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from '../template/pipeline/src/namespaces';
import {dashCaseToCamelCase} from '../util';

import {SECURITY_SCHEMA} from './dom_security_schema';
Expand All @@ -18,6 +19,13 @@ const NUMBER = 'number';
const STRING = 'string';
const OBJECT = 'object';

function normalizeTagName(tagName: string): string {
const tagNameLower = tagName.toLowerCase();
const [ns, name] = splitNsName(tagNameLower, false);

return ns === SVG_NAMESPACE || ns === MATH_ML_NAMESPACE ? `:${ns}:${name}` : name;
}

/**
* This array represents the DOM schema. It encodes inheritance, properties, and events.
*
Expand Down Expand Up @@ -377,8 +385,9 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
return true;
}

if (tagName.indexOf('-') > -1) {
if (isNgContainer(tagName) || isNgContent(tagName)) {
const normalizedTag = normalizeTagName(tagName);
if (normalizedTag.includes('-')) {
if (isNgContainer(normalizedTag) || isNgContent(normalizedTag)) {
return false;
}

Expand All @@ -389,8 +398,7 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
}
}

const elementProperties =
this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown')!;
const elementProperties = this._schema.get(normalizedTag) || this._schema.get('unknown')!;
return elementProperties.has(propName);
}

Expand All @@ -399,8 +407,9 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
return true;
}

if (tagName.indexOf('-') > -1) {
if (isNgContainer(tagName) || isNgContent(tagName)) {
const normalizedTag = normalizeTagName(tagName);
if (normalizedTag.includes('-')) {
if (isNgContainer(normalizedTag) || isNgContent(normalizedTag)) {
return true;
}

Expand All @@ -410,7 +419,7 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
}
}

return this._schema.has(tagName.toLowerCase());
return this._schema.has(normalizedTag);
}

/**
Expand All @@ -433,16 +442,16 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
propName = this.getMappedPropName(propName);
}

// Make sure comparisons are case insensitive, so that case differences between attribute and
// property names do not have a security impact.
tagName = tagName.toLowerCase();
const normalizedTag = normalizeTagName(tagName);
propName = propName.toLowerCase();
let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
if (ctx) {
return ctx;
}
ctx = SECURITY_SCHEMA()['*|' + propName];
return ctx ? ctx : SecurityContext.NONE;

const securitySchema = SECURITY_SCHEMA();
const ctx =
securitySchema[normalizedTag + '|' + propName] ??
securitySchema['*|' + propName] ??
SecurityContext.NONE;

return ctx;
}

override getMappedPropName(propName: string): string {
Expand Down Expand Up @@ -482,14 +491,15 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
}

allKnownAttributesOfElement(tagName: string): string[] {
const elementProperties =
this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown')!;
const normalizedTag = normalizeTagName(tagName);
const elementProperties = this._schema.get(normalizedTag) || this._schema.get('unknown')!;
// Convert properties to attributes.
return Array.from(elementProperties.keys()).map((prop) => _PROP_TO_ATTR.get(prop) ?? prop);
}

allKnownEventsOfElement(tagName: string): string[] {
return Array.from(this._eventSchema.get(tagName.toLowerCase()) ?? []);
const normalizedTag = normalizeTagName(tagName);
return Array.from(this._eventSchema.get(normalizedTag) ?? []);
}

override normalizeAnimationStyleProperty(propName: string): string {
Expand Down
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.