bug: IAM5 resource based granular suppressions need better serialisation
See original GitHub issueWhat is the problem?
The resource based rule findings for the IAM5 rule come out as [object Object]
when the resource ARNs are complex.
Example:
[Error at /thglaser-ldn-mainline/api/apiHandlerRole/Resource] AwsSolutions-IAM5[Resource::[object Object]]: The IAM entity contains wildcard permissions and does not have a cdk_nag rule suppression with evidence for those permission.
Reproduction Steps
Create a policy that constructs the ARN based on some inputs like so:
export interface LambdaBaseRoleProps {
functionName: string;
}
export class LambdaBaseRole extends Role {
constructor(scope: IConstruct, id: string, props: LambdaBaseRoleProps) {
super(scope, id, {
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
inlinePolicies: {
logAccess: new PolicyDocument({
statements: [
new PolicyStatement({
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
resources: [
Stack.of(scope).formatArn({
service: 'logs',
resource: 'log-group',
resourceName: `/aws/lambda/${props.functionName}`,
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
}),
Stack.of(scope).formatArn({
service: 'logs',
resource: 'log-group',
resourceName: `/aws/lambda/${props.functionName}:log-stream:*`,
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
}),
],
}),
],
}),
},
});
NagSuppressions.addResourceSuppressions(
this,
[
// {
// id: 'AwsSolutions-IAM5',
// reason: 'The resource is scoped down to relevant log streams',
// },
],
true,
);
}
}
What did you expect to happen?
Anything but [object Object]
as it makes it impossible to suppress a specific resource
What actually happened?
(see problem description)
cdk-nag version
2.7.0
Language
Typescript
Other information
I have a sketch of a fix in my mind. It would be ideal if the resource object could be flattened so that I can granularly suppress.
Consider the following function:
const flattenCfn = (node: any): string {
if (typeof node === 'string') {
return node
}
if (node["Fn::Join"]) {
const delimiter = node["Fn::Join"][0];
const items = node["Fn::Join"][1]
return items.map(flattenCfn).join(delimiter)
}
if (node['Fn::Sub']) {
return flattenCfn(node['Fn::Sub'])
}
if (node['Fn::GetAtt']) {
return "${" + flattenCfn(node['Fn::GetAtt'][0]) + "." + flattenCfn(node['Fn::GetAtt'][1]) + "}"
}
if (node['Fn::ImportValue']) {
return flattenCfn(node['Fn::ImportValue'])
}
if (node['Ref']) {
return "${" + flattenCfn(node['Ref']) + "}"
}
return JSON.stringify(node)
}
The function would turn this:
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Fn::Sub": "cdk-62b12fb5cc-assets-${AWS::AccountId}-eu-west-2"
},
"/*"
]
]
}
into this
"arn:${AWS::Partition}:s3:::cdk-62b12fb5cc-assets-${AWS::AccountId}-eu-west-2/*"
which would be much easier to suppress than [object Object]
.
I’d be happy to contribute a PR to that effect.
Issue Analytics
- State:
- Created 2 years ago
- Comments:5
Top Results From Across the Web
No results found
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Ah I see! I’m not super familiar withjsii
, are there restrictions for the internal code too, like can I use the JavaScriptRegExp
class inside of the rules engine?Edit: Nevermind, you already answered that in your original message. 😁
Ok, sounds good, I’ll start working on that PR!
I like the regex idea and I think it would also simplify suppressions of similar findings.
While we can use any TypeScript related feature on the backend, our public APIs need to work across languages. Fortunately jsii allows us to use interfaces and will generate language specific bindings to use that API. Your suggested hint for CloudFormation is probably how we want to structure the interface.