Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: CDK Stages not linted by Nag rules #1726

Open
Clebiez opened this issue Jun 17, 2024 · 4 comments
Open

bug: CDK Stages not linted by Nag rules #1726

Clebiez opened this issue Jun 17, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@Clebiez
Copy link

Clebiez commented Jun 17, 2024

What is the problem?

By using a CDK Stage and using the command cdk synth '**', annotations are correctly generated by cdk but cdk-nag doesn't lint anything from this stack.
Seems to be related to #637

Reproduction Steps

  • Create a new cdk project
  • Install cdk-nag latest version
  • Create file as following:
// ./lib/cdktest-stack.ts
import * as cdk from "aws-cdk-lib";
import { Bucket } from "aws-cdk-lib/aws-s3";
import { Construct } from "constructs";

export class CdktestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const bucket = new Bucket(this, "bucket-test");
  }
}

// ./lib/cdktest-stage.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { CdktestStack } from "./cdktest-stack";

export class CdkTestStage extends cdk.Stage {
  constructor(scope: Construct, id: string, props: cdk.StageProps) {
    super(scope, id, props);
    new CdktestStack(this, "cdk-test-stack");
  }
}

// ./lib/pipeline.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as pipeline from "aws-cdk-lib/pipelines";
import * as codecommit from "aws-cdk-lib/aws-codecommit";
import { CdkTestStage } from "./cdktest-stage";
import { Bucket, BucketEncryption } from "aws-cdk-lib/aws-s3";

export class PipelineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);
    const arnCodeCommit = `arn:aws:codecommit:us-east-1:00000000:testRepo`;
    const artifactBucket = new Bucket(this, "artifact", {
      encryption: BucketEncryption.KMS,
      bucketName: "djslkfsdlkf-artifact",
    });
    // On importe le code-commit dgeq-devops-pes-planb-gep, on doit passer par l'Arn car il est dans un autre compte
    const testRepo = codecommit.Repository.fromRepositoryArn(
      this,
      "testRepo",
      arnCodeCommit
    );

    const sourceCodeCommit = pipeline.CodePipelineSource.codeCommit(
      testRepo,
      "main"
    );

    // Création de l'étape pour CDK Synth
    const synthBuildStep = new pipeline.CodeBuildStep("Synth", {
      input: sourceCodeCommit,
      installCommands: ["npm install -g aws-cdk"],
      commands: [],
      primaryOutputDirectory: "pipeline/cdk.out",
    });

    //Pipeline de base
    const pipelineInfra = new pipeline.CodePipeline(this, "pipelineInfra", {
      synth: synthBuildStep,
      artifactBucket,
    });

    pipelineInfra.addStage(new CdkTestStage(this, "cdk-test-stage", props));

    pipelineInfra.buildPipeline();
  }
}

// bin/main.ts
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { AwsSolutionsChecks } from "cdk-nag";
import { PipelineStack } from "../lib/pipeline";
const app = new cdk.App();
new PipelineStack(app, "PipelineStack", {
  env: {
    region: "us-east-1",
    account: "111111111",
  },
});
cdk.Aspects.of(app).add(new AwsSolutionsChecks());
  • Then, use the command: cdk synth '**'

Rules regarding to the pipeline will be correctly reported here but the test-bucket inside the CdkTestStack seems to be ignored.

Directly instanciating the CdkTestStack inside the bin/main.ts will correctly report S3 rules.

What did you expect to happen?

Trying to lint both of the pipeline and stage rules.

What actually happened?

Only pipeline rules are displayed.

cdk-nag version

2.28.144

Language

Typescript

Other information

No response

@Clebiez Clebiez added bug Something isn't working needs-triage This issue or PR still needs to be triaged. labels Jun 17, 2024
@dontirun
Copy link
Collaborator

This seems to be an issue with cdk Aspects not getting applied on all child scopes and should be raised on the CDK repo. I can reproduce the issue with a normal Aspect

Given the following bin/main.ts

import * as cdk from 'aws-cdk-lib';
import * as codecommit from 'aws-cdk-lib/aws-codecommit';
import { Bucket, BucketEncryption } from 'aws-cdk-lib/aws-s3';
import * as pipeline from 'aws-cdk-lib/pipelines';
import { Construct } from 'constructs';

export class CdktestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    new Bucket(this, 'bucket-test');
  }
}


export class CdkTestStage extends cdk.Stage {
  constructor(scope: Construct, id: string, props: cdk.StageProps) {
    super(scope, id, props);
    new CdktestStack(this, 'cdk-test-stack');
  }
}


export class PipelineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);
    const arnCodeCommit = 'arn:aws:codecommit:us-east-1:00000000:testRepo';
    const artifactBucket = new Bucket(this, 'artifact', {
      encryption: BucketEncryption.KMS,
      bucketName: 'djslkfsdlkf-artifact',
    });
    // On importe le code-commit dgeq-devops-pes-planb-gep, on doit passer par l'Arn car il est dans un autre compte
    const testRepo = codecommit.Repository.fromRepositoryArn(
      this,
      'testRepo',
      arnCodeCommit,
    );

    const sourceCodeCommit = pipeline.CodePipelineSource.codeCommit(
      testRepo,
      'main',
    );

    // Création de l'étape pour CDK Synth
    const synthBuildStep = new pipeline.CodeBuildStep('Synth', {
      input: sourceCodeCommit,
      installCommands: ['npm install -g aws-cdk'],
      commands: [],
      primaryOutputDirectory: 'pipeline/cdk.out',
    });

    //Pipeline de base
    const pipelineInfra = new pipeline.CodePipeline(this, 'pipelineInfra', {
      synth: synthBuildStep,
      artifactBucket,
    });

    pipelineInfra.addStage(new CdkTestStage(this, 'cdk-test-stage', props));

    pipelineInfra.buildPipeline();
  }
}

export class TestAspect implements cdk.IAspect {
  public visit(node: Construct): void {
    if (node instanceof cdk.CfnResource) {
      console.log(`${node.node.path} : ${node.cfnResourceType}`);
    }
  }
}

const app = new cdk.App();
new PipelineStack(app, 'PipelineStack', {
  env: {
    region: 'us-east-1',
    account: '111111111',
  },
});

cdk.Aspects.of(app).add(new TestAspect);

I run cdk synth '**' and get the following output

PipelineStack/artifact/Key/Resource : AWS::KMS::Key
PipelineStack/artifact/Resource : AWS::S3::Bucket
PipelineStack/artifact/Policy/Resource : AWS::S3::BucketPolicy
PipelineStack/testRepo/PipelineStackpipelineInfraPipeline62852AFC-main-EventRule/Resource : AWS::Events::Rule
PipelineStack/pipelineInfra/Pipeline/Role/Resource : AWS::IAM::Role
PipelineStack/pipelineInfra/Pipeline/Role/DefaultPolicy/Resource : AWS::IAM::Policy
PipelineStack/pipelineInfra/Pipeline/Resource : AWS::CodePipeline::Pipeline
PipelineStack/pipelineInfra/Pipeline/EventsRole/Resource : AWS::IAM::Role
PipelineStack/pipelineInfra/Pipeline/EventsRole/DefaultPolicy/Resource : AWS::IAM::Policy
PipelineStack/pipelineInfra/Pipeline/Build/Synth/CdkBuildProject/Role/Resource : AWS::IAM::Role
PipelineStack/pipelineInfra/Pipeline/Build/Synth/CdkBuildProject/Role/DefaultPolicy/Resource : AWS::IAM::Policy
PipelineStack/pipelineInfra/Pipeline/Build/Synth/CdkBuildProject/Resource : AWS::CodeBuild::Project
PipelineStack/pipelineInfra/CodeBuildActionRole/Resource : AWS::IAM::Role
PipelineStack/pipelineInfra/CodeBuildActionRole/DefaultPolicy/Resource : AWS::IAM::Policy
PipelineStack/pipelineInfra/UpdatePipeline/SelfMutation/Role/Resource : AWS::IAM::Role
PipelineStack/pipelineInfra/UpdatePipeline/SelfMutation/Role/DefaultPolicy/Resource : AWS::IAM::Policy
PipelineStack/pipelineInfra/UpdatePipeline/SelfMutation/Resource : AWS::CodeBuild::Project
cross-account-support-stack-00000000/PipelineStackpipelineInfraPipeline62852AFC-Source-testRepo-ActionRole/Resource : AWS::IAM::Role
cross-account-support-stack-00000000/PipelineStackpipelineInfraPipeline62852AFC-Source-testRepo-ActionRole/DefaultPolicy/Resource : AWS::IAM::Policy

@dontirun dontirun removed needs-triage This issue or PR still needs to be triaged. labels Jun 24, 2024
@pradoz
Copy link
Contributor

pradoz commented Aug 7, 2024

Any updates/workarounds for this?

@dontirun
Copy link
Collaborator

dontirun commented Aug 7, 2024

No, this issue would need to be raised in the CDK repo to be addressed

@Clebiez
Copy link
Author

Clebiez commented Aug 9, 2024

One workaround you can do is to isolate the infra created in the stage in a separated function.

const generateInfra = (scope: App | Stage, props: InfraProps) => { YOUR INFRA HERE }

Then your CodePipeline stage will call this function directly, scope will be the stage context this

Create another entrypoint called lint.ts by example and call directly the generateInfra function, the scope will be cdk.App instance.

import { generateInfraStack } from "../lib/generate-infra-stack";

const app = new cdk.App();
cdk.Aspects.of(app).add(new NIST80053R5Checks({ verbose: true }));
generateInfraStack(app, {
    env
    ...other_props
  });
});

Finally, to lint by using use the following script in your package json:

"lint": "cdk synth -a 'npx ts-node --prefer-ts-exts ./bin/lint.ts'"

Cdk-nag will works, and you will do not lint the pipeline stack but only your real infra.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants