diff --git a/packages/rrweb-snapshot/src/css.ts b/packages/rrweb-snapshot/src/css.ts index 00816bcf1b..e51fc79fe9 100644 --- a/packages/rrweb-snapshot/src/css.ts +++ b/packages/rrweb-snapshot/src/css.ts @@ -439,10 +439,9 @@ export function parse(css: string, options: ParserOptions = {}) { return; } - /* @fix Remove all comments from selectors - * http://ostermiller.org/findcomment.html */ + /* @fix Remove all comments from selectors */ const splitSelectors = trim(m[0]) - .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '') + .replace(/\/\*[\s\S]*?\*\/+/g, '') .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (m) => { return m.replace(/,/g, '\u200C'); }) diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index ffcc2ae1a1..bcbde42da5 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -1345,7 +1345,7 @@ export function serializeNodeWithId( (!preserveWhiteSpace && _serializedNode.type === NodeType.Text && !_serializedNode.isStyle && - !_serializedNode.textContent.replace(/^\s+|\s+$/gm, '').length) + !_serializedNode.textContent.trim().length) ) { id = IGNORED_NODE; } else { diff --git a/packages/rrweb-snapshot/test/css.test.ts b/packages/rrweb-snapshot/test/css.test.ts index c48f35e4c9..b446cc4e0f 100644 --- a/packages/rrweb-snapshot/test/css.test.ts +++ b/packages/rrweb-snapshot/test/css.test.ts @@ -97,6 +97,29 @@ describe('css parser', () => { expect(decl.parent).toEqual(rule); }); + it('should strip comments from selectors', () => { + const cases: Array<[string, string[]]> = [ + ['.foo /* comment */, .bar { color: red; }', ['.foo', '.bar']], + ['a /* x */ b, c /* y */ d { color: red; }', ['a b', 'c d']], + ['.x /* trailing */ { color: red; }', ['.x']], + ]; + for (const [css, expected] of cases) { + const rules = parse(css).stylesheet!.rules.filter( + (r): r is Rule => r.type === 'rule', + ); + expect(rules[0].selectors?.map((s) => s.trim())).toEqual(expected); + } + }); + + it('should not catastrophically backtrack on unterminated selector comments', () => { + const evil = '/*' + '\n*'.repeat(40); + const start = Date.now(); + expect(() => + parse(`${evil} { color: red; }`, { silent: true }), + ).not.toThrow(); + expect(Date.now() - start).toBeLessThan(500); + }); + // TODO(sentry): our parser can't handle this atm it.skip('parses { and } in attribute selectors correctly', () => { const result = parse('foo[someAttr~="{someId}"] { color: red; }');