# WASViking® Documentation (complete) > The full text of every public WASViking documentation page, in the order of the documentation navigation. Each page starts with its title, section and canonical URL. Index: https://docs.wasviking.com/llms.txt Page map: https://wasviking.com/llms.txt Platform brief: https://wasviking.com/llms-full.txt --- # Welcome to WASViking Section: Introduction Source: https://docs.wasviking.com/introduction/welcome/ Summary: What WASViking is, what it covers, and how to find your way around these docs. WASViking is a continuous exposure management and DAST platform for modern web applications, APIs, and software supply chains. These docs cover the platform in full: concepts, capabilities, how to run a scan, how to integrate with your stack, and how to operate the evidence the platform produces. ## What you can do with WASViking WASViking ships three answers to the security leader, in one console: 1. **See what you expose.** External and internal DAST, modern protocol coverage (REST, OpenAPI, GraphQL, SOAP/WSDL, WebSocket, JWT), asset inventory with drift detection. 2. **Know what is in your software.** Cloud-side component detection, premise-side SBOM, CI/CD SCA gate, signed Evidence Bundle, daily OSV and CISA KEV ingest. 3. **Operate the evidence.** Findings workflow with Risk Score, SLA digest, Exploit Path Graph, Posture Shares, compliance mapping across five frameworks. ## How these docs are organized - **Introduction** explains the platform and how it works at a high level. - **Getting Started** walks a new user from account creation through the first scan, team setup, and authenticated scanning. - **Concepts** defines the vocabulary used across the product: targets, assets, findings, Risk Score, scan profiles, Environment Profile. - **Capabilities** documents each scanner and analyzer in detail. - **Sentinel agent** covers the on-premises agent: install, internal scanning, SBOM, secrets, and the CI/CD gate. - **Integrations** covers Jira, Slack and Teams, Webhooks, SAML SSO, and SIEM destinations. - **API Reference** documents the public REST API, scopes, endpoints, and webhook events. - **Compliance** maps findings to PCI DSS v4.0, LGPD, GDPR, BACEN, and ISO 27001:2022, and explains the Evidence Bundle. - **Partner Console** is for resellers and MSSPs operating WASViking on behalf of customers. - **Security** describes the platform architecture, tenant isolation, and how to report a vulnerability responsibly. ## Where to start If you are evaluating WASViking, start with [Platform overview](https://docs.wasviking.com/introduction/platform-overview/). If you have an account and want to run your first scan, go to [Your first scan](https://docs.wasviking.com/getting-started/first-scan/). After that, [Activate your modules](https://docs.wasviking.com/getting-started/activate-your-modules/) walks through turning on everything your plan includes. If you are integrating WASViking into a CI/CD pipeline, head to [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). ## How to give feedback Email [support@wasviking.com](mailto:support@wasviking.com). We read every message. --- # Platform overview Section: Introduction Source: https://docs.wasviking.com/introduction/platform-overview/ Summary: What sits inside WASViking, how the pieces fit together, and what you get out. WASViking is built around deep deterministic engines with a thin, deliberate AI layer that explains, prioritizes, and plans rather than detects. The platform is delivered as SaaS, with an optional on-premise agent for internal scope. ## High-level architecture The platform has four components a customer-facing user touches: | Component | Role | |---|---| | **Portal** | Web console where customers configure organizations, targets, scans, and review findings. Multi-tenant, RBAC, SAML SSO. | | **API** | Public REST API plus the scanning orchestration layer. Hosts every analyzer under one engine fabric. | | **Sentinel agent** | Go binary the customer installs on-premises. Dials outbound mTLS to open a tunnel for internal scanning. Also runs SBOM and secrets locally. | | **Marketing and docs** | This site, plus `wasviking.com`. | ## How a scan flows 1. **Discovery.** Target Discovery maps the attack surface: headless-browser SPA crawl, OpenAPI ingest, GraphQL introspection, robots and sitemap, CSRF-aware login. 2. **Environment Profile.** A per-host fingerprint is captured: stack, protocols, defenses, auth surface, frontend rendering. Shared with every analyzer. 3. **Authenticated session.** If the scan profile uses Form Login, the AI Form Autofill detects selectors, classifies compatibility, and establishes a shared session. All analyzers reuse it. 4. **Analyzers run.** Deterministic analyzers run across seven categories of OWASP Top 10 web application risks, plus modern protocol coverage. The injection-class analyzer consolidates its detectors in a single pass. 5. **Findings written.** Each finding gets a stable fingerprint, a primary risk category, a CWE mapping, and a Risk Score 0-100. 6. **AI layer.** An LLM produces the executive summary, the business risk narrative, and the prioritized action. The engine override forces the LLM verdict to match the engine on disagreement. 7. **Routing.** Status transitions emit webhook events. Alerts route to Slack, Teams, webhook, or email per organization configuration. ## What you get out A scan produces: - **Findings** with payload, evidence, raw HTTP transcript, AI recommendation, CWE, Risk Score, SLA window, and compliance control mapping. - **Asset Inventory** updated with `first_seen`, `disappeared`, `reappeared` events. - **PDF report** with brand cover, executive summary, findings detail, and compliance tab. - **API access** to the same data via the public REST API. - **Exploit Path Graph** updates if the scan produced chains. ## What is in scope WASViking covers: - External web applications and APIs (REST, OpenAPI, GraphQL, SOAP/WSDL, WebSocket, JWT-protected endpoints). - Internal web applications and APIs reachable through a Sentinel agent. - Software supply chain via SBOM N1 (cloud-side) and N2 (premise-side). - Secrets in source code and git history via the Sentinel agent. - SSL/TLS certificate monitoring and TLS configuration. - Sensitive port and subdomain monitoring with auto-discovery scan. - Edge adversary traffic correlation via Cloudflare integration. ## Next steps - New to the platform: [Your first scan](https://docs.wasviking.com/getting-started/first-scan/). - Operating internally: read about the [Sentinel agent](https://docs.wasviking.com/sentinel/architecture/). - Integrating with CI/CD: [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). --- # Create your account Section: Getting Started Source: https://docs.wasviking.com/getting-started/create-account/ Summary: How WASViking accounts are provisioned, from first contact to first sign-in. WASViking® accounts are provisioned by our team as part of a guided onboarding. There is no self-serve signup: you request a demo or a quote, we talk through your scope, and we create your organization with the right plan and modules from day one. ## Request access 1. Submit the form at [wasviking.com/get-a-demo](https://wasviking.com/get-a-demo/), or email [contact@wasviking.com](mailto:contact@wasviking.com). 2. Our team replies within one business day to schedule a short conversation about what you need to cover: targets, modules, and deployment. 3. After the conversation we provision your **Organization** and send an invitation email to the person you designate as the first administrator. Most evaluations start with a guided demo followed by a sales-assisted evaluation against targets you authorize, so your team reviews real findings from your own environment before any commitment. ## Accept your invitation The invitation email arrives from `@wasviking.com` and the link is single-use and expires. If the email does not arrive: - Check spam and any corporate quarantine. - Add `@wasviking.com` to your allow list. - Ask your account team to resend the invitation. ## Multi-factor authentication WASViking enforces MFA for every operator. On first login you set it up with a TOTP authenticator app (Authy, 1Password, Google Authenticator, Microsoft Authenticator). Backup codes are issued at setup; store them safely. WASViking sends a 6-digit code by email as the second factor if your authenticator is unavailable. This is a fallback, not the default. ## Your first sign-in After login you land on the Cyber Risk dashboard. The dashboard is empty until your first scan completes. Before the first scan, you can: - Configure the organization (logo, default time zone, contact email). - Invite teammates. - Create your first target. - Connect a Sentinel agent for internal scope. ## Organization vs Personal A WASViking account is always scoped to an organization. There is no "personal workspace". Every action you take, every finding you read, every scan you launch, happens inside an Organization with RBAC. The audit log records it that way. ## Where next - [Inviting your team](https://docs.wasviking.com/getting-started/inviting-your-team/) - [Authenticated scanning](https://docs.wasviking.com/getting-started/authenticated-scanning/) - [Your first scan](https://docs.wasviking.com/getting-started/first-scan/) --- # Inviting your team Section: Getting Started Source: https://docs.wasviking.com/getting-started/inviting-your-team/ Summary: Pick the right role, send the invitation, track its state, and manage active and inactive users. WASViking® ships with four default roles. Each role maps to a curated set of per-module permissions. Invitations are emailed with a secure single-use link. Users cannot be deleted, only **deactivated**, so the audit trail is preserved. The whole flow lives at **Team Management** in the portal. ## Step 1: Understand the role catalog Open **Team Management → Role Access Overview**. Four roles ship by default: | Role | Designed for | Includes | Headline | |---|---|---|---| | **Admin** | Organization owners | Billing, settings, user management | Full access to all modules, users, roles, billing, settings and integrations. | | **Manager** | Security leads | Users, scans, schedules, alerts | Manages scans, alerts and users. No billing or organization settings. | | **Analyst** | Security analysts | Scan execution and findings review | Runs scans and investigates findings. No admin access. | | **ReadOnly** | Stakeholders and auditors | Visibility without changes | View-only access for dashboards, findings, reports and monitoring data. | > The portal capitalizes the fourth role as **ReadOnly** (single word). > Match the spelling when referencing it programmatically. ### Detailed role permissions Click **View detailed role permissions** to expand the per-module matrix. The summary below mirrors what the portal shows. #### Admin | Module | Permissions | |---|---| | Dashboard | View | | Targets | View, create, edit, delete | | Scans | View, create, edit, cancel, rerun, export | | Schedules | View, create, edit, delete, pause, resume | | Reports | View, export, share | | Certificates | View and manage | | Notifications | Full operational control | | Edge Threat Radar | View and block IP | | Exposure Intelligence | View and reveal sensitive data | | Supply-chain IOC | View and apply | | SSO | View and manage settings | | Users & Roles | Full access | | API Tokens | View, create, revoke, rotate | | Billing | Full access | | Settings & Integrations | Full access | | Audit Logs | View | #### Manager | Module | Permissions | |---|---| | Dashboard | View | | Targets | View, create, edit | | Scans | View, create, cancel, rerun | | Schedules | View, create, edit, pause, resume | | Reports | View, export | | Certificates | View and manage | | Notifications | View, edit, test, enable, disable | | Edge Threat Radar | View and block IP | | Exposure Intelligence | View and reveal sensitive data | | Supply-chain IOC | View and apply | | SSO | View only | | Users | View, invite, revoke, reactivate, change role, delete | | Roles | View and assign | | Billing | View only | | Settings & Integrations | View only | | Audit Logs | View | #### Analyst | Module | Permissions | |---|---| | Dashboard | View | | Targets | View only | | Scans | View, create, cancel, rerun | | Vulnerabilities | View | | Reports | View only | | Certificates | View only | | Notifications | View only | | Edge Threat Radar | View only | | Exposure Intelligence | View and reveal sensitive data | | Supply-chain IOC | No access | | SSO | View only | | Users | View only | | Usage | View | | Billing | Invoices view only | | Org Settings | View only | #### ReadOnly | Module | Permissions | |---|---| | Dashboard | View | | Targets | View only | | Scans | View only | | Vulnerabilities | View only | | Reports | View only | | Certificates | View only | | Notifications | View only | | Edge Threat Radar | View only | | Exposure Intelligence | View only | | Supply-chain IOC | No access | | SSO | View only | | Users | View only | | Usage | View only | | Billing | Invoices view only | | Actions | No create, edit, delete, export or admin actions | ## Step 2: Send an invitation Click **Invite User** at the top of the Team Management page. The modal shows: | Field | What to put | |---|---| | Email | The teammate's business email. | | Role | Admin, Manager, Analyst, or ReadOnly. The info block below the dropdown explains the chosen role. | Below the form: > *Only invite users from your organization's email domain unless your > policy allows external invites.* WASViking enforces this with a B2B email domain policy on submission (free, public, and disposable email providers are refused). Click **Send Invitation**. The invitee receives an email with a secure single-use link that expires automatically. ## Step 3: Track invitations Open the **Invitations** tab. The table tracks pending, accepted, expired, or revoked invitations. | Column | Notes | |---|---| | Email | The invited address. | | Role | The role pre-selected at invitation time. | | Status | `pending`, `accepted`, `expired`, or `revoked`. | | Expires | When the secure link stops working. | | Actions | Resend or revoke. | Resend issues a fresh secure link with a new expiry. Revoke invalidates the link immediately. ## Step 4: Manage active users Open the **Users** tab. > *Manage active and inactive users. Deactivation blocks access but > preserves scans, schedules and logs.* | Column | Notes | |---|---| | Name | The user's display name. | | Email | The user's email. A **LOCAL** tag indicates a user authenticating with a local password and MFA, as opposed to a federated SSO user. | | Role | Editable inline by Admin or Manager. | | Status | `Active` or `Inactive`. | | Actions | Deactivate (for an active user) or Reactivate (for an inactive one). | ### Deactivating a user Deactivation: - **Blocks access** to the portal and the public API. - **Preserves scans, schedules, and audit logs** owned by the user. - **Is reversible**: click Reactivate to restore access without re-inviting. Users cannot be deleted by design. The audit trail must remain attributable. ## Anti-lockout protection WASViking refuses operations that would leave the organization with zero active Admins. You cannot: - Deactivate the last active Admin. - Demote the last active Admin to a lower role. To rotate the last Admin: invite a new one first, have them accept, then act on the original Admin. ## Federated access (SAML 2.0 SSO) When SSO is enabled at the organization level, operators sign in through your Identity Provider. MFA is enforced by the IdP. WASViking still enforces RBAC on every action. New accounts created via SSO **land as ReadOnly by default**. An existing Admin or Manager promotes them in Team Management. See [SAML 2.0 SSO](https://docs.wasviking.com/integrations/saml-sso/) for the full setup. ## API keys are a separate surface Team Management governs operator access via the four-role catalog. The **public REST API** has its own fine-grained scope catalog (e.g., `findings:read`, `scans:run`, `sca:submit`). See [Scopes catalog](https://docs.wasviking.com/api-reference/scopes/) for the API surface. --- # Your first scan Section: Getting Started Source: https://docs.wasviking.com/getting-started/first-scan/ Summary: Add your first asset, pick a scan template and profile, configure preferences, and read findings. This walkthrough takes you from a fresh account to a completed scan with findings. The default Starter plan includes everything you need to follow along. The portal flow is two steps: create the **asset** you want to scan, then configure and run the **scan**. ## Prerequisites - An active WASViking® organization. If you do not have one, [create an account](https://portal.wasviking.com/register/). - A web application or API you are authorized to test. WASViking enforces ownership via a self-attestation at asset creation. - Optional: credentials for an authenticated scan. ## Step 1: Add your first asset In the portal, go to **Assets Inventory → Add New Asset**. ### Asset configuration | Field | What to put | |---|---| | Name | Operator-readable identifier, e.g., `Daily Web Scan - Main Portal`. | | Description | Free text, e.g., `Monthly risk assessment for compliance`. | | Protocol | `https://` or `http://`. Dropdown. | | Target URL | The host or full path the asset resolves to, e.g., `api.customerportal.com`. | | Monitor SSL | Toggle. When on, the asset's TLS certificate is monitored continuously with severity escalation on expiration, weak chain, or hostname mismatch. | ### Authorization WASViking enforces ownership at asset creation by self-attestation. In the **Authorization** section, check: > I confirm that I am authorized to scan this asset. The attestation is captured in your customer-facing audit log along with the operator and timestamp. ### Confirm Click **Add New Asset**. The asset becomes available for selection on the New Scan screen. ## Step 2: Configure and run a scan Go to **Scans → New Scan**. The screen is a two-column form: configuration tabs on the left, the form on the right. The header bar shows the running summary of `TARGET`, `TEMPLATE`, and `PROFILE` choices. ### Step 2.1: Target & Template Open the **Target & Template** tab. | Field | What to put | |---|---| | Target Address | Pick the asset you created in Step 1. | | Execution Mode | `Direct (External)` for cloud-egress scans. Pick a Sentinel agent for internal-network targets (see [Internal scanning](https://docs.wasviking.com/sentinel/internal-scanning/)). | | Scan Template | `Full Coverage (System) - Default` is selected by default. Pick another saved template if you have one. | | Override template settings for this scan only | Toggle. When off, the form below is locked to the template's preferences. When on, every field is editable for this run; the template itself is not modified. | ### Step 2.2: Scan Profile Open the **Scan Profile** tab. The profile picks the depth of the assessment and the primary compliance catalog. Nine profiles ship by default: | Profile | Use for | |---|---| | **Full Coverage** (recommended) | Crawl, OWASP injection class, SQLi, XSS, headers, JWT, SOAP, TLS, ports, and every other analyzer in the platform. | | Web Application | Crawl, headers, OWASP injection class, SQLi, XSS. Ideal for standard web pentest evidence. | | API and JWT | REST and GraphQL discovery, JWT advanced testing, header hardening. For API-first products. | | SOAP and WSDL | WSDL ingestion, type-aware envelopes, XXE, XML Bomb, XPath, WS-Security bypass, SOAP-context injection. | | Network and TLS | Exposed ports, TLS configuration, certificate hygiene, SSL monitoring. No application-layer probes. | | Custom | Pick analyzers individually. Useful for compliance windows or targeted regression evidence. | | **PCI DSS** (compliance) | Targets Requirement 6.5 (web app vulnerabilities), 4.1 (transport encryption), and 8 (authentication). Web crawler, security headers, SQLi, XSS, OWASP injection class, JWT, TLS, and credential exposure. | | **LGPD** (compliance) | Mapped to Art. 46 (data protection measures). Same web app + auth + transport coverage as PCI, focused on personal-data surface under Brazilian privacy law. | | **GDPR** (compliance) | Targets EU Regulation 2016/679 Art. 32 (security of processing) and Art. 25 (data protection by design). Same surface as LGPD; the report cites the European articles instead. | ### Step 2.3: Per-protocol profiles The left configuration list shows four protocol-specific entries that can be inspected and adjusted independently of the main Scan Profile. Each shows the current configuration as a status line. | Protocol entry | Example status | |---|---| | **JWT Advanced** | `Wave 1 only` | | **SOAP / WSDL** | `XXE, WS-Security, XML-Enc` | | **WebSocket** | `Enabled` | | **GraphQL** | `Enabled · 10 checks` | Open each to review or change the protocol-specific options. JWT Advanced and SOAP / WSDL are driven by saved profiles; WebSocket and GraphQL are direct toggles plus check selection. ### Step 2.4: Preferences The **Preferences** block has four sub-tabs that apply across the scan regardless of profile. | Tab | What you control | |---|---| | **Scan Method** | Execution Path. `Direct (External)` for cloud-egress. `Via Sentinel Agent` for internal targets. See [Internal scanning](https://docs.wasviking.com/sentinel/internal-scanning/). | | **Authentication** | None, Form Login (with AI Form Autofill), Bearer token, Cookie, or Custom header. See [Authenticated scanning](https://docs.wasviking.com/getting-started/authenticated-scanning/). | | **Crawl** | Custom User-Agent string, excluded paths, depth controls. | | **AI & Compliance** | AI Recommendation on/off, primary compliance framework for the report (LGPD, GDPR, PCI DSS, BACEN, ISO 27001). | ### Step 2.5: Run Click **Scan**. The scan moves through `queued → discovering → scanning → analyzing → done`. Typical duration is 8 to 25 minutes depending on the profile and the surface size. You can leave the page. A notification arrives when the scan completes (email, Slack, Teams, or webhook depending on your integration setup). ## Step 3: Read the findings Open **Findings**. Each finding carries: - **Category** (SQLi, XSS, SSRF, GraphQL BOLA, and so on). - **Severity** and **Risk Score 0-100**. The Risk Score combines severity with asset criticality, environment, industry, and SLA window. - **CWE**, with a single canonical mapping. - **Evidence**: payload, raw HTTP request and response, the analyzer that produced it. - **AI recommendation**: executive summary, business risk narrative, and a prioritized action. EN, PT-BR, or ES. - **Status workflow**: `open → accepted | mitigated | false_positive | fixed`, with audit log. > **Engineering override.** The engine's verdict wins on every > disagreement with the LLM. AI cannot drift past the engine. ## Step 4: Route alerts In **Integrations**, connect Jira, Slack, Teams, or a webhook. Status transitions emit signed webhook events. Routing is per organization. ## Where next - **Activate the rest of your plan.** Work through [Activate your modules](https://docs.wasviking.com/getting-started/activate-your-modules/) to turn on every module your organization purchased. - **Authenticated scanning at scale.** Save a scan template under **Scan Templates** so every team member runs the same baseline. See [Scan profiles and templates](https://docs.wasviking.com/concepts/scan-profiles-and-templates/). - **Internal applications.** Set up the [WASViking Sentinel Tunnel](https://docs.wasviking.com/getting-started/sentinel-tunnel/) on a host inside your network. - **CI/CD.** Drop the `wasviking-sentinel` binary in your pipeline for SBOM, secrets, or a policy-driven scan gate. See [Sentinel CI](https://docs.wasviking.com/sentinel/sentinel-ci/). - **Compliance.** Open the **Compliance** tab on the scan report to see per-control mapping across PCI DSS, LGPD, GDPR, BACEN, and ISO 27001:2022. - **Attack chains.** Once findings exist, open **Exploit Paths** in the portal to see how findings chain toward critical sinks. The model is explained in [Exploit Path Graph](https://docs.wasviking.com/concepts/exploit-path-graph/). --- # Activate your modules Section: Getting Started Source: https://docs.wasviking.com/getting-started/activate-your-modules/ Summary: One checklist that takes your organization from the first scan to every purchased module running, with the portal path and the verification step for each. Your first scan proves the platform works. This page covers everything after that: each WASViking® module, where to turn it on, and how to confirm it is producing results. Work top to bottom; every row links to the page with the full detail. You need the **Admin** role for most of the steps below. Module availability depends on your plan; anything your plan does not include appears locked in the portal. ## Runs on every scan, nothing to switch on These capabilities are part of the scan engine itself. If your [first scan](https://docs.wasviking.com/getting-started/first-scan/) completed, they are already active. | Module | Where results appear | Detail | |---|---|---| | External DAST | **Findings** and the scan report. | [External DAST](https://docs.wasviking.com/capabilities/external-dast/) | | Modern API Security (GraphQL, SOAP, WebSocket, JWT) | **Findings**, when the scan profile covers APIs. | [Modern API Security](https://docs.wasviking.com/capabilities/modern-api-security/) | | Out-of-Band Validation (OAST) | **Application Security → Out-of-Band (OAST)**, plus the findings it confirms. | [Out-of-Band Validation](https://docs.wasviking.com/capabilities/out-of-band-validation/) | | Component detection (cloud side) | **Inventory → Software Bill of Materials**. | [Software Supply Chain](https://docs.wasviking.com/capabilities/software-supply-chain/) | | Sensitive port baseline | **Findings**, category `exposed_port`. | [Sensitive Port Monitoring](https://docs.wasviking.com/capabilities/sensitive-port-monitoring/) | | Exploit Paths | **Exploit Paths** page, once findings exist. | [Exploit Path Graph](https://docs.wasviking.com/concepts/exploit-path-graph/) | | Compliance mapping | The **Compliance** tab on every scan report. | [Framework mapping](https://docs.wasviking.com/compliance/framework-mapping/) | To widen API coverage, pick the right scan profile (`api_jwt` for REST, GraphQL, and JWT work; `soap` for SOAP services) and attach your OpenAPI or WSDL document to the target. See [Scan profiles and templates](https://docs.wasviking.com/concepts/scan-profiles-and-templates/). ## Switch on per asset or per domain | Module | Turn it on | Confirm it works | |---|---|---| | Certificate Monitoring | **Assets Inventory → Add New Asset** (or edit an existing asset) and toggle **Monitor SSL**. Alert thresholds live under **Settings → System Settings → Notifications & Alerts**. | The asset appears under **Certificates → Certificate Monitoring** with a Last Checked timestamp. [Detail](https://docs.wasviking.com/capabilities/certificate-monitoring/) | | Exposure Intelligence | **Settings → System Settings → Add Monitored Domain**, then complete the consent checkboxes and the DNS TXT verification. | The domain shows as verified and matches surface under **Cyber Risk → Exposure Intelligence**. [Detail](https://docs.wasviking.com/capabilities/exposure-intelligence/) | | Edge Threat Radar | Connect your Cloudflare zone following the [Cloudflare integration guide](https://docs.wasviking.com/getting-started/cloudflare/). | Events appear on the **Edge Intelligence** dashboard within a few polling cycles. [Detail](https://docs.wasviking.com/capabilities/edge-threat-radar/) | | Header Advisor | **Edge Threat Radar → Header Advisor → Add hostname**, then deploy the two discovery headers at your edge or origin following [Set up Header Advisor](https://docs.wasviking.com/getting-started/header-advisor/). | The advisor moves from **Waiting for the first report** to **Learning from real traffic** and the source table fills as users browse. [Detail](https://docs.wasviking.com/capabilities/header-advisor/) | | Code Security (SAST, dependencies, secrets, SBOM) | **Settings → System Settings → Connected Repositories**, connect GitHub or Bitbucket, then switch **Monitored** on for each repository under **Application Security → Code Security**. Full walkthrough in [Set up Code Security](https://docs.wasviking.com/getting-started/connected-repositories/). | The row shows the first run moving through *Queued*, *Cloning*, *Scanning* and *Recording results*; findings appear under **Findings** filtered by that repository and the **WASViking SAST Findings** tile counts them. Then [triage your first SAST finding](https://docs.wasviking.com/getting-started/triage-your-first-sast-finding/). [Detail](https://docs.wasviking.com/capabilities/code-security/) | | Custom sensitive ports | **Settings → System Settings → Notifications & Alerts**, add your ports to the monitored list. | The next scan raises findings for any of those ports found open. [Detail](https://docs.wasviking.com/capabilities/sensitive-port-monitoring/) | ## Deploy something on your side These modules need a component running inside your environment. | Module | Turn it on | Confirm it works | |---|---|---| | Internal scanning (Sentinel agent) | Register an agent in the portal and install it on a host that can reach your internal targets. Follow [WASViking Sentinel Tunnel](https://docs.wasviking.com/getting-started/sentinel-tunnel/). | The agent shows **Active** on the Sentinel Agents page, and an internal target scan completes. [Detail](https://docs.wasviking.com/sentinel/internal-scanning/) | | SBOM from your repositories | Run [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) against your projects and submit. | Submissions appear under **Inventory → Software Bill of Materials**. | | Hard-coded secrets on disk | Run [`wasviking-sentinel secrets`](https://docs.wasviking.com/sentinel/sentinel-secrets/) against your repositories. | Submissions appear under **Application Security → Hard-coded Secrets**, with verified-live flags where applicable. [Detail](https://docs.wasviking.com/capabilities/secrets-detection/) | | CI/CD gates (DAST, SCA, secrets) | Add the binary to your pipeline. Start from [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/), or use the GitHub recipes for [DAST](https://docs.wasviking.com/getting-started/github-actions-dast/) and [SCA, SBOM and secrets](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/). | The pipeline run exits with the documented gate codes and the results land in the portal. | | AI Guardian on employee devices | Follow [Set up WASViking AI Guardian](https://docs.wasviking.com/getting-started/ai-guardian/): enable the feature, mint the install key, run the installer per device. | Devices report under **Cyber Risk → AI Guardian** and a test event is classified. [Detail](https://docs.wasviking.com/capabilities/ai-guardian/) | | Infrastructure Defense | Enabled per organization. The module sees your infrastructure two ways, both in the same place. Install the **Sentinel Host agent** on each server for the deep per-machine view ([host setup](https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/)), and run a **Sentinel Probe** on each network segment for the agentless network scan that reaches what no agent can and provides PCI DSS internal scanning ([probe setup](https://docs.wasviking.com/getting-started/sentinel-probes/)). | Enrolled servers show **Online** on the Assets screen with a Viking Exposure Score; probe-discovered devices appear there too, with a **Probe** source. [Detail](https://docs.wasviking.com/capabilities/infrastructure-defense/) | ## Watch what you shipped Once SBOMs are flowing, two supply chain layers start working for you. | Module | Turn it on | Confirm it works | |---|---|---| | Supply Chain Intel | Nothing to configure. Every submitted SBOM is re-checked daily against new advisories (Pro plan and above). | Matches appear under **Inventory → Supply Chain Intel**. [Detail](https://docs.wasviking.com/capabilities/supply-chain-intel/) | | Supply-chain IOC | **Inventory → SBOM → Supply-chain IOC**: enter an indicator, dry-run, then apply. | The dry-run preview lists affected components before any finding is created. [Detail](https://docs.wasviking.com/capabilities/supply-chain-ioc/) | ## Assess the apps you ship to devices | Module | Turn it on | Confirm it works | |---|---|---| | Mobile Security Assessment | Enabled per organization. Once it is on, open **Mobile Security → Assessments** and upload an Android or iOS package. Nothing else to configure. | The assessment reaches Completed and the report opens with a grade and a finding list. [Detail](https://docs.wasviking.com/capabilities/mobile-security/) | ## Automate the operation | What | Where | Detail | |---|---|---| | Alert routing | Create channels first: Slack, Teams, email, or webhook. | [Notification Channels](https://docs.wasviking.com/integrations/notification-channels/) | | Recurring scans | **Scans → Scan Schedules**: pick a target, a template, and a cadence. | [Scan Schedules](https://docs.wasviking.com/capabilities/scan-schedules/) | | Autonomous planning | **Scans → AI Scan Planner**: turn on the **Enabled** toggle and review the daily decisions. | [AI Scan Planner](https://docs.wasviking.com/capabilities/ai-scan-planner/) | | Ticketing | **Settings → Integrations**: connect Jira or ServiceNow for two-way finding sync. | [Jira](https://docs.wasviking.com/integrations/jira/), [ServiceNow](https://docs.wasviking.com/integrations/servicenow/) | | SIEM and automation | Ship events to your SIEM or your own consumers. | [SIEM](https://docs.wasviking.com/integrations/siem/), [Webhooks](https://docs.wasviking.com/integrations/webhooks/) | | Single sign-on | Enforce your identity provider for portal access. | [SAML 2.0 SSO](https://docs.wasviking.com/integrations/saml-sso/) | ## Prove it When an auditor, a customer, or a prospect asks for evidence, three surfaces answer without giving anyone portal access: - The **Compliance** tab and PDF report per scan, mapped across PCI DSS, LGPD, GDPR, BACEN, and ISO 27001. See [Framework mapping](https://docs.wasviking.com/compliance/framework-mapping/). - A read-only snapshot of your posture via a Posture Share. The click-by-click flow is in [Posture Shares](https://docs.wasviking.com/getting-started/posture-shares/); the recipient visibility contract lives in the [reference page](https://docs.wasviking.com/compliance/posture-shares/). - A signed, verifiable SBOM Evidence Bundle. The click-by-click flow is in [SBOM Evidence Bundles](https://docs.wasviking.com/getting-started/sbom-evidence-bundles/); the package contents and verification live in [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/). ## Where next - Scanning behind a login: [Authenticated scanning](https://docs.wasviking.com/getting-started/authenticated-scanning/). - Understanding scores and SLAs: [Findings and Risk Score](https://docs.wasviking.com/concepts/findings-and-risk-score/). - Automating over the REST API: [Authentication](https://docs.wasviking.com/api-reference/authentication/). --- # Authenticated scanning Section: Getting Started Source: https://docs.wasviking.com/getting-started/authenticated-scanning/ Summary: Carry credentials into every analyzer through one shared session, without locking out the test account. Unauthenticated scans cover the front door. Most of the interesting attack surface lives behind a login. WASViking® ships authenticated scanning as a first-class capability with four auth modes and a shared-session contract that all analyzers honor. ## Auth modes | Mode | When to use | |---|---| | **Form Login** | Classic login form with username and password. AI Form Autofill detects selectors automatically. | | **Bearer token** | REST API with `Authorization: Bearer …`. Paste the token. | | **Cookie** | Opaque session cookie issued by your stack. Paste the cookie value. | | **Custom header** | Anything non-standard. Define the header name and value. | ## Form Login with AI Autofill WASViking ships an LLM-backed selector detector that: 1. Loads the login page. 2. Identifies the username, password, and submit selectors. 3. Falls back to a headless-browser SPA crawler if the page is JavaScript-only. 4. Returns a five-verdict compatibility classifier: `compatible`, `captcha`, `spa`, `multi-step`, `uncertain`. The verdict recommends the right auth mode for the scan. | Verdict | Recommendation | |---|---| | `compatible` | Use Form Login. Selectors detected, no special handling. | | `captcha` | Use Bearer or Cookie. Form is captcha-gated; automation cannot help. | | `spa` | Use Form Login with the headless-browser fallback (enabled automatically). | | `multi-step` | Use Bearer or Cookie. Login is multi-step; try a session export. | | `uncertain` | Try Form Login first; switch to Bearer or Cookie if it fails. | ## Shared session contract When a scan picks Form Login, the platform authenticates once and shares the resulting cookies with every analyzer in the scan. SQL Injection, XSS, JWT, GraphQL, SOAP, WebSocket, and the injection-class checks all share one session. The practical effect: a multi-analyzer scan looks like a single authenticated user to the target, so it stays well clear of typical anti-brute-force thresholds. ## Bearer, Cookie, Header modes For Bearer, Cookie, and Custom Header modes, WASViking carries the credential through every request via the same shared-session mechanism. You paste it once at scan configuration; it is encrypted at rest and never logged in plaintext. ## Multi-step login If the login is multi-step (email page, password page, MFA, etc.) and the AI classifier returns `multi-step`, the best path is: 1. Authenticate manually once in your browser. 2. Export the session cookie or Bearer token using your dev tools. 3. Paste it into WASViking as Cookie or Bearer mode. 4. Document the token's lifetime so the scan window fits inside it. ## Token refresh Long-running scans against short-lived tokens fail. Two approaches: - **Use a longer-lived service account** for scanning where your stack allows it. - **Schedule shorter scans** during a window your token covers. Scan templates let you split a profile across multiple shorter runs. ## What never leaves the platform WASViking treats authentication material as sensitive: - Credentials are encrypted at rest using a tenant-scoped key. - Credentials are never written to logs. - The Sentinel gRPC service redacts proto, job, and response payloads so internal-target cookies do not appear in operator logs. - API keys are masked in the UI after creation. ## Validating an authenticated scan worked In the scan report, look at the **Coverage** tab. An authenticated scan should show: - Coverage of routes under `/admin/`, `/account/`, or whatever your authenticated area is. - Findings tagged with an `auth_context: form_login` or similar marker. - A higher count of discovered URLs than an unauthenticated scan against the same target. If you see none of these, the session probably did not stick. Open the scan log and check the AI Form Autofill verdict. --- # WASViking Sentinel Tunnel Section: Getting Started Source: https://docs.wasviking.com/getting-started/sentinel-tunnel/ Summary: Scan the applications that never touch the internet. Install the Sentinel agent inside your network, register it with a one-time token, and let it open an outbound mTLS tunnel that you can rotate or revoke at any time. Your most sensitive applications are usually the ones a cloud scanner cannot see: the staging environment behind the VPN, the internal API that only the office network reaches, the admin panel on a private address. Testing them normally forces a bad choice: open a hole in the firewall for a vendor, or leave them untested. The **WASViking® Sentinel** removes that choice. It is a small agent you install on one Linux host inside your network. The agent dials **out** to the WASViking cloud over mutual TLS and keeps a tunnel open; scan traffic for your internal targets flows through that tunnel. Nothing connects in. Your firewall keeps every inbound port closed, and the scans reach the private addresses of your Dev and STG infrastructure as if the scanner were sitting next to them. ## Why your security team will approve this This is the part to bring to the security and compliance review before installing anything: - **Outbound only.** The agent opens one outbound HTTPS connection on port 443. No inbound ports, no NAT rules, no VPN accounts for a third party, nothing for an external attacker to find. - **Mutual TLS with a per-agent identity.** During registration the agent receives its own client certificates, bound to your organization. Both sides authenticate; a connection without a valid client certificate for your org is refused. - **You hold the kill switch.** Every agent can have its token rotated or be revoked from the portal, and a revoked agent is cut off immediately. Access by WASViking to your internal network exists only while you keep an agent registered and running. - **One-time bootstrap.** The registration token is shown once and is valid for a single registration. After that, the certificate is the identity; the token is useless to anyone who finds it later. - **Scoped reach.** The agent only scans the internal targets you register in the portal, and you can restrict its network reach further with your own firewall rules around the host it runs on. The full transport and hardening detail lives in [Sentinel architecture](https://docs.wasviking.com/sentinel/architecture/); it is written to be handed to a security reviewer. ## Where this helps in the day to day - **Dev and STG before release.** The same scan engine that covers your public site runs against pre-production addresses, so issues are caught before they ship. - **Internal-only applications.** Intranet apps, internal APIs, and admin panels get the same findings workflow, risk scoring, and compliance mapping as everything else. - **Segmented networks.** Install one agent per segment or data center; the portal routes each scan through the right agent. - **Evidence for the auditor.** Internal applications show up in your posture with scan history, which answers the recurring audit question about coverage beyond the public perimeter. ## Pre-requisites | Requirement | Detail | |---|---| | A host inside the network | A Linux host (Ubuntu/Debian package shown here) that can reach the internal targets you want to scan. | | Outbound network | HTTPS to the WASViking cloud on port 443. No inbound access is needed. | | Clock sync | NTP enabled on the host; certificate validation depends on a correct clock. | | Portal role | Admin, to register agents and copy the bootstrap token. | --- ## Step 1: Open the Sentinel Agents page In the portal, go to **Sentinel → Sentinel Agents**. The status pills across the top (Total, Active, Pending, Offline, Revoked) summarize your fleet, and the table lists each agent with its hostname, address, status, and last heartbeat. The **Actions** column is where the control lives: **Details**, **Rotate Token**, and **Revoke**. ![Sentinel Agents page with the status pills and the agent table](https://docs.wasviking.com/static/docs/images/sentinel-tunnel/01-agents-page.png) *Sentinel → Sentinel Agents.* --- ## Step 2: Register the agent and copy the token Click **Add Sentinel Agent** and give it a display name. This is a human label shown in the portal, not the machine hostname, and it must be unique in your organization. Name it after the site and segment it will serve, for example `Florida - DMZ Internal STG 01`, so that a year from now the table still reads like a map of your network. ![Add Sentinel Agent dialog with the display name field](https://docs.wasviking.com/static/docs/images/sentinel-tunnel/02-add-agent-modal.png) *A human-friendly label, unique in your organization. You can rename it later.* Click **Create**. The portal shows the **RAW bootstrap token once**. Copy it and store it safely; it is what you will use on the host in the next step. The token authorizes a single registration, so if you lose it before registering, use **Rotate Token** on the agent row to mint a fresh one. ![Agent created dialog showing the one-time RAW bootstrap token](https://docs.wasviking.com/static/docs/images/sentinel-tunnel/03-bootstrap-token.png) *The bootstrap token is displayed once. Copy it before closing.* If you are rolling out several segments, **Create another** repeats the flow without leaving the dialog. One agent per segment or data center is the pattern that scales. --- ## Step 3: Install and register on the host Click **Get Agent** to open the install instructions for your platform. For Ubuntu/Debian the flow is three commands long. Download the `.deb` package and, if your change process requires it, check the SHA256 and signature with the **Verify download** link. Then install and enable the service: ```bash sudo dpkg -i wasviking-sentinel__amd64.deb sudo systemctl enable --now wasviking-sentinel ``` Register the agent with the token from Step 2. Registration runs **once** per agent: it securely downloads the client certificates that become the agent's identity and writes the configuration to `/opt/wasviking-sentinel/configs/config.yaml`. ```bash cd /opt/wasviking-sentinel ./wasviking-sentinel register --token sudo systemctl start wasviking-sentinel sudo systemctl status wasviking-sentinel ``` ![Install WASViking Sentinel instructions with the download, install, and register commands](https://docs.wasviking.com/static/docs/images/sentinel-tunnel/04-install-instructions.png) *Get Agent: download, install, register, start.* --- ## Step 4: Confirm the tunnel is up Back on **Sentinel → Sentinel Agents**, the agent flips from **Pending** to **Active** and the **Last Seen** column starts updating with its heartbeat. That is your confirmation that the outbound tunnel is established. If the agent stays **Pending** for more than a few minutes, the cause is almost always one of these: - Outbound connectivity on port 443 is blocked for that host. - A corporate proxy or TLS interception appliance is breaking the mutual TLS handshake. The tunnel needs to pass through untouched. - The system clock is off; check NTP. The step-by-step diagnosis lives in the troubleshooting section of [Installing the Sentinel agent](https://docs.wasviking.com/sentinel/installation/). --- ## Step 5: Run the first internal scan Register an internal target the same way you registered your first public one, but point it at the private address (for example `https://stg-app.internal.local` or `http://10.20.30.40:8080`). Then start the scan at **Scans → New Scan** (`/portal/scans/new`). This is the step that actually routes the scan through the tunnel: in **Preferences → Scan Method**, set **Execution Path** to **Sentinel Tunnel (Internal)** and pick your agent in the **Sentinel Agent** dropdown, for example `Florida - DMZ Internal STG 01 (Active)`. Do not leave **Execution Path** on **Direct (External)** for an internal target. Direct is for addresses that are public on the internet; the cloud scanner cannot reach a private address, so an internal scan dispatched as Direct has nothing to test. Sentinel Tunnel (Internal) plus the right agent is what carries the scan safely to the internal address. ![New Scan preferences with Execution Path set to Sentinel Tunnel (Internal) and the Sentinel Agent selected](https://docs.wasviking.com/static/docs/images/sentinel-tunnel/05-new-scan-execution-path.png) *Preferences → Scan Method: Execution Path and the agent created in Step 2.* Click **Scan now**. The scan is dispatched through the tunnel, runs against the internal address, and the findings land in the same **Findings** workflow as everything else, with the target clearly marked as internal. The target fields, authentication options, and per-agent routing are documented in [Internal scanning](https://docs.wasviking.com/sentinel/internal-scanning/). --- ## Operating the fleet - **Rotate Token** issues a new bootstrap for an agent, which is the move when a token leaked before registration or when you rebuild a host and need to re-register. - **Revoke** cuts the agent off immediately. Registered certificates stop being accepted, scans stop routing through it, and the row stays in the table for the audit trail. - **One agent per segment.** Agents are cheap to run; give each network segment or data center its own, name them consistently, and target routing picks the right one. - The same installed binary also runs [`sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) and [`secrets`](https://docs.wasviking.com/sentinel/sentinel-secrets/) against your repositories, so the host you just set up can feed the supply chain modules too. ## Where next - Point your first internal target at the agent: [Internal scanning](https://docs.wasviking.com/sentinel/internal-scanning/). - Hand the transport design to your security team: [Sentinel architecture](https://docs.wasviking.com/sentinel/architecture/). - Put the same binary in your pipeline: [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). - Schedule recurring internal scans: [Scan Schedules](https://docs.wasviking.com/capabilities/scan-schedules/). --- # CI/CD DAST with GitHub Actions Section: Getting Started Source: https://docs.wasviking.com/getting-started/github-actions-dast/ Summary: Run automated DAST scans inside your GitHub Actions pipeline with the WASViking Sentinel. Scans the app on localhost over an ephemeral mTLS tunnel, runs authenticated scans with a token minted in the pipeline, prioritizes the endpoints you list, fails the build on real findings, and publishes results to the GitHub Security tab. This integration runs a WASViking® DAST scan inside your GitHub Actions runner, against the application you start on `localhost`, and fails the pipeline when it finds real vulnerabilities. The app is never exposed to the public internet: the Sentinel agent opens an ephemeral mTLS tunnel and the DAST engine sends its probes back through it. The setup has two halves, and the order matters: you configure the **WASViking portal first** (a Sentinel agent token and an API Key scoped to `ci:scan`), then wire those into **GitHub Actions**. ## What this integration does - Runs a DAST scan on every push and pull request, against the build you stand up in the runner. - Fails the build by severity with `--fail-on`, so vulnerable releases are blocked before merge. - Compares against the base branch with `--baseline`, so a PR only fails on what it introduces, not pre-existing debt. - Runs **authenticated scans** with a short-lived token your pipeline mints at runtime (`--auth-bearer` / `--auth-header`), so protected areas behind a login are actually reached, with no static credentials in a config. - **Prioritizes the endpoints you list** with `--path`, scanning your own API routes first, before the auto-discovered surface. Essential for authenticated APIs and SPAs that have no crawlable HTML links. - Emits **SARIF 2.1.0**, consumed natively by GitHub Code Scanning, plus a full JSON report. - Provisions and tears down the agent per run, with no persistent credentials left on the runner. - Meters against your CI/CD scan quota and records every run in the portal. ## How it works 1. The runner downloads and installs the Sentinel agent using the Sentinel token. 2. The agent provisions an ephemeral mTLS bundle (valid 60 minutes, not reusable) from the WASViking API. 3. The agent opens a gRPC over mTLS tunnel to the WASViking tunnel server. 4. The agent requests a scan against your `localhost` target. The API validates that the target is a private address, checks quota and concurrency, and starts the DAST engine. 5. The engine runs its checks (OWASP Top 10, SQLi, XSS, security headers, and more) by sending probes back through the tunnel; the agent executes them locally against your app. 6. The agent writes `wasviking-scan.sarif` and `wasviking-scan.json`, and the workflow uploads the SARIF to GitHub Code Scanning. ## Access posture - The agent only scans private targets: `localhost`, RFC1918, and link-local. Public addresses are rejected by the API target validator. - The mTLS bundle is ephemeral (60 minutes) and single-use. - No production traffic is intercepted. Only the test instance you start in the runner is scanned. - Revoke access at any time by revoking the API Key in the portal. ## Pre-requisites | Requirement | Detail | |---|---| | WASViking plan | CI/CD Pipeline Scans enabled (Pro or higher). | | Portal role | Admin or Manager, to issue API Keys and Sentinel tokens. | | GitHub repository | Permission to create Actions secrets/variables and files under `.github/`. | | Workflow permissions | `contents: read` and `security-events: write` (for the SARIF upload). | | Target app | Able to start in the runner and answer on `localhost:`. | | Runner | GitHub-hosted (`ubuntu-latest` recommended) or self-hosted Linux x86_64. | | Network egress | HTTPS to `api.wasviking.com`, `sentinel.wasviking.com`, and `github.com` on 443. No inbound is needed. | > Not on GitHub Actions? The same gate runs on any CI system where the > `wasviking-sentinel` binary can execute. Start from > [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). --- ## 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](https://docs.wasviking.com/static/docs/images/ci-dast/01-add-sentinel-agent.png) *Sentinel → Sentinel Agents → Add Sentinel Agent.* ![Sentinel agent created, showing the one-time bootstrap token](https://docs.wasviking.com/static/docs/images/ci-dast/02-sentinel-token.png) *The bootstrap token is shown only once. Copy it before closing.* --- ## Step 2: Create an API Key scoped to `ci:scan` (portal) This is the credential the scan itself runs 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 CI/CD pipeline`. Use one key per repository so you can revoke it without affecting other pipelines. | | Scopes | Select **`ci:scan`** only (`Trigger scans 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 3 as `WASV_DAST_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. ![System Settings, API Keys tab, with the key list](https://docs.wasviking.com/static/docs/images/ci-dast/03-api-keys.png) *Settings → System Settings → API Keys.* ![API Key scope picker with ci:scan selected](https://docs.wasviking.com/static/docs/images/ci-dast/04-key-scopes.png) *Select ci:scan only for a CI/CD pipeline key.* --- ## Step 3: Add the GitHub secrets and variable 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**. Under **Secrets**, add: | Name | Value | |---|---| | `WASV_SENTINEL_API_KEY` | The bootstrap token from Step 1. | | `WASV_DAST_API_KEY` | The API Key from Step 2. | Under **Variables**, add: | Name | Value | |---|---| | `WASV_TEMPLATE` | The **CI/CD slug** of a Scan Template from the portal, for example `ci-fast`. | Use Secrets (encrypted), never Variables, for the two keys. For multiple environments (staging, production), prefer **Environment secrets** with required reviewers and deployment protection rules. ### Picking a Scan Template A Scan Template is a reusable, named bundle of scan preferences (crawl, auth, analyzer selection, AI commentary), so the pipeline does not reconfigure those each run. Browse them in the portal under **Scans → Scan Templates**; the **CI/CD Slug** column is exactly the value you put in `WASV_TEMPLATE`. For pipeline gates, use **`ci-fast`**: it runs crawl, security headers, exposed ports, and TLS only, skipping the heavy active modules, so a per-PR or per-commit merge gate returns in seconds. Pair it with a scheduled full-coverage run (`full-coverage`) against staging to keep deep coverage without slowing down PRs. You can use the system templates as-is or create your own with **+ New Template** (optionally starting from an existing one). See [Scan profiles and templates](https://docs.wasviking.com/concepts/scan-profiles-and-templates/) for the full list and how to build one. ![Scan Templates list with the CI/CD Slug column](https://docs.wasviking.com/static/docs/images/ci-dast/05-scan-templates.png) *Scans → Scan Templates. The CI/CD Slug column is the WASV_TEMPLATE value.* --- ## Step 4: Add the workflow Create `.github/workflows/wasviking-dast.yml`. The example below starts a test app with Docker Compose; replace the **Start app** step with however your application boots in CI. ```yaml name: WASViking DAST on: push: branches: [main] pull_request: branches: [main] permissions: contents: read security-events: write actions: read jobs: dast: runs-on: ubuntu-latest timeout-minutes: 25 steps: - name: Checkout uses: actions/checkout@v4 - name: Start app run: | docker compose up -d for i in {1..30}; do curl -sf http://localhost:8080/ >/dev/null && break sleep 2 done - 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 scan env: WASV_DAST_API_KEY: ${{ secrets.WASV_DAST_API_KEY }} WV_TEMPLATE: ${{ vars.WASV_TEMPLATE }} run: | mkdir -p wasviking-reports ./.wasviking/wasviking-sentinel scan \ --api-key "$WASV_DAST_API_KEY" \ --template "$WV_TEMPLATE" \ --fail-on critical \ --baseline new \ --out ./wasviking-reports \ http://localhost:8080 - name: Upload SARIF to GitHub code scanning if: always() && hashFiles('wasviking-reports/wasviking-scan.sarif') != '' uses: github/codeql-action/upload-sarif@v4 with: sarif_file: wasviking-reports/wasviking-scan.sarif category: wasviking-dast - name: Upload raw reports as artifact if: always() uses: actions/upload-artifact@v4 with: name: wasviking-reports 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 instead. --- ## Step 5: Scan flags The scan command takes the target URL as its final argument: ``` wasviking-sentinel scan [flags] ``` | Flag | Required | Description | |---|---|---| | `` | Yes | Target URL. Must resolve to a private address. | | `--api-key` | Yes | API Key with the `ci:scan` scope. | | `--template` | Yes | Slug of a Scan Template from the portal, to reuse a standard scan config. | | `--auth-bearer` | No | Bearer token to run the scan authenticated (`Authorization: Bearer `). Prefer the `WV_AUTH_BEARER` env var so the token never lands in the process argv or CI logs. Overrides any auth in `--template`. | | `--auth-header` | No | Custom auth header in `Name: value` form, e.g. `X-Api-Token: `. Prefer the `WV_AUTH_HEADER` env var. Overrides any auth in `--template`. Use either `--auth-bearer` or `--auth-header`, not both. | | `--path` | No | Extra endpoint to scan **first**, before the auto-discovered surface. Repeatable (`--path /api/v1/users --path /api/v1/orders`) or via `WV_SEED_PATHS` (comma-separated). Relative paths or same-origin URLs; max 500. | | `--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 base branch count) or `all`. Default: `all`. | | `--out` | No | Output directory for SARIF and JSON. Default: current directory. | | `--timeout` | No | Total timeout. Default: `45m`. | Two files are produced: - `wasviking-scan.sarif`: SARIF 2.1.0 (GitHub Code Scanning, GitLab, and others). - `wasviking-scan.json`: full output with WASViking metadata. --- ## Authenticated scans An unauthenticated scan only sees what an anonymous visitor sees. To scan the area behind a login, the part that actually holds your business logic, the engine needs a credential. Instead of storing static credentials in a Scan Template, **mint a short-lived token in the pipeline and pass it to the scan**. The token stays valid only for the duration of the run. Two modes, pick one: | Mode | Flag | Env var (preferred) | Sent as | |---|---|---|---| | Bearer token | `--auth-bearer ` | `WV_AUTH_BEARER` | `Authorization: Bearer ` | | Custom header | `--auth-header 'Name: value'` | `WV_AUTH_HEADER` | `Name: value` (e.g. `X-Api-Token: …`) | **Always pass the token via the environment variable, not the flag.** A value on the command line ends up in the process argv and in the CI log; an environment variable does not. GitHub masks it further when you use `::add-mask::`. How it behaves: - **Overrides the template.** If a credential is supplied, it replaces the `authentication` block of your `--template` entirely; everything else in the template (crawl rules, analyzer selection, compliance profile) is preserved. So one template can serve both anonymous and authenticated pipelines. - **Encrypted at rest, never logged.** The secret travels over the same TLS channel as the API Key, is encrypted at rest, and is stamped only as a mode marker (`bearer`/`header`) in the run's audit trail, never the value. - **No silent downgrade.** On success the CLI prints `Authenticated scan confirmed (mode=bearer)`. If you request an authenticated scan but the server does not apply the credential, the CLI **fails the step (exit 2)** instead of running unauthenticated and reporting a false all-clear. ### Minting the token The right step depends on how your app issues tokens. Two common patterns, both against the instance you started in the runner: ```yaml # Option A: OAuth2 client-credentials (machine-to-machine) - name: Mint scan token (OAuth2) env: OAUTH_CLIENT_ID: ${{ secrets.SCAN_CLIENT_ID }} OAUTH_CLIENT_SECRET: ${{ secrets.SCAN_CLIENT_SECRET }} run: | TOKEN="$(curl -sf -X POST http://localhost:8080/oauth/token \ -d grant_type=client_credentials \ -d client_id="$OAUTH_CLIENT_ID" \ -d client_secret="$OAUTH_CLIENT_SECRET" | jq -r '.access_token')" test -n "$TOKEN" && test "$TOKEN" != null || { echo "token mint failed"; exit 1; } echo "::add-mask::$TOKEN" echo "WV_AUTH_BEARER=$TOKEN" >> "$GITHUB_ENV" ``` ```yaml # Option B: app login endpoint returning {"access_token": "..."} - name: Mint scan token (login) env: TEST_USER: ${{ secrets.SCAN_TEST_USER }} TEST_PASSWORD: ${{ secrets.SCAN_TEST_PASSWORD }} run: | TOKEN="$(curl -sf -X POST http://localhost:8080/api/login \ -H 'Content-Type: application/json' \ -d "{\"username\":\"$TEST_USER\",\"password\":\"$TEST_PASSWORD\"}" \ | jq -r '.access_token')" test -n "$TOKEN" && test "$TOKEN" != null || { echo "login failed"; exit 1; } echo "::add-mask::$TOKEN" echo "WV_AUTH_BEARER=$TOKEN" >> "$GITHUB_ENV" ``` Use a dedicated, low-privilege test account or client for scanning, never a real user or an admin credential. --- ## Prioritizing specific endpoints (seed paths) The engine auto-discovers your surface (crawl, `robots.txt`, `sitemap`, OpenAPI/Swagger, GraphQL introspection). But **API routes and SPA views often have no crawlable HTML links**, so the crawler never reaches them. List them explicitly with `--path` and they are scanned **first**, before the auto-discovered surface. ```bash --path /api/v1/users \ --path /api/v1/orders \ --path '/api/v1/admin/dashboard' ``` Or, equivalently, via the environment (comma-separated): ```bash WV_SEED_PATHS=/api/v1/users,/api/v1/orders,/api/v1/admin/dashboard ``` - **Relative paths** (`/api/v1/users`) or **same-origin URLs**; up to 500. - Seeded endpoints are crawled and actively tested first; the engine then follows their links, so authenticated sub-pages get discovered too. - Pair with authentication: the token unlocks the protected routes, and `--path` makes sure the scanner actually visits them. On success the CLI prints `Priority seed paths confirmed (N applied)`. --- ## Full example: authenticated scan with prioritized endpoints `.github/workflows/wasviking-dast-auth.yml`. This is the enterprise shape: a protected GitHub Environment for the secrets, a token minted at runtime, the developer's own API routes scanned first, and SARIF published to Code Scanning. ```yaml name: WASViking DAST (authenticated) on: push: branches: [main] pull_request: branches: [main] permissions: contents: read security-events: write actions: read jobs: dast-auth: runs-on: ubuntu-latest timeout-minutes: 25 # Environment secrets add required reviewers and deployment # protection to the scan credentials. Configure under # Settings → Environments → dast. environment: dast steps: - name: Checkout uses: actions/checkout@v4 - name: Start app run: | docker compose up -d for i in {1..30}; do curl -sf http://localhost:8080/ >/dev/null && break sleep 2 done # Mint a short-lived token against the instance under test. # Swap for your own auth flow (see "Minting the token" above). - name: Mint scan token env: TEST_USER: ${{ secrets.SCAN_TEST_USER }} TEST_PASSWORD: ${{ secrets.SCAN_TEST_PASSWORD }} run: | TOKEN="$(curl -sf -X POST http://localhost:8080/api/login \ -H 'Content-Type: application/json' \ -d "{\"username\":\"$TEST_USER\",\"password\":\"$TEST_PASSWORD\"}" \ | jq -r '.access_token')" test -n "$TOKEN" && test "$TOKEN" != null || { echo "login failed"; exit 1; } echo "::add-mask::$TOKEN" echo "WV_AUTH_BEARER=$TOKEN" >> "$GITHUB_ENV" - 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 authenticated WASViking scan env: WASV_DAST_API_KEY: ${{ secrets.WASV_DAST_API_KEY }} WV_TEMPLATE: ${{ vars.WASV_TEMPLATE }} # WV_AUTH_BEARER was exported by the "Mint scan token" step and is # masked. It is NOT passed as a flag, so it never reaches argv/logs. WV_SEED_PATHS: /api/v1/users,/api/v1/orders,/api/v1/admin/dashboard run: | mkdir -p wasviking-reports ./.wasviking/wasviking-sentinel scan \ --api-key "$WASV_DAST_API_KEY" \ --template "$WV_TEMPLATE" \ --fail-on high \ --baseline new \ --out ./wasviking-reports \ http://localhost:8080 - name: Upload SARIF to GitHub code scanning if: always() && hashFiles('wasviking-reports/wasviking-scan.sarif') != '' uses: github/codeql-action/upload-sarif@v4 with: sarif_file: wasviking-reports/wasviking-scan.sarif category: wasviking-dast - name: Upload raw reports as artifact if: always() uses: actions/upload-artifact@v4 with: name: wasviking-reports path: wasviking-reports/ ``` > Both `WV_AUTH_BEARER` and `WV_SEED_PATHS` are read from the environment > by the `scan` command, so the scan step stays free of secrets and long > path lists on the command line. To use a custom header instead of a > bearer token, export `WV_AUTH_HEADER` (e.g. `X-Api-Token: `) in > the mint step in place of `WV_AUTH_BEARER`. --- ## 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 PRs. | | `--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 (typically a couple of sprints). ## Baseline diff `--baseline` controls what counts toward the fail-on policy. | Mode | Behavior | |---|---| | `all` (default) | Every finding counts. Use for release branches, scheduled scans, and audits. | | `new` | Only findings absent from the base branch count. Use for day-to-day pull requests, to avoid friction with pre-existing debt. | --- ## 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 the `.wasviking/` directory, or any production traffic or data. Data is processed in the US region, encrypted in transit (TLS 1.2+) and at rest (AES-256). A DPA and EU data residency are available on request; see the [Trust Center](https://wasviking.com/trust-center/) for the full compliance mapping (ISO 27001, SOC 2, LGPD, GDPR, NIST SSDF, OWASP DSOMM). ## Common problems | Problem | Likely cause | |---|---| | `HTTP 401 Unauthorized` | API Key revoked, expired, or missing the `ci:scan` scope. | | `HTTP 400 target must be private` | The target URL resolved to a public address. Scan `localhost` or a private range. | | `HTTP 429 quota exceeded` | Monthly CI/CD scan quota reached. Wait for the cycle to roll over, upgrade, or buy an add-on pack. | | `HTTP 429 concurrency limit` | Too many simultaneous scans for your plan. | | Scan stuck in running | The target app did not respond. Add a health check before the scan step. | | Empty SARIF | The app was not reachable or returned only 5xx errors. | | `install.sh` download error | Network policy blocking `api.wasviking.com` or the release bucket. | | Findings differ between runs | Non-deterministic app behavior (random IDs, timestamps). | | Step exits 2, "server did not apply any credential" | You requested an authenticated scan but the token was not applied. Check the token was minted (not empty) and exported to `WV_AUTH_BEARER`/`WV_AUTH_HEADER`. | | `token mint failed` / `login failed` | The mint step got an empty token. Verify the auth endpoint URL, the test credentials, and that the app was healthy before this step. | | Authenticated pages still show as unreached | Token expired mid-run or lacks access to those routes; and list the routes with `--path`/`WV_SEED_PATHS` so the crawler visits them. | | `choose only one of --auth-bearer / --auth-header` | Both a bearer token and a custom header were supplied. Use exactly one. | ## Where this fits in the platform - The CI/CD scan quota and run history live under **User → CI/CD Pipeline** in the portal. - The same DAST gate on Bitbucket Pipelines is [CI/CD DAST with Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-dast/). - Scan Templates are managed under [Scan profiles and templates](https://docs.wasviking.com/concepts/scan-profiles-and-templates/). - Alert routing is documented under [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # CI/CD SCA, SBOM & Secrets with GitHub Actions Section: Getting Started Source: https://docs.wasviking.com/getting-started/github-actions-sca-sbom/ Summary: 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](https://docs.wasviking.com/getting-started/github-actions-dast/), 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** (one API Key scoped to `ci:scan`, `sca:submit`, and `secrets:submit`), then wire it into **GitHub Actions**. ## What this integration does - Generates a **CycloneDX 1.5 SBOM** from your manifests (npm, pip, go, composer, Maven, gem, pub) 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 downloads and installs the Sentinel agent using the API Key. 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. | | 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`, `yarn.lock`, `pnpm-lock.yaml`, `requirements.txt`, `Pipfile.lock`, `go.sum`, `composer.lock`, `pom.xml`, `Gemfile.lock`, `pubspec.lock`. The lockfile must be committed to the repository. | | 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? These gates run on any CI system where the > `wasviking-sentinel` binary can execute. There is a worked example for > [Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/), > and the vendor-neutral reference is > [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). --- ## Step 1: Create an API Key for the pipeline (portal) One API Key covers the whole flow: it downloads the agent installer and authenticates the SBOM and secrets submissions. No Sentinel agent token is involved in this integration. 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 **`ci:scan`** (`Trigger scans from a CI/CD pipeline`, also what authorizes the agent installer download), **`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 2 as `WASV_SCA_API_KEY`. The same key carries all three scopes, so it covers the agent install, the SBOM, and the secrets scan. > WASViking authenticates with the `Authorization: ApiKey ` 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](https://docs.wasviking.com/static/docs/images/ci-dast/03-api-keys.png) *Settings → System Settings → API Keys.* ![API Key scope picker with the pipeline scopes selected](https://docs.wasviking.com/static/docs/images/ci-sca/01-key-scopes.png) *Select the pipeline scopes when creating the key.* --- ## 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_SCA_API_KEY` | The API Key from Step 1 (carries `ci:scan`, `sca:submit`, and `secrets:submit`). | 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-sca.yml`: ```yaml 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_SCA_API_KEY: ${{ secrets.WASV_SCA_API_KEY }} run: | curl -fsSL -H "Authorization: ApiKey $WASV_SCA_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 4: 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: `high`. | | `--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). > Without `--app-name` the project is named after the checkout > directory. When that directory carries a build-location name > (`build`, `dist`, `target`, `vendor` and similar), the agent uses the > CI repository name instead, so the inventory does not fill up with > entries called "build". An explicit `--app-name` always wins, which is > why the workflow above passes one. ### `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 **Application Security → Hard-coded Secrets**. ## Exit codes | Code | Meaning | |---|---| | `0` | Success. Nothing at or above the `--fail-on` threshold. | | `70` | SCA gate: a KEV-flagged dependency at or above the threshold. Known exploited, treat as urgent. | | `71` | SCA gate: findings at or above the threshold, none of them in KEV. | | `73` | Secrets gate: a credential confirmed live by `--verify`. | | `74` | Secrets gate: matches at or above the threshold that were not verified. | | `79` | Coverage failure: the scan root could not be traversed, so nothing was scanned. Not returned for an empty project or one with no supported manifest. See [Coverage failures](https://docs.wasviking.com/sentinel/sentinel-ci/#coverage-failures-exit-79). | | `1` / `2` | Operational error (unreadable manifest, rejected key, invalid argument). | A failed OSV enrichment or a failed submission does not fail the build. Both warn, keep the artifacts on disk, and let the gate decide on what the run could see. --- ## 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](https://wasviking.com/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 `ci:scan` / `sca:submit` / `secrets:submit`. WASViking uses `Authorization: ApiKey `, 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 ` or the equivalent. | | `Drift detected: yes` every run | Your dependency set really is changing on every run, usually a lockfile regenerated during the build. Install with `npm ci` or the equivalent and commit the lockfile. Drift compares components against the previous submission with the same `--app-name` and ignores `--app-version`. | | Drift never reported | `--app-name` is not stable between runs, so each submission starts its own lineage with nothing to compare against. Derive it from the repository, not from the checkout path. | ## Where this fits in the platform - The software inventory lives under **Inventory → SBOM**; secret matches under **Application Security → Hard-coded Secrets**. - The DAST counterpart is [CI/CD DAST with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-dast/). - The same two gates on Atlassian are [CI/CD SCA, SBOM & Secrets with Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/). - Prefer no pipeline change at all? [Code Security](https://docs.wasviking.com/getting-started/connected-repositories/) runs the same SBOM and secrets engines server-side on the repositories you connect through the GitHub App or Bitbucket OAuth, on your plan cadence and on push; the pipeline gate stays the way to block a build. - Alert routing is documented under [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # CI/CD SCA, SBOM & Secrets with Bitbucket Pipelines Section: Getting Started Source: https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/ Summary: Run the WASViking Sentinel inside Bitbucket Pipelines to build a CycloneDX SBOM, check dependencies against OSV and CISA KEV, scan the working tree and git history for hard-coded credentials, and stop a vulnerable release before it reaches your main branch. This guide runs **Software Composition Analysis (SCA)** and a **secrets scan** inside a Bitbucket Pipelines build. The WASViking® Sentinel reads your dependency manifests, builds a CycloneDX SBOM, enriches it with OSV and CISA KEV, walks the working tree and the git history for hard-coded credentials, and fails the build before a vulnerable dependency or a leaked key reaches your main branch. Both gates are the same binary and the same API used in the [GitHub Actions guide](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/). Neither one is tied to a CI vendor: the agent runs as a one-shot command, authenticates with an organization API Key, and submits over HTTPS REST. What changes between vendors is the YAML around it, plus two Bitbucket details that are easy to miss and that this page calls out explicitly (the clone depth and secured variables). There is no mTLS tunnel in this flow. 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** (one API Key scoped to `ci:scan`, `sca:submit`, and `secrets:submit`), then wire it into **Bitbucket**. ## What this integration does - Generates a **CycloneDX 1.5 SBOM** from your manifests (npm, pip, go, composer, Maven, gem, pub) on pull requests and on pushes to `main`. - Enriches components with OSV and **CISA KEV** (known exploited vulnerabilities). - Scans the working tree and, with `--git`, the repository history for **hard-coded secrets**, submitting only redacted matches. - Optionally verifies a match against the provider's identity endpoint with `--verify`, so a rotated key does not read like a live incident. - Fails the build by severity with `--fail-on`, which blocks the merge. - Writes **SARIF 2.1.0** and the raw CycloneDX JSON as build artifacts. - Builds a consolidated software inventory per organization and detects **drift** between consecutive submissions. - Leaves nothing behind on the runner. The agent is installed per build and dies with the container. ## How it works 1. The build container downloads and installs the Sentinel agent using the API Key. 2. The agent runs a license preflight against the WASViking API. An approval is cached for 30 minutes under `~/.wasviking/`, which in a Bitbucket build means it is fetched fresh on every run. 3. It walks the project manifests and builds a CycloneDX SBOM. 4. It enriches components against OSV and applies CISA KEV validation. 5. It scans the tree, and the git history when `--git` is set, for hard-coded credentials. 6. It submits the SBOM and the redacted matches over HTTPS REST. The API validates quota, registers the snapshot, detects component drift, and promotes findings. 7. The agent writes the JSON and SARIF outputs into `--out`, and Bitbucket collects that directory as a build artifact. ## Access posture - Only manifests and the dependency graph are read. No source code, no repository variables beyond the key you pass, and nothing outside `.wasviking/` is collected. - Raw secret values never leave the runner. Submissions carry a hash and a masked preview. - Submission is HTTPS REST with an API Key header. There is no mTLS tunnel and no inbound connection to the runner. - `--verify` adds read-only calls to the provider identity endpoints of the matched credentials (for example the GitHub or AWS identity API). Drop the flag if outbound calls to third parties are not acceptable in your build network. - `--air-gapped` on the `sbom` gate guarantees zero external egress: no OSV lookup and no submission. - Revoke access at any time by revoking the API Key in the portal. ## Pre-requisites | Requirement | Detail | |---|---| | WASViking plan | SBOM and secrets submissions enabled (Pro or higher). | | Portal role | Admin or Manager, to issue API Keys. | | Bitbucket | Pipelines enabled on the repository, under **Repository settings → Pipelines → Settings**. | | Bitbucket permission | Admin on the repository, to create secured repository variables and commit `bitbucket-pipelines.yml`. | | Build image | Any Linux x86_64 image with `curl`, `sha256sum`, and `dpkg-deb` (or `ar`, from binutils). `atlassian/default-image:5` has all of them. | | Clone depth | `clone: depth: full` when you scan git history with `--git`. Bitbucket clones shallow by default. | | Project manifests | At least one supported lockfile: `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `requirements.txt`, `Pipfile.lock`, `go.sum`, `composer.lock`, `pom.xml`, `Gemfile.lock`, `pubspec.lock`. The lockfile must be committed to the repository. | | Network egress | HTTPS to `api.wasviking.com` and `api.osv.dev` on 443. No inbound is needed. | > On GitLab CI, Jenkins, CircleCI, or anything else that can execute a > Linux binary? The commands below are identical. Only the YAML dialect > changes. Start from > [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). --- ## Step 1: Create an API Key for the pipeline (portal) One API Key covers the whole build: it authorizes the agent installer download and both submissions. No Sentinel agent token is involved here, that belongs to the tunnel flow. Go to **Settings → System Settings → API Keys** and click **+ New Key**. | Field | Value | |---|---| | Label | Something you will recognise in six months, for example `Bitbucket SCA, checkout-api`. Use one key per repository so revoking it does not take down other pipelines. | | Scopes | **`ci:scan`** (`Trigger scans from a CI/CD pipeline`, which is also what authorizes the installer download), **`sca:submit`** (`Send SBOMs from the Sentinel agent. OWASP A06`), and **`secrets:submit`** (`Send hard-coded credential matches from the Sentinel agent. OWASP A07`). | | Expiration | 90 days is a sensible default. Rotate on that cadence. | Save and **copy the key once**. You will paste it into Bitbucket in Step 2 as `WASV_SCA_API_KEY`. > WASViking authenticates with the `Authorization: ApiKey ` header, > not `Bearer`. Scopes can be adjusted later with **Edit** without > changing the key value, so the pipeline keeps working. ![System Settings, API Keys tab, with the key list](https://docs.wasviking.com/static/docs/images/ci-dast/03-api-keys.png) *Settings → System Settings → API Keys.* ![API Key scope picker with the pipeline scopes selected](https://docs.wasviking.com/static/docs/images/ci-sca/01-key-scopes.png) *Select the three pipeline scopes when creating the key.* --- ## Step 2: Store the key as a secured repository variable Never commit a key to the repository. In Bitbucket, go to **Repository settings → Pipelines → Repository variables** and add: | Name | Value | Secured | |---|---|---| | `WASV_SCA_API_KEY` | The API Key from Step 1. | Yes | Tick **Secured** so the value is masked in the build log and cannot be read back from the UI. If several repositories share one key, a workspace variable works the same way, though a key per repository gives you a cleaner revocation story. One Bitbucket behaviour to plan for: pull request builds triggered from a **forked** repository do not receive secured variables. The first line of the step below is a guard that fails the build immediately in that case, instead of letting the scan run unauthenticated and report a false all-clear. --- ## Step 3: Add the pipeline Create `bitbucket-pipelines.yml` at the repository root: ```yaml image: atlassian/default-image:5 # Full Git history is required when using the secrets --git option. clone: depth: full definitions: steps: - step: &wasviking-security-scan name: WASViking SCA + SBOM + Secrets max-time: 20 script: - test -n "$WASV_SCA_API_KEY" # Run from the repository root. - cd "$BITBUCKET_CLONE_DIR" # WASViking API endpoint. - export WASV_API_BASE="https://api.wasviking.com" # Install WASViking Sentinel. - | curl -fsSL \ -H "Authorization: ApiKey $WASV_SCA_API_KEY" \ "$WASV_API_BASE/api/v1/sentinel/install.sh" | sh - mkdir -p wasviking-reports # Generate CycloneDX SBOM, run SCA, and submit results. - | ./.wasviking/wasviking-sentinel sbom \ --api "$WASV_API_BASE" \ --api-key "$WASV_SCA_API_KEY" \ --path . \ --app-name "$BITBUCKET_REPO_FULL_NAME" \ --app-version "$BITBUCKET_COMMIT" \ --fail-on high \ --submit \ --out ./wasviking-reports # Scan the working tree and Git history for hard-coded secrets. - | ./.wasviking/wasviking-sentinel secrets \ --api "$WASV_API_BASE" \ --api-key "$WASV_SCA_API_KEY" \ --path . \ --git \ --verify \ --fail-on high \ --submit \ --out ./wasviking-reports artifacts: - wasviking-reports/** pipelines: pull-requests: '**': - step: *wasviking-security-scan branches: main: - step: *wasviking-security-scan custom: wasviking-security-scan: - step: *wasviking-security-scan ``` Four choices in that file are worth understanding before you adapt it: - **`clone: depth: full`.** The `--git` flag walks commit history. With Bitbucket's default shallow clone the agent only sees the last commits, so a credential committed months ago and later removed from the working tree stays invisible. Drop both `depth: full` and `--git` together if you only want the current tree scanned, which makes builds faster. - **The YAML anchor** (`&wasviking-security-scan`). The step is declared once under `definitions` and referenced three times. Pull requests and `main` get the gate automatically, and the `custom` entry lets anyone run it on demand from **Pipelines → Run pipeline**, which is handy for the first rollout. - **`max-time: 20`.** A ceiling in minutes for the step. Repositories with deep history or many manifests should raise it. The agent has its own timeouts, 5 minutes for the SBOM pipeline and 10 for secrets, both adjustable with `--timeout`. - **`artifacts`.** Bitbucket keeps `wasviking-reports/**` attached to the build, so the SARIF and JSON outputs are downloadable from the **Artifacts** tab after the run. Commit the file and the pipeline starts on the next push or pull request. --- ## Step 4: Command flags ### `sbom` (SCA and SBOM) ``` wasviking-sentinel sbom [flags] ``` | Flag | Required | Description | |---|---|---| | `--api-key` | Yes | API Key with the `sca:submit` scope. Also reads `WASV_API_KEY`. | | `--api` | No | WASViking API base URL. Default: `https://api.wasviking.com`. | | `--path` | No | Directory scanned recursively for manifests. Default: current directory. | | `--app-name` | No | Project name in the CycloneDX metadata. `$BITBUCKET_REPO_FULL_NAME` gives you `workspace/repo`. Pass it: without it the agent falls back to the CI repository name, since the checkout directory on Bitbucket is always called `build` and would make a poor inventory label. | | `--app-version` | No | Project version in the metadata. `$BITBUCKET_COMMIT` ties the SBOM to the exact commit. | | `--fail-on` | No | Lowest severity that fails the build: `critical`, `high`, `medium`, `low`, `none`. Default: `high`. | | `--submit` | No | Send the SBOM to WASViking. Omit for a local-only run. | | `--out` | No | Output directory for the CycloneDX JSON and SARIF. Default: current directory. | | `--no-osv` | No | Skip OSV enrichment and ship a bare SBOM. | | `--from-cyclonedx` | No | Ingest an SBOM produced by another tool instead of generating one. | | `--air-gapped` | No | Fully offline. No OSV lookup and no submission. | | `--timeout` | No | Wall-clock ceiling for the SBOM pipeline. Default: 5 minutes. | Outputs: `wasviking-sbom.cdx.json` (CycloneDX 1.5) and `wasviking-sbom.sarif` (SARIF 2.1.0). ### `secrets` ``` wasviking-sentinel secrets [flags] ``` | Flag | Required | Description | |---|---|---| | `--api-key` | Yes | API Key with the `secrets:submit` scope. | | `--api` | No | WASViking API base URL. | | `--path` | No | Directory scanned recursively. Default: current directory. | | `--git` | No | Also walk the git history. Requires `clone: depth: full`. | | `--verify` | No | Confirm matches against the provider identity endpoints, read-only. | | `--fail-on` | No | Same scale and default (`high`) as the SBOM gate. | | `--submit` | No | Send redacted matches to WASViking. | | `--out` | No | Output directory. | | `--timeout` | No | Wall-clock ceiling. Default: 10 minutes. | Outputs: `wasviking-secrets.json` and `wasviking-secrets.sarif`. ## Exit codes Bitbucket fails the step on any non-zero exit, so these codes are what turn a finding into a blocked merge. | Code | Meaning | |---|---| | `0` | Nothing at or above the `--fail-on` threshold. | | `70` | SCA gate: a KEV-flagged dependency at or above the threshold. Treat as urgent, it is a known exploited vulnerability. | | `71` | SCA gate: findings at or above the threshold, none of them in KEV. | | `73` | Secrets gate: a credential confirmed live by `--verify`. | | `74` | Secrets gate: matches at or above the threshold that were not verified. | | `79` | Coverage failure: the scan root could not be traversed, so nothing was scanned. See below. | | `1` / `2` | Operational error: unreadable manifest, rejected API key, network failure on `--submit`. | ## Coverage failure (exit 79) A gate that scans nothing finds nothing, and without a check that reads exactly like a clean run. Exit 79 keeps those apart: the agent measures what it covered against what the scan root actually contained, and stops rather than reporting a pass over an empty traversal. It is not a finding, and it does not fire on an empty repository or on one with no supported manifest. Those walk correctly and pass, and the log reports how many entries were walked so you can see the difference. Bitbucket is worth one note here. `$BITBUCKET_CLONE_DIR` is always `/opt/atlassian/pipelines/agent/build`, so on this platform the scan root is called `build`, which is also a name the agent excludes **inside** projects. Exclusions apply to sub-directories only, so the checkout is scanned in full and the log states that the root name was recognised. A `build/` directory inside your repository is still excluded, as it should be. The full reference is [Coverage failures](https://docs.wasviking.com/sentinel/sentinel-ci/#coverage-failures-exit-79). ## 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, useful for the first week. | | `--fail-on critical` | Fails on critical only. | | `--fail-on high` | Fails on high and critical. The default, and a good one for pull requests. | | `--fail-on medium` | Fails on medium and above. Stricter, more friction. | | `--fail-on low` | Maximum posture. Realistic on new projects with no accumulated debt. | A rollout that tends to survive contact with a real team: start at `none` on pull requests to see the volume, move to `high` once the backlog is triaged, and keep `main` stricter than feature branches. ## Reading the results The portal is the system of record. Dependency data lands under **Inventory → SBOM** and credential matches under **Inventory → Secrets**, both with the promoted findings attached to your organization's posture. The SARIF files are written for tools that consume the format. Bitbucket does not ingest SARIF natively, so on this platform they are build artifacts you can download or hand to another tool, not an annotation layer on the pull request. The build log carries the summary line, and the portal carries the history. Drift compares the component set of this submission against the previous one carrying the same `--app-name`, and it ignores `--app-version` entirely. A commit that changes no dependency reports no drift even though the version string moved. That makes `--app-name` the value to keep stable. If it varies between runs, every submission starts its own lineage with nothing to compare against, and drift silently never fires. Deriving it from `$BITBUCKET_REPO_FULL_NAME`, as the pipeline above does, keeps it constant for the life of the repository. ## Air-gapped mode Build networks with no external egress can still produce an SBOM: ``` ./.wasviking/wasviking-sentinel sbom \ --path . \ --air-gapped \ --out ./wasviking-reports ``` `--air-gapped` skips the OSV lookup and the submission, working from the bundled KEV data. Use it on isolated build farms, or to validate locally before shipping an artifact by hand. ## What is and isn't collected WASViking does not collect your repository source code, repository variables beyond the key you pass, files outside `.wasviking/`, or any production data. The SBOM holds the dependency graph (package names, versions, licenses, manifest hashes). The secrets gate submits a hash and a masked preview, never the credential itself. 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](https://wasviking.com/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 | |---|---| | Step fails on the first line, before any output | `WASV_SCA_API_KEY` is not set. On a pull request from a fork, secured variables are not delivered to the build. | | `HTTP 401 Unauthorized` | Key revoked, expired, or missing `ci:scan`, `sca:submit`, or `secrets:submit`. The header is `Authorization: ApiKey `, not `Bearer`. | | `HTTP 402 quota exceeded` | Monthly submission quota reached. Wait for the cycle, upgrade, or add a pack. | | Secrets scan finds nothing in history | `clone: depth: full` is missing, so the shallow clone has no history to walk. | | `neither dpkg-deb nor ar is available` | A minimal build image (Alpine and similar). Install `binutils`, or switch the step to `atlassian/default-image:5`. | | `install.sh` download error | Network policy blocking `api.wasviking.com` or the release bucket. | | Step killed at 20 minutes | Deep history plus `--verify` on a large repository. Raise `max-time` and the agent `--timeout`, or drop `--verify` on pull requests and keep it on `main`. | | Exit 79 | The scan root could not be traversed. Check the `Root:` line against your repository layout, then check that the step can read the checkout. | | `manifest not detected` | No supported lockfile under `--path`. Commit your lockfiles. | | OSV timeout | `api.osv.dev` slow or unreachable. Use `--no-osv` for a bare SBOM, or `--air-gapped`. | | Findings differ between runs | A lockfile regenerated during the build. Install with `npm ci` or the equivalent, and commit the lockfile. | | Unexpected exit 70 on a small change | A new transitive dependency arrived through the lockfile and matches CISA KEV. Inspect with `npm ls ` or the equivalent. | ## Where this fits in the platform - The software inventory lives under **Inventory → SBOM**, credential matches under **Application Security → Hard-coded Secrets**. - The GitHub Actions version of this same pipeline is [CI/CD SCA, SBOM & Secrets with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/). - The cloud DAST gate is a third subcommand of the same binary, with the same API Key model. See [CI/CD DAST with Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-dast/) for the Bitbucket worked example, [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/) for the reference, and [CI/CD DAST with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-dast/) for the GitHub version. - Prefer no pipeline change at all? [Code Security](https://docs.wasviking.com/getting-started/connected-repositories/) runs the same SBOM and secrets engines server-side on the repositories you connect through the GitHub App or Bitbucket OAuth, on your plan cadence and on push; the pipeline gate stays the way to block a build. - Alert routing is documented under [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # Set up Code Security (SAST, SCA, secrets and SBOM) Section: Getting Started Source: https://docs.wasviking.com/getting-started/connected-repositories/ Summary: Connect GitHub or Bitbucket once and let WASViking clone and assess the repositories you choose on its own, with SAST, dependency analysis, secret detection and SBOM generation, on your plan's cadence and on every push. No CI change and no agent to install. Code Security is the pipeline-free way to assess your source code continuously. You connect your GitHub account (through the WASViking GitHub App) or your Bitbucket Cloud workspace (through OAuth), pick which repositories WASViking may read, and WASViking® takes it from there: it clones the default branch into a private, short-lived workspace, runs **static application security testing (SAST)**, **dependency analysis**, **secret detection** and **SBOM generation**, and files the results in **Findings**, on the Code Security page and in your software inventory under the repository name. Nothing changes in your CI. Nothing is installed on your side. The repository becomes an asset in your inventory, like a target. The capability itself is described in [Code Security](https://docs.wasviking.com/capabilities/code-security/). ## When to use which | You want | Use | |---|---| | Block a merge or a release on a vulnerable dependency or a leaked secret | The pipeline gates ([GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/), [Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/)) | | Continuous coverage of every repository, including the ones without a pipeline, with SAST on the application source | Code Security (this page) | | Both | Both. Results merge under the same repository name in Code Security, the inventory and Findings, so a repository covered by a gate and by a connection shows one lineage, one drift history and one set of findings. | ## What WASViking does with a connected repository - Clones the **default branch** (shallow by default; full history only when git-history secret scanning is on) into an isolated scratch area on a dedicated worker. Nothing from the repository is ever executed. - Runs **SAST** on the application source: automated code discovery ranks the files that matter for security, the analysis reviews them and reports each weakness with the file and line, the route and parameter, why it is exploitable, an attack scenario and the fix. Every file with an open finding is reviewed again on each scan, and the remaining review budget rotates across the rest of the repository. - Runs the **SBOM engine**: dependency manifests (npm, pip, go, composer, Maven, gem, pub) become a CycloneDX SBOM, enriched with OSV and CISA KEV, with drift detection against the previous scan of the same repository. - Runs the **secrets engine** on the working tree, and on the git history when your plan includes it and you switch it on for that repository. - Records the results under **Application Security → Code Security** (coverage, last run, components, open findings by severity, SAST findings, risk), **Inventory → Software Bill of Materials**, **Application Security → Hard-coded Secrets** and **Findings** (filter by repository), with the branch, commit and provider attached to every submission. - Deletes the clone and every intermediate file at the end of the run, also when the run fails or times out. Only derived data stays: the findings with their evidence, the component list, vulnerability matches, redacted secret evidence and the run record. ## Before you start - A plan that includes Code Security (Pro and above; a partner or our team can enable it on other plans). Your **Repositories** tab under *Account → Subscription* shows the allowance and what is in use. - An account role with the **Connected Repositories** capability (Admins and Managers edit, Analysts view). - On GitHub: you must **own** the account or organization that owns the repositories. WASViking confirms that during the connect flow, so a member who is allowed to install the App but does not own the organization cannot complete the connection; ask an owner to finish it. On Bitbucket: access to the workspace and permission to add repository webhooks. ## Step 1: connect the provider 1. Open **Settings → System Settings → Connected Repositories**. 2. Click **Connect GitHub**. You are sent to GitHub, choose the account, choose **Only select repositories** (recommended) and pick the ones WASViking may read. GitHub returns you to WASViking and the connection appears with the granted repositories. The App asks for **Contents: read**, **Metadata: read** and **Members: read** only. The last one is how WASViking confirms that whoever connects an organization owns it; it is never used to read a repository. 3. Or click **Connect Bitbucket**. You are sent to Bitbucket, approve the WASViking consumer, and (if you belong to several workspaces) choose the workspace on return. The consumer asks for **Account: read**, **Repositories: read** and **Webhooks: read and write**. Adding repositories later: edit the installation on GitHub (the list syncs on its own) or click **Sync** on the connection. Bitbucket lists every repository of the workspace you can read. ![Settings, Connected Repositories tab, with an active GitHub connection showing repositories granted, how many are monitored and the last sync](https://docs.wasviking.com/static/docs/images/connected-repos/01-connect-provider.png) *Settings → System Settings → Connected Repositories: the connection, what it granted, and where to sync or disconnect.* The connection card is the record of what you handed over: which account it belongs to, how many repositories the grant covers, how many of those you are monitoring, and when the list was last synced. **Manage on GitHub** takes you back to the installation to add or remove repositories, and **Disconnect** ends the access from our side and revokes the runner key. ## Step 2: choose what to monitor Open **Application Security → Code Security**. Every granted repository is listed; switch **Monitored** on for the ones WASViking should assess. Each monitored repository counts one unit against your plan allowance, whatever its size and however often it is scanned. **Git history**: on plans that include it, switch it on per repository to run the secrets engine on the full history of the default branch as well. It is heavier (a full clone) so keep it for the repositories where leaked credentials in past commits matter. ![Code Security page listing each granted repository with Monitored and Git history switches, last scan, components, open findings by severity, WASViking SAST Findings and risk](https://docs.wasviking.com/static/docs/images/connected-repos/02-code-security.png) *Application Security → Code Security: one row per repository, with the switches, the last scan and what it found.* The row is where you read the result without leaving the page: the commit that was scanned, how many components were catalogued, the open findings split by severity, the **WASViking SAST Findings** by weakness class with when the code was last reviewed, and the risk score. A **Drift** marker means the dependency set changed since the previous scan, and **KEV** means at least one finding is on the CISA catalogue of exploited vulnerabilities, which is the one to look at first. **Open findings** jumps to the Findings list already filtered by that repository. The tiles at the top summarise the fleet: how many repositories the connections granted, how many you monitor, open findings across all of them, components tracked, SAST findings, runs this month, and how much of your plan allowance is in use. ## Step 3: scan now, then let it run - **Scan now** on a monitored repository queues a run immediately (one manual run per repository every 10 minutes). The row shows the run moving through *Queued*, *Cloning*, *Scanning* and *Recording results*, and the **Runs** panel keeps the history: trigger, branch, commit, duration, components, secret matches, SAST findings, findings promoted, and a plain reason when a run does not complete. - **On the plan cadence**: every monitored repository is scanned at least once per interval (Pro every 24 hours, Business every 4 hours, custom on Enterprise), with your plan's number of runs at a time. - **On push**: a push to the default branch triggers a scan right away when the interval since the last scan has elapsed; otherwise the push is remembered and the next scheduled slot picks it up, so a busy repository is never scanned more often than the plan cadence. Server-side runs consume the same monthly SBOM and secrets submission allowances as the pipeline gates. When an allowance is used up the run records that reason and the next run retries after the reset. ## What happens when access changes - Uninstalling the GitHub App, or suspending it, is picked up automatically: the connection shows the state, monitored repositories are released and no further clone is attempted. - Repositories removed from the installation (or deleted, archived, transferred) are marked *No longer granted* and stop counting. - **Disconnect** in Settings uninstalls the App (GitHub) or drops the tokens and removes the push webhooks (Bitbucket), releases the repositories and deactivates the runner credential. Your inventory, SBOMs, secrets history and findings stay. - Closing your WASViking account revokes every provider grant as one of the immediate closure effects. ## Security notes - Provider credentials never reach your browser and are stored encrypted. WASViking mints a short-lived, repository-scoped token for each run; it is passed to git through the process environment, never on a command line, and dropped when the clone finishes. - Clones use https to `github.com` / `bitbucket.org` only, with prompts, LFS and submodules disabled and a size ceiling per repository. - Each run has its own credential for submitting results (a system key scoped to the connection, hidden from the API Keys page and revoked on disconnect), so a run can never write outside your organization. - Live credential verification (calling third-party providers to confirm a leaked secret still works) is off for server-side runs. - On an organization, the connection is accepted only from an owner, and that check is the sole use of the App's **Members: read** permission. Seeing an installation is not the same as owning it: GitHub shows an organization installation to every member who can reach one covered repository, so without the ownership proof any employee could bind your organization to a WASViking tenant of their own and read everything the installation grants. ## Where this fits in the platform - Coverage, SAST findings and runs: **Application Security → Code Security**; components under **Inventory → Software Bill of Materials**; secret matches under **Application Security → Hard-coded Secrets**; triage under **Findings** with the repository filter. - Usage and plan capacity: **Account → Subscription → Repositories**. - Reading and closing the first SAST result, step by step: [Triage your first SAST finding](https://docs.wasviking.com/getting-started/triage-your-first-sast-finding/). - The pipeline gates remain the way to block a build: [GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/) and [Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/). - Alert routing is documented under [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # Triage your first SAST finding Section: Getting Started Source: https://docs.wasviking.com/getting-started/triage-your-first-sast-finding/ Summary: Open the first weakness WASViking found in your source code, read the evidence down to the file and line, decide what to do with it and confirm it closes on its own after the fix. Once [Set up Code Security](https://docs.wasviking.com/getting-started/connected-repositories/) has produced its first run, the weaknesses WASViking® found in your source code are waiting in **Findings**, next to everything the platform found in traffic. This page walks one of them from the list to closure: where to find it, how to read the evidence, how to record a decision and how the platform confirms the fix without anyone reopening the ticket. ## Before you start - A monitored repository with at least one completed run. The row on **Application Security → Code Security** shows *Completed* under **Last scan** and a count under **WASViking SAST Findings**. - Access to **Findings**. Reading is open to every role; changing the status or the owner of a finding follows the permissions of your role. ## Step 1: open the SAST findings of a repository The shortest path starts on the Code Security page. On the row of the repository, the chips under **WASViking SAST Findings** name the weakness classes found (for example IDOR, BOLA, BAC, Clickjacking). Click **Open findings** to land in **Findings** already filtered by that repository, by the category **Broken Access Control (IDOR, BOLA, BFLA, clickjacking)** and by the status **Open and active**. From **Findings** itself, set **Category** to the same value, pick the repository under **Repository** and leave **Status** on *Open and active*. The counter above the table tells you how many findings match and the active filters appear as chips you can remove one by one. Each row shows the risk score, the severity, the title, the OWASP category and the asset. **Sort** defaults to *Severity first*, so the finding to look at first is at the top. ![Findings filtered by repository and by the access control category, with the panel of the first finding open on its overview, summary and recommendation](https://docs.wasviking.com/static/docs/images/code-security/01-sast-finding-evidence.png) *Findings filtered by repository and category. Clicking a row opens the panel with the overview, the summary and the recommendation.* ## Step 2: read the evidence Click the row. The panel opens on the right with everything the analysis knows about the weakness. **Overview** carries the fields the rest of the platform uses for every finding: risk score, severity, status, category, CWE, OWASP category, confidence, asset, owner, SLA window, due date, SLA state, first seen, last seen, times seen and the last scan that saw it. **Summary** states the weakness in one paragraph with the file, the line and the parameter. **Recommendation** is the fix for that exact code, not a generic guideline. Scroll to the block titled **WASViking SAST Findings**. This is the source-level evidence: - the weakness class, then **Location** (file and line), **Route / function**, **Attacker input** (the parameter an attacker controls) and **Language**; - **Why this is exploitable**, in plain language, and the **Attack** scenario that reproduces it; - **Vulnerable code**, the excerpt with the vulnerable line highlighted; - **References** to the OWASP and CWE entries behind the class, and the **Audit log** of the finding, starting with the scan that created it. Every quoted line was verified against the real file before the finding was created. If the analysis could not ground a weakness in the code, it was discarded, so what you see in this block is in the repository at the commit shown under **Last scan**. ![The WASViking SAST Findings block of a finding: weakness class, file and line, route and parameter, why it is exploitable, the attack scenario and the code excerpt with the vulnerable line highlighted](https://docs.wasviking.com/static/docs/images/code-security/02-sast-evidence-block.png) *The evidence block: location, route, attacker input, why it is exploitable, the attack scenario and the vulnerable code with the line highlighted.* Reading order that works: the attack scenario tells you whether the weakness is reachable in your deployment; the route and parameter tell you which request to try; the excerpt tells the developer where to look. ## Step 3: decide and record the decision The bottom of the panel holds the remediation controls. Pick **New status**, add a **Comment (audit log)** and click **Save**. | Decision | What to set | What happens next | |---|---|---| | Someone will fix it | **In progress**, plus an **Owner** | The finding stays in the open count and the SLA clock keeps running until it is resolved. | | The risk is understood and accepted for now | **Accepted risk**, with **Accepted for (days)** | The finding leaves the open count and returns for review when the acceptance expires. | | The analysis is wrong for this code | **False positive**, with a comment explaining why | The finding is closed with that reason on record. | | The code is already fixed | **Resolved**, or leave it and let the next scan confirm it (Step 4) | Resolved findings reopen on their own if the weakness comes back. | **Severity override** lets you raise or lower the severity when your context justifies it; the original stays on record. Every change lands in the **Audit log** section of the panel with who did it and when, and new or reopened findings reach Slack, Teams, Jira, ServiceNow or a webhook through the same notification rules as every other finding. ## Step 4: fix the code and let the platform confirm it Push the fix to the default branch. A push triggers a scan right away when the interval since the last scan has elapsed; otherwise click **Scan now** on the repository row under **Application Security → Code Security**. The file that carried the open finding is reviewed again on every scan. The finding resolves on its own once two consecutive reviews of the same file no longer find the weakness. Nothing to click. If the weakness returns in a later commit, the same finding reopens with its history intact, so the team sees a regression, not a new item. The SLA clock and the risk score follow the same rules as a finding from a scan of the running application. ## Step 5: hand over the record **Report** on the repository row produces a PDF with every open finding of that repository by engine, including the SAST section with the evidence, for a code owner or an auditor. For a fleet view, the **WASViking SAST Findings** tile on the Code Security page counts open weaknesses across every monitored repository. ## Where to go next - The capability in full: [Code Security](https://docs.wasviking.com/capabilities/code-security/). - How risk scores, SLAs and statuses work for every finding: [Findings and Risk Score](https://docs.wasviking.com/concepts/findings-and-risk-score/). - Routing findings to your tools: [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/), [Jira](https://docs.wasviking.com/integrations/jira/), [ServiceNow](https://docs.wasviking.com/integrations/servicenow/), [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # CI/CD DAST with Bitbucket Pipelines Section: Getting Started Source: https://docs.wasviking.com/getting-started/bitbucket-pipelines-dast/ Summary: Run an automated WASViking Sentinel DAST scan inside Bitbucket Pipelines against the app you bring up on localhost, over an ephemeral mTLS tunnel. The app is never exposed publicly, the build fails on real findings, and results land in the portal with SARIF and JSON as build artifacts. This guide runs a WASViking® DAST scan inside a Bitbucket Pipelines build, against the application you start on `localhost` in the runner, and fails the pipeline when it finds real vulnerabilities. The app is never exposed to the public internet: the Sentinel agent opens an ephemeral mTLS tunnel and the DAST engine sends its probes back through it. It is the same binary, the same API, and the same one-shot `scan` subcommand as the [GitHub Actions guide](https://docs.wasviking.com/getting-started/github-actions-dast/). What changes between vendors is the YAML around it, plus two Bitbucket details this page calls out explicitly: you bring the app up as a **service** so it answers on `localhost`, and credentials live in **secured repository variables**. The setup has two halves, and the order matters: configure the **WASViking portal first** (one API Key scoped to `ci:scan`), then wire it into **Bitbucket**. ## What this integration does - Runs a DAST scan on demand, on pull requests, or on pushes to `main`, against the build you stand up in the runner. - Fails the build by severity with `--fail-on`, so a vulnerable release is blocked before merge. - Compares against the base branch with `--baseline`, so a pull request only fails on what it introduces, not pre-existing debt. - Runs **authenticated scans** with a short-lived token your pipeline mints at runtime (`--auth-bearer` / `--auth-header`), so protected areas behind a login are actually reached, with no static credentials in a config. - **Prioritizes the endpoints you list** with `--path`, scanning your own API routes first, before the auto-discovered surface. Essential for authenticated APIs and SPAs with no crawlable HTML links. - Emits **SARIF 2.1.0** plus a full JSON report as build artifacts. - Provisions and tears down the agent per run, with no persistent credentials left on the runner. - Meters against your CI/CD scan quota and records every run in the portal. ## How it works 1. The build container downloads and installs the Sentinel agent using the API Key. 2. The agent provisions an ephemeral mTLS bundle (valid 60 minutes, not reusable) from the WASViking API. 3. The agent opens a gRPC over mTLS tunnel to the WASViking tunnel server. 4. The agent requests a scan against your `localhost` target. The API validates that the target is a private address, checks quota and concurrency, and starts the DAST engine. 5. The engine runs its checks (OWASP Top 10, SQLi, XSS, security headers, and more) by sending probes back through the tunnel; the agent executes them locally against your app. 6. The agent writes `wasviking-scan.sarif` and `wasviking-scan.json`, and Bitbucket collects that directory as a build artifact. ## Access posture - The agent only scans private targets: `localhost`, RFC1918, and link-local. Public addresses are rejected by the API target validator, which is intentionally stricter than the persistent on-premise flow. - The mTLS bundle is ephemeral (60 minutes) and single-use. - No production traffic is intercepted. Only the test instance you start in the runner is scanned. - No source code, no repository variables beyond the key you pass, and nothing outside `.wasviking/` is collected. - Revoke access at any time by revoking the API Key in the portal. ## Pre-requisites | Requirement | Detail | |---|---| | WASViking plan | CI/CD Pipeline Scans enabled (Pro or higher). | | Portal role | Admin or Manager, to issue API Keys. | | Bitbucket | Pipelines enabled on the repository, under **Repository settings → Pipelines → Settings**. | | Bitbucket permission | Admin on the repository, to create secured repository variables and commit `bitbucket-pipelines.yml`. | | Build image | Any Linux x86_64 image with `curl`. `atlassian/default-image:5` works. | | Target app | Able to start in the runner and answer on `localhost:`, whether as a Bitbucket service or a container you launch in the step. | | Network egress | HTTPS to `api.wasviking.com` and `sentinel.wasviking.com` on 443. No inbound is needed. | > On GitLab CI, Jenkins, CircleCI, or anything else that can execute a > Linux binary? The commands are identical. Only the YAML dialect > changes. Start from > [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). --- ## Step 1: Create an API Key scoped to `ci:scan` (portal) One API Key covers the whole build: it authorizes the agent installer download and the scan itself. No Sentinel agent token is involved. The CI flow provisions an ephemeral agent on the fly from this key, so there is no persistent agent to register. Go to **Settings → System Settings → API Keys** and click **+ New Key**. | Field | Value | |---|---| | Label | Something you will recognise later, for example `Bitbucket DAST, checkout-api`. Use one key per repository so revoking it does not take down other pipelines. | | Scopes | **`ci:scan`** (`Trigger scans from a CI/CD pipeline`, which also authorizes the installer download). Least privilege: a DAST pipeline needs nothing else. | | Expiration | 90 days is a sensible default. Rotate on that cadence. | Save and **copy the key once**. You will paste it into Bitbucket in Step 2 as `WASV_DAST_API_KEY`. > WASViking authenticates with the `Authorization: ApiKey ` header, > not `Bearer`. If you already run the > [SCA / SBOM / Secrets pipeline](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/), > its key already includes `ci:scan`, so you can reuse the same variable > instead of issuing a second key. ![System Settings, API Keys tab, with the key list](https://docs.wasviking.com/static/docs/images/ci-dast/03-api-keys.png) *Settings → System Settings → API Keys.* ![API Key scope picker with ci:scan selected](https://docs.wasviking.com/static/docs/images/ci-dast/04-key-scopes.png) *Select ci:scan only for a CI/CD pipeline key.* --- ## Step 2: Store the key as a secured repository variable Never commit a key to the repository. In Bitbucket, go to **Repository settings → Pipelines → Repository variables** and add: | Name | Value | Secured | |---|---|---| | `WASV_DAST_API_KEY` | The API Key from Step 1. | Yes | | `WASV_TEMPLATE` | Optional. The **CI/CD slug** of a Scan Template, for example `ci-fast`. | No | Tick **Secured** on the key so the value is masked in the build log and cannot be read back from the UI. Leave `WASV_TEMPLATE` unsecured; it is not a secret, and seeing it in the log is helpful. One Bitbucket behaviour to plan for: pull request builds triggered from a **forked** repository do not receive secured variables. The first line of the step below is a guard that fails the build immediately in that case, instead of running the scan unauthenticated. ### Picking a Scan Template A Scan Template is a reusable, named bundle of scan preferences (crawl, auth, analyzer selection, AI commentary), so the pipeline does not reconfigure those each run. Browse them in the portal under **Scans → Scan Templates**; the **CI/CD Slug** column is exactly the value you put in `WASV_TEMPLATE`. For a per-PR or per-commit gate, use **`ci-fast`**: crawl, security headers, exposed ports, and TLS only, skipping the heavy active modules, so the gate returns in seconds. Pair it with a scheduled full-coverage run against staging. Leave `WASV_TEMPLATE` unset to use the built-in CI defaults. ![Scan Templates list with the CI/CD Slug column](https://docs.wasviking.com/static/docs/images/ci-dast/05-scan-templates.png) *Scans → Scan Templates. The CI/CD Slug column is the WASV_TEMPLATE value.* --- ## Step 3: Add the pipeline Create or extend `bitbucket-pipelines.yml` at the repository root. The example brings up **OWASP Juice Shop** as the app under test so you can run it end to end today; replace the service with however your own application boots in CI. ```yaml image: atlassian/default-image:5 definitions: services: # The application under test. Bitbucket makes a service reachable on # localhost, so the agent scans it at http://localhost:3000. Replace # this image with your own app; keep it intentionally on a private # port, never public. app-under-test: image: bkimminich/juice-shop:latest memory: 2048 pipelines: custom: wasviking-dast: - step: name: WASViking DAST max-time: 25 services: - app-under-test script: - test -n "$WASV_DAST_API_KEY" - export WASV_API_BASE="https://api.wasviking.com" - export TARGET_URL="http://localhost:3000" # Wait until the app answers before scanning. - | up="" for i in $(seq 1 60); do if curl -fsS "$TARGET_URL/" >/dev/null 2>&1; then echo "target is up after ${i} tries"; up=1; break fi sleep 3 done test -n "$up" || { echo "target never answered on $TARGET_URL"; exit 1; } # Install WASViking Sentinel. - | curl -fsSL \ -H "Authorization: ApiKey $WASV_DAST_API_KEY" \ "$WASV_API_BASE/api/v1/sentinel/install.sh" | sh - mkdir -p wasviking-reports # Run the DAST scan through the ephemeral mTLS tunnel. # --fail-on none keeps the first runs green while you review # the findings; switch to --fail-on high (or critical) to make # it a merge gate. WASV_TEMPLATE is optional. - | TEMPLATE_ARG="" if [ -n "$WASV_TEMPLATE" ]; then TEMPLATE_ARG="--template $WASV_TEMPLATE"; fi ./.wasviking/wasviking-sentinel scan \ --api "$WASV_API_BASE" \ --api-key "$WASV_DAST_API_KEY" \ --scan-type singlescan \ --fail-on none \ --baseline all \ --out ./wasviking-reports \ $TEMPLATE_ARG \ "$TARGET_URL" artifacts: - wasviking-reports/** ``` Four choices in that file are worth understanding before you adapt it: - **The service on `localhost`.** Bitbucket makes a service container reachable from the build step at `localhost` on the port the service listens on. That is why the agent, which executes the engine's probes locally, can scan `http://localhost:3000`. Some apps need memory; `memory: 2048` gives Juice Shop room, and for a heavier app you may need `size: 2x` on the step to enlarge the pool. If you prefer to launch the app yourself, add `- docker` to `services` and `docker compose up -d` the app instead, then scan the same localhost URL. - **A `custom` pipeline.** The scan runs on demand from **Pipelines → Run pipeline → Custom → wasviking-dast**, which is the right shape for the first rollout. Move the step under `pull-requests` or `branches: main` (with a YAML anchor, as the SCA guide shows) once it is proven. - **`--fail-on none` to start.** The first runs stay green so you can read the findings in the portal and in the artifacts without a red build masking the integration. Tighten to `high` or `critical` when the baseline is understood. - **`artifacts`.** Bitbucket keeps `wasviking-reports/**` attached to the build, so `wasviking-scan.sarif` and `wasviking-scan.json` are downloadable from the **Artifacts** tab after the run. Commit the file, then run it from **Pipelines → Run pipeline → Custom**. --- ## Step 4: Scan flags The scan command takes the target URL as its final argument: ``` wasviking-sentinel scan [flags] ``` | Flag | Required | Description | |---|---|---| | `` | Yes | Target URL, passed **last**. Must resolve to a private address (`localhost`, RFC1918, link-local). | | `--api-key` | Yes | API Key with the `ci:scan` scope. Also reads `WASV_API_KEY`. | | `--api` | No | WASViking API base URL. Default: `https://api.wasviking.com`. | | `--scan-type` | No | `singlescan` (default, recommended for CI) or `fullscan`. | | `--template` | No | Slug of a Scan Template, to reuse a standard scan config. Also reads `WV_TEMPLATE`. Empty = built-in CI defaults. | | `--fail-on` | No | Single threshold: `critical`, `high`, `medium`, `low`, or `none`. "Or above" logic: `high` fails on high and critical. Default: `critical`. | | `--baseline` | No | `all` (every finding counts, default) or `new` (only findings absent from the latest scan on the base branch). | | `--auth-bearer` | No | Bearer token to run authenticated. Prefer the `WV_AUTH_BEARER` env var so it never lands in argv or the log. Overrides any auth in `--template`. | | `--auth-header` | No | Custom auth header in `Name: value` form. Prefer `WV_AUTH_HEADER`. Use either `--auth-bearer` or `--auth-header`, not both. | | `--path` | No | Extra endpoint to scan **first**, before the auto-discovered surface. Repeatable, or via `WV_SEED_PATHS` (comma-separated). Relative paths or same-origin URLs; up to 500. | | `--out` | No | Output directory for SARIF and JSON. Default: current directory. | | `--timeout` | No | Total wall-clock timeout. Default: 45 minutes. | Two files are produced: `wasviking-scan.sarif` (SARIF 2.1.0) and `wasviking-scan.json` (full output with WASViking metadata). ## Exit codes Bitbucket fails the step on any non-zero exit, so these codes are what turn a finding into a blocked merge. | Code | Meaning | |---|---| | `0` | Nothing at or above the `--fail-on` threshold. | | `1` | Findings at or above the threshold, or an unmapped runtime failure. | | `2` | Invalid `--baseline` value. | | `70` | The `--template` slug was not found for your organization. | | `71` | The `--template` slug exists but your key may not use it. | If you also enable the local `--sca` or `--secrets` pre-checks on the same command, they run before provisioning and add their own exit codes (`70`/`71` for SCA, `73`/`74` for secrets), documented in the [SCA guide](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/#exit-codes). ## Authenticated scans An unauthenticated scan only sees what an anonymous visitor sees. To reach the area behind a login, **mint a short-lived token in the pipeline and pass it to the scan**, rather than storing static credentials in a template. | Mode | Flag | Env var (preferred) | Sent as | |---|---|---|---| | Bearer token | `--auth-bearer ` | `WV_AUTH_BEARER` | `Authorization: Bearer ` | | Custom header | `--auth-header 'Name: value'` | `WV_AUTH_HEADER` | `Name: value` (e.g. `X-Api-Token: …`) | **Always pass the token via the environment variable, not the flag.** A value on the command line ends up in the process argv and the build log; an environment variable does not. How it behaves: - **Overrides the template.** A supplied credential replaces the `authentication` block of your `--template` entirely; everything else in the template is preserved. One template can serve both anonymous and authenticated pipelines. - **Encrypted at rest, never logged.** The secret travels over the same TLS channel as the API Key and is stamped only as a mode marker (`bearer`/`header`) in the audit trail, never the value. - **No silent downgrade.** On success the CLI prints `Authenticated scan confirmed (mode=bearer)`. If the server does not apply the credential, the CLI **fails the step (exit 2)** instead of running unauthenticated and reporting a false all-clear. Mint the token against the instance you started in the runner, then export it before the scan step: ```yaml - | TOKEN="$(curl -fsS -X POST http://localhost:3000/api/login \ -H 'Content-Type: application/json' \ -d "{\"email\":\"$SCAN_TEST_USER\",\"password\":\"$SCAN_TEST_PASSWORD\"}" \ | python3 -c 'import sys,json;print(json.load(sys.stdin)["authentication"]["token"])')" test -n "$TOKEN" || { echo "login failed"; exit 1; } export WV_AUTH_BEARER="$TOKEN" ``` Use a dedicated, low-privilege test account for scanning, never a real user or an admin credential. Store `SCAN_TEST_USER` and `SCAN_TEST_PASSWORD` as secured repository variables. ## Prioritizing specific endpoints (seed paths) The engine auto-discovers your surface (crawl, `robots.txt`, `sitemap`, OpenAPI/Swagger, GraphQL introspection). But **API routes and SPA views often have no crawlable HTML links**, so the crawler never reaches them. List them explicitly with `--path` and they are scanned **first**: ```bash --path /rest/products/search \ --path /api/v1/orders ``` Or, equivalently, via the environment (comma-separated): ```bash export WV_SEED_PATHS=/rest/products/search,/api/v1/orders ``` Relative paths or same-origin URLs, up to 500. Pair with authentication: the token unlocks the protected routes, and `--path` makes sure the scanner actually visits them. On success the CLI prints `Priority seed paths confirmed (N applied)`. ## 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, useful for the first runs. | | `--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 `none` to see the volume, move to `critical`, then tighten to `high` once your baseline is clean (typically a couple of sprints). ## Baseline diff `--baseline` controls what counts toward the fail-on policy. | Mode | Behavior | |---|---| | `all` (default) | Every finding counts. Use for release branches, scheduled scans, and audits. | | `new` | Only findings absent from the latest scan on the base branch count. Use for day-to-day pull requests, to avoid friction with pre-existing debt. | ## Reading the results The portal is the system of record. Every run and its findings land under **User → CI/CD Pipeline**, with the run tagged as **Bitbucket Pipelines**, and its findings attached to your organization's posture. The SARIF file is written for tools that consume the format. Bitbucket does not ingest SARIF natively, so on this platform it is a build artifact you download from the **Artifacts** tab or hand to another tool, not an annotation layer on the pull request. The build log carries the scan summary (a severity breakdown and `Result: PASS/FAIL`), and the portal carries the full history. ## 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 the `.wasviking/` directory, or any production traffic or data. Only the test instance you start in the runner is scanned. Data is processed in the US region, encrypted in transit (TLS 1.2+) and at rest (AES-256). A DPA and EU data residency are available on request; see the [Trust Center](https://wasviking.com/trust-center/) for the full compliance mapping (ISO 27001, SOC 2, LGPD, GDPR, NIST SSDF, OWASP DSOMM). ## Common problems | Problem | Likely cause | |---|---| | Step fails on the first line, before any output | `WASV_DAST_API_KEY` is not set. On a pull request from a fork, secured variables are not delivered to the build. | | `target never answered on http://localhost:3000` | The app service did not come up in time. Raise the wait loop, give the service more `memory`, or check the service image starts cleanly. | | `HTTP 401 Unauthorized` | Key revoked, expired, or missing the `ci:scan` scope. The header is `Authorization: ApiKey `, not `Bearer`. | | `HTTP 400 target must be private` | The target resolved to a public address. Scan `localhost` or a private range. | | `HTTP 429 quota exceeded` | Monthly CI/CD scan quota reached. Wait for the cycle, upgrade, or buy an add-on pack. | | `HTTP 429 concurrency limit` | Too many simultaneous scans for your plan. | | Exit 70 | The `--template` slug does not exist for your organization. Fix the `WASV_TEMPLATE` value or unset it to use CI defaults. | | Scan stuck in running | The target app did not respond to probes. Confirm the health check passed before the scan step. | | Empty SARIF | The app was not reachable or returned only 5xx errors. | | `install.sh` download error | Network policy blocking `api.wasviking.com` or the release bucket. | | Step exits 2, "server did not apply any credential" | You requested an authenticated scan but the token was not applied. Check the token was minted (not empty) and exported to `WV_AUTH_BEARER`/`WV_AUTH_HEADER`. | ## Where this fits in the platform - The CI/CD scan quota and run history live under **User → CI/CD Pipeline** in the portal. - The GitHub Actions version of this same pipeline is [CI/CD DAST with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-dast/). - The dependency and secrets gates run on Bitbucket too, from the same binary and key model. See [CI/CD SCA, SBOM & Secrets with Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/). - The reference for the `scan` subcommand across CI systems is [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/). - Alert routing is documented under [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # CI/CD Mobile Security with GitHub Actions Section: Getting Started Source: https://docs.wasviking.com/getting-started/github-actions-mobile/ Summary: 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](https://docs.wasviking.com/getting-started/github-actions-dast/), 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 `.ipa` your 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 1. The runner installs the Sentinel agent using your `mobile:scan` key. 2. The agent asks the API to authorise one upload and receives a short-lived, single-purpose authorisation for one object. 3. The agent uploads the package straight to secure object storage. The bytes do not pass through the API or the CDN. 4. The API verifies the stored object, confirms it is a real application package, and queues the assessment against your organization. 5. 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. 6. The agent writes `wasviking-mobile.sarif` and `wasviking-mobile.json`, and the workflow uploads the SARIF to GitHub Code Scanning. ## Access posture - Only the application package you name with `--file` is 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 ` header, not `Bearer`. - 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-sentinel` binary can execute. See > [wasviking-sentinel mobile](https://docs.wasviking.com/sentinel/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. ```yaml 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-key` on 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](https://wasviking.com/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 `, 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](https://docs.wasviking.com/capabilities/mobile-security/). - The DAST counterpart is [CI/CD DAST with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-dast/); the dependency and secrets counterpart is [CI/CD SCA, SBOM & Secrets](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/). --- # SBOM Evidence Bundles Section: Getting Started Source: https://docs.wasviking.com/getting-started/sbom-evidence-bundles/ Summary: Answer a customer audit request with a signed, password-protected SBOM evidence package. Generate the bundle in the portal, deliver the link and the password through separate channels, and track every download. Sooner or later one of your customers asks what is inside your software. The request usually lands on your compliance inbox as a vendor security questionnaire, a third-party risk review, or an auditor asking for proof that you track the components you ship. Answering by hand means exporting spreadsheets, pasting versions into an email thread, and hoping nobody asks how that list was produced. An **SBOM Evidence Bundle** replaces that thread with one artifact: a cryptographically signed package of every CycloneDX SBOM submitted for a target during a period, wrapped with a cover page in your customer's name, tagged with the compliance frameworks they care about, and delivered through a password-protected link that records every download. The customer's auditor gets verifiable evidence. You get an audit trail showing exactly what was shared, with whom, and when. WASViking® holds none of the unlocking material: the share works on a token plus password split, so the link alone opens nothing. The frameworks referenced on the cover map to the audit requests you are most likely to receive: PCI DSS 6.3.3, ISO 27001 A.8.8, LGPD Art. 46, GDPR Art. 32, and BACEN CMN 4.893 Art. 3. ## Where this helps in the day to day - **A customer's compliance team requests third-party analysis of your software.** Generate a bundle scoped to that customer, send the link, and answer the request the same day. - **A procurement gate asks for an SBOM before contract renewal.** The signed package carries more weight than an exported file because the recipient can verify the signature and the SHA-256 offline. - **An auditor wants evidence for a specific window.** The period filter scopes the bundle to exactly the quarter or the release cycle under review. - **You need to prove later what was shared.** Delivered, expired, and revoked bundles keep their audit trail, so the history of every disclosure stays on record. ## Pre-requisites | Requirement | Detail | |---|---| | SBOM submissions | At least one CycloneDX submission for the target inside the chosen period. Submissions come from [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) or the [CI/CD pipeline](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/). | | Portal access | A role that can manage the SBOM inventory, such as Admin. | | A second channel to the customer | Phone, SMS, or a separate mailbox to deliver the password. The password must never travel in the same message as the link. | --- ## Step 1: Open the Evidence Bundles page In the portal, go to **Inventory → SBOM → Evidence Bundles** (`/portal/inventory/sbom/bundles/`). The page opens with four counters that summarize your sharing posture: **Total Bundles**, **Ready to Share** (active customer share links), **Delivered** (customers who downloaded at least once), and **Expired / Revoked** (kept for the compliance audit trail). Below them, the table lists every bundle with its status, target, customer, period, language, compliance tags, and download count. ![SBOM Evidence Bundles page with the four counters and the bundle table](https://docs.wasviking.com/static/docs/images/sbom-bundles/01-bundles-page.png) *Inventory → SBOM → Evidence Bundles.* On a fresh organization the table is empty and points you to the **+ Generate Bundle** button. That is the whole flow: one button per audit request. --- ## Step 2: Generate the bundle Click **+ Generate Bundle** and fill the form: | Field | What to enter | |---|---| | Target / Project | One target, or **All targets (organization-wide)** for a company-level disclosure. | | Period from / to | The submission window the bundle covers. It defaults to the last 90 days; narrow it to the quarter or release cycle the auditor asked about. | | Customer name (cover page) | Required. Printed on the bundle cover, so use the name the recipient expects to see, for example `Acme Bank Compliance`. | | Language | Language of the cover page and reports. | | Share lifetime (days) | How long the share link stays live. Default 30. | | Compliance frameworks | Tick the frameworks the request mentions (BACEN, GDPR, ISO27001, LGPD, PCI). Hover each one to see which controls it covers; the tags appear on the cover and on the recipient page. | | Share password (min 12 chars) | Accept the generated one or type your own. This is what you will deliver out-of-band. | | Notes for customer | Optional free text shown to the recipient. | Click **Generate**. The bundle is built asynchronously, and the share URL and password are shown on this screen **only once**. Copy both before closing the dialog. ![Generate SBOM Evidence Bundle dialog with target, period, customer name, frameworks, and share password](https://docs.wasviking.com/static/docs/images/sbom-bundles/02-generate-modal.png) *The share URL and password appear once. Copy both before closing.* What goes into the package: every CycloneDX submission in the period, a consolidated CycloneDX, the branded cover PDF, and the drift, findings, audit, and compliance CSVs, plus the `verification.txt` used to check the signature. The full contents and the cosign verification procedure are documented in [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/). --- ## Step 3: Deliver the link and the password separately Send the **share URL** through your normal channel, usually email. Deliver the **password** through a different one: a phone call, SMS, or a separate mailbox. Never put the two in the same message; if that email is ever forwarded or leaked, the link alone must be useless. Your customer's analyst lands on a page that states what they are about to receive before anything is unlocked: your customer name on the cover, the target, the period in UTC, the compliance tags, and the bundle size. They enter the password and click **Unlock and download**. The download is single use and nothing is cached on their device. ![Recipient unlock page showing customer, target, period, compliance tags, the password field, and the offline SHA-256](https://docs.wasviking.com/static/docs/images/sbom-bundles/03-recipient-unlock.png) *What your customer sees: the unlock page with the offline verification hash.* The page also shows the bundle's **SHA-256** so the recipient can verify the file offline after downloading. For audit teams that go further, the signature chain is verifiable with cosign; point them to [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/) for the exact command. --- ## Step 4: Track, expire, revoke Back on **Inventory → SBOM → Evidence Bundles**, the table is your disclosure record: - **Status** tracks the bundle through its life: building, ready to share, delivered, expired or revoked. - **Downloads** counts recipient access, and the **Delivered** counter tells you at a glance which customers actually collected their evidence. - The link dies on its own when the **share lifetime** runs out. If a request is withdrawn or a link was sent to the wrong contact, revoke the bundle; access stops immediately. - Expired and revoked bundles keep their audit trail. When your own auditor asks what was disclosed last year, the answer is on this page. If the same customer asks again next quarter, generate a new bundle with the new period. Each request gets its own artifact, its own password, and its own trail. ## Automating it Everything above is also available over the REST API (`POST /v1/sca/bundles` to issue, plus list, revoke, and audit endpoints), which is the path for teams that answer recurring audit requests on a schedule. The endpoints and payloads are documented in [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/), and the `bundle.created`, `bundle.accessed`, and `bundle.revoked` [webhook events](https://docs.wasviking.com/api-reference/webhook-events/) let your own systems react when a customer downloads their evidence. ## Where next - No submissions yet for the period? Start with [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) or wire the [CI/CD SCA and SBOM pipeline](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/). - Sharing posture instead of components: a [Posture Share](https://docs.wasviking.com/getting-started/posture-shares/) gives the same password-protected model over your dashboard and findings summary. - The framework tags on the cover come from the [compliance mapping](https://docs.wasviking.com/compliance/framework-mapping/). --- # Posture Shares Section: Getting Started Source: https://docs.wasviking.com/getting-started/posture-shares/ Summary: Share a read-only, password-protected view of your security posture with an auditor, a customer, or an investor. Create the link, choose how much detail the recipient sees, and keep the access log. A customer security review, an investor's due diligence, an auditor who wants to see your posture rather than receive another PDF. These requests all end the same way: someone outside your company needs to look at your security numbers, and giving them portal access is out of the question. A **Posture Share** solves this with a password-protected, read-only link to your aggregated posture: risk score, compliance summary, and asset coverage, rendered in the recipient's language. Each link is independent, with its own purpose, password, expiration, and access log. The recipient sees a curated report; they never touch your portal, your raw findings, or your evidence. WASViking® cannot recover a link's token or password after creation (one-way hash with argon2id, by design). That is a feature: if the original leaks, whoever holds it without the password holds nothing, and you can re-emit a fresh URL at any time while the old one dies automatically. ## Where this helps in the day to day - **A customer's vendor questionnaire.** Instead of filling the same spreadsheet every quarter, send a link that answers the recurring questions with live numbers. - **A sales conversation gets serious.** When the prospect's security team enters the room, a scoped link with minimal detail shows maturity without exposing recon-sensitive data. - **Investor or M&A diligence.** A time-limited, view-capped link gives the data room what it needs and expires when the process ends. - **The auditor handoff.** Pair a Posture Share (the live view) with an [SBOM Evidence Bundle](https://docs.wasviking.com/getting-started/sbom-evidence-bundles/) (the signed artifact) and the audit request is answered end to end. What the recipient sees and, just as important, what they never see (raw HTTP evidence, other targets, your audit log) is detailed in [Posture Shares](https://docs.wasviking.com/compliance/posture-shares/). ## Pre-requisites | Requirement | Detail | |---|---| | Posture data | Completed scans on the domain you intend to share, so the report has numbers to show. | | Portal access | A role that can manage Posture & Compliance, such as Admin. | | A second channel to the recipient | Phone, SMS, or a separate mailbox for the password. The password never travels with the URL. | --- ## Step 1: Open the Posture Shares page In the portal, go to **Posture & Compliance → Posture Shares** (`/portal/posture/shares/`). The table lists every link with its purpose, who created it, when it expires, how many views it served, failed password attempts, and its status. You can search by purpose and filter by status, which matters once different teams start issuing links for different audiences. ![Posture Shares page with the search, filter, and the share links table](https://docs.wasviking.com/static/docs/images/posture-shares/01-shares-page.png) *Posture & Compliance → Posture Shares.* One rule to know before you start: if a recipient loses a link or the password, there is nothing to "recover". Open the link's **Details** page and use **Re-emit** to generate a fresh URL and password; the old link is revoked automatically in the same action. --- ## Step 2: Create the share link Click **+ New share link** and fill the form: | Field | What to enter | |---|---| | Domain scope | **Whole organization (all domains)** for a company-level view, or a specific domain to scope the report to one product. The report is restricted to that domain and its subdomains, and the scope is immutable after creation. | | Purpose | An internal description, for example `Enterprise customer vendor questionnaire, Q2 2026`. Never shown to recipients; it is how you will find this link in the table a year from now. | | Password + confirm | Minimum 12 characters with at least 3 of: lowercase, uppercase, digit, symbol. A passphrase with 4 or more distinct words also works. | | Expiration | How long the link lives. 7 days is the recommended default; match it to the engagement, not longer. | | Maximum views (optional) | A hard cap on total accesses. The link auto-revokes when reached. Useful for diligence processes with a known audience size. | | Report language | Renders the share page and PDF in this language. Pick the recipient's language, not your own. | | Auto-update with new snapshots (live mode) | Checked, the link always serves the latest published snapshot, so an ongoing customer or auditor relationship never needs a reissue. Unchecked, the link is pinned to the current snapshot version, which is what you want for time-stamped audit evidence. | Then choose the **exposure level**, which controls how much detail the recipient sees: | Level | Shows | Right for | |---|---|---| | Minimal | Score, compliance, and asset coverage only. | Cold prospects, when you do not want to expose severity counts. | | Standard (recommended) | Adds severity counts and issue categories with open/closed counts and oldest open age. | Vendor security questionnaires, audit evidence, mid-relationship clients. Equivalent to mature SaaS Trust Center disclosures. | | Detailed | Adds remediation performance over the last 90 days: median time to remediate and SLA percentages. Figures appear once findings have been remediated in the window. | Ongoing customer relationships and post-incident communication. | Next, pick the **sections to include**. They sit on top of the classic report, follow the exposure level you chose, and are immutable after creation, like the scope: | Section | Shows | Available when | |---|---|---| | Security domains | Application Security and External Exposure, each with its own index on the same 0 to 100 scale, so the recipient reads where the posture comes from: web and API, repository review, software supply chain, mobile, leaks and brand abuse. | Always. | | Infrastructure | A fleet index with open vulnerability instances by severity, patch currency, and configuration hardening. Fleet-level percentages only: no host, operating system, package, or CVE is disclosed, and the number of hosts is never stated. | Your plan includes Infrastructure Defense and the scope is the whole organization. | | Active security controls | Which protective controls you operate, such as recurring assessments, edge monitoring, or a patch approval workflow. State only, never volumes. | The scope is the whole organization. Only controls the platform can see running are listed. | A domain that was never assessed reads "Not assessed" on the report; it is never shown as clean. Links created before sections existed keep rendering the classic report, so nothing new appears on a link that is already in someone's hands. The optional **alerts** keep you informed without watching the table: notify on first view, on access from outside your country, on more than 10 accesses in 24 hours, and if the link auto-revokes after repeated wrong passwords (this last one comes enabled). Click **Generate link**. The password is shown **only once** after creation; copy it before leaving the screen. ![New Posture Share Link form with domain scope, purpose, password, expiration, exposure level, and alerts](https://docs.wasviking.com/static/docs/images/posture-shares/02-new-share-form.png) *The password appears once, right after creation.* --- ## Step 3: Deliver the link and the password separately Send the **URL** through your normal channel and the **password** through a different one: a call, SMS, or a separate mailbox. If the email carrying the link is ever forwarded beyond your contact, the link alone opens nothing. The recipient lands on a minimal page that asks only for the password. After entering it, they get the read-only posture report in the language you chose. Nothing is cached on their device and there is nothing to download unless you shared the PDF rendering. ![Recipient page asking for the password before showing the shared posture report](https://docs.wasviking.com/static/docs/images/posture-shares/03-recipient-password.png) *What your recipient sees: view only, nothing cached.* --- ## Step 4: Track, re-emit, revoke Back on the table, each link tells its own story: - **Views** counts successful accesses; **Failed** counts wrong password attempts. A failed count climbing on a link that should be private is your early signal, and the brute-force alert revokes and notifies on its own. - **Expires** and the optional view cap end the link without any action from you. - **Re-emit** (on the link's Details page) answers the "the auditor lost the email" case: new URL, new password, old link revoked, one action. - Every access is logged on both sides, so when your own audit asks who saw your posture last year, the answer is in the table and in the audit log. Treat one link per audience as the habit: the purpose field, the password, and the access log stay meaningful when a link maps to one relationship. ## Automating it Posture Shares are created and managed in the portal; there is no public API for them today. What runs on its own is the follow-up: each link can notify you by email on the first view, on an unusual volume of accesses, and when it revokes itself after repeated wrong passwords, so nobody has to watch the table. ## Where next - The signed counterpart for component-level evidence: [SBOM Evidence Bundles](https://docs.wasviking.com/getting-started/sbom-evidence-bundles/). - What feeds the compliance summary on the report: [Framework mapping](https://docs.wasviking.com/compliance/framework-mapping/). - The full recipient-visibility contract and operator controls: [Posture Shares](https://docs.wasviking.com/compliance/posture-shares/). --- # Edge Threat Radar (Cloudflare) Section: Getting Started Source: https://docs.wasviking.com/getting-started/cloudflare/ Summary: Connect your Cloudflare account so the Edge Threat Radar can correlate adversary traffic with your posture. Read-only by default; one Edit scope for the optional approval-gated blocklist. The Cloudflare integration powers the [Edge Threat Radar](https://docs.wasviking.com/capabilities/edge-threat-radar/). WASViking® reads WAF, Firewall, and Bot signals from your Cloudflare account via the GraphQL API and correlates them with your open findings, amplifying the Risk Score when adversary traffic matches a real exposure. ## What this integration does - Pulls Cloudflare WAF, Firewall, and Bot events at a 5-minute cadence. - Correlates events to open Findings and amplifies the Risk Score. - Enriches IPs with AbuseIPDB and ThreatFox. - Powers the Edge Threat Radar dashboard, the AI assistant scoped to edge events, and multi-channel alerts. - Optionally maintains a **WASViking-managed dynamic IP blocklist** in your Cloudflare account, with one important guardrail: **no IP is blocked automatically.** Each block is gated on explicit customer approval, per event. ## Access posture - **Read-only by default.** No traffic is intercepted, no rules are pushed, no DNS is changed. - **One exception:** the optional dynamic blocklist needs an `Edit` scope on `Account Filter Lists`. It is used **only** to write IPs the customer has explicitly approved. - **Revocable at any time** from the Cloudflare side (delete the API token). ## Pre-requisites | Requirement | Detail | |---|---| | Cloudflare account | Active. | | Plan | Pro or higher (required for the API surfaces WASViking reads). | | Domains | Already proxied through Cloudflare. | | Cloudflare permissions | Administrative access to the account. | | WASViking plan | Edge Threat Radar module enabled (Pro plan and above). | --- ## Step 1: Enable the required Cloudflare features These three features must be on at the zone level. If any is off, the events WASViking reads will be empty. ### 1.1 WAF Cloudflare Dashboard → select your domain → **Security → WAF**. - WAF must be **Enabled**. - Under **Managed Rules**, enable: - Cloudflare Managed Rules - OWASP Core Rule Set ### 1.2 Firewall Rules **Security → WAF → Firewall Rules**. - Confirm there is at least one active rule. If none exist, create a minimal log-only rule. Empty rule sets produce empty event streams. ### 1.3 Bot Protection **Security → Bot traffic → Settings**. Enable the best option available on your plan: | Option | Available on | |---|---| | **Bot Fight Mode** (minimum) | Free, Pro | | **Super Bot Fight Mode** (recommended) | Pro, Business | | **Bot Management** (best signal) | Enterprise | --- ## Step 2: Create the API token WASViking reads data via the Cloudflare API (GraphQL). Create a dedicated **Custom Token**, read-only except for the optional blocklist. ### 2.1 Create the token - Cloudflare → **My Profile → API Tokens → Create Token**. - Select **Create Custom Token**. ![Cloudflare API Tokens screen with the Create Custom Token option](https://docs.wasviking.com/static/docs/images/edge-radar/01-create-custom-token.png) ### 2.2 Account permissions | Permission | Access | |---|---| | Account Analytics | Read | | Account Firewall Access Rules | Read | | Account WAF | Read | | DDoS Protection | Read | | Logs | Read | | Radar | Read | | Application Security Reports | Read | | Cloudforce One | Read | | DDoS Botnet Feed | Read | | Intel | Read | | DNS Firewall | Read | | URL Scanner | Read | | **Account Filter Lists** | **Edit** (only if you want the customer-approved dynamic blocklist; otherwise skip) | > The `Edit` on **Account Filter Lists** is the only non-read scope. > WASViking uses it solely to execute customer-approved IP blocks. > Each block is preceded by an email notification with the event > details and an explicit approve step. No IP is added without that. ### 2.3 Zone permissions | Permission | Access | |---|---| | Zone | Read | | Analytics | Read | | Logs | Read | | Firewall Services | Read | | Zone WAF | Read | | Bot Management | Read | | HTTP DDoS Managed Ruleset | Read | | SSL and Certificates | Read | | DNS | Read | | API Gateway | Read | | Page Shield | Read | | Fraud Detection | Read | | Managed Headers | Read | > **DNS** (Read) lets WASViking read the zone's DNS records as an extra > subdomain discovery source. It surfaces hosts hidden behind a wildcard > certificate, which public certificate logs cannot enumerate, and feeds > [Certificate Monitoring](https://docs.wasviking.com/capabilities/certificate-monitoring/). It stays > strictly read-only: no DNS record is ever created or changed. If this > scope is missing, discovery simply falls back to the public sources and > the rest of the integration is unaffected. > Do not grant any `Edit` scope you do not need. Least privilege wins. ### 2.4 Scope the token Under **Resources**: - **Include → Specific Account** → your account. - **Include → Specific Zones** → only the domains you want monitored. ### 2.5 Create and save the token - Click **Continue to summary → Create Token**. - **Copy the token immediately.** Cloudflare does not show it again. --- ## Step 3: Create the Cloudflare lists Create these lists in Cloudflare **before** you configure the portal. The portal's **Load Lists** button (Step 4) can only show lists that already exist in your Cloudflare account, so this step comes first. Both are Account Filter Lists, created under Cloudflare → **Security → Settings → IP Lists**. | List | Purpose | Needed | |---|---|---| | `wasviking_whitelist` | Your own infrastructure IPs and CIDR blocks. WASViking treats these as trusted, so they are never raised as adversary traffic. This is how you suppress false positives from known-good sources. | Recommended | | `wasviking_edge_blocklist` | The list the Edge Threat Radar writes to when you approve a risky IP for blocking in the portal. Create it even though it starts empty; the Radar populates it over time. | Required | ### 3.1 Infrastructure allowlist (recommended) Cloudflare → **Security → Settings → IP Lists → Create List**. | Field | Value | |---|---| | Name | `wasviking_whitelist` | | Kind | IP | | Description | `Trusted infrastructure IPs excluded from Edge Threat Radar detections` | Add every IP or CIDR block that belongs to your own infrastructure: office ranges, VPN egress, monitoring, CI runners, partner systems. Click **Create**. You can keep adding entries at any time; WASViking re-reads the list on each poll, so a known-good source flagged as a false positive can be cleared simply by adding it here. ### 3.2 Dynamic blocklist (required) Create this list even though it starts empty. The Edge Threat Radar writes to it whenever you approve a risky IP for blocking from the portal, so it must exist for that flow to work. Cloudflare → **Security → Settings → IP Lists → Create List**. | Field | Value | |---|---| | Name | `wasviking_edge_blocklist` | | Kind | IP | | Description | `Dynamic IP blocklist managed by WASViking Edge Threat Radar` | Click **Create**. The list appears with `kind = ip` and `0 records`. It stays empty until you approve your first block in the portal. The security rule that actually enforces this list is created later, in Step 5. ![Cloudflare Custom Lists screen with the wasviking_edge_blocklist entry](https://docs.wasviking.com/static/docs/images/edge-radar/04-custom-lists.png) *The new list under Manage account → Configurations → Lists.* --- ## Step 4: Configure on the WASViking side In the portal, go to **Settings → System Settings → Edge Threat Radar**. The **Cloudflare Edge Settings** card lists the zones already connected to this organization, each with its Zone ID, lookback window, status, and last run. ![WASViking System Settings, Edge Threat Radar tab, listing connected Cloudflare zones](https://docs.wasviking.com/static/docs/images/edge-radar/02-edge-settings-tab.png) *Settings → System Settings → Edge Threat Radar.* ### 4.1 Add a zone Click **+ Add Cloudflare Zone**. The **Cloudflare Edge Configuration** dialog opens. > **Fill in Zone ID, Account ID, and the API Token first, and check > they are correct.** The **Custom List** and **Infrastructure IP List** > dropdowns are populated by **Load Lists**, which reads your Cloudflare > account using exactly those three values. If any of them is wrong or > missing, the dropdowns come up empty, you cannot select the lists, and > the integration will not monitor as expected. Fill it in: | Field | Value | |---|---| | Name | A label for this zone (for example, the domain it covers). | | Zone ID | Cloudflare Dashboard → select the zone → **Overview → Zone ID**. | | Account ID | Cloudflare Dashboard → **Overview → Account ID**. | | API Token | Paste the token from Step 2.5. It is stored encrypted and never shown again after saving. | | Custom List | Click **Load Lists**, then select `wasviking_edge_blocklist` (created in Step 3.2). This is where the Radar writes IPs you approve for blocking. | | Infrastructure IP List | Click **Load Lists**, then select `wasviking_whitelist` (created in Step 3.1). This is what tells the Edge to treat those IPs as trusted and not raise them as threats. | | Default lookback (minutes) | How far back each poll reads. `60` is a good default. | | Enable this configuration for Edge Intel ingestion | Turn on to start ingestion for this zone. | > If the values are correct but **Load Lists** still returns nothing, > the API Token is missing the `Account Filter Lists` scope (Step 2.2). ![Cloudflare Edge Configuration dialog in the WASViking portal](https://docs.wasviking.com/static/docs/images/edge-radar/03-edge-config-dialog.png) *The Cloudflare Edge Configuration dialog opened from + Add Cloudflare Zone.* ### 4.2 Save and confirm Click **Save**. The zone returns to the list. Once the first poll succeeds, its row shows **Active** with a **success** badge and a **Last run** timestamp, and the **Edge Threat Radar** dashboard begins to populate within a few minutes. To change any field later, use **Edit** on the zone row. --- ## What WASViking collects Per Cloudflare event: - Source IP. - Country and ASN. - Attack type. - WAF rule triggered. - URL attacked. - Bot score. - Action applied by Cloudflare (challenge, block, log). - Timestamp. Each event is enriched with AbuseIPDB and ThreatFox reputation, correlated to open Findings, and surfaced on the Edge Threat Radar dashboard. --- ## Step 5: Enforce blocks with a security rule This rule is what makes Cloudflare act on `wasviking_edge_blocklist`. Without it, IPs you approve in the portal are added to the list but are never actually blocked at the edge. > This step comes after Step 3.2 on purpose. The rule's condition picks > the list from a selector, so `wasviking_edge_blocklist` must already > exist; if it was not created first, it will not appear there. ### 5.1 Create the security rule Cloudflare → **Security → Security Rules → Create rule → Custom rules**. Configure: | Field | Value | |---|---| | Rule name | `wasviking_edge_blocklist` | | Condition | `IP Source Address` **is in list** `wasviking_edge_blocklist` | | Action | **Block** | | Response type | Default Cloudflare WAF block page | | Response code | 403 | Add an exclusion for the WASViking scanner egress IPs so security tests are never caught by this rule. With the `not ... is in` rows added, the expression looks like: ``` (ip.src in $wasviking_edge_blocklist and not ip.src in {SCANNER_IP_1} and not ip.src in {SCANNER_IP_2} and not ip.src in {SCANNER_IP_3}) ``` Replace the placeholders with the current WASViking scanner egress IPs. ![Custom rule condition: IP Source Address is in list wasviking_edge_blocklist](https://docs.wasviking.com/static/docs/images/edge-radar/05-rule-condition.png) *Condition: IP Source Address is in list wasviking_edge_blocklist.* Click **Deploy**. ### 5.2 Keep trusted IPs from being blocked Your own infrastructure is already protected if you set the zone's **Infrastructure IP List** to `wasviking_whitelist` (Step 3.1 and 4.1): WASViking treats those addresses as trusted and never adds them to the blocklist. The security rule above also keeps a small set of WASViking scanner egress IPs out of the block via the `not ip.src in {...}` clause. Keep that clause in place so security tests are never blocked by your own rule. ### 5.3 Approval flow (how blocks actually happen) ``` Edge event detected │ ▼ WASViking risk amplification │ ▼ Notification email to the operator (event details + approve link) │ ▼ Operator approves in the portal │ ▼ WASViking writes the IP to wasviking_edge_blocklist │ ▼ Cloudflare custom rule blocks the IP at the edge ``` No IP reaches the blocklist without the operator step. The approve / reject decision is captured in the audit log on both sides. --- ## Your first results Once the zone is saved and enabled, the first poll runs within a few minutes and the **Edge Threat Radar** dashboard (**Dashboard → Edge Intelligence**) starts to fill in: a global attack map, classification breakdown, and a ranked list of top attackers with country, risk score, request volume, and last-seen time. From here you can open any IP for full intelligence and approve a block when warranted. ![Edge Threat Radar dashboard with the global attack map, classification breakdown, and top attackers](https://docs.wasviking.com/static/docs/images/edge-radar/04-edge-dashboard.png) *Dashboard → Edge Intelligence, populated a few minutes after setup.* --- ## Operating notes - **Cadence.** WASViking polls every 5 minutes per zone. - **Backfill.** On first connection, WASViking pulls the last 24 hours of events as initial baseline. - **Quota.** The Edge module meters per monitored domain. See the Usage tab. - **Multi-zone.** One token can carry multiple zones. Add or remove zone IDs in WASViking without re-issuing the token. ## Common problems | Problem | Likely cause | |---|---| | **Load Lists returns nothing** | Account ID wrong, or the token is missing the `Account Filter Lists` scope. | | **Zone row never turns Active / status stays in error** | Wrong token, token scope missing the right zones, or a Zone ID typo. Use **Edit** to correct and save again. | | Zone not found on first poll | Zone ID typo, or the zone is on a free plan that lacks the API surface. | | Empty Edge Threat Radar dashboard | WAF / Firewall Rules / Bot Protection not enabled at the zone level, or the ingestion toggle left off. | | `403` adding to the blocklist | `Account Filter Lists` scope set to Read instead of Edit. | | Blocks not applying | Custom rule not deployed, or `wasviking_edge_blocklist` not referenced. | | Legitimate scans blocked by the rule | Scanner egress IPs missing from the rule exclusion (Step 5.1), or trusted IPs missing from `wasviking_whitelist` (Step 5.2). | ## Revoking the integration - **Soft revoke:** in the WASViking portal, open **Settings → System Settings → Edge Threat Radar**, **Edit** the zone, and turn off **Enable this configuration for Edge Intel ingestion**. Reads stop; the configuration is kept so you can re-enable it later. - **Hard revoke:** delete the API token in Cloudflare. WASViking shows an error on the next poll and stops trying. No data is retained beyond the configured event retention window. ## Compliance posture - Aligned with the principle of least privilege. - All-read scopes by default; one optional Edit gated by explicit customer approval per event. - Compatible with ISO 27001, LGPD, GDPR, NIST, OWASP Top 10 requirements for monitoring and audit. ## Where this fits in the platform - The capability lives at [Edge Threat Radar](https://docs.wasviking.com/capabilities/edge-threat-radar/). - Risk amplification logic is documented under [Findings and Risk Score](https://docs.wasviking.com/concepts/findings-and-risk-score/). - Alert routing is documented under [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # Edge Threat Radar (Google Cloud Armor) Section: Getting Started Source: https://docs.wasviking.com/getting-started/google-cloud-armor/ Summary: Connect Google Cloud Armor so the Edge Threat Radar can correlate adversary traffic with your posture. Read-only by default; one optional role for the approval-gated IP blocking. The Google Cloud Armor integration powers the [Edge Threat Radar](https://docs.wasviking.com/capabilities/edge-threat-radar/) for workloads that sit behind a Google Cloud external Application Load Balancer instead of (or alongside) Cloudflare. WASViking® reads the load balancer request logs — where every Cloud Armor decision is recorded — via the Cloud Logging API and correlates them with your open findings, amplifying the Risk Score when adversary traffic matches a real exposure. If your edge is Cloudflare, follow the [Cloudflare guide](https://docs.wasviking.com/getting-started/cloudflare/) instead. You can run both at the same time: each connected zone or policy counts against the same Edge Threat Radar target allowance on your plan. ## What this integration does - Pulls Cloud Armor allow/deny decisions and WAF rule hits at a 5-minute cadence, straight from your load balancer request logs. - Correlates events to open Findings and amplifies the Risk Score. - Enriches IPs with AbuseIPDB and ThreatFox. - Powers the same Edge Threat Radar dashboard, AI assistant scope, and multi-channel alerts as the Cloudflare integration — data from both providers lands in one place. - Optionally maintains **WASViking-managed deny rules** in your Cloud Armor security policy, with the same guardrail as Cloudflare: **no IP is blocked automatically.** Each block is gated on explicit customer approval, per event. - Reads your **public Cloud DNS zones** (read-only) as an extra subdomain discovery source, surfacing hosts hidden behind wildcard certificates for [Certificate Monitoring](https://docs.wasviking.com/capabilities/certificate-monitoring/) — the same capability the Cloudflare integration gets from its **DNS (Read)** zone scope. ## Access posture - **Read-only by default.** The service account only needs permission to read logs and DNS zones. No traffic is intercepted, no rules are pushed, no infrastructure is changed. - **One exception:** the optional approval-gated blocking needs the **Compute Security Admin** role so WASViking can write deny rules to the policy. It is used **only** to execute blocks the customer has explicitly approved. - The Cloud DNS subdomain discovery uses only the read-only **DNS Reader** role: zones and records are listed, never created or changed, and private zones are never read. - **Revocable at any time** from the Google Cloud side (delete the service account key, or the service account itself). ## Pre-requisites | Requirement | Detail | |---|---| | Google Cloud project | Active, with billing enabled. | | Load balancer | A global external Application Load Balancer in front of the application. | | Cloud Armor | A security policy attached to the load balancer's backend service. | | Cloud DNS (if used) | Public zones hosted in the same project are read as an extra subdomain discovery source. Having the zones elsewhere is fine — discovery then relies on the public sources only. | | Google Cloud permissions | Enough access to create a service account and grant project IAM roles (Project IAM Admin or Owner). | | Service account keys allowed | The project must permit service account key creation. Many enterprise orgs block it by default — see Step 2.3 for the one-time exception an Org Policy Administrator grants. | | WASViking plan | Edge Threat Radar module enabled (Pro plan and above). | --- ## Google Cloud setup with gcloud Everything on the Google Cloud side is done from the command line, so you can run it against an **existing** project — the one that already hosts your load balancer and Cloud Armor policy. Open [Cloud Shell](https://shell.cloud.google.com/) (no local install needed) or a terminal with the `gcloud` CLI authenticated. Set your variables once and reuse them through the rest of this guide: ```bash # The project that hosts the load balancer and the security policy. # This is the project ID (e.g. my-company-prod), not the display name. export PROJECT_ID="your-project-id" # The existing objects you want to monitor. export SECURITY_POLICY="your-cloud-armor-policy" export BACKEND_SERVICE="your-backend-service" gcloud config set project "$PROJECT_ID" ``` --- ## Step 1: Turn on the signals at the source Cloud Armor has no events API. Every allow/deny decision it makes is written into the load balancer request logs, and those logs are what WASViking reads. Three settings have to be right for the logs to carry useful data. ### 1.1 Confirm the policy is attached to the backend service ```bash gcloud compute backend-services describe "$BACKEND_SERVICE" \ --global \ --format="yaml(name,securityPolicy)" ``` The `securityPolicy` line must point at your policy. If it is empty, attach it: ```bash gcloud compute backend-services update "$BACKEND_SERVICE" \ --global \ --security-policy="$SECURITY_POLICY" ``` > Use the exact `$SECURITY_POLICY` name later in the portal (Step 3). A > typo here is the most common reason an integration connects but stays > empty. ### 1.2 Enable request logging on the backend service If logging is off, Cloud Armor still protects you, but nothing is written for WASViking to read. ```bash gcloud compute backend-services update "$BACKEND_SERVICE" \ --global \ --enable-logging \ --logging-sample-rate=1.0 ``` Confirm it took: ```bash gcloud compute backend-services describe "$BACKEND_SERVICE" \ --global \ --format="yaml(logConfig)" # logConfig: # enable: true # sampleRate: 1.0 ``` > A sample rate below `1.0` logs only that fraction of requests. The > integration still works, but the Radar only sees the sampled slice. > For security visibility keep it at `1.0`. ### 1.3 Switch the policy to verbose logging By default Cloud Armor logs the decision but not the detail. Verbose logging adds the attacker's country and the specific WAF rules that fired — the context the Radar uses for geographic attribution and rule-level detail. This flag is CLI-only; the console has no toggle for it. ```bash gcloud compute security-policies update "$SECURITY_POLICY" \ --log-level=VERBOSE ``` > If you leave it on `NORMAL`, ingestion still works — the country and > triggered-rule columns just come up thinner. Google recommends verbose > mainly while tuning or troubleshooting, since it writes more request > detail into your logs; for edge threat visibility it is worth leaving on. ### 1.4 Confirm Cloud Armor decisions are being logged Send a request through the load balancer (or wait for real traffic), then read the load balancer logs. Confirming this now tells you the Google side is correct before you configure the portal: ```bash gcloud logging read \ 'resource.type="http_load_balancer" AND jsonPayload.enforcedSecurityPolicy.name="'"$SECURITY_POLICY"'"' \ --project="$PROJECT_ID" \ --freshness=30m \ --limit=20 \ --format="table( timestamp, httpRequest.remoteIp, httpRequest.requestMethod, httpRequest.requestUrl, httpRequest.status, jsonPayload.statusDetails, jsonPayload.enforcedSecurityPolicy.priority, jsonPayload.enforcedSecurityPolicy.outcome )" ``` You should see one row per request. A normal request looks like `OUTCOME: ACCEPT`; a blocked one like `OUTCOME: DENY` with `STATUS: 403` and `STATUS_DETAILS: denied_by_security_policy`. If you want to force a DENY to confirm blocking is logged, hit a path your WAF rules reject, for example: ```bash curl -i -A "sqlmap/1.8" "http://YOUR_LB_IP/" ``` > If this query returns nothing, stop here — the problem is on the > Google side (logging disabled, sample rate `0`, policy not attached, > or a policy-name typo), not in WASViking. Fix it before Step 3. --- ## Step 2: Create the service account and key WASViking authenticates to the Cloud Logging API with a dedicated service account, following the same pull model as the Cloudflare integration. Keep it separate from everything else so it can be revoked on its own. ### 2.1 Create the service account and grant the roles ```bash gcloud iam service-accounts create wasviking-edge-radar \ --display-name="WASViking Edge Threat Radar" \ --description="Read-only access to Cloud Armor load balancer logs and Cloud DNS zones" export SERVICE_ACCOUNT_EMAIL="wasviking-edge-radar@${PROJECT_ID}.iam.gserviceaccount.com" # Required: read the load balancer request logs. gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \ --role="roles/logging.viewer" # Required: read the public Cloud DNS zones for subdomain discovery. gcloud services enable dns.googleapis.com --project="$PROJECT_ID" gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \ --role="roles/dns.reader" ``` > **DNS Reader** mirrors the **DNS (Read)** zone scope of the > [Cloudflare integration](https://docs.wasviking.com/getting-started/cloudflare/): it lets > WASViking read the zones' records as an extra subdomain discovery > source. It surfaces hosts hidden behind a wildcard certificate, which > public certificate logs cannot enumerate, and feeds > [Certificate Monitoring](https://docs.wasviking.com/capabilities/certificate-monitoring/). It > stays strictly read-only: no DNS record is ever created or changed, > and **private zones are never read** — only public zones feed > discovery. If this project hosts no public Cloud DNS zones (your DNS > lives elsewhere), the grant is harmless and discovery simply keeps > working from the public sources. Add the optional role **only** if you want the approval-gated IP blocking. It lets WASViking write customer-approved deny rules into the policy; without it the integration is strictly read-only and the block button in the portal simply reports that it is unavailable. ```bash # Optional: only for one-click, approval-gated blocking. gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \ --role="roles/compute.securityAdmin" ``` > Least privilege wins. If you are not sure yet, grant the two required > read-only roles now and add `roles/compute.securityAdmin` later when > you decide to enable blocking. ### 2.2 Create the JSON key ```bash gcloud iam service-accounts keys create wasviking-edge-radar-key.json \ --iam-account="$SERVICE_ACCOUNT_EMAIL" ``` The file `wasviking-edge-radar-key.json` is what you paste into the portal in Step 3. Keep it only until the configuration is saved, then delete the local copy (`rm wasviking-edge-radar-key.json`). In Cloud Shell, use the file's **⋮ → Download** menu to bring it to your machine first. ### 2.3 If key creation is blocked Many organizations enforce the org policy `constraints/iam.disableServiceAccountKeyCreation` (Google enables it by default for organizations created on or after May 3, 2024). When it is active, the key command fails with: ``` FAILED_PRECONDITION: Key creation is not allowed on this service account. ``` If that happens, an **Organization Policy Administrator** must allow key creation for this project. This is an org-level change — a Project Owner cannot do it: ```bash gcloud services enable orgpolicy.googleapis.com --project="$PROJECT_ID" cat > allow-sa-keys.yaml < If your organization forbids service account keys entirely and will > not grant a per-project exception, contact WASViking support — a > keyless setup can be arranged. ### 2.4 Check the key is valid Confirm the file is a complete JSON key before pasting it — a truncated or empty file is the most common cause of a failed connection: ```bash python3 -c "import json,sys; d=json.load(open('wasviking-edge-radar-key.json')); sys.exit('Invalid key file') if not (d.get('private_key') and d.get('type')=='service_account') else print('OK', d['client_email'])" ``` --- ## Step 3: Configure on the WASViking side In the portal, go to **Settings → System Settings → Edge Threat Radar**. Below the Cloudflare card you will find the **Google Cloud Armor Settings** card, listing the policies already connected to this organization, each with its project, lookback window, status, and last run. ![WASViking System Settings, Edge Threat Radar tab, Google Cloud Armor Settings card](https://docs.wasviking.com/static/docs/images/edge-radar/10-gca-settings-card.png) *Settings → System Settings → Edge Threat Radar → Google Cloud Armor Settings.* ### 3.1 Add a policy Click **+ Add Cloud Armor Policy**. The **Google Cloud Armor Configuration** dialog opens. Fill it in: | Field | Value | |---|---| | Name | A label for this collector (for example, the application it covers). | | GCP Project ID | Your `$PROJECT_ID`. It is the ID (like `my-company-prod`), not the display name or the project number. | | Security Policy name | Your `$SECURITY_POLICY`, exactly as confirmed in Step 1.1. | | Service Account key (JSON) | Open `wasviking-edge-radar-key.json` from Step 2.2 and paste the **entire** JSON content. It is stored encrypted and never shown again after saving. | | Trusted infrastructure IPs/CIDRs | One IP or CIDR per line: office ranges, VPN egress, monitoring, CI runners. WASViking treats these as your own infrastructure — they are never raised as adversary traffic and never blocked. | | Default lookback (minutes) | How far back each poll reads. `60` is a good default. | | Enable this configuration for Edge Intel ingestion | Turn on to start ingestion for this policy. | > Unlike Cloudflare, there are no lists to load here — Cloud Armor has > no list concept. The trusted addresses live in this dialog, and the > blocklist lives inside the policy itself as WASViking-managed rules > (more on that below). ![Google Cloud Armor Configuration dialog in the WASViking portal](https://docs.wasviking.com/static/docs/images/edge-radar/11-gca-config-dialog.png) *The Google Cloud Armor Configuration dialog opened from + Add Cloud Armor Policy.* ### 3.2 Save and confirm Click **Save**. The policy returns to the list. Once the first poll succeeds, its row shows **Active** with a **success** badge and a **Last run** timestamp, and the **Edge Threat Radar** dashboard begins to populate within a few minutes. To change any field later, use **Edit** on the row — leaving the key field blank on edit keeps the stored key. --- ## What WASViking collects Per load balancer log entry: - Source IP. - Country (with verbose logging on). - URL and method attacked. - HTTP status returned. - The Cloud Armor decision (allow, deny, throttle, redirect). - The rule priority and the preconfigured WAF rule expressions that fired (with verbose logging on). - User agent. - Timestamp. Each event is enriched with AbuseIPDB and ThreatFox reputation, correlated to open Findings, and surfaced on the same Edge Threat Radar dashboard as your Cloudflare traffic. > Cloud Armor has no bot score equivalent to Cloudflare's Bot > Management. WASViking's own behavioral scoring (request rate, path > spread, sensitive-path probing, tooling signatures) fills that role, > so classification still works — it just leans on behavior instead of > an upstream score. --- ## How blocking works on Cloud Armor ### Enabling the block action (one-time) Blocking is **off until you grant it.** With only the two read-only roles from Step 2.1 the integration cannot change anything: the block button in the portal will report that the action is unavailable, and any attempt returns an HTTP `403` from Google. To turn it on, grant the blocking role to the **same service account** — no new key and no portal change are needed: ```bash gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \ --role="roles/compute.securityAdmin" ``` > IAM changes can take up to a minute to propagate. After that, the > **Block this IP at the edge** button on a Cloud Armor IP goes live and > writes to your policy. To confirm the policy is reachable, describe it: > `gcloud compute security-policies describe "$SECURITY_POLICY" > --format="value(name)"`. > Keep least privilege in mind: this role lets WASViking add and remove > deny rules on the policy. It never touches rules you authored (see > below). If you later want to make the integration read-only again, > remove the binding with `gcloud projects remove-iam-policy-binding` > using the same `--member`/`--role`. ### What a block does There is no separate blocklist to create. When you approve a block in the portal, WASViking writes a **deny rule** directly into your security policy: - Every managed rule is tagged `wasviking-managed` and placed at a high priority (starting at `100000`), so it never overrides the rules you authored. WASViking only ever reads or changes its own tagged rules — your rules are never touched. - The action is `deny(403)`. - There is a capacity limit on how many addresses can be blocked this way. If it is ever reached, you get a clear message in the portal instead of a silent failure. The approval flow is identical to Cloudflare: ``` Edge event detected │ ▼ WASViking risk amplification │ ▼ Notification email to the operator (event details + approve link) │ ▼ Operator approves in the portal │ ▼ WASViking adds a deny rule to the security policy │ ▼ Cloud Armor blocks the IP at the edge ``` No rule reaches the policy without the operator step. The approve / reject decision is captured in the audit log on both sides. > If WASViking also runs DAST scans against the application behind this > load balancer, add your own **allow** rule for the WASViking scanner > egress IPs at a priority **lower than 100000** (lower number = higher > precedence). That guarantees an approved block can never catch your > own security tests. --- ## Your first results Once the policy is saved and enabled, the first poll runs within a few minutes and the **Edge Threat Radar** dashboard (**Dashboard → Edge Intelligence**) starts to fill in: a global attack map, classification breakdown, and a ranked list of top attackers with country, risk score, request volume, and last-seen time. Traffic from Cloud Armor and Cloudflare shows up side by side. From here you can open any IP for full intelligence and approve a block when warranted. ![Edge Threat Radar dashboard with the global attack map, classification breakdown, and top attackers](https://docs.wasviking.com/static/docs/images/edge-radar/04-edge-dashboard.png) *Dashboard → Edge Intelligence, populated a few minutes after setup.* --- ## Operating notes - **Cadence.** WASViking polls every 5 minutes per policy, same as Cloudflare zones. - **Shared allowance.** Every connected collector — Cloudflare zone or Cloud Armor policy — consumes one Edge Threat Radar target from your plan. See the Usage tab. - **Lookback.** Each poll reads the window you configured (default 60 minutes); events already seen are de-duplicated, so overlapping windows are safe. - **Multiple policies.** Add one configuration per security policy. The same service account key can be reused across policies in the same project. ## Common problems | Problem | Likely cause | |---|---| | **Row never turns Active / status stays in error** | The pasted key is not the full JSON file (a blocked key creation can leave a 0-byte file — see Step 2.3/2.4), the key was deleted in Google Cloud, or the Project ID is wrong (remember: the ID, not the display name). Use **Edit** to correct and save again. | | Empty Edge Threat Radar dashboard | Request logging off on the backend service (Step 1.2), sample rate set to `0`, policy name typo (Step 1.1), or the service account missing `roles/logging.viewer`. Confirm with the log-read query in Step 1.4 first. | | Country column empty | Verbose logging not enabled — it is CLI-only (Step 1.3). | | Triggered rule IDs missing | Same cause: verbose logging off. | | `FAILED_PRECONDITION: Key creation is not allowed` | The `iam.disableServiceAccountKeyCreation` org policy is enforced (default for orgs created on or after May 3, 2024). An Org Policy Administrator grants a per-project exception — see Step 2.3. | | Block action fails | Service account missing `roles/compute.securityAdmin` (Step 2.1). | | Cloud DNS subdomains not appearing | Service account missing `roles/dns.reader`, the Cloud DNS API (`dns.googleapis.com`) not enabled on the project (Step 2.1), the zone lives in a different project, or the zone is **private** (private zones are never read). Discovery keeps working from the public sources meanwhile. | | "Block capacity reached" | The WASViking-managed deny rules are full. Review and prune the `wasviking-managed` rules in your policy. | | Legitimate scans blocked | Scanner egress IPs missing an allow rule below priority `100000`, or your own ranges missing from **Trusted infrastructure IPs/CIDRs** (Step 3.1). | ## Revoking the integration - **Soft revoke:** in the WASViking portal, open **Settings → System Settings → Edge Threat Radar**, **Edit** the policy, and turn off **Enable this configuration for Edge Intel ingestion**. Reads stop; the configuration is kept so you can re-enable it later. - **Hard revoke:** delete the key (or the whole service account) in Google Cloud. WASViking shows an error on the next poll and stops trying. No data is retained beyond the configured event retention window. ```bash # List the key IDs, then delete the one in use: gcloud iam service-accounts keys list \ --iam-account="$SERVICE_ACCOUNT_EMAIL" gcloud iam service-accounts keys delete KEY_ID \ --iam-account="$SERVICE_ACCOUNT_EMAIL" # Or remove the service account entirely: gcloud iam service-accounts delete "$SERVICE_ACCOUNT_EMAIL" ``` ## Compliance posture - Aligned with the principle of least privilege. - Read-only role by default; one optional admin role gated by explicit customer approval per event. - Compatible with ISO 27001, LGPD, GDPR, NIST, OWASP Top 10 requirements for monitoring and audit. ## Where this fits in the platform - The capability lives at [Edge Threat Radar](https://docs.wasviking.com/capabilities/edge-threat-radar/). - The Cloudflare flavor of this integration is documented at [Edge Threat Radar (Cloudflare)](https://docs.wasviking.com/getting-started/cloudflare/). - Risk amplification logic is documented under [Findings and Risk Score](https://docs.wasviking.com/concepts/findings-and-risk-score/). - Alert routing is documented under [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # WASViking AI Guardian Section: Getting Started Source: https://docs.wasviking.com/getting-started/ai-guardian/ Summary: Roll out the WASViking AI Guardian on employee laptops and desktops. Enable it in the portal, install the agent and the managed browser extension, validate detection and the first policy block, then operate it day to day (updates, identity mapping, advanced detection). The **WASViking® AI Guardian** gives you visibility and control over how employees use generative-AI tools on their work devices. A lightweight Sentinel agent runs on each host and pairs with a managed browser extension (Chrome, Edge, or Firefox). Together they classify AI exposure (which vendors are visited, what kind of content is involved, a risk score, and masked evidence) and forward that telemetry to WASViking over the secure mTLS tunnel. Privacy is built in: the agent forwards **classifications, scores and masked evidence only, never raw prompts, pasted content, or files**. The local audit log stays on the device; cloud forwarding and storage are governed by the master toggle in the portal. The setup has two halves, and the order matters: configure the **WASViking portal first** (enable AI Guardian, set the governance policy, and mint an API Key scoped to `ai_guardian:install`), then run a one-line **install command on each device**. You do *not* need to create a Sentinel Agent token for AI Guardian. For background on what AI Guardian detects and how the policy engine works, see [WASViking AI Guardian](https://docs.wasviking.com/capabilities/ai-guardian/). ## What this deployment does - Discovers and classifies **AI usage per device**: which AI vendors (OpenAI, Anthropic, Google, Microsoft, Perplexity, and more) are used, and how. - Forwards **privacy-preserving telemetry**: classifications, risk scores, and **masked** evidence. Raw prompts, pasted content, and files never leave the device. - Enforces **vendor governance**: classify each AI vendor as *approved* (sanctioned), *blocked* (flagged red), or leave it for *review* (the default: every known vendor is treated as Shadow AI). - **Force-installs and pins** the managed browser extension via the OS managed-policy channel so it cannot be removed by the user. - Protects the deployment with a **master password** required to uninstall the agent or lift the force-installed browser policy. - Governs agent self-updates with a **release channel** and an optional **pinned version** for homologated builds. - Surfaces everything in the portal under **AI Guardian** (Executive Summary, AI Applications, dashboard) and routes alerts through your notification channels. ## How it works 1. You enable AI Guardian and set the governance policy in the portal. 2. You mint an API Key scoped to `ai_guardian:install`. Creating the key shows a **one-time install command** that embeds a short-lived bootstrap. 3. On each device, the install command downloads the agent, registers it (mTLS identity) using the bootstrap, and stores the org API key in the OS secret store (Keychain / Credential Manager / Secret Service). 4. The installer registers the **native messaging host** and **force-installs** the published browser extension via managed policy. 5. The agent runs as a per-user service and the extension pairs with it over loopback. AI exposure is classified locally and forwarded to WASViking over the mTLS tunnel. 6. The portal correlates the telemetry against your vendor governance and raises alerts on blocked or Shadow-AI usage. ## Access posture - The agent forwards **classifications, scores, and masked evidence only**. Raw prompts, pasted content, and files never leave the device. - The local audit log (JSONL) stays on the device; the master toggle in the portal controls cloud forwarding and storage. Disabling it stops forwarding immediately; the **browser sensor never holds this control**. - Forwarding uses the secure **mTLS tunnel**, not a plain bearer call. - The **master password** is stored as a one-way hash and is never shown again; it gates uninstall and policy removal on managed hosts. - Revoke access at any time by toggling AI Guardian off, revoking the API Key, or uninstalling the agent with the master password. ## Pre-requisites | Requirement | Detail | |---|---| | WASViking plan | AI Guardian (AI browser monitoring) enabled for your organization. | | Portal role | Admin or Manager, to enable AI Guardian and issue API Keys. | | Device OS | Windows 10/11 (x64/arm64), macOS (Intel/Apple Silicon), or Linux (amd64/arm64). | | Browser | Google Chrome, Microsoft Edge, or Mozilla Firefox. | | Network egress | HTTPS to `api.wasviking.com` on 443 for registration and the mTLS tunnel. No inbound is needed. | | Privileges | A standard per-user install needs no admin rights. Force-installing the browser policy and the Windows Enterprise MSI use elevation/MDM. | --- ## Step 1: Enable AI Guardian (portal) In the portal, go to **Settings → System Settings → AI Guardian** and turn on the **WASViking AI Guardian** master toggle. The status reads **Enabled** once active. This toggle governs cloud forwarding and storage of AI-exposure telemetry. When it is off, registered agents stop forwarding to WASViking; the local audit log on each device is unaffected. The browser sensor cannot override this control. The change takes effect within a few minutes on every registered endpoint and is logged in the audit trail as `ai_guardian.toggle`. If you do not see the AI Guardian tab, the feature is not yet active on your plan; contact `support@wasviking.com` to have it activated. ![AI Guardian master toggle in System Settings, showing Status: Enabled](https://docs.wasviking.com/static/docs/images/ai-guardian/01-enable-toggle.png) *Settings → System Settings → AI Guardian → master toggle.* --- ## Step 2: Set a master password (recommended) In the same tab, under **Master password**, set a password of at least **12 characters** and click **Save**. It is stored as a one-way hash and **never shown again**. This password is **required to uninstall the agent or lift the force-installed browser policy** on a managed host. Without it, a user cannot remove the agent or disable the extension. Use **Remove master password** to clear it (and the uninstall protection). Keep the passphrase in your secrets manager. The portal stores only a one-way hash and the endpoint never sees the cleartext. If you lose it, set a new one in the portal before any agent can be uninstalled. ![Master password panel with New / Confirm fields and Current status: Set](https://docs.wasviking.com/static/docs/images/ai-guardian/02-master-password.png) *Master password: gates uninstall and policy removal.* --- ## Step 3: Configure agent updates (optional) Under **Agent updates**, choose how devices self-update: | Field | Purpose | |---|---| | Release channel | Cadence of the self-update flow: **Default (stable)**, **Stable**, **Beta**, or **Canary**. | | Pinned version | Forces a specific homologated build (for example, `1.5.20`) regardless of channel. Leave empty to follow the channel. | Both empty means the agent follows the default stable channel. Click **Save**. A pinned version that does not exist in the release catalog is treated as "no update available" rather than an error. ![Agent updates panel with Release channel dropdown and Pinned version field](https://docs.wasviking.com/static/docs/images/ai-guardian/03-agent-updates.png) *Agent updates: release channel and optional pinned version.* --- ## Step 4: Configure vendor governance Under **Vendor governance**, classify each AI vendor: | Classification | Effect | |---|---| | **Approved** | Usage is recorded as *sanctioned*. | | **Blocked** | Usage is recorded as *blocked* and shown red in the dashboard. **Blocked wins on overlap.** | | *Neither* (default) | The vendor is treated as **Shadow AI** and surfaced for review. | Tick the built-in vendors (OpenAI, Anthropic, Google, Microsoft, Perplexity, Quora, DeepSeek, xAI, OpenRouter, HuggingFace, Mistral, Lovable) in either column, and add any others in the **Custom vendors** textareas (one per line). Click **Save**. The agent picks up both lists on the next monitor restart (which includes any self-update). Events reclassify immediately on the next event after the restart. > Blocking a vendor here does **not** by itself stop an event; it > classifies and flags it. To actively enforce on the endpoint, create an > **AI Guardian Policy** rule (portal → **AI Guardian → AI Guardian > Policies**). Note that rule event matching is strict: an `event:paste` > rule does not cover prompt submissions or file uploads; use **Any** for > full coverage. ![Vendor governance with Approved and Blocked columns of vendor checkboxes and custom textareas](https://docs.wasviking.com/static/docs/images/ai-guardian/04-vendor-governance.png) *Vendor governance: approved, blocked, and custom vendors.* --- ## Step 5: Create an API Key scoped to `ai_guardian:install` (portal) This key authorizes installing the AI Guardian endpoint on a device. Go to **Settings → System Settings → API Keys** and click **+ New Key**. | Field | Value | |---|---| | Label | Something identifiable, for example `AI Guardian rollout` or one per fleet/team. | | Scopes | Select **`ai_guardian:install`**: *"Install the AI Guardian endpoint on a new machine. A one-time bootstrap is minted with the key, so the full install command is shown only here."* | Click **Create Key**. The portal shows the raw key **once**, together with the **install command** for Linux, macOS, and Windows (switch with the OS tabs). A short-lived bootstrap is minted when the install script is fetched, so **copy the command now**: it is shown only on this screen. You can reuse the same key to install on as many machines as you need while the key is active. The key is bootstrap only: once an agent is registered, it identifies itself by its mTLS client certificate, not by the API key, so you can rotate or revoke the install key without disconnecting endpoints that are already running. > Adding `ai_guardian:install` to an *existing* key later (via **Edit**) > does **not** reveal a new install command; the bootstrap is minted at > creation. To get a fresh command, create a new key. ![Create API Key modal with the ai_guardian:install scope checked](https://docs.wasviking.com/static/docs/images/ai-guardian/05-api-key-scope.png) *API Key scope picker with ai_guardian:install selected.* ![Key display modal showing the raw key and the OS-tabbed install command](https://docs.wasviking.com/static/docs/images/ai-guardian/06-install-command.png){: style="max-width:380px" } *The one-time install command, shown only at key creation.* --- ## Step 6: Install the agent on each device Run the command from Step 5 on the device. It downloads the agent, registers it over mTLS using the bootstrap, stores the API key in the OS secret store, installs the native messaging host, and **force-installs the browser extension** via managed policy. ### Linux / macOS ```bash curl -fsSL -H "Authorization: ApiKey wv_live_xxx" \ https://api.wasviking.com/api/v1/sentinel/ai-guardian/install.sh | sh ``` The agent runs as a per-user service (`systemd --user` on Linux, a LaunchAgent on macOS). No root or sudo is required for the standard install. On Linux, to keep it running after logout: `sudo loginctl enable-linger "$USER"`. Verify the agent: ```bash # Linux systemctl --user status wasviking-sentinel # macOS launchctl list | grep wasviking ``` On the first run, macOS may prompt for permission to allow the browser extension and the native messaging host. Approve both. ### Windows The Windows agent ships in two install vehicles. They differ in scope (per-user vs. per-machine), in boot mechanism (Scheduled Task vs. Windows Service), and in privilege requirements. Pick the vehicle that matches your operating model. | Aspect | PowerShell installer (`install.ps1`) | Enterprise MSI | |---|---|---| | Scope | Per-user. Each profile that monitors AI usage runs its own install. | Per-machine. One install covers every user that signs in. | | Privilege | Runs without local administrator. | Local administrator required. MDM-pushed installs already run as `SYSTEM`. | | Install path | `%LOCALAPPDATA%\WASViking\Guardian\` | `%ProgramFiles%\WASViking\Guardian\` for binaries, `%ProgramData%\WASViking\Guardian\` for config, certs, and logs. | | Boot mechanism | Scheduled Task `WASViking AI Guardian`, trigger `AtLogon`, runs in the user session. | Windows Service `WASVikingGuardian`, start type `Automatic`, runs as `LocalSystem`. | | Native Messaging registration | `HKCU\Software\\NativeMessagingHosts`. | `HKLM\SOFTWARE\\NativeMessagingHosts` (machine-wide). | | API key on disk | Windows Credential Manager, envelope-encrypted by the agent. | Bootstrap is consumed at install time. The mTLS client certificate is the running identity. The API key is not persisted on the endpoint. | | Distribution | Manual or via scripted runner, one user at a time. | Intune Win32 LOB, SCCM Application, Group Policy software installation. | | Right fit for | Single-machine evaluation, IT-administered laptops, contractor BYOD. | Managed fleet, regulated environments, SOC and audit posture. | #### Option A: Single user, single machine (`install.ps1`) Run from a PowerShell session as the user who will be monitored. The script registers the AI Guardian Scheduled Task in that user's session and stores the organization API key in Windows Credential Manager. The force-install of the browser policy prompts for UAC elevation. ```powershell iex (irm -Headers @{Authorization='ApiKey wv_live_xxx'} ` https://api.wasviking.com/api/v1/sentinel/ai-guardian/install.ps1) ``` Verify: ```powershell Get-ScheduledTask "WASViking AI Guardian" Get-ChildItem "$env:LOCALAPPDATA\WASViking\Guardian\" wasviking-sentinel ai-browser key-status ``` The `key-status` command reports the credential storage backend and never prints the key. #### Option B: Fleet deploy, per-machine (MSI) Prerequisites: | Requirement | Value | |---|---| | OS | Windows 10 21H2 or later, Windows 11, Windows Server 2019 or later. | | Architecture | x64. Windows 11 ARM64 runs the x64 agent transparently through Prism. | | Privileges | Local Administrator, or `SYSTEM` under MDM. | | Outbound network | TCP 443 to your tenant gRPC endpoint and to the WASViking artifact CDN for cert bundle fetch. No inbound ports. | | API key | An organization API key with the `ai_guardian:install` scope from Step 5. | Steps: 1. Sign in to the portal and go to **Sentinel Agents > Get Agent > AI Guardian MSI (x64)** to download the installer. 2. Stage the `.msi` on your distribution share or in your MDM artifact store. 3. Push the install with `msiexec` and the properties from the table below. The MSI self-enrolls the endpoint during install, so no post-install scripting is required. ```powershell msiexec /i \wasviking-ai-guardian__amd64.msi /qn ` /l*v "%TEMP%\guardian-install.log" ` WASV_API_KEY="" ` WASV_API="" ``` MSI properties: | Property | Required | Value | |---|---|---| | `WASV_API_KEY` | Yes | Organization API key with the `ai_guardian:install` scope. | | `WASV_API` | No | Tenant API base URL. Defaults to `https://api.wasviking.com`. | **Intune.** Package the MSI as a Win32 LOB app. Set the install command to the `msiexec` line above. Set the uninstall command to `msiexec /x {} /qn`. Retrieve `` from any installed machine with: ```powershell Get-WmiObject Win32_Product -Filter "Name='WASViking AI Guardian'" | Select-Object IdentifyingNumber ``` The `ProductCode` regenerates on every MSI build to enable `MajorUpgrade`. The `UpgradeCode` is stable and is what Intune, SCCM, and Group Policy use for upgrade detection. **SCCM.** Create an Application of type Windows Installer (`.msi`). On the Programs tab, use the `msiexec` line above as the install command. Set the detection method to "Windows Installer" with the `ProductCode` field empty so SCCM detects upgrades by `UpgradeCode`. **Group Policy software installation.** Publish or assign the `.msi` under **Computer Configuration > Software Installation**. Pass `WASV_API_KEY` and `WASV_API` through a transform (`.mst`) generated with Orca, or wrap the install in a startup script that sets the properties. What the MSI installs: | Path | Purpose | |---|---| | `C:\Program Files\WASViking\Guardian\wasviking-sentinel.exe` | Agent binary for direct CLI use. | | `C:\Program Files\WASViking\Guardian\wasviking-sentinel-svc.exe` | Same agent, registered as the LocalSystem service. | | `C:\Program Files\WASViking\Guardian\nm-host\com.wasviking.ai_browser.json` | Chrome and Edge native messaging host manifest. | | `C:\Program Files\WASViking\Guardian\nm-host\ai-guardian-native-host.bat` | Native messaging shim. | | `C:\ProgramData\WASViking\Guardian\configs\` | `config.yaml`, `system.cfg` provisioned by the install custom action. | | `C:\ProgramData\WASViking\Guardian\certs\` | mTLS bundle (`ca.crt`, `client.crt`, `client.key`, `agent_uid`). | | `C:\ProgramData\WASViking\Guardian\logs\sentinel_agent.log` | Agent log. | | `HKLM\SOFTWARE\WASViking\Guardian` | Install metadata (`InstallDir`, `DataDir`, `Version`). | | `HKLM\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.wasviking.ai_browser` | Native messaging registration for Chrome. | | `HKLM\SOFTWARE\Microsoft\Edge\NativeMessagingHosts\com.wasviking.ai_browser` | Native messaging registration for Edge. | | Service `WASVikingGuardian` | LocalSystem, auto-start, runs `ai-browser monitor`. | Verify the MSI install: ```powershell sc.exe query WASVikingGuardian Get-ChildItem 'C:\Program Files\WASViking\Guardian\' -Recurse Get-ChildItem 'C:\ProgramData\WASViking\Guardian\' -Recurse ``` Expected: | Check | Expected result | |---|---| | `sc.exe query WASVikingGuardian` | `STATE: 4 RUNNING`, `ACCEPTS_SHUTDOWN`. | | `Get-Service WASVikingGuardian` | Status `Running`, StartType `Automatic`. | | Agent log (`C:\ProgramData\WASViking\Guardian\logs\sentinel_agent.log`) | Contains `Loaded configuration from config.yaml`. No `ERROR` lines. | | MSI log (`%TEMP%\guardian-install.log`) | Last lines show `Installation completed successfully` and `MainEngineThread is returning 0`. | ### Mass deployment | Platform | Vehicle | Distribution tool | |---|---|---| | Linux | `install.sh` | Ansible, Puppet, Chef, Salt, or any tool that runs a shell script with the `Authorization` header secret injected at deploy time. | | macOS | `install.sh` | Jamf Pro, Workspace ONE UEM, Kandji, Mosyle, or any MDM that runs a shell script as the target user. | | Windows | `.msi` | Intune (Win32 LOB), SCCM (Application), Group Policy software installation. Pass `WASV_API_KEY` and `WASV_API` as MSI properties. | All three vehicles are idempotent and safe to re-run. The same install API key works across the entire fleet. Bootstrap is per-machine, so rotating the install API key has no effect on already-enrolled endpoints. > Replace `wv_live_xxx` with your real key; the portal pre-fills the full > command for you on the key-creation screen. The bootstrap embedded in > the rendered script expires a few minutes after the script is fetched, > so run it promptly. ### What the installer does 1. Installs the agent binary to a per-user directory. 2. Registers the agent (mTLS identity) via the one-time bootstrap. 3. Stores the org API key in the OS secret store: Keychain (macOS), Credential Manager / DPAPI (Windows), or Secret Service (Linux). 4. Installs the **native messaging host** manifest and allowlists the published extension IDs. 5. **Force-installs** the browser extension via managed policy (HKLM `ExtensionInstallForcelist` on Windows, managed-policy JSON on Linux, MDM staging on macOS). 6. Starts the per-user service/task that monitors AI exposure. --- ## The browser extension The managed extension is what observes AI usage in the browser; the agent is the **native messaging host** it pairs with over loopback. **The agent must be installed and running**: without it the extension shows *"Not paired with the local WASViking agent."* The installer in Step 6 force-installs the published extension automatically, but users can also install it manually from the official stores: At the next browser launch, the extension installs and pins itself to the toolbar. The popup shows **Paired with the local WASViking agent** when the bridge to the local monitor is healthy. The force-install uses the standard managed-policy mechanisms the browsers already support; the policy keys are documented by the browser vendors and remain the authoritative reference: - [Chrome Enterprise: Manage Chrome extensions](https://support.google.com/chrome/a/answer/9296680) - [Microsoft Edge: ExtensionInstallForcelist policy](https://learn.microsoft.com/deployedge/microsoft-edge-policies) Operators that deploy through MDM (Intune, Jamf, Workspace ONE) can apply the same managed-policy values through their existing channels instead of relying on the agent to write them. > A manually installed extension still needs the agent. If the popup stays > "not paired", confirm the agent service is running and that the native > messaging host manifest was installed for that browser. --- ## Step 7: Validate detection and policy enforcement ### Confirm the device shows up in the portal Within a couple of minutes of installation, open **Cyber Risk > AI Guardian** in the portal. The endpoint will appear under **Monitored devices** as soon as it processes its first event. ### Generate a test event Open Chrome or Edge on the endpoint. Visit `chat.openai.com` (or any AI tool listed in the dashboard) and paste sensitive content into the composer. The classifier ships with a wide catalog, including: - **Brazilian and global identifiers**: CPF, CNPJ, RG, CNH, PIS, passport, PIX keys (including the random UUID form), IBAN, SWIFT or BIC, SSN, credit card. - **Names, postal addresses, and dates of birth** when introduced by an honorific or a labeled field (for example `Patient: Jane Doe`, `Date of birth: 12/03/1985`, `Rua das Flores, 123, São Paulo`). - **Special category data under LGPD Art. 5 II and GDPR Art. 9**: racial origin, religion, political opinion, union membership, sexual orientation, biometric and genetic data. The classifier treats these as indicators on their own and only escalates them when a personal identifier sits in the same prompt. - **Protected health information**: clinical vocabulary in English and Portuguese, ICD-10 codes, medical record numbers (MRN, CNS), Brazilian professional registries (CRM, CRO, COREN), health insurance carriers. - **Credentials and cloud tokens**: AWS access keys, Anthropic keys, OpenAI project keys, Google API keys, Stripe live keys, Slack webhooks, GitHub fine-grained and OAuth tokens, Azure SAS and storage keys, GCP service account JSON, database connection strings, kubeconfig, and more. - **Source code, internal URLs, SQL objects**, and your own organization lexicon when you provide one through policy. The detection layer also defeats two common bypass shapes without any configuration: - **Unicode look-alike substitution**. Fullwidth digits and homograph characters are normalized before the classifier sees the prompt, so `CPF 111.444.777-35` is detected the same way as the ASCII form. - **Encoded envelopes**. Content wrapped in base64, URL-encoding, JSON escape sequences, or hex is decoded in memory and the classifier runs again on each decoded chunk. Findings recovered this way carry a `via base64` (or `via base64>url`) badge in the dashboard so an analyst can recognize adversarial encoding. Outcomes of the paste: - If no policy matches, the agent records a classified event with severity and masked evidence. The event surfaces in the dashboard table within a minute. - If a Block policy matches, the agent stops the paste and the browser shows the **Paste blocked** card with the rule name and the message you configured. The blocked event is recorded with `policy.action = block`. ### What you will see in the portal When the test event reaches **Portal > AI Guardian**, the classifier metadata flows through to the dashboard: - The event row carries a small **decoded** pill on the Type column whenever at least one match came from an encoded envelope (base64, URL-encode, JSON escape, hex). This is your one-glance signal that the prompt contained encoded exfil rather than literal text. - The detail drawer opens with a **Compliance** row that lists every regulatory framework the event implicates, as colored chips. The classifier stamps stable framework strings on every finding: `LGPD.Art.5.I`, `LGPD.Art.5.II`, `GDPR.Art.4.1`, `GDPR.Art.9`, `HIPAA.PHI`, `HIPAA.Privacy`, `PCI.DSS`, `PCI.CHD`, `SOC2.CC6.1`, `SOC2.CC6.6`, `ISO27001.A.5.13`, `ISO27001.A.8.10`, `NIST.800-53.SC-28`, `NIST.800-53.SC-12`, `NIST.AI.RMF.MAP-4.1`, `NIST.AI.RMF.GOVERN-3`, `CPRA`, `PIPEDA`. - Each classification row in the drawer carries its own chip set, so you can see whether a given finding maps to PCI, HIPAA, GDPR, LGPD, or several of them at the same time. - The masked evidence next to each classification shows enough of the match to confirm a true positive (`•••.•••.•••-35` for a CPF, `AKIA••••••MPLE` for an AWS key, `J••• da S••••` for a name, `••/••/1985` for a date of birth) without ever storing the raw value. This is the same data the policy engine sees, so a chip on the drawer means the policy could have matched on `compliance:LGPD.Art.9` if you wanted to. Filtering and per-tenant compliance widgets are on the roadmap. ### Create a first Block policy To prove the block path works on a real endpoint, go to **Cyber Risk > AI Guardian Policies > New policy** and configure: - **Name:** `Block PII into unsanctioned AI`. - **Event:** Paste. - **Conditions:** Data class is `pii` AND website category is `unsanctioned`. - **Targets:** All users. - **Action:** Block. - **Message:** `Pasting sensitive PII data into an unsanctioned AI tool is not allowed by your organization policy.` Save the rule. The agent fetches the new policy on its next refresh cycle. Repeat the paste from the previous step and confirm the block. --- ## Where the API key lives after install This section applies to the `install.sh` (Linux, macOS) and `install.ps1` (Windows) vehicles. The Enterprise MSI on Windows consumes the bootstrap during install and does not persist the organization API key on the endpoint; identity is the mTLS client certificate from that point on. The install command stores the organization API key in the OS-native encrypted credential store and never writes the key to a file. On macOS the entry lives in the Keychain (service `wasviking-ai-guardian`, account = OS user). On Windows it lives in Credential Manager. On Linux it lives in the Secret Service via D-Bus (GNOME Keyring, KWallet, or the running provider). The value placed in the keystore is an AES-256-GCM envelope wrapped with a key derived from the host's machine identifiers. A snapshot of the entry copied to a different host decrypts to garbage. The env file at `~/.config/wasviking/ai-guardian.env` contains only the API base URL (`WASV_API=`). It does not carry the key. To check where the key is currently resolved from without printing the value: ```bash wasviking-sentinel ai-browser key-status ``` Typical output on a healthy install: ``` OS keystore backend: macOS Keychain (available=true) keystore: present (last 4 chars: ••••0123) legacy file: absent kit env file: WASV_API_KEY absent Effective source: OS keystore (secure). ``` If `Effective source` reports anything other than the OS keystore, restart the AI Guardian service so the agent re-reads the credential state on the next start. The agent reconciles to the OS keystore without operator action. --- ## Uninstalling the agent Removal is **gated by the master password** set in Step 2. On the device, run: ```bash wasviking-sentinel ai-browser uninstall \ --master-password "your-master-password" \ --remove-key ``` The command authenticates the master password online against your tenant, then lifts the force-installed browser policy, removes the native messaging host manifest, stops and removes the per-user service (LaunchAgent / `systemd --user` unit / Scheduled Task), and, with `--remove-key`, deletes the persisted org API key and API base from the OS secret store. If no master password is configured for the organization, the `--master-password` flag can be omitted. | Flag | Purpose | |---|---| | `--master-password` | Org master password. Required unless none is configured. A wrong password refuses the removal (exit `77`). | | `--remove-key` | Also delete the persisted org API key and API base. Omit to keep them (for example, to re-enforce later). | | `--browser` | `chrome`, `edge`, `chromium`, `firefox`, `both` (chrome+edge, default), or `all`. | | `--keep-events` | Archive `events.jsonl` under `~/.wasviking/events-archive/` before removing the install dir. | | `--keep-runtime` | Leave the monitor service running (only lifts policy + host manifest). | | `--dry-run` | Show what would be removed without authorizing or deleting. | > Preview first with `--dry-run`. Without `--master-password` on a > protected host the command exits `77` ("master password rejected") and > nothing is removed. For a machine installed with the Enterprise MSI, uninstall through your management tool instead: ```powershell msiexec /x {} /qn ``` --- ## Day-2 operations ### Disabling monitoring temporarily Toggle the **Status** switch on the AI Guardian card off. The authoritative ingest gate on the WASViking side stops persisting new events for your organization within five minutes. The endpoints themselves keep running and the local audit trail keeps recording, so you can re-enable without redeploying. ### Rotating the install API key Revoke the existing key from **Settings > System Settings > API Keys** and create a new one with the **ai_guardian:install** scope. Already-registered endpoints are unaffected because they no longer depend on the key. ### Updating the agent The agent updates itself in place through a safe, verified flow. The operator can trigger it at any time and the host always ends in one of two states: the new binary running and healthy, or the previous binary running and healthy. There is no broken intermediate. Governance lives in the **Agent updates** card from Step 3 (release channel and pinned version). On the endpoint, run from any account that can read the host's persisted api configuration: ```bash # What would happen, without changing anything (exit 10 if an update is available, 0 if not) wasviking-sentinel ai-browser update --check # Apply the update non-interactively wasviking-sentinel ai-browser update --yes # Restore the previous binary if you need to revert manually wasviking-sentinel ai-browser update --rollback ``` No flags are needed in the common case. The command auto-detects the binary the service manager supervises, reads the api base from the install configuration, and reads the org api key from the host keystore set at install time. **What the safety gate does.** On `--yes`, the agent fetches the release manifest, downloads the binary, recomputes its `sha256`, verifies the operating system code signature, takes a single-flight lock, copies the current binary to a `.bak`, atomically renames the new binary in place, restarts the service, and waits for the local `/healthz` endpoint to report the expected new version. If any of these steps fails the agent restores the `.bak`, restarts the service, and exits non-zero with a clear message. Disk + service state never end up half-applied. The exit codes are stable so they can be wired into a fleet manager: | Code | Meaning | |---|---| | 0 | Already up to date, applied successfully, or the operator declined the prompt | | 10 | `--check` saw an update available | | 30 | Apply failed and was rolled back to the previous binary | | 40 | Self-update is not supported on this OS (Linux uses `apt`; production Windows uses MSI patch) | | 75 | No api key or the key was rejected | ### Auditing changes Every action on this flow is logged in the customer-facing audit log (`Settings > History`): - `ai_guardian.toggle` - `ai_guardian.master_password_set` / `.clear` - `ai_guardian.update_policy_set` - `ai_guardian.approved_vendors_set` (single action covers approved + blocked changes) - `apikey.create` / `apikey.revoke` - `ai_guardian.policy.create` / `.update` / `.delete` --- ## Advanced configuration The defaults are tuned to give a healthy signal on the first day without configuration. Three knobs let you tighten or extend the classifier per tenant. All three live in the agent policy file on each endpoint (`/etc/wasviking/ai-guardian-policy.json` on Linux, under `~/Library/Application Support/WASViking/` on macOS, `%ProgramData%\WASViking\` on Windows). Portal UI for managing these centrally is on the roadmap. Until then, distribute the file through your endpoint management tool. ### Per-tenant custom detectors Add proprietary patterns the built-in detectors do not cover, for example an internal project code, an employee identifier, or a customer reference. Each entry compiles into the same pipeline as the built-ins and the agent refuses to start if a pattern is malformed, so a typo never silently disarms detection. ```json { "custom_detectors": [ { "label": "internal_employee_id", "category": "pii", "regex": "\\bEMP\\d{6}\\b", "confidence": "high", "mask": "digits", "mask_keep": 2, "compliance_tags": ["LGPD.Art.5.I", "GDPR.Art.4.1"], "description": "Internal employee ID format" }, { "label": "internal_project_code", "category": "internal", "regex": "\\bPROJ-\\d{4}\\b", "confidence": "high", "compliance_tags": ["ISO27001.A.5.13"] } ] } ``` Valid categories are `pii`, `secret`, `phi`, `sensitive`, `internal`, and `source_code`. Mask options are `digits`, `token`, `middle`, or `none`. You can add up to 64 custom detectors per tenant. ### Confidence calibration per label Damp a noisy detector for your tenant without disabling it. The multiplier is in the `[0, 1]` range and only lowers confidence, never raises it. ```json { "label_thresholds": { "phone": 0.7, "br_bank_account": 0.4 } } ``` A value of `0.4` collapses a `high` finding to `low` for that label, so policy rules conditioned on `min_severity` no longer escalate it. This is the manual lever today. An automated feedback loop driven from a "Mark false positive" button in the portal is on the roadmap. ### LLM-assisted classification (opt in) For cases where the deterministic engine returns low confidence or a high score and you want a second opinion, the agent can call an LLM provider directly to add semantic classifications. You bring your own API key. The prompt content goes from the endpoint to the LLM provider over public TLS and never transits WASViking infrastructure. ```json { "llm_assist": { "enabled": true, "provider": "anthropic", "model": "claude-haiku-4-5", "api_key": "sk-ant-api03-...", "min_trigger_score": 40, "min_trigger_confidence": "low", "max_content_chars": 8000, "cache_ttl_hours": 720, "budget_per_day_usd": 5.0, "timeout_ms": 6000 } } ``` The agent caches results by SHA-256 of the prompt on disk, so the same content is never classified twice. The daily budget guard stops LLM calls when the configured cap is reached. Findings produced by the LLM layer appear in the drawer with the `llm_` prefix so they are distinguishable from regex hits. This layer is most useful for paraphrased PII, contract text, M&A context, and proprietary IP that pattern matching cannot describe. --- ## Identity and directory Agents report an operating-system user name on each device. To target policies by group, show real names in the dashboards, and erase a person across every device they use, map those OS user names to corporate identities under **Portal > AI Guardian > Identity**. Only mappings you confirm affect enforcement; suggestions never act on their own. ### Connect an identity provider (SCIM) 1. Open **Portal > AI Guardian > Identity** and, in the **Identity provider (SCIM 2.0)** card, click **Generate token**. Copy the token immediately: it is shown only once. Note the SCIM endpoint shown next to it (for example `https://api.wasviking.com/api/scim/v2`). 2. Configure your identity provider's provisioning to point at that endpoint using an **OAuth bearer token** with the token you copied. This covers Microsoft Entra ID and Okta, which both push to this SCIM endpoint. Google Workspace works differently and has its own section below. 3. Assign the users and groups you want WASViking to know about. They appear on the Identity page after the first sync. Rotating the token in the portal invalidates the previous one immediately; revoking it stops directory sync. **Microsoft Entra ID or Okta.** Create an enterprise application with automatic user provisioning, set the **Tenant URL** (or equivalent) to the SCIM endpoint, set the **Secret Token** to the bearer token, test the connection, then enable provisioning and assign the users and groups to sync. If your organization already uses Microsoft 365 / Office 365, this is the same Entra ID directory those accounts already live in; there is nothing separate to set up for Office 365. ### Sync Google Workspace (directory pull) Google Workspace does not push SCIM to an app you create yourself, so the SCIM card above does not apply to it. Instead, WASViking reads your directory directly, on a schedule, using Google's read-only Admin SDK Directory API. You grant a read-only service account access once and paste it into the portal, and WASViking keeps your users and their groups in sync. This is the right path for any Google Workspace customer, and it also brings in your groups, which a plain Google user export leaves out. **In Google (one-time setup, by a Workspace or Cloud admin):** 1. In the [Google Cloud console](https://console.cloud.google.com), create a project or pick an existing one. 2. Go to **APIs & Services > Library**, search for **Admin SDK API**, and click **Enable**. 3. Go to **IAM & Admin > Service Accounts** and create a service account (for example, "workspace-directory-reader"). It needs no project roles. 4. Open the service account, go to **Details > Advanced settings**, and turn on **Enable Google Workspace Domain-wide Delegation**. Copy the **Client ID** it shows. 5. In the [Google Admin console](https://admin.google.com), go to **Security > Access and data control > API controls > Domain-wide delegation** and click **Add new**. Paste that Client ID, and in the **OAuth scopes** field add these three read-only scopes, comma separated: - `https://www.googleapis.com/auth/admin.directory.user.readonly` - `https://www.googleapis.com/auth/admin.directory.group.readonly` - `https://www.googleapis.com/auth/admin.directory.group.member.readonly` Then click **Authorize**. 6. Back on the service account in Google Cloud, go to **Keys > Add key > Create new key**, choose **JSON**, and click **Create**. Your browser downloads a JSON key file. Keep it private. **In the portal, under Portal > AI Guardian > Identity:** 1. Find the **Google Workspace directory sync** card. 2. Upload the **service-account JSON key** you just downloaded. 3. Enter the **admin email to impersonate**: any Google Workspace admin address, such as `admin@yourdomain.com`. WASViking reads the directory as this admin. 4. Leave **Reconcile leavers and group changes** on (recommended). With it on, people removed in Google are deactivated here and group membership stays in step with Google. Turn it off if you only want to add and update people, never remove them. 5. Leave the interval at its default and click **Connect Google Workspace**. The sync then runs on a schedule. 6. Click **Sync now** to run the first pull right away. Your users and groups appear in the lists below within a few seconds. WASViking only manages the users and groups it pulls from Google. Anything you added by CSV or by hand, and every mapping you have already confirmed, stays exactly as it is. Leave the **Customer id** field as `my_customer` unless Google gave you a specific customer id that starts with `C`. ### Import identities from a CSV No identity provider yet? Use the **CSV import** card instead. Provide a file with columns `user_name`, `display_name`, `department`, and `groups` (group names separated by ";"). Re-importing updates existing rows. You can also add a single identity by hand from the same card. If you upload a raw Google Workspace user export, the portal tells you the columns do not match and points you to Google Workspace directory sync, which also pulls in the groups a user export leaves out. ### Confirm which login belongs to whom 1. Click **Scan devices for usernames**. WASViking reads the distinct OS user names seen in your event stream over the last 31 days and proposes matches against your identities. 2. Review **Pending suggestions**: each row shows the OS user name, the suggested identity, and the heuristic that produced it (exact login, email local part, or display name). Click **Confirm** to activate a mapping or **Reject** to dismiss it. 3. Resolve **Unmapped usernames** (logins with no automatic match) by choosing an identity and clicking **Map**, which confirms the mapping in the same step. Confirmations and rejections are recorded in the audit log (**Settings > History**). Only confirmed aliases feed group policies, dashboard name resolution, and erasure by person, so a wrong guess is never enforced. ### Target a policy at a directory group 1. Open **Portal > AI Guardian Policies** and create or edit a rule. 2. Under **Targets**, choose **Directory groups** and select one or more groups. The builder shows how many confirmed users each group covers today, so you can see the real reach before saving. 3. Optionally list OS user names to exclude. Save the rule. When the policy is served to agents, the group is expanded to the confirmed user names behind it. Enrolled endpoints pick up the change on their next policy refresh (about 15 seconds); no agent restart is needed. If you add people to the group in your identity provider later, confirm their aliases on the Identity page and they are covered automatically on the next refresh. ### Erase a person across every device A right-to-be-forgotten request can name a corporate identity instead of a single login. It expands to every confirmed alias of that person and removes their events across all of their devices in one operation. This is the reliable way to satisfy LGPD Art. 16 and GDPR Art. 17 when an employee has used more than one machine. --- ## Troubleshooting | Symptom | Likely cause | |---|---| | Install command returns `401` | API key revoked, expired, or missing the `ai_guardian:install` scope. Create a new key. | | Install command fails with an auth/bootstrap error | The short-lived bootstrap expired (run the command soon after fetching), or the API Key was revoked / lacks `ai_guardian:install`. | | Endpoint never appears in **Monitored devices** | AI Guardian Status toggle is off for the organization, or the endpoint cannot reach the tenant gRPC endpoint on port 443. | | Extension popup says "Not paired with the local WASViking agent" | The agent isn't installed or isn't running on that device, or the native messaging host manifest is missing for that browser. | | No events appear in the portal, popup still green | Cloud forwarding is off (master toggle), or the native host cached a stale receiver token after a reinstall; restart the agent so it re-reads the token. | | Paste blocks do not trigger | The browser extension was not reloaded after install, the policy has not propagated yet (wait one refresh cycle), or no rule matches the event and conditions. | | Blocked vendor still records events | Vendor governance classifies and flags; it does not enforce. Add an AI Guardian Policy rule (use event **Any** for full coverage). | | Adding the scope to an existing key shows no install command | Expected: the bootstrap is minted at key creation. Create a new key to get a fresh command. | | Browser extension is not pinned | The managed-policy file was not applied. On macOS and Windows, deploy through MDM or run the installer with administrator privileges. | | Firefox "Agent not reachable" while Chrome works | The Firefox native messaging host manifest wasn't installed, or its directory is not writable by your user. Reinstall with all browsers selected. | | User can't uninstall the agent | By design: uninstall and policy removal require the master password set in Step 2. | | Uninstall refuses with exit code `77` | The master password provided does not match the one set in the portal. | | Agent refuses to start after a policy edit | A custom detector regex failed to compile, a label collides with a built-in name, or a category is invalid. The agent logs the offending entry. Fix the policy file and restart the service. | | No `llm_` classifications appear after enabling `llm_assist` | The configured `api_key` is empty or invalid (the agent silently treats the layer as off), the prompt scored below `min_trigger_score`, or the daily budget cap was reached. | | Drawer shows the `decoded` pill but no value looks encoded | At least one finding came from a multi-pass decode (base64, URL-encode, JSON escape, or hex). Look for the `via ` badge on the individual classification rows in the drawer. | | MSI exits with code `1603` | Generic install failure. Open `%TEMP%\guardian-install.log` (or the path passed to `/l*v`), search for `Error 1722` and the surrounding `CustomAction RegisterAgentWithKey` block. Common causes: `WASV_API_KEY` missing the `ai_guardian:install` scope, `WASV_API` unreachable from the target subnet, or the API key revoked. | | MSI exits with code `1920` after `sc query WASVikingGuardian` shows `STATE: STOPPED` | The Service registered but did not complete the SCM handshake within 30 seconds. Inspect `C:\ProgramData\WASViking\Guardian\logs\sentinel_agent.log` for the agent startup error. A missing or unreadable `configs\config.yaml` is the typical cause and points back to a `RegisterAgentWithKey` failure earlier in the install. | | `update` exits `30` (rolled back) | The post-restart health check did not see the expected new version within the timeout. The previous binary was restored automatically. Most common causes: the new release is not signed for this OS, the service manager points at a different binary than the one updated (use `--binary ` to target it explicitly), or the monitor failed to bind its loopback port. | | `update` exits `40` | Self-update is not supported on this OS. Use `apt update && apt upgrade wasviking-sentinel` on Linux. On production Windows, deploy the new MSI through your management tool. | | `update --check` always reports "no update" | The org pinned version does not exist in the manifest yet, or the channel resolved to a version equal to or older than the agent's current build. Re-check the **Agent updates** card in **Settings → System Settings → AI Guardian**. | | A group policy does not cover someone in that group | The person's OS user name is not confirmed on the **Identity** page. Run **Scan devices for usernames**, then confirm or map their alias. Only confirmed aliases are expanded behind a group target. | | SCIM directory sync returns `401` | Your Entra ID or Okta bearer token is wrong or was rotated. Generate a new token on the Identity page and update it in your identity provider's provisioning settings. | | Google Workspace sync fails with `400` or a customer-id error | Leave the **Customer id** field on the connector set to `my_customer`. A numeric value (such as the delegation Client ID pasted there by mistake) is not valid; WASViking falls back to `my_customer`, so re-saving usually clears it. | | Google Workspace sync fails with a credential or delegation error | Domain-wide delegation is not authorized for the three read-only scopes, or the admin email cannot be impersonated. Re-check the Client ID and scopes in **Admin console > Security > API controls > Domain-wide delegation**, and confirm the admin email is a real Workspace admin. | | A Google group syncs but shows no members | Only members who also exist as users in your Google directory are attached. External members and nested groups are skipped. | For anything not covered here, contact `support@wasviking.com`. ## Where this fits in the platform - AI exposure lives under **AI Guardian**: Executive Summary, AI Guardian dashboard, and AI Applications. - Enforcement rules live under **AI Guardian → AI Guardian Policies**. - Alert routing is documented under [Notification Channels](https://docs.wasviking.com/integrations/notification-channels/), [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/), and [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # Set up Infrastructure Defense Section: Getting Started Source: https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/ Summary: Enroll your first servers step by step. Create an activation key, install the Sentinel Host agent on Windows, Linux or macOS (including MSI mass deployment), meet your fleet on the Assets screen, and set the policies that govern assessment and patching. This guide takes you from an empty module to a scored fleet: by the end you will have agents reporting from your servers, every host carrying a Viking Exposure Score with its factors visible, and the rules of engagement (what gets assessed, who approves changes, when changes are allowed) written down as policies. Nothing here requires opening an inbound port or touching a firewall rule: the agent always connects out, over mutual TLS. ## Before you start Infrastructure Defense is an add-on module enabled per organization by your WASViking contact or partner. Once it is on, the **Infrastructure Defense** section appears in the portal sidebar. Enrolling and managing agents needs the Manage permission on the module; approving patch jobs needs the dedicated Remediate permission, so you can keep those two responsibilities in different hands from day one. ## Step 1: choose how you will enroll Open **Infrastructure Defense → Sentinel Hosts**. The screen offers the two onboarding paths side by side: - **Use an activation key** is the recommended path: one reusable credential enrolls any number of servers, each machine registers itself under its own hostname and certificate identity, and a reinstall on the same machine resumes the same asset. This guide follows it. - **Enroll a single server** mints a one-time token for one machine, for a one-off host. The token expires unused after the window set in Fleet housekeeping (seven days by default). Either way, a host counts against your monitored hosts allowance only after its first inventory: enrollments that never complete are attempts, not assets, and the platform retires them on its own once the machine reports. If the server sits behind an antivirus, endpoint detection or a TLS inspection device, read [Endpoint protection exclusions for Sentinel Host](https://docs.wasviking.com/getting-started/endpoint-protection-exclusions/) first. The installer checks its path to the cloud before registering and stops on a blocked path, naming the product in the way. ## Step 2: create an activation key Open the **Activation keys** tab, give the key a title your team will recognize ("Production servers", "Data center onboarding"), optionally cap how many agents it may enroll and when it expires, pick the policy its hosts start under, and generate it. You can disable, edit or delete a key at any time without touching the agents already enrolled, and the counter next to each key shows how much of your plan's host allowance it has consumed. ![Activation keys tab with a reusable key, its agent cap, policy and status](https://docs.wasviking.com/static/docs/images/infrastructure-defense/02-activation-keys.png) ## Step 3: install the agent Click **Install agent** next to the key. The install page carries the key inside every command, so installation is one download and one line on the server: the agent trades the credential for its own certificate identity and starts the service, always connecting outbound over mutual TLS. Copy the command straight from that page, or use the reference below. ### One line, two credentials The install command is the same everywhere; the only thing that changes is which credential you hand it. - `--activation-key` takes the reusable key from the **Activation keys** tab. Any number of machines can use it, within the cap you set; each one enrolls under its own hostname and identity. This is the right choice for rollouts and automation, and the path this guide follows. - `--token` takes the one-time token from **Enroll a single server** on the **Agents** tab. It is shown once, works for exactly one machine, and you name the agent up front. The right choice for a first try on one or two hosts. Swap one flag for the other and everything else in the commands below stays exactly the same. ### Windows Unpack the zip from an elevated PowerShell. An elevated PowerShell opens in `C:\Windows\System32`, so the line below switches to the Downloads folder first; if you saved the package somewhere else, cd there instead (swap `amd64` for `arm64` on ARM64 hosts): ``` cd $env:USERPROFILE\Downloads; Expand-Archive .\wasviking-sentinel-host__windows-amd64.zip -DestinationPath .\wasviking-sentinel-host -Force; cd .\wasviking-sentinel-host ``` Then, from the unpacked folder, run the install command: ``` .\wasviking-sentinel-host.exe install --activation-key ``` Or, with a one-time token from the Agents tab: ``` .\wasviking-sentinel-host.exe install --token ``` The executable copies itself to Program Files, registers the host and starts the Windows service. No scripts, so the execution policy never gets in the way. For fleets there is a second, fully unattended path on the same page: the **MSI package** (x64). Push it through group policy or the device management tool you already run, with the activation key passed as an installer property, and every server enrolls itself with no interaction: ``` msiexec /i wasviking-sentinel-host__amd64.msi /qn WVH_ACTIVATION_KEY= ``` While testing the rollout, add `/l*v C:\wvh-install.log` to the line: `msiexec` fails silently under `/qn`, and the log answers why in seconds. ### Linux As root, on x86_64 or ARM64: ``` tar -xzf wasviking-sentinel-host__linux-amd64.tar.gz sudo ./wasviking-sentinel-host install --activation-key ``` The agent registers and starts under systemd. ### macOS Same one-command flow on Intel or Apple Silicon, started under launchd: ``` tar -xzf wasviking-sentinel-host__darwin-arm64.tar.gz sudo ./wasviking-sentinel-host install --activation-key ``` ### In production, that is the whole command The commands above are complete for a production install: the agent ships already knowing how to reach the WASViking cloud, and everything else it needs, certificates included, arrives during registration. You may run into examples elsewhere that carry `--api`, `--grpc` and `--tls-server-name` flags, or the `WVH_API`, `WVH_GRPC` and `WVH_TLS_SERVER_NAME` MSI properties. Those overrides exist only for environments pointed at a different WASViking endpoint, such as an evaluation environment arranged with your WASViking contact. Unless you were explicitly given custom endpoints, leave them out. And when in doubt, trust the install page: it always shows the exact command for your organization, ready to copy as is. One more habit worth keeping: to upgrade a machine that is already enrolled, run `install` with no credential at all; the agent reuses its enrollment and identity. In practice you will rarely need even that: when a new agent version ships, the console offers a one-click update per agent, or for the whole selection at once. ![Install page with the Windows package selected, installation steps and the MSI fleet deployment block](https://docs.wasviking.com/static/docs/images/infrastructure-defense/03-install-windows-msi.png) ## Step 4: meet your fleet Back on **Sentinel Hosts → Agents**, every enrolled machine appears right away with its operating system, IP, agent version, policy and status. Windows Server and Windows 10/11, Ubuntu, Debian, the RHEL family and macOS all sit in the same table with their own icons, so a mixed fleet stays readable at a glance. From the row menu you can run an inventory on demand, request the agent's logs, restart it, or uninstall it remotely; tags let you slice the fleet the way your team thinks about it ("web servers", "payment zone", "staging"). ![Agents tab listing macOS, Ubuntu, Windows and Debian hosts with status, version and policy](https://docs.wasviking.com/static/docs/images/infrastructure-defense/01-agents.png) Within a few minutes of its first inventory each host also appears on the **Assets** screen, and its first assessment follows: packages correlated against published vulnerabilities, missing security updates, configuration checks, and the score. ## Step 5: read the score, then tell the platform what the host is Open any host on the Assets screen. The Asset Summary shows the Viking Exposure Score with every factor and its points listed next to it, so the number is never a mystery: worst open vulnerability, exploitation evidence, exposure, pending updates, end of life state. Two inputs here are yours to set, and both move the score: under **Edit business context**, set the **environment** (production raises the stakes, staging lowers them) and the **criticality** of the asset. Internet exposure you can leave on automatic: the platform's own attack surface discovery proves it, names the evidence on the screen, and a manual choice always wins if you disagree. ![Asset detail with the Viking Exposure Score and its named factors, host identity and business context](https://docs.wasviking.com/static/docs/images/infrastructure-defense/04-asset-detail.png) ## Step 6: write the rules down as policies Open **Infrastructure Defense → Policies**. A policy states exactly how far the platform may go for the hosts under it: which assessments run, whether the platform may recommend and create patch jobs, whether a person must approve them (on by default), whether anything may ever deploy or reboot automatically (both off by default), and the maintenance window where changes are allowed. Assign a policy per activation key, per agent, or in bulk from the Agents tab. The default policy ships safe: assess everything, change nothing without approval. The same policy carries the **performance and resource protection** profile of its hosts: pick **Conservative** for critical production (a tenth of the machine, assessment twice a day, work waits as soon as the host gets busy), **Balanced** for the fleet at large, **Performance** for development, or **Custom** to set every number yourself, including a reduced activity window in host local time (08:00 to 18:00 keeps business hours quiet). The agent enforces the CPU share as a hard cap on Linux and Windows and as scheduling priority on macOS, and postpones scheduled assessments and approved patch jobs while the host is above the thresholds you set. Each asset page shows what the agent reports. ![Policies screen with the default policy: assessment scope, remediation authority and safety guardrails](https://docs.wasviking.com/static/docs/images/infrastructure-defense/05-policies.png) ## Step 7: route the outcomes to your team In **Alert Destinations**, enable the **Infrastructure patch job** event on the channels your team watches (Slack, Teams, email or webhook). A verified job announces itself with the measured numbers (score before and after, vulnerabilities closed); a failed job arrives with the reason. Decisions a person made in the portal, like rejecting a job, are deliberately never broadcast. ## Step 8: run your first remediation When the first assessment lands, open **Resolve**. Recommended actions group the pending security updates per host, ranked by the score reduction they are expected to deliver. Schedule one, approve it (run now, or let it wait for the maintenance window), and watch the cycle finish honestly: the agent runs pre-checks, applies the updates through the platform's native mechanism, and the job only completes when the next inventory proves which vulnerabilities closed and how far the score dropped. If a reboot is still pending, the job says so instead of declaring victory early. ## What to look at next - **Configuration** and **Compliance** show the CIS-aligned check results and the compliance percentage per host and for the fleet. - **Software** answers inventory questions across the fleet, down to which hosts run a specific package and version. - **Download report** on the Overview exports the branded PDF: fleet posture, verified risk reduction and top risks, ready for a stakeholder who will never open the portal. - **Coverage**, the third tab of Assets, crosses your Attack Surface with the fleet: which internet-facing assets you own are served by a host you manage, and the next step for the ones that are not. See [Bring your internet-facing assets under management](https://docs.wasviking.com/getting-started/attack-surface-coverage/). - **Roll back this change** on a completed job undoes an update that misbehaved, with the same approval and a measured verdict. See [Roll back a change that misbehaved](https://docs.wasviking.com/getting-started/roll-back-a-change/). For the full picture of what the module assesses and how the score is built, see [Infrastructure Defense](https://docs.wasviking.com/capabilities/infrastructure-defense/) in Capabilities. --- # Set up Sentinel Probes Section: Getting Started Source: https://docs.wasviking.com/getting-started/sentinel-probes/ Summary: Bring the network view to Infrastructure Defense. A Sentinel Probe is a virtual scanner appliance that discovers every device on the networks you authorize and checks it for exposure, with no agent on the target. This guide covers what it is, why it satisfies PCI DSS internal scanning, and how to install, scope and read it. The Sentinel Host agent gives you a deep, authenticated view from inside every server you can install it on. Plenty of things on your network will never run an agent: a managed switch, a printer, an IP camera, a database appliance, a payment terminal, a contractor's laptop. The Sentinel Probe is how you see those, and how you produce the internal vulnerability scan an auditor asks for. It is the network side of WASViking® Infrastructure Defense, and it works alongside the agent rather than replacing it. ![How a Sentinel Probe sees your network: one virtual appliance on a segment discovers every device and reports outward over mutual TLS](https://docs.wasviking.com/static/docs/images/sentinel-probes/01-how-it-works.png) *One probe on a segment, discovering everything on it, reporting outward over mutual TLS. Nothing is installed on the devices it scans.* ## What a Sentinel Probe is A Sentinel Probe is a virtual scanner appliance. You install one lightweight program on a single machine inside a network segment, and from there it discovers every reachable device on the networks you have authorized, identifies the services and versions they expose, and reports what it finds to the WASViking cloud for correlation and scoring. Nothing is installed on the devices it scans. One probe covers a whole segment. It never opens an inbound port. Like the Host agent, the probe connects outward over mutual TLS, so placing one inside a sensitive network does not widen your attack surface. The agent and the probe answer different questions. The agent is the view from inside one machine, complete down to the installed package. The probe is the view of the segment, including everything the agent can never reach. A device the probe finds that carries no agent shows up in your inventory as unmanaged, so you always know the difference between what you manage and what merely exists on the wire. ## Why it matters for PCI DSS PCI DSS asks you to run internal vulnerability scans of the systems in scope, at least once every three months and again after any significant change, and to keep the dated results as evidence. Where a system accepts credentials, the standard expects an authenticated scan that looks past the network surface. The Sentinel Probe is built to produce exactly that evidence. - It scans the internal networks you declare, on the cadence you set, and records a dated run for every scan (requirement 11.3.1). - It supports authenticated scanning over SSH on Linux and WinRM on Windows, so a credentialed check finds what an unauthenticated one cannot (requirement 11.3.1.2). - A one-click **Scan now** runs an off-cycle scan after a change and files the run as on demand, with who asked for it and why (requirement 11.3.1.3). - Each finding closes itself when a later scan no longer sees it, which is the rescan that confirms a fix. What you get is an internal scan program an assessor can actually read: the current cadence, the retained history and an export they can take away, with no spreadsheet to maintain in parallel. ## Before you start Infrastructure Defense is enabled per organization by your WASViking contact or partner. Once it is on, open **Infrastructure Defense → Sentinel Probes** in the portal sidebar. Configuring a probe needs the Manage permission on the module. One rule shapes everything else: a probe sees a network at the depth of where it sits. On its own segment it reads the full layer-2 picture, including a host that answers no other traffic. A network it reaches only through a router is seen at layer 3, where a firewalled host can stay hidden. For complete coverage of every in-scope system, place one probe inside each segment you need to certify. The probe screen tells you, per network, whether it is on the local segment or reaching it over routing, so you always know where a blind spot could be. ## The two tabs: Probes and Activation keys The Sentinel Probes screen has two tabs, and they answer two different questions. **Activation keys** is where onboarding lives. An activation key is a reusable credential that lets a probe enroll itself: you create a key, and the install command carries it. It is the same idea as the host activation keys, and one key can bring up more than one probe. You create, cap, expire and disable keys here. **Probes** is where the running appliances live. Every probe that has enrolled appears here with its health and version, and opening one shows its detail: the networks it is authorized to scan, its scan settings, any credentials for authenticated scanning, and its scan history with the 90-day cadence indicator. In short, you use Activation keys to bring a probe online, and Probes to operate it from then on. ## Step 1: create an activation key Open the **Activation keys** tab and create a key. Give it a title your team will recognize, and optionally cap how many probes it may enroll and when it expires. Generate it, and keep the value handy for the install command in the next step. ## Step 2: install the probe Pick a Linux machine inside the segment you want to scan. A small virtual machine is enough, because the probe is light. It runs on Linux and scans Windows, Linux and network devices alike, so one Linux appliance covers a mixed segment. From the key's Install page, copy the single command: ``` sudo wasviking-sentinel-probe install --activation-key ``` That one line copies the program into place, registers the probe under its own certificate identity, and starts it as a service. Within moments the probe shows **Online** on the Probes tab. There is nothing to schedule for upgrades: the probe updates itself when a new version ships. ## Step 3: authorize the networks it may scan A probe scans nothing until you tell it what it may touch. Open the probe from the Probes tab, and under **Authorized networks** add each network in CIDR form (for example `10.20.0.0/24`), with a label and your affirmation that you are authorized to scan it. Only networks that are both enabled and authorized are ever contacted, public ranges are refused by default, and you can exclude individual addresses inside a range. The probe reads its scope from the portal, so it can never widen its own reach. The **Segment** column next to each network tells you whether the probe is on that segment or reaching it over routing, which is your cue for where a second probe would close a gap. ## Step 4: tune the scan Under **Scan settings** you choose how the probe works: the intensity, the port profile (a common set, a PCI-relevant set, or every port), and how often it scans. Two options are off by default and safe to leave that way at first. ICMP and ARP discovery finds hosts that answer no TCP port and needs a small privilege on the probe host. Scanning public ranges stays disabled unless you deliberately need it. A change you make here reaches the probe within about a minute, with no restart. ## Step 5, optional: authenticated scanning An unauthenticated scan sees a host from the outside. An authenticated scan logs in and reads what is actually installed, which is how PCI expects credentialed systems to be checked. Under **Credentials for authenticated scanning**, add an SSH login for Linux hosts or a WinRM login for Windows hosts, and scope it to the addresses it applies to. The secret is stored encrypted and is only ever handed to the probe over mutual TLS at scan time. On the probe it stays in memory, drives read-only inventory commands, and is never written to disk. This is the deeper view, and it is where the probe finds the vulnerabilities the network surface hides. ## Step 6: read the results Everything the probe discovers lands in the places you already use. - On **Assets**, each discovered device appears with a Probe source. Filter by source to separate what the probe found from what an agent manages, and to spot the unmanaged devices that deserve an agent or a closer look. - Open a device to see its exposures and, where a service could be matched, its vulnerabilities, alongside the same Viking Exposure Score the rest of the fleet carries. - On the probe's own page, **Scan history and cadence** is your audit trail: the dated runs, whether you are inside the 90-day window, and the findings a later scan confirmed remediated. **Export CSV** hands the whole record to an assessor. When you change something on a scoped network, open the probe and choose **Scan now**. The probe runs an off-cycle scan on its next check, and the run is filed as on demand with the reason you gave, which is the evidence PCI asks for after a significant change. ## What to look at next - [Set up Infrastructure Defense](https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/) is the host agent guide; it pairs the deep per-machine view with the network view you just enabled. - [Infrastructure Defense](https://docs.wasviking.com/capabilities/infrastructure-defense/) in Capabilities explains how the score is built and what the module assesses. - **Compliance** and **Configuration** turn the same facts into the CIS-aligned and PCI-mapped results your auditor reads. A network you cannot see is a network you cannot defend. With a probe on each segment, Infrastructure Defense stops being a story about the servers you happened to install an agent on, and becomes the honest picture of everything on your network, scored and ready for the audit. --- # Set up Header Advisor Section: Getting Started Source: https://docs.wasviking.com/getting-started/header-advisor/ Summary: Add one report-only header at your edge or origin, let the browsers of your users teach WASViking what the application loads, then approve and roll out the Content Security Policy in two moves. [Header Advisor](https://docs.wasviking.com/capabilities/header-advisor/) builds the Content Security Policy an application needs from the reports of its own users' browsers. This guide takes one hostname from the first header to an enforced policy that stays current. WASViking® never touches your edge or origin: every step that changes a header is yours, with the exact value to paste. ## Pre-requisites | Requirement | Detail | |---|---| | WASViking plan | Header Advisor enabled (Pro plan and above). Pro allows one hostname, Business three. | | Role | **Admin** or **Manager** to start, approve, pause and remove advisors. | | Target | The hostname, or a domain it belongs to, registered under **Assets Inventory**. The advisor only accepts hostnames your organization owns. | | Somewhere to set a response header | Your CDN or edge (Cloudflare, Google Cloud Load Balancer), your web server (nginx, Apache, IIS) or the application itself (Express, Next.js, Django, Spring Boot, ASP.NET Core). | ## What to expect | Phase | What happens | Typical duration | |---|---|---| | Waiting for the first report | The advisor exists, the header is not live yet. | Until the header is deployed. | | Learning from real traffic | Browsers report what the pages load. Sources accumulate with days, browsers and pages. | 7 days by default (3 to 30). Ready earlier when no new source appears for 72 hours. | | Ready | The proposal is on screen with a grade and the decision cards. | Until you approve. | | Approved, then Candidate | You copy the policy as report-only. WASViking detects it from the reports. | 3 clean days before enforcing. | | Enforced | You rename the header to `Content-Security-Policy`. Detected the same way. | Ongoing. Analysis runs every 10 minutes. | | Update needed | A legitimate source appeared after deployment and reached quorum. A diff for the next version is ready. | Until you approve the update and paste the new value. | --- ## Step 1: Add the hostname Open **Edge Threat Radar → Header Advisor** and click **Add hostname**. The field lists your targets; pick one, or type a subdomain under one of them. The hint under the field tells you on the spot whether the value is accepted. Keep **Content Security Policy** as the policy and click **Start learning**. The advisor opens on **Step 1: Add the discovery header** with the status **Waiting for the first report**. ## Step 2: Add the discovery header on your platform The step shows two headers and a walkthrough per platform. Both headers go on the responses of that hostname: | Header | Purpose | |---|---| | `Reporting-Endpoints` | Tells modern browsers where to send reports. | | `Content-Security-Policy-Report-Only` | The discovery policy. It reports every load and blocks nothing. | The discovery policy is deliberately `default-src 'none'`: in report-only mode that means "report everything the page loads", which is exactly the evidence the learning window needs. > The header name must end in **-Report-Only**, exactly as copied. > `Content-Security-Policy` without that suffix enforces the discovery > policy and blocks every resource on the page. ### Cloudflare, the worked example In the Cloudflare dashboard open the zone and go to **Rules → Overview**. Click **Create rule** and pick **Response Header Transform Rule**. ![The Cloudflare rule that carries the discovery header: hostname match, two static response headers, placed last](https://docs.wasviking.com/static/docs/images/header-advisor/01-cloudflare-transform-rule.png) *One rule per hostname: the expression matches the host, the two rows carry the header names and values copied from the portal, and the rule is placed last so it wins over older ones.* 1. **Rule name**: `WASViking Header Advisor - `. Ignore the two templates at the top of the page; fill in the form below them. 2. **If incoming requests match**: keep **Custom filter expression**. In the builder set **Field** to Hostname, **Operator** to equals and **Value** to your hostname. The link next to the field switches between the builder and the plain editor (**Edit expression** and **Use expression builder**); in the editor the expression is `http.host eq ""`. 3. **Then → Modify response header**: choose **Set static**. Use the **Copy name** and **Copy** buttons in the portal and paste each header name in **Header name** and each value in **Value**, exactly as they are, quotes included. Click **Set new header** for the second row. 4. **Place at**: leave **Last**. 5. Click **Deploy**. **Save as Draft** does not publish the rule. Two mistakes worth a second look before you deploy: the **Value** of the `Reporting-Endpoints` row is the `wasviking="https://..."` string, not the header name again; and the hostname in the expression is the one you added to the advisor, so `www` and the apex are two different rules if you advise both. ### Google Cloud Load Balancer Response headers are set on the backend service of the external HTTPS load balancer. The portal gives you the `gcloud` command with the `--custom-response-header` flags for that hostname. The flag replaces the whole custom header list, so include any custom response headers the backend already has. ### nginx, Apache and IIS The portal renders the exact configuration lines: `add_header` for the server block in nginx (put them in every `location` that already uses `add_header`, because a location-level `add_header` replaces the server-level ones), `Header always set` for Apache, and the `customHeaders` entries for `web.config` in IIS. Reload the server after the change. ### Express, Next.js, Django, Spring Boot and ASP.NET Core When the header is easier to set in the application, the portal renders the middleware or configuration snippet for each framework, with the values already escaped for that language. Deploy the application as usual. Whatever the platform, the two header values are the same. Prefer the edge when you have one: the header goes live in seconds, without a release. ## Step 3: Confirm the header is live Open the application in a browser. The advisor recognises the header from the browsers' own reports within a few minutes and the page refreshes on its own. **Check header** fetches your home page and reads the headers it sends right now, which is the fastest way to confirm the rule before any user visits. If the status stays on **Waiting for the first report**, the rule is usually still a draft, the expression matches a different hostname, or the header name lost its suffix. ## Step 4: Watch the learning window ![Step 2 of the advisor on the first day: sources, pages seen, browsers, and the source table with verdicts](https://docs.wasviking.com/static/docs/images/header-advisor/02-learning-from-real-traffic.png) *Day one of the learning window. Every source carries its directive, its classification, the evidence behind it and a verdict.* The step shows the day counter, the number of sources, pages seen, distinct browsers, sources new in the last 24 hours and whether the evidence is stable. The table underneath lists every source: - **Recommended** sources reached quorum and passed the reputation check. They go into the policy. - **Needs your decision** marks inline scripts, inline event handlers, inline styles, `eval`, blob workers and hosts under frequently abused top-level domains. The proposal asks you what to do with each. - **Watching** sources are below quorum. They are not proposed yet. - **Threat** sources are look-alikes of your domain, address literals or sources pulled by addresses Edge Threat Radar flags. They are never proposed. Documented services are completed automatically: when the browsers report Google Tag Manager, the directives its documentation requires are added even if the learning window never saw them. A staging or internal application with fewer than ten browsers runs in low-traffic mode, with a quorum of one, so it can still get a policy. **Settings** on the advisor lets you change the learning window and the quorum. ## Step 5: Review and approve the policy When the window ends, or earlier when the evidence is stable, the advisor moves to **Step 3: Review and apply the policy** and, if you opted in, the **Header Advisor** event reaches your notification channels. The proposal shows the policy one directive per line and its grade: **Strong**, **Partial**, **Weak** or **Missing**, with the reason. Each decision card lists the options with their impact. Hashes for inline scripts and styles are computed from your pages when the hostname is reachable; a per-response nonce is offered when the application can emit the header itself; `unsafe-inline` is the pragmatic fallback and the grade says what it costs. The policy updates as you change the cards. Click **Approve this policy**. The version is recorded with its decisions and evidence and becomes the reference for deployment detection and drift. ## Step 6: Roll out in two moves The approved policy comes with two tabs: **Copy as report-only (candidate)** and **Copy enforced**, each with the same per-platform walkthrough as the discovery header. 1. **Candidate.** Replace the discovery header with the approved policy, still as `Content-Security-Policy-Report-Only`. The browsers now report only what the policy would block. WASViking recognises the policy from the reports and marks the advisor **Candidate**. Let it run for three clean days; anything it reports in that window is either a source to add or content that does not belong. 2. **Enforce.** Rename the header to `Content-Security-Policy`. Keep the reporting directives, they are how drift and injections keep being detected. The advisor becomes **Enforced** and shows **In sync with the approved policy**. ## Day two: keeping the policy current - **Update needed.** A legitimate source that appeared after the deployment and reached quorum shows under **Review changes** with a diff for the next version. Select the sources, click **Approve vN with the selected sources**, copy the new value to your edge or origin. The advisor marks it in sync once the browsers report it. - **Threat signals.** An injected inline script, a look-alike domain or a source pulled by attacker addresses appears under **Threat signals** with severity, page and sample. Investigate, then **Acknowledge**. Nothing in that list is ever added to a policy. - **Pause and Remove.** **Pause** ignores reports while the header can stay in place; **Remove** deletes the advisor and its evidence. Remove the header from your edge or origin as well. - **Alerts.** Enable the **Header Advisor** event on each channel under **Notification Channels** to be told when a policy is ready, an update is waiting or a threat signal appears. ## Troubleshooting | Symptom | Cause | What to do | |---|---|---| | Stays on **Waiting for the first report** | Rule saved as draft, expression on another hostname, header name without `-Report-Only`. | Deploy the rule, check the expression, copy the names again. Use **Check header**. | | **Check header** says the hostname does not resolve | The hostname has no public DNS record, or resolves to a private address. | Check header needs a public hostname. Learning still works from the browsers. | | Reports are dropped as foreign | Browsers on `www` report to an advisor for the apex, or the reverse. | Advise the hostname users actually open, or one advisor per hostname. | | The policy is ready with very few sources | Low traffic, or the learning window ran on a page nobody visited. | Extend the learning window under **Settings**, or wait for the stability signal. | | A legitimate provider is missing after enforcement | It appeared after the learning window. | It shows under **Review changes** once it reaches quorum; approve the update. | | Application breaks right after the discovery header | The header was deployed as `Content-Security-Policy`. | Rename it to `Content-Security-Policy-Report-Only`. The discovery policy is never meant to enforce. | --- # Endpoint protection exclusions for Sentinel Host Section: Getting Started Source: https://docs.wasviking.com/getting-started/endpoint-protection-exclusions/ Summary: What to exclude in your antivirus, endpoint detection and TLS inspection products so the Sentinel Host agent can register, report and update. Covers the domains, folders, processes and ports the agent uses, the check that names the product in the way, and where the setting lives in the most common products. The Sentinel Host agent connects out to the WASViking® cloud over mutual TLS and never opens an inbound port. That design survives firewalls and proxies well, with one exception every fleet eventually meets: a security product that terminates and re-signs TLS traffic on the machine or on the network. Mutual TLS cannot pass through a re-signed connection by design, so the agent registers and then never sends its first inventory, or does not even register. The portal shows the enrollment as "Never reported" or "Token never used". This page lists exactly what to exclude, how to confirm it worked, and where the setting usually lives. Apply it before installing on servers behind endpoint protection or a web gateway, or as soon as an enrollment stalls. ## The rule of thumb Exclude the WASViking domains from TLS inspection, and exclude the agent's binary and data folder from real-time scanning and behavioral blocking. Do not turn off the product's global scanning or its TLS inspection for everything else; a narrow exclusion is all the agent needs. ## What the agent uses **Domains.** Exempt `*.wasviking.com` from TLS inspection, or at least these two names: | Domain | Purpose | Port | | --- | --- | --- | | `api.wasviking.com` | Registration, certificate bundle, agent updates | 443 (HTTPS) | | `sentinel.wasviking.com` | Inventory, heartbeats, Resolve jobs (mutual TLS over gRPC) | 443 | **Folders and processes.** | Platform | Binary | Data directory | | --- | --- | --- | | Windows | `%ProgramFiles%\WASViking\SentinelHost\wasviking-sentinel-host.exe` | `%ProgramData%\WASViking\SentinelHost` | | Linux | `/usr/local/bin/wasviking-sentinel-host` | `/var/lib/wasviking-sentinel-host` | | macOS | `/usr/local/bin/wasviking-sentinel-host` | `/Library/Application Support/WASViking/SentinelHost` | The Windows service is named `WASVikingSentinelHost`; on Linux the unit is `wasviking-sentinel-host.service`; on macOS the daemon label is `com.wasviking.sentinel-host`. When the product supports trusted applications or process exclusions, add the binary path; when it only supports folders, add the binary folder and the data directory. The data directory holds the agent's certificate, its state and its logs, and the agent writes there every cycle. **Why the binary matters too.** Agent updates arrive as a new binary downloaded from `api.wasviking.com` and swapped into place. A product that quarantines unknown executables can stop an update the same way it stops a first install. ## Confirm before and after On the host, run the check as an administrator: ``` wasviking-sentinel-host check ``` The verdict names the product re-signing TLS when there is one ("TLS interception detected ... signed by "), tells a DNS filter from a captive portal, and proves the mutual TLS channel end to end once the exclusion is in place. Since agent 0.1.29 the installer runs the same probes before registering and stops on a blocked path, so a failed install leaves nothing behind on the platform. After the exclusion, do not mint another token or run the installer again: the agent already installed reconnects on its own within minutes. A reinstall on the same machine resumes the same asset. ## Where the setting lives The names below are the products' own. Menus move between versions, so treat the path as a pointer to the right screen and search the product's documentation for "trusted domains", "SSL inspection exclusions" or "exclusions" when it differs. **Kaspersky Endpoint Security (Security Center or Cloud console).** Open the security profile applied to the host, pick the operating system tab, then General settings, Network settings. Under "Encrypted connections scan", add `*.wasviking.com` to Trusted domains. Under Trusted applications, add the agent binary with "Do not scan network traffic", and add the binary folder and the data directory as scan exclusions. Keep the global scan on. **Microsoft Defender for Endpoint and Defender Antivirus.** Through Intune or Group Policy, add the agent binary as a process exclusion and the data directory as a folder exclusion. Defender does not re-sign TLS, so the domains rarely need anything; if Network Protection or a web content filter is in place, allow the two domains there. **CrowdStrike Falcon.** Falcon does not intercept TLS. If the sensor blocks or quarantines the agent, create a Machine Learning exclusion or an IOA exclusion for the binary path and the data directory in the Exclusions area of the console. **SentinelOne.** Add a path exclusion for the binary and the data directory in the policy's Exclusions, with interoperability mode if the agent's updates keep getting flagged. **Sophos (Intercept X, Central and Sophos Firewall).** In Central, add the binary and data directory to Global Exclusions. If SSL/TLS decryption is on at the firewall or in the endpoint web control, add `*.wasviking.com` to the decryption exclusions. **ESET (Endpoint Security and PROTECT).** Under Web and email, SSL/TLS filtering, add the agent binary to the list of applications excluded from filtering, and add the binary folder and data directory as real-time exclusions. **Bitdefender GravityZone.** In the policy, add the paths to Exclusions and, if "Encrypted web scan" is enabled, exclude `*.wasviking.com`. **Trend Micro Apex One and Vision One.** Add the paths to the exception list, and if Web Reputation inspects HTTPS, add the domains to its approved list. **Zero Trust and web gateways (Zscaler, Netskope, Cloudflare Gateway, Cisco Umbrella).** Add `*.wasviking.com` to the SSL inspection bypass list, sometimes called "Do not decrypt". The host client of these products is a frequent cause on laptops and jump hosts. **Firewalls with decryption (Palo Alto, Fortinet, Check Point, WatchGuard).** Add the two domains to the decryption exclusion list of the policy that covers the server's egress. ## Still stuck Send the support bundle the check writes (`wasviking-host-check__.json` and `.txt`) to your WASViking contact. It carries the certificate chain the host saw, the processes that can filter traffic, and the agent's recent log lines, with no secrets. --- # Bring your internet-facing assets under management Section: Getting Started Source: https://docs.wasviking.com/getting-started/attack-surface-coverage/ Summary: Cross the Attack Surface with Infrastructure Defense. See which of the internet-facing assets you own are served by a host you manage, which are not, and take each unmanaged one from "we found this exposed" to "this is now under control" in a few clicks. Attack Surface discovery tells you what the internet can see of your organization. Infrastructure Defense tells you what is running inside the servers you manage. This guide connects the two: for every internet-facing asset you own, WASViking® works out whether a host you manage serves it, labels the asset with a management state, and gives you the next step for the ones that are not covered yet. By the end you will read one sentence on the Command Center ("N externally exposed assets are not currently protected by Infrastructure Defense"), open the list behind it, and bring those assets under management one by one. ## Before you start You need Infrastructure Defense enabled for your organization and at least one of the following in place: - A Target or a promoted asset in your Attack Surface. Coverage only looks at assets you own: the domains you seeded as Targets and the discoveries you promoted in **Discovery**. An asset still waiting for triage is never counted as unprotected, so triage first (see [Targets and assets](https://docs.wasviking.com/concepts/targets-and-assets/)). - Optionally, hosts already enrolled with the Sentinel Host agent or discovered by a Sentinel Probe (see [Set up Infrastructure Defense](https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/) and [Set up Sentinel Probes](https://docs.wasviking.com/getting-started/sentinel-probes/)). Without any host, every owned asset simply shows as unmanaged, which is a perfectly honest starting point. Reading the coverage needs the View permission on Infrastructure Defense. Linking an asset to a host by hand and running a recheck need Manage. ## What the four management states mean Every host and every internet-facing asset carries one of four states. The same rule is used on every screen, so the number on the Command Center, the chip on the Assets list and the badge on an asset detail never disagree. | State | What it means | What is counted | | --- | --- | --- | | **Fully managed** | A Sentinel Host agent reports for the host and its policy keeps every capability on: inventory, vulnerability assessment, configuration assessment and patch management through Resolve. | Agent, Inventory, Vulnerabilities, Configuration, Patch | | **Partially managed** | Something reports, but at least one capability is missing: an agent under a policy that leaves a capability off, an operating system Resolve does not execute on yet, or a Sentinel Probe that inventoried the host with credentials (inventory and vulnerabilities, but no configuration or patching). | The capabilities that are actually on | | **Unmanaged** | Nothing reports. The host was only seen on the network by a probe, the agent is enrolled but silent, was deactivated or removed, or the asset resolves to a public address that no enrolled host owns. | Nothing | | **Unknown** | Only for internet-facing assets: nothing could be matched from outside. The asset is served through a CDN or WAF (the origin is invisible), the name did not resolve, or it is hosted somewhere you do not manage. | Not decided yet | Liveness is a separate signal. A fully managed host that is offline right now is still fully managed; the Assets list shows both. ## Step 1: own your attack surface Coverage starts from the assets you own, so the first step is to make sure the inventory reflects that. Open **Discovery** under Assets and promote the discoveries that belong to you; leave third-party services as third party and ignore what is not yours. Domains you registered as Targets are owned already. Two more things happen behind the scenes. Names that were discovered without an address (subdomain monitoring and the SSL check record where a name came from, not where it points) are resolved by the platform, and only names that resolve to a public address count as internet facing. A name and its address are one story: an IP address asset that is the address of an owned name is folded into the name, so one server never counts twice. ## Step 2: read the External attack surface panel Open **Infrastructure Defense → Overview**. In the Executive view, the fourth panel is **External attack surface**: how many internet-facing assets you own, split into the four states, and one sentence that says how many of them are not protected by Infrastructure Defense. ![External attack surface panel on the Command Center: the number of owned internet-facing assets, the four state counters and the sentence about unprotected assets](https://docs.wasviking.com/static/docs/images/attack-surface-coverage/01-command-center-external-attack-surface.png) *ACME owns 18 internet-facing assets: one is fully managed, five resolve to public addresses no enrolled host owns, twelve sit behind a CDN or WAF and could not be tied to a host yet.* Each counter is a link into the list behind it. The panel also tells you how many discovered assets still wait for triage in Discovery: those are not counted anywhere on this panel until you decide whether they are yours. ## Step 3: open the Coverage tab and start with Unmanaged Click the **Unmanaged** counter, or open **Infrastructure Defense → Assets** and switch to the **Coverage** tab. The list leads with what needs a decision: unmanaged assets first, then unknown, then partially and fully managed. ![Coverage tab: four state tiles, the search box, and the table of owned internet-facing assets with exposure, discovery source, management state, host, risk and the next step for each](https://docs.wasviking.com/static/docs/images/attack-surface-coverage/02-coverage-tab.png) *The Coverage tab. Every row is one asset you own; the Management column says how covered the host behind it is, and Action is the next step.* Each row reads left to right: - **Asset**: the name or address, with the public addresses it resolves to and the ports the Attack Surface observed on it. - **Exposure**: **Internet facing** when it resolves to a public address. "Through a CDN or WAF" means the address belongs to an intermediary, not to your server. - **Discovery**: where the Attack Surface learned about it (a Target seed, a scan, subdomain monitoring, the SSL check). - **Management**: the state and the reason, in plain words. When a host is matched, the reason says how: the public address is assigned to the host, the host identifies itself by that name, or the host's own exposure evidence already named it. - **Host** and **Risk**: the enrolled host that serves the asset and its Viking Exposure Score. With no host, Risk falls back to the criticality you set on the asset. - **Action**: the next step that fits the row. Use the tiles to filter by state and the search box to find one asset, a host or an address. **Export CSV** honors the same filters. ## Step 4: bring an asset under management The **Action** column proposes the step that fits each row; the same step appears on the host's detail page as **Bring under management**. - **Install Sentinel Host** for an asset served by a server nobody enrolled yet, or for a host the probe found that can run an agent. Follow [Set up Infrastructure Defense](https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/): create an activation key and run the install command on that server. - **Add probe credentials** for a device that will never run an agent (a switch, a printer, an appliance). An authenticated scan over SSH or WinRM turns it from Unmanaged into Partially managed, with inventory and vulnerabilities. See [Set up Sentinel Probes](https://docs.wasviking.com/getting-started/sentinel-probes/). - **Review policy** for a Partially managed host whose policy leaves a capability off. Turning the capability on under **Infrastructure Defense → Policies** changes the state on the spot. - **Open Sentinel Hosts** for a host whose agent was deactivated or revoked: reactivate it or enroll again. You do not need to come back and tell the platform what you did. When the new agent sends its first inventory, the coverage of your organization is rechecked right away and the asset moves to the state its host earned. The full pass also runs once a day. **Recheck now** on the Coverage tab runs it on demand. ## Step 5: link an asset behind a CDN or WAF to its host An asset that shows **Unknown** with "Served through a CDN or WAF" cannot be matched from outside: the public address is the edge provider's, and the origin server behind it is invisible to the Attack Surface. You know which host serves it, so tell the platform. On the row, open **Link to a host**, type the hostname exactly as the Assets list shows it (the field suggests your enrolled hosts) and click **Link**. ![A Coverage row with the Link to a host form open and a hostname typed in, next to the Install Sentinel Host action of an unmanaged row](https://docs.wasviking.com/static/docs/images/attack-surface-coverage/03-link-to-a-host.png) *www.acme-example.com sits behind a CDN. The form links it to acme-app-02, the host that serves it.* The asset now follows that host: its management state is the host's, the reason reads "Linked to a host by your team" with the name of who did it, and the choice is recorded in the audit log. A manual link always wins over the automatic matching until someone clicks **Unlink**, which hands the asset back to the next coverage pass. ![The same two ACME rows after the link: www.acme-example.com now shows Fully managed with acme-app-02 as its host and an Unlink button, next to app.acme-example.com matched automatically by its public address](https://docs.wasviking.com/static/docs/images/attack-surface-coverage/04-linked-and-fully-managed.png) *After the link, both ACME assets are fully managed: one matched automatically by its public address, one linked by hand.* The same link is the right answer when a load balancer or a NAT address sits in front of the server, because the public address is then not assigned to the host itself. To link several assets to the same host at once, tick their rows, type the hostname in the bar above the table and click **Link selected**. **Unlink selected** hands the ticked manual links back to the automation. When an unmanaged asset resolves to an address inside a network you have authorized a Sentinel Probe to scan, the next step reads **Scan with a Sentinel Probe** instead: the probe can reach it from inside and, with credentials, inventory it. ## Step 6: verify on the host and in the inventory Open the host from the **Host** column. Its detail page has a **Management and Exposure** card: the state, a checklist of the five capabilities, the next step when one is missing, and on the exposure side the public addresses assigned to the host, the internet-facing assets it serves, the evidence behind the Internet exposed flag and any active attack traffic the edge blocked against it in the last days. ![Management and Exposure card on a host: Fully managed with the five capabilities checked, and the exposure side listing the public assets the host serves and the evidence behind the flag](https://docs.wasviking.com/static/docs/images/attack-surface-coverage/05-management-and-exposure.png) *acme-app-02 is fully managed and serves app.acme-example.com and www.acme-example.com, one matched by address and one linked by hand.* The same verdict is visible from the other side. In **Assets → Assets** (the inventory), the detail panel of an internet-facing asset carries an **Infrastructure Defense** section with the state, the host and a link straight to the Coverage tab, so whoever works from the attack surface sees whether the asset is protected without changing screens. ![Inventory detail panel of www.acme-example.com with the Infrastructure Defense section: management state, host, why, when it was checked and a link to open the Coverage tab](https://docs.wasviking.com/static/docs/images/attack-surface-coverage/07-inventory-panel.png) *From the attack surface side: the inventory panel of the asset carries the same verdict and the host behind it.* ## Day two - The **Assets** list of Infrastructure Defense has a **Management** column and a **Management** facet, and the **Unmanaged** chip under Needs attention counts by the same rule as the Command Center tile. ![Assets list with the Needs attention chips, the Management facet and the Management column showing Fully managed and Unmanaged badges per host](https://docs.wasviking.com/static/docs/images/attack-surface-coverage/06-assets-management-column.png) *Hosts side: the Management facet and column use the same four states, so a probe-discovered device that nobody manages is visible at a glance.* - Coverage is recomputed once a day for your whole organization, and again whenever a host sends its first inventory. Linked assets are never overwritten by the pass. - Every state is derived from what reports today. Deactivating an agent, revoking it, or removing a capability from a policy changes the state on the next page load; there is nothing to reset. - **Export CSV** on the Coverage tab gives you the same rows with the matching reason, the host, its score, the public addresses and the provider, for a spreadsheet or an auditor. - The Command Center chip **unprotected assets · 24h** counts the assets that became unmanaged in the last day and opens them; the Coverage tab's **Changed in the last 24 hours** filter is the same window. - The **Unprotected Internet Asset** alert sends one digest per coverage pass to the channels you enable under Settings, Alert Destinations (email, Slack, Teams, webhook) whenever assets you own become unmanaged: the names, the total and a link to the Coverage tab. Assets behind a CDN or WAF and assets you linked by hand never alert. - **Download report** on the Overview includes an External attack surface section with the four counts and the unprotected assets, for the stakeholder who reads the PDF instead of the portal. ## Troubleshooting **An asset I own shows Unknown.** Three causes, each spelled out in the Management column. "Served through a CDN or WAF": link it to its host (Step 5). "The name did not resolve": the record may be gone; check DNS or retire the asset in Discovery. "Waiting for the next coverage check": a link was just removed or the asset was just promoted; use **Recheck now** or wait for the daily pass. **The host is fully managed but its asset shows Unmanaged.** The asset resolves to a public address that is not assigned to the host: a load balancer, a NAT gateway or a reverse proxy in front of it. Link the asset to the host by hand. **The Command Center sentence counts fewer assets than Discovery shows.** By design. Discovered assets waiting for triage are never counted as unprotected; promote the ones that are yours and they join the panel on the next pass. Third-party and ignored assets stay out for good. **Two rows for the same name disappeared into one.** The inventory can hold the same value as a domain seeded from a Target and as a subdomain found by a scan. Coverage keeps one row per name; the inventory panel of either row shows the shared verdict. **A private address shows up as an asset but not in Coverage.** Only names and addresses that are reachable from the internet are internet-facing assets. Private, reserved and host-local names are left out of Coverage on purpose; the probe is the tool for what lives inside the network. ## What to look at next - [Infrastructure Defense](https://docs.wasviking.com/capabilities/infrastructure-defense/) in Capabilities explains how the Viking Exposure Score is built and how internet exposure proven by the Attack Surface feeds it. - [Set up Infrastructure Defense](https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/) walks through enrolling the first servers, and [Set up Sentinel Probes](https://docs.wasviking.com/getting-started/sentinel-probes/) covers the devices that will never run an agent. - [Targets and assets](https://docs.wasviking.com/concepts/targets-and-assets/) describes how the Attack Surface is discovered and triaged. --- # Roll back a change that misbehaved Section: Getting Started Source: https://docs.wasviking.com/getting-started/roll-back-a-change/ Summary: Undo a completed security update job from WASViking Resolve. Each package returns to the version it had before, the change goes through the same approval and window as the update did, the reassess measures the risk that came back, and the returned updates stay out of the recommendations until you decide otherwise. Sometimes an update breaks something the vulnerability never did: a TLS library that no longer handshakes with a partner, a cumulative update that makes a database service stall. Resolve lets you undo the change with the same discipline it applied to install it. The rollback is a job of its own, tied to the job it undoes, so the record shows what went in, what came back out, who decided each step and why. By the end of this guide you will have rolled back a completed job on a Linux or Windows host, read the verdict of the reassess, and seen how the returned updates are held out of the recommendations for a while. ## Before you start - A completed **Apply security updates** job on a Linux or Windows host. Only completed security update jobs can be rolled back; a restart job or a third-party application upgrade has nothing to roll back. - The Sentinel Host agent **0.1.38 or later** on that host. The Resolve screen does not offer the action on an older agent; update it from **Infrastructure Defense → Sentinel Hosts** first. - The Manage permission on Infrastructure Defense to create the rollback job, and the Approve permission to sign it off, exactly as for any other change. The approval governance of the host's environment applies to a rollback as it applies to an update. ## What a rollback does on each platform | Platform | What the agent does | What it never does | | --- | --- | --- | | **Linux** (apt and dnf) | Downgrades each package to the exact version it had before the job, one transaction per package, with a fresh index so the previous version can be found. | Touch the kernel. The previous kernel normally stays installed; boot it from the boot menu if the new one is the problem. | | **Windows** | Removes each update by its KB number through the Windows Update Agent, the same engine that installed it. An update the platform does not allow removing (a servicing stack update, a definition update) is reported as such. A KB the agent no longer lists is tried through the standalone installer. | Install or remove anything the original job did not name. | | **macOS** | Nothing. Apple offers no way to remove an installed macOS update; the Resolve screen says so instead of offering the action. Recover the host from a backup or reinstall it. | | A rollback is a change like any other. The host keeps its "one change at a time" rule, the maintenance window still applies, and a host with a restart still pending from an earlier change holds the rollback the same way it holds an update. ## Step 1: find the change and open the rollback Open **Infrastructure Defense → Resolve** and scroll to **Jobs**. A completed security update job that can be rolled back shows **Roll back this change** in its Actions column. Open it. ![Jobs table on the Resolve screen with the Roll back this change form open on a completed job of acme-app-01: the reason, the back-out plan carried over from the original job, an incident reference, and the number of days the returned updates stay out of Resolve](https://docs.wasviking.com/static/docs/images/roll-back-a-change/01-roll-back-this-change.png) The form asks for: - **Why this change has to go.** Required. This is the evidence that the update misbehaved; it goes into the approval record, the e-mail to the approvers and the CSV export. - **Back-out plan.** Pre-filled with the plan the requester stated when the original job was scheduled, if any. Adjust it for the record. - **Change or incident reference.** Optional, for the ticket in your own tool. - **Keep these updates out of Resolve for N days.** Fourteen by default, up to ninety, zero for none. After the rollback the same updates come straight back as pending on the next inventory; this hold stops WASViking® from recommending them again the same afternoon (see Step 5). - **Run now** skips the maintenance window when the governance approves the job on creation. When a person has to approve it, that person makes the scheduling choice. - **Allow a reboot** appears only when the host policy allows automatic reboot; a Windows removal may ask for one. On a Windows host the same form explains the KB-based removal and which updates cannot be removed. ![The same form on the Windows host acme-db-01, with the note that Windows removes each update by its KB through Windows Update and that servicing stack and definition updates are reported as not removable](https://docs.wasviking.com/static/docs/images/roll-back-a-change/07-windows-rollback-form.png) Confirm with **Create the rollback job**. Nothing runs yet: a rollback job is created for the same packages, frozen with the version each one returns to. ## Step 2: approve the rollback The new job appears at the top of the Jobs table with a **Rollback of** badge naming the job it undoes, the expected score movement (from the current score back to the score the host had before the update), and the approval the governance of its environment requires: automatic, one person, or a formal quorum by people other than the requester. ![The rollback job pending approval: the Rollback of badge with the original job id, three packages, VES 70 to 88 expected, the approval window date, and the Approve or reject form open with the incident reference carried over](https://docs.wasviking.com/static/docs/images/roll-back-a-change/02-rollback-job-pending-approval.png) Approve it as you would approve an update. Choose **Run now** to skip the maintenance window, or leave it and the host picks the job up when the window opens. An emergency approval is available under the same rules as for any formal job. ## Step 3: what happens on the host The agent runs its pre-checks (privileges, free space, memory on Linux) and then returns each package, reporting the outcome per item: - **downgraded to** the previous version, or **removed** for a Windows KB, when the package really went back; - **not installed on the host** when the update was not there any more; - **not uninstallable** when Windows does not allow removing that update; - **skipped** for a kernel package, for a package with no previous version recorded, or for a version the agent refused to pass to the package manager; - **failed** with the reason, for example a previous version that no configured repository carries any more. The job fails only when nothing at all went back. A partial rollback is reported as such, item by item, never as a full success. ## Step 4: read the verdict The rollback completes when the next inventory has been reassessed. The Status column then reads **Rolled back: VES 70 to 88; 3 of 3 packages returned to the previous version; 17 vulnerabilities open again.** The numbers are measured by the reassess, never estimated: the score goes back up because the vulnerabilities the update closed are open again, and the line says so plainly. ![The verified rollback job: Completed, the Rolled back verdict with the score movement, the packages returned and the vulnerabilities open again, next to the original job it undid](https://docs.wasviking.com/static/docs/images/roll-back-a-change/03-rollback-verified.png) A Windows removal that needs a restart says **restart pending to finish the removal**; the host shows the pending restart and the usual restart actions apply. ## Step 5: the hold on the returned updates The updates a rollback returned come straight back as pending on the next inventory. To keep Resolve from recommending them again right away, the packages that really went back are **held** on that host for the days the rollback job asked. While the hold lasts: - the **Recommended actions** row of the host leaves those packages out of the plan and says so; - the host's asset page shows a **Held Back After Rollback** card with each package, the version that was rolled back, the date the hold ends and the rollback job that created it. ![Recommended actions row of acme-app-01 with the Review open: only libxml2 and sudo in the plan and a Held back note saying that three updates rolled back earlier stay out of the plan until the hold date](https://docs.wasviking.com/static/docs/images/roll-back-a-change/04-held-back-in-resolve.png) ![Held Back After Rollback card on the asset page of acme-app-01: openssl, libssl3t64 and curl, the versions rolled back, the date the hold ends, the rollback job id and a Release hold button per row](https://docs.wasviking.com/static/docs/images/roll-back-a-change/05-held-back-after-rollback.png) A hold expires on its own. To offer an update again earlier, for example after the vendor published a fixed build, press **Release hold** on that row. The release is recorded under your name and the update is back in the recommendations from the host's next assessment. ![The notice after releasing a hold: Resolve offers this update again from the next assessment of the host](https://docs.wasviking.com/static/docs/images/roll-back-a-change/06-hold-released.png) The hold governs what WASViking® recommends. It does not stop the operating system's own automatic updates: a Windows host with automatic installation turned on may reinstall a removed update on its own schedule. Pause automatic updates on that host through your usual policy if the hold has to be strict. ## Day two - **Alerts.** A verified rollback raises a **Rollback Verified** alert on your channels with the packages returned, the vulnerabilities open again and the hold; a failed one raises **Patch Job Failed** with the reason. - **Evidence.** The rollback job carries the reason, the back-out plan, the reference, the job it undid and the hold, and every approval decision on it. **Export approval records** on the Resolve screen writes all of it to CSV, one row per decision, with the original job id in the `rollback_of` column. - **Audit trail.** Creating the rollback, approving it and releasing a hold are audit entries like every other Resolve action. ## Troubleshooting **The job has no Roll back this change action.** Either it is not a completed security update job, the host runs macOS, the agent is older than 0.1.38, the host has another job active, or the job was already rolled back (a job is rolled back once). The notice after the click names the reason when the button was there a moment ago. **Nothing in this job can be rolled back.** The job only contained kernel packages, or entries with no previous version or KB identifier. The kernel is never downgraded on purpose; boot the previous kernel instead. **A package came back as "failed: still at" the new version.** The previous version is not available in any configured repository any more (some distributions keep only the current build of a security update). Restore the package from your own mirror or snapshot; the other packages of the job went back on their own lines. **A Windows update came back as "not uninstallable".** Windows does not allow removing servicing stack updates and definition updates, and some combined packages can only be removed by the platform's own tooling. The per-item outcome names them; the rest of the job went back. **The update was recommended again the next day.** The rollback job was created with a hold of zero days, or the hold was released. Roll back with a longer hold next time, or leave the update unticked in the Review of the next job. ## What to look at next - [Set up Infrastructure Defense](https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/) walks through scheduling and approving an update, the change a rollback undoes. - [Infrastructure Defense](https://docs.wasviking.com/capabilities/infrastructure-defense/) in Capabilities explains Resolve, the approval governance and how the Viking Exposure Score is measured before and after every change. --- # Targets and assets Section: Concepts Source: https://docs.wasviking.com/concepts/targets-and-assets/ Summary: How WASViking represents what you scan, and the difference between a target you declare and an asset the engine discovers. WASViking® separates two ideas a lot of tools conflate: - A **target** is something you declare. A URL, an OpenAPI document, an SOAP WSDL endpoint, a host with sensitive ports to monitor. - An **asset** is something the engine discovers while scanning a target. A subdomain, a login form, an API path, a GraphQL operation, an exposed port, a TLS certificate. The distinction matters because the work happens at the asset level. Findings, evidence, Risk Score, and SLA all attach to assets, not to the target you typed in. ## Targets A target is the unit of authorization. WASViking enforces ownership at target creation time. Without ownership proof, the target cannot be scanned. This is policy, not engine code. A target has: | Field | Purpose | |---|---| | URL | Where the scan starts. | | Subdomain coverage | `single host` or `wildcard`. Wildcard expands to discovered subdomains. | | Group | Optional tag for filtering and reports. | | Scan profile | Default profile for scheduled scans. | | Auth mode | Form Login, Bearer, Cookie, Header, or unauthenticated. | ## Assets Assets are produced by the Target Discovery Engine and by analyzers as they crawl. The platform tracks five kinds: | Asset kind | Source | |---|---| | Subdomain | DNS enumeration, certificate transparency, headless-browser crawl. | | URL | Crawl, OpenAPI ingest, Swagger ingest, GraphQL introspection. | | Login form | Form discovery + AI Form Autofill. | | Sensitive port | Sensitive port monitoring. | | Certificate | TLS certificate monitoring. | Each asset carries a parent reference so chains (host → subdomain → URL → form) survive across scans. ## Asset lifecycle and drift Assets have three lifecycle events the Risk Score and webhooks consume: - `first_seen`: the asset just appeared. - `disappeared`: the asset is no longer reachable. - `reappeared`: the asset returned after a `disappeared` event. Asset drift is a signal in itself. A subdomain that disappears and reappears with a different certificate is worth knowing about. ## Subdomain discovery When an asset is configured with **Monitor SSL → Domain and discovered subdomains** (set at asset creation time), WASViking continuously enumerates subdomains under the root domain and tracks them as assets in their own right. ### How discovery works | Source | What it brings | |---|---| | Certificate transparency logs | New subdomains appear in CT logs as soon as a certificate is issued. WASViking observes those events. | | Passive DNS | Subdomains visible in DNS records the platform can resolve. | | Crawl signals | URLs the DAST crawler discovers during scans under the root domain. | Discovery runs at the root domain level. To opt in for an asset, toggle **Monitor SSL** with scope **Domain and discovered subdomains**. ### Lifecycle events Each discovered subdomain is treated as a regular asset and follows the same `first_seen` / `disappeared` / `reappeared` lifecycle as a manually declared one. Webhook events fire identically. ### Per-org safeguards Discovery and the auto-scan that follows are bounded: - **Per-org cap** on how many subdomains a single root can fan out to (driven by your plan). - **Deny-list** of patterns you do not want scanned (`staging.*`, `*.internal.*`, dev hostnames). - **24-hour cooldown** between auto-scans on the same host. - **TCP probe gate** so unreachable subdomains do not consume a scan slot. ## Auto-discovery scan When subdomain monitoring sees a newly enumerated subdomain, WASViking triggers a full-coverage DAST scan automatically (subject to the guards above). Net new attack surface gets evidence within a day, not a quarter. The same Auto-discovery scan also kicks in for assets created manually with the discovered-subdomains scope as soon as the first enumeration cycle completes. ## Asset Inventory The Asset Inventory page at `/portal/inventory/` is a flat, filterable view across every asset every scan has produced. Use it to answer questions like: - "Which subdomains run TLS 1.2 only?" - "Which assets reappeared last week?" - "Which assets have an open finding right now?" - "Which assets carry components flagged by KEV?" The inventory is the foundation for the Exposure Intelligence module and for the Findings risk amplification rules. --- # Findings and Risk Score Section: Concepts Source: https://docs.wasviking.com/concepts/findings-and-risk-score/ Summary: What a finding is, how WASViking ranks it, and how the workflow keeps the team operating on the highest risk first. A **finding** is a specific, reproducible security issue WASViking® detected. Findings are the unit of work for the security team. Everything in the workflow, the Risk Score, the SLA, the audit log, the AI recommendation, exists to make findings actionable. ## Anatomy of a finding Every finding carries: | Field | Purpose | |---|---| | Stable fingerprint | The same issue is the same row across scans. Drives status preservation. | | Category | `sqli`, `xss`, `graphql_bola`, `cve`, and so on. Drives compliance mapping. | | Severity | `critical`, `high`, `medium`, `low`. Raw engine output. | | CWE | Single canonical mapping. See [CWE mapping](https://docs.wasviking.com/concepts/findings-and-risk-score/#cwe-canonical-mapping). | | Risk Score 0-100 | Combines severity with context. The number dashboards rank on. | | Evidence | Payload, raw HTTP transcript, the analyzer that produced it. | | Status | `open`, `accepted`, `mitigated`, `false_positive`, `fixed`. | | SLA window | Days by severity, configurable per organization. | | Audit log | Every status change with operator and timestamp. | | Compliance mapping | Control IDs across PCI, LGPD, GDPR, BACEN, ISO 27001. | | AI recommendation | Executive summary, business risk narrative, prioritized action. | ## Risk Score 0-100 Severity alone is too blunt. WASViking computes a Risk Score that combines: - **Severity weight.** Critical 100, High 80, Medium 40, Low 10. - **Asset criticality.** Inferred from inventory signals (login form, payment form, admin area). - **Environment.** Production amplifies, staging dampens. - **Industry.** A SQLi against a payment endpoint in a fintech weighs more than the same SQLi against a marketing form. - **SLA window.** A finding inside its SLA window keeps its weight; a finding approaching breach gets amplified. The Risk Score is what the Cyber Risk dashboard ranks on. The team works the top of that list, not the latest report. ## Edge correlation and risk amplification When the Edge Threat Radar observes adversary traffic that maps to an open finding, WASViking amplifies the finding's Risk Score. This ties internet adversary activity to your own posture, in real numbers. ## Finding status workflow ``` open ──▶ accepted ──┐ │ │ ├─▶ mitigated ────┤ │ ├──▶ fixed └─▶ false_positive ┘ ``` Every transition is auditable and emits a webhook event. Status preservation across scans is the reason the stable fingerprint exists: a `mitigated` finding stays `mitigated` after a re-scan, not a fresh `open`. ## SLA windows Each open finding gets a remediation deadline based on its severity. The deadline is `first seen` plus the SLA window for that severity. Once the deadline passes and the finding is still open, it counts as breached. Set the policy under **Settings → System Settings → Findings SLA**. The window is expressed in **days** per severity: | Severity | Default window | |---|---| | Critical | 7 days | | High | 30 days | | Medium | 60 days | | Low | 90 days | | Informational | Off | Leave a field empty or set it to **Off** to stop SLA tracking for that severity. Informational findings have no SLA by default. Two save behaviors, deliberately separate: - **Save policy** applies the new windows to findings going forward. Existing findings keep the deadline they already have. - **Recompute existing** rewrites the due date and breach state of every open finding to match the current policy. Use it when you tighten or loosen a window and want it to apply retroactively. The SLA breach digest runs on schedule and pushes near-breach findings to the configured destinations so the team sees what is about to age out before it does. ## AI recommendation The AI layer produces an executive summary, a business risk narrative, and a prioritized action per finding, bilingual EN, PT-BR, and ES. The engine's `primary_risk_category` wins on every disagreement with the LLM. This is enforced in code, not in marketing. AI cannot drift past the engine. JWT claim contents in findings stay raw. Enterprise visibility under contract beats blanket redaction for this specific data class. ## CWE canonical mapping WASViking pins a single canonical CWE per finding category. The mapping is version-controlled and tested. Adding a new analyzer means adding a mapping line, by policy. --- # Scan profiles and templates Section: Concepts Source: https://docs.wasviking.com/concepts/scan-profiles-and-templates/ Summary: How WASViking selects which analyzers run, and how to lock a baseline your whole team uses. A **scan profile** picks which analyzers run, which protocol coverage is on, which compliance catalog is primary, and which payload class settings apply. A **scan template** locks a profile plus its configuration so every team member runs the same baseline. ## Built-in scan profiles WASViking® ships six profiles. Pick the one that matches the application, or pick `full` if you are not sure. | Profile | Use case | Primary compliance catalog | |---|---|---| | `full` | Default. All analyzers enabled. | Driven by industry signal. | | `web_app` | Classic web application. Skips API-specific analyzers. | OWASP Top 10 + ISO 27001. | | `api_jwt` | REST API with JWT auth. JWT advanced analyzer active. | OWASP API Security + ISO 27001. | | `soap` | SOAP / WSDL service. WSDL parser and SOAP-context analyzers active. | BACEN for BR financial; ISO 27001 otherwise. | | `network` | Sensitive port and subdomain monitoring, SSL/TLS scan. | ISO 27001 + PCI infrastructure. | | `custom` | Operator-defined via `analyzer_toggles`. | Operator-defined. | The profile choice also drives: - **Compliance primary catalog.** Renders first in the PDF and the portal Compliance tab. - **AI prompt context.** The AI Recommendation favors the right framework vocabulary. - **`analyzer_toggles` gating.** Disabled analyzers are skipped even if the catalog lists them. ## Subdomain coverage Independent from the scan profile. A target with `wildcard` coverage expands to discovered subdomains; `single host` does not. Wildcard coverage runs through the Target Discovery Engine to enumerate the surface. ## Scan templates A template is a saved profile plus configuration. Templates make scans reproducible across the team. A template captures: - Scan profile (`full`, `web_app`, etc.). - Analyzer toggles (override the profile). - Auth mode and stored credentials reference. - Scope (subdomain coverage, allow-list, deny-list). - Schedule (one-shot or recurring). - SLA overrides per severity (if any). Templates have: - **Versioning.** Every edit creates a new version. The previous version stays referenceable in scan history. - **Lock.** Locked templates cannot be edited without unlock; protects baselines. - **Bulk apply.** Apply a template to a group of targets in one action. - **History and restore.** Revert to a previous version. - **Export and import.** Move a template between organizations. Secrets are stripped on export. ## Built-in templates WASViking seeds six system templates: | Template | Profile | Notes | |---|---|---| | Quick web scan | `web_app` | Fast feedback for development. | | Full external | `full` | Default for production-grade external scans. | | REST API + JWT | `api_jwt` | OpenAPI ingest + JWT advanced. | | SOAP / WSDL | `soap` | WSDL parser + SOAP-context analyzers. | | Network surface | `network` | Sensitive ports + SSL/TLS. | | Compliance pass | `full` | Compliance-first, longer evidence capture. | You can clone any system template to start your own, or build one from scratch. ## CI/CD usage The Sentinel CI gate accepts an org-scoped template slug. The server resolves the template; secrets never reach the runner. Exit code 70 if the template is not found, 71 if forbidden. See the Sentinel agent section for the full CI integration recipe. --- # Environment Profile Section: Concepts Source: https://docs.wasviking.com/concepts/environment-profile/ Summary: The per-host fingerprint that lets WASViking analyzers adapt to your stack instead of firing a static catalog. The **Environment Profile** is a per-host fingerprint WASViking® captures on every scan. It is the reason analyzers can adapt payloads to the detected stack and produce findings that survive engineering review. ## What it captures A profile records, per host: | Dimension | Examples | |---|---| | Stack | Spring Boot 2.x, Django 4.2, ASP.NET 6, Express 4. | | Protocols | REST, OpenAPI 3.x, GraphQL, SOAP 1.1/1.2, WebSocket, gRPC. | | Defenses | Cloudflare WAF, AWS WAF, ModSecurity, rate limiting, bot management. | | Auth surface | Form login present, OIDC issuer, JWT verify endpoint, Bearer support. | | Frontend | SPA flag (Next.js, React, Vue), SSR markers, hydration patterns. | | Headers | CSP class (strict, report-only, none), HSTS, COOP/COEP, Referrer-Policy. | | TLS | Protocol version, cipher class, certificate chain quality. | | Signals | Server header, X-Powered-By, generator meta, response timing. | ## How analyzers consume it The profile is shared with every analyzer running in the same scan, so they all read from the same source of truth. Six analyzers calibrate against the profile: | Coverage | What it adapts | |---|---| | **SQL Injection** | DBMS fingerprint drives payload variant selection. | | **Cross-Site Scripting (XSS)** | SPA detection switches to headless browser execution. | | **Injection class** | Defenses inform payload class selection (e.g., suppress noisy SSRF where egress filtering is detected). | | **JWT and token security** | JWKS placement determines kid-confusion attempts. | | **Sensitive file and path exposure** | Server stack drives the positive-fingerprint set. | | **Security headers** | Profile drives severity calibration. | ## Why this matters Static-payload scanners produce noise because they cannot tell a Postgres endpoint from a SQLite endpoint, an SPA from a server-rendered app, or a WAF-fronted endpoint from a bare one. Calibrated payloads reduce false positives without reducing coverage. A second-order benefit: the profile is itself a discovery artifact you can read in the portal. "Host runs Spring Boot 2.x with a JWT-protected REST API, HSTS on, no CSP, has an admin login form" is more useful than "host returned 200 OK". ## Persistence The profile is stored per scan. The portal surfaces it under the scan detail page so the team can see what the engine saw. ## How coverage grows Every signal the profile records is consumed by at least one analyzer. New fingerprint signals are added only when an analyzer adapts to them, so the profile stays a working input to detection rather than a descriptive sidebar. --- # Exploit Path Graph Section: Concepts Source: https://docs.wasviking.com/concepts/exploit-path-graph/ Summary: How WASViking materializes attack chains across findings and ranks them by chokepoint. Individual findings often look medium-severity. Chains of findings are critical. The **Exploit Path Graph** materializes the chains and ranks them, so the team works on real compound risk instead of flat severity lists. ## The model A node in the graph is a finding. An edge represents a logical dependency: "to reach finding B, an attacker first uses finding A." A path is an ordered sequence of nodes that ends at a high-value sink: an IAM token, a session secret, a privileged endpoint, a database boundary. Example path: ``` auth weakness ────┐ ├─▶ internal SSRF ─▶ metadata svc ─▶ IAM token no egress filter ──┘ │ ▼ chokepoint score: 92 ``` ## Chokepoint score The chokepoint score asks: if we remediate this node, how many paths collapse? Nodes that sit on many distinct paths score higher. The score is what the team triages on; one fix at a chokepoint clears more risk than three fixes at leaves. ## How chains are produced A materializer engine runs over the findings store and produces: - The set of valid paths under the current finding graph. - The chokepoint score per node. - Path validity flags (the chain still holds; the chain broke because a node was fixed). The materializer is deterministic. Same input findings produce the same graph. No LLM in the loop. ## The page `/portal/exploit-paths/` lists paths ranked by chokepoint score, with the underlying findings and remediation guidance per node. ## What is and is not in scope The graph models chains across findings WASViking detected. It does not fabricate exposures. The signal a path produces is only as good as the underlying findings; that is why depth in the analyzer catalog matters. A second design point: the model is conservative. Paths that require an assumption the platform cannot verify do not appear. The platform would rather show fewer high-confidence paths than many speculative ones. --- # External DAST Section: Capabilities Source: https://docs.wasviking.com/capabilities/external-dast/ Summary: Coverage, analyzers, and how WASViking calibrates payloads to the environment. The external DAST core covers a typical web application surface end to end. Every analyzer reads a per-host **Environment Profile** so payloads adapt to the detected stack rather than firing a static catalog. ## Analyzer catalog | Coverage | What it covers | Primary CWEs | |---|---|---| | **SQL Injection** | Error-based, boolean-blind, time-based, UNION-based, out-of-band SQLi across seven injection points. DBMS fingerprint drives payload selection. | CWE-89 | | **Cross-Site Scripting (XSS)** | Reflected, stored, and DOM. SPA-aware via headless browser execution. Auth-context propagation. | CWE-79 | | **JWT and token security** | Alg confusion, weak secret recovery, JWKS proprietary-path discovery, form-login JWT auto-discovery, raw claim visibility. | CWE-347, CWE-287 | | **Injection class** | One pass covers SSRF, CmdInj, Path Traversal/LFI, SSTI, Open Redirect, XXE, Insecure Deserialization, CRLF, RFI, IDOR, Race Conditions. | CWE-918, CWE-78, CWE-22, CWE-1336, CWE-601, CWE-611, CWE-502, CWE-93, CWE-98, CWE-639, CWE-362 | | **Component detection** | Fingerprints frameworks, CMS, and libraries from outside. Enriched with OSV.dev and CISA KEV. EOL heuristic. | CWE-937, CWE-1104, CWE-1395 | | **Sensitive file and path exposure** | Path classification with soft-404 calibration (three canary shapes), content-type gating, per-kind positive fingerprints. | CWE-538 | | **Security headers** | OWASP secure headers, CSP, HSTS, COOP/COEP, Referrer-Policy. Severity calibrated against the Environment Profile. | CWE-693 | ## Authenticated scanning Authenticated scans share a single form-login session across every analyzer. SQL Injection, XSS, JWT, GraphQL, and the injection-class checks all consume the same authenticated cookies, so a multi-analyzer run looks like one user to the target's anti-brute-force controls. The AI Form Login Autofill feature detects login selectors automatically and falls back to a headless browser for SPAs. A five-verdict compatibility classifier returns one of: `compatible`, `captcha`, `spa`, `multi-step`, `uncertain`. The verdict recommends the right auth mode (Form Login, Bearer, or Cookie). ## Blind-class detection (OAST) Blind SSRF, blind XXE, blind RFI, blind SSTI, and blind CmdInj are resolved through an out-of-band collaborator WASViking ships and operates itself. - Per-scan token, single-tenant correlation. - HTTP and DNS interactions captured. - Native integration with the injection-class checks. - No third-party data path. Everything stays in your tenant. The collaborator URL is `https://.oast.wasviking.com/`. The Out-of-Band Validation page covers this in full, including the portal view where captured interactions are listed. ## Environment Profile calibration The Environment Profile is available to every analyzer on each scan. Six analyzers adapt to it, which means fewer false positives and findings that survive engineering review. | Coverage | What it adapts | |---|---| | **SQL Injection** | DBMS fingerprint drives payload variant selection. | | **Cross-Site Scripting (XSS)** | SPA detection switches to headless browser execution. | | **Injection class** | Defenses (WAF, CSP) inform payload class selection. | | **JWT and token security** | JWKS placement determines kid-confusion attempts. | | **Sensitive file and path exposure** | Server stack drives the positive-fingerprint set. | | **Security headers** | Profile drives severity calibration. | ## False-positive controls WASViking ships three mechanisms specifically to suppress noise common in naive scanners: 1. **Soft-404 calibration with three canary shapes** in the sensitive-file and path checks, including a dotfile canary, kills empty-200 catch-all matches. 2. **Stable finding fingerprint** so the same issue is the same row across scans, with audit-tracked status. 3. **Primary risk category override** in the AI layer so the LLM cannot reclassify a finding away from the engine's verdict. ## Output Every external DAST finding carries: - A stable fingerprint. - The payload that triggered it. - The raw HTTP request and response that proves it. - A canonical CWE mapping. - A Risk Score 0-100. - An SLA window. - A compliance control mapping. - An AI recommendation, bilingual (EN, PT-BR, ES). ## What is not covered here - **Modern API protocols.** See [Modern API Security](https://docs.wasviking.com/capabilities/modern-api-security/). - **Internal applications.** See [Internal scanning](https://docs.wasviking.com/sentinel/internal-scanning/). - **Software composition.** See [Software Supply Chain](https://docs.wasviking.com/capabilities/software-supply-chain/). --- # Modern API Security Section: Capabilities Source: https://docs.wasviking.com/capabilities/modern-api-security/ Summary: GraphQL, SOAP/WSDL, WebSocket, and JWT analyzers in one platform, with shared discovery and shared session. REST is the easy half of modern API coverage. WASViking® ships first-class analyzers for the protocols legacy DAST tools skip or sell as separate SKUs: GraphQL, SOAP/WSDL, WebSocket, and JWT. ## GraphQL 15 detectors across three tiers. ### Tier 1 surface - Introspection enabled (per environment policy). - Suggestion-mode information disclosure. - Verbose error responses. - Unbounded list arguments. - Deprecated field leakage. ### Tier 2 authorization - **BOLA** (Broken Object-Level Authorization), CWE-639 critical. - **Field-level authorization across sessions**, CWE-863 high. - **APQ allowlist bypass**, CWE-863 critical. - **Persisted-query bypass**, CWE-639 high. ### Tier 3 DoS (opt-in) - Depth attack with configurable threshold. - Alias attack. - Batching abuse. DoS detectors are off by default; enable on the scan profile if your environment is safe to probe with depth and alias amplification. ## SOAP / WSDL Full WSDL 1.1 and 2.0 parser, type-aware envelopes, plus a SOAP-context extension to the rest of the analyzer catalog: - **XXE** (XML External Entity). - **XML bomb / billion laughs**. - **XPath injection**. - **SOAPAction spoofing**. - **WS-Security bypass**. - **SOAP-context SQLi, CmdInj, SSRF** through the injection-class engine. - **WSDL information disclosure**: leaks of internal endpoints, type hierarchies, and operation lists. - **Verbose fault** detection. The WSDL parser produces a typed operation map; the analyzer generates envelopes that match the schema, not random payloads. ## WebSocket 11 detection classes: - **CSWSH** (Cross-Site WebSocket Hijacking). - **No-auth upgrade** (handshake accepted without credentials). - **Token in URL** (auth material reachable via referrer / proxy logs). - **Plaintext with cookies**. - **Subprotocol downgrade**. - **Verbose error in close frames**. - **XSS via message** (server echoes message content into a DOM sink). - **Message-level SQLi / CmdInj / SSRF / JSON / Path Traversal**. - **Compression bomb** (opt-in). - **Broadcast leak** (one client receives another client's data). - **Sensitive data leak** (PII / secrets in regular messages). Validated against the WASViking test target across all 11 classes. ## JWT advanced Wave 1 and Wave 2 attacks: - **Algorithm confusion** (`alg: none`, HS to RS swap). - **Weak secret recovery** (offline dictionary + targeted brute). - **Kid confusion** (path traversal in `kid` claim). - **JWKS proprietary-path discovery**. - **Form-login JWT auto-discovery** (detects JWT issuance on login). - **Decoded-claim visibility** (raw claim content surfaced in the finding; deliberate decision for enterprise visibility under contract). - **WAF advisory** when a target rejects probes uniformly. ## Shared discovery All four analyzers consume the shared **Target Discovery**: - Headless-browser SPA crawler for endpoints behind a JS shell. - OpenAPI 3.x and Swagger 2.x ingest. - GraphQL introspection if enabled. - WSDL parse from `?wsdl` or operator-supplied URL. - Robots, sitemap, CSRF-aware login. Discovery output feeds every analyzer, so a GraphQL endpoint discovered while crawling a REST API still gets the right tests. ## Shared authenticated session Authenticated runs establish one form-login session. The SQL Injection, XSS, JWT, GraphQL, SOAP, and WebSocket analyzers all consume the same session. One login, one session, every analyzer. ## What it does not do - It does not generate clients (no SDK generation). - It does not act as a proxy to record real traffic. - It does not implement positive-security (allow-listing) testing. For positive-security testing, run WASViking alongside a contract-first test suite. The combination is what mature API security looks like in practice. ## Set up There is no separate toggle. API coverage follows the scan configuration: pick a profile that covers APIs (`api_jwt` for REST, GraphQL, and JWT work; `soap` for SOAP services) and attach the OpenAPI or WSDL document, endpoint list, or seed URLs to the target. See [Scan profiles and templates](https://docs.wasviking.com/concepts/scan-profiles-and-templates/) and the [module activation checklist](https://docs.wasviking.com/getting-started/activate-your-modules/). --- # Out-of-Band Validation (OAST) Section: Capabilities Source: https://docs.wasviking.com/capabilities/out-of-band-validation/ Summary: How WASViking confirms blind vulnerabilities with its own out-of-band collaborator, and how to review captured interactions in the portal. No third-party data path. Some of the most serious vulnerabilities never show up in the response. A blind SSRF, a blind XXE, or a blind command injection runs on the server and leaves the page looking normal. The only proof is that the target reached out to somewhere it should not have. WASViking® **Out-of-Band Validation** captures that proof. When a payload makes the target call back to a server we control, the callback is recorded and tied to the exact scan, target, and parameter that caused it. A finding that would otherwise be a guess becomes a confirmed result with evidence behind it. ## How it works Every scan that runs a blind-class check issues a unique token from the WASViking® collaborator. The token is embedded in the payload, either in an HTTP URL or a DNS hostname, and the request goes out through the normal scan path. When a Sentinel agent is in play, the probe travels through the same tunnel as the rest of the scan, so internal targets are covered without any extra setup. If the target processes the payload, it contacts the collaborator. That interaction is written down with its protocol, source address, and timestamp, then matched back to the token. The injection-class analyzer reads the result and promotes a confirmed finding into the same Findings workflow as every other result. The collaborator is reached on a WASViking® operated address and listens for both HTTP and DNS callbacks. The unique token travels inside the callback itself, so every interaction is tied back to the exact scan, target, and parameter that produced it, and scoped to your tenant. ## Classes it confirms | Class | What the callback proves | CWE | |---|---|---| | **Blind SSRF** | The server fetched an attacker-supplied URL. | CWE-918 | | **Blind XXE** | An XML parser resolved an external entity and called out. | CWE-611 | | **Blind RFI** | A remote file include reached an external host. | CWE-98 | | **Blind SSTI** | A template engine evaluated an injected expression that triggered a request. | CWE-1336 | | **Blind command injection** | The host ran an injected command that produced a network callback. | CWE-78 | ## Where your data stays This is the part that matters for regulated teams. The collaborator is built and operated by WASViking®, inside the platform you already trust with your scans. Validation data does not pass through a third-party hosted service, and interaction records are scoped to your tenant. For organizations under LGPD, GDPR, or sector rules like BACEN, that single data path is often the difference between a control you can sign off on and one you cannot. ## Reviewing interactions in the portal Captured callbacks are listed under **Application Security → Out-of-Band (OAST)**. Each row is one interaction with the context needed to act on it: | Column | Meaning | |---|---| | **Class** | The vulnerability class the probe was testing, with the technique used. | | **Severity** | Severity assigned to the confirmed issue. | | **Target** | The scanned URL the probe was sent to. | | **Parameter** | The input that carried the payload. | | **Protocol** | Whether the callback arrived over HTTP or DNS. | | **Source IP** | The address that contacted the collaborator, usually the affected host itself. | | **When** | Timestamp of the interaction, in UTC. | The tiles at the top summarize the period at a glance: total interactions, how many tokens were probed, how many distinct vulnerability classes were seen, and the most frequent class. A blind finding always links back to the interaction that proves it, so a reviewer can trace a result from the Findings list down to the raw callback. ## Privacy and noise control The collaborator is deliberately quiet. It only records callbacks for tokens it issued, so background internet noise hitting the wildcard address is dropped rather than stored. Sensitive request headers such as authorization, cookies, and API keys are stripped before an interaction is written, so a callback record never becomes a place where a target's secrets leak. ## Set up Nothing to configure. The collaborator participates automatically whenever a scan exercises a blind-capable class. Review captured interactions under **Application Security → Out-of-Band (OAST)**, and see the [module activation checklist](https://docs.wasviking.com/getting-started/activate-your-modules/) for the rest of your plan. --- # Software Supply Chain (SBOM, SCA, KEV) Section: Capabilities Source: https://docs.wasviking.com/capabilities/software-supply-chain/ Summary: Four coordinated layers that answer OWASP A06, from cloud-side detection to a signed Evidence Bundle. WASViking® ships software supply chain coverage in four coordinated layers, not four point tools. Cloud-side detection, premise-side SBOM, a CI/CD gate, and a signed Evidence Bundle, all sharing one component inventory and one KEV rule set. ## Layer N1: Cloud-side detection Cloud-side component detection fingerprints components from outside the network using: - Path regex matchers per ecosystem. - File and asset SHA1 hashes against a known catalog. - A Wappalyzer subset for framework detection. - Meta-generator parsing. - Response header signals. - Cookie name signals. - CMS well-known paths (`/wp-admin/`, `/administrator/`, etc.). Each detection is enriched with **OSV.dev** advisories and **CISA KEV** flags. An EOL heuristic surfaces components past end-of-life. CWE mapping: CWE-937 (Vulnerable Components), CWE-1104 (Use of Unmaintained Component), CWE-1395 (Outdated Software). ## Layer N2: Premise-side SBOM `wasviking-sentinel sbom` walks build manifests on the host and produces a CycloneDX 1.5 SBOM enriched with OSV and KEV. Submitted to your tenant over the same mTLS tunnel. See [wasviking-sentinel sbom](https://docs.wasviking.com/sentinel/sentinel-sbom/) for the agent reference. ## Layer N3: CI/CD gate `wasviking-sentinel ci --sca` runs the SBOM, OSV, and KEV pass at build time. Deterministic exit codes: 70 KEV, 71 non-KEV, 72 OK. Pipelines fail before the merge. See [wasviking-sentinel ci](https://docs.wasviking.com/sentinel/sentinel-ci/) for the gate reference. ## Evidence Bundle: vendor due-diligence artifact A signed per-submission package, plus a consolidated org-wide CycloneDX, plus a brand cover PDF, plus drift / findings / audit / compliance CSVs, plus `verification.txt`. Distribution model: **token + password split** share, time-limited, revocable. Public REST scope `sca:read`. The same zero-knowledge model as Posture Shares. | Action | Endpoint | |---|---| | Create bundle | `POST /v1/sca/bundles` (`evidence.share`) | | List bundles | `GET /v1/sca/bundles` (`sca:read`) | | Revoke bundle | `POST /v1/sca/bundles/{id}/revoke` | Operators can issue, reissue, and revoke bundles from the portal under **Inventory → SBOM**. ## Component search The SBOM inventory keeps an inverted index of components across every submission. Use the search to answer questions in one query: ``` GET /api/v1/inventory/components/search?name=log4j-core&version=2.14.1 { "matches": 3, "hosts": ["checkout-api.prod", "billing-worker.stg", "legacy-portal.dr"], "kev": true, "first_seen": "2026-04-18T11:02Z" } ``` ## Continuous Supply Chain Watch Daily cross-reference of every submitted SBOM against OSV and CISA KEV. New advisories become Findings automatically. Re-alerts only fire on meaningful state changes (KEV listing, severity escalation, fix availability). See the dedicated page: [Supply Chain Intel](https://docs.wasviking.com/capabilities/supply-chain-intel/). ## Supply-chain IOC (Manual Indicators) For operator-supplied indicators outside the automated OSV + KEV feeds. Define a package and version range, dry-run to preview matches, apply to promote them to Findings. See the dedicated page: [Supply-chain IOC](https://docs.wasviking.com/capabilities/supply-chain-ioc/). ## Where it lives in the portal - **Inventory → Software Bill of Materials**: submissions and component view. - **Inventory → Supply Chain Intel**: Continuous Watch. - **Inventory → Supply-chain IOC**: operator-supplied indicators. - **Inventory → SBOM Evidence Bundles**: signed audit packages. - **Findings**: filterable by category `vulnerable_component`. - **Settings → API Keys**: for `sca:submit`, `sca:read`, `sca:ioc`, and `sca:intel:read` scopes. ## Set up The three entry points activate independently: 1. **Cloud-side detection** runs on every external scan. Nothing to configure. 2. **Premise-side SBOM** starts when you run [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) against a repository and submit the result. 3. **The CI/CD gate** starts when you add the binary to your pipeline. See [wasviking-sentinel in CI/CD](https://docs.wasviking.com/sentinel/sentinel-ci/) or the [GitHub Actions recipe](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/). Once submissions exist, issue evidence following [SBOM Evidence Bundles](https://docs.wasviking.com/getting-started/sbom-evidence-bundles/) and read the daily advisory watch under [Supply Chain Intel](https://docs.wasviking.com/capabilities/supply-chain-intel/). --- # Supply Chain Intel (Continuous Watch) Section: Capabilities Source: https://docs.wasviking.com/capabilities/supply-chain-intel/ Summary: Daily cross-reference of every submitted SBOM against OSV and CISA KEV, prioritized by EPSS exploitability, with formal risk acceptance, OpenVEX attestation, and a branded Exploitability Report. WASViking® **Supply Chain Intel** runs Continuous Watch over every SBOM your organization has submitted. SBOMs land in the inventory once and are re-evaluated every day against the latest advisories. No re-scan or re-submission needed. This is the page at **Inventory → Supply Chain Intel** in the portal. ## What it does > Continuous Watch cross-references every SBOM you have submitted > against OSV and CISA KEV every day. Each vulnerable component > becomes a Finding with its own lifecycle. Three concrete outcomes: 1. **Net new advisories** published today are matched against every live SBOM you submitted in the past. 2. **Existing advisories that change** (KEV listing, severity bump, fix released) trigger a fresh alert per the re-notify policy. 3. **Status hygiene**: a vulnerable component shipped under the same `dedupe_key` does not produce duplicate Findings; it updates the existing one. ## How it works ### Sources | Source | What it brings | |---|---| | **OSV.dev** | Open Source Vulnerabilities database. Comprehensive coverage across npm, PyPI, Go, Maven, RubyGems, Composer, NuGet, and more. GHSA advisories arrive inside the OSV feed. | | **CISA KEV** | Known Exploited Vulnerabilities catalog. Flags advisories that are confirmed in-the-wild exploitation. | Ingestion is incremental: only new and modified advisories are pulled each cycle. Both feeds are public; no customer data leaves your tenant during ingest. ### Cadence Continuous Watch runs **daily**. Each cycle: 1. Refreshes OSV deltas per ecosystem. 2. Diffs the CISA KEV catalog. 3. Matches every advisory against every active SBOM in your organization. 4. Promotes matches to Findings with category `vulnerable_component` and `source = continuous_watch`. 5. Routes alerts per the re-notify policy and the channel configuration. ### Severity WASViking computes a single severity per match in this precedence: 1. CVSS v4 if available, otherwise CVSS v3. 2. GHSA qualitative severity (`critical` / `high` / `moderate` / `low`) if there is no CVSS. 3. KEV-listed advisories default to at least `high` even without a CVSS score, because exploitation is confirmed. 4. `unknown` only if none of the above is available. ### Re-notify policy By design, Continuous Watch is signal-only. A new alert fires only on **meaningful state changes** for an advisory already in your inventory: | Trigger | What changed | |---|---| | **KEV listing** | The advisory was just added to the CISA KEV catalog. | | **Severity escalation** | Severity reclassified to `critical`. | | **Fix availability** | A fixed version was published upstream. | Repeated daily runs that find the same advisory in the same state do not re-fire. Suppressed matches stay suppressed; if an escalation matches a suppressed advisory, the audit log captures it with **"escalated while suppressed"**, but no alert is sent. ## Where it lives in the portal **Inventory → Supply Chain Intel** is the operator view. The page lists matches with filters by ecosystem, severity, and status (open, acknowledged, suppressed, fixed), a CISA KEV badge, and an EPSS exploitability column. Each row drills into: - Advisory details (CVE / GHSA / KEV linkage, references, sanitized description). - Affected component and version range. - Source SBOM and target. - Status timeline with operator transitions. - The risk-acceptance exception panel (approver, expiry, justification, and VEX status) when the match is suppressed. ## Exploitability prioritization (EPSS + KEV) A CVSS severity tells you how bad a vulnerability is in theory. It does not tell you how likely it is to be exploited. Continuous Watch adds that missing signal so your team works the advisories that matter first. Every advisory carries two exploitability signals: - **CISA KEV** flags advisories confirmed under active exploitation in the wild. - **EPSS** is the FIRST.org Exploit Prediction Scoring System, a daily probability that a CVE will be exploited within the next 30 days. It is shown as a percentage in the **EPSS** column, with the exact score and percentile on hover. The list is ordered by real-world risk: KEV-listed advisories first, then the highest EPSS, then CVSS severity. A recent medium-severity advisory that attackers are already using outranks an old critical that no one has touched. EPSS is keyed to CVEs, so an advisory with no CVE (a GHSA-only entry) shows a dash until a CVE alias appears. EPSS refreshes daily alongside the OSV and KEV ingest, so the ordering tracks the live threat landscape with no action on your side. ## Risk acceptance and exceptions Not every matched advisory is a problem you need to fix. A vulnerable package may sit in a build-only dependency, or in a code path your application never runs. For these cases the operator accepts the risk, and WASViking® records that decision the way an auditor expects to see it. The **Accept risk** action on the match detail page captures: - **Who approved it.** The acting operator is recorded as the approver. - **Why.** A written justification is mandatory. The decision is not saved without one. - **Until when.** The acceptance carries an expiry date that you set. - **Re-attestation.** Before the expiry you can re-attest, which extends the window and records a fresh justification and timestamp. An accepted risk is not permanent. A daily job reopens any exception whose expiry has passed, so a lapsed acceptance resurfaces for review instead of staying hidden. Every grant, re-attestation, and expiry is written to the audit log. This is what turns a suppression into a defensible control: a time-boxed, attested decision with a named owner, rather than a checkbox that hides a finding forever. ## VEX attestation When you accept a risk on the grounds that a component is genuinely not exploitable, you can record the reason as a formal **VEX** statement. VEX (Vulnerability Exploitability eXchange) is the CISA-backed standard for stating, per vulnerability and per component, whether a product is affected and why not. At the point of accepting the risk you choose one of the five OpenVEX `not_affected` justifications: | Justification | Use when | |---|---| | `component_not_present` | The flagged component is not actually shipped. | | `vulnerable_code_not_present` | The vulnerable code was removed or was never included. | | `vulnerable_code_not_in_execute_path` | The code exists but your application never calls it. | | `vulnerable_code_cannot_be_controlled_by_adversary` | The path is not reachable by an attacker. | | `inline_mitigations_already_exist` | A compensating control already blocks the vector. | A match with a valid justification is exported as `not_affected` in the OpenVEX document. A risk you accept without one is exported honestly as `affected`, so the attestation never overstates your posture. WASViking generates the machine-readable OpenVEX document on demand, so a customer or an auditor can ingest it into their own tooling. ## Exploitability Report (PDF) The **Export Exploitability Report** button on the Supply Chain Intel page produces a branded PDF that answers the question a regulated customer or auditor actually asks: of the known vulnerabilities in your software, which ones can be exploited and which cannot, with the justification for each. The report contains: - A cover with your organization, the generation date, and the scope filters applied. - A short explanation of what the report is, so a reader without context understands it. - An executive summary with counts of advisories in scope, affected, not affected, fixed, KEV-listed, and critical. - A statement table ordered by KEV and EPSS, with the VEX status and the justification for each item. The report respects the current status and ecosystem filters, so you can scope it to a single project or ecosystem before exporting. It is generated server-side with the same engine as the WASViking® security assessment report, and is built to drop straight into an auditor binder or a vendor due-diligence response. The same underlying determinations are available as machine-readable OpenVEX for tooling ingestion. ## Findings produced Every match promotes to a Finding under the standard workflow. | Field | Value | |---|---| | Category | `vulnerable_component` | | Source | `continuous_watch` | | CWE | Per-advisory, with `CWE-1395` as fallback (Dependency on Vulnerable Third-Party Component) | | Severity | From the precedence above | | Risk Score | Combined with asset criticality, environment, and SLA | Findings inherit the standard [Findings workflow](https://docs.wasviking.com/concepts/findings-and-risk-score/), including status transitions and webhook events. ## Alert channels Continuous Watch reuses the platform's alert routing. Configure recipients under **Settings → Notification Channels**. On each channel modal, enable the **Supply Chain Advisory** event. | Channel | Notes | |---|---| | Email | Branded transactional email with the advisory, affected components, and a deep link to the match. Routed through the canonical email pipeline (audited). | | Slack | Block format. One message per advisory, with the matched components inline. | | Microsoft Teams | Adaptive Card. Same content as Slack. | | Webhook | `{"event": "supply_chain.advisory.matched", "schema_version": 1, "data": {…}}`. Signed delivery. | See [Webhook events](https://docs.wasviking.com/api-reference/webhook-events/) for the full event catalog. ## REST API Public read access for tenant integrations and SIEM ingestion. | Method | Path | Scope | |---|---|---| | `GET` | `/api/v1/public/supply-chain/advisories/` | `sca:intel:read` | Auth scheme is `ApiKey wv_live_*`. Encrypted IDs on the wire (standard across the public API). See [Authentication](https://docs.wasviking.com/api-reference/authentication/). ## Plan availability Continuous Watch is a **Pro plan and above** feature. Free and Starter plans see the SBOM inventory but not the daily ingest or the re-notify pipeline. Per-plan limits: | Plan element | Notes | |---|---| | `continuous_watch` | Enabled on Pro and above. | | `alerts_per_day` | Capped per plan. Tracked in **Settings → Usage**. | When you are on Starter, the **Supply Chain Intel** page shows a disabled state inviting you to upgrade. ## What it does NOT do - **No outbound traffic to your repositories.** Continuous Watch operates over SBOMs already submitted; it does not pull source. - **No automatic remediation.** Matches become Findings; remediation is operator-driven. - **No customer data in the alert payload.** Alerts carry the advisory and the matched component, plus an opaque match reference. - **Does not replace the CI/CD gate.** The [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) gate runs at build time. Continuous Watch is the after-the-build safety net for advisories published after your last build. ## How this fits with the rest of the supply chain story | Layer | What it answers | |---|---| | [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) | "What is in this build, right now?" | | `wasviking-sentinel ci --sca` | "Is this build safe to ship?" | | **Supply Chain Intel (this page)** | "Did anything change overnight in something I already shipped?" | | [Supply-chain IOC](https://docs.wasviking.com/capabilities/supply-chain-ioc/) | "Is this specific package + version anywhere in my inventory?" | | Exploitability Report / OpenVEX (this page) | "Of what I shipped, which advisories can actually be exploited, and can I attest it?" | | [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/) | "Can I prove this to my auditor or my customer?" | The capability summary lives at [Software Supply Chain](https://docs.wasviking.com/capabilities/software-supply-chain/). ## Set up Nothing to configure. The watch starts as soon as your organization has submitted at least one SBOM and your plan includes the module (Pro and above). To start submitting, see [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/). --- # Supply-chain IOC (Manual Indicators) Section: Capabilities Source: https://docs.wasviking.com/capabilities/supply-chain-ioc/ Summary: Operator-supplied indicators (package + version range) cross-referenced against every submitted SBOM. Dry-run before apply, full audit history. WASViking® **Supply-chain IOC** lets an operator apply indicators of compromise (IOCs) sourced from threat intel, vendor advisories, internal triage, or anywhere outside the automated OSV + CISA KEV feeds. Each IOC is a package and version range that gets cross-referenced against every SBOM submitted by your Sentinel agents. This is the page at **Inventory → SBOM → Supply-chain IOC** in the portal. ## When to use it The automated [Supply Chain Intel](https://docs.wasviking.com/capabilities/supply-chain-intel/) covers OSV and CISA KEV. Use **Manual IOC** when: - A vendor advisory is published before OSV picks it up. - Your threat intel team has a specific package + version they want validated against your inventory before a public advisory exists. - You are responding to an incident and need to confirm exposure to a known-bad component in minutes. - You want to mark an internal package as restricted, with the same Finding lifecycle as a public advisory. ## How it works The operator opens **Supply-chain IOC**, fills the IOC form, runs a **Dry-run** to preview matches, then clicks **Apply** to promote each match to a Finding. > Cross-reference your operator-supplied supply-chain indicators > (package + version range) against every SBOM submitted by your > Sentinel agents. Matches are promoted into Findings with category > `vulnerable_component` and `source = manual_ioc`. ## The form | Field | Value | |---|---| | **Mode** | `Simple (one IOC)` for a single indicator. Bulk mode is available for high-volume operators. | | **Ecosystem** | `Any` matches across ecosystems. Pick a specific one (npm, PyPI, Go, Maven, etc.) to scope. | | **Package name** | The package identifier (`@tanstack/react-router`, `requests`, `github.com/owner/repo`). | | **Version range** | A version range expression, e.g., `>=1.131.0 <1.131.4`. Semver or ecosystem-specific syntax accepted. | | **Severity** | `Critical`, `High`, `Medium`, `Low`, or `Informational`. Drives the resulting Finding severity. | | **Advisory ref** | Optional reference identifier (`GHSA-xxxx`, `CVE-…`, vendor advisory URL). Carried on the Finding for traceability. | ## Dry-run before Apply The form has two buttons: - **Dry-run**: previews what would happen. The system runs the match against every active SBOM and shows the count of matching SBOMs, matching components, and Findings that would be opened or re-opened. No Findings are created, no alerts are sent. - **Apply**: commits the IOC. Matches become Findings under the standard workflow; alerts fire according to your [Notification Channels](https://docs.wasviking.com/integrations/slack-teams/) configuration. > Always Dry-run first when applying an IOC with a broad version range > or `Any` ecosystem. Broad IOCs can match more components than > intended. ## Findings produced | Field | Value | |---|---| | Category | `vulnerable_component` | | Source | `manual_ioc` | | Severity | From the IOC form | | Advisory ref | From the IOC form | | Audit trail | Operator, timestamp, dry-run history, application history | Findings inherit the standard [Findings workflow](https://docs.wasviking.com/concepts/findings-and-risk-score/), with status transitions and webhook events. The `source = manual_ioc` tag distinguishes them from advisories that came in through automated Continuous Watch. ## Monthly usage and quota Each plan has a per-month cap on IOC applications. The portal shows the current usage at the top of the page: ``` MODE MONTHLY USAGE Simple (one IOC) 0 of 5 used this period ``` When the quota is exhausted, the **Apply** button is disabled until the next period or until you upgrade your plan. ## Recent applications The bottom of the page lists the last 50 IOC applications. | Column | Notes | |---|---| | When | Timestamp of the Apply action. | | IOCs | Number of IOCs in the application (1 in Simple mode). | | SBOMs | Number of SBOMs the matcher ran against. | | Matches | Number of component matches found. | | Findings | Number of Findings opened or re-opened. | | Reopened | Number of previously closed Findings that came back. | The history is for operator audit. Every Apply action is also written to the customer-facing audit log under `audit_logs:read` scope. ## REST API For automation (importing IOCs from a threat intel platform, for example). | Method | Path | Scope | |---|---|---| | `POST` | `/api/v1/public/supply-chain/iocs/dry-run` | `sca:ioc` | | `POST` | `/api/v1/public/supply-chain/iocs/apply` | `sca:ioc` | | `GET` | `/api/v1/public/supply-chain/iocs/history` | `sca:ioc` | Auth scheme is `ApiKey wv_live_*`. Encrypted IDs on the wire. See [Authentication](https://docs.wasviking.com/api-reference/authentication/). ## Plan availability Manual IOC is available across all plans, with **per-plan monthly quotas**: | Plan | Default monthly IOC quota | |---|---| | Starter | Limited; see Usage. | | Pro | Higher cap. | | Enterprise | Negotiable. | Tracked in **Settings → Usage**. Quota can be increased on request. ## What it does NOT do - **No real-time feed.** This is operator-driven, one IOC at a time (or bulk via API). - **Does not replace Supply Chain Intel.** Use it alongside the automated Continuous Watch, not instead of it. - **No automatic remediation.** Matches become Findings; the operator decides what to do next. ## How this fits with the rest of the supply chain story | Layer | When to use | |---|---| | [Supply Chain Intel](https://docs.wasviking.com/capabilities/supply-chain-intel/) | The default. Daily OSV + CISA KEV cross-reference, fully automated. | | **Supply-chain IOC (this page)** | Operator-supplied indicators outside the automated feeds. | | [`wasviking-sentinel sbom`](https://docs.wasviking.com/sentinel/sentinel-sbom/) | Build-time gate. | | [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/) | Auditable artifact for compliance and customer due diligence. | The capability summary lives at [Software Supply Chain](https://docs.wasviking.com/capabilities/software-supply-chain/). --- # Secrets Detection Section: Capabilities Source: https://docs.wasviking.com/capabilities/secrets-detection/ Summary: Find leaked credentials on disk, in git history, and in public web responses. AI classifier triages matches before promotion. Verified-live secrets flagged for immediate rotation. WASViking® covers leaked credentials in two parallel places: where they hide on disk inside the customer environment, and where they accidentally surface on the public web. Coverage maps to **OWASP A07:2021** (Identification and Authentication Failures) and **CWE-798** (Use of Hard-coded Credentials). ## How a detection becomes a Finding ``` Sentinel scans source tree │ ▼ Detector match (32 patterns) │ ▼ AI classifier (real_secret / placeholder / test_fixture) │ ├─▶ suppressed (test, doc, placeholder), kept in submission │ for audit, not promoted to Findings │ ▼ Promoted to Findings workflow (category: token_exposure, CWE-798) │ ▼ Live verification (optional, 10 detectors) │ ▼ Findings flagged "VERIFIED LIVE" for triage priority ``` ## Premise-side: `wasviking-sentinel secrets` 32 detectors, 10 of them with live verification. **Raw secrets never leave the host.** Only a SHA-256 hash and a masked preview reach WASViking. See [wasviking-sentinel secrets](https://docs.wasviking.com/sentinel/sentinel-secrets/) for the full agent reference. ### Detector tiers Detectors are grouped by tier, which drives default severity and helps operators prioritize: | Tier | Detectors include | Default severity | |---|---|---| | **Cloud** | AWS access keys, AWS secret keys, AWS session tokens, Azure client secrets, GCP service account JSON, Azure connection strings. | High | | **Payments** | Stripe (live and test), SendGrid, Twilio. | High | | **VCS** | GitHub PAT (classic and fine-grained), GitHub app installation tokens, GitLab PAT, GitLab runner tokens. | High | | **Comms** | Slack bot tokens, Slack user tokens, Slack webhooks, PagerDuty integration keys. | Medium-High | | **Database** | Postgres / MySQL / MongoDB / Redis connection URIs with credentials embedded. | High | | **Keys** | RSA, EC, OpenSSH private keys. | High | | **Generic** | High-entropy strings that look like credentials but do not match a specific provider pattern. | Medium | ### Cloud-side detection The parallel leak class is secrets exposed in HTML, JSON, and JavaScript responses on the public web. WASViking applies the same pattern catalog to scan responses during a DAST run. Matches feed the same `token_exposure` Findings pipeline. ## The AI classifier Pattern matching alone produces noise. WASViking routes every match through an AI classifier before promotion. The classifier reads the match in its file or response context and returns one of: | Verdict | What happens | |---|---| | `real_secret` | Promoted to a Finding. Confidence is recorded. | | `placeholder` | Suppressed. Counted under "test / docs / placeholder". | | `test_fixture` | Suppressed. | | `uncertain` | Promoted, but flagged for operator review. | The classifier verdict is rendered alongside the match in the portal: > AI classification: `real_secret` (confidence 80%). > Detector `aws_access_key` matched a provider-shaped credential. Suppressions are kept in the submission record so an operator can audit what the classifier filtered, but they do not appear as Findings. ## Privacy guarantee - **Agent.** Raw secret held in memory only long enough to verify (if requested), then discarded. Submission payload carries hash, masked preview, and the verifier result. Nothing else. - **Cloud.** The matched substring is the only secret material the platform sees. A masked preview is rendered in the portal; the raw match is only visible to operators with the right scope, on explicit reveal. ## False-positive controls In order of how they fire: 1. **Path-based suppression** for `*.test.*`, `__tests__/`, `/docs/` and similar canonical test paths. 2. **Pattern-based suppression** for canonical placeholders (`EXAMPLE`, `PLACEHOLDER`, `XXXX`). 3. **AI classifier** as the final layer. Sees the match in context and decides `real_secret` / `placeholder` / `test_fixture` / `uncertain`. 4. **Per-detector quality score**. Low-quality detectors are gated by config and contribute lower default severity. The classifier's `confidence` is exposed in the portal so the operator can spot-check borderline cases. ## In the portal: Hard-coded Secrets The inventory lives at **Application Security → Hard-coded Secrets** in the portal sidebar. ### List view Header stats summarize the org-wide state: | Stat | Meaning | |---|---| | **Submissions** | How many secret-scan submissions are in the inventory. | | **With verified-live matches** | Submissions that contain at least one live-verified credential. | | **Open secret findings** | Currently open Findings of category `token_exposure`. | Filters: | Filter | What it does | |---|---| | **Target** | Substring search by target name. | | **Verified live only** | Toggle. Limits the list to submissions with at least one verified-live match. | Table columns: | Column | What it shows | |---|---| | Submitted | Timestamp of the submission. | | Target | The asset the scan ran against, as the agent reported it. | | Source | The submission source (`sentinel_run`, `cloud_dast`, `manual_intake`). | | Files | Number of files scanned in this submission. | | Matches | Raw detections, including suppressed. | | Verified live | Number of credentials verified live with the provider. | | Findings | Number of Findings promoted from this submission. | | Severity | Severity distribution badges (`HIGH · 4`, `MEDIUM · 2`). | ### Submission detail Click any row to open the submission detail. It carries the agent identity, the host that produced it, the runtime version, and a breakdown. Top cards: | Card | Meaning | |---|---| | Files scanned | Total files walked, plus the cumulative byte size. | | Matches | Raw detector hits (before AI suppression). | | Verified live | Hits confirmed live with the provider. | | Findings promoted | Hits that became Findings under the workflow. | | Suppressed | Hits the AI classifier ruled `placeholder` or `test_fixture`. | **Detector breakdown** table shows, per detector: | Column | Meaning | |---|---| | Detector | Detector identifier (`aws_access_key`, `sendgrid_api_key`, `generic_high_entropy`, etc.). | | Provider | Vendor (AWS, SendGrid, Azure, Generic). | | Tier | The category tag (CLOUD, PAYMENTS, GENERIC). | | Matches | Match count for this detector in this submission. | | Verified live | Live-verified subset. | | Severity | Severity distribution. | **Top Findings** lists the highest-risk matches with full context: - Severity, verification state (VERIFIED LIVE / UNVERIFIED), tier badges. - File path with line number. - Masked preview (`AKIA••••••••••••OTAU`) and the entropy score. - A code excerpt with the masked secret highlighted. - AI classifier verdict and confidence. - CWE mapping, status, seen count, deep link to the canonical Finding. ## Manual Leak Intake For security teams that already subscribe to breach feeds, the **Manual Leak Intake** page lets operators ingest known-leaked credentials and have the platform match them across the inventory. Scope: `sca:ioc` (shared with manual SBOM IOC). Same lifecycle as agent-submitted matches: AI classifier → optional verification → Findings workflow. ## Where it lives in the portal - **Application Security → Hard-coded Secrets**: submissions inventory + submission detail. - **Inventory → Manual Leak Intake**: operator-supplied indicators. - **Findings**: filter by category `token_exposure` or `credential_exposure`. - **Settings → API Keys**: `secrets:submit` scope for agents. ## What it does NOT do - **No raw secret leaves the customer environment.** Hash + masked preview only. - **No automatic rotation.** Verified-live matches are flagged so the team can rotate them; WASViking does not call provider rotation APIs. - **No public web crawling beyond the configured DAST surface.** Cloud-side detection runs against the scan responses your scan configuration already produces. ## Set up Cloud-side detection runs on every scan with no extra configuration. Premise-side detection starts when you run [`wasviking-sentinel secrets`](https://docs.wasviking.com/sentinel/sentinel-secrets/) against a repository, either ad hoc or as a [CI/CD gate](https://docs.wasviking.com/sentinel/sentinel-ci/). Results land under **Application Security → Hard-coded Secrets**. --- # Exposure Intelligence Section: Capabilities Source: https://docs.wasviking.com/capabilities/exposure-intelligence/ Summary: Monitor credential and identity exposure on domains you own. Domain ownership verified via DNS TXT. Explicit customer consent required. Sensitive fields masked by default; reveal actions audited. WASViking® **Exposure Intelligence** matches leaked credential and identity data against domains the customer owns and has explicitly authorized for monitoring. It is a legitimate security monitoring capability with strong guardrails: domain ownership is proven via a DNS TXT record, the operator confirms authorization with a consent attestation, sensitive fields are masked by default, and every reveal action is recorded for audit. > **Exposure Intelligence is intended for authorized security > monitoring only.** Findings may include sensitive or personal data > related to security incidents. Fields are masked by default and > reveal actions may be logged for audit and abuse prevention. ## How a domain enters monitoring Three steps, gated end-to-end. The flow lives at **Settings → System Settings → Add Monitored Domain**. ### Step 1: Select a target domain The dropdown lists only existing targets in the organization. You cannot monitor a domain that is not already a declared asset in your account. This prevents scope abuse: an operator cannot point Exposure Intelligence at a domain the organization does not own. ### Step 2: Customer consent The operator confirms two things, with a required checkbox: > By continuing, you confirm that you are authorized to monitor this > domain and that such monitoring is performed for legitimate > security purposes. > You acknowledge that exposure intelligence findings may include > sensitive or personal data (such as email addresses or credential > indicators), and that access to such data is controlled, may be > audited, and must be handled in accordance with applicable laws > and your organization's policies. The attestation is captured in the audit log along with the operator identity and timestamp. ### Step 3: DNS TXT verification WASViking issues a verification token. The operator adds a DNS TXT record at the root of the domain's DNS zone (`@`) with the token as the value. Once propagated, the operator clicks **Verify DNS Record**. | Field | Value | |---|---| | Name | `@` | | Type | `TXT` | | Value | The verification token issued by WASViking. | DNS propagation may take up to 48 hours depending on the provider. The token can be re-generated if needed. Without a successful DNS check, the domain stays unverified and no matches are surfaced. This is the legal proof that the customer controls the domain. ## What gets matched Per monitored domain, WASViking cross-references inbound exposure artifacts against your domain. A match becomes a row in the **Leaked Exposure Matches** table at **Cyber Risk → Exposure Intelligence**. Each row exposes: | Column | Notes | |---|---| | Email | The email address from the artifact, masked by default. Reveal is an explicit, audited action. | | Username | The username (if present in the artifact), masked. | | Password | A masked indicator of presence. Reveal is audited. | | Application URL | The service the credential was used against. | | Breach date | When the artifact was added to the upstream feed. | | Action | Opens the detail view for the match. | The header carries the page-level guarantee: > Sensitive data is masked by default. All reveal actions may be > recorded for audit and security purposes. ## Match detail Click **Details** on any row. The match detail modal shows everything the platform has on the artifact, organized in four sections. ### Exposure Summary | Field | Meaning | |---|---| | Risk level | Computed severity for this match (`High`, `Medium`, `Low`). | | Confidence | Confidence score in the match. | | Flags | Whether `Email`, `Username`, and `Password` are present in the artifact. | ### Timeline | Field | Meaning | |---|---| | First seen | When WASViking first observed this artifact in the upstream feed. | | Last seen | Most recent appearance. | | Provider leak date | The date the upstream feed assigns to the breach. | ### Exposed Data Masked representations of the email, username, and password. A **Record Hash** identifies the artifact for deduplication and correlation across feeds without exposing the raw content. ### Source & Artifact Provenance metadata: - Leak Category - Provider Type - Media (forum, paste, marketplace, etc.) - XScore (upstream confidence indicator) - Detected Family and Detected Type - Confidence - Provider Added At - Artifact Created At - Tenant Evidence Lines (how many lines in the artifact reference the monitored tenant domain) ### Recommended Actions Per match, WASViking surfaces a short action plan: - Reset affected credentials immediately and invalidate active sessions. - Investigate authentication logs for suspicious access or reuse attempts. - Check for credential reuse across other services and enforce MFA where applicable. - Continue monitoring this domain for additional exposure matches. ## Privacy and access posture - **Masking by default.** Email, username, and password fields render masked. Operators must explicitly request a reveal. - **Audited reveals.** Every reveal action writes to the customer- facing audit log with the operator identity, the record hash, and the timestamp. - **No raw credential persistence beyond what is needed for matching.** Artifacts are stored with the data the operator needs to act, plus the record hash for dedupe. - **Scope limited to verified domains.** A match against a domain whose DNS verification has expired is suppressed. ## What this is and is not **It is** a legitimate security monitoring capability for credential exposures targeting domains the customer has proven ownership of. **It is not** a generic breach feed reseller. WASViking does not expose data about domains the operator has not verified, and does not provide bulk access to upstream feeds. **It is not** a takedown service. Matches surface the artifact and provenance; takedown coordination with providers is operator work. ## Plan availability Exposure Intelligence is gated per plan and per monitored domain. | Plan element | Notes | |---|---| | Monitored domains | Per-plan cap (e.g., Plan limit: 2 on entry tiers). Tracked at **Settings → System Settings → Add Monitored Domain**. | | Continuous monitoring | Pro plan and above. | | Trial accesses | Exposure Intelligence is excluded from trial accesses by policy and activates on conversion to paid. | ## Where it lives in the portal - **Cyber Risk → Exposure Intelligence**: Leaked Exposure Matches table and the match detail modal. - **Settings → System Settings**: Add and verify monitored domains; rotate verification tokens; revoke a monitored domain. - **Audit Log**: every consent attestation, DNS verification, and field reveal action. ## Compliance posture - **LGPD / GDPR**. Monitoring is limited to domains under operator- attested ownership with documented consent. Sensitive data access is masked and audited. - **ISO 27001:2022 Annex A.5 / A.8**. Identity exposure monitoring with documented access control and audit logging. - **Principle of least privilege**. The Exposure Intelligence module inherits RBAC and is gated by the `Exposure Intelligence` per-role permission set (see [Inviting your team](https://docs.wasviking.com/getting-started/inviting-your-team/)). --- # Edge Threat Radar Section: Capabilities Source: https://docs.wasviking.com/capabilities/edge-threat-radar/ Summary: Correlate adversary traffic at your CDN edge with your own findings, with real risk amplification. The **Edge Threat Radar** ingests Cloudflare events at 5-minute cadence and correlates them with your own findings. The result: a Risk Score that is amplified when adversary traffic actually targets a weakness you have. ## What it ingests From Cloudflare: - Firewall events (blocks, challenges, bypasses). - Bot management scores and classifications. - Rate-limited paths and clients. - Geographic and ASN signals. - Browser and protocol fingerprints. WASViking® pulls the events via the Cloudflare API at a 5-minute cadence per zone. Configure the integration under **Integrations → Cloudflare**. ## Edge correlation For each open Finding on a public asset, WASViking checks: - Is adversary traffic targeting the same path or parameter? - Is the targeted client geography consistent with abuse traffic observed across other tenants? - Is the bot classification suspicious? When the signal correlates, the Finding's Risk Score is amplified. This is the difference between "we have a SQLi" and "we have a SQLi that the internet is currently probing." ## Alert rules Not every correlated event should page someone. Tune which edge events raise an alert under **Settings → System Settings → Notifications & Alerts → Edge Threat Intelligence**. Two controls: - **Minimum Risk Score.** Only events at or above this score trigger an alert. Default `60`. - **Alert on classifications.** Pick which traffic classifications raise an alert. | Classification | Meaning | |---|---| | `human_like` | Normal user behavior. | | `unknown` | Low volume, no clear intent. | | `suspicious` | Irregular, probing behavior. | | `automation` | Scripted or bot traffic. | | `aggressive_automation` | High volume, multi-path. | | `scanner` | Active scanning or exploitation. | | `authenticated_attack` | Attack from a logged-in user. | By default WASViking alerts on `suspicious`, `automation`, `aggressive_automation`, `scanner`, and `authenticated_attack`, and leaves `human_like` and `unknown` off, so the channel stays focused on high-risk activity. Adjust to taste and select **Save Notification Settings**. These rules decide when an Edge Threat Intelligence event is delivered to your [Notification Channels](https://docs.wasviking.com/integrations/notification-channels/). They do not change the Risk Score amplification described above, which always applies. ## Brand abuse on the same plane The Edge Threat Radar surface also runs the brand abuse / typosquatting analyzer (see [Exposure Intelligence](https://docs.wasviking.com/capabilities/exposure-intelligence/)). Findings of that class show up here too, with edge correlation when abusive domains are being clicked into your real surface. ## RAG over edge events The Ask WASViking AI assistant scopes a RAG (retrieval-augmented generation) index over your edge events. Ask in natural language: - "Are we being probed by any KEV-listed exploit pattern this week?" - "Which paths got hit hardest from non-US ASNs in the last 24h?" - "Are there bot patterns that match the SQLi finding on the checkout API?" The assistant answers from the event corpus only, with citations to specific events. ## Where it lives in the portal - **Edge Intelligence dashboard**: the main view. Cards, charts, correlation table. - **Findings**: `risk_score` shows the amplified value; the underlying composition (base, criticality, environment, edge boost) is visible on click. - **Ask WASViking AI**: scope a question to edge events. ## How risk amplification reads back Findings amplified by edge correlation carry an `edge_boost` field in the API response. ```json { "finding_id": "f_8ab2", "risk_score": 88, "risk_components": { "severity": 55, "asset_criticality": 12, "environment": 6, "sla_proximity": 0, "edge_boost": 15 } } ``` `edge_boost` is bounded so a single correlated event cannot single-handedly push a low-severity finding into critical. The boost is proportional to correlation strength and observed adversary volume. ## What you need to enable it - A Cloudflare zone token with `Read` permission for the relevant logs. - The Edge module enabled on your plan (Pro and above). - Per monitored domain capacity, see [Pricing](https://docs.wasviking.com/api-reference/rate-limits/#monthly-metering). For the full step-by-step (WAF, Firewall, Bot Protection, API token permissions, dynamic blocklist with customer-approval gate), see the [Cloudflare integration guide](https://docs.wasviking.com/getting-started/cloudflare/). ## What this is not This is not a WAF replacement. WASViking reads from Cloudflare; it does not produce or push rules to your CDN. The pattern is: the WAF blocks, WASViking interprets the blocking pattern against your posture. If your edge is not Cloudflare, the module is dormant for now. AWS WAF and Akamai ingest paths are on the roadmap. --- # Certificate Monitoring Section: Capabilities Source: https://docs.wasviking.com/capabilities/certificate-monitoring/ Summary: Continuous SSL/TLS certificate health monitoring with expiration alerts, protocol/cipher inspection, and auto-discovery across subdomains. WASViking® **Certificate Monitoring** runs continuous health checks on SSL/TLS certificates for assets the customer owns. Expiring certificates, weak chains, hostname mismatches, and deprecated protocols are surfaced with risk scoring, and operators get email alerts well before expiry. This is the portal section at **Certificates → Certificate Monitoring** (and **Certificates → Certificate Events** for the timeline). ## Enable per asset Certificate Monitoring is opt-in per asset, set at asset creation or edit time. Open **Assets Inventory → Add New Asset** (or edit an existing asset) and toggle **Monitor SSL**. > Enable this option to automatically monitor the SSL certificate > expiration of this domain. You will be notified by email 7 days > before expiration. If this is a root domain, discovered subdomains > will also be monitored. ### Monitoring Scope When the toggle is on, choose the scope: | Scope | What it monitors | |---|---| | **Only this domain** | The exact hostname on the asset. | | **Domain and discovered subdomains** | The root domain plus any subdomains WASViking discovers via passive enumeration (certificate transparency logs, DNS, crawl signals). | Choosing the broader scope is how a single asset can grow into a list of dozens of monitored hostnames automatically. See [Subdomain discovery](https://docs.wasviking.com/concepts/targets-and-assets/#auto-discovery-scan) for the discovery model. ## Notification cadence WASViking sends expiry alerts in advance. Defaults: **30 / 15 / 7 days** before expiration. Three independent thresholds, fully configurable. Configure under **Settings → System Settings → Notifications & Alerts → SSL Expiration – Notify in advance**. | Threshold | Default | Editable | |---|---|---| | First warning | 30 days | Yes | | Second warning | 15 days | Yes | | Final warning | 7 days | Yes | Routes to the alert destinations configured for your organization (email, Slack, Teams, webhook). ## In the portal ### SSL Certificates Overview Three KPI cards summarize the org-wide state. | Card | Meaning | |---|---| | **Expiring in N Days** | Count of certificates whose `notAfter` falls within the selected period (filter at the top of the page). | | **Active Certificates** | Count of certificates currently in a `Valid` state. | | **Total Certificates** | All certificates the monitor is tracking, including expired and revoked. | A **Filter by Period** control switches the lookahead window (`Next 7 days`, `Next 30 days`, `Next 1 Year`, etc.). ### SSL Certificate Monitoring table | Column | Meaning | |---|---| | Subdomain | Hostname being monitored. | | Last Checked | When the monitor last reached the host. | | Expiration Date | The certificate's `notAfter`. | | Days Remaining | Days until expiry, badge-color-coded by risk. | | Status | `Valid` or `Expiring Soon`. Expired certificates surface as findings. | | SANs (Count) | Subject Alternative Names on the certificate; click to expand. | | Monitoring | Per-row toggle to pause/resume monitoring without removing the asset. | | Action | **View Report** opens the certificate risk detail. | ### Certificate risk detail Click **View Report** to open the risk modal. It carries: - **Status** badge (`Valid`, `Expiring Soon`, `Expired`). - **Risk** badge (`Low`, `Medium`, `High`, `Critical`). - Expiration warning prose when expiry is imminent. **Certificate overview table:** | Field | Notes | |---|---| | Subdomain | Hostname. | | Valid until | The `notAfter` of the certificate. | | Days until expiry | Convenience field for triage. | | Protocol | Negotiated TLS protocol (`TLSv1.2`, `TLSv1.3`). | | Cipher suite | Negotiated cipher (e.g., `TLS_AES_256_GCM_SHA384 (256 bits)`). | | Issuer | The certificate's `Issuer CN` (e.g., `Let's Encrypt`). | | SANs | Subject Alternative Names list. | ## Certificate Events A second portal page, **Certificates → Certificate Events**, lists transitions over time: - Certificate first observed. - Certificate renewed (new fingerprint). - Issuer changed. - Protocol downgraded. - Cipher weakened. - Hostname mismatch detected. - Certificate expired. Events are append-only and feed both the audit log and webhooks. ## Risk amplification on the Asset In **Assets Inventory**, each asset row shows an **SSL Monitoring** column (`Enabled` / `Disabled`) and a **Risk Exposure** column with per-severity badges. An asset with monitoring enabled and an expiring certificate is amplified in the Risk Exposure score. ## What turns into a Finding Certificate Monitoring promotes a Finding when: - The certificate is **expired**. - The certificate is **within the final warning threshold** (7 days default) without a renewal observed. - The certificate uses a **weak cipher** or **deprecated protocol** (TLSv1.0, TLSv1.1). - The certificate has a **hostname mismatch**. - The certificate chain is **broken**. Findings inherit the standard [Findings workflow](https://docs.wasviking.com/concepts/findings-and-risk-score/), with status transitions and webhook events. ## Plan availability Certificate Monitoring is available on all plans, with per-plan caps on the number of monitored certificates. | Plan | Monitored certificates | |---|---| | Starter | Limited cap. | | Pro | Higher cap. | | Enterprise | Negotiable. | Tracked under **Settings → Usage**. ## What it does NOT do - **No certificate issuance or renewal.** WASViking monitors and alerts; renewal is the operator's responsibility (Let's Encrypt automation, ACME, vendor portal, etc.). - **No private key inspection.** The monitor reads the public certificate via standard TLS handshake. - **No CA-side telemetry.** WASViking does not query Certificate Transparency logs to enumerate beyond what is needed for subdomain discovery on monitored root domains. ## Where it lives in the portal - **Certificates → Certificate Monitoring**: dashboard, table, risk detail. - **Certificates → Certificate Events**: transitions timeline. - **Assets Inventory**: per-asset Monitor SSL toggle and scope selector. - **Settings → System Settings → Notifications & Alerts**: SSL expiry advance thresholds. - **Findings**: filter by category `tls_misconfiguration` or `certificate_expired`. --- # Sensitive Port Monitoring Section: Capabilities Source: https://docs.wasviking.com/capabilities/sensitive-port-monitoring/ Summary: Continuous monitoring of risky and customer-specified network ports on your public assets, with findings in the report and alerts when a port that should not be exposed appears on the internet. WASViking® **Sensitive Port Monitoring** watches the network ports exposed on your public assets and raises a finding when a port that should not be reachable from the internet is open. It covers a built-in list of high-risk ports and any additional ports you specify. Ports get exposed for ordinary reasons: an internal team opens a database port for a quick test, a firewall rule is loosened during an incident, a new host ships with a default service enabled. These changes often outlive their purpose and are never reviewed. Sensitive Port Monitoring is the control that catches them. ## Two ways a port is checked **During a scan.** When WASViking scans an asset, it inspects the ports it observes and turns risky open ports into findings in the report, with a severity bump for ports that are sensitive by nature. This is part of the normal scan output. **Continuously, outside the scan.** WASViking also checks the sensitive ports for your assets on a recurring schedule, independent of when you run a scan. If a sensitive port that was closed becomes open between scans, you are alerted without having to wait for the next scan to run. This is what turns port hygiene from a point-in-time check into ongoing monitoring. ## What counts as a sensitive port WASViking ships with a baseline of ports that should generally not be exposed to the public internet: | Port | Service | |---|---| | 22, 2222 | SSH | | 3389 | RDP | | 23 | Telnet | | 3306 | MySQL | | 5432 | PostgreSQL | | 1433 | MSSQL | | 27017 | MongoDB | | 6379 | Redis | | 9200 | Elasticsearch | | 11211 | Memcached | | 445 | SMB | On top of this baseline you can add your own ports. Use this for services specific to your environment that you want held to the same standard, for example an admin panel, a message broker, or an internal API that must never be reachable from outside. ## Configure the ports you care about Add custom ports under **Settings → System Settings → Notifications & Alerts → Sensitive Port – Monitored Ports**. Enter port numbers separated by commas, for example `21,25,1433`. > List additional ports to be monitored, separated by commas. Your custom ports are monitored in addition to the built-in baseline, not instead of it. Leaving the field empty still monitors the baseline. ## Alerting When a sensitive port is found open, WASViking routes a **Sensitive Port** alert to the channels you have configured. Set up delivery under **Alerts → Notification Channels** (email, Slack, Microsoft Teams, or an API webhook). See [Notification Channels](https://docs.wasviking.com/integrations/notification-channels/) for the channel and event model. The alert tells you which host and port are exposed so the team can decide whether the exposure is intended and, if not, close it. ## What turns into a Finding An open sensitive port is promoted to a Finding in the category `exposed_port`, titled like `Sensitive port exposed: SSH (22/tcp)`. Findings created from both the in-scan check and the continuous monitor are reconciled to the same host and port, so you do not get duplicates for the same exposure. These findings follow the standard [Findings workflow](https://docs.wasviking.com/concepts/findings-and-risk-score/): status transitions, audit log, Risk Score, and webhook events. A sensitive port also raises the Risk Exposure score on the asset in **Assets Inventory**. ## What it does not do - **It does not close ports for you.** WASViking detects and alerts. Closing the port is an action for your team or your firewall. - **It is not a full network port scan.** The monitor focuses on the sensitive and customer-specified ports, not an exhaustive 0-65535 sweep. For broader infrastructure coverage, use the `network` scan profile. See [Scan profiles](https://docs.wasviking.com/concepts/scan-profiles-and-templates/). ## Where it lives in the portal - **Settings → System Settings → Notifications & Alerts**: the Sensitive Port monitored-ports list. - **Alerts → Notification Channels**: where Sensitive Port alerts are delivered. - **Findings**: filter by category `exposed_port`. - **Scan reports**: in-scan port findings appear with the rest of the scan output. --- # Scan Schedules Section: Capabilities Source: https://docs.wasviking.com/capabilities/scan-schedules/ Summary: Predictable, operator-defined recurring scans with locked-template compliance windows, per-schedule preferences, and explicit enable/disable state. WASViking® **Scan Schedules** is the operator-controlled recurring scan layer. The operator picks a target, a scan template (or inline configuration), a frequency, and the platform runs the scan on cadence with the chosen preferences. Each schedule is independent, individually enable/disable-able, and can be pinned to a specific template version for compliance windows. This is the page at **Scans → Scan Schedules** in the portal. ## When to use Scan Schedules Schedules are the right tool when you want a **predictable cadence**: - Compliance windows that require a scan every 30 days against a specific configuration. - Weekly maintenance scans against staging. - Monthly attestation runs against a documented baseline. - One-time runs scheduled in advance for a known window (post-release, post-migration, after a change freeze). For ad-hoc investigations, use a [manual scan](https://docs.wasviking.com/getting-started/first-scan/). For dynamic, AI-driven scan selection across a large portfolio, see [AI Scan Planner](https://docs.wasviking.com/capabilities/ai-scan-planner/). ## The list view | Column | Meaning | |---|---| | Domain Name | The target the schedule applies to. | | Scan Type | Single Domain Scan or other type. | | Execution Path | `Direct (External)` for cloud egress, or the name of a Sentinel agent for internal targets. | | Status | A per-row toggle to enable or disable the schedule without deleting it. | | Next Scheduled Run | When the schedule will fire next, or `Start time not set` for incomplete schedules. | | Frequency | The cadence (One-Time, Daily, Weekly, Monthly, etc.). | | Manage | Edit pencil. | Status is the master switch. A disabled schedule preserves its configuration; toggle it back on to resume. ## Frequency Two top-level options: | Frequency | Use for | |---|---| | **One-Time Scan** | A scan scheduled to run once at a specific future moment. After running, the schedule completes. | | **Recurring Scan** | A scan that repeats. Configurable cadence (daily, weekly, monthly, custom) with start time. | The frequency drives the next-run computation and is shown in the list. ## Edit a schedule Opening **Edit Schedule** shows two top-level sections, the **Scan Template** block and the **Edit Scan Schedule** block, followed by the **Preferences** sub-tabs and the **Scan Profile** card. ### Scan Template Pick which template the schedule uses: | Field | Notes | |---|---| | Use a saved configuration | Dropdown of templates from [Scan Templates](https://docs.wasviking.com/concepts/scan-profiles-and-templates/). Default: `Full Coverage (System) - Default`. | | Lock this schedule to the template's current version | Toggle. See lock behavior below. | > **Lock behavior.** When unlocked, this schedule follows the latest > version of the template on every run. When locked, it pins to the > version snapshotted at save time. Use lock when you need > **compliance windows** that prove the scan ran against an exact, > version-stamped configuration even if the template itself is > edited later. ### Edit Scan Schedule | Field | Notes | |---|---| | Domain | Read-only. Set at schedule creation; not editable later. | | Enable Schedule | Master toggle. Off = paused but preserved. | | Frequency | One-Time Scan or Recurring Scan, with the relevant cadence options. | ### Preferences (sub-tabs) The Preferences block has four tabs that mirror the New Scan form. Any preference set here applies to every dispatch of this schedule, unless the schedule is locked to a template version (in which case the template's settings win and Preferences is read-only). | Tab | What it controls | |---|---| | **Scan Method** | Execution Path: `Direct (External)` for cloud egress, or a specific Sentinel agent for internal targets. | | **AI & Compliance Settings** | AI Recommendation on/off, primary compliance framework for the report. | | **Authentication** | None, Form Login, Bearer, Cookie, Custom header. | | **Crawl** | Custom User-Agent, excluded paths, depth controls. | ### Scan Profile Pick the depth of the scan (Full Coverage, Web Application, API and JWT, SOAP and WSDL, Network and TLS, Custom, PCI DSS, LGPD, GDPR). See [Scan profiles and templates](https://docs.wasviking.com/concepts/scan-profiles-and-templates/) for the full profile catalog. ## Compliance windows The combination of **Lock this schedule to the template's current version** plus **fixed frequency** is the WASViking pattern for compliance windows: 1. Create the template that satisfies the audit's scope. 2. Create a schedule that uses that template at the required cadence (every 30 days for PCI DSS, for example). 3. Lock the schedule to the template version. 4. The schedule will continue to run that exact configuration even if the template is later edited. The audit binder can cite the pinned version. Edits to the template propagate to other schedules and to manual scans, but the locked compliance schedule stays on the snapshotted version until the operator explicitly unlocks and updates it. ## Execution Path | Value | Notes | |---|---| | **Direct (External)** | Scans the target via the WASViking cloud egress. Default for public-facing targets. | | **Via Sentinel agent** | Scans the target through a configured agent's mTLS tunnel. Required for internal targets and recommended when the agent is geographically closer to the target. See [Sentinel internal scanning](https://docs.wasviking.com/sentinel/internal-scanning/). | The Execution Path is per-schedule. A single target can have one schedule running via the cloud and another running via an internal agent (rare, but supported). ## How it fits with the other dispatch sources WASViking has three independent dispatch sources: | Source | Trigger | Best for | |---|---|---| | **Manual scan** | Operator clicks Scan. | Investigations, ad-hoc validation, demos. | | **Scan Schedules (this page)** | Operator-defined cadence. | Predictable cadence, compliance windows, post-change validation. | | **[AI Scan Planner](https://docs.wasviking.com/capabilities/ai-scan-planner/)** | Daily priority review across the portfolio. | Continuous coverage of a large portfolio without manual triage. | A target can be touched by all three in the same week. The platform applies a **24-hour cooldown** between any two dispatches of the same target to avoid redundant work; the AI Scan Planner will not re-scan a target that a schedule already scanned within the cooldown window. ## What it does NOT do - **Schedules do not auto-discover.** A schedule runs against the target it was configured for. New subdomains discovered later do not automatically become new schedules. [Auto-discovery scan](https://docs.wasviking.com/concepts/targets-and-assets/#auto-discovery-scan) is a separate behavior. - **Schedules do not adapt.** The cadence is what you set, not what the platform thinks is optimal. For adaptive cadence, use AI Scan Planner. - **Schedules do not bypass quota.** Each scan dispatched from a schedule consumes scan capacity per your plan. Over-quota schedules surface a warning at save time. - **Schedules do not retain custom credentials.** Authentication credentials are encrypted at rest, scoped to the schedule, and not reusable outside it. ## Audit and contestability Every schedule lifecycle event is recorded in the customer-facing audit log: - Schedule created, edited, paused, resumed, or deleted. - Lock toggled on or off. - Frequency changed. - Each dispatched scan, with the source set to `scan_schedule` and the schedule identifier. Filter the audit log by `scan_schedule_*` actions for a clean operational history. ## Where it lives in the portal - **Scans → Scan Schedules**: list view and edit view. - **Scans → Scan Templates**: the saved configurations a schedule picks from. - **Audit Log**: filterable history of schedule changes and dispatches. - **Settings → Usage**: scans-per-month metering across all dispatch sources. --- # AI Scan Planner Section: Capabilities Source: https://docs.wasviking.com/capabilities/ai-scan-planner/ Summary: Daily portfolio review that picks which targets to scan today and explains why. Contestable by the operator at every step. Complements Scan Schedules and manual scans. The **AI Scan Planner** is a daily decision layer that watches every target in your portfolio, weighs risk against budget, and proposes a scan plan for the day with a written rationale per target. The operator can accept the plan, force a scan, veto a target for the next cycle, or pause the planner with Vacation Mode. It does not replace manual scans or [Scan Schedules](https://docs.wasviking.com/capabilities/scan-schedules/). It runs alongside them as a third source of dispatches, driven by signals the operator does not have time to monitor every day. > The page at **Scans → AI Scan Planner** opens with the daily > rationale: > > *Daily portfolio review picks which targets to scan today and > explains why. Every decision is contestable: force a scan, veto > next cycle, accept a proposal, or pause the planner with vacation > mode.* ## How a decision is made Every cycle (default daily, 02:00 in the org's time zone), the planner walks every target in your portfolio and computes a **priority score** between 0.000 and 1.000. The score combines four weighted inputs per target: | Input | What it measures | |---|---| | **Risk** | Aggregated severity and Risk Score of open Findings on the target. | | **Decay** | How long since the last successful scan. The longer the silence, the higher the pull. | | **Evidence** | Signals that something changed: Environment Profile drift, certificate change, new subdomain, new technology detected. | | **Cost** | The estimated scan budget cost relative to the daily quota. Used to skip low-value runs when the quota is tight. | The weights are tuned per organization. Operator feedback (force, veto, apply, ignore) nudges the weights in the direction of what the team has historically accepted. The team's signal becomes part of the model. ## Action types Per target, the planner emits one of these actions: | Action | What it means | |---|---| | **Full scan** | Run a full-coverage scan today. Used when the score exceeds the threshold. | | **Pulse** | A maintenance-class scan within the daily quota. Lightweight; keeps the inventory fresh without consuming the full budget. | | **Skip** | Score is below threshold or quota is exhausted. The target is left alone today; the rationale explains why. | | **Propose profile** | The detected environment suggests a different scan profile than the one currently used (e.g., switch from `web_app` to `api_jwt` because the stack now exposes GraphQL). Operator clicks **Apply** or **Ignore**. | | **Propose add** | A newly discovered surface element (subdomain, route, port) is worth adding to the existing scan scope. Operator clicks **Add to scope** or **Ignore**. | The rationale column is always plain English. Examples: - *Maintenance pulse within daily quota.* - *Below threshold; skipped to preserve budget.* - *Environment profile changed; full scan recommended.* - *GraphQL detected; suggest `api_jwt` profile.* ## Daily quota The planner is budget-aware. Each plan tier ships a daily quota that caps how many autonomous dispatches the planner runs in 24 hours: | Plan | Daily quota | |---|---| | Starter | 1 | | Pro | 3 | | Enterprise | 10 | | Platinum | Custom | Above the quota, the planner emits Skip rows so the operator can see what was *not* scanned and why. Force-scanning a Skip row is one click; the operator decision overrides the quota for that target. ## The portal page **Scans → AI Scan Planner**. ### Header | Element | What it does | |---|---| | **Enabled / Disabled** toggle | Master switch. When off, no autonomous dispatch happens. Schedules and manual scans continue to work. | | **Run review now** | Forces a planner cycle on demand. Useful after a config change. | | **Vacation mode** | Pauses autonomous dispatch for a number of days (0 to 60). | | Pills | Quick status: `Vacation: off | active until `, `Daily quota: N`, `Last review: `. | ### Decisions table | Column | Meaning | |---|---| | Target | The target the decision applies to. | | Action | One of the five action types above. | | Rationale | Plain-English explanation of why this action was chosen. | | Priority | Score 0.000 to 1.000. Threshold is org-dependent. | | Status | `PENDING` (decision recorded, not yet dispatched), `OK` (dispatched), `VETOED`, `APPLIED`. | | Decided | When the planner made the call. | | Actions | Operator buttons, contextual to the action: see below. | ### Contextual buttons | Action | Buttons available | |---|---| | Full scan / Pulse / Skip | **Force scan now** (immediate dispatch) / **Veto next cycle** (30-day block on this target for this action). | | Propose profile | **Apply** (set the target's preferred scan template) / **Ignore** (dismiss for the cooldown window). | | Propose add | **Add to scope** / **Ignore**. | Every button click is captured in the customer-facing **Audit Log** page in the portal, with the operator identity and the original decision. ## Per-target template suggestion The planner reads the per-host Environment Profile produced by every scan and proposes a more appropriate template when the detected stack disagrees with the current configuration. | Signal observed | Suggested template | |---|---| | GraphQL endpoint detected | `api_jwt` | | OpenAPI / Swagger document | `api_jwt` | | JWT issuance observed on login | `api_jwt` | | SOAP service detected | `soap` | | WebSocket upgrade observed | `web_app` | | Server-rendered web stack with forms | `web_app` | | Inconclusive | No proposal | The suggestion is silent for 30 days per (target, template). After 30 days, the planner will propose again if the signal still holds. ### Auto-apply safety net If the operator does not click **Apply** or **Ignore** on a profile proposal by the time the planner dispatches the next scan for that target, the planner applies the suggested template itself. The target's `preferred_scan_template` is updated; an audit row is written with `actor = ai_scan_planner` and a `auto-applied` note. This is by design. The cost of letting the planner adapt is lower than the cost of running the wrong profile against a stack that changed. ## Drift radar Outside the daily quota, the planner monitors **environment profile drift**. When the per-host fingerprint changes (new technology, auth surface change, protocol change), and the change is observed outside business hours, the planner emits a full-scan dispatch flagged as `drift_dispatch` to capture evidence before the change propagates further. Drift dispatches do not count against the daily quota. They are audited under `ai_scan_planner_drift_dispatch`. ## Vacation mode Pauses the planner for up to 60 days. Useful for: - Maintenance windows where the team does not want autonomous scans. - Holidays or quiet periods where alerts should stay silent. - Investigations where the team wants to control the scan timeline manually. Vacation mode does not pause **Scan Schedules** or manual scans; it only pauses the autonomous decision layer. Drift dispatches respect vacation mode. ## How it fits with Scan Schedules and manual scans The three dispatch sources are independent and complementary: | Source | Trigger | When to use | |---|---|---| | **Manual scan** | Operator clicks Scan. | Investigations, ad-hoc validation, demos. | | **[Scan Schedules](https://docs.wasviking.com/capabilities/scan-schedules/)** | Operator-defined cron. | Predictable cadence, compliance windows. | | **AI Scan Planner** | Daily priority review. | Continuous coverage of a large portfolio without manual triage. | A target can be touched by all three in the same week. The planner respects a 24-hour cooldown between any two dispatches of the same target, so a manual scan in the morning will not be redundantly re-scanned by the planner at 02:00. ## Plan availability | Plan | Notes | |---|---| | Starter | Daily quota 1. Lower-tier autonomous coverage. | | Pro | Daily quota 3. Drift radar enabled. | | Enterprise | Daily quota 10. Drift radar enabled. Custom weight tuning available. | | Platinum | Custom quota and weights. | The current quota is shown as a pill on the planner page and can be raised by request. ## Audit and contestability Every planner action is auditable: - Each decision row is append-only with full rationale. - Every operator override (Force, Veto, Apply, Ignore, Vacation toggle, Master toggle) writes an entry in the customer-facing audit log with `actor = human`. - Every autonomous action (dispatch, drift dispatch, auto-apply) writes an entry with `actor = ai_scan_planner`. - Vetoes carry a 30-day TTL and are revocable at any time. The audit log is queryable from **Audit Log** in the portal sidebar, filterable by `ai_scan_planner_*` action codes. ## What it does NOT do - **No black-box decisions.** Every action has a rationale and is contestable in one click. - **No surprise scans.** Cooldown prevents re-runs within 24 hours; quota prevents budget blowout; vacation mode pauses the planner entirely. - **No takeover of manual scans or schedules.** The planner is a third dispatch source, not a replacement. - **No external data sharing.** All signals are computed from your own targets, findings, and Environment Profiles. The planner does not call out to third-party intelligence to score a target. ## Where it lives in the portal - **Scans → AI Scan Planner**: the decisions page. - **Scans → Scan Schedules**: cron-driven scans (independent). - **Audit Log**: filterable history of every planner action. - **Settings → Usage → AI Scan Planner tab**: daily quota usage, monthly AI-driven scan count, vacation mode state, pending proposals, active vetoes. --- # WASViking AI Guardian Section: Capabilities Source: https://docs.wasviking.com/capabilities/ai-guardian/ Summary: AI exposure monitoring on employee endpoints. Detects sensitive data sent to public AI tools, enforces organization policy at paste time, and reports activity per user, device, and AI application. WASViking® AI Guardian is an AI exposure intelligence layer for the endpoint. It observes how employees interact with public AI tools (ChatGPT, Claude, Gemini, Copilot, Perplexity, and others), classifies the content they are about to send, and enforces the policies your security team configures in the portal. It is not a DLP proxy, a man in the middle, an SSL inspector, or a keylogger. The browser sensor reads the composer text only at submit or paste time and the agent processes it in memory. Coverage maps to insider risk, data minimization (GDPR, LGPD), PCI DSS masking, and HIPAA confidentiality of PHI. ## What it detects The agent classifies content with a deterministic engine that combines high precision detectors with a context layer. The catalog covers Brazilian and global identifiers, protected health information, financial data, special category data under LGPD and GDPR, cloud credentials, names and addresses, and structural signals. ### Personal data (PII) - **Brazilian identifiers**: CPF, CNPJ, RG (São Paulo state checksum unlabeled, every state when labeled), CNH (Denatran dual mod-11 check digits), PIS, PASEP, NIS, NIT, passport, and PIX keys in all four shapes (CPF, CNPJ, email, phone, and the random UUID form). - **Global identifiers**: email, phone, SSN, credit card validated by Luhn, and full names introduced by an honorific (`Sr.`, `Dr.`, `Mr.`, `Mrs.`, `Prof.`, including the Portuguese connectives `da`, `de`, `dos`) or a labeled field (`Name:`, `Patient:`, `Cliente:`). - **Postal addresses**: Brazilian forms anchored by `Rua`, `Av.`, `Travessa`, `Praça`, `Rodovia`, and English forms anchored by `Street`, `Avenue`, `Boulevard`, `Highway`. - **Dates of birth** in labeled contexts (`DOB:`, `Date of birth:`, `Nascimento:`, `Data de nascimento:`). Evidence is masked to year only (`••/••/1985`) so the age stays recoverable while the exact birthday does not leave the endpoint. ### Re-identification risk (quasi-identifier cluster) Even when no single field is PII, three or more quasi-identifiers in the same prompt re-identify a person with high probability (Sweeney 2000). The agent fires a `quasi_identifier_cluster` classification when three or more of the following co-occur in a single prompt: gender, age, US ZIP, Brazilian CEP, city or state, marital status, occupation, employer, nationality, or an inline date of birth. The evidence row shows the matched categories rather than the raw values, so the analyst sees why the cluster fired without any single quasi-identifier leaving the host. ### Protected health information (PHI) Clinical vocabulary in English and Portuguese, ICD-10 codes, common medication and laboratory test names (RxNorm and LOINC subsets), Brazilian professional registries (CRM, CRO, COREN), and health insurance carriers (Unimed, Amil, Bradesco Saúde, Hapvida, and others). These are treated as health topics on their own and only escalate to a real PHI finding when a patient identifier (MRN, CNS) is present, or when a personal identifier sits in the same prompt. A general question like "explain hypertension" stays informational; the same text with a CPF becomes a PHI incident. ### Special category data (LGPD Art. 5 II and GDPR Art. 9) Racial origin, religion, political opinion, union membership, sexual orientation, biometric data, and genetic data. Each category is detected with a bilingual vocabulary and treated the same way as PHI: a topic on its own, a real finding only when a personal identifier corroborates the subject. So "explain Catholic doctrine" stays a topic; the same content next to a CPF becomes an Art. 5 II or Art. 9 incident. ### Financial data Credit card validated by Luhn, IBAN validated by the mod-97 check across a country-length table of 80 IBAN-issuing countries, SWIFT and BIC codes in labeled context, and Brazilian agency-and-account pairs. ### Credentials and cloud tokens AWS access keys, JWTs, private keys (RSA, EC, OpenSSH, DSA, PGP), Anthropic API keys (`sk-ant-`), OpenAI project-scoped keys (`sk-proj-`), Google API keys (`AIza...`), Stripe live keys (`sk_live_`, `rk_live_`, `pk_live_`; the test variants are deliberately ignored), Slack webhooks, GitHub fine-grained PATs and OAuth and refresh and server-to-server tokens, Azure SAS tokens and storage `AccountKey=`, GCP service account JSON, SendGrid, Mailgun, Twilio, database connection strings with embedded credentials (entropy-gated to skip documentation snippets), and kubeconfig YAML. Provider tokens that lack a dedicated detector are still caught by the generic `key = value` assignment rule with an entropy gate that rejects placeholders like `change-me`, `process.env.X`, or `os.getenv(...)`. ### Source code, SQL objects, and internal context Structural signals (fences, imports, function definitions, `SELECT`, `INSERT`, `CREATE TABLE`, `mysqldump`, `pg_dump`), RFC1918 ranges, `.internal`, `.corp`, `.local`, `.lan`, plus your own organization names and internal domains as configured in policy. ### Production and confidentiality markers Mentions of `production`, `prod`, `confidential`, `NDA`, `proprietary` amplify the risk of any adjacent finding. The classifier emits a co-occurrence boost when an amplifier sits next to real sensitive data, so "improve this PRODUCTION code for banco XPTO" carries more risk than "improve this code". ### Bypass resistance Two adversarial shapes are defeated without configuration: - **Unicode look-alike substitution**. Fullwidth digits and homograph characters are normalized (NFKC) before the classifier sees the prompt, so `CPF 111.444.777-35` and `password=раssword123` are treated as `CPF 111.444.777-35` and `password=password123`. - **Encoded envelopes**. Content wrapped in base64, URL-encoding, JSON escape sequences, or hex is decoded in memory (up to three passes deep) and the classifier runs again on each decoded chunk. Findings recovered this way carry a `via base64` or `via base64>url` badge in the portal. ### Evidence masking Every classification carries a masked evidence sample so a reviewer can audit a true positive without the raw value ever leaving the endpoint. Examples: | Classification | Evidence shape | |---|---| | CPF, CNPJ, RG, CNH, PIS, phone, SSN | `•••.•••.•••-35` | | Credit card | `••••-••••-••••-1234` | | IBAN | `DE89••••••••3000` | | SWIFT, BIC | `DEUT•••FXXX` | | Email | `j•••@•••.com` | | AWS, Stripe, Anthropic, GitHub keys | `AKIA••••••MPLE` | | Full name | `J••• da S••••` | | Date of birth | `••/••/1985` | | Source code, SQL, internal terms | literal token, truncated | ## What it does not capture The privacy contract is the product. The agent never persists raw prompts, raw file contents, or screen captures. Persistent storage holds only metadata: a short SHA-256 fingerprint, character and token counts, classification labels, the masked evidence samples above, and a pseudonymized user reference. The cleartext OS user name and primary egress IPv4 are recorded for organization attribution, an intentional posture choice for insider risk investigations. Uploads (DOCX, XLSX, ZIP, PDF, plain text) are inspected in memory and the bytes are discarded. Only metadata, the SHA-256 of the file, and the resulting classifications are stored. ## How the organization API key is stored on the endpoint The organization API key is held in the operating system's native encrypted credential store: macOS Keychain, Windows Credential Manager, or Linux Secret Service. An additional application-layer envelope binds the credential to the originating host, so a keystore entry copied to another machine decrypts to garbage. Nothing sensitive is written to a plain file on the endpoint. What this gives you: - **Backup-safe.** Time Machine, iCloud, and corporate backup tools do not sweep the credential store, so a routine restore does not resurrect the key on a different machine. - **Support-safe.** When an operator pastes the agent's configuration for a support ticket, no credential goes with it. - **Compliance-ready.** Auditors covering SOC 2 CC6.6, ISO 27001 A.10.1, and HIPAA Technical Safeguards ask whether host credentials are encrypted at rest. The answer is yes, by the OS-native credential store with an application-layer envelope on top. Honest scope: the keystore does not defeat an attacker who already has the user's interactive session and can read the running agent's process memory, or who can redirect the API endpoint to capture the outbound Authorization header. Those residual insider-threat risks are addressed by complementary controls: short-lived credentials, anomaly detection at the API side, and revocation workflows. They are out of scope of this page. Operators verify the on-host credential state with `wasviking-sentinel ai-browser key-status`. The command reports the storage source and never prints the key. ## Regulatory mapping Every classification carries a stable list of compliance tags that map the finding to the regulatory frameworks it implicates. The mapping is deterministic at the detector level, so the same prompt produces the same tag set on every host. | Framework | Tags emitted | |---|---| | Brazil LGPD | `LGPD.Art.5.I` (personal data), `LGPD.Art.5.II` (special categories) | | EU GDPR | `GDPR.Art.4.1` (personal data), `GDPR.Art.9` (special categories) | | US HIPAA | `HIPAA.PHI`, `HIPAA.Privacy` | | PCI DSS | `PCI.DSS`, `PCI.CHD` | | SOC 2 | `SOC2.CC6.1` (logical access), `SOC2.CC6.6` (encryption controls) | | ISO 27001 | `ISO27001.A.5.13` (data classification), `ISO27001.A.8.10` (information deletion) | | NIST 800-53 | `NIST.800-53.SC-28`, `NIST.800-53.SC-12` | | NIST AI RMF | `NIST.AI.RMF.MAP-4.1`, `NIST.AI.RMF.GOVERN-3` | | US states | `CPRA` | | Canada | `PIPEDA` | The dashboard surfaces these as colored chips. The event row in the table carries a deduplicated rollup of every framework the event implicates; the detail drawer shows the per-classification chip set so you can see whether a single finding maps to PCI DSS, HIPAA, GDPR, LGPD, or several of them at the same time. ## Advanced detection Three configuration knobs let you tighten or extend the classifier per tenant. All three live in the agent policy file. ### Per-tenant custom detectors Add proprietary patterns the built-in detectors do not cover, for example an internal employee ID, a project code, or a customer reference. Each entry carries its own label, category, regex (RE2 syntax), confidence, mask, and compliance tags. The agent refuses to start if a pattern fails to compile, so a typo never silently disarms detection. Up to 64 custom detectors per tenant. ### Confidence calibration per label A per-label multiplier in the `[0, 1]` range damps a noisy detector for your tenant without disabling it. The multiplier only lowers confidence, never raises it. The same engine surfaces both regex and custom-detector findings, so calibration covers both. ### LLM-assisted classification (opt in) For prompts where the deterministic engine returns low confidence or a high score and you want a second opinion, the agent can call an LLM provider directly with your API key to add semantic classifications. The customer-direct posture is intentional: the prompt content goes from the endpoint to the LLM provider over public TLS and never transits WASViking infrastructure. Results are cached on disk by SHA-256 of the prompt, so identical prompts are never re-classified. A daily budget guard stops calls when the cap is reached. LLM findings appear in the dashboard with the `llm_` prefix so they are distinguishable from regex hits. This layer is most useful for paraphrased PII, contract text, M&A context, and proprietary IP that pattern matching cannot describe. The configuration shape for all three knobs is documented in [Set up WASViking AI Guardian](https://docs.wasviking.com/getting-started/ai-guardian/). ## Policy enforcement at the endpoint You define rules under **Portal > AI Guardian Policies**. Each rule combines: - **Event:** prompt submit, paste, file upload, or any. - **Conditions (ANDed):** website category (generative AI, unsanctioned, sanctioned), data class (PII, PHI, secret, source code, internal), minimum severity, AI platform, browser. - **Targets:** all users, specific OS user names (include and exclude), or **directory groups** synced from your identity provider. Group targets are resolved to confirmed users when the policy is served, so a rule can read "Finance" instead of a list of logins (see [Identity and directory](#identity-and-directory)). - **Action:** Block, Warn, Audit, or Allow. Stronger actions win across overlapping rules. When a user pastes a credit card into a chat with an unsanctioned AI tool and a Block rule matches, the agent stops the paste and displays a branded "Paste blocked" card with the rule name and the message you configured. The page receives no content. Block decisions and policy metadata are written to the local audit trail and forwarded to your tenant. ## Identity and directory Agents observe an operating-system user name on each device. On its own that string is hard to govern at scale: the same person can have different logins on different machines, and a login like `jsilva` means nothing to an auditor. The Identity page under **Cyber Risk > AI Guardian > Identity** maps those OS user names to corporate identities from your directory, which unlocks group-based policy targeting, real names in the dashboards, and erasure requests that cover every device a person uses. - **Directory sync (SCIM 2.0).** Point your identity provider's provisioning at the SCIM endpoint shown on the page and paste the bearer token generated there. Supported today: **Microsoft Entra ID** (the directory behind Microsoft 365 / Office 365, so there is no separate integration to configure if you already use Microsoft 365) and **Okta**. Users and groups sync automatically. Rotating the token invalidates the previous one immediately. The token is shown once at generation time and stored only as a hash. - **Google Workspace directory sync.** Google Workspace does not push SCIM to an app you create yourself, so WASViking pulls your directory instead, on a schedule, using a read-only Google service account (Admin SDK Directory API). This brings in your users and their groups, which a plain Google user export leaves out. The setup guide has the one-time Google steps. - **CSV import.** For organizations without an identity provider, import identities from a CSV with `user_name`, `display_name`, `department`, and `groups` (separated by ";"). Re-imports update existing rows. - **Alias mapping.** A scan reads the distinct OS user names seen in your event stream over the last 31 days and proposes matches against your identities using deterministic heuristics (exact login, email local part, display name). Every suggestion is reviewed by a person: **only confirmed aliases ever affect enforcement, dashboards, or erasure.** A suggestion never blocks or attributes anything on its own, so the system cannot act on the wrong person. Confirmations and rejections are written to the audit log. - **Group policy targeting.** Once aliases are confirmed, an enforcement rule can target a directory group. When the policy is served to agents the group is expanded to the confirmed user names behind it, so the agent enforces exactly the same way it always has; the directory layer changes who a rule covers, not how the endpoint behaves. - **Erasure by person.** A right-to-be-forgotten request can name a corporate identity instead of a single login. The request expands to every confirmed alias of that person and removes their events across all of their devices in one operation, which is what LGPD Art. 16 and GDPR Art. 17 expect when an employee uses more than one machine. Confirmed mappings resolve to real names in the dashboards; events whose user name has no confirmed identity keep the raw login and are shown as unverified, so the console never implies an attribution it cannot stand behind. ## Dashboards in the portal Four pages under **Cyber Risk** read the same per-tenant event store, each designed for a different audience: - **Executive Summary** (`/portal/cyber-risk/ai-guardian/executive/`): C-level and compliance rollup. AI Security Score (0 to 100 composite of four pillars: vendor governance, data minimization, enforcement coverage, user concentration), four headline KPIs (unapproved apps, sensitive uploads, high-risk users, block decisions), vendor posture buckets (Approved / Review / Blocked), sensitive data sent to AI by category, and top users by risk. The score formula is published in the same page so an auditor can challenge the math. - **AI Guardian** (`/portal/cyber-risk/ai-guardian/`): SOC drill-down. KPI tiles (events, critical and high, monitored devices, top AI platform), time series, severity, vendor posture, policy, OS and browser donuts, top users, and a paginated events table with masked evidence in the detail drawer. The Type column carries a `decoded` pill when a match came from an encoded envelope; the drawer opens with a Compliance row that lists every regulatory framework the event implicates. - **AI Applications** (`/portal/cyber-risk/ai-guardian/applications/`): IT and compliance view. Per-AI-app rollup with users, alerts, uploads, sanctioned posture, severity mix, and trend pills against the previous window. - **AI Guardian Policies** (`/portal/cyber-risk/ai-guardian/policies/`): security engineering. Row builder for the rules described above. ## Updates and release governance The agent updates itself in place through a verified flow. Each apply recomputes `sha256` on the downloaded binary, validates the operating system code signature, takes a single-flight lock, backs up the running binary, atomically renames the new one in place, restarts the service, and checks `/healthz` for the expected new version. If any step fails, the previous binary is restored automatically. The host never sits in a half-applied state. Per-organization governance lives under **Settings > System Settings > AI Guardian**. Administrators choose the **release channel** (`stable`, `beta`, or `canary`) and may set a **pinned version** to homologate one build at a time. The same tab carries the **Vendor governance** card with three states per vendor: Approved (sanctioned in the dashboard), Blocked (explicitly forbidden, recorded as blocked in the dashboard) and Review (the default, treated as Shadow AI). The portal controls win over what an endpoint requests on the wire. ## Where to go next Follow the setup guide: [Set up WASViking AI Guardian](https://docs.wasviking.com/getting-started/ai-guardian/). --- # Mobile Security Assessment Section: Capabilities Source: https://docs.wasviking.com/capabilities/mobile-security/ Summary: Static security assessment of Android and iOS application packages against the OWASP Mobile Application Security Verification Standard, with SBOM, contextual risk scoring, and release-to-release comparison. WASViking® **Mobile Security Assessment** analyses a compiled Android or iOS application package and reports what it exposes. It covers configuration, transport security, cryptography, local data storage, platform interaction, binary hardening, third-party components, embedded credential material, and the data-collection SDKs the release ships with. The assessment is static. Nothing is installed, nothing is executed, and no device is required. You upload the artefact your build system produced and read the result. This is the portal section at **Mobile Security → Assessments**. ## What you can upload | Format | Platform | Notes | |---|---|---| | `.apk` | Android | The standard application package. | | `.aab` | Android | App Bundle. The base module is analysed. | | `.xapk`, `.apks` | Android | Split-package containers. The base module is extracted and analysed; the report says which one. | | `.ipa` | iOS | The App Store package. | The format is decided by reading the container, not the file extension. A package that is not a mobile application is refused at upload with the reason. Uploads are capped at 600 MB. If your artefact is larger than that, contact support before the assessment window. ## Running an assessment 1. Open **Mobile Security → Assessments** and choose **Analyse a package**. 2. Drop the file, or browse for it. 3. Optionally add a **Label**. Use the release name or the ticket reference; it is what you will look for later when comparing versions. 4. Start the assessment. Progress is shown live while it runs, stage by stage. A typical package completes in under a minute. Large packages with many dependencies take longer. You need the **Upload** permission on the Mobile Security resource. See [Inviting your team](https://docs.wasviking.com/getting-started/inviting-your-team/) for the role matrix. ## Reading the report The report opens on **Overview** and is organised in tabs. | Tab | What it answers | |---|---| | **Overview** | Grade, risk score, severity breakdown, the executive summary, the highest-risk findings, and what the assessment could not see. | | **Findings** | The full list, filterable by severity, category, MASVS group and OWASP Mobile Top 10 slot. Selecting a finding opens the detail panel. | | **MASVS Coverage** | Every MASVS control, whether it was assessed, and how many findings landed against it. | | **Components & SBOM** | The dependency inventory recovered from the package, and the CycloneDX download. | | **Secrets** | Credential-shaped values found in the package, masked. | | **Network** | Every endpoint the package carries, with the cleartext ones marked, plus the transport configuration. | | **Privacy & Trackers** | Third-party data-collection SDKs, grouped by purpose. | | **Package Detail** | The decoded manifest or property list, components, permissions, entitlements, and binary hardening flags. | | **Coverage & Limitations** | Which checks ran, which did not, and why. | ### The finding detail panel Each finding carries: - **What it is** and the concrete attacker capability it creates. - **Why it matters here**: the attack path, and what an attacker needs before they can use it. "Anyone who downloads the app" and "a rooted device the attacker already controls" are very different answers, and the panel says which one applies. - **Risk score** with the inputs that produced it, written out rather than asserted. - **Evidence**: where in the package the condition was found. Values that look like credentials are masked. - **Remediation**: the specific setting, API or configuration to change. - **Standards**: the MASVS controls (current and legacy identifiers), the CWE, the OWASP Mobile Top 10 slot, and the MASTG procedure to reproduce the result by hand. The MASTG procedure is worth calling out. Every finding tells a tester how to confirm or refute it manually, which is what an external penetration test report has to be able to do. ## Severity, risk score and grade Severity describes the class of problem. The **risk score**, 0 to 100, describes this problem in this application, and it is what the list sorts by. It accounts for: - how an attacker reaches the weakness, - how the package is distributed, - what the application is built with, since a value inside a JavaScript bundle is easier to reach than one inside compiled native code, - how confident the check is, - anything in the package that already mitigates it. The package receives a letter grade from A to F, driven by the highest finding with a bounded contribution from the rest. A long tail of informational findings cannot move the grade on its own. The scoring model is published inside every report, so a reviewer can reproduce any score rather than take it on trust. ## Findings, and where they go Findings above informational are promoted into the platform **Findings** workflow, with an owner, an SLA, a status and an audit trail, alongside your web and API results. Vulnerable-component findings are promoted under the existing component category so they sit with the rest of your supply chain work. A finding is identified by the application it belongs to, not by the file you uploaded. The same weakness in version 4.1 and version 4.2 is the same row, so fixing it resolves it and reintroducing it reopens it. ## Software bill of materials Every assessment produces a CycloneDX 1.5 SBOM for the shipped artefact, downloadable from the report. Components are matched against the same advisory corpus used by [Supply Chain Intel](https://docs.wasviking.com/capabilities/supply-chain-intel/), including the CISA KEV catalog. A component whose exact version cannot be recovered from the package appears in the SBOM but is excluded from advisory matching. The report states how many components fell into that group, so the supply chain result is read as the lower bound it is. ## Comparing two releases **Mobile Security → Compare versions** puts two assessments side by side and reports: - findings **introduced** by the newer build, - findings **resolved** since the older one, - findings **carried forward** unchanged, - findings whose severity moved, - dependencies added, removed or upgraded. The comparison warns you when the two assessments are not really comparable, for example when they are different applications or were produced by different engine versions. ## Accuracy Mobile static analysis is prone to attributing a bundled library's own content to the application. A cryptographic provider ships a catalogue of every algorithm it supports; a networking library references certificate validation because it implements it correctly. Reported naively, both produce findings that are wrong. WASViking evaluates where the evidence came from before scoring it. Findings that belong to a bundled dependency rather than to your code are downgraded or set aside, with the reason recorded on the finding and listed in the report. Nothing is removed silently, so you can review the judgement instead of trusting it. ## Exports | Export | Contents | |---|---| | **CSV** | One row per finding, with severity, risk score, attack path, standards mapping and remediation. Built for a remediation backlog. | | **JSON** | The complete report, in a versioned contract. | | **CycloneDX** | The SBOM on its own. | | **Package** | The artefact you uploaded, while it is still retained. | ## Retention Two horizons, because the two artefacts carry different sensitivity. | Artefact | Default | Note | |---|---|---| | Uploaded package | Purged on the shorter horizon | It is your intellectual property and frequently carries production credentials, so it is not kept longer than the analysis needs. | | Assessment report | Kept for the full retention window | Trend and version comparison depend on it. | Both horizons are set per organization. An assessment can be placed under **legal hold**, which exempts it from retention until the hold is released. Applying a hold requires a written justification and is recorded in the audit log. ## What it does NOT do - **No dynamic analysis.** The application is not installed, launched, instrumented or driven on a device or emulator. Findings that require runtime observation are outside the current scope. - **No source code required, and none inferred.** The assessment reads the compiled artefact. Where a result needs a human to confirm a call site, the finding says so and gives the procedure. - **No store submission.** WASViking assesses the package; publishing remains yours. - **No modification of your artefact.** The uploaded package is read, never rewritten or repackaged. ## Where it lives in the portal - **Mobile Security → Assessments**: upload, history, and the application portfolio. - **Mobile Security → Rule catalogue**: every check the engine runs, with its severity and standards mapping. Readable before you upload anything. - **Findings**: mobile results in the shared remediation workflow. - **Settings → Usage**: assessments consumed in the current period. ## In your pipeline Assessments do not have to start in the portal. The WASViking Sentinel submits the package your build produces straight from CI/CD, fails the build on new findings with a baseline diff, and publishes SARIF to the GitHub Security tab. Pipeline runs land in the same history, tagged with a **CI** badge. See [CI/CD Mobile Security with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-mobile/) and the command reference at [wasviking-sentinel mobile](https://docs.wasviking.com/sentinel/sentinel-mobile/). ## Set up Mobile Security Assessment is enabled per organization. If **Mobile Security** appears in your sidebar but the page shows a locked panel, the module is not enabled for your organization yet; contact your account team. Once it is enabled there is nothing to configure, upload your first package and the assessment runs. See [Activate your modules](https://docs.wasviking.com/getting-started/activate-your-modules/) for the full activation checklist. --- # Infrastructure Defense Section: Capabilities Source: https://docs.wasviking.com/capabilities/infrastructure-defense/ Summary: Security posture for your infrastructure. The Sentinel Host agent inventories each machine and the Sentinel Probe scans the networks around them, the cloud correlates vulnerabilities and misconfigurations, the Viking Exposure Score prioritizes them, and Resolve executes approved fixes with a measured risk reduction. WASViking® Infrastructure Defense is the security posture of the servers running your business, in one place: what you have, where it is vulnerable, which fixes matter most, and how much risk each executed fix actually removed. It works from facts a lightweight agent collects on each host and it never ends at "N vulnerabilities found"; the cycle only closes when a later inventory proves the risk dropped. ## How it works A small agent, the WASViking Sentinel Host, runs on each enrolled server and reports a security posture inventory over mutual TLS: operating system and kernel, installed packages, running services and listening ports, pending updates, and security configuration such as the firewall state and the SSH hardening options. The asset inventory also carries the machine facts an operator expects on an asset record: naming (FQDN, DNS hostname, and on Windows the NetBIOS name), IPv4 and IPv6 addresses, manufacturer and model, processor and total memory, fixed volumes with free space, and the local account names as the operating system reports them. It reads posture only; it never opens user files or content, and account names are collected without any profile data or per-user activity. Every inventory is correlated in the WASViking cloud: - Installed packages are matched against the OSV vulnerability database, qualified by the exact distribution release so a patched host is never flagged for another branch's bug. macOS updates come from the published security releases, and third-party Windows applications from the package manager inventory. Amazon Linux, which OSV does not cover, is matched against the vendor's own security advisories (ALAS) with the same per-package, release-qualified comparison, for Amazon Linux 2023 and 2. - Windows hosts are assessed by OS build revision. Every security fix Microsoft shipped in a cumulative update the host has not reached is an open vulnerability, named with the KB and build that close it. The revision is the authoritative signal, so an update that is installed but still waits for a restart does not count as applied, and the pending list from the update service shows which cumulative closes what. - Each vulnerability carries its CVSS severity, its EPSS exploitation probability, and whether it appears in the CISA Known Exploited Vulnerabilities catalog. - Every "what changed" number is a door, never a dead end. The Command Center chips (new and fixed findings in the last 24 hours) and the status tiles on the Vulnerabilities page open the exact detections behind the count: host, package, installed and fixed versions, and the moment each one was first seen or proven closed. When a host enrolled in the same period accounts for part of the new findings, the list says so, because a first inventory reports every existing finding as new. The CSV export follows the same filter. - Pending security updates become "missing patch" context: which open vulnerabilities a published fix would close. - An operating system or a tracked software product past its vendor's end of support is flagged from published lifecycle data, with the date on the label, because no patch is coming for it. - A configuration catalog aligned with CIS benchmark sections evaluates the collected settings and scores each host with a compliance percentage, with a PCI DSS mapping of the same results. Every control records the expected value next to what the host reported, so a failure explains itself. The catalog covers credential protection and lateral movement on Windows (SMBv1, Remote Desktop Network Level Authentication, LSA protection, WDigest, LAPS, LLMNR, SMB signing, NTLM level, UAC, WinRM, the advanced audit policy and Microsoft Defender), kernel and access hardening on Linux (SELinux or AppArmor, ASLR, network kernel parameters, auditd, persistent logging, time synchronization, the effective sshd configuration, sudo, local accounts, system file permissions, repository signatures), and the login, root account, stealth mode and security data updates on macOS. Platform posture is collected alongside: disk encryption, Secure Boot and TPM state on Windows, System Integrity Protection, Gatekeeper and MDM enrollment on macOS. The agent reports facts only; a fact an older agent does not report is not applicable, never a failure. - Each host policy selects the profile its hosts are held to: the WASViking baseline, or CIS Level 1 with the stricter thresholds and the additional Level 1 controls. Thresholds (password length, inactivity lock, lockout) can be tuned per policy and individual checks switched off. An approver can accept the risk of a failing control for a stated reason until a date: the control then counts as passed with exception, stays visible as an accepted risk, and returns to the open list when the exception expires. Open critical and high misconfigurations add a bounded factor to the Viking Exposure Score. - A few findings carry a hardening action the operator can send to the host with the Remediate permission: disable SMBv1, require Network Level Authentication, turn off LLMNR, apply the kernel network hardening. The agent applies the documented change, reports a fresh inventory, and the finding closes on that evidence. - The platform's own attack surface discovery proves internet exposure: when a discovered public asset resolves to an address assigned to the host, the exposure flag lights with the proof named, and a manual decision by your team always wins over the automation. Edge telemetry adds an active attack factor when blocked attack-grade traffic hit a hostname the server serves in the last seven days; the factor decays on its own when the traffic stops. - The same correlation runs the other way. Every internet-facing asset you own in the Attack Surface is matched against your enrolled hosts and labeled with a management state: fully managed, partially managed, unmanaged or unknown. The Command Center says in one sentence how many externally exposed assets are not protected yet, the Coverage tab lists them with the next step for each (install the agent, add probe credentials, link the asset to the host behind a CDN or WAF), and the asset inventory shows the same verdict from the other side. Assets still waiting for triage in Discovery are never counted as unprotected. The step by step is in [Bring your internet-facing assets under management](https://docs.wasviking.com/getting-started/attack-surface-coverage/). ## The network view: Sentinel Probes The agent gives you the inside of every machine you can install it on. A Sentinel Probe gives you the rest of the network. It is a virtual scanner appliance: one lightweight program on a single machine in a segment discovers every reachable device on the networks you authorize, identifies the services and versions they expose, and reports them to the same cloud for correlation and the same Viking Exposure Score. Nothing is installed on the devices it scans, and like the agent it only connects outward over mutual TLS. This is what covers the things that will never run an agent, such as a managed switch, a printer, a database appliance or a payment terminal, and it is how the module produces the internal vulnerability scan PCI DSS asks for. The networks you declare are scanned on a cadence you set, each run is kept as dated evidence (requirement 11.3.1), an authenticated pass over SSH or WinRM looks past the surface where a system accepts credentials (requirement 11.3.1.2), and a one-click Scan now files an off-cycle run after a change (requirement 11.3.1.3). A device the probe finds that has no agent appears in your inventory as unmanaged, so you always know what you manage and what merely exists on the network. The [Sentinel Probes setup guide](https://docs.wasviking.com/getting-started/sentinel-probes/) walks through installing one, authorizing its networks and reading the results. ## The Viking Exposure Score Each host gets a Viking Exposure Score from 0 to 100. The worst open vulnerability sets the base; exploitation evidence (KEV, EPSS), proven internet exposure, active attack traffic seen at the edge, end of life state, business criticality, and environment adjust it. The score is never a black box: every screen shows the exact factors and points behind the number, so "why is this critical" always has a concrete answer. ## Resolve: remediation with proof WASViking Resolve turns the priorities into work under your control: 1. Recommended actions group the pending security updates of each host, ranked by the score reduction they are expected to deliver. 2. Scheduling an action freezes it as a job with the exact package list, so the approver signs off on a concrete change. Open Review and untick the updates you want to leave for later: the job only touches what stays ticked, and the vulnerabilities closed and the expected score are recomputed for that selection. Updates left out stay recommended for the next job. 3. A person with the Remediate permission approves the job, optionally inside a maintenance window defined by the host policy. Automatic deployment and automatic reboot are off by default. 4. The agent runs pre-checks, applies the updates through the native mechanism of each platform (the system package manager on Linux, the update service on Windows, software updates on macOS), and reports the outcome per item. Third-party applications on Windows (browsers, readers, runtimes) get their own action, "Upgrade third-party applications", fed by the upgrades the winget channel offers on that host. Same freeze, approval and per-item outcome; the agent upgrades each application by its channel id and never anything the job did not name. For the applications the detection engine maps (browsers, mail clients, editors, runtimes and other common desktop software), the installed version is matched against the NVD by product and version, so the action shows the vulnerabilities the upgrade closes and the expected score, like a security update does; an application without a mapping keeps the "app currency" label, and the job keeps it on the vendor's current version. The verdict is the next inventory no longer offering the upgrade, with the findings it closed. 5. The next inventory is the verdict: the job completes only when the reassess shows which vulnerabilities closed and how far the score dropped. The before and after numbers are measured, never estimated, and when a reboot is still pending the job says so instead of declaring victory early. One change at a time. Right before installing, the agent reads the host's own restart signals (the servicing stack, Windows Update and pending file renames on Windows; the reboot-required marker or needs-restarting on Linux). When a restart from an earlier change is still pending, the job is held instead of stacking a second change on an unfinished one: the Resolve screen shows "Held, restart pending" with the reason, the asset shows since when and after which change the restart is due, and the job resumes on its own the moment the host reports the restart done. The policy decides the exception: restart the host first (only when the approver also granted the reboot) or install anyway on Linux and macOS. Windows always waits, since Windows Update refuses installs with a restart pending. A "Restart host" action on the asset and on the Agents screen clears the wait with one click, for people with the Remediate permission, and a job still waiting after a day raises an alert in your channels. A restart can also be scheduled as a job of its own. The Resolve screen recommends "Restart host to finish pending changes" for every host with a restart pending, and the "Hosts waiting for a restart" report lists them oldest first with the cause, the host role, the signed-in sessions and the policy that applies, with a CSV export and a "Schedule restart for all" action. A restart job follows the same contract as any change: approval under the policy, the maintenance window, and a verdict from the next inventory, which proves the restart by the boot time the host reports. The agent tells servers from workstations on its own. A server restarts after the countdown the policy sets; a workstation with a person signed in is asked first and may postpone as many times as the policy allows, after which the countdown runs without a question. A host whose restart stays pending for seven days raises its own alert, job or no job. A change that misbehaved can be undone with the same discipline. A completed security update job on a Linux or Windows host offers "Roll back this change": a rollback job for the same packages, each one returned to the exact version it had before, with a mandatory reason, the back-out plan carried over, and the approval, window and restart rules of any other change. Linux downgrades through the package manager and never touches the kernel; Windows removes each update by its KB through Windows Update and reports the ones the platform does not allow removing; macOS has no uninstall path, and the screen says so. The reassess measures the result honestly, "VES 70 to 88, 3 of 3 packages returned, 17 vulnerabilities open again", and the returned updates stay held out of the recommendations for the days the job asked, visible on the host and released early with one click. See [Roll back a change that misbehaved](https://docs.wasviking.com/getting-started/roll-back-a-change/). Approval governance decides who signs off, per environment. Each host carries an environment (production, staging, development or other), and the Approval Governance screen sets a rule for each one: follow the host policy, approve automatically, let one person approve (the requester included), or require a formal approval. A formal rule says how many approvals a job needs and, optionally, who may give them, by name or by role; the person who scheduled the job never counts. Every decision is recorded with who decided, when, the stated reason and the change reference, and the approval records export as CSV: the evidence a change control audit asks for. A production rule can also require the same packages to have completed in staging or development within a window before production accepts them; a job that arrives untested says so, and an approver has to waive it on purpose. Approvals that wait past the window set in the rule are cancelled, formal approvals waiting are announced to your alert channels and to the approvers by e-mail, and the requester hears the decision. When the rule allows it, an emergency approval runs the job without the quorum, with a stated reason; every approver is notified and the job stays flagged until another approver reviews it. When the rule names its approvers, the emergency path belongs to that list too. The person scheduling a change can state why it is needed and how it is undone, and a rule can refuse a change scheduled without that back-out plan; both stay in the approval record and the export. A job answers to the environment it was created in: moving a host to a less guarded environment afterwards changes nothing for jobs already waiting, and the move itself needs the Approve permission, is recorded with the before and after, and raises an alert on your channels. Policies decide how far the platform may go on each group of hosts: what gets assessed, whether jobs need manual approval where the governance follows the policy, and when changes are allowed. Every action lands in the audit trail, and the patch job outcome can notify your Slack, Teams, email, or webhook channels. Performance and resource protection is part of the same policy. It states how much of a host the agent may take and when it must step aside: a share of the machine's CPU (a hard cap through the systemd control group on Linux and through a job object on Windows, a scheduling priority on macOS), the inventory and check-in cadence, reduced scheduling priority for the agent and every subprocess it spawns, and a daily reduced activity window in the host's own local time. Three presets cover the usual cases: conservative for critical production, balanced for the fleet at large, performance for development and test; custom exposes every number. Beyond the static limit, the agent looks at the host before every scheduled assessment and before every patch job. A host above the policy's CPU or memory thresholds, or under critical load, is left alone: the assessment waits and looks again every ten minutes, up to the policy maximum, then runs anyway at reduced priority so coverage never lapses in silence; a patch job goes back to the cloud as deferred, is offered again a quarter of an hour later and fails visibly, with the numbers in the note, if the host never calms down inside the maximum wait. Each asset page shows what its agent reports: the profile revision it applies, how the platform enforces it, the last load sample and any work on hold. Agents from release 0.1.34 apply the profile; older agents follow its inventory cadence only. Fleet hygiene keeps the agent list honest without anyone tidying it. The monitored hosts allowance counts assets that reported, never enrollment attempts. An installer that cannot reach the cloud stops before registering and names the product in the way; an enrollment that registered but never sent its first inventory raises one alert with the check to run and the exclusions to apply; a token never used expires; and when a machine finally reports, the earlier enrollments of the same hostname that never completed are retired on their own. The Sentinel Hosts screen flags what needs attention (never reported, token never used, duplicate hostname, silent for a week), offers a one-click removal of the incomplete enrollments of a hostname, and lets you set the housekeeping windows: how long a silent host is kept, how long a token lives unused, and how soon a missing first inventory is announced. ## Reporting The Overview screen answers the ten second question: overall infrastructure risk, the fleet numbers, and the top risks with their reasons. A branded PDF report exports the same story for stakeholders: fleet posture, verified risk reduction, top vulnerabilities, misconfigurations, and per host compliance. ## Set up Infrastructure Defense is an add-on module enabled per organization. Once it is on, open **Infrastructure Defense → Sentinel Hosts** in the portal. For a single server, enroll a host to get its one time credentials and run the two commands shown on the enrollment card (register, then run). For a fleet, open the **Activation keys** tab, generate a reusable key (with an optional agent cap and expiry), pick the platform on its install page, download the agent package for Windows, Linux or macOS and run the install command shown there: it carries the key, registers the host and starts the service in one step. Windows fleets can use the standard MSI package with the mass deployment tooling they already run, and enrolled agents keep themselves current with one-click self-update from the console. Either way the host appears on the Assets screen with its first inventory within minutes and its score follows the first assessment. For the whole flow with screenshots, from the first activation key to the first verified patch job, follow the [step by step setup guide](https://docs.wasviking.com/getting-started/set-up-infrastructure-defense/). To add the network view, run a Sentinel Probe on each segment with the [Sentinel Probes setup guide](https://docs.wasviking.com/getting-started/sentinel-probes/). The [module activation checklist](https://docs.wasviking.com/getting-started/activate-your-modules/) covers turning the module on. --- # Header Advisor Section: Capabilities Source: https://docs.wasviking.com/capabilities/header-advisor/ Summary: Learn the Content Security Policy an application really needs from the browsers of its own users, deploy it with confidence, and keep it current as the application changes. WASViking® **Header Advisor** writes the Content Security Policy (CSP) an application needs from evidence, not from guesswork. You add one report-only header at your edge or origin. The browsers of your own users then report every script, style, font, image, frame, API call and inline block the application loads, page by page. From that evidence the platform proposes the policy, grades it, detects when you deploy it, tells you when the application changes and the policy has to follow, and separates legitimate change from injected content. ## The problem it solves A missing or weak CSP is one of the most common findings on any web application, and one of the hardest to close. The policy has to list every origin the application loads, including the ones added by tag managers, chat widgets, payment providers and a decade of front-end decisions nobody documented. Written by hand, the first draft breaks the application or ends up so permissive it protects nothing. Written once, it goes stale the next time the marketing team adds a script. Header Advisor turns that into a measured process. The application keeps running while the policy is learned. The proposal is built from what real users loaded, with the decisions that need a human called out explicitly. After deployment the same evidence stream keeps the policy honest. ## How it works 1. **Add the discovery header.** The portal gives you the exact header names and values for your platform. The header is `Content-Security-Policy-Report-Only`, which never blocks a request; it only asks the browser to report what the page loads to the WASViking collector. 2. **Learn from real traffic.** Every page your users open adds evidence. Each source is aggregated by directive and origin with the days it was seen, the number of distinct browsers, the pages it appeared on and a sample of what was blocked. The learning window is seven days by default; the policy is also ready earlier when no new source has appeared for three days. 3. **Review and approve.** The proposal lists every source with a verdict and the reason behind it. Inline scripts, inline event handlers, inline styles, `eval` and blob workers each get a decision card with the options and their impact on the grade. The policy is graded with the same grader the scan engine uses for security headers, so the advisor and your findings never disagree. 4. **Deploy in two moves.** Copy the approved policy as report-only first. WASViking recognises it from the browsers' own reports and marks the advisor as a **candidate**; after three clean days you rename the header to `Content-Security-Policy` and the advisor becomes **enforced**. Deployment is detected, not declared. 5. **Keep it current.** Analysis runs every ten minutes. A new legitimate source that reaches quorum after deployment becomes an update waiting for review, with a ready diff for the next version. Content that does not belong to the application becomes a threat signal instead. ## What you see | Screen | What it shows | |---|---| | **Header Advisor** (list) | One card per hostname with its status, the headline (sources to review, open threat signals), reports in the last seven days, the approved version and its grade. | | **Step 1: Add the discovery header** | Header names and values with a walkthrough for Cloudflare, Google Cloud Load Balancer, nginx, Apache, IIS, Node.js (Express), Next.js, Django, Spring Boot and ASP.NET Core, plus **Check header** to confirm the hostname already sends it. | | **Step 2: Learning from real traffic** | Day counter, sources, pages seen, distinct browsers, new sources in the last 24 hours, stability, and the source table with verdict, classification, evidence and reputation. | | **Step 3: Review and apply the policy** | The proposed or approved policy one directive per line, its grade, the decision cards, the rollout in two moves and the per-platform snippets. | | **Deployment and monitoring** | Reports, noise filtered, drift pending and open threat signals; the deployed version and disposition as seen by the browsers; the review changes list with the diff for the next version. | | **Threat signals** | Injected inline scripts, look-alike domains, sources pulled by addresses Edge Threat Radar flags, each with severity, page, sample and an acknowledge action. | | **Policy history** | Every version with status, grade, source count and dates. | ## Verdicts | Verdict | Meaning | |---|---| | Recommended | Reached quorum (days and browsers) and passed the reputation check. Included in the policy. | | Needs your decision | Inline code, `eval`, blob workers or a host under a frequently abused top-level domain. A decision card or an explicit include is required. | | Watching | Seen, but below quorum. Not included yet. | | Excluded | Excluded by you. Kept in the evidence so the exclusion is visible. | | Threat | A look-alike of your own domain, an address literal, a punycode label or a source pulled mostly by addresses Edge Threat Radar flags as attackers. Never proposed. | Quorum scales with the audience: a source has to be seen on at least three days by a share of the observed browsers, with a floor of three and a cap you set. A staging or internal application with fewer than ten browsers runs in a low-traffic mode with a quorum of one, so it can still get a policy. ## Sources the catalog completes Around fifty documented services are known to the advisor: tag managers, analytics, fonts, CAPTCHA and challenge platforms, payment providers, chat and support widgets, error tracking, identity providers and CDNs. When the browsers report one of them, the directives its documentation requires are completed automatically, so the first deployment does not break a checkout because a provider loads a frame the learning window never saw. ## Hardening grade Every proposal and every approved version carries a grade: **Strong**, **Partial**, **Weak** or **Missing**, with the reason. `unsafe-inline` and `unsafe-eval` count as weak only where they weaken script protection; `unsafe-inline` for styles alone is partial. The decision cards show the impact of each option before you approve, and the hashes of your inline scripts and styles are computed from your pages when the hostname is reachable, so the strong option is one click when the application allows it. ## Safe by design - **Report-only never blocks.** The discovery header and the candidate header only ask the browser to report. Nothing the application loads is affected. - **Reports travel out of band.** The browser sends them after the page has loaded and drops them silently if the collector is unreachable. An outage on the WASViking side costs an interval of learning, never a request on your side. - **Deployment is detected from evidence.** The platform never writes to your edge or origin. You copy the header; the browsers confirm it. - **The enforcing move is yours.** Enforcement is a separate, deliberate step, taken after the candidate ran clean. ## What is collected Reports carry the page path (no query string), the directive, the blocked source and, for inline code, a short sample of at most 160 characters. The collector aggregates them per source; raw reports are not stored. Browsers are counted through a keyed weekly fingerprint, never by raw address. Reports for a hostname other than the one being advised are dropped, and the collector rate-limits per hostname and per client. Retention of the aggregates follows your plan. ## Alerts One event type, **Header Advisor**, available on every notification channel (Slack, Microsoft Teams, email, API). It fires when a policy is ready for review, when an update is waiting after deployment and when a new threat signal appears. Opt in per channel under **Notification Channels**. ## What this is not **It is not** a JavaScript snippet on your pages. Header Advisor adds nothing to the application and cannot slow it down. **It is not** an automatic deployer. Applying the policy at your edge or origin stays in your hands, with the exact value to paste. **It is not** a web application firewall. It shapes what the browser is allowed to load; it does not inspect traffic. ## Plan availability | Plan element | Notes | |---|---| | Monitored hostnames | Pro: 1. Business: 3. Enterprise: negotiated. One advisor per hostname; subdomains of your targets are accepted. | | Evidence retention | Pro: 30 days. Business: 90 days. Enterprise: negotiated. | | Roles | **Admin** and **Manager** start, approve, pause and remove advisors. **Analyst** and **Read only** view them. | ## Where it lives in the portal - **Edge Threat Radar → Header Advisor**: the list, each advisor's three steps, monitoring, threat signals and history. - **Vulnerabilities → Security Headers**: the Content Security Policy finding offers to start the advisor for that hostname. - **Notification Channels**: the Header Advisor event on each channel. ## Set up Follow [Set up Header Advisor](https://docs.wasviking.com/getting-started/header-advisor/): add the hostname, deploy the discovery header on your platform, watch the learning window, approve and roll out the policy in two moves. --- # Code Security Section: Capabilities Source: https://docs.wasviking.com/capabilities/code-security/ Summary: Connect your GitHub or Bitbucket repositories and let WASViking assess the source continuously with static application security testing (SAST), AI / LLM security review, dependency analysis, secret detection and SBOM generation, with every risk and its remediation context in one place. WASViking® **Code Security** is the Application Security hub for the source code behind your applications. You connect a GitHub account or a Bitbucket workspace once and choose the repositories to monitor. From then on the platform clones each default branch on its own and runs four analyses on it: static application security testing (SAST), dependency analysis, secret detection and SBOM generation. Everything it finds lands in **Findings** with the same lifecycle, risk score, SLA and alerting as the rest of the platform, and the repository becomes an asset in your inventory, like a target. No pipeline change is required. ## The problem it solves Most code security tooling lives inside the pipeline. Repositories without a pipeline, legacy services nobody deploys any more and the internal tools that never got a CI job receive no coverage at all, and they are often the ones holding the oldest code. Even where a pipeline exists, each tool reports into its own console, so the team triages dependency alerts in one place, leaked secrets in another and never sees the weaknesses in its own application logic, which no dependency database and no external scanner can reach. Code Security turns that into one motion. WASViking reads the code, correlates what it finds with what your scans already know about the running application, and files the result where your team already works: the Findings queue, the risk score, the alerts and the tickets. ## What runs on every monitored repository 1. **Static application security testing.** WASViking reads the application source itself. Automated code discovery ranks the files that matter for security, such as routes, controllers, request handlers and authorization code, and the analysis reviews them for weaknesses in how the application enforces access and protects its users. Every weakness is reported with source-level evidence: the file and line, the route and the parameter involved, why it is exploitable, a concrete attack scenario and the fix for that exact code. Each reported line is verified against the real file before it reaches you; anything the analysis cannot ground in the code is discarded. Every file carrying an open finding is reviewed again on each scan, and the remaining review budget rotates across the rest of the repository, so coverage widens with successive scans. The same review covers the code that puts a language model to work; see [AI / LLM Security](#ai--llm-security) below. 2. **Dependency analysis.** Dependency manifests (npm, pip, go, composer, Maven, gem, pub) become a component list matched against OSV and the CISA KEV catalogue, with drift detection against the previous scan of the same repository. 3. **Secret detection.** The working tree, and the git history of the default branch when your plan includes it and you switch it on for that repository, are scanned for hard-coded credentials. Evidence is redacted before it is stored. 4. **SBOM generation.** Each scan produces a CycloneDX SBOM for the repository. It feeds the daily re-check of Supply Chain Intel and can be exported as a signed SBOM Evidence Bundle. ## AI / LLM Security Teams put language models into applications faster than security learns about it: a support chat, a copilot inside the admin, an agent that can refund an order, a retrieval layer over internal documents. Code Security treats that code as part of the same SAST review, with the same question it already asks about access control: can someone who should not, reach something they should not? On every scan WASViking first discovers where the repository uses AI, without calling any model. It records the providers and gateways the code talks to, the agent and orchestration frameworks, how many agents and tools are defined, whether documents are retrieved into the prompt (RAG), whether vector stores or MCP servers are in play and which model names the code references. That profile appears on the Code Security row and in the run history, so you know where AI already lives in your systems before the first weakness is found. Then the review reads each file that talks to a model together with the modules that complete its flow, such as the route that feeds the agent and the module that defines its tools, and reconstructs the trust path: where untrusted content enters, how it reaches the prompt, what the model's output can reach on the way out and whether a check sits in between. System instructions are trusted. The user prompt, retrieved documents, fetched pages, inbound email, uploaded files and webhook payloads are untrusted. Tools and actions, databases, internal APIs, secrets, payments, account operations and other tenants' data are sensitive. A path from untrusted to sensitive with no adequate control in the middle is a finding. | Finding | What WASViking found in the code | | --- | --- | | Prompt Injection Exposure | User content is embedded into the instruction context with no boundary between trusted instructions and untrusted content. | | Indirect Prompt Injection | Retrieved, fetched, uploaded or inbound content enters the model context and is treated as instructions. | | Unsafe LLM Tool Execution | The model can trigger a sensitive tool or action and no authorization check verifies the caller may perform it. | | Sensitive Data Exposure to LLM | Secrets, personal, financial, health or internal data are sent to the model when the operation does not need them. | | Cross-Tenant AI Context | Retrieval, memory or cached context is not scoped to the caller's tenant, so one customer's content can reach another's prompt. | Severity follows what the path reaches, not the presence of a prompt. User content reaching the model with nothing downstream is medium. A path that reaches sensitive data is high. A path that can trigger a tool or action with no gate in between is critical. That keeps a chat endpoint that only echoes the model from being rated like an agent that can refund payments, and it is what makes the finding worth a developer's attention when it is critical. The evidence panel shows the class and its OWASP Top 10 for LLM Applications mapping, the entry point, the untrusted input, the model call, the sensitive operation, whether an authorization check was found, the trust path drawn step by step, why it is exploitable, an attack scenario, the code excerpt with the flagged line and the fix for that exact code. Every reported line is verified against the real file, and the same file-and-handler identity keeps one risky path one finding across scans. The review never sends prompts to your model, never calls your providers and never executes the repository. It reads source only. ## Findings that behave like findings - **Stable identity.** The same weakness in the same place is one finding across scans, even as line numbers move. It reopens on regression and resolves on its own once two consecutive reviews of the same file no longer find it. - **Same lifecycle as everything else.** Severity, risk score, SLA clock, audit trail, assignment, webhooks and ticketing work exactly as they do for a DAST finding. A fix is measured the same way whether the weakness was found in traffic or in code. - **Evidence a developer can act on.** The **WASViking SAST Findings** panel on a finding shows the weakness class, the file and line, the route and parameter, a plain explanation of why it is exploitable, the attack scenario, the remediation and the code excerpt with the vulnerable line highlighted. The access-control family it reports today includes IDOR, BOLA, BFLA, missing authorization checks and clickjacking; the AI / LLM family covers prompt injection, indirect prompt injection, unsafe tool execution, sensitive data sent to the model and cross-tenant context. New classes are added as the analysis grows. ## What you see - **Application Security → Code Security**: the fleet view. Tiles for repositories granted, repositories monitored, open findings, **WASViking SAST Findings**, **AI / LLM Security** with how many repositories use AI, components tracked, runs this month and plan allowance in use. One row per repository with the **Monitored** and **Git history** switches, the commit that was scanned, components, open findings by severity, the SAST findings by weakness class with when the code was last reviewed, the AI / LLM findings by class with the AI usage profile of the repository (providers, frameworks, agents, tools, RAG), a **Drift** marker when the dependency set changed and **KEV** when a finding is on the CISA catalogue. The **Runs** panel keeps the history of every scan with its trigger, branch, commit, duration and what it found. - **Findings**: filter by repository and by category to triage, or by the OWASP Top 10 for LLM Applications entry. Every SAST finding opens with its evidence panel. - **Repository report**: a PDF with every open finding of one repository, by engine, including the SAST and AI / LLM sections and the AI usage profile, for a code owner or an auditor. - **Software inventory**: components under **Inventory → Software Bill of Materials**, daily advisory matches under **Inventory → Supply Chain Intel**, secret submissions under **Application Security → Hard-coded Secrets**. ## Alerts and tickets New and reopened findings follow your notification rules like any other finding: [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/), [Webhooks](https://docs.wasviking.com/integrations/webhooks/), [Jira](https://docs.wasviking.com/integrations/jira/) and [ServiceNow](https://docs.wasviking.com/integrations/servicenow/). ## Safe by design - The clone happens in an isolated, short-lived workspace on a dedicated worker. Nothing from the repository is ever executed. - Provider credentials never reach your browser and are stored encrypted. Each run uses a short-lived, repository-scoped token that is dropped when the clone finishes. - Clones use https to the provider only, with prompts, LFS and submodules disabled and a size ceiling per repository. - The clone and every intermediate file are deleted at the end of the run, also when the run fails or times out. Only derived data stays: findings with their evidence, the component list, vulnerability matches, redacted secret evidence and the run record. - On a GitHub organization the connection is accepted only from an owner, so a member who can see an installation cannot bind your organization to another tenant. ## Plan availability Code Security is included on the Pro plan and above; a partner or our team can enable it on other plans. Each monitored repository counts one unit against the plan allowance, whatever its size and however often it is scanned. Every monitored repository is scanned at least once per interval (Pro every 24 hours, Business every 4 hours, custom on Enterprise) and right away on a push to the default branch when the interval has elapsed. Admins and Managers connect providers and choose what to monitor; Analysts read the results. ## Where it lives in the portal - **Application Security → Code Security**: coverage, results and runs. - **Settings → System Settings → Connected Repositories**: the provider connection, what it granted, sync and disconnect. - **Findings**: triage, with the repository and category filters. - **Account → Subscription → Repositories**: allowance and usage. ## Set up Follow [Set up Code Security](https://docs.wasviking.com/getting-started/connected-repositories/): connect GitHub or Bitbucket, switch **Monitored** on for the repositories WASViking should read, run the first scan and let the cadence take over. Then walk through [Triage your first SAST finding](https://docs.wasviking.com/getting-started/triage-your-first-sast-finding/) to read the evidence, fix the code and confirm the finding closes on its own. --- # Sentinel architecture Section: Sentinel agent Source: https://docs.wasviking.com/sentinel/architecture/ Summary: How the agent dials outbound mTLS to open a tunnel, and why no inbound port is ever required. WASViking® Sentinel is a Go binary you install on a host inside your network. The agent dials outbound to WASViking over mutual TLS and opens a bidirectional gRPC tunnel. Every dynamic analyzer routes its HTTP probes through that tunnel transparently, with no analyzer-specific code. ## Transport - **Outbound only.** No inbound ports, no static IP allow-listing, no VPN. The agent initiates. - **Mutual TLS.** The client certificate is provisioned during `wasviking-sentinel register` and rotates on schedule. - **gRPC over HTTP/2.** Long-lived stream, server-side flow control. - **Standard load-balancer compatible.** Works behind common managed load balancers that terminate mTLS and forward the verified client certificate. - **Org binding.** The certificate's CN/O identifies the organization. Cross-tenant requests are refused at the gRPC service layer. ## Hardening on the host The Sentinel package ships a systemd unit with: - `NoNewPrivileges=true` - `ProtectSystem=strict` - `MemoryDenyWriteExecute=true` - Restricted system calls - Cosign-signed releases (verifiable with the published public key) An obfuscated enterprise build is available for environments that require it. ## Operator privacy The gRPC service redacts secrets at the boundary. The server never logs full proto, job, or response payloads. Cookies, CSRF tokens, and internal target bodies do not appear in operator logs. This is enforced in code at the service layer, not in policy. Every new gRPC method has redaction baked in by design. ## Tunneled analyzers The tunnel is a property of the platform, not a feature of one scanner. The same DAST analyzers that run against external targets run against internal targets through the tunnel: - SQL Injection, XSS, JWT, and the injection-class checks. - GraphQL, SOAP/WSDL, and WebSocket. - Component detection, sensitive file and path exposure, and security headers. No analyzer carries tunnel-specific code. The HTTP layer routes transparently. ## What the agent can do beyond DAST The Sentinel CLI also exposes: - `wasviking-sentinel sbom`: premise-side SBOM generation, OSV + KEV enrichment, REST submission. - `wasviking-sentinel secrets`: filesystem and git history secrets scan, optional live verification, hash + masked preview transit only. - `wasviking-sentinel ci`: CI/CD gate for SCA, secrets, and template-driven scans, with deterministic exit codes. See the next pages in this section for each subcommand. ## Job dispatch Internal scans are queued on the WASViking side. The gRPC server hands a job to the connected agent for the right organization, the agent runs it locally, and streams results back. Heartbeats keep the connection healthy and detect zombie agents. ## Failure modes | Symptom | Likely cause | |---|---| | Agent fails to register | Token expired or rotated. Reissue via the portal. | | Tunnel drops every few minutes | Network proxy enforces idle timeout. Configure keep-alive. | | 403 on submit endpoints | Org binding mismatch. Re-register or check certificate CN. | | Scans queued but not running | Agent disconnected. Check `systemctl status wasviking-sentinel`. | The Sentinel Agents page in the portal shows agent state, last heartbeat, and provisioning history. For step-by-step fixes (agent stays Pending, tunnel keeps dropping, certificate problems), see the troubleshooting section of [Installing the Sentinel agent](https://docs.wasviking.com/sentinel/installation/). --- # Installing the Sentinel agent Section: Sentinel agent Source: https://docs.wasviking.com/sentinel/installation/ Summary: Register an agent in the portal, install the signed package, register on the host to fetch certificates, and start the service. mTLS to your tenant, no inbound ports. The WASViking® Sentinel agent installs from a signed package. The whole flow is driven from your portal: create the agent record to get a one-time token, install the package on the host, register with the token to fetch certificates, and start the service. The mTLS tunnel comes up and the WASViking cloud can then scan your internal targets. ## Supported platforms | Platform | Package | |---|---| | Ubuntu / Debian | `.deb` | | RHEL / Rocky / Alma / Amazon Linux | `.rpm` | | Other Linux with systemd | tarball | | macOS | Supported for local evaluation. | | Windows | Not supported. | Minimum requirements: 1 vCPU, 512 MB RAM, 1 GB disk for cache and ephemeral scan output. Network: outbound HTTPS to your tenant gRPC endpoint (`sentinel.wasviking.com`, port 443). No inbound ports required. For the walkthrough with portal screenshots, see [WASViking Sentinel Tunnel](https://docs.wasviking.com/getting-started/sentinel-tunnel/) in Getting Started. This page is the full installation reference. ## Step 1: Register a new agent in the portal Sign in to `portal.wasviking.com`. In the left sidebar, under **WASVIKING SENTINEL**, open **Sentinel Agents** and click **Add Sentinel Agent**. Set the **Agent display name**: a human-friendly label shown in the portal, for example `London - DMZ Scanner 01`. It is not the machine hostname, must be unique within your organization, and is renameable later. Click **Create**. The portal issues a **bootstrap token**, displayed **once**. Copy it and keep it safe. If you lose it, revoke this agent record and create a new one. ## Step 2: Download the package Click **Get Agent** at the top of the Sentinel Agents page. The portal serves the right package for the platform you select. Verify the download before installing. The **Verify download (SHA256 & signature)** action on the modal gives you the expected checksum and the signature to validate the artifact has not been tampered with. ## Step 3: Install and enable the service On Ubuntu / Debian: ```bash sudo dpkg -i wasviking-sentinel__amd64.deb sudo systemctl enable --now wasviking-sentinel # Replace with the release version printed in the portal. ``` The package installs the agent under `/opt/wasviking-sentinel/` and registers a systemd unit named `wasviking-sentinel`. At this point the service is enabled but waits for registration to provide its certificates. ## Step 4: Register and start the agent Register the agent once with the bootstrap token from Step 1. The agent securely downloads its client certificates and writes its configuration file. ```bash # Step A: Register this agent (replace with your token from the portal) cd /opt/wasviking-sentinel ./wasviking-sentinel register --token # What happens during registration: # - Client certificates are securely downloaded # - Configuration is saved to: # /opt/wasviking-sentinel/configs/config.yaml # Step B: Start and verify the service sudo systemctl start wasviking-sentinel sudo systemctl status wasviking-sentinel ``` In CI or scripts, read the token from an environment variable instead of pasting it on the command line: ```bash export WASVIKING_BOOTSTRAP_TOKEN="" ./wasviking-sentinel register --token "$WASVIKING_BOOTSTRAP_TOKEN" ``` Registration output: ``` Registering with WASViking API... Certificates downloaded successfully into /opt/wasviking-sentinel/certs/ Config written to /opt/wasviking-sentinel/configs/config.yaml ``` The **private key never leaves the host**. The CA certificate, the client certificate, and the client key are written under `/opt/wasviking-sentinel/certs/`: ```bash ls -l /opt/wasviking-sentinel/certs # -rw-r--r-- ca.crt # -rw-r--r-- client.crt # -rw------- client.key ``` After a successful registration and start, the agent connects to the WASViking cloud automatically and appears as **Active** in your portal within a few seconds. ## The config file `register` writes `/opt/wasviking-sentinel/configs/config.yaml`: ```yaml grpc_server: sentinel.wasviking.com agent_hostname: dmz-scanner-01 agent_id: "" use_tls: true tls_ca_cert: certs/ca.crt tls_cert: certs/client.crt tls_key: certs/client.key use_mtls: true log_path: logs/sentinel_agent.log ``` `agent_id` is empty until the first run. The service populates it automatically and persists it back to the file. You do not need to edit this file by hand in the common case. ```yaml # After the first run, agent_id is filled in: agent_id: 56b3bd08aa4d5201edafd434740d9c140c1e477742d47360d71a76842b09ec21 # auto-generated on first run, do not edit ``` ## Agent status In **Sentinel Agents**, each agent shows one of: | Status | Meaning | |---|---| | **Active** | Registered, tunnel connected, heartbeat current. | | **Pending** | Registered but has not connected yet. | | **Offline** | Was Active, no recent heartbeat. | | **Revoked** | Certificate invalidated; tunnel refused. | Counters at the top of the page summarize the fleet: `Total · Active · Pending · Offline · Revoked`. ### If the agent stays Pending If an agent remains **Pending** for more than a few minutes, check these common causes: - Outbound connectivity on port 443 is allowed. - No TLS interception or proxy blocks HTTPS to `sentinel.wasviking.com`. - System clock is synchronized (NTP). Certificate validation fails on a skewed clock. - Client and CA certificates are present and valid under `/opt/wasviking-sentinel/certs/`. ## How the portal lists agents | Column | Meaning | |---|---| | Name | The display name set on creation. Editable in the row actions. | | Hostname | The host the agent reports. | | Address | The egress address the WASViking side observes. | | Status | Active / Pending / Offline / Revoked. | | Last Seen | Time of the last heartbeat. | | Actions | Rename, rotate certificate, revoke. | ## Trigger your first internal scan With the agent **Active**, create a target whose URL resolves only inside your network. On the New Scan screen, the agent appears in the routing selector (Execution Mode). Pick it, configure auth if needed, and start the scan. Every analyzer routes its HTTP probes through the tunnel. See [Internal scanning](https://docs.wasviking.com/sentinel/internal-scanning/) for the full walkthrough. ## Certificate rotation and revocation Both are operator actions in the portal, no host change needed. - **Rotate.** From the agent's row actions, click **Rotate certificate**. The portal issues a new certificate set; the agent picks it up on the next reconnect. The previous certificate is immediately revoked WASViking-side. - **Revoke.** Click **Revoke**. The certificate is invalidated immediately; the agent's status flips to `Revoked` and the tunnel drops. The host record stays in the list for audit. The client certificate also auto-rotates before expiry as defense-in-depth. ## Upgrade Download the new package from **Get Agent**, install it over the existing one, and restart the service. The `certs/` and `configs/config.yaml` are preserved across upgrades. ```bash sudo dpkg -i wasviking-sentinel__amd64.deb sudo systemctl restart wasviking-sentinel ``` ## Uninstall ```bash sudo systemctl disable --now wasviking-sentinel sudo dpkg -r wasviking-sentinel sudo rm -rf /opt/wasviking-sentinel ``` In the portal, **Revoke** the agent record so the certificate's CN is no longer accepted on the WASViking side. ## Troubleshooting | Symptom | Likely cause | |---|---| | `register` returns 401 | Bootstrap token already consumed or expired. Create a new agent in the portal. | | Agent stays `Pending` | See the four common causes above (port 443, TLS interception, NTP, certificates). | | `Peer certificate verification failed` | `certs/ca.crt` is stale. Rotate from the portal and re-run `register`. | | `403` from gRPC after some time | The agent was revoked in the portal. Create a new one. | | Tunnel keeps dropping | A proxy enforces an idle timeout. Confirm egress to `sentinel.wasviking.com:443` and any HTTP/2-aware proxy in the path. | --- # Internal scanning Section: Sentinel agent Source: https://docs.wasviking.com/sentinel/internal-scanning/ Summary: Run a WASViking scan against an internal target through the agent tunnel. Once a Sentinel agent is online, scanning an internal application looks identical to scanning an external one. The same scan profiles, the same analyzers, the same finding format. The transport is the only thing that changes. ## Create an internal target In the portal, **Targets → New target**: | Field | Value | |---|---| | URL | `https://internal-app.corp` (whatever resolves inside your network) | | Routing | Pick an **online Sentinel agent** instead of "Cloud egress" | | Subdomain coverage | `single host` is the safest default for internal targets | | Scan profile | Pick the one that matches the application | Internal targets cannot route through the WASViking cloud egress. The Routing field enforces this. ## Authentication Internal apps usually require login. Pick the auth mode that matches your stack: - **Form Login** with AI Autofill works for most server-rendered apps. - **Bearer** is right for internal REST APIs with OIDC. - **Cookie** is the escape hatch for opaque session systems. Credentials are encrypted at rest in the WASViking control plane. They travel to the agent encrypted, are used in memory, and never written to disk on the host. ## What happens during the scan 1. The portal queues the scan in Redis. 2. The gRPC server hands the job to the right agent (matched on org binding and online state). 3. The agent receives the scan plan, opens the requested probes, and streams responses back to the cloud-side analyzers via the tunnel. 4. Analyzers process the data on the WASViking side. Findings are written to MongoDB, an Evidence record links the agent + host. 5. The AI Recommendation runs on the cloud side once the engine verdict is available. The agent does not run analyzer logic locally for DAST. It is a transport. This keeps the analyzer engine consistent regardless of internal vs external. ## Finding format Findings produced through a Sentinel agent are indistinguishable from external findings in the portal, with one exception: each carries a `source.agent_id` and `source.agent_host` field. Filter the Findings page by agent to see only what came through a given host. ## Limits and guardrails - **One concurrent scan per agent** by default. Configurable per agent. Heavy hosts can run 2 to 4 in parallel. - **Bandwidth.** The tunnel uses HTTP/2; analyzer probes are small but numerous. A 1 Mbps link is enough; a 10 Mbps link is comfortable. - **Scan timeout.** 12 hours hard ceiling per scan (stale-scan cleanup). Most internal scans finish in 20 to 45 minutes. ## Restricting agent reach For sensitive networks, restrict what the agent can reach with host-side firewall rules. The agent honors these and reports unreachable hosts as discovery failures in the scan log. A typical pattern: - Allow the agent to reach the application subnet. - Block the agent from reaching the management network. - Block egress from the agent to anything other than the WASViking gRPC endpoint. ## Multiple agents You can install multiple agents per organization. Use this to: - Cover network segments that cannot reach each other. - Pair an agent per data center / cloud region. - Isolate scanning load from production-critical agents. The portal shows all agents and their state. Target routing picks one. ## What it does not do Sentinel does not scan the host it runs on. The agent is a transport for DAST; it does not implement host-based vulnerability scanning, endpoint protection, or network discovery. For SBOM and secrets on the host, see the next pages in this section. If a scan stays queued or the tunnel drops, see the troubleshooting section of [Installing the Sentinel agent](https://docs.wasviking.com/sentinel/installation/). --- # wasviking-sentinel sbom Section: Sentinel agent Source: https://docs.wasviking.com/sentinel/sentinel-sbom/ Summary: Generate a CycloneDX 1.5 SBOM on-premises, enrich with OSV and CISA KEV, and submit to your tenant. `wasviking-sentinel sbom` walks build manifests on the host and produces a CycloneDX 1.5 SBOM, enriched with OSV.dev advisories and CISA KEV flags. The output can be written locally or submitted to your WASViking® tenant. ## Supported manifests | Ecosystem | Manifest files | |---|---| | npm | `package-lock.json`, `npm-shrinkwrap.json` | | yarn | `yarn.lock` | | pnpm | `pnpm-lock.yaml` | | Python | `requirements.txt`, `Pipfile.lock`, `poetry.lock` | | Go | `go.sum`, `go.mod` | | PHP | `composer.lock` | | Java | `pom.xml`, `gradle.lockfile` | | Ruby | `Gemfile.lock` | | Dart / Flutter | `pubspec.lock` | The walker reads from a directory you pass in. It does not require the language toolchain to be installed; it parses the lock format directly. ## License check (preflight) Before any local work, `sbom` calls the WASViking preflight endpoint to confirm the organization API key is active. This is required even when you are not submitting results. - `--api-key` (or env `WASV_API_KEY`) is **required**. Missing or empty refuses with exit 1. - The check is `POST /api/v1/sentinel/preflight`. Any active org API key passes; no specific scope is needed for the preflight itself. - Result cached at `~/.wasviking/preflight_cache.json` (mode `0600`) for 30 minutes by default (TTL from server, clamped 60s..6h). - Within TTL, subsequent calls skip the network entirely. - If the API is unreachable but a recent successful approval is on disk (within a 24-hour grace window), the run continues. Short WASViking outages do not break customer CI. - If the API actively rejects the key (401 / 403), the grace window does **not** apply. Revoked keys block on the next cache expiration. - The cache is keyed by a truncated SHA-256 of the API key, so rotating the key invalidates the cache automatically. > The `--submit` flag is independent. `--api-key` is required regardless > of whether you submit the SBOM. ## Basic usage From the directory containing the source tree: ```bash export WASV_API_KEY="wv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" wasviking-sentinel sbom --path . ``` The default writes two files into `--out` (default `.`): - `wasviking-sbom.cdx.json`: CycloneDX 1.5 document. - `wasviking-sbom.sarif`: SARIF report of the vulnerable components, for direct ingestion in IDE / code scanning tooling that consumes SARIF. ## Submit to your tenant ```bash wasviking-sentinel sbom \ --path . \ --app-name checkout-api \ --app-version "$CI_COMMIT_TAG" \ --submit \ --api-key "$WASV_API_KEY" ``` The agent posts to `POST /api/v1/sentinel/sbom/submit`. The submission carries: - The CycloneDX 1.5 document. - App name and version (from `--app-name` / `--app-version` or auto- detected from the manifests). - OSV and KEV enrichment. - The hostname of the machine that produced it. In the portal, the submission lands at **Inventory → SBOM** and feeds the [Supply Chain Watch](https://docs.wasviking.com/concepts/findings-and-risk-score/). ## Flags reference | Flag | Purpose | Default | |---|---|---| | `--path` | Directory to scan for manifests, recursive. | `.` | | `--out` | Directory to write `wasviking-sbom.cdx.json` and `wasviking-sbom.sarif`. | `.` | | `--app-name` | Project name embedded in the BOM `metadata.component`. | (auto-detected) | | `--app-version` | Project version embedded in the BOM `metadata.component`. | (auto-detected) | | `--fail-on` | Severity threshold: `critical`, `high`, `medium`, `low`, `none`. | `high` | | `--no-osv` | Skip OSV.dev enrichment (ships a bare SBOM). | `false` | | `--air-gapped` | Guarantee no external HTTP; uses the bundled KEV seed only. | `false` | | `--submit` | POST the SBOM to the WASViking API after generation. | `false` | | `--api` | WASViking API base URL. Env: `WASV_API`. | `https://api.wasviking.com` | | `--api-key` | Organization API key. **Required** for every run (preflight). Add the `sca:submit` scope on the key if you also use `--submit`. Env: `WASV_API_KEY`. | (required) | | `--timeout` | Max wall-clock time for the SBOM pipeline. | `5m0s` | > **Air-gapped runs.** With `--air-gapped`, the SBOM uses a CISA KEV > snapshot bundled inside the binary. No external HTTP is performed. > The KEV snapshot moves forward with each release; refresh the binary > to refresh the snapshot. ## Determinism Two runs over the same source tree produce SBOMs that diff cleanly. The walker: - Sorts components by `purl` before output. - Pins enrichment timestamps to the run start. - Normalizes version strings per ecosystem rules. This matters for SCA gate decisions and for diff'ing across builds. ## Exit codes | Exit code | Meaning | |---|---| | 0 | SBOM produced, submitted if requested, nothing at or above `--fail-on`. | | 1 | Generic failure (parsing error, IO, network). | | 2 | Invalid argument, or `--submit` without an API key. | | 70 | KEV-flagged finding at or above `--fail-on`. Known exploited, treat as urgent. | | 71 | Findings at or above `--fail-on`, none of them in KEV. | | 79 | Coverage failure: the root could not be traversed, so nothing was scanned. Never returned for a project that is simply empty or has no supported manifest. See [Coverage failures](https://docs.wasviking.com/sentinel/sentinel-ci/#coverage-failures-exit-79). | Two conditions deliberately do **not** fail the build: a failed OSV or KEV enrichment, and a failed submission. Both print a warning, the artifacts stay on disk, and the gate is decided on what the run could actually see. A broken network should not block a merge on its own. `sbom` is itself usable as a CI gate via `--fail-on`. For the higher- level wrapper that also runs the secrets and template-driven scans in one pass, see [wasviking-sentinel ci](https://docs.wasviking.com/sentinel/sentinel-ci/). ## What this is not This is not a runtime scanner. It reads lockfiles, not running processes. For application-layer component detection from outside, the platform ships cloud-side component detection (N1 layer) and pairs it with this premise-side N2 layer for the full picture. --- # wasviking-sentinel secrets Section: Sentinel agent Source: https://docs.wasviking.com/sentinel/sentinel-secrets/ Summary: Find leaked secrets on disk and in git history, optionally verify them live, with raw secrets never leaving the host. `wasviking-sentinel secrets` runs locally on the host, walks the filesystem and optionally the git history, and reports leaked credentials. The key property: **raw secrets never leave the host**. Only a SHA-256 hash and a masked preview reach WASViking®. OWASP coverage: A07 (Identification and Authentication Failures), CWE-798 (Use of Hard-coded Credentials). ## What it detects The detector catalog ships with 32 patterns, including: | Provider | What is matched | |---|---| | AWS | Access key ID + secret access key pairs, session tokens. | | GitHub | Personal access tokens (classic and fine-grained), app installation tokens. | | GitLab | Personal access tokens, runner registration tokens. | | Stripe | Live and test API keys (sk_live, sk_test). | | SendGrid | API keys. | | Slack | Bot tokens, user tokens, webhook URLs. | | PagerDuty | Service integration keys. | | Twilio | Account SIDs + auth tokens. | | Database | Postgres / MySQL / MongoDB / Redis connection URIs with credentials. | | Generic | RSA / EC / OpenSSH private keys. | | Cloud providers | GCP service account JSON, Azure connection strings. | The full detector catalog is part of the binary. New detectors land per release. ## What it does NOT match To suppress noise: - Test fixtures and well-known dummy values (`aws_secret_key_AKIA…EXAMPLE`). - Documentation placeholders with `EXAMPLE`, `PLACEHOLDER`, `XXXX`. - Files in common doc/test paths (`/docs/`, `/__tests__/`, `*.test.{js,ts,py}`). - Patterns flagged by the AI classifier as obvious placeholder text. ## License check (preflight) Before any local work, `secrets` calls the WASViking preflight endpoint to confirm the organization API key is active. This is required even when you are not submitting results. - `--api-key` (or env `WASV_API_KEY`) is **required**. Missing or empty refuses with exit 1. - The check is `POST /api/v1/sentinel/preflight`. Any active org API key passes; no specific scope is needed for the preflight itself. - Result cached at `~/.wasviking/preflight_cache.json` (mode `0600`) for 30 minutes by default (TTL from server, clamped 60s..6h). - Within TTL, subsequent calls skip the network entirely. - If the API is unreachable but a recent successful approval is on disk (within a 24-hour grace window), the run continues. Short WASViking outages do not break customer CI. - If the API actively rejects the key (401 / 403), the grace window does **not** apply. Revoked keys block on the next cache expiration. - The cache is keyed by a truncated SHA-256 of the API key, so rotating the key invalidates the cache automatically. > The `--submit` flag is independent. `--api-key` is required regardless > of whether you submit results. ## Basic usage From the directory containing the source tree: ```bash export WASV_API_KEY="wv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" wasviking-sentinel secrets --path . ``` ``` [sentinel] Walking . [sentinel] Files scanned: 5,142 [sentinel] Secrets detected: 4 [sentinel] aws_secret_key src/config/prod.py:18 unverified [sentinel] github_pat scripts/deploy.sh:5 unverified [sentinel] stripe_sk_live src/billing/keys.py:3 unverified [sentinel] slack_webhook src/alerts/post.go:9 unverified ``` Report artifacts are written into `--out` (default `.`). ## Live verification 10 detectors support optional live verification. The agent probes the legitimate provider endpoint to check whether the secret is currently valid. ```bash wasviking-sentinel secrets --path . --verify ``` ``` [sentinel] aws_secret_key src/config/prod.py:18 VERIFIED LIVE [sentinel] stripe_sk_live src/billing/keys.py:3 VERIFIED LIVE [sentinel] github_pat scripts/deploy.sh:5 invalid (404 from provider) [sentinel] slack_webhook src/alerts/post.go:9 no verifier available ``` Live verification: - Uses read-only provider identity endpoints where possible. - Never modifies state on the remote provider. - Logs the verifier endpoint in the agent log so an auditor can see exactly what was probed. ## Privacy guarantee The submission payload to WASViking contains: - Detector ID and severity. - File path and line number. - A **SHA-256 hash** of the raw secret. - A **masked preview** (`AKIA••••••••••••••••XYZ7`). - Live verification result (boolean). The raw secret is held in memory on the host only long enough to verify it (if requested) and is then discarded. It is never written to any WASViking system. ## Git history ```bash wasviking-sentinel secrets --path . --git ``` `--git` walks the repository's git history in addition to the working tree. Useful for finding secrets that were committed and then "removed" by a later commit but still live in history. Slower than a working-tree scan; runs against all reachable commits in the local repository. ## Submit to your tenant ```bash wasviking-sentinel secrets \ --path . \ --submit \ --api-key "$WASV_API_KEY" ``` Findings land in the portal under **Application Security → Hard-coded Secrets** as Findings with category `token_exposure` or `credential_exposure`. They feed the Risk Score and the Findings workflow. ## Flags reference | Flag | Purpose | Default | |---|---|---| | `--path` | Directory to scan, recursive. | `.` | | `--out` | Directory to write report artifacts. | `.` | | `--fail-on` | Severity threshold: `critical`, `high`, `medium`, `low`, `none`. | `high` | | `--git` | Also walk the repository git history (slower). | `false` | | `--verify` | Call provider identity endpoints to confirm matches are live (read-only). | `false` | | `--submit` | POST the matches to the WASViking API after scanning. | `false` | | `--api` | WASViking API base URL. Env: `WASV_API`. | `https://api.wasviking.com` | | `--api-key` | Organization API key. **Required** for every run (preflight). Add the `secrets:submit` scope on the key if you also use `--submit`. Env: `WASV_API_KEY`. | (required) | | `--timeout` | Max wall-clock time for the secrets pipeline. | `10m0s` | ## Exit codes | Exit code | Meaning | |---|---| | 0 | OK. Nothing at or above `--fail-on`. | | 1 | Generic failure (IO, network, parse). | | 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: the root could not be traversed, so nothing was scanned. Never returned for a project that is simply empty. See [Coverage failures](https://docs.wasviking.com/sentinel/sentinel-ci/#coverage-failures-exit-79). | The split between 73 and 74 exists so a pipeline can treat a confirmed live credential as an incident and an unverified match as a review item, without parsing the log. `secrets` is itself usable as a CI gate via `--fail-on`. For the higher- level wrapper that combines `secrets` + `sbom` + template-driven scans in a single pass, see [wasviking-sentinel ci](https://docs.wasviking.com/sentinel/sentinel-ci/). ## CI integration See [wasviking-sentinel ci](https://docs.wasviking.com/sentinel/sentinel-ci/) for the CI/CD gate wrapper. The standalone `secrets` subcommand is fine for pipelines that only need this gate. --- # wasviking-sentinel mobile Section: Sentinel agent Source: https://docs.wasviking.com/sentinel/sentinel-mobile/ Summary: Submit an Android or iOS application package from any pipeline for a static assessment against OWASP MASVS and MASTG, with a baseline diff and deterministic exit codes. `wasviking-sentinel mobile` submits a compiled application package to the WASViking® Mobile Security Assessment, waits for the verdict, writes SARIF and JSON, and fails the build on findings above a threshold you choose. It is the file-based counterpart of the cloud `scan`: there is no mTLS tunnel and no running app to reach, because the artefact is the input. For a full GitHub Actions walkthrough, see [CI/CD Mobile Security with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-mobile/). This page is the command reference for any pipeline. ## Supported packages | Format | Platform | |---|---| | `.apk` | Android | | `.aab` | Android App Bundle | | `.xapk`, `.apks` | Android split-package containers | | `.ipa` | iOS | The format is decided by reading the container, not the file extension. A file that is not a recognised application package is refused at upload. ## License check (preflight) Before any work, `mobile` calls the WASViking preflight endpoint to confirm the organization API key is active. - `--api-key` (or env `WASV_API_KEY`) is **required**. - The check is `POST /api/v1/sentinel/preflight`. Any active org API key passes. - The result is cached briefly on disk, so repeated runs skip the network. - A short WASViking outage does not break your pipeline, but an actively rejected key (401 / 403) blocks immediately. ## Scope The API key needs the **`mobile:scan`** scope. The same key also downloads the agent from `install.sh`, so a mobile-only pipeline does not need any other scope. ## How it runs 1. The agent asks the API to authorise one upload and receives a short-lived, single-purpose authorisation for one object. 2. The package is uploaded straight to secure object storage. The bytes do not pass through the API or a CDN, so package size is not capped by a proxy. 3. The API verifies the stored object, confirms it is a real application package, and queues the assessment. 4. The agent polls until the assessment reaches a terminal state, then pulls the SARIF. ## Basic usage ```bash export WASV_API_KEY="wv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" wasviking-sentinel mobile --file app-release.apk ``` Two files are written into `--out` (default `.`): - `wasviking-mobile.sarif`: SARIF 2.1.0, for GitHub Code Scanning, GitLab, and any SARIF-aware tooling. - `wasviking-mobile.json`: the run summary with severity counts, risk score, and build provenance. ## Flags | Flag | Required | Description | |---|---|---| | `--file` | Yes | Path to the application package. | | `--api-key` | Yes | API Key with `mobile:scan`. Prefer env `WASV_API_KEY`. | | `--label` | No | Label stored with the assessment (release name, commit). | | `--fail-on` | No | `critical`, `high`, `medium`, `low`, or `none`. "Or above" logic. Default: `critical`. | | `--baseline` | No | `new` (only findings absent from the previous assessment of the same app) or `all`. Default: `all`. | | `--out` | No | Output directory. Default: current directory. | | `--timeout` | No | Budget for upload plus analysis. Default: `40m`. | | `--api` | No | API base URL. Default: `https://api.wasviking.com` (env `WASV_API`). | ## Baseline diff The baseline is the previous completed assessment of the same application (same platform and package identifier), from a pipeline or a manual upload. The comparison is by finding identity, so a version bump does not reset it. With `--baseline new`, the build fails only on findings the release introduces, which keeps pull requests free of friction from inherited debt. The SARIF marks each result as new, existing, or fixed. ## Exit codes | Code | Meaning | |---|---| | `0` | Nothing at or above the threshold (given the baseline). | | `1` | Findings exceed the threshold. Blocks the merge. | | `2` | Operational error (missing package, bad arguments, auth or upload failure). | ## Build provenance The command reads the standard CI environment variables and records the provider, repository, branch, commit, run, and actor with the assessment, with no extra flags. GitHub Actions, GitLab CI, Bitbucket Pipelines, and CircleCI are recognised automatically. The portal shows this next to the run, tagged with a **CI** badge, and the same context rides in the SARIF. ## Where results land Pipeline assessments appear alongside manual uploads under **Mobile Security → Assessments**. The capability itself is documented at [Mobile Security Assessment](https://docs.wasviking.com/capabilities/mobile-security/). --- # wasviking-sentinel in CI/CD Section: Sentinel agent Source: https://docs.wasviking.com/sentinel/sentinel-ci/ Summary: 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: - [wasviking-sentinel sbom](https://docs.wasviking.com/sentinel/sentinel-sbom/) - [wasviking-sentinel secrets](https://docs.wasviking.com/sentinel/sentinel-secrets/) - (scan subcommand reference: see Cloud DAST below) For ready-to-paste recipes, see [CI/CD DAST with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-dast/), [CI/CD SCA, SBOM & Secrets with GitHub Actions](https://docs.wasviking.com/getting-started/github-actions-sca-sbom/), and [CI/CD SCA, SBOM & Secrets with Bitbucket Pipelines](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/). ## Quick start (GitHub Actions) ```yaml # .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](https://docs.wasviking.com/getting-started/bitbucket-pipelines-sca-sbom/)), GitLab CI, CircleCI, Jenkins, and Buildkite. Any Linux runner that can execute the binary works. ## Authentication ```bash 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](https://docs.wasviking.com/sentinel/sentinel-sbom/#license-check-preflight). ## SCA gate (`sbom`) ```bash ./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](https://docs.wasviking.com/sentinel/sentinel-sbom/). ## Secrets gate (`secrets`) ```bash ./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](https://docs.wasviking.com/sentinel/sentinel-secrets/). ## Cloud DAST gate (`scan`) ```bash ./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`. --- # Notification Channels Section: Integrations Source: https://docs.wasviking.com/integrations/notification-channels/ Summary: The central place to configure automated security alert delivery across Slack, Microsoft Teams, API Webhook, and Email, with a shared event subscription model. **Notification Channels** is the central hub for automated security alerts. It lives at **Alerts → Notification Channels** in the portal. > Manage the channels used to send automated security alerts. Four channel types ship by default. Each is configured independently and subscribes to the same event taxonomy, so you can route different event classes to different destinations. ## The four channels | Channel | Delivery | |---|---| | **Slack** | Incoming webhook to a Slack workspace, optional channel override. | | **Microsoft Teams** | Incoming webhook to a Teams channel, optional channel override. | | **API Webhook** | Signed JSON to any HTTPS endpoint, optional Authorization header. | | **Email** | One or more recipient email addresses. | ## Channel lifecycle Each channel card has three actions: | Action | What it does | |---|---| | **Configure** | Open the channel modal to set the destination and the event subscriptions. | | **Test** | Send a synthetic alert to confirm delivery before relying on it. Available once configured. | | **Enable** | Activate the channel. A configured-but-disabled channel keeps its settings but does not deliver. | A channel shows **Not Configured** until you complete the Configure step. ## Event taxonomy Every channel subscribes to the same set of events. Check the events you want delivered to that channel. The tag in brackets is the category label shown in the portal. | Event | Tag | Fires when | |---|---|---| | Scan Result | `Scan` | A scan completes (API Webhook channel). | | New subdomain | `New subdomain` | Subdomain monitoring records a new subdomain. | | New subdomain | `Discovery` | The discovery pipeline surfaces a new asset. | | Sensitive Port | `Security` | A monitored sensitive port is detected open. | | SSL Expiration | `SSL` | A monitored certificate crosses an expiry threshold. | | Supply Chain Advisory | `Supply Chain` | Continuous Watch matches an advisory to a live SBOM. | | Edge Threat Intelligence | `Edge Intel` | Edge Threat Radar raises a correlated event above the alert threshold. | | Credential Exposure | `Credential Exposure` | Exposure Intelligence matches a leaked credential to a monitored domain. | | Finding Activity | `Findings` | A DAST scan or a CI submission creates, reopens or escalates findings. One alert per execution; repeated detections of known findings never alert. | | SAST Findings | `SAST` | The source code review of a connected repository (access control and AI / LLM security) creates, reopens or escalates findings. One alert per scan, with the split by review family. Dependency and secret activity of the same scan stays under Finding Activity. | Route events deliberately. A common pattern: | Channel | Subscribed events | |---|---| | `#sec-ops` Slack | Everything. | | `#sec-criticals` Slack | Credential Exposure, Sensitive Port. | | Email to the on-call DL | SSL Expiration, Supply Chain Advisory. | | SIEM API Webhook | Scan Result, Edge Threat Intelligence, Credential Exposure, Supply Chain Advisory. | ## Per-channel configuration ### Slack | Field | Notes | |---|---| | Webhook URL | The Slack incoming webhook URL. Masked; click **Show** to reveal. | | Channel (optional) | Override the channel the webhook posts to. | | Events to notify | Event subscription checkboxes. | See [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) for the workspace setup. ### Microsoft Teams | Field | Notes | |---|---| | Webhook URL | The Teams incoming webhook URL. Masked; click **Show**. | | Channel (optional) | Override the channel. | | Events to notify | Event subscription checkboxes. | ### API Webhook | Field | Notes | |---|---| | Webhook Endpoint URL | Any HTTPS endpoint, e.g., `https://example.com/webhook`. | | Authorization Header (optional) | A scheme dropdown (`Bearer`) plus a token. Sent on every delivery so your endpoint can authenticate WASViking. | | Events to notify | Includes **Scan Result**, which the chat channels do not. | Deliveries are signed. See [Webhooks](https://docs.wasviking.com/integrations/webhooks/) for the payload schema and signature verification, and [SIEM](https://docs.wasviking.com/integrations/siem/) for SIEM-specific receivers. ### Email | Field | Notes | |---|---| | Recipient Email(s) | One or more addresses, comma-separated (`example@domain.com, team@domain.com`). | | Events to notify | Event subscription checkboxes. | Email is routed through the canonical email pipeline (audited delivery), not raw SMTP. ## Test before you rely on it Every channel modal has a **Test Integration** button. Use it after Configure and after any subscription change. The test sends a synthetic payload shaped like a real event so your receiver logic (Slack format, webhook signature verification, email filters) actually runs against it. ## Smart re-notify Supply chain and edge correlation events re-fire only on meaningful state changes (KEV bump, severity escalation, fix availability for supply chain; threshold crossing for edge). This keeps the channel signal-only. See [Supply Chain Intel](https://docs.wasviking.com/capabilities/supply-chain-intel/) and [Edge Threat Radar](https://docs.wasviking.com/capabilities/edge-threat-radar/). ## Where it lives in the portal - **Alerts → Notification Channels**: configure, test, enable the four channels. - **Alerts → History**: delivery history per channel. - **Settings → System Settings → Notifications & Alerts**: global thresholds (sensitive ports, SSL expiry advance windows, edge alert rules). See [Sensitive Port Monitoring](https://docs.wasviking.com/capabilities/sensitive-port-monitoring/), [Certificate Monitoring](https://docs.wasviking.com/capabilities/certificate-monitoring/), and [Edge Threat Radar](https://docs.wasviking.com/capabilities/edge-threat-radar/#alert-rules) for what each threshold controls. ## Deep dives - [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) - [Webhooks](https://docs.wasviking.com/integrations/webhooks/) - [SIEM](https://docs.wasviking.com/integrations/siem/) --- # Slack and Teams Section: Integrations Source: https://docs.wasviking.com/integrations/slack-teams/ Summary: Route findings, SLA breaches, and supply chain alerts to your team's channel. WASViking® routes alerts to Slack and Microsoft Teams with the same event model used by webhooks. The integration is per organization; multiple channels per org are supported. ## What gets sent By default, the following events route to Slack / Teams: - `finding.escalated` - `finding.sla_breached` - `secret.verified_live` - `sbom.intel_match` You can subscribe a channel to any subset. ## Slack setup 1. In WASViking: **Integrations → Slack → Connect**. 2. Authorize the WASViking app in your Slack workspace. 3. Pick the default channel for alerts (you can override per subscription). 4. Choose the event subscription set. The WASViking Slack app requests: - `chat:write` to post messages. - `chat:write.public` to post to public channels without an invite. - `incoming-webhook` for legacy webhook delivery (optional). ## Teams setup Microsoft Teams uses an Incoming Webhook in the target channel: 1. In Teams: configure an Incoming Webhook for the channel you want alerts in. Copy the webhook URL. 2. In WASViking: **Integrations → Teams → Connect**, paste the URL, pick the event subscription set. The format is Adaptive Card v1.4. Themes adapt to dark and light. ## Smart re-notify Supply chain alerts (`sbom.intel_match`) only re-notify the channel when: - A component is newly listed as KEV-exploited (KEV bump). - A component's severity bumps. - A fix version is now available. Other intel updates land silently in the inventory. This keeps the channel signal, not background noise. The same rule applies to `finding.escalated`: a finding only re-notifies on a meaningful risk change, not on every dashboard refresh. ## Per-channel routing Route different event sets to different channels: | Channel | Event subscription | |---|---| | `#sec-ops` | Everything. | | `#sec-criticals` | `finding.sla_breached`, `secret.verified_live`. | | `#sbom-watch` | `sbom.intel_match`, `bundle.accessed`. | | `#exec-summary` | `finding.escalated` (critical only), weekly digest. | Per-channel subscriptions are configured under **Integrations → Slack → Channels** or **Integrations → Teams → Channels**. ## Quiet hours Configure quiet hours per integration: events captured during the window are queued and delivered at the end of the window in a single digest message. Useful for nights and weekends without dropping signal. Quiet hours apply per organization timezone (Settings → Organization). ## Test delivery Each integration ships a `Test` button that delivers a synthetic alert to the channel. Use it after connecting and after any subscription change. ## Scope of this integration Alert routing is one-way: WASViking posts events to your channel. You cannot transition a finding's status from a Slack message. For inbound automation such as transitioning findings use the public REST API or the webhook events directly. To start a scan from Slack with a slash command and get the results posted back, see [Run scans from Slack](https://docs.wasviking.com/integrations/run-scans-from-slack/). That is a separate integration, configured under **Settings** rather than **Alerts**. --- # Run scans from Slack Section: Integrations Source: https://docs.wasviking.com/integrations/run-scans-from-slack/ Summary: Connect your Slack workspace and start a scan from any channel with /wasviking scan, then get the results posted back when the scan finishes. WASViking® can run a scan from inside Slack. Once your workspace is connected, anyone in it can type a slash command in a channel to start a scan and the results are posted back to that same channel when the scan finishes. ``` /wasviking scan https://app.yoursite.com ``` This is a separate integration from alert routing. Alert routing (findings, SLA breaches, supply chain advisories) is configured under **Alerts** and described in [Notification Channels](https://docs.wasviking.com/integrations/notification-channels/) and [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/). The slash command described here is configured under **Settings**. ## Before you start You need three things: - A target already registered in WASViking. The slash command will only scan a domain or subdomain that exists in **Scans → Targets**. See [Targets and assets](https://docs.wasviking.com/concepts/targets-and-assets/) and [Your first scan](https://docs.wasviking.com/getting-started/first-scan/) for how to add one. - A role that can edit integrations. The **Connect to Slack** button is available to roles with the integrations permission (Org Admin by default). Other roles see the button disabled with a note explaining which role is required. - Slack workspace administrator rights, which Slack requires to install an app and approve the requested permissions. ## Connect your workspace 1. In the portal, go to **Settings → System Settings → Slack**. 2. The panel shows **Not connected**. Select **Connect to Slack**. 3. Slack opens its authorization screen. Review the permissions WASViking requests and approve them for your workspace. 4. Slack returns you to the portal. The panel now shows **Connected to** your workspace name, the workspace ID, and the date the connection was made. One Slack workspace maps to one WASViking organization. ### Permissions WASViking requests | Permission | Why it is needed | |---|---| | `commands` | Register and receive the `/wasviking` slash command. | | `chat:write` | Post scan results back to the channel. | | `channels:read` | Read basic channel information for routing. | WASViking does not read your channel history or message content. The app posts only in response to a `/wasviking` command and only to the channel where the command was run. ## Register the target first A scan from Slack runs against an existing target. If the domain or subdomain in your command is not registered, the scan does not start and Slack replies that the target is not in WASViking, with a link to add it. This is intentional. Targets carry the authorization, scope, and scan configuration for a host. Requiring the target up front keeps Slack a trigger, not a way to scan arbitrary hosts. Add the target once in **Scans → Targets**, then scan it from Slack as often as you need. To check what is available without leaving Slack, run `/wasviking targets` to list the targets registered for your organization. ## Commands | Command | What it does | |---|---| | `/wasviking scan ` | Start a scan on a registered target. | | `/wasviking targets` | List the targets registered for your organization. | | `/wasviking help` | Show the available commands and usage. | ### Examples ``` /wasviking scan https://app.company.com /wasviking scan app.company.com /wasviking targets /wasviking help ``` You can pass the URL with or without the `https://` prefix, and Slack's automatic link formatting is handled for you. The host has to resolve to a target you have already registered. Command replies are private to the person who ran the command. The final scan results are posted to the channel so the rest of the team can see them. ## What happens after you run a scan 1. WASViking confirms the scan has started and notes that it can take up to about fifteen minutes. 2. The scan runs with the configuration saved on that target, the same engine used for scans started from the portal. 3. When the scan finishes, WASViking posts a result message to the channel with: - the target that was scanned, - a count of findings by severity (High, Medium, Low, Info), - a button to open the full report in the portal, - a button to download a PDF report, available for a limited time. If the scan fails or is canceled, the channel gets a short message saying so instead of a severity summary. ## Replies you may see | Situation | Reply | |---|---| | Target not registered | The target is not in WASViking, with a link to add it under Targets, then run the scan again. | | Hourly limit reached | The scan limit for your organization has been reached. Wait before starting another. | | URL not understood | A note asking for a full URL such as `https://app.company.com`. | | Workspace not connected | A note that the workspace is not connected, with a pointer to set up the integration in the portal. | ## Limits - Scans started from Slack are rate limited to five per hour per organization. Scans started from the portal, schedules, or the API are not affected by this limit. - Each command reply is visible only to the person who ran it. Only the final result message is posted to the channel. - Results are delivered to the channel the command was run in. ## Security and audit - The connection stores a Slack bot token for your workspace. It is encrypted at rest and never shown after the connection is made. - Every request from Slack is verified using Slack's request signing before WASViking acts on it, and stale requests are rejected. - Every `/wasviking` command is recorded with the Slack user, channel, command text, target, and outcome, so you have an audit trail of what was triggered from Slack and by whom. ## What it does not do The slash command starts scans and reports results. It does not change finding status, manage targets, or alter configuration from Slack. For inbound automation such as transitioning findings or wiring scans into other systems, use the public REST API or the [webhook events](https://docs.wasviking.com/integrations/webhooks/). ## Disconnect To remove the integration, go to **Settings → System Settings → Slack** and select **Disconnect Workspace**. The `/wasviking` command stops working for the workspace immediately. Reconnect at any time with **Connect to Slack**. ## Related - [Notification Channels](https://docs.wasviking.com/integrations/notification-channels/) - [Slack and Teams](https://docs.wasviking.com/integrations/slack-teams/) - [Targets and assets](https://docs.wasviking.com/concepts/targets-and-assets/) - [Your first scan](https://docs.wasviking.com/getting-started/first-scan/) --- # Webhooks Section: Integrations Source: https://docs.wasviking.com/integrations/webhooks/ Summary: Subscribe to signed JSON events for SIEM ingestion, automation, or anything that doesn't fit a built-in integration. Webhooks are the catch-all integration. Anything not covered by Jira, Slack, or Teams can subscribe to events directly. Payloads are signed JSON over HTTPS. For the full event catalog and signature verification examples, see [API Reference → Webhook events](https://docs.wasviking.com/api-reference/webhook-events/). ## Common patterns ### SIEM ingestion Subscribe to: - `finding.created` - `finding.escalated` - `finding.sla_breached` - `secret.verified_live` - `sbom.intel_match` Push the verified payload straight into your SIEM index. WASViking® signs every event with HMAC-SHA256; verification rejects forged deliveries before they touch your pipeline. ### CMDB / asset inventory sync Subscribe to: - `asset.first_seen` - `asset.disappeared` - `asset.reappeared` Use these to keep an external CMDB in sync with what WASViking sees on your surface. ### Notification fan-out If your team prefers a single-system fan-out (PagerDuty for criticals, Slack for warnings, email digest for lows), wire all events into your fan-out engine via webhook and let it route. ## Registering ```bash curl -sS https://api.wasviking.com/v1/webhooks \ -H "Authorization: ApiKey ${KEY}" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/wasviking-hook", "events": ["finding.escalated", "finding.sla_breached"], "description": "SIEM ingestion" }' ``` The response includes the signing secret. Store it; it is shown once. ## Rotating the secret ```bash curl -sS https://api.wasviking.com/v1/webhooks/{id}/rotate \ -H "Authorization: ApiKey ${KEY}" ``` Both the old and new secrets verify for a 24-hour overlap window. Roll your consumer to the new secret inside that window, then revoke the old one explicitly. ## Test delivery ```bash curl -sS https://api.wasviking.com/v1/webhooks/{id}/test \ -H "Authorization: ApiKey ${KEY}" ``` WASViking sends a `webhook.test` event to your endpoint. The payload shape matches a real event so your verification logic can be exercised in CI. ## Delivery guarantees - **At-least-once.** Network errors retry with exponential backoff for up to 24 hours. - **Per-finding ordering.** Events for the same finding are delivered in order. There is no global ordering guarantee. - **Signed.** Every delivery includes `Wasviking-Signature` and `Wasviking-Delivery` headers. Verify both. ## Failure handling If your endpoint returns non-2xx, WASViking retries with backoff: 1m, 5m, 30m, 2h, 8h, then stops at 24h. Failures are visible at **Integrations → Webhooks → Deliveries** so you can inspect the response body and replay. Replay endpoint: ```bash curl -sS https://api.wasviking.com/v1/webhooks/{id}/deliveries/{delivery_id}/replay \ -H "Authorization: ApiKey ${KEY}" ``` ## Cap and rate Each webhook subscription has a delivery cap of 50,000 events per day by default. Bursty workloads are smoothed; sustained excess returns `429` on subscription writes and surfaces in the portal Usage tab. --- # Jira Section: Integrations Source: https://docs.wasviking.com/integrations/jira/ Summary: Step by step setup of the Atlassian Jira integration, from the API token to the first synced issue. The Jira integration turns WASViking® findings into issues in your Jira Cloud project and keeps both sides in step. When your team moves the issue in Jira, the finding follows. When a scan reopens the finding, the issue gets a note. This page is the full setup, in order, with the two screens you will actually be looking at. ## What you need before you start - A Jira Cloud site on `atlassian.net`. The address you type in WASViking has to end in `.atlassian.net`, so a custom domain in front of Jira is not accepted. - An Atlassian account that can create issues in the target project. Use a service account owned by the team, not a personal login. The integration acts as that account, and it inherits exactly that account's permissions. - An administrator in WASViking. **Settings → Integrations** is open to the Admin and Manager roles. ## Step 1. Create the API token in Atlassian Sign in to Atlassian with the account the integration will use, then open [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens). The same page is reachable from the Atlassian account menu under **Security → API tokens**. ![Atlassian account Security tab, API Tokens page, with the Create API token button and a token named acme listing its creation date, expiry date and last access](https://docs.wasviking.com/static/docs/images/jira/01-atlassian-api-token.png) *The API Tokens page of the Atlassian account. One token per integration keeps the audit trail readable, and the Revoke action on the right is how you cut access later.* 1. Select **Create API token**. That is the plain one on the left. The button next to it creates a scoped token, which adds a permission step you do not need here. 2. Give the token a name you will recognize a year from now, for example `WASViking`. The name is only a label, so it can be anything. 3. Pick an expiry date and write it down. When the token expires, the sync stops until you paste a new one, so treat the date as a maintenance task. 4. Copy the token. Atlassian shows it once. If you lose it, revoke that token and create another. Treat the token like a password. It carries the full access of the account that created it. New tokens can take up to a minute to become usable, which matters in the next step. ## Step 2. Test and save the credentials in WASViking In the portal, open **Settings → Integrations** and select the **Atlassian Jira** tab. Section 1 is the only place the token is entered. ![WASViking Integrations page, Atlassian Jira tab, section 1 Credentials with the base URL, the account email and the masked API token filled in, next to the Test connection and Save credentials buttons](https://docs.wasviking.com/static/docs/images/jira/02-portal-credentials.png) *Section 1 filled in for the ACME example. Test connection proves the three values work together before anything is stored, and Save credentials then keeps the token encrypted.* | Field | What to type | ACME example | |---|---|---| | Base URL | Your Jira site address, nothing after the domain | `https://acme.atlassian.net` | | Account email | The Atlassian account that created the token | `jira-sync@acme-example.com` | | API token | The value you copied in Step 1 | pasted, then hidden | The Base URL always follows the same shape: `https://your-company.atlassian.net`. Leave out `/jira`, `/browse` and any project path. If you are unsure of the company part, look at the address bar while you are inside Jira. The API token field is write-only. After you save it, the portal shows that a token exists but never shows the value again, not to you and not to anyone else in your organization. Replacing it means pasting a fresh one. Select **Test connection** first. The test runs against the values typed in the form, not against anything stored, so you can correct a typo before it reaches the database. The page answers `connection ok` when Atlassian accepted the three values together. If the test fails within the first minute after creating the token, wait and try again before changing anything. Then select **Save credentials**. The token is stored encrypted and the page loads your projects, issue types, fields and statuses from Jira, which is what sections 2 to 4 need. Until that first save, those three sections say so instead of showing empty pickers. The chip next to *1. Credentials* tracks where you are: *Not connected yet*, *Unsaved changes*, or *Saved*. ## Step 3. Choose the project and issue type Section 2 reads the projects the account can see and the issue types that project defines. Pick the project that owns security work and the issue type your team triages, usually Task or Bug. Both lists come from your own Jira, so a custom issue type shows up on its own. ## Step 4. Map the fields Section 3 decides which finding data lands in which Jira field. Select **Auto-detect** and the page proposes the obvious matches, then review them. Out of the box the mapping is: | WASViking | Jira | |---|---| | Title | Summary | | Description (composed body) | Description | | Severity | Priority | Everything else is optional and yours to add: category, CWE, OWASP category, risk score, asset URL, SLA window, due date, first and last seen, times seen, fingerprint hash, finding id, and the pretty-printed evidence. Each one can go to a Jira system field or to a custom field you already have. The schema of every Jira field is read from your site, so the value is converted to the shape Jira expects. Severity mapped to Priority becomes Highest through Lowest. Severity mapped to a select field becomes Critical through Informational. Numbers go to number fields and dates to date fields. Jira rejects a create that carries a field the project does not accept. When that happens WASViking drops the offending field, retries, and records what it dropped, so one bad mapping never blocks the issue. ## Step 5. Map the statuses Section 4 has two columns, and both matter. **Outbound** is what WASViking does to the issue when the finding changes status. Open, In progress and Resolved are mandatory, the rest are optional. Reopened left blank uses the Open transition, so an issue in Done comes back to the board when a scan detects the finding again. **Inbound** is the reverse: it reads the Jira status category, not the status name, so any workflow your team invented still lands somewhere sensible. New maps to open, In progress to in progress, Done to resolved. **Auto-detect** fills both sides with the common model. Review it, because this is where a custom workflow usually needs one manual choice. ## Step 6. Choose what gets forwarded Section 5 keeps the Jira project from filling up with tickets nobody asked for. - **Forward general findings** covers the engine findings: DAST, SSL, secrets, headers, JWT and the rest. - **Forward SCA and SBOM findings** covers vulnerable third-party components. It is off by default. Agree on the expected volume with the Jira project owner before turning it on. - **Minimum severity** forwards only findings at or above a threshold. - **Categories to forward** picks the finding categories that deserve an issue. Leaving every category selected means a category the engine gains later is forwarded too. - **Repositories to forward** is the one rule that adds instead of narrowing. Add a repository and everything it owns reaches Jira, whatever the category, the severity or the SCA toggle say, because picking a repository means that repository goes to the tracker. Everything that does not belong to a listed repository keeps answering to the rules above. Search by name and add one at a time, so the list stays readable whether you have three repositories or six hundred. An empty list adds nothing. The block only appears when your findings carry a repository. - **Forward only findings from these repositories** flips that around. On, the list stops adding and becomes the whole scope: only those repositories reach Jira and the rules above no longer matter. Off is the default. Each repository you add shows how many open findings it has, counted the way the findings page counts them, next to how many of those are reaching Jira right now. Every filter is applied together. A finding reaches Jira when its bucket is on, its severity is at or above the minimum, its category is on the list and, if it belongs to a repository, that repository is on the list. The page previews the effect of the filters before you save. It answers three things: how many open findings match, how many of those already have an issue, and how many a backfill would create. The breakdown underneath reads step by step, each filter counting what was left after the one before it, so the numbers always add up to the total. ## Step 7. Turn the sync on At the bottom of the mapping, turn on **Enable bidirectional sync** and select **Save mapping**. Nothing is pushed until this is on. **Recreate Jira issue automatically if deleted on Jira side** is the neighbor switch, and it is covered further down. ## Step 8. Backfill what already exists Section 6 pushes the findings you already have. Findings that are linked to an issue are skipped, so the backfill is safe to run more than once. Pushes are spaced two seconds apart and capped at thirty minutes, which keeps your site inside the Jira rate limits. Two options change the scope: - **Include resolved / accepted-risk / false-positive** brings closed work across as well. Most teams leave it off. - **Force resync** pushes every finding again, linked or not. It is the repair tool for a mapping you fixed after the fact, not part of a normal setup. Run the backfill once, after the filters are the way you want them. ## What the Jira issue looks like The summary is the finding title, trimmed to what Jira accepts. The description is composed rather than copied: severity, category, CWE, OWASP category and risk score, then the component and CVE when the finding came from a dependency, then the engine summary and the recommendation. Near the end comes the WASViking finding id, which is what makes an issue traceable back to the platform months later, followed by a link that opens that exact finding in the portal. The link is a shortcut, not a key. It asks for sign-in and second factor like any other page, and the portal resolves the organization from the session before it reads the link, so it shows nothing to someone outside your organization. Existing issues pick the link up on their next update. Evidence is appended to the description unless you mapped it to a field of its own. ### Labels for routing Every issue also carries labels, so a team can pick up its own work from a board filter or a JQL query without opening the card. The set is small and always follows the same grammar. | Label | Meaning | Example | |---|---|---| | `wasviking` | On every issue the integration creates. `labels = wasviking` lists them all. | `wasviking` | | `wasviking-area-` | The part of the platform that found it, which is usually the team that fixes it: `web`, `mobile`, `code`, `supply-chain`, `infrastructure` or `exposure`. | `wasviking-area-mobile` | | `wasviking-category-` | The finding category. | `wasviking-category-xss` | | `wasviking-repo-` | The repository, for findings that came from a pipeline or repository scan. | `wasviking-repo-acme-payments-api` | | `wasviking-app-` and `wasviking-platform-` | The application and its platform, for findings from a mobile assessment. | `wasviking-app-com.acme.bank`, `wasviking-platform-android` | Values are lowercase and hyphenated, with no spaces, so Jira always accepts them. A repository named `acme/payments-api` reads `wasviking-repo-acme-payments-api`. Labels your team adds in Jira are left alone. The sync only ever adds or removes labels that follow the grammar above, and it does that through label operations rather than by rewriting the field, so a `sprint-42` or `needs-review` your team put on the card survives every update. If the labels field is not on the project's create or edit screen, the issue is created without labels and everything else still syncs. Issues created before labels existed receive theirs on their next update, or right away through a **Force resync** backfill. The complete catalog, the rules the sync follows and a playbook for organizing boards, automation and reporting by team are on the [Jira labels](https://docs.wasviking.com/integrations/jira-labels/) page. ## What comes back from Jira WASViking polls the linked issues every five minutes and maps the Jira status category back to the finding status through your inbound mapping. A short anti-loop window keeps a push and its own echo from bouncing the status between the two systems. Only a status that actually changed is read back. An issue still in the status WASViking last saw or set carries no news, even when a field update or a notice comment refreshed it, so a finding a scan just reopened stays reopened until your team moves the issue. ## Closure and reopen notices Whenever WASViking closes or reopens a finding, the linked issue receives a comment saying who did it, when and why, so nobody reading the ticket sees a status move without an explanation: - **Closed by a scan.** The finding was no longer detected. The comment names the scan, the date and the reason (a component no longer present in the latest SBOM, say) and tells the reader the issue reopens if a later scan detects it again. - **Closed by a person.** The comment names the operator, the status they chose (Resolved, Accepted risk or False positive), the standardized reason and the note they left. An accepted risk also carries its expiry date. - **Closed by a suppression rule.** The comment names the rule. - **Reopened.** A scan that detects a resolved finding again, a risk acceptance that expired, or an operator moving the finding back all produce a comment with the reason, the date and the finding's severity and category. Each comment ends with a link back to the finding in WASViking. It is posted once per status change, after the issue status is synced, and only for changes made in WASViking: a status your team changes in Jira is never echoed back as a comment. A manual push or a Force resync re-sends fields and never comments. When the status could not be moved (the workflow has no transition to the mapped status, say), the comment says so, so the card can be moved by hand. ## When someone deletes the issue in Jira The link in WASViking is left pointing at nothing. With **Recreate Jira issue automatically** off, which is the default, the next push records the error in the sync history and stops trying. With it on, the push detects the missing issue, creates a new one, and carries on. Choose based on whether deleting a ticket in Jira means the work is cancelled or means someone made a mistake. ## Rate limits Jira throttles per site. WASViking retries with a growing pause when Jira answers 429 or a gateway error, and large backfills spread themselves out. Nothing is dropped on the floor. ## Turning the integration off Turn off **Enable bidirectional sync**. The issues already in Jira stay where they are, and WASViking stops pushing and polling. The links are kept, so turning it back on later picks up where it left off instead of creating duplicates. To cut Atlassian access as well, revoke the token on the Atlassian API tokens page from Step 1. ## Troubleshooting | What you see | What it means | What to do | |---|---|---| | `base_url must be https` | The address was typed with `http` or without a scheme | Type the full `https://your-company.atlassian.net` | | The base URL is refused because it must end with `.atlassian.net` | A custom domain in front of Jira, or a typo in the domain | Use the `atlassian.net` address of the site, not a vanity domain | | `auth failed (http 401)` | Email and token do not match, or the token was revoked | Confirm the email owns the token, then paste a fresh token | | `auth failed (http 403)` | Credentials are valid, permissions are not | Give the account access to the project, or point the integration at a project it can already see | | Connection fails right after creating the token | Atlassian has not finished propagating it | Wait a minute and test again | | The project list is empty | The account cannot browse any project | Check the account's project permissions in Jira | | No Jira fields loaded yet | Credentials were never saved | Save credentials first, then return to the field mapping | | Test connection says `connection ok, not saved yet` | The test used values that differ from the stored ones | Select Save credentials to keep them | | Issues are created but stay in the first status | The outbound status mapping has no transition for that status | Re-run Auto-detect on the status mapping and save | | Nothing reaches Jira after setup | Bidirectional sync is off, or the filters exclude everything | Check the toggle in section 5 and the filter preview | Prefer building your own sync? Subscribe to finding events directly via [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # Jira labels Section: Integrations Source: https://docs.wasviking.com/integrations/jira-labels/ Summary: The complete catalog of labels WASViking writes on Jira issues, the rules the sync follows, and a playbook for organizing boards, automation and reporting by team. Every issue the [Jira integration](https://docs.wasviking.com/integrations/jira/) creates carries a small set of labels that say where the finding came from, what kind of weakness it is and which repository or application it belongs to. The labels exist so that a team can pick up its own work from a board filter, an automation rule or a dashboard without opening the card. This page is the reference for those labels: the full catalog, the rules the sync follows, and the patterns teams use to organize security work around them. ## The grammar All labels follow one shape: ``` wasviking wasviking-- ``` - `wasviking` is on every issue the integration creates. - `` is one of `area`, `category`, `repo`, `app` or `platform`. - `` is lowercase and hyphenated. It never contains a space, because Jira refuses a label with a space in it. The namespace is deliberate. Your own labels, such as `sprint-42`, `needs-review` or `team-payments`, never collide with these, and a JQL query can tell the two apart at a glance. ## Catalog ### The root label | Label | On which issues | |---|---| | `wasviking` | Every issue the integration creates. `labels = wasviking` lists all of them. | ### Area labels The area is the part of the platform that found the weakness, which in most organizations is also the team that fixes it. There are six. | Label | What lands there | Suggested owner | |---|---|---| | `wasviking-area-web` | Findings from scans of running web applications and APIs: cross-site scripting, SQL injection, other injection, authentication and authorization, GraphQL, sensitive files, security headers, known CVEs, and secrets, access control or AI security weaknesses observed at runtime. | The product team that owns the application, or the web platform team. | | `wasviking-area-mobile` | Findings from mobile application assessments, including vulnerable components bundled inside the app. | The mobile team. Split by `wasviking-platform-android` and `wasviking-platform-ios` when two teams own the platforms. | | `wasviking-area-code` | Findings from repository scans: code review for broken access control, AI and LLM security review, and hard-coded secrets. | The team that owns the repository. The `wasviking-repo-` label names it. | | `wasviking-area-supply-chain` | Vulnerable or outdated third-party components from SBOM and SCA submissions and from runtime component detection. | The team that owns the repository, with platform engineering for shared base images and frameworks. | | `wasviking-area-infrastructure` | SSL and TLS weaknesses and exposed sensitive ports. | Infrastructure, SRE or network operations. | | `wasviking-area-exposure` | Credentials found in breach data and brand abuse or typosquatting domains. | Security operations, with IT for credential resets and legal or brand teams for typosquatting takedowns. | An issue carries exactly one area label. When a finding could belong to two, its origin decides: a vulnerable component found inside a mobile app is `wasviking-area-mobile`, not `wasviking-area-supply-chain`, and a secret found in a repository is `wasviking-area-code` while the same secret observed in an HTTP response is `wasviking-area-web`. ### Category labels The category is the finding category as WASViking® reports it in the portal, the API and the CSV export, written with hyphens. There are eighteen. | Label | Category in the portal | Usual area | |---|---|---| | `wasviking-category-xss` | Cross-site scripting | web | | `wasviking-category-sqli` | SQL Injection | web | | `wasviking-category-injection` | Injection (SSRF, command injection, path traversal, SSTI, XXE and others) | web | | `wasviking-category-auth` | Authentication / Authorization | web | | `wasviking-category-graphql` | GraphQL (introspection, IDE exposed, GET mutation, verbose errors, field suggestion) | web | | `wasviking-category-sensitive-file` | Sensitive file exposure | web | | `wasviking-category-headers` | Security headers | web | | `wasviking-category-cve` | Known CVE | web | | `wasviking-category-token-exposure` | Secret / token exposure | web at runtime, code in a repository | | `wasviking-category-access-control` | Broken Access Control (IDOR, BOLA, BFLA, clickjacking) | web at runtime, code in a repository | | `wasviking-category-ai-security` | AI / LLM Security (prompt injection, unsafe tool execution, data exposure) | web at runtime, code in a repository | | `wasviking-category-vulnerable-component` | Vulnerable / outdated third-party component | supply-chain, or mobile inside an app | | `wasviking-category-mobile` | Mobile application | mobile | | `wasviking-category-ssl` | SSL / TLS | infrastructure | | `wasviking-category-exposed-port` | Exposed sensitive port | infrastructure | | `wasviking-category-credential-exposure` | Credential exposure (breach data) | exposure | | `wasviking-category-brand-abuse` | Brand abuse / typosquatting | exposure | | `wasviking-category-other` | Other | web | A category the engine gains later appears as `wasviking-category-` on its own, so a filter written on the root or area labels keeps working. ### Repository label | Label | On which issues | Example | |---|---|---| | `wasviking-repo-` | Findings that came from a repository or pipeline scan: code review, AI security review, hard-coded secrets, and SBOM or SCA submissions. | `acme-corp/payments-api` reads `wasviking-repo-acme-corp-payments-api` | The repository name is the one the pipeline reported, which is the `--app-name` given to the Sentinel CLI or the name of the scanned folder. It is folded to the label character set: lowercase, everything outside letters, digits, dots, underscores and hyphens becomes one hyphen, accents are removed, so `serviço/api` reads `wasviking-repo-servico-api`. Findings from scans of running applications carry no repository label, because they have no repository. ### Application and platform labels | Label | On which issues | Example | |---|---|---| | `wasviking-app-` | Findings from a mobile assessment. The identifier is the Android application id or the iOS bundle id, dots kept. | `wasviking-app-com.acme.bank` | | `wasviking-platform-` | Findings from a mobile assessment. | `wasviking-platform-android`, `wasviking-platform-ios` | Mobile findings carry these two instead of a repository label. ### What one issue looks like | Finding | Labels | |---|---| | Reflected cross-site scripting found by a scan of a web application | `wasviking`, `wasviking-area-web`, `wasviking-category-xss` | | IDOR found by code review in `acme-corp/payments-api` | `wasviking`, `wasviking-area-code`, `wasviking-category-access-control`, `wasviking-repo-acme-corp-payments-api` | | Vulnerable library in the SBOM of `acme-corp/payments-api` | `wasviking`, `wasviking-area-supply-chain`, `wasviking-category-vulnerable-component`, `wasviking-repo-acme-corp-payments-api` | | Insecure storage in an Android app | `wasviking`, `wasviking-area-mobile`, `wasviking-category-mobile`, `wasviking-app-com.acme.bank`, `wasviking-platform-android` | | Expired certificate on a public host | `wasviking`, `wasviking-area-infrastructure`, `wasviking-category-ssl` | | Employee credential found in breach data | `wasviking`, `wasviking-area-exposure`, `wasviking-category-credential-exposure` | ## The rules the sync follows - **Your labels are never touched.** The sync adds and refreshes its own labels through label operations, never by rewriting the field, so every label your team adds survives every update. - **Only the grammar above is managed.** The sync removes a label only when it has the shape `wasviking--` and no longer applies, for example the old repository label after a repository was renamed in the pipeline. A label of yours that merely starts with the name, such as `wasviking-reviewed`, is not in the grammar and is left alone. - **`wasviking` is never removed.** If someone deletes it from a card, it is back on the next update. - **Labels are refreshed on every push.** A push happens when the finding changes, when a scan sees it again, on a manual Push to Jira and on a backfill with Force resync. Issues created before labels existed receive theirs on their next push. - **Labels are a mirror, not a control.** Removing an area label in Jira does not change the finding; the label comes back. To route work differently, add your own label or an automation rule rather than editing these. - **The labels field has to be on the screen.** If the issue type's create or edit screen does not include Labels, the issue is created without labels and everything else still syncs. Add the field to both screens and run a backfill with Force resync. ## Organizing the work by team The labels are the raw material. What follows is how organizations with several engineering teams usually put them to work. ### Start with an ownership matrix Decide once who owns each area and write it down where the teams can see it. A minimal matrix has three columns: the area label, the owning team, and the escalation contact for anything rated Highest. Repositories refine it: a `wasviking-repo-` label maps to the team in your service catalog, and mobile platforms map to the Android and iOS teams. ### One board per team, driven by a quick filter Each team's board keeps its own filter, so the team sees security work next to its feature work instead of on a separate security board nobody visits. | Team | Quick filter | |---|---| | Mobile, Android | `labels = wasviking-area-mobile AND labels = wasviking-platform-android` | | Mobile, iOS | `labels = wasviking-area-mobile AND labels = wasviking-platform-ios` | | Payments squad | `labels = wasviking-repo-acme-corp-payments-api` | | Web platform | `labels = wasviking-area-web` | | Infrastructure | `labels = wasviking-area-infrastructure` | | Security operations | `labels = wasviking-area-exposure` | ### One security board with a swimlane per area For the security team, a single board with swimlanes based on JQL gives the whole picture. One swimlane per area label, ordered by priority, shows at a glance which team is behind. ### Route automatically with Jira Automation Automation rules turn a label into an owner the moment the issue is created, so nobody has to triage by hand. | When | Condition | Then | |---|---|---| | Issue created | Labels contains `wasviking-area-mobile` | Set the Team or Component to Mobile, assign to the mobile lead | | Issue created | Labels contains `wasviking-repo-acme-corp-payments-api` | Set Component to `payments-api`, assign to the component lead | | Issue created | Labels contains `wasviking-category-credential-exposure` | Set priority to Highest, add the security lead as watcher, post to the security channel | | Issue created | Labels contains `wasviking-area-supply-chain` and priority is Highest | Create a linked task for platform engineering | Keep the ownership in your own field, Team or Component, and let the labels feed it. Renaming or deleting a WASViking label to change ownership does not work, because the sync puts it back. ### Dashboards and reporting Saved filters on the labels give the weekly review its numbers without anyone assembling a spreadsheet. | Question | JQL | |---|---| | Everything open from WASViking | `labels = wasviking AND statusCategory != Done` | | Open work per team | `labels = wasviking-area-mobile AND statusCategory != Done`, one filter per area | | What arrived this week | `labels = wasviking AND created >= -7d` | | Highest and High still open, by repository | `labels = wasviking AND priority in (Highest, High) AND statusCategory != Done` grouped by label in a two dimensional statistics gadget | | Anything resolved by a scan rather than by hand | `labels = wasviking AND statusCategory = Done AND resolved >= -30d` | A two dimensional filter statistics gadget with labels on one axis and status on the other is the single most useful view: every area, every state, one table. ### Keep the two namespaces apart Use your own labels freely for anything the sync does not express: sprint, ownership, review state, customer impact. Leave the `wasviking-` labels to the sync. If a value does not fit your naming convention, map it with an automation rule to a label or field of your own instead of editing the label on the card. ## JQL reference | Goal | JQL | |---|---| | All issues from WASViking | `labels = wasviking` | | One area | `labels = wasviking-area-code` | | Several areas | `labels in (wasviking-area-web, wasviking-area-code)` | | One repository | `labels = wasviking-repo-acme-corp-payments-api` | | One category across every area | `labels = wasviking-category-access-control` | | One mobile app | `labels = wasviking-app-com.acme.bank` | | One platform | `labels = wasviking-platform-ios` | | Everything except supply chain | `labels = wasviking AND labels != wasviking-area-supply-chain` | | Not yet labelled by a team | `labels = wasviking AND component is EMPTY` | Labels are matched exactly and are case sensitive in JQL, so type them as they appear here, in lowercase. --- # ServiceNow Section: Integrations Source: https://docs.wasviking.com/integrations/servicenow/ Summary: Two-way sync of findings with ServiceNow incidents, with state mapping, sync filters, and polling. The ServiceNow integration creates WASViking® findings as records on the Incident table of your instance, with two-way state mapping, per-integration sync filters, and polling for status updates back into WASViking. ## What syncs - **Finding → Incident.** Short description, a composed description (severity, category, CWE, OWASP, risk score, evidence), and any incident field you map, including custom `u_*` fields. - **Status → Incident state.** Configurable mapping (open ↔ New, resolved ↔ Resolved, etc.). States are read live from your instance, so custom states appear automatically. When a finding moves an incident to Resolved or Closed, the mandatory Resolution code and Close notes are filled in automatically using the resolution codes your instance actually defines. - **Incident state → Finding status.** State changes made by your team in ServiceNow are polled back and mapped to WASViking statuses by state category (new, in progress, done). - **Closure and reopen notices.** Whenever WASViking closes or reopens a finding, the incident receives a work note saying who did it, when and why: the scan that stopped detecting it, the operator and the reason they chose, the suppression rule, or the scan that detected it again. When WASViking resolves the incident, the Close notes carry the same reason. A state your team changes in ServiceNow is never echoed back. The integration tracks a stable link per finding (incident number and sys_id), so re-syncs do not duplicate incidents. ## Requirements - An instance on `service-now.com` or `servicenowservices.com` (Government Community Cloud). Custom vanity URLs are not accepted. - A dedicated integration user with the `itil` role, authenticated with Basic auth. Use a service account, not a personal user. - Set the integration user's timezone to GMT/UTC. Inbound polling filters by update time and ServiceNow interprets those timestamps in the querying user's timezone. ## Setup 1. In ServiceNow: create the integration user with the `itil` role and GMT/UTC timezone. 2. In WASViking: **Settings → Integrations → ServiceNow**. 3. Provide the instance URL, the username, and the password, then **Test connection**. The test runs against the values typed in the form, so you can confirm them before anything is stored. Once it answers `connection ok`, select **Save credentials**: the password is stored encrypted and the incident fields and states are read from your instance. 4. Map WASViking fields to incident fields. **Auto-detect** fills the common ones. Severity mapped to Urgency, Impact, or Severity is converted to the 1/2/3 choice values automatically. Priority cannot be mapped: ServiceNow computes it from impact and urgency. 5. Map WASViking statuses to incident states, outbound and inbound. **Auto-detect** proposes the vanilla model (open → New, in progress → In Progress, resolved → Resolved). Reopened is optional: left blank, a reopened finding takes the open transition, so an incident closed by a scan comes back when the finding is detected again. 6. Choose what to forward with the sync filters (see below). 7. Turn on **Enable bidirectional sync** and **Save mapping**. ## Field mapping (defaults) | WASViking | ServiceNow | |---|---| | `title` | Short description | | `description` + evidence | Description | | `severity` | Urgency (converted to 1/2/3) | | `risk_score` | Any numeric field, e.g. `u_wv_risk_score` | | `cwe` | Any string field, e.g. `u_wv_cwe` | | `last_scan_correlation_id` | Correlation ID | Unlike Jira, ServiceNow does not reject unknown fields; it silently ignores them. Map to fields that exist on your incident form, or create `u_*` custom fields first and re-run **Auto-detect**. ## Sync filters Each integration decides what it forwards: - **Buckets.** General findings and SCA / SBOM component findings have independent toggles. - **Minimum severity.** Only forward findings at or above a threshold. - **Categories.** Choose which finding categories create incidents. - **Repositories.** The one rule that adds. A repository you add has all of its findings turned into incidents, whatever the category, the severity or the SCA toggle say. Everything outside the list keeps answering to the rules above. An empty list adds nothing. - **Forward only findings from these repositories.** On, the list stops adding and becomes the whole scope: only those repositories reach ServiceNow. Off is the default. Every filter is applied together: bucket, severity, category and repository all have to agree before an incident is created. The page previews the impact live before you save. Manual push of a single finding always bypasses the filters; that click is an explicit operator decision. ## Backfill Backfill creates an incident for every existing finding that does not have one yet. Already-linked findings are skipped, and pushes are spread out (2 seconds apart, capped at 30 minutes) to respect instance rate limits. Run it once after the first setup, after the sync filters are configured the way you want. ## Polling ServiceNow has no native outbound webhooks, so WASViking polls the linked incidents every 5 minutes and brings back state changes, mapped to WASViking statuses through your inbound mapping. Incidents created by WASViking are stamped with the correlation display marker `WASViking`, which keeps the poll scoped to the integration's own records instead of scanning the whole Incident table. A short anti-loop window prevents an outbound push and its own echo from ping-ponging the status between the two systems. Only a state that actually changed is read back. An incident still in the state WASViking last saw or set carries no news, even when a field update or a work note refreshed it, so a finding a scan just reopened stays reopened until your team moves the incident. ## Deleted incidents If someone deletes a linked incident directly in ServiceNow, the next push logs an error in Sync history and stops trying. With **Recreate the incident automatically** turned on, the integration detects the missing record, recreates the incident under a new number, and resumes the normal flow. ## Rate limiting ServiceNow throttling is defined per instance by rate limit rules. WASViking honors the `Retry-After` header on 429 responses and backs off with the queue intact. Large backfills spread out on their own. ## Removing the integration Turn off **Enable bidirectional sync**. The existing incidents remain; WASViking simply stops pushing and polling. Re-enabling later will not duplicate incidents, because the finding links are kept. Prefer building your own sync? Subscribe to finding events directly via [Webhooks](https://docs.wasviking.com/integrations/webhooks/). --- # SIEM Section: Integrations Source: https://docs.wasviking.com/integrations/siem/ Summary: Ship WASViking findings, audit events, and supply chain alerts into your SIEM. WASViking® integrates with any SIEM through the same webhook event model used by everything else. There is no SIEM-specific plugin layer because there is no need for one. ## Pattern 1. Create a WASViking API key with `webhooks:manage`. 2. Register a [webhook](https://docs.wasviking.com/integrations/webhooks/) pointing at your SIEM's HTTP collector. 3. Subscribe to the right event set (recommendations below). 4. Verify signatures in your SIEM's collector. 5. Index. For event payload schemas and signature verification, see [Webhook events](https://docs.wasviking.com/api-reference/webhook-events/). ## Recommended subscriptions For a general security operations SIEM index: | Event | Why | |---|---| | `finding.created` | New attack surface findings. | | `finding.escalated` | Risk score jumped to a higher band. | | `finding.sla_breached` | Past SLA, needs immediate attention. | | `secret.verified_live` | Active leaked credential. | | `sbom.intel_match` | KEV-listed component just identified. | | `audit.event` | Generic audit trail event. | ## SIEM-specific notes ### Splunk HTTP Event Collector (HEC) The webhook URL is the HEC endpoint with the token: ``` https://splunk.example.com:8088/services/collector/event ``` Header to add on the WASViking side: `Authorization: Splunk `. WASViking lets you add static headers per webhook under **Integrations → Webhooks → Headers**. Signature verification: implement as a Splunk pre-collector script or use HEC's authentication-only mode and verify with a downstream search. ### Elastic Common Schema (ECS) WASViking does not emit ECS-shaped events natively. The simplest adapter is a Logstash or a Fluent Bit filter that maps WASViking event fields to ECS fields: | WASViking | ECS | |---|---| | `type` | `event.action` | | `created_at` | `@timestamp` | | `data.finding_id` | `vulnerability.id` | | `data.cwe` | `vulnerability.classification` | | `data.risk_score` | `vulnerability.score.base` | | `data.severity` | `event.severity` | ### Datadog Use Datadog's webhook integration as a receiver: - Pass the WASViking signature header through. - Verify in a small Lambda or in a Datadog Forwarder rewrite rule. - Tag the event with `source:wasviking`. ### Microsoft Sentinel Land WASViking events into a Log Analytics workspace via the HTTP Data Collector API: - Use an Azure Function as the receiver. - Verify the signature. - Forward to Log Analytics with `WASViking_CL` as the custom log name. ## Audit log shipping The customer-facing audit log can be pushed to SIEM as a stream too, via the same webhook event `audit.event`. The audit feed covers: - Operator sign-ins and MFA challenges. - RBAC changes. - API key issuance and revocation. - Evidence Bundle lifecycle. - Finding status transitions. For pull-based audit shipping, the REST endpoint `GET /audit-log` supports `since` for incremental fetch. ## Compliance For PCI DSS v4.0, BACEN, and ISO 27001:2022, shipping security events into a SIEM and retaining them for the required window is an explicit control. Verify retention on the SIEM side; WASViking's own audit retention is configurable per plan and is not a substitute for SIEM retention. ## What this is not WASViking does not include a SIEM. Findings are designed to be ingested into yours. --- # SAML 2.0 SSO Section: Integrations Source: https://docs.wasviking.com/integrations/saml-sso/ Summary: Federate WASViking with your IdP using SAML 2.0. Attribute mapping, default role policy, and a Google Workspace step-by-step. WASViking® implements SAML 2.0 as Service Provider. Sign your team in through your Identity Provider, with attribute mapping and optional group-to-role propagation. Default role on first SSO sign-in is **Read-only** by policy; promotion requires an existing Admin or Manager. ## Supported Identity Providers Any SAML 2.0 compliant Identity Provider works. The integration is tested against: - Google Workspace (full step-by-step below) - Microsoft Entra (Azure AD) - Okta - OneLogin - JumpCloud - Auth0 ## Service Provider endpoints | Setting | Value | |---|---| | ACS URL (Assertion Consumer Service) | `https://portal.wasviking.com/sso/saml/acs/` | | Entity ID / Audience | `https://portal.wasviking.com/sso/saml/metadata` | | Metadata URL (auto-config) | `https://portal.wasviking.com/sso/saml/metadata` | | SAML signature algorithm | RSA-SHA256 | | NameID format | EmailAddress | The same ACS and Entity ID apply to every organization. Routing is done by the primary email domain you configure on the WASViking side. ## Required attributes The IdP must send these attributes in the SAML assertion: | Google Directory attribute | App attribute (case-sensitive) | Required | |---|---|---| | Primary Email | `email` | Yes | | First Name | `first_name` | Yes | | Last Name | `last_name` | Yes | | Group memberships | `groupMemberships` | Optional | > **Casing matters.** WASViking expects `first_name` and `last_name` in > snake_case. CamelCase (`firstName` / `lastName`) is not recognized. ## Configure on the WASViking side **Portal → Settings → System Settings → SSO & Identity → SAML**. The screen has: | Field | Value | |---|---| | **Enable SAML authentication** | Toggle on once IdP-side is ready. | | **Primary Email Domain** | The email domain users will sign in with (e.g., `acme.com`). Users with this domain are routed to SSO. | | **IdP Entity ID** | From your IdP. | | **IdP SSO URL / Metadata URL** | From your IdP. Users are redirected here to authenticate. | | **IdP X.509 Certificate** | Paste the full certificate including `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` markers. | Click **Save SSO Settings**. Test the configuration with **Test SSO** before flipping enforcement on for everyone. ## Default role policy Every new account that signs in via SSO **lands as Read-only** by default. This is policy, not a setting: SSO-provisioned accounts are read-only on first login, regardless of IdP group memberships. Promotion to any other role (Admin, Manager, Analyst) requires an existing Admin or Manager already on the platform. The promotion is done in **Settings → Team**, with audit log. Why this matters: it gives the WASViking-side admin a moment to validate the new operator before granting elevated rights, even when the IdP would otherwise propagate a group-derived role automatically. ## Group governance flow Once SAML is enabled, operational access is driven from the IdP: - **Add a user to the WASViking-allowed group** in your IdP → user gains access on next sign-in. - **Remove a user from the group** → user loses access immediately on next session validation. - **Disable a user in the IdP** → user loses access immediately. The pattern is the IAM standard: lifecycle in the IdP, authorization in WASViking. You do not need to manage operator accounts on the WASViking side once SSO is on. ## Group-to-role mapping (optional) If your IdP sends a `groupMemberships` attribute, you can map specific groups to WASViking roles. Mappings apply **on user promotion**, not on first sign-in (which is always Read-only). | IdP group | WASViking role | |---|---| | `sec-admins` | Admin | | `sec-managers` | Manager | | `sec-analysts` | Analyst | | `auditors` | Read-only | A user in multiple mapped groups receives the highest privilege among them at promotion time. ## MFA When SSO is enabled, MFA is handled by your IdP. WASViking does not prompt for its own MFA on SSO logins. This is the standard SP pattern. For org-level break-glass access (when the IdP is down), Admins can enable **Emergency Local Login** under Settings → SSO. The flag has a 72-hour TTL and triggers a high-severity audit event when used. ## Single Logout If your IdP supports SLO, configure the Single Logout endpoint and WASViking honors logout requests originated at the IdP. Local logout in WASViking also issues SLO if the user signed in via SAML. ## Enforcing SSO Once SSO is tested, enable enforcement under **Settings → System Settings → SSO & Identity**. After this: - Local password login is disabled for accounts on the configured email domain. - API keys still work (they are not SSO-managed). - Emergency Local Login remains available to break the loop. --- ## Google Workspace step-by-step This walkthrough mirrors the canonical **WASViking Google Workspace SSO SAML Integration Guide v1.2**. ### Step 1: Create a group for access control Google Admin → **Groups → Create group**. | Field | Suggested value | |---|---| | Name | `WASViking Users` | | Email | `wasviking-sso@` | | Description | Users allowed to access WASViking via SSO | Group security: - Type: **Custom**. - Only invited members can join. - Do not allow external members. Click **Create group**. ### Step 2: Add users to the group Open the group and click **Add members**. Only members of this group will be able to authenticate against WASViking. ### Step 3: Create the SAML application in Google Workspace Google Admin → **Apps → Web & Mobile Apps → Add App → Add custom SAML app**. Suggested app name: **WASViking**. Click **Continue**. ### Step 4: Save the Google IdP identity On the Google IdP details screen, copy and keep: - **SSO URL** - **IdP Entity ID** - **X.509 Certificate** You will paste these into WASViking in Step 7. Click **Continue**. ### Step 5: Configure WASViking as Service Provider Fill exactly: | Field | Value | |---|---| | ACS URL | `https://portal.wasviking.com/sso/saml/acs/` | | Entity ID | `https://portal.wasviking.com/sso/saml/metadata` | | Name ID | Email | | ID of Name | Basic Information > Primary email | Click **Continue**. ### Step 6: Map attributes Add these mappings exactly: | Google Directory attribute | App attribute | |---|---| | Primary Email | `email` | | First Name | `first_name` | | Last Name | `last_name` | Click **Finish**. ### Step 7: Restrict to the group (governance) Google Admin → **Apps → Web & Mobile Apps → WASViking → Service status**. - Select **Groups**, search for `WASViking Users`. - Set status to **ON**. - Save. Only members of `WASViking Users` can now sign in via this SAML app. ### Step 8: Optional: import via Metadata URL If your IdP supports importing the SP automatically, use: ``` https://portal.wasviking.com/sso/saml/metadata ``` ### Step 9: Configure on the WASViking side In the WASViking Portal: **Settings → System Settings → SSO & Identity → SAML**. Fill: - **IdP Entity ID** (from Step 4) - **IdP SSO URL / Metadata URL** (from Step 4) - **IdP X.509 Certificate** (paste between `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`) - **Primary Email Domain** (the domain users sign in with) Toggle **Enable SAML authentication** and click **Save SSO Settings**. ### Step 10: Day-to-day operation - Add a user to `WASViking Users` in Google Workspace → they gain access on next sign-in (lands as **Read-only**). - Remove from the group → access revoked. - Promote to Admin / Manager / Analyst → done by an existing Admin or Manager in WASViking. --- ## Common problems | Problem | Likely cause | |---|---| | User cannot sign in | Not a member of the WASViking group on the IdP. | | 403 on the ACS callback | Entity ID or ACS URL mismatch. Re-check Step 5. | | User signs in but name is missing | Attribute mapping missing or wrong case (must be `first_name` / `last_name`). | | No SAML response from the IdP | The app is not active for the group. | | Certificate-invalid error | IdP signing certificate expired. Rotate on the IdP and update on WASViking. | | Wrong role after first SSO sign-in | Expected. Default is Read-only; promote in **Settings → Team**. | ## Security notes - Only members of the IdP group can authenticate. - WASViking follows least-privilege: first sign-in is Read-only. - Role administration stays on the WASViking side. - Every authentication and role change is captured in the audit log. ## What this is not WASViking does not implement OIDC. SAML 2.0 is the standard for enterprise SSO into security tools and is what every Identity Provider above supports natively. OIDC support is on the roadmap. WASViking is not an Identity Provider. You cannot use WASViking to log into other SaaS tools. --- # Authentication Section: API Reference Source: https://docs.wasviking.com/api-reference/authentication/ Summary: Authenticate to the WASViking REST API with an ApiKey scheme. Bearer is silently rejected. The WASViking® public REST API uses the **ApiKey** authentication scheme. Tokens look like `wv_live_*` for production and `wv_test_*` for test environments. ## Header ``` Authorization: ApiKey wv_live_xxxxxxxxxxxxxxxxxxxx ``` > **Important.** The scheme is `ApiKey`, not `Bearer`. A request with > `Authorization: Bearer wv_live_…` returns 401 with no body. This is a > deliberate hard rule, not a bug. ## Issuing a key Go to **Settings → API Keys → New key**. The portal shows the key once at creation time. After that the key is stored hashed; the portal can display only a prefix and the masked tail. | Field | Notes | |---|---| | Name | Operator-readable identifier (`ci-prod`, `siem-export`). | | Scopes | Subset of the scope catalog. Least privilege wins. | | Expiration | Optional. Recommended for keys handed to vendors. | | IP allow-list | Optional. Limits where the key can be used from. | ## Scopes (excerpt) | Scope | Allows | |---|---| | `scans:run` | Trigger scans (including via templates). | | `scans:read` | Read scan status and reports. | | `findings:read` | Read findings, evidence, AI recommendations. | | `findings:update` | Status transitions, comments, assignment. | | `inventory:read` | Read Asset Inventory. | | `inventory:export` | CSV export. | | `audit_logs:read` | Read the customer-facing audit log. | | `sca:submit` | Submit SBOM documents from Sentinel. | | `sca:read` | Read SBOM inventory and Supply Chain Watch. | | `secrets:submit` | Submit secret detections from Sentinel. | | `templates:read` | Resolve org-scoped scan templates by slug. | | `webhooks:manage` | Create and rotate webhook subscriptions. | | `api_keys:manage` | Issue and rotate API keys (Admin role only by default). | The full list is at **Settings → API Keys → Scopes**. ## Rate limits Default rate limits apply per key: - 600 requests per minute for read endpoints. - 60 requests per minute for write endpoints. - 12 concurrent scans triggered via API per org (configurable). Rate-limited responses return `429` with a `Retry-After` header in seconds. ## Errors Common errors: | Status | Meaning | |---|---| | `401` | Missing, malformed, or revoked key. | | `403` | Key valid but lacks the required scope. | | `404` | Resource does not exist in the key's organization. | | `409` | Conflict (e.g., target already exists). | | `422` | Validation error. Body is JSON with the failing fields. | | `429` | Rate limited. | | `5xx` | Server-side. Retry with exponential backoff. | Error body schema: ```json { "error": "rate_limited", "message": "API rate limit exceeded. Retry in 30 seconds.", "request_id": "req_8fae22c4", "details": {} } ``` `request_id` is logged on both sides. Quote it when contacting support. ## Rotating a key In the portal: **Settings → API Keys → Rotate**. The old key keeps working for 24 hours (overlap window) so you can roll your CI without downtime. After 24 hours the old key is hard-revoked. For incident response, **Revoke now** kills the key immediately. ## Curl example ```bash curl -sS https://api.wasviking.com/v1/findings \ -H "Authorization: ApiKey ${WASVIKING_API_KEY}" \ -H "Accept: application/json" ``` ## SDKs There is no official SDK at this time. The API is small and JSON-only; your standard HTTP client is enough. Examples on the next pages use `curl`. ## Test environment Test keys (`wv_test_*`) operate against a separate environment with synthetic data. Use them for SDK builds and contract tests; do not mix live and test keys in the same pipeline. --- # Scopes catalog Section: API Reference Source: https://docs.wasviking.com/api-reference/scopes/ Summary: Every scope a WASViking API key can carry, what it allows, and which UI surface exposes it. API keys are bags of scopes. The portal exposes a curated subset; the backend supports every scope listed here. Keys can be created via the portal UI or programmatically with `api_keys:manage`. ## Read scopes | Scope | Allows | |---|---| | `scans:read` | Read scan status, reports, evidence. | | `findings:read` | Read findings, evidence, AI recommendations. | | `inventory:read` | Read Asset Inventory. | | `templates:read` | Resolve org-scoped scan templates by slug. | | `audit_logs:read` | Read the customer-facing audit log. | | `sca:read` | Read SBOM inventory and Supply Chain Watch. | ## Write scopes | Scope | Allows | |---|---| | `scans:run` | Trigger scans, cancel scans. | | `findings:update` | Status transitions, comments, assignment, bulk updates. | | `targets:manage` | Create, update, archive targets. | | `inventory:export` | Export the Asset Inventory as CSV. | | `sca:submit` | Submit SBOM documents (used by `sentinel sbom`). | | `secrets:submit` | Submit secret detections (used by `sentinel secrets`). | | `sca:ioc` | Manual IOC ingestion (staff and tenants). | | `evidence.share` | Create SBOM Evidence Bundles. | | `webhooks:manage` | Create, rotate, delete webhook subscriptions. | ## Admin scopes | Scope | Allows | Default role | |---|---|---| | `api_keys:manage` | Issue and rotate API keys. | Admin | | `rbac:manage` | Create roles, change role assignments. | Admin | | `org:settings` | Edit organization-wide settings. | Admin | | `billing:read` | Read invoices and usage. | Admin | | `sso:configure` | Configure SAML 2.0 SSO. | Admin | ## Ticketing scopes | Scope | Allows | |---|---| | `ticketing:read` | Read ticketing integration state. | | `ticketing:manage` | Configure Jira / Linear / GitHub Issues sync. | These scopes are mapped to backend capabilities; the portal UI surfaces them as part of the Integrations page rather than as raw scope toggles. ## What is hidden vs surfaced in the UI Five scopes are intentionally hidden in the API Key creation UI to keep the chooser short and avoid least-privilege violations. They are still available via `api_keys:manage` if you need them: - `sca:ioc` - `findings:read` (combined into a broader UI bundle by default) - `audit_logs:read` - `ticketing:read` - `ticketing:manage` Curated UI scopes you will see in the modal: - Read findings and evidence - Run scans - Submit SBOMs (Sentinel) - Submit secrets (Sentinel) - Manage webhooks The verb+noun labeling in the UI is deliberate ("Submit SBOMs" instead of "CycloneDX submit"). ## Scope changes after issuance A key's scope set is immutable. To change scopes: 1. Create a new key with the new scopes. 2. Roll the old key to the new one in your automation. 3. Revoke the old key. This forces an explicit decision and an auditable trail. ## Recommended scope sets | Use case | Scopes | |---|---| | Read-only dashboard | `findings:read`, `inventory:read`, `audit_logs:read` | | CI/CD (full) | `scans:run`, `sca:submit`, `secrets:submit`, `templates:read` | | CI/CD (SCA only) | `sca:submit` | | SIEM ingestion | `findings:read`, `audit_logs:read`, `webhooks:manage` | | Compliance export | `findings:read`, `inventory:read`, `sca:read` | | Partner Console handoff | (none from this catalog; partners use the partner ApiKey realm) | --- # Endpoints Section: API Reference Source: https://docs.wasviking.com/api-reference/endpoints/ Summary: The core REST endpoints, grouped by resource. Every endpoint requires an ApiKey header. Base URL: `https://api.wasviking.com/v1/`. All endpoints require the `Authorization: ApiKey wv_live_…` header. All bodies are JSON. ## Scans | Method | Path | Scope | Purpose | |---|---|---|---| | `POST` | `/scans` | `scans:run` | Trigger a new scan. | | `GET` | `/scans/{id}` | `scans:read` | Scan status and metadata. | | `GET` | `/scans/{id}/findings` | `findings:read` | Findings produced by a scan. | | `GET` | `/scans/{id}/report.pdf` | `scans:read` | PDF report. | | `POST` | `/scans/{id}/cancel` | `scans:run` | Cancel a running scan. | ### Trigger a scan ```bash curl -sS https://api.wasviking.com/v1/scans \ -H "Authorization: ApiKey ${KEY}" \ -H "Content-Type: application/json" \ -d '{ "target": "https://app.example.com", "template": "prod-web-strict" }' ``` ```json { "id": "scan_8fae22c4", "status": "queued", "template": "prod-web-strict", "created_at": "2026-05-21T14:08:11Z" } ``` ## Findings | Method | Path | Scope | Purpose | |---|---|---|---| | `GET` | `/findings` | `findings:read` | List findings, filterable. | | `GET` | `/findings/{id}` | `findings:read` | Finding detail with evidence. | | `PATCH` | `/findings/{id}` | `findings:update` | Status transition, comment, assignee. | | `POST` | `/findings/bulk` | `findings:update` | Bulk update. | ### List findings ```bash curl -sS "https://api.wasviking.com/v1/findings?status=open&min_risk=70" \ -H "Authorization: ApiKey ${KEY}" ``` Supported query parameters: | Parameter | Notes | |---|---| | `status` | `open`, `accepted`, `mitigated`, `false_positive`, `fixed`. | | `category` | `sqli`, `xss`, `cve`, `token_exposure`, etc. | | `severity` | `critical`, `high`, `medium`, `low`. | | `min_risk`, `max_risk` | 0-100. | | `asset_id` | Limit to one asset. | | `since` | ISO 8601 timestamp. | | `cursor` | Pagination cursor. | | `limit` | Default 50, max 200. | ## Targets | Method | Path | Scope | |---|---|---| | `GET` | `/targets` | `inventory:read` | | `POST` | `/targets` | `targets:manage` | | `GET` | `/targets/{id}` | `inventory:read` | | `PATCH` | `/targets/{id}` | `targets:manage` | | `POST` | `/targets/{id}/archive` | `targets:manage` | ## Inventory | Method | Path | Scope | |---|---|---| | `GET` | `/inventory/assets` | `inventory:read` | | `GET` | `/inventory/assets/{id}` | `inventory:read` | | `GET` | `/inventory/components/search` | `sca:read` | | `GET` | `/inventory/sbom` | `sca:read` | ## SBOM (Sentinel submit + read) | Method | Path | Scope | |---|---|---| | `POST` | `/sentinel/sbom/submit` | `sca:submit` | | `GET` | `/sca/bundles` | `sca:read` | | `POST` | `/sca/bundles` | `evidence.share` | | `POST` | `/sca/bundles/{id}/revoke` | `evidence.share` | ## Secrets | Method | Path | Scope | |---|---|---| | `POST` | `/sentinel/secrets/submit` | `secrets:submit` | | `GET` | `/inventory/secrets` | `findings:read` | ## Audit log | Method | Path | Scope | |---|---|---| | `GET` | `/audit-log` | `audit_logs:read` | Supports `since`, `actor`, `action`, `cursor`, `limit`. ## Webhooks | Method | Path | Scope | |---|---|---| | `GET` | `/webhooks` | `webhooks:manage` | | `POST` | `/webhooks` | `webhooks:manage` | | `DELETE` | `/webhooks/{id}` | `webhooks:manage` | | `POST` | `/webhooks/{id}/test` | `webhooks:manage` | See [Webhook events](https://docs.wasviking.com/api-reference/webhook-events/) for the event catalog. ## Pagination Cursor-based. The response carries `next_cursor` and `prev_cursor` when more pages exist. Cursors are opaque; do not parse them. ```json { "items": [...], "next_cursor": "Y3Vyc29yX2FiYzEyMw==", "prev_cursor": null } ``` ## Idempotency POST endpoints accept `Idempotency-Key` header for safe retries. Same key replays the same response within 24 hours. --- # Webhook events Section: API Reference Source: https://docs.wasviking.com/api-reference/webhook-events/ Summary: The event catalog WASViking emits, the payload shape, and how to verify the signature. WASViking® emits signed webhook events on every meaningful state transition. Events are JSON-over-HTTPS to a URL you register, with HMAC-SHA256 signing. ## Registering a webhook ```bash curl -sS https://api.wasviking.com/v1/webhooks \ -H "Authorization: ApiKey ${KEY}" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/wasviking-hook", "events": ["finding.escalated", "finding.sla_breached"], "description": "SIEM ingestion" }' ``` ```json { "id": "wh_88aa12", "url": "https://example.com/wasviking-hook", "secret": "whsec_xxxxxxxxxxxxxxxxxxxx", "events": ["finding.escalated", "finding.sla_breached"] } ``` The `secret` is shown once at creation. Store it; you cannot retrieve it later. ## Event catalog ### Findings | Event | When | |---|---| | `finding.created` | A new finding is written. | | `finding.reopened` | A previously closed finding came back. | | `finding.escalated` | Status or risk score jumped to a higher band. | | `finding.status_changed` | Any status transition. | | `finding.sla_breached` | Finding crossed its SLA window. | | `finding.assigned` | Owner field updated. | ### Scans | Event | When | |---|---| | `scan.queued` | Scan accepted into the queue. | | `scan.started` | Scan transitioned to running. | | `scan.completed` | Scan completed (with or without findings). | | `scan.failed` | Engine error or hard timeout. | | `scan.canceled` | Operator cancellation. | ### Inventory and assets | Event | When | |---|---| | `asset.first_seen` | New asset discovered. | | `asset.disappeared` | Asset is no longer reachable. | | `asset.reappeared` | Asset returned after a `disappeared`. | ### Supply chain | Event | When | |---|---| | `sbom.submitted` | New SBOM landed via `/sentinel/sbom/submit`. | | `sbom.intel_match` | Daily OSV+KEV ingest matched a live SBOM. | | `bundle.created` | [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/) issued. | | `bundle.accessed` | Recipient accessed a share. | | `bundle.revoked` | Operator revoked a share. | ### Secrets | Event | When | |---|---| | `secret.detected` | New secret detection landed. | | `secret.verified_live` | Live verifier confirmed the secret is active. | ### Edge intel | Event | When | |---|---| | `edge.correlation_match` | Adversary traffic matched an open finding (risk amplification). | ## Payload shape Every event is a JSON object with: ```json { "id": "evt_88aa12d4", "type": "finding.escalated", "created_at": "2026-05-21T14:08:11Z", "organization": "acme", "data": { "finding_id": "f_8ab2", "category": "graphql_bola", "cwe": "CWE-639", "risk_score": 88, "previous_risk_score": 62, "asset_id": "a_19ff", "asset_criticality": "high", "sla_window_hours": 24, "primary_risk_category": "authorization", "compliance": ["PCI 6.5.8", "LGPD Art.46"] } } ``` `type` tells your consumer how to read `data`. Treat unknown event types as forward-compatible: log and skip rather than fail. ## Signing WASViking signs every payload with HMAC-SHA256 using your webhook secret. Headers on every delivery: | Header | Value | |---|---| | `Wasviking-Signature` | `t=,v1=` | | `Wasviking-Event` | The event type, mirrored in the body. | | `Wasviking-Delivery` | UUID for this delivery attempt. | ### Verifying in Python ```python import hmac, hashlib, time def verify(body: bytes, header: str, secret: str, tolerance: int = 300) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) timestamp = int(parts["t"]) sig = parts["v1"] if abs(time.time() - timestamp) > tolerance: return False payload = f"{timestamp}.".encode() + body expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, sig) ``` ### Verifying in Node ```js const crypto = require("crypto"); function verify(body, header, secret, tolerance = 300) { const parts = Object.fromEntries(header.split(",").map(p => p.split("="))); if (Math.abs(Date.now() / 1000 - Number(parts.t)) > tolerance) return false; const payload = `${parts.t}.${body}`; const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex"); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)); } ``` ## Delivery semantics - **At-least-once.** A network error retries with exponential backoff for up to 24 hours. - **Ordered per finding.** Events for the same finding are delivered in order. - **Test delivery.** `POST /webhooks/{id}/test` sends a synthetic `webhook.test` payload to verify your endpoint. ## Common consumer patterns - **SIEM**. Subscribe to `finding.*`, `secret.verified_live`, and `sbom.intel_match`. Forward straight to your SIEM index. - **Slack / Teams**. Subscribe to `finding.escalated` and `finding.sla_breached`. Most teams find broader subscriptions noisy. - **Internal automation**. Subscribe to `asset.first_seen` to kick off internal asset workflows. --- # Rate limits Section: API Reference Source: https://docs.wasviking.com/api-reference/rate-limits/ Summary: Per-key limits, how they are signaled, and how to handle 429s correctly. WASViking® applies rate limits per API key. Limits are sized for normal operational use and are documented per endpoint class. ## Default limits | Endpoint class | Limit | |---|---| | Read endpoints (`GET`) | 600 requests per minute. | | Write endpoints (non-scan) | 60 requests per minute. | | Trigger scans | 12 concurrent scans per organization. | | SBOM submit | 60 submissions per minute. | | Secrets submit | 60 submissions per minute. | | Webhook test deliveries | 10 per minute per webhook. | ## Headers Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Limit for this endpoint class. | | `X-RateLimit-Remaining` | Requests remaining in the current window. | | `X-RateLimit-Reset` | Seconds until the window resets. | When throttled: | Status | Header | Meaning | |---|---|---| | `429` | `Retry-After: 30` | Wait at least 30 seconds before retrying. | ## Handling 429 correctly - Respect `Retry-After`. Do not retry sooner. - Use exponential backoff with jitter on repeated 429s. - Cache idempotent read responses where you can. - Coalesce. Most consumers issue many small reads that could be one paginated query. A reasonable retry policy in pseudocode: ```python delay = float(headers.get("Retry-After", 30)) for attempt in range(5): sleep(delay + random.uniform(0, 0.5 * delay)) response = call_api() if response.status_code != 429: return response delay *= 2 raise RetryExceeded() ``` ## Concurrency caps The 12 concurrent scans cap is per organization, not per key. If you run multiple CI pipelines against the same org, plan for it. The portal shows current concurrency under **Settings → API Usage**. When the cap is hit and you call `POST /scans`, the API: 1. Waits up to 60 seconds for a slot to open. 2. If a slot opens, accepts the scan and returns `201 Created`. 3. If no slot opens, returns `429` with `Retry-After`. In `sentinel ci`, this surfaces as exit code 77 (`scan_capacity`). ## Monthly metering Some plans meter: - AI recommendations per month. - Scans per month. - SBOM submissions per month. When the meter is exhausted on a metered scope, the API returns `403` with `error: "metered"`. The portal shows the current meter state under **Settings → API Usage** and **Billing → Usage**. `sentinel ci` surfaces this as exit code 78. ## Raising your limits Limits can be raised per organization for sustained operational need. Open a request from **Settings → API Usage → Request increase** or email [contact@wasviking.com](mailto:contact@wasviking.com). Include: - The endpoint class and the new target. - Peak QPS and average QPS you expect. - A short justification. We approve increases that match real usage. We refuse blanket "remove all limits" requests. ## What the limits are NOT - They are not a hard quota on the organization (other than monthly metering, which is explicit). - They are not per-user; they are per-key. - They are not enforced at the edge separate from the application. The WASViking application stack enforces them; the Cloudflare edge runs a separate, much larger floor against abuse for public endpoints. --- # Framework mapping Section: Compliance Source: https://docs.wasviking.com/compliance/framework-mapping/ Summary: How WASViking maps findings to PCI DSS v4.0, LGPD, GDPR, BACEN, and ISO 27001:2022 controls from one rule table. WASViking® maps every finding category to specific controls across five frameworks, from a single rule table. The mapping is rendered in the PDF report and in the portal Compliance tab from the same source of truth. ## Frameworks covered | Framework | Scope of mapping | |---|---| | PCI DSS v4.0 | Cardholder data environment, AppSec, vulnerability management. | | LGPD | Article 46 security measures. | | GDPR | Article 32 technical measures, processor accountability. | | BACEN (CMN 4.893 and BCB 85) | Cybersecurity policy for banks (Resolution CMN 4.893/2021) and payment institutions (Resolution BCB 85/2021), as amended in December 2025. Art. 3 controls: vulnerability assessment and correction, periodic tests and scans, secure development, secure configuration profiles. | | ISO 27001:2022 | Annex A.5-A.8 technical controls. | ## How a finding maps Each finding category carries a list of control IDs per framework. The mapping is many-to-many: a single SQLi can map to PCI 6.5.1, LGPD Art.46, GDPR Art.32, BACEN Art.3 §2 VIII, and ISO A.8.25 at the same time. Example, condensed: | Finding category | PCI v4.0 | LGPD | GDPR | BACEN | ISO 27001 | |---|---|---|---|---|---| | `sqli` | 6.5.1, 6.4.2 | Art.46 | Art.32(1)(b) | Art.3 §2 VIII | A.8.25, A.8.28 | | `xss` | 6.5.7 | Art.46 | Art.32(1)(b) | Art.3 §2 VIII | A.8.28 | | `graphql_bola` | 6.5.8 | Art.46 | Art.32(1)(b) | Art.3 §2 IX | A.8.3, A.5.15 | | `jwt_alg_confusion` | 6.5.10 | Art.46 | Art.32(1)(d) | Art.3 §2 I | A.8.5 | | `vulnerable_component` | 6.2, 6.3 | Art.46 | Art.32(1)(d) | Art.3 §2 VIII | A.8.8, A.8.9 | | `token_exposure` | 8.2, 8.3 | Art.46 | Art.32(1)(b) | Art.3 §2 IV | A.8.5 | | `tls_misconfiguration` | 4.2 | Art.46 | Art.32(1)(a) | Art.3 §2 II | A.8.24 | The full mapping table lives in code and is the source of truth. Adding a new analyzer category requires adding its line, by policy. ## Primary catalog per scan A scan profile selects a **primary compliance catalog**. The portal Compliance tab and the PDF report render that catalog first. | Scan profile | Primary catalog (default) | |---|---| | `full` | Driven by industry signal on the org. | | `web_app` | OWASP Top 10 + ISO 27001. | | `api_jwt` | OWASP API Security + ISO 27001. | | `soap` | BACEN for BR financial; ISO 27001 otherwise. | | `network` | ISO 27001 + PCI infrastructure. | The other four catalogs remain available in the Compliance tab; only the primary is leading. ## What this is not The mapping is a tool to find the right controls and produce evidence for them. It is not a substitute for an auditor's judgement and it is not a guarantee that any control is met. A control is met when the finding it maps to has been mitigated and the mitigation evidence is on file. WASViking can also help with the second half (the [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/), [Posture Shares](https://docs.wasviking.com/compliance/posture-shares/), the [Supply Chain Intel](https://docs.wasviking.com/capabilities/supply-chain-intel/) Exploitability Report and OpenVEX attestation for third-party components, and the audit log), but the operator decides what is "met." ## Where in the portal - **Reports → Scan report PDF.** The Compliance section renders the primary catalog and lists framework hits per finding. - **Findings.** Filter by `compliance` (e.g., `pci:6.5.1`) to see every finding that maps to a specific control. - **Compliance dashboard.** Per-control counts open, accepted, mitigated, fixed. Shows the team where the auditor's questions will land first. --- # SBOM Evidence Bundle Section: Compliance Source: https://docs.wasviking.com/compliance/evidence-bundle/ Summary: The signed artifact your auditor or customer accepts in lieu of a portal access. The **SBOM Evidence Bundle** is a signed package WASViking® builds directly from your tenant data. It is the artifact you hand to an auditor, a customer due-diligence team, or a regulator, without giving them portal access. ## What is in the bundle | Artifact | Notes | |---|---| | CycloneDX 1.5, per submission | Original Sentinel submissions, signed. | | Consolidated CycloneDX | Org-wide aggregate at bundle creation time. | | Cover PDF | Brand cover, executive summary, organization metadata. | | Drift CSV | Component drift over the chosen window. | | Findings CSV | Open and accepted findings tied to components. | | Audit CSV | Audit log slice scoped to the bundle window. | | Compliance CSV | Per-control hits for the primary catalog. | | `verification.txt` | The chain of signatures and how to verify them. | ## How it is shared Token plus password, time-limited, revocable. Same model as [Posture Shares](https://docs.wasviking.com/compliance/posture-shares/). For the portal walkthrough with screenshots, see [SBOM Evidence Bundles](https://docs.wasviking.com/getting-started/sbom-evidence-bundles/). 1. Operator clicks **Inventory → SBOM → Generate Bundle**. 2. WASViking builds and signs the bundle. 3. Operator gets a one-time share URL and a separate password. 4. Operator delivers each piece out of band (the URL by email, the password by phone or chat, for example). 5. Recipient enters the password on first access and downloads. Bilateral audit log records every access on both sides. Operators can revoke any time; revocation is immediate. ## How verification works `verification.txt` documents: - The signing chain for every artifact in the package. - The signature algorithm and key fingerprint. - The bundle creation time and the window it covers. - The hashing strategy for the consolidated CycloneDX. A recipient can verify with: ```bash # Pseudocode; the file contains exact commands per artifact cosign verify-blob \ --signature bundle.sig --certificate bundle.crt \ --certificate-identity-regexp '^evidence@wasviking.com' \ --certificate-oidc-issuer https://accounts.google.com \ consolidated-cyclonedx.json ``` The WASViking signing identity and OIDC issuer for each environment are listed in the file. ## API | Method | Path | Scope | Purpose | |---|---|---|---| | `POST` | `/v1/sca/bundles` | `evidence.share` | Issue a bundle. | | `GET` | `/v1/sca/bundles` | `sca:read` | List bundles for the org. | | `POST` | `/v1/sca/bundles/{id}/revoke` | `evidence.share` | Revoke a bundle. | | `GET` | `/v1/sca/bundles/{id}/audit` | `audit_logs:read` | Access log. | Example, issue a bundle: ```bash curl -sS https://api.wasviking.com/v1/sca/bundles \ -H "Authorization: ApiKey ${KEY}" \ -H "Content-Type: application/json" \ -d '{ "window_days": 90, "include": ["sbom", "drift", "findings", "audit", "compliance"], "description": "Customer XYZ due diligence" }' ``` Response: ```json { "id": "bundle_88aa12", "share_url": "https://posture.wasviking.com/b/88aa12d4", "password": "9F7K-X3WT-4B2P-LMNE", "expires_at": "2026-08-19T00:00:00Z", "scope": ["sbom", "drift", "findings", "audit", "compliance"] } ``` Both `share_url` and `password` are shown once at issuance. Store them. ## When to use it - **Customer due diligence.** Replace the PDF dump with a verifiable artifact your prospect can validate. - **Auditor binder.** Attach the bundle to the per-control evidence in your audit binder. - **Procurement gates.** Some procurement processes ask for an SBOM and proof of continuous scanning. The bundle answers both. ## When NOT to use it - For ad-hoc internal sharing inside your organization, the portal Compliance tab is faster. - For a Slack channel notification, use webhooks; the bundle is not the right unit. --- # Posture Shares Section: Compliance Source: https://docs.wasviking.com/compliance/posture-shares/ Summary: Prove your security posture to a third party without giving them portal access. A **Posture Share** is a tokenized, password-protected, time-limited snapshot of your WASViking® security posture, accessible at `posture.wasviking.com`. The pattern is the same as the SBOM Evidence Bundle: zero-knowledge share, full access log for the owner, revocable any time. ## What recipients see For the portal walkthrough with screenshots, see [Posture Shares](https://docs.wasviking.com/getting-started/posture-shares/) in Getting Started. A Posture Share renders a signed, aggregated report. It never opens a window into the portal: - Posture Score (0 to 100) with last assessment date and 90-day trend. - **Why this score**: the exact points each term of the formula takes from 100, so the number can be audited without reading the methodology. - **Posture history**: a chart and 90-day / 30-day / today cards rebuilt from earlier signed snapshots. Nothing is recomputed after the fact. - Framework correlation indicators (PCI DSS, LGPD, GDPR, SOC 2, ISO 27001): the mapped findings split into remediated, accepted and open. A treatment picture, never a certification status. - Asset coverage. - At the Standard level: open findings by severity, issue categories with OWASP mapping, and 30-day activity, with remediation and acceptance always shown apart. - **Risk treatment**: open, formally accepted and remediated findings by severity. Accepting a risk is a decision, not a fix: it leaves the score but never leaves the report. - At the Detailed level: remediation performance over the last 90 days (median time to remediate, SLA percentages). - **Assessment coverage** per surface (with the Security domains and Infrastructure sections): how much of each surface the figures stand for, how many surfaces have a gap, and the risk of what is not covered. - **Evidence origin** on every domain figure: externally observed, agent verified, pipeline verified, repository verified, or application package analyzed. No figure in the report is self-attested. - Optional sections chosen per link: **Security domains** (Application Security and External Exposure, each with its own index), **Infrastructure** (fleet index, patch currency, configuration hardening, as fleet-level percentages only) and **Active security controls** (state only). - A Methodology & Scope Statement that says exactly what was assessed, how each figure is computed, and what the report is not. Recipients never see: - Hosts, URLs, payloads, packages, CVE identifiers, or any single finding. - The size of your fleet or the list of your targets. - Raw HTTP evidence. - Your audit log or anything outside the chosen scope. A domain that was never assessed reads "Not assessed"; it is never shown as clean. ## How sharing works 1. **Operator scopes the share.** Pick the domain scope (or the whole organization), the exposure level, and which sections to include. 2. **WASViking builds the snapshot.** Point-in-time rebuild, signed. 3. **Operator gets share URL + password.** Each delivered out of band. 4. **Recipient lands on `posture.wasviking.com/s/`.** Password prompt before any data renders. 5. **Bilateral audit log records every access.** ## Operator controls | Action | Effect | |---|---| | Rebuild | Builds a new snapshot version. Links in live mode follow it; pinned links keep the version they were created with. | | Revoke | Immediately kill the share. Cannot be undone. | | Extend | Push the expiry forward, up to 180 days from the creation of the link. | | Re-emit | Generates a new URL and a new password, and revokes the old link in the same action. | Every action is recorded in the lifecycle audit of the link, next to its access log. Both are visible to the operator on the details page of the link, and the access log can be exported as CSV. The recipient sees neither of them. ## When to use a Posture Share - **Sales motion.** A prospect's security team asks "show us your posture." A Posture Share is faster than a SOC 2 report attempt and fresher than a stale PDF. - **Vendor due diligence.** Same use case, opposite direction; respond to a vendor questionnaire with a live snapshot. - **Investor diligence.** Demonstrate operational security maturity without exposing the operating console. - **Auditor handoff.** Pair with the [SBOM Evidence Bundle](https://docs.wasviking.com/compliance/evidence-bundle/) for the full-coverage audit package. ## When NOT to use it - For deep operator collaboration. Add the operator as a Read-only user in your org instead; they get the live portal, scoped by RBAC. - For high-frequency status updates to a team you already trust. Use a webhook into their Slack instead. ## Automation Posture Shares are created and managed in the portal. There is no public API for them today, and share events are not part of the webhook catalog. What each link can do on its own is notify you by email: on the first view, on an unusual volume of accesses, and when it revokes itself after repeated wrong passwords. ## Security properties - **Tokenized.** Share code is opaque, not enumerable. - **Password-gated.** Independent secret; both required. - **Time-limited.** Expiration is chosen at creation, from 1 hour to 90 days (7 days is the default), and can be extended up to 180 days from creation. - **Revocable.** One-click revoke; effect is immediate. - **Audited.** Every access, failed password attempt and PDF download is logged with its time and IP address, and is visible to the operator. The recipient sees none of it. - **Brute-force resistant.** Repeated wrong passwords lock the link for a while, and continued attempts revoke it automatically and notify its owner. Every answer of the public host takes a uniform minimum time, so a wrong link and a wrong password look the same from outside. ## What the recipient gets in their email WASViking does not auto-email the recipient. The operator delivers the URL and the password through whatever channel matches the sensitivity of the share (typically the URL by email and the password by phone or Signal). This is deliberate: no auto-email avoids a phishing training problem. --- # Partner Console overview Section: Partner Console Source: https://docs.wasviking.com/partner-console/overview/ Summary: What the partner console is, where it lives, and how it sits next to the customer portal. The Partner Console is a dedicated, separate console for partners who operate WASViking® on behalf of customers. It lives at `partners.wasviking.com` (and `partners.localhost` in dev). It is isolated from the customer portal by host, by URL config, by identity table, and by data access boundary. ## Who uses it | Audience | What they do | |---|---| | Resellers | Quote, provision, manage commercial accesses for customers. | | MSSPs | Operate the platform for customers under co-managed or fully-managed engagements. | | Technology Alliances | Use the public REST API; the console is supplementary. | ## What lives in it - **Dashboard** with charts: customer portfolio funnel, quote pipeline, plan mix, top customers by partner cost, aggregate security posture. - **Customers** list with operating model, plan, status, usage. - **Customer detail** with real posture metrics (gated by operating model). - **Quote engine** with live price preview. - **Quotes** in flight. - **Billing** with consolidated monthly previews, PDF and CSV export. - **Demos** with ephemeral fully-populated tenants, 24h auto-destroy. - **Team** with role-gated invites and anti-lockout. - **Audit log** scoped to the partner. - **Help & FAQ** humanized, bilingual. - **Settings** with language switcher. ## What is NOT in it - Customer-managed engagements do not show posture data. The partner sees commercial state only. This is enforced in code, not in a contract clause. - WASViking-side staff tools for managing partners live on the internal portal host, not on the partner host. - Real session impersonation does not exist. Impersonation is a read-only console preview only, audited. - White-label / custom branding is a permanent design decision: it is not implemented and will not be. The Partner Console is WASViking- branded. ## How a partner gets access 1. Partner submits an application at `partners.wasviking.com/apply`. B2B email policy refuses free, public, and disposable domains at submission, same engine as customer registration. 2. WASViking staff review the application and either approve or decline. 3. On approval, the partner admin gets an invitation email with a one-time set-password link. 4. The partner admin sets a password, sets MFA, and lands on the Partner Dashboard. ## Identity boundary The Partner Console runs on a separate identity table (`PartnerUser`, session key `partner_user_id`). Partner operators are not Django users. The customer portal cannot see partner operators; the partner host cannot see customer users. This is by design and is enforced at every view. ## Multi-tenancy A partner can manage many customer accesses. Each access is a soft reference to a customer organization, not a hard foreign key, so the two schemas remain independent and migrations stay separate. ## Documentation in this section - [Operating models](https://docs.wasviking.com/partner-console/operating-models/) - [Quote engine and pricing](https://docs.wasviking.com/partner-console/quote-engine/) - [Customer demos](https://docs.wasviking.com/partner-console/customer-demos/) - [Billing](https://docs.wasviking.com/partner-console/billing/) --- # Operating models Section: Partner Console Source: https://docs.wasviking.com/partner-console/operating-models/ Summary: Customer-managed, co-managed, fully-managed. Same product, three relationship modes, the boundary in code. WASViking® ships three operating models that map to how partner-customer relationships actually work. The model is declared per customer access and enforced in code at every view. ## The three models | Model | Posture visibility to partner | Best for | |---|---|---| | **Customer-managed** | None. Partner sees commercial state only. | Resellers without a security operations team. | | **Co-managed** | Findings, inventory, SBOM, posture. Customer keeps ownership. | MSSPs supporting an internal security team. | | **Fully managed** | Full posture. Customer access is read-only or off. | Full-service MSSP delivery with no internal security operator. | ## What "commercial state only" means In a customer-managed access, the partner sees: - Plan, billing cycle, subscription status. - Renewal date. - Usage and metering against the plan. - Add-ons configured. - Trial state and conversion windows. The partner does NOT see: - Findings, inventory, posture metrics. - Scan history, scan reports, evidence. - Security configuration (RBAC, SAML, API keys). - The customer's audit log. This is the right model when the partner is purely a commercial seller and security operations stay with the end customer. ## Where the enforcement lives The boundary is enforced in `org_link.partner_posture_visible(access)`: ```python def partner_posture_visible(access) -> bool: return access.operating_model != "customer_managed" ``` Every customer-detail view and every dashboard widget calls this gate before rendering posture data. Aggregate posture metrics in the partner dashboard explicitly exclude customer-managed accesses from the sum, so a partner with 10 co-managed customers and 50 customer- managed customers does not see false aggregate numbers. ## Setting the model The operating model is set at quote time and is part of the provisioned access. To change it: 1. Open the customer detail page. 2. **Operating model → Change**. 3. Pick the new model and provide a brief reason. 4. Confirm. Every change emits a `PartnerAuditLog` event with the old and new model and the operator who made the change. The customer's own audit log also reflects the change on the customer side. ## Billing is decoupled The operating model controls **what the partner sees**. It does NOT control billing. Billing stays consolidated to the partner regardless of the model. This separation is deliberate. A customer-managed access can still be billed to the partner if the commercial agreement says so; the only thing the model changes is operational visibility. ## Trials and operating models POC / trial accesses default to customer-managed. The end customer must explicitly elect co-managed or fully-managed during trial setup. The reason: in a trial, the customer is evaluating both the platform and the partner relationship, and giving up posture visibility on day one is a high bar. ## What "fully managed" implies operationally When an access is fully-managed, the partner is the operating party. Practical implications: - The partner runs the scans. - The partner triages findings. - The partner handles SLA windows. - The partner produces the Compliance evidence and Posture Shares. If the customer wants read-only access in fully-managed mode, the partner invites them as a Read-only user inside the customer's org. Their access is independent of the partner console. ## Defaults - New quote default: **customer-managed**. - After conversion to paid: stays as quoted. Operator changes explicitly. - Per-tier default: not configurable today. The choice is per-access. --- # Quote engine and pricing Section: Partner Console Source: https://docs.wasviking.com/partner-console/quote-engine/ Summary: How the live quote calculator works, how guardrails protect unit economics, and how auto-provisioning fits. The Partner quote engine prices a deal in real time as the partner operator configures it. Tier discounts apply per component, category caps protect unit economics, and guardrails refuse deals that would breach minimum margin. ## What gets priced A quote has three pricing surfaces: | Surface | Notes | |---|---| | **Plan** | Customer list price minus the partner's tier discount on plans. | | **Add-ons (metered)** | Per-category discount cap on top of the tier. | | **Modules (flat entitlements)** | Cost-anchored flat price per module, tier discount within the module category cap. | The same customer list price is used everywhere. The partner price is the spread: list minus tier discount, clamped per category. ## Tiers (operational) The tier names are public; the actual discount structure is shared in the partner brief after application. The Partner Console renders the live spread in each quote, never as a public table. | Tier | Designed for | |---|---| | Silver | Resellers entering the program. | | Gold | MSSPs and resellers actively building pipeline. | | Platinum | Design partners shaping the program. | ## Live quote preview As the operator changes plan, add-ons, or modules, the preview endpoint recomputes: - Monthly customer price, partner price, partner earnings. - Annual customer price, partner price, partner earnings. - Per-component breakdown. - Whether the quote breaches any guardrail (BLOCK / REVIEW / OK). The preview endpoint returns the **public view** only. Internal cost, minimum price, and margin numbers never reach the partner-facing context. ## Auto-provisioning threshold A quote that: - Is below the partner's auto-provision MRR ceiling, - Does not breach any guardrail, - Has `provisioning_mode = auto_below_threshold`, - Has projected MRR within the tier's limit, is provisioned automatically on submit. The customer access is created, the subscription starts, and an invitation goes out to the customer admin via the canonical invitation flow. Quotes above the threshold or with any guardrail near-breach route to WASViking review. ## Guardrails Guardrails enforce the minimum-margin floor and prevent quotes that would damage unit economics. Guardrail values are operator-managed on the WASViking side and applied to every partner quote. Examples of guardrails: - Minimum monthly margin per access in absolute dollars. - Minimum margin percentage per access. - Per-category maximum discount. - Storage cost driver, scan cost driver, edge cost driver, retention cost driver. If a quote breaches a guardrail, the live preview surfaces the breach and the submit button blocks until either the deal is adjusted or the partner operator routes for review. ## Protected categories Some add-on categories carry conservative commercial terms across all tiers by policy: - **Edge Threat Radar.** Continuous per-domain cost. - **Exposure Intel retention.** Storage cost grows linearly. - **Sentinel.** Engineering support overhead. - **SBOM.** OSV+KEV refresh cost. Discounts on these categories default to 0%. Platinum tier partners can negotiate exceptions, subject to guardrail review. ## Trials and POCs A quote can request `trial_days`. WASViking approves the request or lowers it within the requested range. Trial accesses: - Are NOT billed. - Have **Exposure Intel modules withheld** at provisioning time (re-applied on conversion to paid). - Are auto-suspended at T+0 if not converted, with a T-3 warning event in the audit log. - Convert to paid in one click (early or after suspension). ## Annual billing models Two choices per access: - **Annual prepaid.** Single annual charge; excluded from the consolidated monthly invoice; daily renewal task re-charges and rolls the commitment. - **Monthly commit (12 months).** Stays in the monthly consolidated invoice; locks the access for 12 months. Minimum monthly commit gate applies. The choice is gated by the quote preview (`annual_commit_eligible`). Server-side revalidation rejects an ineligible monthly_commit and falls back to prepaid with a warning. ## Where this lives in the console - **New customer** form: plan, add-ons, modules, trial, billing cycle, operating model. Live sticky preview bar shows the running cost. - **Quotes** list: in-flight quotes, awaiting approval or auto- provisioned. - **Customer detail**: change requests recompute the quote and route the same way. Once a quote converts, charges land on the consolidated monthly invoice. See [Billing](https://docs.wasviking.com/partner-console/billing/). --- # Customer demos Section: Partner Console Source: https://docs.wasviking.com/partner-console/customer-demos/ Summary: Provision a populated demo tenant in minutes. Auto-destroy in 24 hours. Zero garbage left behind. WASViking® demos are not slide decks. The Partner Console provisions a fully populated tenant with synthetic data anchored to today's date, seeded across every product surface a prospect will ask about. Demos hard-destroy 24 hours after the scheduled presentation. ## What a demo contains | Surface | Population | |---|---| | Targets | 5 | | Assets | 17 | | Findings | ~118 across all 15 categories with severity and SLA mix | | Exploit paths | 74 from the real materializer engine | | Scans | 30 with rich port CVE data | | SSL certificates | 5 active, 4 expiring, 3 valid, 1 expired | | Edge threat intel summaries | 16 to 18 | | SBOM submissions | 3 | | Exposure intel records | 4 | | Headline finding | MySQL 3306 exposed, public CVEs | The data is synthetic but realistic. Names, IPs, paths, and CVE refs look like a real environment. Dates lead with today and step back so the dashboard never looks stale. ## Lifecycle ``` scheduled ──▶ active ──▶ destroyed │ ▲ └─ at presentation_at │ └─ at presentation_at + 24h ``` | State | Meaning | |---|---| | `scheduled` | Provisioned, waiting for the presentation time. | | `active` | Live now. Partner can hand login credentials to the prospect. | | `destroyed` | Hard-deleted. Audit snapshot survives in the partner audit log. | Hourly Celery task `partners.expire_demos` runs the state transitions. ## Quotas | Quota | Default | |---|---| | Active demos per partner | 3 | | Demo lifetime | 24 hours from presentation_at | | Demos per partner per month | Plan-dependent | When at quota, the **New demo** action surfaces a "destroy one first" message. ## Login routing Demo logins use a synthetic email under `@demo.wasviking.com` (null MX, never delivered). MFA codes and new-device alerts route to the partner operator's real email, not the synthetic mailbox, via the `demo_email.resolve_auth_recipient` safeguard. If the routing cannot resolve (corrupted partner data), MFA mail is **suppressed** and logged as an operator alert. The platform refuses to let demo MFA leak to a real-looking address by accident. ## Creating a demo 1. **Demos → New demo.** 2. Fill the form: prospect name, presentation time (`datetime-local`, tz-naive; the form writes a tz-aware ISO-8601 hidden field). 3. Check the demo-use attestation (24-hour synthetic showcase, no real prospect data, no real scans against prospect targets). 4. Submit. Provisioning steps run inside a single Django transaction: 1. Internal organization flag set (`is_internal=True`, bypasses license). 2. Login user created with a temporary password. 3. Portal MySQL seed: 17 assets, 118 findings, 30 scans, etc. 4. Exploit Path Graph materializer runs on the demo org. 5. After commit, the API-side Mongo seed runs via the secured `POST /api/v1/sensor/demo/seed/` endpoint, populating SSL, Edge, and scan-detail. Credentials arrive at the partner operator's real email. ## Destroying a demo `destroy_demo` runs: 1. API-side purge first (deletes Mongo `{organization_id, demo: true}` docs). 2. Snapshot of partner-added Targets and ScanResults to `PartnerAuditLog` so traceability survives the hard delete. 3. Cascade-delete the demo Organization (drops portal MySQL). 4. Audit event `demo.destroyed` carries the snapshot. Zero garbage left behind in either MySQL or Mongo. ## Operator runbook - `reseed_demo ` re-runs the seed for an existing demo (portal + API). Refuses non-`is_internal` orgs. - The Demos page shows current state, presentation time, and the audit trail. - Failed provisioning leaves a clean state (the transaction rolls back). ## Live activity on a demo Demo orgs allow the prospect to add real targets and trigger real scans during the live walkthrough. Consent is enforced at Target creation (the regular platform policy), not by a separate partner attestation; duplicating it was tried and removed. Activity from the live session is snapshotted into the destroy event so the partner can review what was done after the demo wraps. ## Demo screenshot tile Demo Targets get an inline-SVG mock screenshot data URI as `thumbnail_url`, not a broken S3 presigned URL. This is gated to `is_internal=True` orgs only. Real customer accounts are not affected. Demo Targets are grouped under one **Demo** group so the Targets list shows "Demo" in the Group column. --- # Billing Section: Partner Console Source: https://docs.wasviking.com/partner-console/billing/ Summary: Consolidated monthly invoices, annual prepaid, monthly commit, soft-limit overage. How partner billing actually works. Partner billing is consolidated to the partner, not to each end customer. A single monthly invoice covers every active customer access under the partner. ## The monthly invoice Each month, WASViking® runs the Celery task `partners.monthly_invoice_previews` (1st @ 06:00 UTC) and renders a preview for each partner. Contents: - Per-access lines: plan, modules, metered packs, prorations. - Soft-limit overage if any access exceeded its provisioned capacity. - Trials excluded (POC accesses are never billed). - Annual prepaid accesses excluded (charged separately). Available in **Billing → Invoices** as PDF (WeasyPrint) and CSV. Access to the invoice is gated by the `partner_billing` capability and every view is audited. ## Soft-limit overage WASViking does NOT hard-block at the limit. Instead: - Capacity is provisioned per access (max targets, scan slots, AI recommendations per month, etc.). - Usage above provisioned capacity is **billable overage**, computed per unit and added to the next monthly invoice. - The partner sees the overage projection in real time on the customer detail page. This keeps the platform operational at burst time and bills for the real consumption afterward. ## Annual prepaid For accesses on `annual_billing_mode = prepaid`: - A single annual charge runs at access creation or renewal. - The access is excluded from the monthly consolidated invoice. - The daily Celery task `partners.annual_renewals` (06:15 UTC) re-charges and rolls the commitment 12 months forward. Annual prepaid is the cleanest model for partners that prefer to lock revenue and avoid a monthly accounting hit. ## Monthly commit (12-month) For accesses on `annual_billing_mode = monthly_commit`: - Stays in the monthly consolidated invoice. - Locks the access for 12 months from the commitment start. - Subject to the `annual_monthly_commit_min` minimum (guardrail- controlled). - Eligibility checked at quote time (`annual_commit_eligible`). Server-side revalidation refuses an ineligible monthly_commit selection at submit and falls back to prepaid with a warning. ## Trial conversion timing The commitment clock starts: - At access creation for new paid accesses. - At conversion for trial accesses. `convert_trial_to_paid` flips Subscription to `active`, resets `current_period`, clears `trial_start`/`trial_end`, and refreshes `renewal_date`. From that moment the customer is billed to the partner on the normal monthly cycle (no proration of the trial window). ## Invoice export | Format | Notes | |---|---| | PDF | WeasyPrint, `invoice_pdf.html`, branded. Best for accounting handoff. | | CSV | One line per access plus aggregate rows. Best for the partner's downstream billing system. | Both formats are produced from the same invoice data; signing both is roadmap. ## What WASViking does NOT bill - Trial accesses (during the trial). - Customer accesses on a disabled subscription. - Demo organizations (they have no `PartnerOrganizationAccess` row; the billing pipeline iterates `partner.customers` only). ## What the partner controls - Plan and add-on selection per access. - Operating model per access. - Annual prepaid vs monthly commit (per access). - Trial days requested per quote (WASViking approves or lowers within request). ## What WASViking controls - The customer list price. - The tier discount structure. - The guardrail floor. - The internal cost model used to compute margins (never shown to the partner). ## What partners cannot do - Consolidated billing TO the end customer through WASViking. The partner bills the customer directly; WASViking bills the partner. This is a deliberate scope decision. - White-label invoices. The invoice is WASViking-branded by design. The pricing inputs behind every invoice line (tier discount, guardrails, annual choices) are fixed at quote time. See [Quote engine and pricing](https://docs.wasviking.com/partner-console/quote-engine/). --- # Platform architecture Section: Security Source: https://docs.wasviking.com/security/platform-architecture/ Summary: How WASViking is built and operated. The security posture, the isolation model, and the engineering practices behind the platform. This page is the architecture summary the procurement and security teams of prospects ask for. It covers the components, how they communicate, how data is protected, and the engineering practices behind the platform. It is written so it can be attached to a vendor diligence response without further editing. ## Components | Component | Role | |---|---| | **Customer Portal** | Multi-tenant web console for customers. Organization management, RBAC, SSO, bilingual UI. | | **API and engine fabric** | Public REST API, scanning orchestration, all DAST analyzers, AI orchestration, threat intel ingest, OAST collaborator, Sentinel gRPC server. | | **Sentinel agent** | Customer-installed, single static binary. Outbound mTLS gRPC tunnel for internal DAST. CLI subcommands for SBOM, secrets, CI gates. | | **Marketing and documentation** | `wasviking.com` and `docs.wasviking.com`. Public surface. | | **Partner Console** | Separate host (`partners.wasviking.com`). Isolated identity table for partner operators. | | **Posture share** | Separate host (`posture.wasviking.com`). Tokenized, password-protected, time-limited share artifacts. | ## Data plane | Storage class | Purpose | |---|---| | Managed relational database | Organizations, users, RBAC, findings, audit log, billing, subscriptions, inventory metadata. | | Managed document database | Scan output, AI recommendations, environment profile fingerprints, OAST interactions, SBOM documents, threat intel events. | | Managed in-memory broker | Job queue, gRPC dispatch coordination, rate limiting. | | Managed object storage | Scan artifacts, evidence bundles, signed reports. Per-tenant IAM scoping; no cross-tenant object visibility. | The schema is owned by a single source of truth on the portal side. The API consumes that schema in read-mostly mode for its own data needs. Migrations and schema changes go through the same pull-request flow as application code. ## Encryption | Channel | Mechanism | |---|---| | Portal and API in transit | TLS 1.2+ on every public endpoint, HSTS preload, secure cookies with strict scope. | | Sentinel agent in transit | Mutual TLS with per-agent client certificate. Outbound only. | | Database at rest | Provider-managed encryption (AES-256). Tenant-scoped Fernet key for sensitive fields above the database envelope. | | Object storage at rest | Provider-managed encryption with bucket-level IAM scoping. | | API keys | Stored hashed. Cleartext shown once at creation; never retrievable afterward. | | Authenticated-scan credentials | Encrypted at rest with the tenant key. Held in memory only during the scan; never written to logs. | | Secrets detected by the agent | The raw secret never leaves the host. Only a SHA-256 hash and a masked preview reach WASViking. | Key rotation is automated for short-lived material (agent client certificates, session keys) and operator-driven for long-lived material (API keys, with a 24-hour overlap window). ## Tenant isolation Isolation is enforced at four layers: - **View layer**: every customer view refuses a request without an organization context. - **ORM layer**: querysets are scoped to the requesting user's organization. Cross-tenant queries are treated as a bug. - **Audit layer**: every audit row records the operator's organization. The customer-facing audit log is scoped accordingly. - **Billing layer**: billing rows attach to organization, not to users, so a deleted user does not lose billing trail. API keys carry their issuing organization. Cross-organization use of a key returns `404` (not `403`) to avoid leaking the existence of other tenants. The Partner Console uses a separate identity table from the customer realm. Partner operators are not customer users. The two cannot see each other across hosts. ## Identity and access - **SAML 2.0 SSO** as Service Provider. Standard attribute mapping, optional group-to-role propagation. First sign-in defaults to Read-only by policy; promotion requires an existing Admin or Manager. - **Multi-factor authentication** is mandatory for every operator. TOTP-based when SSO is not enforced; IdP-enforced when SSO is on. - **RBAC** with four default roles (Admin, Manager, Analyst, Read-only) and a granular capability catalog of 25+ scopes. Roles are editable per organization. - **API keys** scoped per organization. Granular scope set per key. Hashed at rest, rotatable with overlap, optionally IP-restricted. - **Emergency local login** for SSO-enforced organizations as break-glass, 72-hour TTL, high-severity audit event on use. ## Network architecture - **Sentinel agent is outbound-only.** The agent dials WASViking from inside the customer network. No inbound ports, no static IP allow- listing, no VPN required. - **mTLS termination at the proxy** with the verified client certificate forwarded inward. Compatible with managed cloud load balancers. The agent's private key never leaves the host. - **Gateway redaction** at the gRPC service layer. The server never logs full proto, job, or response payloads, so cookies, CSRF tokens, and internal target bodies do not appear in operator logs. - **Public endpoints sit behind a managed CDN with edge rate-limiting** and bot management. Application-layer rate limits run on top as a second floor. ## Hardening Agent: - Single static Go binary, distributed with cosign signatures so customers can verify before running. - systemd unit with `NoNewPrivileges`, `ProtectSystem=strict`, `MemoryDenyWriteExecute`, restricted system call filter. - Optional obfuscated enterprise build for higher-assurance customers. Platform: - Container images built from minimal base images, no source volumes in production. - Runtime principal-of-least-privilege at the cloud account level. - Production access requires MFA, is time-bound, and is logged. - Secrets manager for application credentials. No long-lived cleartext secrets on disk. ## Secure software development WASViking dogfoods its own product against its own codebase: - **Continuous DAST** runs against staging deployments on every release. - **SBOM and SCA** with KEV cross-reference run in CI for every service. - **Secrets detection** runs in CI for every push. CI exit codes block merges on KEV-listed components or verified-live secrets. - **Dependency review** is mandatory on pull requests. - **Branch protection** with required reviews, signed commits where the platform supports it, and required status checks before merge. - **Signed release artifacts** for agent releases (cosign) and internal containers. - **Internal threat-model review** for new analyzers and any change to the identity, billing, or data-isolation surface. This is the same set of gates WASViking ships as a product. The product page set has the full reference: [CI/CD with wasviking-sentinel](https://docs.wasviking.com/sentinel/sentinel-ci/). ## Operational hygiene The platform runs a small set of background jobs to keep the data plane consistent and the alert channel signal-only: - Scheduled scans (per organization configuration). - Hygiene sweeps for orphan and stale long-running scans. - Daily threat-intel ingest (OSV.dev + CISA KEV) with retroactive match against live SBOMs. - Smart re-notify rules so alerts only re-fire on KEV bump, severity bump, or fix availability. - Integrations polling (Jira, SLA breach digest, AI quota reset). - Monthly billing previews; daily annual renewal task. Job names and schedules are an internal implementation detail. Customers see the outputs: dashboards, findings, webhooks, audit events. ## Logging and audit - **Customer-facing audit log** at `/portal/audit-log/`, gated by the `audit_logs.view` RBAC capability. Public REST scope `audit_logs:read`. - **Mutation events** (RBAC change, API key issuance, finding status transition, posture share access, evidence bundle access) are written immutably. - **Operator and system events** stream to the WASViking-side observability stack with their own retention. - **Default audit retention** is 24 months; configurable up to 7 years on Enterprise plans. - **Signed webhook events** mirror audit-relevant transitions for external SIEM ingestion. ## Backups, durability, disaster recovery - **Daily automated backups** of the relational and document databases. - **30-day backup retention** by default. - **Cross-region replication** for Enterprise plans, with point-in- time recovery to a secondary region. - **Restoration is operator-initiated and audited**; no silent rollbacks. - **Quarterly recovery exercises** validate the runbook. ## Data residency Production data is hosted in US AWS regions today. Regional residency options for additional jurisdictions are part of the Enterprise roadmap. Sub-processors and their regions are documented on the Trust Center. ## Compliance posture Two postures, kept strictly separate: **What the product supports.** WASViking maps findings to controls across PCI DSS v4.0, LGPD, GDPR, BACEN (CMN 4.893 and BCB 85), and ISO 27001:2022, from a single rule table. Evidence Bundles are designed to be attached directly to an audit binder. See [Framework mapping](https://docs.wasviking.com/compliance/framework-mapping/). **What the company is certified for.** WASViking LLC is working toward **SOC 2 Type I** and **ISO 27001 Certification**. Current status, sub-processor list, DPA, and the security questionnaire (CAIQ / SIG) are available on the Trust Center at `wasviking.com/trust-center/`. ## Vulnerability disclosure WASViking welcomes coordinated disclosure. The policy, scope, safe harbor, and PGP key are at [Reporting vulnerabilities](https://docs.wasviking.com/security/reporting-vulnerabilities/). Acknowledged reporters are named (with permission) when WASViking notifies affected customers about the issue. ## What we publish externally - This documentation. - The institutional marketing site. - The Trust Center with DPA, privacy policy, sub-processors, and the SOC 2 roadmap. - A coordinated disclosure policy and PGP key. ## What we do not publish externally The following are shared only on signed mutual NDA (or not at all): - Internal IP ranges, network diagrams, and provider-account identifiers. - Specific framework, database, and runtime versions in use. - Internal admin routes and operator runbooks. - Margin and cost models. - Incident response procedures. - Customer-specific configurations. These are deliberately withheld because they are operationally sensitive and provide no security value to a prospect. --- # Tenant isolation and data handling Section: Security Source: https://docs.wasviking.com/security/tenant-isolation/ Summary: How WASViking keeps organizations apart, what we encrypt, what we keep, and what never leaves your environment. WASViking® is multi-tenant by design. Isolation is enforced at the view layer, the ORM layer, the audit layer, and the billing layer. ## Organization scope Every customer artifact carries an `organization_id`. Every ORM query that touches a customer artifact filters on the requesting user's organization. This is enforced through: - Base view mixins that refuse a request without an organization context. - ORM querysets scoped via `.filter(organization=request.user.organization)` patterns. - Audit log writes that record the operator's organization. - Billing rows linked to organization, not to users. Cross-tenant data fetch is impossible through the supported API surfaces. We treat any cross-tenant query attempt as a bug. ## API key scoping API keys (`wv_live_*`) carry the organization they were issued in. The auth middleware sets `request.organization` from the key. Any attempt to use the key against a different org's resource returns `404` (not `403`) to avoid leaking existence. ## Partner / customer boundary The partner host (`partners.wasviking.com`) and the customer portal host live on the same Django application but on separate URL configs and separate identity tables. Partner operators (`PartnerUser`) are not Django users; customer users are not partners. - Partner operators cannot see customer users. - Customer users cannot see partners. - Operating model (see [Operating models](https://docs.wasviking.com/partner-console/operating-models/)) gates whether a partner sees posture data on a managed customer. ## Sentinel agent isolation - The agent's client certificate binds to one organization. - Cross-tenant gRPC method calls are refused at the service layer. - Internal scan jobs are dispatched only to agents bound to the same organization. - The gRPC server never logs full proto, job, or response payloads. ## Encryption - **In transit.** TLS 1.2+ on every public endpoint. mTLS on the Sentinel tunnel. - **At rest.** AWS-managed encryption on RDS (MySQL) and DocumentDB (MongoDB) where applicable. Tenant-scoped Fernet key for per-tenant sensitive fields. - **API keys** are stored hashed, not in cleartext. - **Authentication credentials** for authenticated scanning are encrypted with the tenant-scoped key. - **Secrets detected** (`sentinel secrets`) submit only the SHA-256 hash and a masked preview; the raw secret never reaches WASViking. ## What never leaves the customer environment - **Raw secrets detected by `sentinel secrets`.** Only hash and masked preview cross the boundary. - **Internal target HTTP bodies in operator logs.** The gRPC service redacts proto, job, and response. - **SBOM source files.** Only the parsed CycloneDX is submitted. ## Retention | Data | Default retention | |---|---| | Scan output | 12 months. Configurable per plan. | | Findings | Indefinite while the organization is active. | | Audit log | 24 months. Configurable per plan up to 7 years. | | MFA codes | 5 minutes. | | API request logs | 30 days. | | Posture Share / Bundle access events | Same as audit log. | | SBOM submissions | Indefinite while the organization is active. | When an organization closes its account, its data is erased from every active store at the end of a 60-day reversible window; only the records the law requires survive (billing records for 7 years, accepted terms and audit evidence for 5 years, and the record of the closure itself). The procedure, the cancel path and the evidence you receive are described in [Account closure and data erasure](https://docs.wasviking.com/security/account-closure-and-data-erasure/). Audit log rows that name external parties (auditors who accessed a share, for example) are preserved with identities pseudonymized and the third-party access trail intact. ## Backups - Automated backups of MySQL and MongoDB. - 7-day rotation: point-in-time recovery for the relational database and a rolling 7-day window for the document store. Object storage is not versioned. - Restoration is operator-initiated and audited. - Copies of erased data expire with that rotation and are never restored into production. ## Sub-processors WASViking uses a short list of sub-processors: - AWS for compute, storage, network. - An AI model provider for the AI recommendation surfaces (named in the sub-processor register on the Trust Center). - Stripe for billing. - SendGrid for transactional email. - Cloudflare for edge, WAF, and CDN. The current list is on the Trust Center (`wasviking.com/trust-center/`). We notify customers under DPA when the sub-processor list changes. ## Where to ask deeper questions - DPA: provided under request from the Trust Center. - Security questionnaire (CAIQ / SIG): provided under NDA. - SOC 2 roadmap: posted on the Trust Center. --- # Reporting vulnerabilities Section: Security Source: https://docs.wasviking.com/security/reporting-vulnerabilities/ Summary: How to report a security issue to WASViking. Coordinated disclosure, safe harbor, response timelines. WASViking® welcomes coordinated disclosure of security issues. This page is the policy and the procedure. If you believe you have found a vulnerability in the platform, follow the steps below. ## Where to send a report Email **security@wasviking.com** with: - A clear description of the issue. - Steps to reproduce. - The affected URL, endpoint, or component. - Your assessment of impact (what an attacker could do). - Optional: your name and contact for acknowledgement. PGP key fingerprint for sensitive submissions: posted on the Trust Center under `wasviking.com/trust-center/security/`. ## What to expect after submission | Phase | Target SLA | |---|---| | Acknowledgement | Within 1 business day. | | Initial assessment | Within 5 business days. | | Fix or mitigation plan | Within 30 days for high severity; 90 days for medium and low. | | Public disclosure (if applicable) | Coordinated with the reporter. | We will keep you informed throughout. We do not gag valid reporters. ## Safe harbor If you operate in good faith under the rules below, WASViking will not pursue legal action. You may: - Test against `*.wasviking.com` endpoints you can reach without abusing authentication. - Test against your own organization's WASViking tenant (you own what you scan inside your tenant). - Test the public REST API with credentials WASViking has issued to you. - Test the Sentinel agent code published in our release artifacts. You may NOT: - Test against another customer's tenant. Tenant boundary respect is a hard requirement. - Exfiltrate, retain, or share customer data. - Run denial-of-service tests. - Perform social engineering against WASViking employees, contractors, or sub-processors. - Modify, alter, destroy, or use customer or production data. ## What is in scope - `wasviking.com` (marketing site). - `docs.wasviking.com` (this site). - `portal.wasviking.com` (customer portal). - `api.wasviking.com` (public REST API). - `partners.wasviking.com` (Partner Console). - `posture.wasviking.com` (Posture Shares). - The Sentinel agent code in the published release artifacts. - Our SAML 2.0 SP integration. ## What is out of scope - Issues in third-party software we use that already have public advisories (report those to the upstream). - Issues that require physical access to a victim's device. - Issues that require an attacker to already have a privileged WASViking account. - Social engineering against customers or our team. - DOS / DDOS / volumetric tests. - Findings that depend on outdated browser versions. - Self-XSS without a meaningful exploitation chain. - Disclosure of public information. - Missing security headers without an exploit chain. - TLS configuration without a meaningful exploit chain (we already run modern TLS). ## What we will publish For resolved high or critical issues, WASViking notifies affected customers directly with: - A short description. - The affected components. - The remediation timeline. - Acknowledgement of the reporter (with permission). Minor findings are fixed and noted in the release notes without a dedicated advisory. ## Bounties WASViking does not run a public bug bounty program at this time. We acknowledge reporters publicly (with permission) and may offer discretionary recognition. ## Why this policy looks the way it does We treat coordinated disclosure as a partnership. The fastest path to a safe internet is researchers and vendors working together with clear expectations. This page is our side of that bargain. --- # Account closure and data erasure Section: Security Source: https://docs.wasviking.com/security/account-closure-and-data-erasure/ Summary: How an organization administrator closes a WASViking account, what happens during the 60-day reversible window, what is erased, what is kept and why, and how the erasure is evidenced. WASViking® lets the organization close its own account from the portal, without a support ticket, and erases the organization's data from our active systems at the end of a reversible window. This page describes the procedure as it runs in production, the records that survive it because the law requires them, and the evidence you receive when it completes. It applies to organizations that contract WASViking directly. If your account is provisioned and managed by a WASViking partner, the closure is handled through your provider. ## Who can close the account Closing the account is a protected operation. It is enabled by an organization administrator who also holds billing authority and has a verified e-mail address. That person enables the closure authority for the organization with a code from an authenticator app, and only then does the option to start the closure appear. Members without those roles see an explanation of who can enable it and can ask support@wasviking.com from the owner's address if they need help. ## Before you start Export what you need. During the reversible window the portal remains readable and exportable (reports, CSV downloads), and you can ask our team for a full export, but after the window closes the data is gone. ## How to close the account The whole procedure runs from one page of the portal, **User > Privacy & Data**. The top of the page restates what we hold about your organization and how to exercise your privacy rights; the **Close organization account** card at the bottom is where you enable and start the closure, and where the cancel action lives during the reversible window. ![Privacy & Data page with the Your data at WASViking and Your privacy rights cards, and the Close organization account card asking the administrator to set up an authenticator app first](https://docs.wasviking.com/static/docs/images/account-closure/01-privacy-data.png) *User > Privacy & Data. This administrator has not set up an authenticator app yet, so the card points to account settings first; once it is set up, the same card shows Enable closure authority.* 1. Sign in and open **User > Privacy & Data**. 2. Under **Close organization account**, select **Enable closure authority** and confirm with the code from your authenticator app. Nothing is closed by this step; it only makes the next one available. If your authenticator app is not set up yet, the page sends you to your account settings first. 3. Select **Start account closure**, then **Verify my identity** and enter the code from your authenticator app. 4. Enter the 8-digit confirmation code we e-mail you (it expires in 10 minutes and works once) and select **Confirm account closure**. The page shows a closure reference. Keep it: every message about this closure quotes it, and support uses it to find the record. ## What happens right away As soon as the closure is confirmed the organization is suspended: - every open session is signed out; - API keys and Sentinel agents are disabled; - the subscription stops renewing (no further renewal is charged); - every write in the portal is blocked. Reading and exporting remain available, and the Privacy & Data page keeps the cancel action. You receive a confirmation e-mail with the date after which erasure begins. ## The 60-day reversible window You have 60 days from the confirmation to change your mind. Three days before the window ends we send a final reminder. To keep the account, open Privacy & Data and select **Cancel this closure**; cancelling requires the same verification used to make the request (authenticator code and e-mail code). When a closure is cancelled the organization is unsuspended, the subscription renewal is restored and you receive an acknowledgement e-mail. Nothing was deleted during the window, so the account resumes with all its data. If nobody with closure authority can sign in during the window (lost password, lost authenticator), the usual account recovery applies: password reset from the sign-in page and authenticator backup codes. Beyond that, write to support@wasviking.com from the owner's address. ## What is erased When the window ends the erasure runs automatically. It removes the organization's data from all of our active systems: relational and document databases, object storage and the files generated on our scanning hosts. In practice that means targets, scans, findings, reports, screenshots, evidence files, uploaded mobile binaries, Sentinel agent certificates, integrations, API keys, notification settings and the rest of the organization's working data. The user accounts of the organization are closed at the same time: sign-in credentials, authenticator secrets, sessions and login history are removed and the accounts can no longer be used. Copies held in encrypted backups expire on our rotation schedule within 7 days after the live data is removed and are never restored into production. ## What is kept, and why Records we are legally required to keep survive the erasure. Each is kept only for its mandated period, is access-restricted, and holds no more than the identifiers the obligation needs. | Record | Why it is kept | How long | |---|---|---| | Invoices, payment records and the subscription record | Tax and accounting law | 7 years | | Accepted terms and the audit evidence tied to consent, authorization to scan, and the closure itself | Establishment, exercise or defense of legal claims; fraud prevention (GDPR Art. 17(3), LGPD Art. 16) | 5 years | | The closure request, the deletion manifest and the erasure certificate | Proof that the erasure was requested, verified and completed | 5 years | | A minimal organization identifier record | Anchor for the records above; permanently frozen, never reusable | While the records above exist | Nothing else is retained. ## The completion record When the erasure completes, the person who requested the closure receives a completion e-mail with the closure reference, the time of completion (UTC) and the number of records removed across our active systems. Behind that message WASViking keeps a signed erasure certificate: a deterministic record of what was deleted per store, what was retained and on which legal basis, sealed with hashes so it can be verified later. If you need the certificate for an audit, or the completion record sent again to a verified address, write to support@wasviking.com quoting the closure reference. ## Coming back later During the window, cancel the closure and continue as before. After the erasure there is no reactivation of the same organization: its data no longer exists and the record of its erasure is final. A company that wants to use WASViking again signs up as a new customer, with a new organization and new consents. The e-mail addresses of former users are free to be used again. ## Other privacy requests Access, correction, portability and deletion requests that are not an account closure are handled by our privacy team. Write to privacy@wasviking.com from the address on file. Requests are answered within one month under the GDPR (Article 12) and access requests within 15 days under the LGPD (Article 19). The commitments behind this page are stated in the [Privacy Policy](https://wasviking.com/trust-center/privacy-policy/) and the [Data Processing Agreement](https://wasviking.com/trust-center/data-processing-agreement/) on the Trust Center.