Skip to content

Commit

Permalink
Fixed vulnerability reported by Snyk
Browse files Browse the repository at this point in the history
  • Loading branch information
Phillip Clark committed Oct 26, 2021
1 parent bdca625 commit 5dc458f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/__tests__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './pointer.spec';
export * from './ptr.spec';
export * from './reference.spec';
export * from './utils.spec';
66 changes: 66 additions & 0 deletions src/__tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ describe('Utils', () => {
});
});

interface Prototyped {
__proto__?: { polluted: string };
constructor?: { polluted: string };
prototype?: { polluted: string };
}

describe('setValueAtPath()', () => {
it('throws when target undefined', () => {
expect(() => setValueAtPath(undefined, 0, ['foo'])).to.throw(
Expand All @@ -181,6 +187,42 @@ describe('Utils', () => {
expect(setValueAtPath(data, 'VV', ['one', 5], true)).to.be.undefined;
expect(data.one[5]).to.eql('VV');
});

it('will prevent __proto__ from being polluted', () => {
expect(() => {
setValueAtPath({}, 'yes', ['__proto__', 'polluted'], true);
}).to.throw('Attempted prototype pollution disallowed.');
const prototyped = {} as unknown as Prototyped;
expect(prototyped.__proto__?.polluted).to.not.eql('yes');
});
it('will prevent .constructor from being polluted', () => {
expect(() => {
setValueAtPath({}, 'yes', ['constructor', 'polluted'], true);
}).to.throw('Attempted prototype pollution disallowed.');
const prototyped = {} as unknown as Prototyped;
expect(prototyped.constructor?.polluted).to.not.eql('yes');
});
it('will prevent .prototype from being polluted', () => {
expect(() => {
setValueAtPath({}, 'yes', ['prototype', 'polluted'], true);
}).to.throw('Attempted prototype pollution disallowed.');
const prototyped = {} as unknown as Prototyped;
expect(prototyped.prototype?.polluted).to.not.eql('yes');
});
it('will prevent __proto__ from being polluted by javascript', () => {
expect(() => {
setValueAtPath(
{},
'yes',
// not allowed in TS depending on tsconfig, but hackable in JS:
[['__proto__'], 'polluted'] as unknown as string[],
true,
);
const prototyped = {} as unknown as Prototyped;
expect(prototyped.__proto__?.polluted).to.not.eql('yes');
expect(prototyped.__proto__).to.be.undefined;
}).to.throw('PathSegments must be a string or a number.');
});
});

describe('unsetValueAtPath()', () => {
Expand All @@ -206,5 +248,29 @@ describe('Utils', () => {
expected,
);
});
it('will prevent __proto__ from being polluted', () => {
expect(() => {
unsetValueAtPath({}, ['__proto__', 'polluted']);
}).to.throw('Attempted prototype pollution disallowed.');
});
it('will prevent .constructor from being polluted', () => {
expect(() => {
unsetValueAtPath({}, ['constructor', 'polluted']);
}).to.throw('Attempted prototype pollution disallowed.');
});
it('will prevent .prototype from being polluted', () => {
expect(() => {
unsetValueAtPath({}, ['prototype', 'polluted']);
}).to.throw('Attempted prototype pollution disallowed.');
});
it('will prevent __proto__ from being polluted by javascript', () => {
expect(() => {
unsetValueAtPath(
{},
// not allowed in TS depending on tsconfig, but hackable in JS:
[['__proto__'], 'polluted'] as unknown as string[],
);
}).to.throw('PathSegments must be a string or a number.');
});
});
});
13 changes: 13 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ export function setValueAtPath(
let p: number;
while (++cursor < len) {
step = path[cursor];
if (typeof step !== 'string' && typeof step !== 'number') {
throw new TypeError('PathSegments must be a string or a number.');
}
if (
step === '__proto__' ||
step === 'constructor' ||
Expand Down Expand Up @@ -318,6 +321,16 @@ export function unsetValueAtPath(target: unknown, path: PathSegments): unknown {
let p: number;
while (++cursor < len) {
step = path[cursor];
if (typeof step !== 'string' && typeof step !== 'number') {
throw new TypeError('PathSegments must be a string or a number.');
}
if (
step === '__proto__' ||
step === 'constructor' ||
step === 'prototype'
) {
throw new Error('Attempted prototype pollution disallowed.');
}
if (Array.isArray(it)) {
p = toArrayIndexReference(it, step);
if (p >= it.length) return undefined;
Expand Down

0 comments on commit 5dc458f

Please sign in to comment.