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
6 changes: 3 additions & 3 deletions src/CsvEditorProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1999,7 +1999,7 @@ class CsvEditorController {

private isAllowedExternalScheme(scheme: string): boolean {
const normalized = scheme.toLowerCase();
return normalized === 'http' || normalized === 'https' || normalized === 'ftp' || normalized === 'mailto';
return normalized === 'http' || normalized === 'https' || normalized === 'ftp' || normalized === 'mailto' || normalized === 'file';
}

private isAllowedExternalUrl(rawUrl: unknown): rawUrl is string {
Expand Down Expand Up @@ -2029,8 +2029,8 @@ class CsvEditorController {

private linkifyUrls(escapedText: string): string {
// Match URLs in already-escaped text (handles & in query strings).
// Supports http, https, ftp, mailto, and www.*.* (Google Sheets-like behavior).
const urlPattern = /\b(?:(?:https?:\/\/|ftp:\/\/|mailto:)[^\s<>&"']+(?:&amp;[^\s<>&"']+)*|www\.[^\s<>&"']+\.[^\s<>&"']+)/gi;
// Supports http, https, ftp, file, mailto, and www.*.* (Google Sheets-like behavior).
const urlPattern = /\b(?:(?:https?:\/\/|ftp:\/\/|file:\/\/|mailto:)[^\s<>&"']+(?:&amp;[^\s<>&"']+)*|www\.[^\s<>&"']+\.[^\s<>&"']+)/gi;
return escapedText.replace(urlPattern, (rawMatch) => {
let matched = rawMatch;
let trailing = '';
Expand Down
10 changes: 9 additions & 1 deletion src/test/provider-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ describe('CsvEditorProvider utility methods', () => {
assert.ok(html.includes('>www.google.com</span>,'));
});

it('formatCellContent linkifies file:/// URLs when enabled', () => {
const format = CsvEditorProvider.__test.formatCellContent;
const html = format('Open file:///tmp/example.csv and continue.', true);
assert.ok(html.includes('class="csv-link"'));
assert.ok(html.includes('data-href="file:///tmp/example.csv"'));
assert.ok(html.includes('>file:///tmp/example.csv</span>'));
});

it('formatCellContent leaves URLs as plain text when linkify is disabled', () => {
const format = CsvEditorProvider.__test.formatCellContent;
const html = format('https://example.com?a=1&b=2', false);
Expand All @@ -66,9 +74,9 @@ describe('CsvEditorProvider utility methods', () => {
assert.strictEqual(allowed('http://example.com'), true);
assert.strictEqual(allowed('ftp://example.com/file.txt'), true);
assert.strictEqual(allowed('mailto:user@example.com'), true);
assert.strictEqual(allowed('file:///tmp/x.csv'), true);
assert.strictEqual(allowed('javascript:alert(1)'), false);
assert.strictEqual(allowed('data:text/plain,hello'), false);
assert.strictEqual(allowed('file:///tmp/x.csv'), false);
assert.strictEqual(allowed(''), false);
});

Expand Down