packageurl-js
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -0,1 +1,5 @@ | ||
# 1.0.1 | ||
### Bug Fixes | ||
* Hardened encoding/decoding of URL special chars like `@` and `#` [#37](https://github.com/package-url/packageurl-js/pull/37) | ||
# 1.0.0 | ||
@@ -2,0 +6,0 @@ ### Features |
{ | ||
"name": "packageurl-js", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "JavaScript library to parse and build \"purl\" aka. package URLs. This is a microlibrary implementing the purl spec at https://github.com/package-url", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -79,3 +79,3 @@ /*! | ||
toString() { | ||
var purl = ['pkg:', this.type, '/']; | ||
var purl = ['pkg:', encodeURIComponent(this.type), '/']; | ||
@@ -95,7 +95,7 @@ if (this.type === 'pypi') { | ||
purl.push(encodeURIComponent(this.name).replace('%3A', ':')); | ||
purl.push(encodeURIComponent(this.name).replace(/%3A/g, ':')); | ||
if (this.version) { | ||
purl.push('@'); | ||
purl.push(encodeURIComponent(this.version).replace('%3A', ':')); | ||
purl.push(encodeURIComponent(this.version).replace(/%3A/g, ':')); | ||
} | ||
@@ -109,3 +109,7 @@ | ||
Object.keys(qualifiers).sort().forEach(key => { | ||
qualifierString.push(encodeURIComponent(key).replace('%3A', ':') + '=' + encodeURI(qualifiers[key])); | ||
qualifierString.push( | ||
encodeURIComponent(key).replace(/%3A/g, ':') | ||
+ '=' | ||
+ encodeURIComponent(qualifiers[key]).replace(/%2F/g, '/') | ||
); | ||
}); | ||
@@ -118,3 +122,5 @@ | ||
purl.push('#'); | ||
purl.push(encodeURI(this.subpath)); | ||
purl.push(encodeURIComponent(this.subpath) | ||
.replace(/%3A/g, ':') | ||
.replace(/%2F/g, '/')); | ||
} | ||
@@ -126,3 +132,3 @@ | ||
static fromString(purl) { | ||
if (!purl || !typeof purl === 'string' || !purl.trim()) { | ||
if (!purl || typeof purl !== 'string' || !purl.trim()) { | ||
throw new Error('A purl string argument is required.'); | ||
@@ -144,2 +150,3 @@ } | ||
} | ||
type = decodeURIComponent(type) | ||
@@ -159,5 +166,5 @@ let url = new URL(purl); | ||
} | ||
if (subpath.length === 0) { | ||
subpath = null; | ||
} | ||
subpath = subpath.length === 0 | ||
? null | ||
: decodeURIComponent(subpath) | ||
@@ -164,0 +171,0 @@ if (url.username !== '' || url.password !== '') { |
@@ -24,2 +24,4 @@ /*! | ||
const assert = require('assert'); | ||
const {describe, it} = require("mocha"); | ||
const TEST_FILE = require('./data/test-suite-data.json'); | ||
@@ -31,2 +33,21 @@ | ||
describe('PackageURL', function () { | ||
describe('toString()', function () { | ||
it('all components encode #', function () { | ||
/* The # is a delimiter between url and subpath. */ | ||
var purl = new PackageURL('ty#pe', 'name#space', 'na#me', 'ver#sion', {'foo':'bar#baz'}, 'sub#path'); | ||
assert.strictEqual(purl.toString(), 'pkg:ty%23pe/name%23space/na%23me@ver%23sion?foo=bar%23baz#sub%23path') | ||
}) | ||
it('all components encode @', function () { | ||
/* The @ is a delimiter between package name and version. */ | ||
var purl = new PackageURL('ty@pe', 'name@space', 'na@me', 'ver@sion', {'foo':'bar@baz'}, 'sub@path'); | ||
assert.strictEqual(purl.toString(), 'pkg:ty%40pe/name%40space/na%40me@ver%40sion?foo=bar%40baz#sub%40path') | ||
}) | ||
it('path components encode /', function () { | ||
/* only namespace is allowed to have multiple segments separated by `/`` */ | ||
var purl = new PackageURL('ty/pe', 'namespace1/namespace2', 'na/me'); | ||
assert.strictEqual(purl.toString(), 'pkg:ty%2Fpe/namespace1/namespace2/na%2Fme') | ||
}) | ||
}) | ||
describe('fromString()', function () { | ||
@@ -60,2 +81,29 @@ it('with qualifiers.checksums', function () { | ||
}); | ||
it('namespace with multiple segments', function () { | ||
var purl = PackageURL.fromString('pkg:ty%2Fpe/namespace1/namespace2/na%2Fme') | ||
assert.strictEqual('ty/pe', purl.type) | ||
assert.strictEqual('namespace1/namespace2', purl.namespace) | ||
assert.strictEqual('na/me', purl.name) | ||
}) | ||
it('encoded #', function () { | ||
var purl = PackageURL.fromString('pkg:ty%23pe/name%23space/na%23me@ver%23sion?foo=bar%23baz#sub%23path') | ||
assert.strictEqual('ty#pe', purl.type) | ||
assert.strictEqual('name#space', purl.namespace) | ||
assert.strictEqual('na#me', purl.name) | ||
assert.strictEqual('ver#sion', purl.version) | ||
assert.deepStrictEqual({foo:'bar#baz'}, purl.qualifiers) | ||
assert.strictEqual('sub#path', purl.subpath) | ||
}) | ||
it('encoded @', function () { | ||
var purl = PackageURL.fromString('pkg:ty%40pe/name%40space/na%40me@ver%40sion?foo=bar%40baz#sub%40path') | ||
assert.strictEqual('ty@pe', purl.type) | ||
assert.strictEqual('name@space', purl.namespace) | ||
assert.strictEqual('na@me', purl.name) | ||
assert.strictEqual('ver@sion', purl.version) | ||
assert.deepStrictEqual({foo:'bar@baz'}, purl.qualifiers) | ||
assert.strictEqual('sub@path', purl.subpath) | ||
}) | ||
}); | ||
@@ -71,3 +119,3 @@ | ||
} catch (e) { | ||
assert.equal(true, e.toString().includes('is a required field') || e.toString().includes('Invalid purl')); | ||
assert.ok(e.toString().includes('is a required field') || e.toString().includes('Invalid purl')); | ||
} | ||
@@ -79,3 +127,3 @@ }); | ||
} catch (e) { | ||
assert.equal(true, e.toString().includes('Error: purl is missing the required') || e.toString().includes('Invalid purl')); | ||
assert.ok(e.toString().includes('Error: purl is missing the required') || e.toString().includes('Invalid purl')); | ||
} | ||
@@ -86,22 +134,22 @@ }); | ||
var purl = new PackageURL(obj.type, obj.namespace, obj.name, obj.version, obj.qualifiers, obj.subpath); | ||
assert.equal(obj.type, purl.type); | ||
assert.equal(obj.name, purl.name); | ||
assert.equal(obj.namespace, purl.namespace); | ||
assert.equal(obj.version, purl.version); | ||
assert.equal(JSON.stringify(obj.qualifiers), JSON.stringify(purl.qualifiers)); | ||
assert.equal(obj.subpath, purl.subpath); | ||
assert.strictEqual(purl.type, obj.type); | ||
assert.strictEqual(purl.name, obj.name); | ||
assert.strictEqual(purl.namespace, obj.namespace); | ||
assert.strictEqual(purl.version, obj.version); | ||
assert.deepStrictEqual(purl.qualifiers, obj.qualifiers); | ||
assert.strictEqual(purl.subpath, obj.subpath, ); | ||
}); | ||
it('should be able to convert valid PackageURLs to a string', function () { | ||
var purl = new PackageURL(obj.type, obj.namespace, obj.name, obj.version, obj.qualifiers, obj.subpath); | ||
assert.equal(obj.canonical_purl, purl.toString()); | ||
assert.strictEqual(purl.toString(), obj.canonical_purl); | ||
}); | ||
it('should be able to parse valid PackageURLs', function () { | ||
var purl = PackageURL.fromString(obj.canonical_purl); | ||
assert.equal(purl.toString(), obj.canonical_purl); | ||
assert.equal(purl.type, obj.type); | ||
assert.equal(purl.name, obj.name); | ||
assert.equal(purl.namespace, obj.namespace); | ||
assert.equal(purl.version, obj.version); | ||
assert.equal(JSON.stringify(purl.qualifiers), JSON.stringify(obj.qualifiers)); | ||
assert.equal(purl.subpath, obj.subpath); | ||
assert.strictEqual(purl.toString(), obj.canonical_purl); | ||
assert.strictEqual(purl.type, obj.type); | ||
assert.strictEqual(purl.name, obj.name); | ||
assert.strictEqual(purl.namespace, obj.namespace); | ||
assert.strictEqual(purl.version, obj.version); | ||
assert.deepStrictEqual(purl.qualifiers, obj.qualifiers); | ||
assert.strictEqual(purl.subpath, obj.subpath); | ||
}); | ||
@@ -108,0 +156,0 @@ it('should handle pypi package-urls per the purl-spec', function () { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38814
829