Skip to content

Commit c159e77

Browse files
authored
fix(lambda): validate localMountPath format and length (#31019)
### Issue # (if applicable) Closes #17526 ### Reason for this change Show user-friendly error message faster (before deployment) ### Description of changes Added format and length validation according to [AWS::Lambda::Function FileSystemConfig document](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-filesystemconfig.html#cfn-lambda-function-filesystemconfig-localmountpath) ![image](https://github.com/user-attachments/assets/5296cef5-9b33-4ddc-bfc2-8e6fac00dc66) ### Description of how you validated changes - Added unit test - No integration test because I think it is overkill ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 751a922 commit c159e77

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

packages/aws-cdk-lib/aws-lambda/lib/function.ts

+8
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,14 @@ export class Function extends FunctionBase {
916916
// add additional managed policies when necessary
917917
if (props.filesystem) {
918918
const config = props.filesystem.config;
919+
if (!Token.isUnresolved(config.localMountPath)) {
920+
if (!/^\/mnt\/[a-zA-Z0-9-_.]+$/.test(config.localMountPath)) {
921+
throw new Error(`Local mount path should match with ^/mnt/[a-zA-Z0-9-_.]+$ but given ${config.localMountPath}.`);
922+
}
923+
if (config.localMountPath.length > 160) {
924+
throw new Error(`Local mount path can not be longer than 160 characters but has ${config.localMountPath.length} characters.`);
925+
}
926+
}
919927
if (config.policies) {
920928
config.policies.forEach(p => {
921929
this.role?.addToPrincipalPolicy(p);

packages/aws-cdk-lib/aws-lambda/test/function.test.ts

+93
Original file line numberDiff line numberDiff line change
@@ -2923,6 +2923,99 @@ describe('function', () => {
29232923
});
29242924
});
29252925

2926+
test('validate localMountPath format when mounting efs', () => {
2927+
// GIVEN
2928+
const stack = new cdk.Stack();
2929+
const vpc = new ec2.Vpc(stack, 'Vpc', {
2930+
maxAzs: 3,
2931+
natGateways: 1,
2932+
});
2933+
const securityGroup = new ec2.SecurityGroup(stack, 'LambdaSG', {
2934+
vpc,
2935+
allowAllOutbound: false,
2936+
});
2937+
2938+
const fs = new efs.FileSystem(stack, 'Efs', {
2939+
vpc,
2940+
});
2941+
const accessPoint = fs.addAccessPoint('AccessPoint');
2942+
2943+
// THEN
2944+
expect(() => {
2945+
new lambda.Function(stack, 'MyFunction', {
2946+
vpc,
2947+
handler: 'foo',
2948+
securityGroups: [securityGroup],
2949+
runtime: lambda.Runtime.NODEJS_LATEST,
2950+
code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')),
2951+
filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, '/not-mnt/foo-bar'),
2952+
});
2953+
}).toThrow('Local mount path should match with ^/mnt/[a-zA-Z0-9-_.]+$ but given /not-mnt/foo-bar');
2954+
});
2955+
2956+
test('validate localMountPath length when mounting efs', () => {
2957+
// GIVEN
2958+
const stack = new cdk.Stack();
2959+
const vpc = new ec2.Vpc(stack, 'Vpc', {
2960+
maxAzs: 3,
2961+
natGateways: 1,
2962+
});
2963+
const securityGroup = new ec2.SecurityGroup(stack, 'LambdaSG', {
2964+
vpc,
2965+
allowAllOutbound: false,
2966+
});
2967+
2968+
const fs = new efs.FileSystem(stack, 'Efs', {
2969+
vpc,
2970+
});
2971+
const accessPoint = fs.addAccessPoint('AccessPoint');
2972+
2973+
// THEN
2974+
expect(() => {
2975+
new lambda.Function(stack, 'MyFunction', {
2976+
vpc,
2977+
handler: 'foo',
2978+
securityGroups: [securityGroup],
2979+
runtime: lambda.Runtime.NODEJS_LATEST,
2980+
code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')),
2981+
filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, `/mnt/${'a'.repeat(160)}`),
2982+
});
2983+
}).toThrow('Local mount path can not be longer than 160 characters but has 165 characters');
2984+
});
2985+
2986+
test('No error when local mount path is Tokenized and Unresolved', () => {
2987+
// GIVEN
2988+
const realLocalMountPath = '/not-mnt/foo-bar';
2989+
const tokenizedLocalMountPath = cdk.Token.asString(new cdk.Intrinsic(realLocalMountPath));
2990+
2991+
const stack = new cdk.Stack();
2992+
const vpc = new ec2.Vpc(stack, 'Vpc', {
2993+
maxAzs: 3,
2994+
natGateways: 1,
2995+
});
2996+
const securityGroup = new ec2.SecurityGroup(stack, 'LambdaSG', {
2997+
vpc,
2998+
allowAllOutbound: false,
2999+
});
3000+
3001+
const fs = new efs.FileSystem(stack, 'Efs', {
3002+
vpc,
3003+
});
3004+
const accessPoint = fs.addAccessPoint('AccessPoint');
3005+
3006+
// THEN
3007+
expect(() => {
3008+
new lambda.Function(stack, 'MyFunction', {
3009+
vpc,
3010+
handler: 'foo',
3011+
securityGroups: [securityGroup],
3012+
runtime: lambda.Runtime.NODEJS_LATEST,
3013+
code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')),
3014+
filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, tokenizedLocalMountPath),
3015+
});
3016+
}).not.toThrow();
3017+
});
3018+
29263019
test('correct security group is created when deployed in separate stacks', () => {
29273020
const app = new cdk.App();
29283021

0 commit comments

Comments
 (0)