diff --git a/packages/grpc-js/src/make-client.ts b/packages/grpc-js/src/make-client.ts index 05f06b89..a6cb9100 100644 --- a/packages/grpc-js/src/make-client.ts +++ b/packages/grpc-js/src/make-client.ts @@ -93,6 +93,15 @@ export interface ServiceClientConstructor { service: ServiceDefinition; } +/** + * Returns true, if given key is included in the blacklisted + * keys. + * @param key key for check, string. + */ +function isPrototypePolluted(key: string): Boolean { + return ['__proto__', 'prototype', 'constructor'].includes(key); +} + /** * Creates a constructor for a client with the given methods, as specified in * the methods argument. The resulting class will have an instance method for @@ -122,7 +131,7 @@ export function makeClientConstructor( } Object.keys(methods).forEach((name) => { - if (name === '__proto__') { + if (isPrototypePolluted(name)) { return; } const attrs = methods[name]; @@ -155,7 +164,7 @@ export function makeClientConstructor( ServiceClientImpl.prototype[name] = methodFunc; // Associate all provided attributes with the method Object.assign(ServiceClientImpl.prototype[name], attrs); - if (attrs.originalName && attrs.originalName !== '__proto__') { + if (attrs.originalName && !isPrototypePolluted(attrs.originalName)) { ServiceClientImpl.prototype[attrs.originalName] = ServiceClientImpl.prototype[name]; } @@ -204,7 +213,7 @@ export function loadPackageDefinition( if (Object.prototype.hasOwnProperty.call(packageDef, serviceFqn)) { const service = packageDef[serviceFqn]; const nameComponents = serviceFqn.split('.'); - if (nameComponents.some(comp => comp === '__proto__')) { + if (nameComponents.some((comp: string) => isPrototypePolluted(comp))) { continue; } const serviceName = nameComponents[nameComponents.length - 1]; diff --git a/packages/grpc-js/test/test-prototype-pollution.ts b/packages/grpc-js/test/test-prototype-pollution.ts index 12092608..6dc4b293 100644 --- a/packages/grpc-js/test/test-prototype-pollution.ts +++ b/packages/grpc-js/test/test-prototype-pollution.ts @@ -24,4 +24,8 @@ describe('loadPackageDefinition', () => { loadPackageDefinition({'__proto__.polluted': true} as any); assert.notStrictEqual(({} as any).polluted, true); }); + it('Should not allow prototype pollution #2', () => { + loadPackageDefinition({'constructor.prototype.polluted': true} as any); + assert.notStrictEqual(({} as any).polluted, true); + }); });