Add ASH to a GitHub Actions pipeline¶
In this tutorial you add a workflow that scans every pull request with ASH, fails the check
when ASH finds something, and shows the findings on the pull request and in GitHub code
scanning. It uses the reusable workflows that ASH ships, pinned to v3.7.0.
Before you start¶
- A GitHub repository with Actions enabled, where you can push a branch and open a pull request.
- For step 4, code scanning: any public repository, or a private one with a GitHub Code Security license. Without it, skip step 4; the other steps work the same.
- git, to look up the commit of
v3.7.0in step 1.
Nothing else has to be installed on your machine. Using another CI service? See
Run ASH in GitLab CI or
Run ASH in Jenkins, CodeBuild, Azure Pipelines, CircleCI, and other CI systems,
whose CircleCI section has a .circleci/config.yml.
1. Add the workflow¶
Create .github/workflows/ash.yml on a new branch:
name: ASH
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
ash:
uses: awslabs/automated-security-helper/.github/workflows/run-ash-security-scan.yml@v3.7.0
permissions:
contents: read
checks: write
pull-requests: write
with:
ash-version: v3.7.0
collect-sarif-report: false
What the parts do:
uses: ...@v3.7.0selects the workflow file, andash-version: v3.7.0selects the ASH it installs. Withoutash-version, the workflow installs ASH frommain, so give both the same release to run the ASH that the workflow's steps were written for.- The three permissions let the job read the code, publish a check run, and comment on the pull request.
- The workflow installs the tools that scanners on by default need from the runner:
grype, syft, opengrep, cfn-nag, actionlint, cfn-guard, gitleaks, and trivy. Each has an
install-*input that defaults totrue. collect-sarif-report: falsebecause step 4 uploads the SARIF report from a separate job, on every event. The workflow's own upload works on pull requests from branches of this repository only, and left on, it would upload the report a second time on those.- The
pushtrigger scansmainafter each merge. Code scanning compares a pull request with the analysis of its base branch, and the workflow saves its tool and database caches only from these runs.
If your default branch is not main, change the branch name.
Now pin the uses: line to a commit. ASH's zizmor scanner reports a uses: line that names
a tag as zizmor/unpinned-uses at HIGH, because a tag can be moved and a commit cannot, so
until you pin it every scan of this repository fails on this file. Look up the commit that
v3.7.0 points to:
In the uses: line, replace the v3.7.0 after the @ with the 40-character SHA that
the command prints, and add the comment # v3.7.0 at the end of the line. Set
ash-version to the same SHA, so the workflow file and the ASH it installs both come from
the commit of the v3.7.0 release.
2. Open a pull request and watch the scan¶
Commit the file, push the branch, and open a pull request. Under Checks, the job ash / SAST, SCA, and IaC Scan starts. It downloads the scanner tools and the vulnerability databases first, so expect it to take several minutes. Once the scan has written its reports, whether it passed or failed, a second job, ash / Report ASH results, posts them on the pull request.
When both finish, you see:
- A comment on the pull request with the ASH summary: one row per scanner, with its finding counts and a result of PASSED, FAILED, MISSING, SKIPPED, or ERROR. SKIPPED means the scanner was turned off or found nothing in the repository to scan.
- The same summary in the scan job's summary page.
- A check run named ASH Scan JUnit Test Report, which lists each actionable finding as a failed test.
- An artifact named
ash_outputwith every report, includingash.html.
If the job fails, the end of its log says why:
- A line such as
ERROR (2) Exiting due to 3 actionable findings found in ASH scanmeans ASH found something in your code. The comment and the JUnit check list what. ERROR (1) Exiting because the scan was incomplete, followed by scanners markedMISSING, means a scanner's tool is not on the runner, for example because itsinstall-*input isfalse.- Exit code 5 means ASH refused its command line, for example a mistyped option in
ash-args. The log names the option. - An error such as
the ash-args input holds a line break, another control character or the legacy command marker, so it is refused without being printed. A block scalar of one line is fine; more than one line is refused.means the input it names holds more than one line, another control character, a Unicode line or paragraph separator, or##[, and the job stopped before it installed anything. Writeash-argson one line; see How the workflow checks its inputs.
Exit codes lists the rest.
3. Choose what fails the build¶
By default the job fails when a scanner reports a finding at severity MEDIUM or above that is not suppressed, when a scanner did not run, or when a vulnerability database is past its age bound.
To fail only on HIGH and CRITICAL findings, add .ashrc.yaml at the root of the branch:
Other choices, each set under with: in the workflow:
fail-on-findings: falsereports findings without failing the job. A scan that did not complete still fails.ash-args: --exclude-scanners cfn-nagleaves a scanner out. Set its tool'sinstall-*input tofalsetoo, hereinstall-cfn-nag: false, so the job does not install it. For trivy,install-trivy: falsesaves its download of about 180 MB per run. An input set tofalsewhile its scanner is still on makes that scanner MISSING when the repository has files it scans, and the job fails with exit code 1.
To accept one finding instead, suppress it with a reason and an expiration date; see Suppress findings.
That config file is part of the repository, so a pull request that edits it changes the rules it is judged by: it can raise the threshold, add suppressions, or turn scanners off. To hold repositories to a gate that their own pull requests cannot change, see Roll out ASH across repositories. Set the gate covers every option.
4. Upload results to code scanning¶
Add a second job under jobs:, at the same indentation as ash:
sarif:
needs: ash
if: ${{ !cancelled() && needs.ash.outputs.artifact-id != '' }}
uses: awslabs/automated-security-helper/.github/workflows/upload-ash-sarif.yml@v3.7.0
permissions:
contents: read
security-events: write
Pin its uses: line to the same commit as the first one, with the same # v3.7.0
comment.
This job downloads the ash_output artifact and uploads ash.ghas.sarif to code scanning.
It needs security-events: write, which the scan job cannot hold: a called workflow can
only narrow the permissions its caller grants, and the scan workflow does not ask for this
one. The scan sets its artifact-id output once it has uploaded its reports, which it does
when it fails on findings too, so the if: uploads after such a scan and skips the upload
after a scan that stopped before ASH wrote its reports. Without !cancelled() in it,
GitHub skips the job whenever the scan job fails.
Push the change, then merge the pull request. The merge starts the workflow on main, and
when that run finishes, the repository's Security and quality tab lists ASH's findings
under Code scanning, with the tool name ASH - Automated Security Helper. That analysis
of main is what code scanning compares later pull requests with.
5. See a finding on a pull request¶
Create a new branch from main and add a file with a known problem, for example
ash_demo.py:
Push it and open a pull request. This time:
- The scan job fails with exit code 2: bandit reports the
subprocesscall withshell=True. - The pull request comment shows bandit as FAILED, with its findings counted by severity.
- The ASH Scan JUnit Test Report check is red and names the finding.
- The Code scanning results check annotates the line in Files changed. Code scanning shows a pull request only the alerts on lines that the pull request changes.
Delete the file and push again, and the checks pass, provided main itself had no
actionable findings.
Next steps¶
- Run ASH in GitHub Actions: every input of the reusable workflow, writing the steps yourself, caching, and changed-files scans.
- Read scan results: what each report file is for.
- Choose scanners: turn scanners on and off.
- Scale to large repositories: split a scan across runners.