WASViking Docs
⌘K
Sentinel agent

wasviking-sentinel in CI/CD

Drop the same Go binary in your pipeline for SBOM, secrets, mobile assessment, and policy-driven cloud scans. Independent gates, one preflight, deterministic exit codes.

The same wasviking-sentinel binary that opens the mTLS tunnel also runs as a one-shot tool in CI/CD pipelines. In a CI/CD context the agent does not need to be registered (no mTLS bootstrap, no certificates). It authenticates with an organization API key.

Three independent gates ship today, each a top-level subcommand:

Subcommand Gate OWASP
wasviking-sentinel sbom Vulnerable components (SCA). A06
wasviking-sentinel secrets Hard-coded credentials. A07 / CWE-798
wasviking-sentinel scan Cloud DAST, template-driven. varies

Each subcommand is documented in detail on its own page:

For ready-to-paste recipes, see CI/CD DAST with GitHub Actions, CI/CD SCA, SBOM & Secrets with GitHub Actions, and CI/CD SCA, SBOM & Secrets with Bitbucket Pipelines.

Quick start (GitHub Actions)

# .github/workflows/security.yml
- name: WASViking · SCA
  run: ./wasviking-sentinel sbom --path . --fail-on high --submit
  env:
    WASV_API_KEY: ${{ secrets.WASVIKING_CI_KEY }}

- name: WASViking · Secrets
  run: ./wasviking-sentinel secrets --path . --verify --submit
  env:
    WASV_API_KEY: ${{ secrets.WASVIKING_CI_KEY }}

- name: WASViking · Cloud DAST
  run: ./wasviking-sentinel scan --template prod-web-strict --target https://staging.example.com
  env:
    WASV_API_KEY: ${{ secrets.WASVIKING_CI_KEY }}

The same three commands run on Bitbucket Pipelines (worked example in CI/CD SCA, SBOM & Secrets with Bitbucket Pipelines), GitLab CI, CircleCI, Jenkins, and Buildkite. Any Linux runner that can execute the binary works.

Authentication

export WASV_API_KEY=wv_live_xxxxxxxxxxxxxxxxxxxxxxxx
./wasviking-sentinel version

API keys are created in the portal under Settings → API Keys. The key needs the right scopes for what you actually do:

Subcommand Required scopes
sbom (license check only) any active org key
sbom --submit sca:submit
secrets (license check only) any active org key
secrets --submit secrets:submit
scan scans:run, templates:read

Use a dedicated CI key with only the scopes you need.

License check (preflight)

Every sbom, secrets, and CI-side scan invocation calls POST /api/v1/sentinel/preflight before doing any work. The preflight is mandatory: an empty or rejected API key blocks the run.

  • --api-key (or env WASV_API_KEY) is required, even when you are not submitting results.
  • Successful approvals cache at ~/.wasviking/preflight_cache.json (mode 0600) for 30 minutes.
  • 24-hour grace window if the API is unreachable but a recent approval exists on disk.
  • Active rejection (401 / 403) does not get the grace window: revoked keys block on the next cache expiration.
  • Cache invalidates on key rotation (cache key is a truncated SHA-256 of the API key).

For the full preflight model, see the License check section on the sbom page.

SCA gate (sbom)

./wasviking-sentinel sbom \
  --path . \
  --app-name checkout-api \
  --app-version "$CI_COMMIT_TAG" \
  --fail-on high \
  --submit

Behavior:

  1. Walk manifests under --path (recursive).
  2. Build a CycloneDX 1.5 SBOM.
  3. Enrich with OSV.dev advisories and CISA KEV.
  4. Apply --fail-on policy.
  5. If --submit, POST to the WASViking API.

Exit codes:

Exit code Meaning
0 OK. Nothing at or above --fail-on.
1 Generic failure (parse, IO, network).
2 Invalid argument, or --submit without an API key.
70 KEV-flagged finding at or above --fail-on.
71 Findings at or above --fail-on, none of them in KEV.
79 Coverage failure. See below.

A failed OSV enrichment or a failed submission warns and keeps going. Neither one fails the build on its own.

Default threshold: high. Full flag reference: wasviking-sentinel sbom.

Secrets gate (secrets)

./wasviking-sentinel secrets \
  --path . \
  --git \
  --verify \
  --fail-on high \
  --submit

Behavior:

  1. Walk the working tree under --path.
  2. With --git, also walk the local git history.
  3. Match against 32 detectors.
  4. With --verify, probe provider identity endpoints (read-only) for the 10 detectors that support live verification.
  5. Apply --fail-on policy.
  6. If --submit, POST to the WASViking API (hash + masked preview; raw secrets never leave the host).

Exit codes:

Exit code Meaning
0 OK. Nothing at or above --fail-on.
1 Generic failure.
2 Invalid argument, or --submit without an API key.
73 A credential at or above --fail-on that --verify confirmed live.
74 Matches at or above --fail-on that were not verified.
79 Coverage failure. See below.

Default threshold: high. Full flag reference: wasviking-sentinel secrets.

Cloud DAST gate (scan)

./wasviking-sentinel scan \
  --template prod-web-strict \
  --target https://staging.example.com

The scan subcommand triggers a cloud scan using an org-scoped scan template. The template is resolved server-side, so secrets configured in the template never reach the CI runner. The binary tracks the scan to completion and exits with a policy-aware code.

Common exit codes:

Exit code Meaning
0 Scan completed, findings under the threshold.
1 Findings at or above the threshold, or an unmapped failure (provisioning rejected, engine error, timeout).
2 Invalid argument, for example a --baseline other than all or new.
70 Scan template not found for this organization.
71 Scan template not accessible to this organization.
79 Coverage failure on the local --sca or --secrets pass. See below.

Default threshold: critical.

scan can also run the local SCA and secrets gates in the same pass, with --sca and --secrets. Those run before the cloud scan starts and keep their own codes (70, 71, 73, 74). On a run with --sca, a 70 means a KEV-flagged dependency, not a missing template.

scan is the cloud counterpart of the local sbom and secrets gates. It does not require the mTLS agent to be registered.

Coverage failures (exit 79)

A gate that scans nothing will find nothing, and without a check that looks exactly like a clean run. Exit 79 exists to keep those two apart.

The agent reads the scan root before walking it, then compares what it covered against what was there. When the root holds entries and the walk reached none of them, the run stops with exit 79 instead of reporting a pass. The same applies when the root cannot be listed at all, usually a permission problem on the runner.

What exit 79 is not: a finding, and a complaint about an empty project. A repository with no files, or one whose entire content sits in excluded directories such as node_modules, walks correctly and passes. So does a project with no supported manifest, which reports how many entries it walked so you can see the difference.

Common causes, in the order worth checking:

Cause What to do
The path does not point where you think it does. Check the Root: line in the log against the repository layout.
The build step lacks permission to read the checkout. Fix the ownership or mode on the runner.
The path is a mount or volume that is not populated yet. Move the gate after the step that populates it.

Directory exclusions never cause exit 79. They apply to sub-directories only, so a project checked out into a directory named build, dist, target or vendor is scanned normally. When that happens the log says so explicitly.

Pointing a path at a .git directory is refused up front with a message pointing at --git, which is the flag that scans commit history.

Concurrency and metering

For the scan subcommand, the platform enforces two limits per organization:

  • A concurrent-scan cap. Provisioning is rejected with HTTP 429 once the org's in-flight CI scans reach the cap.
  • A monthly metering window. Starting a scan is rejected with HTTP 402 once the meter is exhausted.

The agent neither queues nor retries in either case. The rejection reaches the build log and the step exits 1, so a capped pipeline fails fast instead of holding a runner for nothing. Both events are surfaced in the portal's CI Usage tab.

What you should pin in CI

  • Binary version. Distribute a specific signed release and verify with cosign before running.
  • Template slug for scan. Templates are versioned server-side; the slug always resolves to the active version of the template.
  • API key scopes. Use the minimum scope per pipeline stage.
  • --fail-on thresholds per pipeline lane (strict on main, permissive on feature branches).

What this binary is NOT in CI mode

  • It does not open the mTLS tunnel. run is the tunneling mode; sbom, secrets, and scan are one-shot CI subcommands.
  • It does not call the portal's session login. The API key is the credential.
  • It does not store findings locally beyond the SBOM / report artifacts in --out. Persistence is portal-side after --submit.