gitlabgitlab-ci

Gitlab CI SAST access to gl-sast-report.json artifact in subsequent stage


I am wanting to use the gl-sast-report.json file created during the SAST process in a subsequent stage of my CI but it is not found.

ci.yml

include:
  - template: Security/SAST.gitlab-ci.yml

stages:
  - test
  - .post
sast:
  rules:
    - if: $CI_COMMIT_TAG

send-reports:
  stage: .post
  dependencies: 
    - sast
  script: 
    - ls
    - echo "in post stage"
    - cat gl-sast-report.json

output:

Running with gitlab-runner 13.2.1 (efa30e33)
on blah blah blah
Preparing the "docker" executor
00:01
.
.
.

Preparing environment
00:01
Running on runner-zqk9bcef-project-4296-concurrent-0 via ff93ba7b6ee2...
Getting source from Git repository
00:01
Fetching changes with git depth set to 50...
Reinitialized existing Git repository in blah blah
Checking out 9c2edf67 as 39-test-dso...
Removing gl-sast-report.json
Skipping Git submodules setup
Executing "step_script" stage of the job script
00:03
$ ls
<stuff in the repo>
$ echo "in .post stage"
in post stage
$ cat gl-sast-report.json
cat: can't open 'gl-sast-report.json': No such file or directory
ERROR: Job failed: exit code 1

You can see the line Removing gl-sast-report.json which I assume is the issue.

I don't see that anywhere in the SAST.gitlab-ci.yml at https://gitlab.com/gitlab-org/gitlab/-/blob/v11.11.0-rc2-ee/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml#L33-45

Any ideas on how to use this artifact in the next stage of my CI pipeline?

UPDATE:

So I tried out k33g_org's suggestion below but to no avail. Seems that this is due to limitations in the sast template specifically. Did the following test.

include:
  - template: Security/SAST.gitlab-ci.yml

stages:
  - test
  - upload

something:
  stage: test
  script:
      - echo "in something"
      - echo "this is something" > something.txt
  artifacts:
      paths: [something.txt]

sast:
  before_script:
      - echo "hello from before sast"
      - echo "this is in the file" > test.txt
  artifacts:
    reports:
      sast: gl-sast-report.json
    paths: [gl-sast-report.json, test.txt]

send-reports:
  stage: upload
  dependencies:
    - sast
    - something
  before_script:
      - echo "This is the send-reports before_script"
  script:
    - echo "in send-reports job"
    - ls
  artifacts:
      reports:
          sast: gl-sast-report.json

Three changes:

  1. Updated code with k33g_org's suggestion
  2. Created another artifact in the sast job (to see if it would pass through to send-reports job)
  3. Created a new job (something) where I created a new something.txt artifact (to see if it would pass through to send-reports job)

Output:

Preparing environment
00:01
Running on runner-zqx7qoq-project-4296-concurrent-0 via e3fe672984b4...
Getting source from Git repository
Fetching changes with git depth set to 50...
Reinitialized existing Git repository in /<repo>
Checking out 26501c44 as <branch_name>...
Removing something.txt
Skipping Git submodules setup
Downloading artifacts
00:00
Downloading artifacts for something (64950)...
Downloading artifacts from coordinator... ok        id=64950 
responseStatus=200 OK token=zoJwysdq
Executing "step_script" stage of the job script
00:01
$ echo "This is the send-reports before_script"
This is the send-reports before_script
$ echo "in send-reports job"
in send-reports job
$ ls
...<other stuff in repo>
something.txt
Uploading artifacts for successful job
00:01
Uploading artifacts...
WARNING: gl-sast-report.json: no matching files    
ERROR: No files to upload                          
Cleaning up file based variables
00:01
Job succeeded

Notes:

I can only conclude that there is something internal to the sast template that is not allowing artifacts to propagate to subsequent jobs.


Solution

  • I spent a full day banging my head against this, trying to access the gl-sast-report.json file generated by the built-in IaC scanner. Here's what ultimately worked for me:

    First and foremost, DO NOT use this code suggested by GitLab's documentation:

    include:
      - template: Security/SAST-IaC.latest.gitlab-ci.yml
    

    The above code works fine if all you want to do is scan for IaC vulnerabilities and download the report from the GitLab UI later. But who wants to do that?! I want to access the report in my next job and fail the pipeline if there are medium+ vulnerabilities in the report!

    If that's what you want to do, you'll need to add all of the code from the official GitLab IaC scanner template to your pipeline, and then make some modifications. You can find the latest template code here, or use my example below.

    Modified template:

    # Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/iac_scanning/
    #
    # Configure SAST with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
    # List of available variables: https://docs.gitlab.com/ee/user/application_security/iac_scanning/index.html
    
    variables:
      # Setting this variable will affect all Security templates
      # (SAST, Dependency Scanning, ...)
      TEMPLATE_REGISTRY_HOST: 'registry.gitlab.com'
      SECURE_ANALYZERS_PREFIX: "$TEMPLATE_REGISTRY_HOST/security-products"
      SAST_IMAGE_SUFFIX: ""
    
      SAST_EXCLUDED_PATHS: "spec, test, tests, tmp"
    
    iac-sast:
      stage: test
      artifacts:
        name: sast
        paths:
          - gl-sast-report.json
        #reports:
        #  sast: gl-sast-report.json
        when: always
      rules:
        - when: never
      # `rules` must be overridden explicitly by each child job
      # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444
      variables:
        SEARCH_MAX_DEPTH: 4
      allow_failure: true
      script:
        - /analyzer run
    
    kics-iac-sast:
      extends: iac-sast
      image:
        name: "$SAST_ANALYZER_IMAGE"
      variables:
        SAST_ANALYZER_IMAGE_TAG: 3
        SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/kics:$SAST_ANALYZER_IMAGE_TAG$SAST_IMAGE_SUFFIX"
      rules:
        - if: $SAST_DISABLED
          when: never
        - if: $SAST_EXCLUDED_ANALYZERS =~ /kics/
          when: never
        - if: $CI_COMMIT_BRANCH
      
    
    Enforce Compliance:
      stage: Compliance
      before_script:
        - apk add jq
      script:
        - jq -r '.vulnerabilities[] | select(.severity == "Critical") | (.severity, .message, .location, .identifiers[].url)' gl-sast-report.json > results.txt
        - jq -r '.vulnerabilities[] | select(.severity == "High") | (.severity, .message, .location, .identifiers[].url)' gl-sast-report.json >> results.txt
        - jq -r '.vulnerabilities[] | select(.severity == "Medium") | (.severity, .message, .location, .identifiers[].url)' gl-sast-report.json >> results.txt
        - chmod u+x check-sast-results.sh
        - ./check-sast-results.sh
    

    You'll also need to make sure to add two stages to your pipeline (if you don't have them already):

    stages:
      # add these to whatever other stages you already have
      - test
      - Compliance
    

    Note: it's extremely important that your job which is trying to access gl-sast-report.json ("Compliance" in this case) is not in the same stage as the sast scans themselves ("test" in this case). If they are, then your job will try to access the report before it exists and fail.

    I'll include my shell script referenced in the pipeline in case you want to use that too:

    #!/bin/sh
    
    if [ -s results.txt ]; then
            echo ""
            echo ""
            cat results.txt
            echo ""
            echo "ERROR: SAST SCAN FOUND VULNERABILITIES - FIX ALL VULNERABILITIES TO CONTINUE"
            echo ""
            exit 1
    fi
    

    This is a basic script that checks to see if the "results.txt" file has any contents. If it does, it exits with code 1 to break the pipeline and print the vulnerabilities. If there are no contents in the file, the script exits with code 0 and the pipeline continues (allowing you to deploy your infra). Save the file above as "check-sast-results.sh" in the root directory of your GitLab repository (the same level where ".gitlab-ci.yml" resides).

    Hope this helps someone out there.