Visitar URL original
fix(platform-browser): escape newlines in Meta selector values by rootvector2 · Pull Request #71236 · angular/angular · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 7 additions & 2 deletions packages/platform-browser/src/browser/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,13 @@ function parseSelector(tag: MetaDefinition): string {

function escapeSelectorValue(value: string): string {
// Escape backslashes and double quotes to prevent CSS selector injection.
// This securely confines the value inside an attribute selector.
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
// This securely confines the value inside an attribute selector. A newline cannot be confined
// that way: it terminates the string token (CSS Syntax 3 4.3.5), and carriage return and form
// feed are preprocessed into one (CSS Syntax 3 3.3), so write those as character escapes.
return `"${value
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/[\n\r\f]/g, (char) => `\\${char.charCodeAt(0).toString(16)} `)}"`;
}

function containsAttributes(tag: MetaDefinition, elem: HTMLMetaElement): boolean {
Expand Down
19 changes: 19 additions & 0 deletions packages/platform-browser/test/browser/meta_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,25 @@ describe('Meta service', () => {
metaService.removeTagElement(meta);
});

it('should escape newlines in selector values derived from the tag definition', () => {
// A raw newline terminates the CSS string token, so the derived `meta[name="..."]`
// selector is invalid and `querySelector` rejects it. Carriage return and form feed are
// preprocessed into a newline, so they behave the same way.
for (const name of ['description\nevil', 'description\revil', 'description\fevil']) {
const meta = metaService.addTag({name, content: 'first'})!;

expect(meta.getAttribute('name')).toEqual(name);
// Re-adding has to resolve the existing tag through the derived selector.
expect(metaService.addTag({name, content: 'first'})).toBe(meta);

metaService.updateTag({name, content: 'second'});
expect(meta.getAttribute('content')).toEqual('second');

// clean up
metaService.removeTagElement(meta);
}
});

it('should not let a quoted name break out of the meta selector and target body', () => {
// This payload attempts to break out of the `meta[name="..."]` constraint entirely
// and inject a comma to target arbitrary DOM elements like the `body` tag.
Expand Down
Loading