Skip to content

Commit

Permalink
Fix #83731
Browse files Browse the repository at this point in the history
  • Loading branch information
octref committed Nov 6, 2019
1 parent e88e032 commit 73d9d76
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const TRUSTED_DOMAINS_STAT: IStat = {
const CONFIG_HELP_TEXT_PRE = `// Links matching one or more entries in the list below can be opened without link protection.
// The following examples show what entries can look like:
// - "https://microsoft.com": Matches this specific domain using https
// - "https://microsoft.com/foo": Matches https://microsoft.com/foo and https://microsoft.com/foo/bar,
// but not https://microsoft.com/foobar or https://microsoft.com/bar
// - "https://*.microsoft.com": Match all domains ending in "microsoft.com" using https
// - "microsoft.com": Match this specific domain using either http or https
// - "*.microsoft.com": Match all domains ending in "microsoft.com" using either http or https
Expand Down
36 changes: 28 additions & 8 deletions src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { Schemas } from 'vs/base/common/network';
import Severity from 'vs/base/common/severity';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { equalsIgnoreCase, startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
Expand All @@ -14,7 +14,10 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { configureOpenerTrustedDomainsHandler, readTrustedDomains } from 'vs/workbench/contrib/url/common/trustedDomains';
import {
configureOpenerTrustedDomainsHandler,
readTrustedDomains
} from 'vs/workbench/contrib/url/common/trustedDomains';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

export class OpenerValidatorContributions implements IWorkbenchContribution {
Expand Down Expand Up @@ -132,10 +135,11 @@ export function isURLDomainTrusted(url: URI, trustedDomains: string[]) {
}

if (url.authority === parsedTrustedDomain.authority) {
return true;
return pathMatches(url.path, parsedTrustedDomain.path);
}

if (trustedDomains[i].indexOf('*') !== -1) {

let reversedAuthoritySegments = url.authority.split('.').reverse();
const reversedTrustedDomainAuthoritySegments = parsedTrustedDomain.authority.split('.').reverse();

Expand All @@ -146,15 +150,31 @@ export function isURLDomainTrusted(url: URI, trustedDomains: string[]) {
reversedAuthoritySegments = reversedAuthoritySegments.slice(0, reversedTrustedDomainAuthoritySegments.length);
}

if (
reversedAuthoritySegments.every((val, i) => {
return reversedTrustedDomainAuthoritySegments[i] === '*' || val === reversedTrustedDomainAuthoritySegments[i];
})
) {
const authorityMatches = reversedAuthoritySegments.every((val, i) => {
return reversedTrustedDomainAuthoritySegments[i] === '*' || val === reversedTrustedDomainAuthoritySegments[i];
});

if (authorityMatches && pathMatches(url.path, parsedTrustedDomain.path)) {
return true;
}
}
}

return false;
}

function pathMatches(open: string, rule: string) {
if (rule === '/') {
return true;
}

const openSegments = open.split('/');
const ruleSegments = rule.split('/');
for (let i = 0; i < ruleSegments.length; i++) {
if (ruleSegments[i] !== openSegments[i]) {
return false;
}
}

return true;
}
70 changes: 44 additions & 26 deletions src/vs/workbench/test/contrib/linkProtection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,67 @@ import * as assert from 'assert';
import { isURLDomainTrusted } from 'vs/workbench/contrib/url/common/trustedDomainsValidator';
import { URI } from 'vs/base/common/uri';

function linkProtectedByRules(link: string, rules: string[]) {
assert.ok(isURLDomainTrusted(URI.parse(link), rules));
function linkAllowedByRules(link: string, rules: string[]) {
assert.ok(isURLDomainTrusted(URI.parse(link), rules), `Link\n${link}\n should be protected by rules\n${JSON.stringify(rules)}`);
}
function linkNotProtectedByRules(link: string, rules: string[]) {
assert.ok(!isURLDomainTrusted(URI.parse(link), rules));
function linkNotAllowedByRules(link: string, rules: string[]) {
assert.ok(!isURLDomainTrusted(URI.parse(link), rules), `Link\n${link}\n should NOT be protected by rules\n${JSON.stringify(rules)}`);
}

suite('Link protection domain matching', () => {
test('simple', () => {
linkNotProtectedByRules('https://x.org', []);
linkNotAllowedByRules('https://x.org', []);

linkProtectedByRules('https://x.org', ['https://x.org']);
linkProtectedByRules('https://x.org/foo', ['https://x.org']);
linkAllowedByRules('https://x.org', ['https://x.org']);
linkAllowedByRules('https://x.org/foo', ['https://x.org']);

linkNotProtectedByRules('https://x.org', ['http://x.org']);
linkNotProtectedByRules('http://x.org', ['https://x.org']);
linkNotAllowedByRules('https://x.org', ['http://x.org']);
linkNotAllowedByRules('http://x.org', ['https://x.org']);

linkNotProtectedByRules('https://www.x.org', ['https://x.org']);
linkNotAllowedByRules('https://www.x.org', ['https://x.org']);

linkProtectedByRules('https://www.x.org', ['https://www.x.org', 'https://y.org']);
linkAllowedByRules('https://www.x.org', ['https://www.x.org', 'https://y.org']);
});

test('localhost', () => {
linkProtectedByRules('https://127.0.0.1', []);
linkProtectedByRules('https://127.0.0.1:3000', []);
linkProtectedByRules('https://localhost', []);
linkProtectedByRules('https://localhost:3000', []);
linkAllowedByRules('https://127.0.0.1', []);
linkAllowedByRules('https://127.0.0.1:3000', []);
linkAllowedByRules('https://localhost', []);
linkAllowedByRules('https://localhost:3000', []);
});

test('* star', () => {
linkProtectedByRules('https://a.x.org', ['https://*.x.org']);
linkProtectedByRules('https://a.b.x.org', ['https://*.x.org']);
linkProtectedByRules('https://a.x.org', ['https://a.x.*']);
linkProtectedByRules('https://a.x.org', ['https://a.*.org']);
linkProtectedByRules('https://a.x.org', ['https://*.*.org']);
linkProtectedByRules('https://a.b.x.org', ['https://*.b.*.org']);
linkProtectedByRules('https://a.a.b.x.org', ['https://*.b.*.org']);
linkAllowedByRules('https://a.x.org', ['https://*.x.org']);
linkAllowedByRules('https://a.b.x.org', ['https://*.x.org']);
linkAllowedByRules('https://a.x.org', ['https://a.x.*']);
linkAllowedByRules('https://a.x.org', ['https://a.*.org']);
linkAllowedByRules('https://a.x.org', ['https://*.*.org']);
linkAllowedByRules('https://a.b.x.org', ['https://*.b.*.org']);
linkAllowedByRules('https://a.a.b.x.org', ['https://*.b.*.org']);
});

test('no scheme', () => {
linkProtectedByRules('https://a.x.org', ['a.x.org']);
linkProtectedByRules('https://a.x.org', ['*.x.org']);
linkProtectedByRules('https://a.b.x.org', ['*.x.org']);
linkProtectedByRules('https://x.org', ['*.x.org']);
linkAllowedByRules('https://a.x.org', ['a.x.org']);
linkAllowedByRules('https://a.x.org', ['*.x.org']);
linkAllowedByRules('https://a.b.x.org', ['*.x.org']);
linkAllowedByRules('https://x.org', ['*.x.org']);
});

test('sub paths', () => {
linkAllowedByRules('https://x.org/foo', ['https://x.org/foo']);
linkAllowedByRules('https://x.org/foo', ['x.org/foo']);
linkAllowedByRules('https://x.org/foo', ['*.org/foo']);

linkNotAllowedByRules('https://x.org/bar', ['https://x.org/foo']);
linkNotAllowedByRules('https://x.org/bar', ['x.org/foo']);
linkNotAllowedByRules('https://x.org/bar', ['*.org/foo']);

linkAllowedByRules('https://x.org/foo/bar', ['https://x.org/foo']);
linkNotAllowedByRules('https://x.org/foo2', ['https://x.org/foo']);

linkNotAllowedByRules('https://www.x.org/foo', ['https://x.org/foo']);

linkNotAllowedByRules('https://a.x.org/bar', ['https://*.x.org/foo']);
linkNotAllowedByRules('https://a.b.x.org/bar', ['https://*.x.org/foo']);
});
});

0 comments on commit 73d9d76

Please sign in to comment.