Prototype pollution in aurelia-path
Description
aurelia-path is part of the Aurelia platform and contains utilities for path manipulation. There is a prototype pollution vulnerability in aurelia-path before version 1.1.7. The vulnerability exposes Aurelia application that uses aurelia-path package to parse a string. The majority of this will be Aurelia applications that employ the aurelia-router package. An example is this could allow an attacker to change the prototype of base object class Object by tricking an application to parse the following URL: https://aurelia.io/blog/?__proto__[asdf]=asdf. The problem is patched in version 1.1.7.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
aurelia-pathnpm | < 1.1.7 | 1.1.7 |
Affected products
1Patches
17c4e235433a4fix: security issue gh closes #44
3 files changed · +351 −315
src/index.ts+7 −4 renamed@@ -158,7 +158,7 @@ function buildParam(key: string, value: any, traditional?: boolean): Array<strin * @param traditional Boolean Use the old URI template standard (RFC6570) * @returns The generated query string, excluding leading '?'. */ -export function buildQueryString(params: Object, traditional?: Boolean): string { +export function buildQueryString(params?: Object, traditional?: boolean): string { let pairs = []; let keys = Object.keys(params || {}).sort(); for (let i = 0, len = keys.length; i < len; i++) { @@ -203,16 +203,19 @@ function processScalarParam(existedParam: Object, value: Object): Object { * @param keys Collection of keys related to this parameter. * @param value Parameter value to append. */ -function parseComplexParam(queryParams: Object, keys: Object, value: any): void { +function parseComplexParam(queryParams: Object, keys: (string | number)[], value: any): void { let currentParams = queryParams; let keysLastIndex = keys.length - 1; for (let j = 0; j <= keysLastIndex; j++) { - let key = keys[j] === '' ? currentParams.length : keys[j]; + let key = keys[j] === '' ? (currentParams as any).length : keys[j]; + if (key === '__proto__') { + throw new Error('Prototype pollution detected.'); + } if (j < keysLastIndex) { // The value has to be an array or a false value // It can happen that the value is no array if the key was repeated with traditional style like `list=1&list[]=2` let prevValue = !currentParams[key] || typeof currentParams[key] === 'object' ? currentParams[key] : [currentParams[key]]; - currentParams = currentParams[key] = prevValue || (isNaN(keys[j + 1]) ? {} : []); + currentParams = currentParams[key] = prevValue || (isNaN(keys[j + 1] as number) ? {} : []); } else { currentParams = currentParams[key] = value; }
test/path.spec.js+0 −311 removed@@ -1,311 +0,0 @@ -import { relativeToFile, join, parseQueryString, buildQueryString } from '../src/index'; - -describe('relativeToFile', () => { - it('can make a dot path relative to a simple file', () => { - var file = 'some/file.html'; - var path = './other/module'; - - expect(relativeToFile(path, file)).toBe('some/other/module'); - }); - - it('can make a dot path relative to an absolute file', () => { - var file = 'http://durandal.io/some/file.html'; - var path = './other/module'; - - expect(relativeToFile(path, file)).toBe('http://durandal.io/some/other/module'); - }); - - it('can make a double dot path relative to an absolute file', () => { - var file = 'http://durandal.io/some/file.html'; - var path = '../other/module'; - - expect(relativeToFile(path, file)).toBe('http://durandal.io/other/module'); - }); - - it('returns path if null file provided', () => { - var file = null; - var path = 'module'; - - expect(relativeToFile(path, file)).toBe('module'); - }); - - it('returns path if empty file provided', () => { - var file = ''; - var path = 'module'; - - expect(relativeToFile(path, file)).toBe('module'); - }); -}); - -describe('join', () => { - it('can combine two simple paths', () => { - var path1 = 'one'; - var path2 = 'two'; - - expect(join(path1, path2)).toBe('one/two'); - }); - - it('can combine an absolute path and a simple path', () => { - var path1 = '/one'; - var path2 = 'two'; - - expect(join(path1, path2)).toBe('/one/two'); - }); - - it('can combine an absolute path and a simple path with slash', () => { - var path1 = '/one'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('/one/two'); - }); - - it('can combine a single slash and a simple path', () => { - var path1 = '/'; - var path2 = 'two'; - - expect(join(path1, path2)).toBe('/two'); - }); - - it('can combine a single slash and a simple path with slash', () => { - var path1 = '/'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('/two'); - }); - - it('can combine an absolute path with protocol and a simple path', () => { - var path1 = 'http://durandal.io'; - var path2 = 'two'; - - expect(join(path1, path2)).toBe('http://durandal.io/two'); - }); - - it('can combine an absolute path with protocol and a simple path with slash', () => { - var path1 = 'http://durandal.io'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('http://durandal.io/two'); - }); - - it('can combine an absolute path and a simple path with a dot', () => { - var path1 = 'http://durandal.io'; - var path2 = './two'; - - expect(join(path1, path2)).toBe('http://durandal.io/two'); - }); - - it('can combine a simple path and a relative path', () => { - var path1 = 'one'; - var path2 = '../two'; - - expect(join(path1, path2)).toBe('two'); - }); - - it('can combine an absolute path and a relative path', () => { - var path1 = 'http://durandal.io/somewhere'; - var path2 = '../two'; - - expect(join(path1, path2)).toBe('http://durandal.io/two'); - }); - - it('can combine a protocol independent path and a simple path', () => { - var path1 = '//durandal.io'; - var path2 = 'two'; - - expect(join(path1, path2)).toBe('//durandal.io/two'); - }); - - it('can combine a protocol independent path and a simple path with slash', () => { - var path1 = '//durandal.io'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('//durandal.io/two'); - }); - - it('can combine a protocol independent path and a simple path with a dot', () => { - var path1 = '//durandal.io'; - var path2 = './two'; - - expect(join(path1, path2)).toBe('//durandal.io/two'); - }); - - it('can combine a protocol independent path and a relative path', () => { - var path1 = '//durandal.io/somewhere'; - var path2 = '../two'; - - expect(join(path1, path2)).toBe('//durandal.io/two'); - }); - - it('can combine a complex path and a relative path', () => { - var path1 = 'one/three'; - var path2 = '../two'; - - expect(join(path1, path2)).toBe('one/two'); - }); - - it('returns path2 if path1 null', () => { - var path1 = null; - var path2 = 'two'; - - expect(join(path1, path2)).toBe('two'); - }); - - it('returns path2 if path1 empty', () => { - var path1 = ''; - var path2 = 'two'; - - expect(join(path1, path2)).toBe('two'); - }); - - it('returns path1 if path2 null', () => { - var path1 = 'one'; - var path2 = null; - - expect(join(path1, path2)).toBe('one'); - }); - - it('returns path1 if path2 empty', () => { - var path1 = 'one'; - var path2 = ''; - - expect(join(path1, path2)).toBe('one'); - }); - - it('should retain leading .. in path1', () => { - var path1 = '../one'; - var path2 = './two'; - - expect(join(path1, path2)).toBe('../one/two'); - }); - - it('should retain consecutive leading .. in path1', () => { - var path1 = '../../one'; - var path2 = './two'; - - expect(join(path1, path2)).toBe('../../one/two'); - }); - - it('should handle .. in path1 and path2', () => { - var path1 = '../../one'; - var path2 = '../two'; - - expect(join(path1, path2)).toBe('../../two'); - }); - - it('should merge .. in path1 and path2', () => { - var path1 = '../../one'; - var path2 = '../../two'; - - expect(join(path1, path2)).toBe('../../../two'); - }); - - it('should retain consecutive leading .. but not other .. in path1', () => { - var path1 = '../../one/../three'; - var path2 = './two'; - - expect(join(path1, path2)).toBe('../../three/two'); - }); - - it('should respect a trailing slash', () => { - var path1 = 'one/'; - var path2 = 'two/'; - - expect(join(path1, path2)).toBe('one/two/'); - }); - - it('should respect file:/// protocol with three slashes (empty host)', () => { - var path1 = 'file:///one'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('file:///one/two'); - }); - - it('should respect file:// protocol with two slashes (host given)', () => { - var path1 = 'file://localhost:8080'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('file://localhost:8080/two'); - }); - - it('should allow scheme-relative URL that uses colons in the path', () => { - var path1 = '//localhost/one:/'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('//localhost/one:/two'); - }); - - it('should not add more than two leading slashes to http:// protocol', () => { - var path1 = 'http:///'; - var path2 = '/two'; - - expect(join(path1, path2)).toBe('http://two'); - }); -}); - -describe('query strings', () => { - it('should build query strings', () => { - let gen = buildQueryString; - - expect(gen()).toBe(''); - expect(gen(null)).toBe(''); - expect(gen({})).toBe(''); - expect(gen({ a: null })).toBe(''); - - expect(gen({ '': 'a' })).toBe('=a'); - expect(gen({ a: 'b' })).toBe('a=b'); - expect(gen({ a: 'b', c: 'd' })).toBe('a=b&c=d'); - expect(gen({ a: 'b', c: 'd' }, true)).toBe('a=b&c=d'); - expect(gen({ a: 'b', c: null })).toBe('a=b'); - expect(gen({ a: 'b', c: null }, true)).toBe('a=b'); - - expect(gen({ a: ['b', 'c'] })).toBe('a%5B%5D=b&a%5B%5D=c'); - expect(gen({ a: ['b', 'c'] }, true)).toBe('a=b&a=c'); - expect(gen({ '&': ['b', 'c'] })).toBe('%26%5B%5D=b&%26%5B%5D=c'); - expect(gen({ '&': ['b', 'c'] }, true)).toBe('%26=b&%26=c'); - - expect(gen({ a: '&' })).toBe('a=%26'); - expect(gen({ '&': 'a' })).toBe('%26=a'); - expect(gen({ a: true })).toBe('a=true'); - expect(gen({ '$test': true })).toBe('$test=true'); - - expect(gen({ obj: { a: 5, b: "str", c: false } })).toBe('obj%5Ba%5D=5&obj%5Bb%5D=str&obj%5Bc%5D=false'); - expect(gen({ obj: { a: 5, b: "str", c: false } }, true)).toBe('obj=%5Bobject%20Object%5D'); - expect(gen({ obj:{ a: 5, b: undefined}})).toBe('obj%5Ba%5D=5'); - - expect(gen({a: {b: ['c','d', ['f', 'g']]}})).toBe('a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d&a%5Bb%5D%5B2%5D%5B%5D=f&a%5Bb%5D%5B2%5D%5B%5D=g'); - expect(gen({a: {b: ['c','d', ['f', 'g']]}}, true)).toBe('a=%5Bobject%20Object%5D'); - expect(gen({a: ['c','d', ['f', 'g']]}, true)).toBe('a=c&a=d&a=f%2Cg'); - expect(gen({a: ['c','d', {f: 'g'}]}, true)).toBe('a=c&a=d&a=%5Bobject%20Object%5D'); - }); - - it('should parse query strings', () => { - let parse = parseQueryString; - - expect(parse('')).toEqual({}); - expect(parse('=')).toEqual({}); - expect(parse('&')).toEqual({}); - expect(parse('?')).toEqual({}); - - expect(parse('a')).toEqual({ a: true }); - expect(parse('a&b')).toEqual({ a: true, b: true }); - expect(parse('a=')).toEqual({ a: '' }); - expect(parse('a=&b=')).toEqual({ a: '', b: '' }); - - expect(parse('a=b')).toEqual({ a: 'b' }); - expect(parse('a=b&c=d')).toEqual({ a: 'b', c: 'd' }); - expect(parse('a=b&&c=d')).toEqual({ a: 'b', c: 'd' }); - expect(parse('a=b&a=c')).toEqual({ a: ['b', 'c'] }); - - expect(parse('a=b&c=d=')).toEqual({ a: 'b', c: 'd' }); - expect(parse('a=b&c=d==')).toEqual({ a: 'b', c: 'd' }); - - expect(parse('a=%26')).toEqual({ a: '&' }); - expect(parse('%26=a')).toEqual({ '&': 'a' }); - expect(parse('%26[]=b&%26[]=c')).toEqual({ '&': ['b', 'c'] }); - - expect(parse('a[b]=c&a[d]=e')).toEqual({a: {b: 'c', d: 'e'}}); - expect(parse('a[b][c][d]=e')).toEqual({a: {b: {c: {d: 'e'}}}}); - expect(parse('a[b][]=c&a[b][]=d&a[b][2][]=f&a[b][2][]=g')).toEqual({a: {b: ['c','d', ['f', 'g']]}}); - expect(parse('a[0]=b')).toEqual({a: ['b']}); - }); -});
test/path.spec.ts+344 −0 added@@ -0,0 +1,344 @@ +import { relativeToFile, join, parseQueryString, buildQueryString } from '../src/index'; +import assert from 'assert'; + +const expect = (input: any) => { + return { + toBe: (output: any) => { + assert.strictEqual(input, output); + }, + toEqual: (output: any) => { + assert.deepStrictEqual(input, output) + } + } +} + +describe('relativeToFile', () => { + it('can make a dot path relative to a simple file', () => { + const file = 'some/file.html'; + const path = './other/module'; + + expect(relativeToFile(path, file)).toBe('some/other/module'); + }); + + it('can make a dot path relative to an absolute file', () => { + const file = 'http://durandal.io/some/file.html'; + const path = './other/module'; + + expect(relativeToFile(path, file)).toBe('http://durandal.io/some/other/module'); + }); + + it('can make a double dot path relative to an absolute file', () => { + const file = 'http://durandal.io/some/file.html'; + const path = '../other/module'; + + expect(relativeToFile(path, file)).toBe('http://durandal.io/other/module'); + }); + + it('returns path if null file provided', () => { + const file = null; + const path = 'module'; + + expect(relativeToFile(path, file)).toBe('module'); + }); + + it('returns path if empty file provided', () => { + const file = ''; + const path = 'module'; + + expect(relativeToFile(path, file)).toBe('module'); + }); +}); + +describe('join', () => { + it('can combine two simple paths', () => { + const path1 = 'one'; + const path2 = 'two'; + + expect(join(path1, path2)).toBe('one/two'); + }); + + it('can combine an absolute path and a simple path', () => { + const path1 = '/one'; + const path2 = 'two'; + + expect(join(path1, path2)).toBe('/one/two'); + }); + + it('can combine an absolute path and a simple path with slash', () => { + const path1 = '/one'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('/one/two'); + }); + + it('can combine a single slash and a simple path', () => { + const path1 = '/'; + const path2 = 'two'; + + expect(join(path1, path2)).toBe('/two'); + }); + + it('can combine a single slash and a simple path with slash', () => { + const path1 = '/'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('/two'); + }); + + it('can combine an absolute path with protocol and a simple path', () => { + const path1 = 'http://durandal.io'; + const path2 = 'two'; + + expect(join(path1, path2)).toBe('http://durandal.io/two'); + }); + + it('can combine an absolute path with protocol and a simple path with slash', () => { + const path1 = 'http://durandal.io'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('http://durandal.io/two'); + }); + + it('can combine an absolute path and a simple path with a dot', () => { + const path1 = 'http://durandal.io'; + const path2 = './two'; + + expect(join(path1, path2)).toBe('http://durandal.io/two'); + }); + + it('can combine a simple path and a relative path', () => { + const path1 = 'one'; + const path2 = '../two'; + + expect(join(path1, path2)).toBe('two'); + }); + + it('can combine an absolute path and a relative path', () => { + const path1 = 'http://durandal.io/somewhere'; + const path2 = '../two'; + + expect(join(path1, path2)).toBe('http://durandal.io/two'); + }); + + it('can combine a protocol independent path and a simple path', () => { + const path1 = '//durandal.io'; + const path2 = 'two'; + + expect(join(path1, path2)).toBe('//durandal.io/two'); + }); + + it('can combine a protocol independent path and a simple path with slash', () => { + const path1 = '//durandal.io'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('//durandal.io/two'); + }); + + it('can combine a protocol independent path and a simple path with a dot', () => { + const path1 = '//durandal.io'; + const path2 = './two'; + + expect(join(path1, path2)).toBe('//durandal.io/two'); + }); + + it('can combine a protocol independent path and a relative path', () => { + const path1 = '//durandal.io/somewhere'; + const path2 = '../two'; + + expect(join(path1, path2)).toBe('//durandal.io/two'); + }); + + it('can combine a complex path and a relative path', () => { + const path1 = 'one/three'; + const path2 = '../two'; + + expect(join(path1, path2)).toBe('one/two'); + }); + + it('returns path2 if path1 null', () => { + const path1 = null; + const path2 = 'two'; + + expect(join(path1, path2)).toBe('two'); + }); + + it('returns path2 if path1 empty', () => { + const path1 = ''; + const path2 = 'two'; + + expect(join(path1, path2)).toBe('two'); + }); + + it('returns path1 if path2 null', () => { + const path1 = 'one'; + const path2 = null; + + expect(join(path1, path2)).toBe('one'); + }); + + it('returns path1 if path2 empty', () => { + const path1 = 'one'; + const path2 = ''; + + expect(join(path1, path2)).toBe('one'); + }); + + it('should retain leading .. in path1', () => { + const path1 = '../one'; + const path2 = './two'; + + expect(join(path1, path2)).toBe('../one/two'); + }); + + it('should retain consecutive leading .. in path1', () => { + const path1 = '../../one'; + const path2 = './two'; + + expect(join(path1, path2)).toBe('../../one/two'); + }); + + it('should handle .. in path1 and path2', () => { + const path1 = '../../one'; + const path2 = '../two'; + + expect(join(path1, path2)).toBe('../../two'); + }); + + it('should merge .. in path1 and path2', () => { + const path1 = '../../one'; + const path2 = '../../two'; + + expect(join(path1, path2)).toBe('../../../two'); + }); + + it('should retain consecutive leading .. but not other .. in path1', () => { + const path1 = '../../one/../three'; + const path2 = './two'; + + expect(join(path1, path2)).toBe('../../three/two'); + }); + + it('should respect a trailing slash', () => { + const path1 = 'one/'; + const path2 = 'two/'; + + expect(join(path1, path2)).toBe('one/two/'); + }); + + it('should respect file:/// protocol with three slashes (empty host)', () => { + const path1 = 'file:///one'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('file:///one/two'); + }); + + it('should respect file:// protocol with two slashes (host given)', () => { + const path1 = 'file://localhost:8080'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('file://localhost:8080/two'); + }); + + it('should allow scheme-relative URL that uses colons in the path', () => { + const path1 = '//localhost/one:/'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('//localhost/one:/two'); + }); + + it('should not add more than two leading slashes to http:// protocol', () => { + const path1 = 'http:///'; + const path2 = '/two'; + + expect(join(path1, path2)).toBe('http://two'); + }); +}); + +describe('query strings', () => { + interface IBuildTestCase { + input: any; + traditional?: boolean; + output: string; + } + const testCases: IBuildTestCase[] = [ + { input: undefined, output: '' }, + { input: null, output: '' }, + { input: {}, output: '' }, + { input: { a: null }, output: '' }, + { input: { '': 'a' }, output: '=a' }, + { input: { a: 'b' }, output: 'a=b' }, + { input: { a: 'b', c: 'd' }, output: 'a=b&c=d' }, + { input: { a: 'b', c: 'd' }, traditional: true, output: 'a=b&c=d' }, + { input: { a: 'b', c: null }, output: 'a=b' }, + { input: { a: 'b', c: null }, traditional: true, output: 'a=b' }, + { input: { a: ['b', 'c'] }, output: 'a%5B%5D=b&a%5B%5D=c' }, + { input: { a: ['b', 'c'] }, traditional: true, output: 'a=b&a=c' }, + { input: { '&': ['b', 'c'] }, output: '%26%5B%5D=b&%26%5B%5D=c' }, + { input: { '&': ['b', 'c'] }, traditional: true, output: '%26=b&%26=c' }, + + { input: { a: '&' }, output: 'a=%26' }, + { input: { '&': 'a' }, output: '%26=a' }, + { input: { a: true }, output: 'a=true' }, + { input: { '$test': true }, output: '$test=true' }, + + { input: { obj: { a: 5, b: "str", c: false } }, output: 'obj%5Ba%5D=5&obj%5Bb%5D=str&obj%5Bc%5D=false' }, + { input: { obj: { a: 5, b: "str", c: false } }, traditional: true, output: 'obj=%5Bobject%20Object%5D' }, + { input: { obj: { a: 5, b: undefined } }, output: 'obj%5Ba%5D=5' }, + + { input: { a: { b: ['c', 'd', ['f', 'g']] } }, output: 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d&a%5Bb%5D%5B2%5D%5B%5D=f&a%5Bb%5D%5B2%5D%5B%5D=g' }, + { input: { a: { b: ['c', 'd', ['f', 'g']] } }, traditional: true, output: 'a=%5Bobject%20Object%5D' }, + { input: { a: ['c', 'd', ['f', 'g']] }, traditional: true, output: 'a=c&a=d&a=f%2Cg' }, + { input: { a: ['c', 'd', { f: 'g' }] }, traditional: true, output: 'a=c&a=d&a=%5Bobject%20Object%5D' }, + ]; + + testCases.forEach(({ input, output, traditional }) => { + it(`builds ${input instanceof Object ? JSON.stringify(input) : input} to "${output}"`, () => { + expect(buildQueryString(input, traditional)).toBe(output); + }); + }); + + interface IParseTestCase { + input: string; + output: any; + } + + const parseTestCases: IParseTestCase[] = [ + { input: '', output: {} }, + { input: '=', output: {} }, + { input: '&', output: {} }, + { input: '?', output: {} }, + + { input: 'a', output: { a: true } }, + { input: 'a&b', output: { a: true, b: true } }, + { input: 'a=', output: { a: '' } }, + { input: 'a=&b=', output: { a: '', b: '' } }, + + { input: 'a=b', output: { a: 'b' } }, + { input: 'a=b&c=d', output: { a: 'b', c: 'd' } }, + { input: 'a=b&&c=d', output: { a: 'b', c: 'd' } }, + { input: 'a=b&a=c', output: { a: ['b', 'c'] } }, + + { input: 'a=b&c=d=', output: { a: 'b', c: 'd' } }, + { input: 'a=b&c=d==', output: { a: 'b', c: 'd' } }, + + { input: 'a=%26', output: { a: '&' } }, + { input: '%26=a', output: { '&': 'a' } }, + { input: '%26[]=b&%26[]=c', output: { '&': ['b', 'c'] } }, + + { input: 'a[b]=c&a[d]=e', output: { a: { b: 'c', d: 'e' } } }, + { input: 'a[b][c][d]=e', output: { a: { b: { c: { d: 'e' } } } } }, + { input: 'a[b][]=c&a[b][]=d&a[b][2][]=f&a[b][2][]=g', output: { a: { b: ['c', 'd', ['f', 'g']] } } }, + { input: 'a[0]=b', output: { a: ['b'] } }, + ]; + + parseTestCases.forEach(({ input, output }) => { + it(`parses ${input} to "${JSON.stringify(output)}"`, () => { + expect(parseQueryString(input)).toEqual(output); + }); + }); + + it('does not pollute prototype', () => { + const path1 = '__proto__[asdf]=asdf'; + assert.throws(() => parseQueryString(path1), 'Prototype pollution detected'); + }); +});
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-3c9c-2p65-qvwvghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-41097ghsaADVISORY
- github.com/aurelia/path/commit/7c4e235433a4a2df9acc313fbe891758084fdec1ghsax_refsource_MISCWEB
- github.com/aurelia/path/issues/44ghsax_refsource_MISCWEB
- github.com/aurelia/path/releases/tag/1.1.7ghsax_refsource_MISCWEB
- github.com/aurelia/path/security/advisories/GHSA-3c9c-2p65-qvwvghsax_refsource_CONFIRMWEB
- www.npmjs.com/package/aurelia-pathghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.