diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 39ad9e80805750471e6a0a02a92a817f7ac48872..4dfeff378ee92a806ec7985a66b6c14c041ea81c 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -56,6 +56,7 @@ export default class URI { private static _empty = ''; private static _slash = '/'; private static _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; + private static _driveLetter = /^[a-zA-Z]:/; private static _driveLetterPath = /^\/[a-zA-Z]:/; private static _upperCaseDrive = /^(\/)?([A-Z]:)/; @@ -212,19 +213,25 @@ export default class URI { let idx = path.indexOf(URI._slash, 2); if (idx === -1) { authority = path.substring(2); - path = URI._empty; + path = URI._slash; } else { authority = path.substring(2, idx); path = path.substring(idx); } } - // Ensure that path starts with a slash - // or that it is at least a slash - if (path[0] !== URI._slash) { + // absolute windows paths get another slash + if (URI._driveLetter.test(path)) { path = URI._slash + path; } + // path must be absolute because we cannot format them + // otherwise: `file:./foo` -> file://./foo -> `{ authority: '.', path: 'foo' }` + // https://tools.ietf.org/html/rfc8089#section-2 + else if (path[0] !== URI._slash) { + throw new Error('[UriError]: relative paths are not supported.'); + } + return new URI('file', authority, path, URI._empty, URI._empty); } diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 3341b0bcb6aae261aafc421e0fcc1d14648af0f1..df2d989d26c149ef65878e33d8d412113181c335 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -38,13 +38,11 @@ suite('URI', () => { assert.equal(URI.file('c:/win/path/').fsPath, 'c:\\win\\path\\'); assert.equal(URI.file('C:/win/path').fsPath, 'c:\\win\\path'); assert.equal(URI.file('/c:/win/path').fsPath, 'c:\\win\\path'); - assert.equal(URI.file('./c/win/path').fsPath, '\\.\\c\\win\\path'); } else { assert.equal(URI.file('c:/win/path').fsPath, 'c:/win/path'); assert.equal(URI.file('c:/win/path/').fsPath, 'c:/win/path/'); assert.equal(URI.file('C:/win/path').fsPath, 'c:/win/path'); assert.equal(URI.file('/c:/win/path').fsPath, 'c:/win/path'); - assert.equal(URI.file('./c/win/path').fsPath, '/./c/win/path'); } }); @@ -318,27 +316,12 @@ suite('URI', () => { test('URI#file, no path-is-uri check', () => { // we don't complain here - let value = URI.file('file://path/to/file'); + let value = URI.file('/file://path/to/file'); assert.equal(value.scheme, 'file'); assert.equal(value.authority, ''); assert.equal(value.path, '/file://path/to/file'); }); - test('URI#file, always slash', () => { - - var value = URI.file('a.file'); - assert.equal(value.scheme, 'file'); - assert.equal(value.authority, ''); - assert.equal(value.path, '/a.file'); - assert.equal(value.toString(), 'file:///a.file'); - - value = URI.parse(value.toString()); - assert.equal(value.scheme, 'file'); - assert.equal(value.authority, ''); - assert.equal(value.path, '/a.file'); - assert.equal(value.toString(), 'file:///a.file'); - }); - test('URI.toString, only scheme and query', () => { var value = URI.parse('stuff:?qüery'); assert.equal(value.toString(), 'stuff:?q%C3%BCery'); @@ -427,13 +410,36 @@ suite('URI', () => { assert.equal(uri.toString(true), 'https://twitter.com/search?src=typd&q=%23tag'); }); + test('class URI cannot represent relative file paths, #34449', function () { + const uri = URI.parse('file:./relative/path'); + assert.equal(uri.scheme, 'file'); + assert.equal(uri.authority, ''); + assert.equal(uri.path, './relative/path'); + + assert.equal(uri.toString(), 'file://./relative/path'); + + // this is asymetric to be spec-compliant + const uri2 = URI.parse(uri.toString()); + assert.equal(uri2.authority, '.'); + assert.equal(uri2.path, '/relative/path'); + }); + + test('class URI cannot represent relative file paths, #34449', function () { + + const path = '/foo/bar'; + assert.equal(URI.file(path).path, path); + + // no relative paths + assert.throws(() => URI.file('foo/bar')); + assert.throws(() => URI.file('./foo/bar')); + }); test('URI - (de)serialize', function () { var values = [ URI.parse('http://localhost:8080/far'), URI.file('c:\\test with %25\\c#code'), - URI.file('\\\\shäres\\path\\c#\\plugin.json'), + URI.file('//shäres/path/c#/plugin.json'), URI.parse('http://api/files/test.me?t=1234'), URI.parse('http://api/files/test.me?t=1234#fff'), URI.parse('http://api/files/test.me#fff'),