Skip to content

Commit ead2606

Browse files
committed
fix: prevent prototype pollution in a number of locations
Prototype pollution is not a realistic attack on CDK, since values never come from untrusted sources. Nevertheless, the presence of possible prototype pollution sites makes for a fertile ground for security reports that we then have to look into. Address these, in one of 3 ways: - If we control the destination of the assignment, use `Object.create(null)` to a create an object literal that is not vulnerable to prototype pollution. - In an assignment where we don't control the destination object, use `assertNoProto` to ensure the key is not `__proto__`. - In a recursive merge function where we don't control the destination object, assert that the key is not one of `__proto__`, `constructor` or `prototype`.
1 parent d12754f commit ead2606

File tree

39 files changed

+100
-65
lines changed

39 files changed

+100
-65
lines changed

packages/@aws-cdk/aws-amplify-alpha/lib/app.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type * as codebuild from 'aws-cdk-lib/aws-codebuild';
33
import * as iam from 'aws-cdk-lib/aws-iam';
44
import type { IResource, SecretValue } from 'aws-cdk-lib/core';
55
import { Lazy, Resource, ValidationError } from 'aws-cdk-lib/core';
6+
import { assertNoProto } from 'aws-cdk-lib/core/lib/helpers-internal';
67
import { addConstructMetadata, MethodMetadata } from 'aws-cdk-lib/core/lib/metadata-resource';
78
import { propertyInjectable } from 'aws-cdk-lib/core/lib/prop-injectable';
89
import type { Construct, IConstruct } from 'constructs';
@@ -349,6 +350,7 @@ export class App extends Resource implements IApp, iam.IGrantable {
349350
*/
350351
@MethodMetadata()
351352
public addEnvironment(name: string, value: string) {
353+
assertNoProto(name);
352354
this.environmentVariables[name] = value;
353355
return this;
354356
}
@@ -361,6 +363,7 @@ export class App extends Resource implements IApp, iam.IGrantable {
361363
*/
362364
@MethodMetadata()
363365
public addAutoBranchEnvironment(name: string, value: string) {
366+
assertNoProto(name);
364367
this.autoBranchEnvironmentVariables[name] = value;
365368
return this;
366369
}

packages/@aws-cdk/aws-amplify-alpha/lib/branch.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
Stack,
1313
ValidationError,
1414
} from 'aws-cdk-lib/core';
15+
import { assertNoProto } from 'aws-cdk-lib/core/lib/helpers-internal';
1516
import { addConstructMetadata, MethodMetadata } from 'aws-cdk-lib/core/lib/metadata-resource';
1617
import { propertyInjectable } from 'aws-cdk-lib/core/lib/prop-injectable';
1718
import { Provider } from 'aws-cdk-lib/custom-resources';
@@ -248,6 +249,7 @@ export class Branch extends Resource implements IBranch {
248249
*/
249250
@MethodMetadata()
250251
public addEnvironment(name: string, value: string) {
252+
assertNoProto(name);
251253
this.environmentVariables[name] = value;
252254
return this;
253255
}

packages/@aws-cdk/aws-glue-alpha/lib/connection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type * as ec2 from 'aws-cdk-lib/aws-ec2';
22
import { CfnConnection } from 'aws-cdk-lib/aws-glue';
33
import * as cdk from 'aws-cdk-lib/core';
4-
import { memoizedGetter } from 'aws-cdk-lib/core/lib/helpers-internal';
4+
import { assertNoProto, memoizedGetter } from 'aws-cdk-lib/core/lib/helpers-internal';
55
import { addConstructMetadata, MethodMetadata } from 'aws-cdk-lib/core/lib/metadata-resource';
66
import { propertyInjectable } from 'aws-cdk-lib/core/lib/prop-injectable';
77
import type * as constructs from 'constructs';
@@ -406,6 +406,7 @@ export class Connection extends cdk.Resource implements IConnection {
406406
*/
407407
@MethodMetadata()
408408
public addProperty(key: string, value: string): void {
409+
assertNoProto(key);
409410
this.properties[key] = value;
410411
}
411412
}

packages/@aws-cdk/aws-redshift-alpha/lib/parameter-group.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CfnClusterParameterGroup } from 'aws-cdk-lib/aws-redshift';
22
import type { IResource } from 'aws-cdk-lib/core';
33
import { Resource, ValidationError } from 'aws-cdk-lib/core';
44
import { addConstructMetadata, MethodMetadata } from 'aws-cdk-lib/core/lib/metadata-resource';
5+
import { assertNoProto } from 'aws-cdk-lib/core/lib/private/prototype-pollution';
56
import { propertyInjectable } from 'aws-cdk-lib/core/lib/prop-injectable';
67
import type { Construct } from 'constructs';
78

@@ -106,6 +107,7 @@ export class ClusterParameterGroup extends ClusterParameterGroupBase {
106107
*/
107108
@MethodMetadata()
108109
public addParameter(name: string, value: string): void {
110+
assertNoProto(name);
109111
const existingValue = Object.entries(this.parameters).find(([key, _]) => key === name)?.[1];
110112
if (existingValue === undefined) {
111113
this.parameters[name] = value;

packages/@aws-cdk/custom-resource-handlers/lib/custom-resources/aws-custom-resource-handler/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ export function decodeSpecialValues(object: object, physicalResourceId: string):
2525
}
2626
if (x && typeof x === 'object') {
2727
for (const [key, value] of Object.entries(x)) {
28+
if (['__proto__', 'prototype', 'constructor'].includes(key)) {
29+
throw new Error(`Unsafe key: ${key}`);
30+
}
2831
x[key] = recurse(value);
2932
}
3033
return x;

packages/@aws-cdk/integ-tests-alpha/lib/assertions/providers/lambda-handler/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { assertNoProtoRec } from 'aws-cdk-lib/core/lib/helpers-internal';
2+
13
/**
24
* Recurse into the given object, trying to parse any string as JSON
35
*/
@@ -12,6 +14,7 @@ export function deepParseJson(x: unknown): unknown {
1214
}
1315
if (x && typeof x === 'object') {
1416
for (const [key, value] of Object.entries(x)) {
17+
assertNoProtoRec(key);
1518
(x as any)[key] = deepParseJson(value);
1619
}
1720

packages/aws-cdk-lib/aws-apigatewayv2/lib/common/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export abstract class ApiBase extends Resource implements IApi {
3434
* @internal
3535
*/
3636
export abstract class StageBase extends Resource implements IStage {
37-
private stageVariables: { [key: string]: string } = {};
37+
private stageVariables: { [key: string]: string } = Object.create(null); // Prevent prototype pollution
3838
public abstract readonly stageName: string;
3939
protected abstract readonly baseApi: IApi;
4040

packages/aws-cdk-lib/aws-apigatewayv2/lib/parameter-mapping.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@ export class ParameterMapping {
8686
* Represents all created parameter mappings.
8787
*/
8888
public readonly mappings: { [key: string]: string };
89+
8990
constructor() {
90-
this.mappings = {};
91+
// Prevent prototype pollution
92+
this.mappings = Object.create(null);
9193
}
9294

9395
/**

packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ export class GraphqlApi extends GraphqlApiBase {
686686
private apiKeyResource?: CfnApiKey;
687687
private domainNameResource?: CfnDomainName;
688688
private mergedApiExecutionRole?: IRole;
689-
private environmentVariables: { [key: string]: string } = {};
689+
private environmentVariables: { [key: string]: string } = Object.create(null); // Prevent prototype pollution
690690

691691
constructor(scope: Construct, id: string, props: GraphqlApiProps) {
692692
super(scope, id);

packages/aws-cdk-lib/aws-codebuild/lib/build-spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Project } from './project';
44
import * as s3_assets from '../../aws-s3-assets';
55
import type { IResolveContext } from '../../core';
66
import { Lazy, Stack, UnscopedValidationError } from '../../core';
7+
import { assertNoProtoRec } from '../../core/lib/private/prototype-pollution';
78

89
/**
910
* BuildSpec for CodeBuild projects
@@ -251,6 +252,7 @@ function mergeDeep(lhs: any, rhs: any): any {
251252
if (isObject(lhs) && isObject(rhs)) {
252253
const ret: any = { ...lhs };
253254
for (const k of Object.keys(rhs)) {
255+
assertNoProtoRec(k);
254256
ret[k] = k in lhs ? mergeDeep(lhs[k], rhs[k]) : rhs[k];
255257
}
256258
return ret;

0 commit comments

Comments
 (0)