Recently people have been adding money to the buzzword jar (same idea as a swear jar), with “DevSecOps”.

In an attempt to add the “Sec” to “DevSecOps”, I took a look at the OWASP Dependency Checker.

I wanted to use AWS Codebuild to run a dependency check each time a developer opened a PR to the master branch to check to see if they’re introducing any third party dependencies with known vulnerabilitites

The OWASP Dependency checker has a solid Jenkins Plugin that track vulnerabilities it finds over time, so wanted to see what could be done using the new AWS Codebuild Test Reports

When I open a PR to master, that triggers the OWASP build.

Here is my buildspec.yml (note this is for a Javascript project)

---
version: 0.2

phases:
  install:
    commands:
      - wget https://dl.bintray.com/jeremy-long/owasp/dependency-check-5.3.0-release.zip
      - unzip dependency-check-*
      - export PATH=dependency-check/bin:$PATH
      - npm install
  build:
    commands:
      - mkdir -p nvd  # Will store nvd here
      - |
        dependency-check.sh \
          --project my-project \
          -s . \
          -f JUNIT \
          --disableNuspec \
          --disableAssembly \
          -d nvd \
          --failOnCVSS $FAIL_ON_CVSS        
# Caching the nvd database so each run doesn't download the
# entire nvd
cache:
  paths:
    - nvd/**/*

reports:
  # ARN of the Codebuild Report created in Cloudformation
  arn:aws:codebuild:us-east-1:111111111111:report-group/OwaspReportGroup-ABC123abc987:
    files:
      - dependency-check-junit.xml
    file-format: JunitXml

Once the build runs, I get an entry in my report group, and can quickly see which dependencies have known vulnerabilities.

Since AWS Codebuild Reports are meant for test reports, even low risk vulnerabilities will cause the report to display as failed. I set the Codebuild project itself to only fail if the CVSS score was greater than 7 (See the $FAIL_ON_CVSS variable)

I can see the Codebuild Report groups having value by showing dependency vulnerabilities over time to show trends in security posture.

What I wish were different

Publishing reports in buildspec.yml

To specify a report group inside the buildspec.yml, I can either add an arbitrary name (which will create the repor group for you), or provide the full arn of a pre-existing report group.

I could do one or both of the following inside the buildspec.yml

reports:
  my-new-report:  # New report group gets created
    files:
      - dependency-check-junit.xml
    file-format: JunitXml
  arn:aws:codebuild:us-east-1:111111111111:report-group/owasp-dependency-checker:
    files:
      - dependency-check-junit.xml
    file-format: JunitXml

However if I want to create my report group in Cloudformation using the AWS::CodeBuild::ReportGroup, and not use the Name property, I can’t pass the ARN (Which is the Return value when using the Ref intrinsic function) to my buildspec.yml as an environment variable, because you can’t use a variable as a key.

I.e. I can’t pass an environment variable REPORT_ARN to the Codebuild Project do this inside my buildspec.yml

reports:
  '${REPORT_ARN}':  # Can't do this
    files:
      - dependency-check-junit.xml
    file-format: JunitXml

Codebuild Reports format

While the CodeBuild Reports are still in preview (as of this writing), and it’s purpose is to publish test results, the display of the OWASP dependency report isn’t fantastic.

In terms of failing your build if third party dependencies have critical vulnerabilities, this setup works fine.

From the Codebuild Reports section of the console, you can get a cursory level understand of what was found, but you’ll need to read the full report, which will get uploaded to S3 if you used the Cloudformation included.

Full Cloudformation for all resources:

---
AWSTemplateFormatVersion: "2010-09-09"

Description: >
  Resources for Codebuild project to run OWASP dependecy checks  
Parameters:
  GithubRepo:
    Type: String
    Description: Github repo to use
    Default: https://github.com/path-to-your/repo.git
  FailOnCVSS:
    Type: String
    AllowedPattern: '[0-9]|10'
    Description: >
      If the score set between 0 and 10 the exit code from dependency-check will indicate if a vulnerability with a CVSS score equal to or higher was identified      
    Default: 7

Resources:
  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256

  GithubSourceCredential:
    Type: AWS::CodeBuild::SourceCredential
    Properties:
      AuthType: PERSONAL_ACCESS_TOKEN
      ServerType: GITHUB
      # Uses my Personal Access token in Github, stored in AWS Secrets Manager
      Token: '{{resolve:secretsmanager:github-access-token:SecretString:token}}'

  CodebuildOwasp:
    Type: AWS::CodeBuild::Project
    DependsOn:
      - GithubSourceCredential
    Properties:
      Artifacts:
        Type: S3
        Location: !Ref Bucket
        Name: owasp
        Packaging: ZIP
      Cache:
        Location: !Sub '${Bucket}/owasp.zip'
        Type: S3
      BadgeEnabled: true
      Description: OWASP dependency check
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:2.0
        Type: LINUX_CONTAINER
        EnvironmentVariables:
          - Name: FAIL_ON_CVSS
            Value: !Ref FailOnCVSS
      ServiceRole: !Ref CodebuildRole
      Source:
        BuildSpec: buildspec.yml
        Type: GITHUB
        Location: !Ref GithubRepo
        ReportBuildStatus: true
      Triggers:
        Webhook: true
        FilterGroups:
          # When a pull request to master branch is created or updated
          - - Type: EVENT
              Pattern: PULL_REQUEST_CREATED, PULL_REQUEST_UPDATED, PULL_REQUEST_REOPENED
            - Type: BASE_REF
              Pattern: ^refs/heads/master$
              ExcludeMatchedPattern: false

  OwaspReportGroup:
    Type: AWS::CodeBuild::ReportGroup
    Properties:
      ExportConfig:
        ExportConfigType: S3
        S3Destination:
          Bucket: !Ref Bucket
          Packaging: ZIP
      Type: TEST

  CodebuildRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Principal:
              Service:
                - codebuild.amazonaws.com
            Effect: Allow
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: logs
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  - !Sub 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*'
        - PolicyName: S3
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetBucketAcl
                  - s3:GetBucketLocation
                Resource:
                  - !Sub
                      - '${BucketArn}*'
                      - BucketArn: !GetAtt Bucket.Arn
        - PolicyName: CodebuildReports
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - codebuild:CreateReportGroup
                  - codebuild:CreateReport
                  - codebuild:UpdateReport
                  - codebuild:BatchPutTestCases
                Resource:
                  - !Sub 'arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/*'