Lambda Trigger Missing Resource-Based Permission
TL-PERM-003
What this rule checks
An in-template trigger (EventBridge rule, SNS lambda subscription, S3 bucket notification) targets a Lambda function but no AWS::Lambda::Permission grants the service principal invoke access. The trigger silently never invokes the function. CDK custom-resource notification patterns are excluded.
How to fix it
- 1Use the CDK L2 wiring, which creates the permission automatically: rule.addTarget(new targets.LambdaFunction(fn)), topic.addSubscription(new subscriptions.LambdaSubscription(fn)), or bucket.addEventNotification(...)
- 2Or add an AWS::Lambda::Permission with Action lambda:InvokeFunction, the trigger service principal, and a SourceArn scoped to the trigger
import * as events from 'aws-cdk-lib/aws-events';
import * as lambda from 'aws-cdk-lib/aws-lambda';
const reporter = new lambda.Function(this, 'Reporter', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromInline('exports.handler = async () => {};'),
});
new events.CfnRule(this, 'Nightly', {
scheduleExpression: 'cron(0 3 * * ? *)',
targets: [{ id: 'reporter', arn: reporter.functionArn }],
});import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as lambda from 'aws-cdk-lib/aws-lambda';
const reporter = new lambda.Function(this, 'Reporter', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromInline('exports.handler = async () => {};'),
});
new events.Rule(this, 'Nightly', {
schedule: events.Schedule.cron({ hour: '3', minute: '0' }),
targets: [new targets.LambdaFunction(reporter)],
});CDK Insights pinpoints the exact file and line in your CDK source for every finding, so you can jump straight to the fix.
Affected resource types
AWS::Lambda::PermissionAWS::Events::RuleAWS::SNS::SubscriptionAWS::S3::BucketIntentional? Suppress this finding
Sometimes a flag is deliberate — a genuinely public endpoint, say. You can dismiss TL-PERM-003 and the reason is kept in the report, not silently hidden.
In .cdk-insights.json:
{
"ignoreRules": [
{ "id": "TL-PERM-003", "reason": "Why this is intentional" }
]
}Or inline in your CDK code:
Validations.of(scope).acknowledge({
id: 'cdk-insights::TL-PERM-003',
reason: 'Why this is intentional',
});Use the rule ID TL-PERM-003 shown above — not the CDK-* ID from SARIF / GitHub code scanning. To dismiss every finding on one construct instead, use ignorePaths. Suppression docs →
Catch this in your stack
$ npx cdk-insights scanCDK Insights runs this and 131+ other rules locally against your synthesised CDK app — free, no account, your code never leaves your machine.