CI/CD SCA, SBOM & Secrets with GitHub Actions
Generate a CycloneDX SBOM and scan for hard-coded secrets in your GitHub Actions pipeline with the WASViking Sentinel. Enriches with OSV and CISA KEV, submits over HTTPS, fails the build on known-vulnerable dependencies or leaked credentials, and publishes to the GitHub Security tab.
This integration runs Software Composition Analysis (SCA) and a secrets scan inside your GitHub Actions runner. The WASViking® Sentinel reads your dependency manifests, builds a CycloneDX SBOM, enriches it with OSV and CISA KEV, scans the tree for hard-coded credentials, and fails the pipeline on known-vulnerable dependencies or leaked secrets before merge.
Unlike the DAST flow, there is no mTLS tunnel: the SBOM and secret matches are submitted over plain HTTPS REST with a bearer API Key. No source code leaves the runner, only the dependency graph (package names and versions) and redacted secret matches.
The setup has two halves, and the order matters: configure the
WASViking portal first (a Sentinel agent token and an API Key scoped
to sca:submit and secrets:submit), then wire those into
GitHub Actions.
What this integration does
- Generates a CycloneDX 1.5 SBOM from your manifests (npm, pip, go, composer, Maven, gem) on every push and pull request.
- Enriches components with OSV and CISA KEV (known exploited vulnerabilities).
- Scans the working tree for hard-coded secrets and submits redacted matches.
- Fails the build by severity with
--fail-on, blocking vulnerable releases before merge. - Emits SARIF 2.1.0 for GitHub Code Scanning, plus the raw CycloneDX JSON.
- Builds a consolidated software inventory per organization and detects drift between consecutive submissions.
- Provisions and tears down the agent per run, with no persistent credentials on the runner.
- Supports air-gapped mode for networks with no external egress.
How it works
- The runner installs the Sentinel agent using the Sentinel token.
- The agent scans the project manifests and builds a CycloneDX SBOM.
- It enriches components against OSV and applies CISA KEV validation.
- It submits the SBOM (and any redacted secret matches) to the WASViking API over HTTPS REST.
- The API validates quota, registers the SBOM snapshot, detects component drift, and promotes findings.
- The agent writes the SARIF and JSON outputs, and the workflow uploads the SARIF to GitHub Code Scanning.
Access posture
- Only manifests and the dependency graph are read. No source code,
runner environment variables (beyond the keys you pass), or files
outside
.wasviking/are collected. - Raw secret values never leave the runner; only redacted matches are submitted.
- Submission is HTTPS REST with a bearer API Key (no mTLS tunnel).
--air-gappedguarantees zero external network egress (no OSV lookup, no submission).- Revoke access at any time by revoking the API Key in the portal.
Pre-requisites
| Requirement | Detail |
|---|---|
| WASViking plan | SBOM submissions enabled (Pro or higher). |
| Portal role | Admin or Manager, to issue API Keys and Sentinel tokens. |
| GitHub repository | Permission to create Actions secrets and files under .github/. |
| Workflow permissions | contents: read and security-events: write (for the SARIF upload). |
| Project manifests | At least one supported lockfile: package-lock.json, requirements.txt, Pipfile.lock, go.sum, composer.lock, pom.xml, Gemfile.lock. |
| Runner | GitHub-hosted (ubuntu-latest recommended) or self-hosted Linux x86_64. |
| Network egress | HTTPS to api.wasviking.com, api.osv.dev, and github.com on 443. No inbound is needed. |
Not on GitHub Actions? The same gates run on any CI system where the
wasviking-sentinelbinary can execute. Start from wasviking-sentinel in CI/CD.
Step 1: Create a Sentinel agent token (portal)
The runner needs a token to download and register the agent.
In the portal, go to Sentinel → Sentinel Agents and click Add Sentinel Agent.
| Field | Value |
|---|---|
| Agent display name | A label shown in the portal, unique in your org (for example, Production CI/CD Bootstrap). This is not the machine hostname; you can rename it later. |
Click Create. The portal shows a RAW bootstrap token once.
Copy it and store it safely. You will paste it into GitHub in Step 3 as
WASV_SENTINEL_API_KEY. If you lose it, create another agent and use
the new token.
Sentinel → Sentinel Agents → Add Sentinel Agent.
The bootstrap token is shown only once. Copy it before closing.
Step 2: Create an API Key scoped to sca:submit and secrets:submit (portal)
This is the credential the SBOM and secrets submissions run under, separate from the agent token.
Go to Settings → System Settings → API Keys and click + New Key.
| Field | Value |
|---|---|
| Label | Something identifiable, for example GitHub Actions SCA pipeline. Use one key per repository so you can revoke it without affecting other pipelines. |
| Scopes | Select sca:submit (Send SBOMs from the Sentinel agent. OWASP A06) and secrets:submit (Send hard-coded credential matches from the Sentinel agent. OWASP A07). Add only what this pipeline needs. |
| Expiration | 90 days is a sensible default; rotate on that cadence. |
Save and copy the key once. You will paste it into GitHub in Step 3
as WASV_SCA_API_KEY. The same key carries both scopes, so it covers
the SBOM and the secrets scan.
WASViking authenticates with the
Authorization: ApiKey <key>header, notBearer. You can review or adjust a key's scopes later with Edit; that keeps the same key value, so the pipeline keeps working without re-issuing the secret.
Settings → System Settings → API Keys.
Select sca:submit and secrets:submit for an SCA + secrets pipeline key.
Step 3: Add the GitHub secrets
Never commit a key to the repository. Store them as encrypted Actions secrets.
In GitHub, go to the repository's Settings → Secrets and variables → Actions → Secrets and add:
| Name | Value |
|---|---|
WASV_SENTINEL_API_KEY |
The bootstrap token from Step 1. |
WASV_SCA_API_KEY |
The API Key from Step 2 (carries sca:submit and secrets:submit). |
Use Secrets (encrypted), never Variables, for the keys. For multiple environments (staging, production), prefer Environment secrets with required reviewers and deployment protection rules.
Step 4: Add the workflow
Create .github/workflows/wasviking-sca.yml:
name: WASViking SCA
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
security-events: write
actions: read
jobs:
sca:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install WASViking Sentinel
env:
WASV_SENTINEL_API_KEY: ${{ secrets.WASV_SENTINEL_API_KEY }}
run: |
curl -sSL -H "Authorization: ApiKey $WASV_SENTINEL_API_KEY" \
https://api.wasviking.com/api/v1/sentinel/install.sh | sh
- name: Run WASViking SBOM (SCA)
env:
WASV_SCA_API_KEY: ${{ secrets.WASV_SCA_API_KEY }}
run: |
mkdir -p wasviking-reports
./.wasviking/wasviking-sentinel sbom \
--api-key "$WASV_SCA_API_KEY" \
--path . \
--app-name "${{ github.repository }}" \
--app-version "${{ github.sha }}" \
--fail-on critical \
--submit \
--out ./wasviking-reports
- name: Run WASViking secrets scan
env:
WASV_SCA_API_KEY: ${{ secrets.WASV_SCA_API_KEY }}
run: |
./.wasviking/wasviking-sentinel secrets \
--api-key "$WASV_SCA_API_KEY" \
--path . \
--fail-on high \
--submit \
--out ./wasviking-reports
- name: Upload SARIF to GitHub code scanning
if: always() && hashFiles('wasviking-reports/*.sarif') != ''
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: wasviking-reports
category: wasviking-sca
- name: Upload SBOM artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: wasviking-sbom
path: wasviking-reports/
Code Scanning upload (the SARIF step) requires GitHub Advanced Security on private repositories. If you do not have it, drop that step and rely on the uploaded artifact and the portal instead.
Step 5: Command flags
sbom (SCA / SBOM)
wasviking-sentinel sbom [flags]
| Flag | Required | Description |
|---|---|---|
--api-key |
Yes | API Key with the sca:submit scope. |
--path |
No | Directory scanned recursively for manifests. Default: current directory. |
--app-name |
No | Project name embedded in the CycloneDX metadata. Default: directory name. |
--app-version |
No | Project version in the metadata. Use ${{ github.sha }}. |
--fail-on |
No | Single threshold: critical, high, medium, low, or none. "Or above" logic. Default: critical. |
--submit |
No | Submit the SBOM to WASViking. Omit for a local-only run. |
--out |
No | Output directory for the CycloneDX JSON and SARIF. Default: current directory. |
--air-gapped |
No | Fully offline. No OSV lookup and no submission. |
Outputs: wasviking-sbom.cdx.json (CycloneDX 1.5) and
wasviking-sbom.sarif (SARIF 2.1.0).
secrets
wasviking-sentinel secrets [flags]
Scans the tree for hard-coded credentials and submits redacted
matches (raw secrets never leave the runner). Takes the same
--api-key (with secrets:submit), --path, --fail-on, --submit,
and --out flags. Results land in the portal under
Inventory → Secrets.
Exit codes
| Code | Meaning |
|---|---|
0 |
Success. Nothing at or above the --fail-on threshold. |
70 / 71 |
Vulnerable dependency at or above the threshold. Blocks the merge. |
72 / 73 / 74 |
Hard-coded secret found at or above the threshold. Blocks the merge. |
1 |
Operational error (invalid manifest, network error on --submit). |
Fail-on policy
--fail-on sets the lowest severity that fails the build; everything
above it also fails.
| Setting | Behavior |
|---|---|
--fail-on none |
Never fails. Report only. |
--fail-on critical |
Fails on critical only. |
--fail-on high |
Fails on high and critical. A good default for PRs. |
--fail-on medium |
Fails on medium and above. Stricter, more friction. |
--fail-on low |
Maximum posture. Use on new projects with no accumulated debt. |
A practical rollout: start at high, then tighten to medium once your
dependency baseline is stable (typically a couple of sprints).
Air-gapped mode
Regulated environments (defense, government, classified healthcare) often forbid external egress. Run the SBOM fully offline:
wasviking-sentinel sbom \
--path . \
--air-gapped \
--out ./wasviking-reports
--air-gapped skips the OSV lookup and the submission, using only the
bundled KEV data. Use it for classified networks with no egress, for
local validation before manually shipping an artifact, or for isolated
build farms.
What is and isn't collected
WASViking does not collect your repository source code, runner
environment variables beyond the keys you pass, runner filesystem
contents outside .wasviking/, or any production data. The SBOM holds
only the dependency graph (package names, versions, licenses, manifest
hashes); the secrets scan submits only redacted matches. Data is
processed in the US region, encrypted in transit (TLS 1.2+) and at rest
(AES-256), with retention set by your plan. A DPA and EU data residency
are available on request; see the
Trust Center for the full
compliance mapping (ISO 27001, SOC 2, LGPD, GDPR, NIST SSDF,
OWASP Top 10 A06/A07, OWASP DSOMM, CISA SBOM minimum elements).
Common problems
| Problem | Likely cause |
|---|---|
HTTP 401 Unauthorized |
API Key revoked, expired, or missing sca:submit / secrets:submit. WASViking uses Authorization: ApiKey <key>, not Bearer. |
HTTP 402 quota exceeded |
Monthly SBOM submission quota reached. Wait for the cycle, upgrade, or buy an add-on pack. |
HTTP 413 payload too large |
SBOM over 100 MiB (rare). Reduce with --no-osv or split the monorepo. |
manifest not detected |
None of the supported lockfiles found under --path. |
| OSV timeout | api.osv.dev slow or unreachable. Use --no-osv for a bare SBOM, or --air-gapped. |
| Findings differ between runs | Non-deterministic lockfile (for example, package-lock.json regenerated without npm ci). Always commit lockfiles. |
install.sh download error |
Network policy blocking api.wasviking.com or the release bucket. |
| Unexpected exit 70 on a small PR | A new transitive dependency arrived via the lockfile and matches CISA KEV. Inspect with npm ls <package> or the equivalent. |
Drift detected: yes every run |
--app-version changes per commit (${{ github.sha }}), which is expected. For real component drift, pin --app-version to the release. |
Where this fits in the platform
- The software inventory lives under Inventory → SBOM; secret matches under Inventory → Secrets.
- The DAST counterpart is CI/CD DAST with GitHub Actions.
- Alert routing is documented under Slack and Teams and Webhooks.
