CI/CD Mobile Security with GitHub Actions
Assess the Android or iOS package your build produces inside your GitHub Actions pipeline with the WASViking Sentinel. Uploads the artefact over HTTPS, runs the static assessment against OWASP MASVS and MASTG, fails the build on new findings with a baseline diff, and publishes results to the GitHub Security tab.
This integration submits the mobile application package your build produces to the WASViking® Mobile Security Assessment, from inside your GitHub Actions runner, and fails the pipeline when the release carries findings you care about. The package is analysed statically against the OWASP Mobile Application Security Verification Standard (MASVS) and the Mobile Application Security Testing Guide (MASTG). Nothing is installed on a device and nothing is executed.
Unlike the DAST flow, there is no mTLS tunnel and no running app to reach: the artefact is a file. The runner uploads it straight to secure object storage with a single-purpose authorisation, so the bytes never pass through the API, and the assessment runs on the same engine the portal uses.
The setup is short. You need one thing in the portal (an API Key scoped
to mobile:scan) and one thing in GitHub Actions (that key as a
secret). The same key installs the agent and runs the assessment, so there
is no separate agent token to manage.
What this integration does
- Assesses the
.apk,.aab, or.ipayour pipeline builds, on every push or pull request. - Fails the build by severity with
--fail-on, so a release that regresses is blocked before merge. - Compares against the previous assessment of the same application with
--baseline new, so a build only fails on findings it introduces, not on pre-existing debt. - Emits SARIF 2.1.0, consumed natively by GitHub Code Scanning, plus a full JSON summary.
- Records who built what: the pipeline stamps the provider, repository, branch, commit, and run so every assessment is traceable to a build.
- Meters against your monthly CI mobile assessment allowance and lists every run in the portal, tagged as a pipeline submission.
How it works
- The runner installs the Sentinel agent using your
mobile:scankey. - The agent asks the API to authorise one upload and receives a short-lived, single-purpose authorisation for one object.
- The agent uploads the package straight to secure object storage. The bytes do not pass through the API or the CDN.
- The API verifies the stored object, confirms it is a real application package, and queues the assessment against your organization.
- The engine runs its checks (configuration, transport security, cryptography, local storage, platform interaction, binary hardening, third-party components, embedded credentials, and tracking SDKs) and scores the result.
- The agent writes
wasviking-mobile.sarifandwasviking-mobile.json, and the workflow uploads the SARIF to GitHub Code Scanning.
Access posture
- Only the application package you name with
--fileis uploaded. No source code, no runner environment variables beyond the key you pass, and no files outside the working directory are collected. - The upload authorisation is single-purpose and short-lived, and pins the exact object; a tampered authorisation is refused.
- Submission is HTTPS with an API Key. WASViking authenticates with the
Authorization: ApiKey <key>header, notBearer. - Revoke access at any time by revoking the API Key in the portal.
Pre-requisites
| Requirement | Detail |
|---|---|
| WASViking module | Mobile Security Assessment enabled for your organization, with a CI assessment allowance provisioned. It is an add-on; talk to your account contact or partner if it is not yet enabled. |
| Portal role | Admin or Manager, to issue API Keys. |
| GitHub repository | Permission to create Actions secrets and files under .github/. |
| Workflow permissions | contents: read and security-events: write (for the SARIF upload). |
| Build artefact | A .apk, .aab, .xapk, .apks, or .ipa produced by an earlier step in the job. |
| Runner | GitHub-hosted (ubuntu-latest recommended) or self-hosted Linux x86_64. |
| Network egress | HTTPS to api.wasviking.com, the object storage endpoint it returns, and github.com on 443. No inbound is needed. |
Not on GitHub Actions? The same gate runs on any CI system where the
wasviking-sentinelbinary can execute. See wasviking-sentinel mobile.
Step 1: Create an API Key scoped to mobile:scan (portal)
This one key both downloads the agent and runs the assessment.
Go to Settings → System Settings → API Keys and click + New Key.
| Field | Value |
|---|---|
| Label | Something identifiable, for example GitHub Actions mobile pipeline. Use one key per repository so you can revoke it without affecting other pipelines. |
| Scopes | Select mobile:scan only (Submit mobile app packages (APK/IPA) for assessment from a CI/CD pipeline). Least privilege: do not add scopes the pipeline does not need. |
| 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 2 as
WASV_MOBILE_API_KEY.
You can review or adjust a key's scopes later with Edit on the key row. Editing scopes keeps the same key value, so the pipeline keeps working without re-issuing the secret.
Step 2: Add the GitHub secret
Never commit a key to the repository. Store it as an encrypted Actions secret.
In GitHub, go to the repository's Settings → Secrets and variables → Actions → Secrets and add:
| Name | Value |
|---|---|
WASV_MOBILE_API_KEY |
The API Key from Step 1. |
Use Secrets (encrypted), never Variables, for the key. For multiple environments (staging, production), prefer Environment secrets with required reviewers and deployment protection rules.
Step 3: Add the workflow
Create .github/workflows/wasviking-mobile.yml. The example builds an
Android APK; replace the Build the app step with however your project
produces its package, and point --file at the artefact.
name: WASViking Mobile Security
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
security-events: write
actions: read
jobs:
mobile-assessment:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@v4
# Replace with your own build. The result must be an .apk, .aab,
# .xapk, .apks, or .ipa on disk.
- name: Build the app
run: ./gradlew assembleRelease
- name: Install WASViking Sentinel
env:
WASV_MOBILE_API_KEY: ${{ secrets.WASV_MOBILE_API_KEY }}
run: |
curl -sSL -H "Authorization: ApiKey $WASV_MOBILE_API_KEY" \
https://api.wasviking.com/api/v1/sentinel/install.sh | sh
- name: Run WASViking mobile assessment
env:
WASV_API_KEY: ${{ secrets.WASV_MOBILE_API_KEY }}
run: |
mkdir -p wasviking-reports
./.wasviking/wasviking-sentinel mobile \
--file app/build/outputs/apk/release/app-release.apk \
--label "${{ github.repository }}@${{ github.sha }}" \
--fail-on high \
--baseline new \
--out ./wasviking-reports
- name: Upload SARIF to GitHub code scanning
if: always() && hashFiles('wasviking-reports/wasviking-mobile.sarif') != ''
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: wasviking-reports/wasviking-mobile.sarif
category: wasviking-mobile
- name: Upload raw reports as artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: wasviking-mobile-reports
path: wasviking-reports/
The assessment command reads the key from
WASV_API_KEY, so you do not have to repeat--api-keyon the command line. 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 4: Command flags
wasviking-sentinel mobile [flags]
| Flag | Required | Description |
|---|---|---|
--file |
Yes | Path to the application package (.apk, .aab, .xapk, .apks, .ipa). |
--api-key |
Yes | API Key with the mobile:scan scope. Prefer the WASV_API_KEY env var so the key never lands in the process argv or CI logs. |
--label |
No | A label stored with the assessment, for example the release name or the commit. |
--fail-on |
No | Single threshold: critical, high, medium, low, or none. "Or above" logic: --fail-on high fails on high and critical. Default: critical. |
--baseline |
No | new (only findings absent from the previous assessment of the same app count) or all (total posture). Default: all. |
--out |
No | Output directory for SARIF and JSON. Default: current directory. |
--timeout |
No | Total budget for upload plus analysis. Default: 40m. |
--api |
No | API base URL. Default: https://api.wasviking.com. Also read from WASV_API. |
Two files are produced:
wasviking-mobile.sarif: SARIF 2.1.0 (GitHub Code Scanning, GitLab, and others).wasviking-mobile.json: the run summary with severity counts, risk score, and provenance.
Exit codes
| Code | Meaning |
|---|---|
0 |
Success. Nothing at or above the --fail-on threshold (given the chosen baseline). |
1 |
Findings exceed the threshold. Blocks the merge. |
2 |
Operational error (missing package, invalid arguments, authentication or upload failure). |
Fail-on policy
--fail-on sets the lowest severity that fails the build, and 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 pull requests. |
--fail-on medium |
Fails on medium and above. Stricter, more friction. |
A practical rollout: start at critical to keep early friction low, then
tighten to high once your baseline is clean.
Baseline diff
--baseline controls what counts toward the fail-on policy. The baseline is
the previous completed assessment of the same application (same platform
and package identifier), whether that earlier run came from a pipeline or a
manual upload in the portal. The comparison is by finding identity, so a
version bump does not reset it.
| Mode | Behavior |
|---|---|
all (default) |
Every finding counts. Use for release branches and audits, to gate on the total posture. |
new |
Only findings absent from the previous assessment count. Use for day-to-day pull requests, so a build fails on what it introduces, not on debt it inherited. |
On the first assessment of an application there is nothing to compare
against, so new gates on the full set for that run and establishes the
baseline for the next one. The SARIF marks each result as new, existing, or
fixed, and lists what a release repaired since the baseline.
Build provenance
The pipeline reads the standard CI environment variables and records the provider, repository, branch, commit, run identifier, and actor with the assessment, with no extra flags. The portal shows this next to the run, and the same context rides in the SARIF document, so a finding on a pull request is traceable to the exact build that produced it. GitHub Actions, GitLab CI, Bitbucket Pipelines, and CircleCI are recognised automatically.
What is and isn't collected
WASViking does not collect your repository source code, runner environment variables beyond the key you pass, runner filesystem contents outside the working directory, or any production data. Only the application package you name is uploaded. Embedded secret values found during analysis are stored redacted, never in the clear. 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, OWASP MASVS, OWASP MASTG, NIST SSDF, OWASP DSOMM).
Common problems
| Problem | Likely cause |
|---|---|
HTTP 401 Unauthorized |
API Key revoked, expired, or missing the mobile:scan scope. WASViking uses Authorization: ApiKey <key>, not Bearer. |
HTTP 403 feature_not_in_plan |
Mobile Security Assessment is not enabled for your organization. It is an add-on; ask your account contact or partner to enable it. |
HTTP 402 quota_exhausted |
Your monthly CI mobile assessment allowance is used up. Wait for the cycle to roll over, or raise the allowance. |
HTTP 402 quota_not_provisioned |
The module is on but no CI assessment allowance is set yet. Provision one in the portal. |
HTTP 400 unsupported_extension / unsupported_format |
The file is not a recognised application package. Point --file at the real .apk/.aab/.ipa, not a wrapper or an HTML download page. |
HTTP 413 file_too_large |
The package is over your organization's upload limit. |
HTTP 409 assessment is still running |
The SARIF was requested before analysis finished. The CLI handles this by waiting; you only see it if you call the endpoint directly. |
| Assessment finished with status=failed | The engine could not analyse the package. The JSON output carries the reason. |
install.sh download error |
Network policy blocking api.wasviking.com or the release bucket. |
Where this fits in the platform
- Pipeline assessments appear alongside manual uploads under Mobile Security → Assessments, tagged with a CI badge.
- The capability itself is documented at Mobile Security Assessment.
- The DAST counterpart is CI/CD DAST with GitHub Actions; the dependency and secrets counterpart is CI/CD SCA, SBOM & Secrets.
