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
5 changes: 5 additions & 0 deletions .changeset/quick-link-backtracking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@livekit/components-core': patch
---

Stop the chat link regex from freezing the page on a long word with no dot in it.
10 changes: 10 additions & 0 deletions packages/core/src/helper/url-regex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,14 @@ describe('url-regex', () => {
// Invalid URLs
expect(createUrlRegExp({}).test('livekitcom')).toBe(false);
});

test('should not backtrack for long on a long word with no url in it', () => {
// the backtracking pattern takes 8s+ on each input, the current one tens of ms, so the
// bound leaves room for a slow CI worker
for (const text of ['a'.repeat(3000), 'x:'.repeat(1500)]) {
const start = performance.now();
expect(text.match(createUrlRegExp({}))).toBeNull();
expect(performance.now() - start).toBeLessThan(5000);
}
});
});
8 changes: 5 additions & 3 deletions packages/core/src/helper/url-regex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ export function createUrlRegExp(options: RegExOptions) {
};

const protocol = `(?:(?:[a-z]+:)?//)?`;
const auth = '(?:\\S+(?::\\S*)?@)?';
const auth = '(?:\\S+@)?';
const ip = new RegExp(
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}',
'g',
).source;
const host = '(?:(?:[a-z\\u00a1-\\uffff0-9][-_]*)*[a-z\\u00a1-\\uffff0-9]+)';
const domain = '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*';
// each label is written so that a run of letters can be split only one way; the nested
// `(?:x[-_]*)*x+` form backtracks for seconds on a long word with no dot
const host = '(?:[a-z\\u00a1-\\uffff0-9]+(?:[-_]+[a-z\\u00a1-\\uffff0-9]+)*)';
const domain = '(?:\\.[a-z\\u00a1-\\uffff0-9]+(?:-+[a-z\\u00a1-\\uffff0-9]+)*)*';
const tld = `(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))(?:\\.(?=[:/?#]))?`;
const port = '(?::\\d{2,5})?';
// a path keeps balanced parentheses but not the punctuation that ends a sentence around it
Expand Down
Loading