`));
49 | }
50 | ```
51 |
52 | ## Default policy
53 |
54 | The default policy defines allowed tags and attributes (sometimes conditioned to
55 | the tag they're used in). The policy is allowlist based. The policy is defined
56 | in accordance with the
57 | [sanitizer_table](https://github.com/google/safevalues/blob/main/src/builders/html_sanitizer/sanitizer_table/default_sanitizer_table.ts)
58 | declaration:
59 |
60 | A tag is allowed if and only if:
61 |
62 | * it's part of the
63 | [`allowedElements`](https://github.com/google/safevalues/blob/main/src/builders/html_sanitizer/sanitizer_table/default_sanitizer_table.ts#L17)
64 |
65 | OR
66 |
67 | * it has a key in the
68 | [`elementPolicies`](https://github.com/google/safevalues/blob/main/src/builders/html_sanitizer/sanitizer_table/default_sanitizer_table.ts#L35)
69 |
70 | An attribute is allowed if and only if:
71 |
72 | * it's
73 | [globally allowed](https://github.com/google/safevalues/blob/main/src/builders/html_sanitizer/sanitizer_table/default_sanitizer_table.ts#L98)
74 | or has a
75 | [global attribute policy](https://github.com/google/safevalues/blob/main/src/builders/html_sanitizer/sanitizer_table/default_sanitizer_table.ts#L199)
76 |
77 | OR
78 |
79 | * it's
80 | [allowed for the element](https://github.com/google/safevalues/blob/main/src/builders/html_sanitizer/sanitizer_table/default_sanitizer_table.ts#L35)
81 | being considered
82 |
83 | Attribute values are preserved, sanitized normalized or dropped following the
84 | policy (`AttributePolicyAction`).
85 |
86 | ## Sanitizer builder API
87 |
88 | The sanitizer builder API can be used to ban additional elements and attributes.
89 | Example:
90 |
91 | ```typescript
92 | // Only allows
elements
93 | const sanitizer = new HtmlSanitizerBuilder()
94 | .onlyAllowElements(new Set(['article']))
95 | .build();
96 | ```
97 |
98 | If needed, `style`, `id`, `data-`, or `class` attributes can be allowed.
99 | Example:
100 |
101 | ```typescript
102 | // Allows some data-* attributes
103 | const sanitizer = new HtmlSanitizerBuilder()
104 | .allowDataAttributes(['data-foo', 'data-bar'])
105 | .build();
106 |
107 | // Allow any data-* attributes
108 | const sanitizer = new HtmlSanitizerBuilder()
109 | .allowDataAttributes()
110 | .build();
111 | ```
112 |
113 | ```typescript
114 | // Allow class and id attributes
115 | const sanitizer = new HtmlSanitizerBuilder()
116 | .allowClassAttributes()
117 | .allowIdAttributes()
118 | .build();
119 | ```
120 |
--------------------------------------------------------------------------------
/src/builders/html_sanitizer/css/css_isolation.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google LLC
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | /**
8 | * @fileoverview Exports a stylesheet that isolates the sanitized content from
9 | * the rest of the page.
10 | *
11 | * One of the design goals of the CSS sanitizer is to ensure that the sanitized
12 | * content cannot affect the rest of the page. For example, the sanitized
13 | * content should not be able to cover other elements on the page, or to show up
14 | * outside of the sanitized container.
15 | *
16 | * This file exports a stylesheet that makes this design goal a reality. Check
17 | * out css_isolation_test.ts to see specific examples of what this stylesheet
18 | * attempts to prevent.
19 | */
20 |
21 | /**
22 | * A set of CSS properties that isolate the sanitized content from the rest of
23 | * the page.
24 | *
25 | * * `display:inline-block`, `clip-path:inset(0)` and `overflow:hidden` - ensure
26 | * that the sanitized content cannot cover other elements on the page.
27 | * * `vertical-align:top` - fixes a quirk when `overflow:hidden` is used on
28 | * inline elements and they are not aligned correctly. See
29 | * https://stackoverflow.com/questions/30182800/css-overflowhidden-with-displayinline-block
30 | * for details.
31 | * * `text-decoration:inherit` - ensures that the sanitized content inherits
32 | * the text decoration from the parent element, which is not the default for
33 | * `display:inline-block`. See
34 | * https://www.w3.org/TR/2022/CRD-css-text-decor-3-20220505/#:~:text=Note%20that%20text%20decorations%20are%20not%20propagated%20to%20any%20out%2Dof%2Dflow%20descendants%2C%20nor%20to%20the%20contents%20of%20atomic%20inline%2Dlevel%20descendants%20such%20as%20inline%20blocks
35 | * for details.
36 | */
37 | export const CSS_ISOLATION_PROPERTIES =
38 | 'display:inline-block;clip-path:inset(0);overflow:hidden;vertical-align:top;text-decoration:inherit';
39 |
40 | /**
41 | * A stylesheet that isolates the sanitized content from the rest of the page.
42 | */
43 | export const CSS_ISOLATION_STYLESHEET: ':host{display:inline-block;clip-path:inset(0);overflow:hidden;vertical-align:top;text-decoration:inherit}' = `:host{${CSS_ISOLATION_PROPERTIES}}`;
44 |
--------------------------------------------------------------------------------
/src/builders/html_sanitizer/css/serializer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google LLC
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | /**
8 | * @fileoverview Exports methods for serializing CSS tokens.
9 | */
10 |
11 | import {CssToken, CssTokenKind} from './tokens.js';
12 |
13 | function escapeCodePoint(c: string): string {
14 | return `\\${c.codePointAt(0)!.toString(16)} `;
15 | }
16 |
17 | function escapeString(str: string): string {
18 | // We don't escape some characters to increase readability.
19 | return (
20 | '"' +
21 | str.replace(/[^A-Za-z0-9_/. :,?=%;-]/g, (c) => escapeCodePoint(c)) +
22 | '"'
23 | );
24 | }
25 |
26 | /**
27 | * Escapes a CSS identifier.
28 | *
29 | * @param ident The identifier to escape.
30 | * @return The escaped identifier.
31 | */
32 | export function escapeIdent(ident: string): string {
33 | // We don't generally escape digits or "-" in identifiers, however we do need
34 | // to do this for the first character to avoid ambiguity.
35 | //
36 | // For example, the string "123" would create a valid number token, but if
37 | // we want to have an ident-token, it needs to be escaped as a "\31 23".
38 | const firstChar = /^[^A-Za-z_]/.test(ident)
39 | ? escapeCodePoint(ident[0])
40 | : ident[0];
41 | return (
42 | firstChar +
43 | ident.slice(1).replace(/[^A-Za-z0-9_-]/g, (c) => escapeCodePoint(c))
44 | );
45 | }
46 |
47 | /**
48 | * Serializes a CSS token to a string.
49 | *
50 | * @param token The token to serialize.
51 | * @return The serialized token.
52 | */
53 | export function serializeToken(token: CssToken): string {
54 | switch (token.tokenKind) {
55 | case CssTokenKind.AT_KEYWORD:
56 | return `@${escapeIdent(token.name)}`;
57 | case CssTokenKind.CDC:
58 | return '-->';
59 | case CssTokenKind.CDO:
60 | return '