From 9acbd968bc8a2127dfa4c42da9448a5105ba7122 Mon Sep 17 00:00:00 2001 From: Dzhuneyt Date: Sat, 11 Dec 2021 23:33:19 +0200 Subject: [PATCH 1/4] feat: Validate Lambda "functionName" parameter --- packages/@aws-cdk/aws-lambda/lib/function.ts | 15 ++++++++++++ .../@aws-cdk/aws-lambda/test/function.test.ts | 24 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 564e45d5a9460..73af1aaeb84e6 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -685,6 +685,13 @@ export class Function extends FunctionBase { } const architecture = props.architecture ?? (props.architectures && props.architectures[0]); + if (props.functionName && props.functionName.length>140) { + throw new Error('Function name can not be longer than 140 characters.'); + } + if (props.functionName && !this.isValidFunctionName(props.functionName)) { + throw new Error('Function name can contain only letters, numbers, hyphens, or underscores with no spaces.'); + } + const resource: CfnFunction = new CfnFunction(this, 'Resource', { functionName: this.physicalName, description: props.description, @@ -1079,6 +1086,14 @@ Environment variables can be marked for removal when used in Lambda@Edge by sett throw new Error('AWS_CODEGURU_PROFILER_GROUP_ARN and AWS_CODEGURU_PROFILER_ENABLED must not be set when profiling options enabled'); } } + + /** + * Validate if the string contains only letters, numbers, hyphens, underscore but no spaces + */ + private isValidFunctionName(functionName: string) { + const regexp = /^[a-zA-Z0-9-_]+$/; + return functionName.search(regexp) !== -1; + } } /** diff --git a/packages/@aws-cdk/aws-lambda/test/function.test.ts b/packages/@aws-cdk/aws-lambda/test/function.test.ts index bd25c33b7a109..48755d19d8251 100644 --- a/packages/@aws-cdk/aws-lambda/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function.test.ts @@ -2270,6 +2270,30 @@ describe('function', () => { }); expect(fn.architecture?.name).toEqual('arm64'); }); + + test('Error when function name is longer than 140 chars', ()=>{ + const stack = new cdk.Stack(); + expect(() => new lambda.Function(stack, 'MyFunction', { + code: lambda.Code.fromInline('foo'), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + functionName: 'a'.repeat(141), + })).toThrow(/Function name can not be longer than 140 characters./); + }); + + test('Error when function name contains invalid characters', ()=>{ + const stack = new cdk.Stack(); + [' ', '\n', '\r', '[', ']', '<', '>', '$'].forEach(invalidChar=>{ + expect(()=>{ + new lambda.Function(stack, `foo${invalidChar}`, { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + functionName: `foo${invalidChar}`, + }); + }).toThrow(/Function name can contain only letters, numbers, hyphens, or underscores with no spaces./); + }); + }); }); function newTestLambda(scope: constructs.Construct) { From 684f44a8f70735ba4165dccb01228381c83e03a8 Mon Sep 17 00:00:00 2001 From: Dzhuneyt <1754428+Dzhuneyt@users.noreply.github.com> Date: Mon, 31 Jan 2022 11:50:07 +0200 Subject: [PATCH 2/4] chore: Apply suggestions from code review Co-authored-by: Kaizen Conroy <36202692+kaizen3031593@users.noreply.github.com> --- packages/@aws-cdk/aws-lambda/lib/function.ts | 2 +- packages/@aws-cdk/aws-lambda/test/function.test.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index d62a1c57bf378..8861924cc1c15 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -690,7 +690,7 @@ export class Function extends FunctionBase { } this._architecture = props.architecture ?? (props.architectures && props.architectures[0]); - if (props.functionName && props.functionName.length>140) { + if (props.functionName && props.functionName.length > 140) { throw new Error('Function name can not be longer than 140 characters.'); } if (props.functionName && !this.isValidFunctionName(props.functionName)) { diff --git a/packages/@aws-cdk/aws-lambda/test/function.test.ts b/packages/@aws-cdk/aws-lambda/test/function.test.ts index 153f4c4b2382c..e1604fce93b64 100644 --- a/packages/@aws-cdk/aws-lambda/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function.test.ts @@ -2279,7 +2279,7 @@ describe('function', () => { expect(fn.architecture?.name).toEqual('arm64'); }); - test('Error when function name is longer than 140 chars', ()=>{ + test('Error when function name is longer than 140 chars', () => { const stack = new cdk.Stack(); expect(() => new lambda.Function(stack, 'MyFunction', { code: lambda.Code.fromInline('foo'), @@ -2289,10 +2289,10 @@ describe('function', () => { })).toThrow(/Function name can not be longer than 140 characters./); }); - test('Error when function name contains invalid characters', ()=>{ + test('Error when function name contains invalid characters', () => { const stack = new cdk.Stack(); - [' ', '\n', '\r', '[', ']', '<', '>', '$'].forEach(invalidChar=>{ - expect(()=>{ + [' ', '\n', '\r', '[', ']', '<', '>', '$'].forEach(invalidChar => { + expect(() => { new lambda.Function(stack, `foo${invalidChar}`, { code: new lambda.InlineCode('foo'), handler: 'index.handler', From 2330799a749df00d2c45b30127b54ede8021d976 Mon Sep 17 00:00:00 2001 From: Dzhuneyt Date: Mon, 31 Jan 2022 12:41:58 +0200 Subject: [PATCH 3/4] chore: Address code review comments --- packages/@aws-cdk/aws-lambda/lib/function.ts | 16 +++++++++------- .../@aws-cdk/aws-lambda/test/function.test.ts | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 8861924cc1c15..a872f1ce7929c 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -5,7 +5,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as logs from '@aws-cdk/aws-logs'; import * as sqs from '@aws-cdk/aws-sqs'; -import { Annotations, ArnFormat, CfnResource, Duration, Fn, Lazy, Names, Stack } from '@aws-cdk/core'; +import { Annotations, ArnFormat, CfnResource, Duration, Fn, Lazy, Names, Stack, Token } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { Architecture } from './architecture'; import { Code, CodeConfig } from './code'; @@ -690,11 +690,13 @@ export class Function extends FunctionBase { } this._architecture = props.architecture ?? (props.architectures && props.architectures[0]); - if (props.functionName && props.functionName.length > 140) { - throw new Error('Function name can not be longer than 140 characters.'); - } - if (props.functionName && !this.isValidFunctionName(props.functionName)) { - throw new Error('Function name can contain only letters, numbers, hyphens, or underscores with no spaces.'); + if (props.functionName && !Token.isUnresolved(props.functionName)) { + if (props.functionName.length > 140) { + throw new Error('Function name can not be longer than 140 characters.'); + } + if (!this.validateFunctionName(props.functionName)) { + throw new Error('Function name can contain only letters, numbers, hyphens, or underscores with no spaces.'); + } } const resource: CfnFunction = new CfnFunction(this, 'Resource', { @@ -1095,7 +1097,7 @@ Environment variables can be marked for removal when used in Lambda@Edge by sett /** * Validate if the string contains only letters, numbers, hyphens, underscore but no spaces */ - private isValidFunctionName(functionName: string) { + private validateFunctionName(functionName: string) { const regexp = /^[a-zA-Z0-9-_]+$/; return functionName.search(regexp) !== -1; } diff --git a/packages/@aws-cdk/aws-lambda/test/function.test.ts b/packages/@aws-cdk/aws-lambda/test/function.test.ts index e1604fce93b64..6c3e5fb255738 100644 --- a/packages/@aws-cdk/aws-lambda/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function.test.ts @@ -11,6 +11,7 @@ import * as signer from '@aws-cdk/aws-signer'; import * as sqs from '@aws-cdk/aws-sqs'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import * as cdk from '@aws-cdk/core'; +import { Intrinsic, Token } from '@aws-cdk/core'; import * as constructs from 'constructs'; import * as _ from 'lodash'; import * as lambda from '../lib'; @@ -2302,6 +2303,21 @@ describe('function', () => { }).toThrow(/Function name can contain only letters, numbers, hyphens, or underscores with no spaces./); }); }); + + test('No error when function name is Tokenized and Unresolved', () => { + const stack = new cdk.Stack(); + expect(() => { + const realFunctionName = 'a'.repeat(141); + const tokenizedFunctionName = Token.asString(new Intrinsic(realFunctionName)); + + new lambda.Function(stack, 'foo', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + functionName: tokenizedFunctionName, + }); + }).not.toThrow(); + }); }); function newTestLambda(scope: constructs.Construct) { From f9d48eb8525551a6b661cdf75c371af3a86165a9 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Thu, 17 Feb 2022 15:57:05 -0500 Subject: [PATCH 4/4] pr feedback and refactoring --- packages/@aws-cdk/aws-lambda/lib/function.ts | 26 +++++++------------ .../@aws-cdk/aws-lambda/test/function.test.ts | 14 +++++----- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index a872f1ce7929c..d0a3caff399c0 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -610,6 +610,15 @@ export class Function extends FunctionBase { physicalName: props.functionName, }); + if (props.functionName && !Token.isUnresolved(props.functionName)) { + if (props.functionName.length > 64) { + throw new Error(`Function name can not be longer than 64 characters but has ${props.functionName.length} characters.`); + } + if (!/^[a-zA-Z0-9-_]+$/.test(props.functionName)) { + throw new Error(`Function name ${props.functionName} can contain only letters, numbers, hyphens, or underscores with no spaces.`); + } + } + const managedPolicies = new Array(); // the arn is in the form of - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole @@ -690,15 +699,6 @@ export class Function extends FunctionBase { } this._architecture = props.architecture ?? (props.architectures && props.architectures[0]); - if (props.functionName && !Token.isUnresolved(props.functionName)) { - if (props.functionName.length > 140) { - throw new Error('Function name can not be longer than 140 characters.'); - } - if (!this.validateFunctionName(props.functionName)) { - throw new Error('Function name can contain only letters, numbers, hyphens, or underscores with no spaces.'); - } - } - const resource: CfnFunction = new CfnFunction(this, 'Resource', { functionName: this.physicalName, description: props.description, @@ -1093,14 +1093,6 @@ Environment variables can be marked for removal when used in Lambda@Edge by sett throw new Error('AWS_CODEGURU_PROFILER_GROUP_ARN and AWS_CODEGURU_PROFILER_ENABLED must not be set when profiling options enabled'); } } - - /** - * Validate if the string contains only letters, numbers, hyphens, underscore but no spaces - */ - private validateFunctionName(functionName: string) { - const regexp = /^[a-zA-Z0-9-_]+$/; - return functionName.search(regexp) !== -1; - } } /** diff --git a/packages/@aws-cdk/aws-lambda/test/function.test.ts b/packages/@aws-cdk/aws-lambda/test/function.test.ts index 6c3e5fb255738..7140fc4fc4058 100644 --- a/packages/@aws-cdk/aws-lambda/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function.test.ts @@ -2280,14 +2280,14 @@ describe('function', () => { expect(fn.architecture?.name).toEqual('arm64'); }); - test('Error when function name is longer than 140 chars', () => { + test('Error when function name is longer than 64 chars', () => { const stack = new cdk.Stack(); expect(() => new lambda.Function(stack, 'MyFunction', { code: lambda.Code.fromInline('foo'), - runtime: lambda.Runtime.NODEJS_12_X, + runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', - functionName: 'a'.repeat(141), - })).toThrow(/Function name can not be longer than 140 characters./); + functionName: 'a'.repeat(65), + })).toThrow(/Function name can not be longer than 64 characters/); }); test('Error when function name contains invalid characters', () => { @@ -2297,10 +2297,10 @@ describe('function', () => { new lambda.Function(stack, `foo${invalidChar}`, { code: new lambda.InlineCode('foo'), handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, functionName: `foo${invalidChar}`, }); - }).toThrow(/Function name can contain only letters, numbers, hyphens, or underscores with no spaces./); + }).toThrow(/can contain only letters, numbers, hyphens, or underscores with no spaces./); }); }); @@ -2313,7 +2313,7 @@ describe('function', () => { new lambda.Function(stack, 'foo', { code: new lambda.InlineCode('foo'), handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_14_X, functionName: tokenizedFunctionName, }); }).not.toThrow();