mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(core): reject dynamic script host elements
The previous fix for GHSA-692r-grfm-v8x7 was incomplete because it rejected script tags only when locating an explicit host element. Dynamic component instantiation can also infer the host element from the component selector. Move the script-host rejection to the point where ComponentFactory has resolved the host element for either path, so createComponent rejects script hosts consistently.
This commit is contained in:
committed by
Alex Rickabaugh
parent
a68e610641
commit
d7f70616a0
@@ -183,6 +183,15 @@ function createHostElement(componentDef: ComponentDef<unknown>, renderer: Render
|
||||
return createElementNode(renderer, tagName, namespace);
|
||||
}
|
||||
|
||||
function assertNotScriptHostElement(tagName: string | null | undefined): void {
|
||||
if (tagName?.toLowerCase() === 'script') {
|
||||
throw new RuntimeError(
|
||||
RuntimeErrorCode.UNSAFE_VALUE_IN_SCRIPT,
|
||||
ngDevMode && `"<script>" tag is not allowed as a component host element.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Infers the tag name that should be used for a component based on its definition.
|
||||
* @param componentDef Definition for which to resolve the tag name.
|
||||
@@ -303,6 +312,7 @@ export class ComponentFactory<T> {
|
||||
const hostElement = rootSelectorOrNode
|
||||
? locateHostElement(hostRenderer, rootSelectorOrNode, cmpDef.encapsulation, rootViewInjector)
|
||||
: createHostElement(cmpDef, hostRenderer);
|
||||
assertNotScriptHostElement(hostElement?.tagName);
|
||||
|
||||
const sharedStylesHost = rootViewInjector.get(SHARED_STYLES_HOST, null);
|
||||
const styleHost = getStyleHost(
|
||||
|
||||
@@ -15,7 +15,6 @@ import {ViewEncapsulation} from '../../metadata/view';
|
||||
import {validateAgainstEventProperties} from '../../sanitization/sanitization';
|
||||
|
||||
import {ProfilerEvent} from '../../../primitives/devtools';
|
||||
import {RuntimeError, RuntimeErrorCode} from '../../errors';
|
||||
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../ng_reflect';
|
||||
import {assertIndexInRange, assertNotSame} from '../../util/assert';
|
||||
import {escapeCommentText} from '../../util/dom';
|
||||
@@ -181,12 +180,6 @@ export function locateHostElement(
|
||||
encapsulation === ViewEncapsulation.ShadowDom ||
|
||||
encapsulation === ViewEncapsulation.ExperimentalIsolatedShadowDom;
|
||||
const rootElement = renderer.selectRootElement(elementOrSelector, preserveContent);
|
||||
if (rootElement.tagName.toLowerCase() === 'script') {
|
||||
throw new RuntimeError(
|
||||
RuntimeErrorCode.UNSAFE_VALUE_IN_SCRIPT,
|
||||
ngDevMode && `"<script>" tag is not allowed as a component host element.`,
|
||||
);
|
||||
}
|
||||
applyRootElementTransform(rootElement as HTMLElement);
|
||||
return rootElement;
|
||||
}
|
||||
|
||||
@@ -873,6 +873,19 @@ describe('innerHTML processing', () => {
|
||||
});
|
||||
|
||||
describe('Component host element validation', () => {
|
||||
it('should throw an error when dynamically creating a component with a script selector', () => {
|
||||
@Component({
|
||||
selector: 'script',
|
||||
template: '',
|
||||
})
|
||||
class ScriptHost {}
|
||||
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
expect(() => {
|
||||
createComponent(ScriptHost, {environmentInjector});
|
||||
}).toThrowError(/"<script>" tag is not allowed as a component host element/);
|
||||
});
|
||||
|
||||
it('should throw an error when dynamically mounting a component onto a script tag', () => {
|
||||
@Component({
|
||||
selector: 'my-sink',
|
||||
|
||||
@@ -338,6 +338,7 @@
|
||||
"areAnimationSupported",
|
||||
"arrRemove",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
"balancePreviousStylesIntoKeyframes",
|
||||
|
||||
@@ -263,6 +263,7 @@
|
||||
"areAnimationSupported",
|
||||
"arrRemove",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
"baseElement",
|
||||
|
||||
@@ -309,6 +309,7 @@
|
||||
"areAnimationSupported",
|
||||
"arrRemove",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
"bind",
|
||||
|
||||
@@ -389,6 +389,7 @@
|
||||
"assertAllValuesPresent",
|
||||
"assertControlPresent",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertPlatform",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
|
||||
@@ -390,6 +390,7 @@
|
||||
"assertAllValuesPresent",
|
||||
"assertControlPresent",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertPlatform",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
|
||||
@@ -375,6 +375,7 @@
|
||||
"areAnimationSupported",
|
||||
"arrRemove",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
"baseElement",
|
||||
|
||||
@@ -423,6 +423,7 @@
|
||||
"arrRemove",
|
||||
"arrayEquals",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
"baseElement",
|
||||
|
||||
@@ -244,6 +244,7 @@
|
||||
"areAnimationSupported",
|
||||
"arrRemove",
|
||||
"assertNotDestroyed",
|
||||
"assertNotScriptHostElement",
|
||||
"assertTypeDefined",
|
||||
"attachPatchData",
|
||||
"baseElement",
|
||||
|
||||
Reference in New Issue
Block a user