WASViking Docs
⌘K
Getting Started

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

  1. The runner installs the Sentinel agent using the Sentinel token.
  2. The agent scans the project manifests and builds a CycloneDX SBOM.
  3. It enriches components against OSV and applies CISA KEV validation.
  4. It submits the SBOM (and any redacted secret matches) to the WASViking API over HTTPS REST.
  5. The API validates quota, registers the SBOM snapshot, detects component drift, and promotes findings.
  6. 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-gapped guarantees 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-sentinel binary 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.

Add Sentinel Agent dialog with the Agent display name field Sentinel → Sentinel Agents → Add Sentinel Agent.

Sentinel agent created, showing the one-time bootstrap token 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, not Bearer. 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.

System Settings, API Keys tab, with the key list Settings → System Settings → API Keys.

API Key scope picker with sca:submit and secrets:submit selected 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