A repo holding CI workflows to run Snyk container tests on the Docker images running in, or being deployed to, cyber-dojo's aws-beta and aws-prod runtime environments.
- A snyk container scan produces a sarif output file, with ignored vulnerabilities from the .snyk policy file already filtered out. We'd prefer a complete picture of all the vulnerabilities.
- The
kosli attest snykcommand creates a non-compliant attestation for any new attestation not in the .snyk file. There are fairly frequent bursts of new low-severity vulnerabilities and we would like to control whether these block the main development workflow.
The Snyk scan runs without the .snyk policy file, so all vulnerabilities are visible
regardless of any .snyk file ignore entries. The .snyk file is only applied during compliance evaluation.
Each individual vulnerability found in a running artifact is evaluated as follows:
- If it has an
ignoreentry in the artifact's.snykfile, it is honoured and treated as compliant. The exception is an ignore entry whose expiry date has passed, in which case it becomes non-compliant immediately. - If it has no
ignoreentry, compliance depends on how long that vulnerability has been present in the artifact running in the given environment. The allowed number of days before non-compliance is set per severity inrego.params.{env}.json. See below.
The top-level generic artifact-level attestation is controlled by the attest_to_kosli input, which defaults to true when the caller's workflow is on main.
The inner trail-level attestations (one per Snyk vulnerability) always take place. A single
kosli evaluate input call then judges every vulnerability found in the artifact at once. The
artifact-level attestation carries that verdict, plus one annotation per vulnerability whose key
says pass or fail and whose value links to that vulnerability's own attestation.
Triggered daily, manually via workflow_dispatch, or on a push
to main that changes snyk-vuln-compliance.rego or the relevant
rego.params.{env}.json. Calls repo_test.yml first, so a policy change that
fails this repo's own tests never reaches a live compliance decision. Then calls
env_snyk_test.yml for the target environment, and finally
check-expiry-and-notify.yml to report on upcoming compliance expirations via
Slack.
| Workflow | Kosli flow (per-artifact) | Kosli flow (per-vuln) |
|---|---|---|
aws-beta.yml |
snyk-aws-beta-per-artifact |
snyk-aws-beta-per-vuln |
aws-prod.yml |
snyk-aws-prod-per-artifact |
snyk-aws-prod-per-vuln |
The per-artifact flow holds one trail per artifact currently running in the environment. Trail
names have the form {repo_name}-{artifact_fingerprint}. Each trail contains one decision
artifact-level attestation named {repo_name}.snyk-container-scan with the sarif output, Rego
policy file, Rego params file, .snyk policy file, the JSON handed to the policy, and the
policy's verdict attached.
The per-vuln flow holds one trail per vulnerability found across all scanned artifacts. Trail
names have the form {repo_name}-{severity}-{snyk_id}, where snyk_id is the Snyk rule id
(for example SNYK-ALPINE322-ZLIB-16078399), not a CVE id. Each trail contains one custom
attestation of type single-snyk-vuln, named snyk-{first 10 characters of the artifact fingerprint}. That attestation holds the data the compliance decision is made from, and its URL
is what the matching annotation on the per-artifact attestation links to.
Each environment has a rego.params.{env}.json file that sets the maximum number of days a
vulnerability may exist in that environment before it is considered non-compliant, by severity. aws-prod has a slightly stricter limit for critical vulnerabilities (0 days, so any critical vuln is immediately non-compliant), reflecting the higher risk of a production environment.
Example rego.params.aws-prod.json:
{
"max_days_by_severity":
{
"critical": 0,
"high": 2,
"medium": 4,
"low": 10
}
}What this means: when a new low severity vulnerability appears in aws-prod then you have 10 days to either fix it, or to add entries to the relevant .snyk files.
Called by aws-beta.yml and aws-prod.yml. Queries Kosli for the artifacts currently running in the environment and fans out to artifact_snyk_test.yml via a matrix strategy.
Inputs
| Name | Required | Description |
|---|---|---|
kosli_env |
yes | Name of the Kosli environment to scan |
kosli_flow |
yes | Name of the Kosli flow to attest evidence in |
Called by aws-beta.yml and aws-prod.yml after the environment scan completes.
Downloads all per-vulnerability artifact files produced during the current run,
identifies the soonest-expiring vulnerability, and sends a Slack message
summarising it. Also writes a step summary to the GitHub Actions run page.
Inputs
| Name | Required | Description |
|---|---|---|
kosli_env |
yes | Name of the Kosli environment that was scanned |
Secrets
| Name | Required | Description |
|---|---|---|
SLACK_WEBHOOK_URL |
yes | Slack incoming webhook URL |
Called by env_snyk_test.yml to scan artifacts running in an environment.
Also called by deployment workflows in the cyber-dojo/aws-prod-co-promotion repo.
For example, aws-prod-co-promotion/.github/workflows/promote_one.yml:
Also called directly by build workflows in other repos to scan a newly built artifact. For example, nginx/.github/workflows/main.yml:
Example use
jobs:
...
snyk-container-scan:
needs: [build-image]
uses: cyber-dojo/snyk-scanning/.github/workflows/artifact_snyk_test.yml@main
with:
artifact_name: ${{ needs.build-image.outputs.tagged_image_name }}
kosli_flow: ${{ vars.KOSLI_FLOW }}
kosli_trail: ${{ github.sha }}
kosli_attestation_name: nginx.snyk-container-scan
secrets:
snyk_token: ${{ secrets.SNYK_TOKEN }}
kosli_api_token: ${{ secrets.KOSLI_API_TOKEN }}Runs a Snyk container test against a single artifact, evaluates the results against a Rego
compliance policy, and makes an artifact-level attestation in Kosli. Attaches the sarif output, the Rego policy file, the Rego params file, the .snyk policy file, the JSON handed to the policy, and the policy's verdict to the attestation.
Inputs
| Name | Required | Default | Description |
|---|---|---|---|
aws_rolename |
no | gh_actions_services |
IAM role for ECR login |
artifact_name |
yes | OCI artifact to scan (image name with tag) | |
kosli_flow |
yes | Kosli flow to attest to | |
kosli_trail |
no | ${{ github.sha }} |
Kosli trail to attest to |
kosli_attestation_name |
yes | Kosli attestation name | |
kosli_env |
no | aws-beta |
Environment the artifact is deployed-in/deploying-to |
repo_name |
no | repository name | Repo the artifact was built in |
snyk_version |
no | v1.1300.2 |
Version of Snyk CLI to use |
raw_snyk_policy_url |
no | .snyk at ${{ github.sha }} |
URL of the .snyk policy file for the artifact's commit |
attest_to_kosli |
no | true on main |
Whether to record the generic artifact-level attestation |
Secrets
| Name | Required | Description |
|---|---|---|
snyk_token |
yes | Snyk API token |
kosli_api_token |
yes | Kosli API token |
Outputs
| Name | Description |
|---|---|
vulns_json |
JSON array of vulnerability objects found for the artifact |
Tests this repo's own code: the Rego policy, the bin/ scripts, and the
single-snyk-vuln attestation-type schema. It scans nothing; the Snyk Test
workflows above scan artifacts.
Runs on every pull request and on pushes to main, and is also called as a
gating job by aws-beta.yml and aws-prod.yml. Every service repo calls
artifact_snyk_test.yml@main, so main is the live deploy target for all of
them at once, which makes green-before-merge the only real gate.
Takes no inputs and needs no secrets: kosli evaluate input, which
test_rego_rules.sh shells out to, evaluates a policy locally, so the workflow
also runs on pull requests from forks.