---
title: "Vulnerability Scanning"
description: "Scan container images and stack compose files for CVEs, secrets, and misconfigurations. Surface severity badges in the Resources Hub, compare scans over time, and gate deploys on policy violations."
---

Sencho integrates with [Trivy](https://trivy.dev) to scan container images and Compose files for vulnerabilities (CVEs), hardcoded secrets, and misconfigurations. Findings surface as severity badges in the Resources Hub and as drillable reports in the scan drawer. Manual scanning, secret and misconfig detection, scan history, comparison, and CVE suppressions are available on every tier. Skipper and Admiral add scheduled fleet scans, policy enforcement, SBOM, and SARIF exports.

<Frame>
  <img src="/images/vulnerability-scanning/resources-badges.png" alt="Resources Hub Images table with severity badges (CRITICAL, HIGH, MEDIUM) on managed image rows alongside the Scan history button" />
</Frame>

## Prerequisites

The Trivy CLI must be available on the machine running Sencho. Trivy is not bundled with the Sencho Docker image; see [Installing Trivy](/operations/trivy-setup) for mount and installation options. Sencho checks for Trivy on startup and hides scanning UI when the binary is not available.

<Note>
  Trivy is installed independently on each Sencho instance. When you select a remote node, **Settings → Security** shows only the scanner status for that node; install, update, or uninstall Trivy from there to manage the remote's binary. Scan policies, CVE suppressions, and misconfig acknowledgements are managed on the control instance and replicate fleet-wide.
</Note>

## Tier availability

| Feature | Community | Skipper | Admiral |
|---------|:---------:|:-------:|:-------:|
| Install / update / uninstall Trivy from Settings | ✓ | ✓ | ✓ |
| On-demand image vulnerability scanning | ✓ | ✓ | ✓ |
| Full scan (vulnerabilities + secrets) | ✓ | ✓ | ✓ |
| Compose file misconfiguration scanning | ✓ | ✓ | ✓ |
| Severity badges in the Resources Hub | ✓ | ✓ | ✓ |
| Scan results drawer with grouped tabs | ✓ | ✓ | ✓ |
| Post-deploy automated scanning | ✓ | ✓ | ✓ |
| Scan history sheet | ✓ | ✓ | ✓ |
| Scan comparison | ✓ | ✓ | ✓ |
| CVE suppressions | ✓ | ✓ | ✓ |
| Misconfig acknowledgements | ✓ | ✓ | ✓ |
| Scheduled fleet scans (all images on a node) |  | ✓ | ✓ |
| Scan policies with `block_on_deploy` enforcement |  | ✓ | ✓ |
| Suppression-aware deploy blocking (optional toggle) |  | ✓ | ✓ |
| SBOM generation (SPDX, CycloneDX) |  | ✓ | ✓ |
| SARIF export (code scanning integration) |  | ✓ | ✓ |
| Auto-update of the managed Trivy binary |  | ✓ | ✓ |

## On-demand scanning

Open the **Resources** tab and switch to the **Images** panel. When Trivy is available, every image row shows a shield icon alongside the inspect and delete actions. Click it and the menu offers two scan modes:

- **Scan (vulnerabilities)**: the fast path. Trivy reads package metadata only.
- **Full scan (vulnerabilities + secrets)**: also walks the image filesystem looking for hardcoded credentials, tokens, and keys. Slower because of the filesystem traversal.

A spinner replaces the shield while the scan runs. Most vulnerability scans finish in 10 to 60 seconds depending on image size and whether the Trivy database is cached. Full scans add the time needed to read the filesystem. When the scan completes, a severity badge appears in the row's Status column. Click the badge to open the results drawer.

### Reading severity badges

Hovering a badge reveals the breakdown by severity and the scan timestamp. The badge color reflects the highest severity found:

| Badge | Meaning |
|-------|---------|
| **CRITICAL** (red) | At least one vulnerability with a CVSS score of 9.0 or higher |
| **HIGH** (orange) | At least one high-severity vulnerability |
| **MEDIUM** (blue) | Only medium and lower vulnerabilities |
| **LOW** (muted) | Only low-severity vulnerabilities |
| **Clean** (green) | Scan completed with zero findings |

Scan results are cached by image digest. If the same digest is scanned again within 24 hours, Sencho returns the cached result instantly instead of re-running Trivy. To force a fresh scan, open the drawer and click **Re-scan**.

## The scan results drawer

The drawer opens as a right-side sheet with the breadcrumb `Security › Scans › <image-ref>`. The header surfaces the primary scan actions, the summary header reports severity counts and fixable findings, and the body groups findings across three tabs: **Vulnerabilities**, **Secrets**, and **Misconfigs**.

<Frame>
  <img src="/images/vulnerability-scanning/scan-details-sheet.png" alt="Vulnerability scan drawer for lscr.io/linuxserver/swag:latest showing the Re-scan, Compare, CSV, and SARIF header actions, the severity summary (2 CRITICAL, 51 HIGH, 65 MEDIUM, 1 LOW), an SBOM download button, the severity filter pills, and a paged CVE table tinted by severity" />
</Frame>

### Header actions

- **Re-scan**: kick off a fresh scan, ignoring the digest cache.
- **Compare**: pick a baseline scan from the dropdown to diff against this one.
- **CSV**: export the full vulnerability list for offline review.
- **SARIF**: download the full scan (vulnerabilities, secrets, and misconfigs) as SARIF 2.1.0 for upload to GitHub code scanning or any SARIF-aware tool. Skipper or Admiral required.

The summary header below the actions reports the per-severity counts, the total, how many findings have a fix available, when the scan ran, and what triggered it. An **SBOM** button below the summary downloads a Software Bill of Materials in SPDX JSON or CycloneDX format (Skipper or Admiral).

### Vulnerabilities tab

Severity filter pills narrow the table, and the paginated list shows every CVE found with these columns:

- **CVE ID**: links to [cve.org](https://www.cve.org) for `CVE-…` identifiers and to the [GitHub Advisory Database](https://github.com/advisories) for `GHSA-…` identifiers.
- **Package**: the affected package name and installed version.
- **Severity**: badge matching the row tint.
- **Fixed**: the version that contains the fix, with a green checkmark when an upgrade is available.

Critical and high rows carry a left accent rail so the entries that need attention catch the eye even before you read the severity column. If the scan was evaluated against a [scan policy](#scan-policies) and the highest severity meets or exceeds the policy threshold, a destructive **Policy violation** banner appears at the top of the drawer naming the policy and the threshold it crossed.

### Secrets tab

Hardcoded credentials or keys detected in the image filesystem, with severity, rule id, title, and the file path including the line number range when available. Secret values are redacted: only the first eight characters of the match are stored, so exporting or comparing a scan cannot leak the underlying credential.

### Misconfigs tab

Misconfiguration findings with severity, check id, title, target file, and a suggested fix. Image scans typically have an empty Misconfigs tab; stack-config scans (see [Compose misconfiguration scanning](#compose-misconfiguration-scanning)) populate it.

## Post-deploy automated scanning

When Trivy is available, Sencho scans every deployed image in the background after a successful deploy. This applies to all deploy paths:

- Stack deploy and redeploy
- Stack update
- Template deploy from the App Store
- Git source apply
- Git source create

The deploy itself is never blocked by scanning. Scans run asynchronously and surface their results via the severity badges in the Resources Hub. If high or critical findings appear, an alert is dispatched through your configured [notification channels](/features/alerts-notifications).

### Opting out per deployment

The App Store deploy sheet has a **Security** section with a **Scan images for vulnerabilities after deploy** checkbox, enabled by default. Uncheck it to skip the post-deploy scan for that single deployment. This does not disable scan policies; it simply opts that deployment out of post-deploy scanning.

<Frame>
  <img src="/images/vulnerability-scanning/app-store-toggle.png" alt="App Store deploy sheet Security section with the auto-scan checkbox enabled" />
</Frame>

## Scheduled fleet scans

<Note>
  Scheduled fleet scans require a **Skipper** or **Admiral** license.
</Note>

You can run recurring scans of every image on a node through the standard [Scheduled Operations](/features/scheduled-operations) system. Create a scheduled task with action **Scan** and a cron expression. The scheduler iterates every image on the target node with a short delay between scans and records the result in the task's run history.

Use scheduled scans to keep badge counts fresh even for images that are rarely redeployed. A nightly cron like `0 3 * * *` is a sensible default for most fleets.

### Completion notifications

When a scheduled scan finishes, Sencho dispatches an alert through your configured [notification channels](/features/alerts-notifications). The alert names the task and summarises scanned, cached, and failed image counts:

- **Info** when every image scanned successfully.
- **Warning** when at least one image failed to scan.

Failures are usually transient (registry timeouts, missing credentials) and never stop the rest of the run from completing. The task's run history holds the detailed output.

## Scan policies

<Note>
  Scan policies require a **Skipper** or **Admiral** license.
</Note>

Policies define severity thresholds that govern whether a stack can deploy. A policy with **Block on deploy** enabled runs a pre-flight scan on every image in the stack before `docker compose up` executes; if any image meets or exceeds the threshold, the deploy is rejected with a dialog listing the offending images. Policies with **Block on deploy** disabled still evaluate every post-deploy and scheduled scan and dispatch warning alerts when the threshold is exceeded.

See [Deploy Enforcement](/features/deploy-enforcement) for the full pre-flight flow, admin bypass path, and audit-log behavior.

<Frame>
  <img src="/images/vulnerability-scanning/security-settings.png" alt="Settings Security page showing the masthead with Node, Policies, and Trivy status, the Vulnerability Scanner card with Auto-update Trivy toggle, an Add Policy button, and the No scan policies configured empty state" />
</Frame>

### Creating a policy

Go to **Settings → Security** and click **Add Policy**.

| Field | Description |
|-------|-------------|
| **Name** | A descriptive label (e.g. `prod-critical-block`). |
| **Stack pattern** | Optional glob against stack names (e.g. `prod-*`). Leave empty to match every stack. |
| **Max severity** | The threshold. If a scan finds any vulnerability at or above this severity, the policy fires. |
| **Block on deploy** | When enabled, deploys are rejected before `docker compose up` runs if any image violates the threshold. When disabled, the policy still evaluates post-deploy and scheduled scans and dispatches warning alerts on violations. |
| **Enabled** | Disabled policies are skipped during evaluation. |

### Policy scoping

When multiple policies match a deploy, Sencho picks the most specific one:

1. Policies scoped to a specific node win over global policies.
2. Policies with a stack pattern win over wildcard policies.
3. Disabled policies are never applied.

Only one policy is evaluated per deploy. Use a single tight pattern rather than overlapping policies for clarity.

<Note>
  Policies created on a control instance replicate to every remote in the fleet automatically. See [Fleet Sync](/features/fleet-sync) for the replication, push retry, and replica demote behavior.
</Note>

### Example policies

**Block criticals and highs in production.** A tight gate that keeps known vulnerable base images out of your production fleet.

- **Name:** `prod-high-gate`
- **Stack pattern:** `prod-*`
- **Max severity:** `HIGH`
- **Block on deploy:** On
- **Enabled:** On

**Alert on criticals in staging, never block.** Lets engineers iterate without friction while still surfacing critical findings for triage.

- **Name:** `staging-critical-alert`
- **Stack pattern:** `staging-*`
- **Max severity:** `CRITICAL`
- **Block on deploy:** Off
- **Enabled:** On

### Honoring suppressions in deploy blocks

By default a block-on-deploy policy evaluates the **raw** scan result, so a CVE you have accepted in [CVE Suppressions](/features/cve-suppressions) still counts toward the block. That keeps the gate strict: suppressions silence alerts and dim findings in reports, but on their own they do not open the deploy path.

To have an accepted CVE stop counting toward the gate, turn on **Honor suppressions in deploy blocks** in **Settings → Security**. With it on, the gate re-derives each image's severity from the findings that remain after suppressions are applied, so an image whose only blocking findings are all suppressed deploys without a manual bypass. When a deploy proceeds for this reason, Sencho records it in the audit log so the suppression-driven pass stays traceable.

The toggle governs the Sencho instance that runs the deploy and is off by default. Enable it on each instance whose deploys should honor suppressions.

### Creating a policy via the API

Policy CRUD endpoints are documented in the [Security API reference](/api-reference/security). A typical create call from CI looks like this:

```bash
curl -X POST https://your-sencho-instance:1852/api/security/policies \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod-high-gate",
    "stack_pattern": "prod-*",
    "max_severity": "HIGH",
    "block_on_deploy": 1,
    "enabled": 1
  }'
```

## Secret detection

Full scans ask Trivy to walk the image filesystem for patterns that look like hardcoded credentials, API tokens, cloud access keys, or private keys. Detection rules cover the common providers (AWS, GCP, GitHub, Slack, Stripe) plus generic high-entropy strings.

Trigger a full scan from the shield-icon menu on any image row with **Full scan (vulnerabilities + secrets)**. Findings appear on the **Secrets** tab of the drawer:

| Column | Description |
|--------|-------------|
| **Severity** | Trivy-assigned severity for the rule that matched. |
| **Rule** | The detection rule identifier (e.g. `aws-access-key-id`). |
| **Title** | A short description of what was detected. The second line shows a redacted excerpt of the match. |
| **Target** | The file path inside the image filesystem, including the line number range when available. |

Only the first eight characters of any matched secret are persisted. The full value is never written to the database, so exporting a scan or comparing two scans cannot leak the underlying credential.

Full scans take longer than vulnerability-only scans because Trivy reads every file in the image. If runtime is a concern, schedule full scans overnight and keep deploy-time scans on the default vulnerability-only setting.

## Compose misconfiguration scanning

Beyond package CVEs, Sencho can run `trivy config` against a stack's Compose file to flag insecure defaults before you deploy. Typical checks cover privileged containers, missing resource limits, host networking, mounted Docker sockets, and overly broad capabilities.

From any stack page, open the **More actions** overflow menu next to the Update button and select **Scan config**. Sencho runs the scanner against the stack's working directory and opens the scan drawer on the **Misconfigs** tab.

<Frame>
  <img src="/images/vulnerability-scanning/scan-config-button.png" alt="Plex stack page with the More actions overflow menu open, showing the Scan config item with a shield icon and the Delete item below" />
</Frame>

The Misconfigs tab columns:

| Column | Description |
|--------|-------------|
| **Severity** | Rule severity (CRITICAL / HIGH / MEDIUM / LOW). |
| **Check** | Rule id or AVD identifier for the violated check. |
| **Title** | Short summary, linked to the upstream advisory when available. The second line shows Trivy's detailed message. |
| **Target** | The file that triggered the finding. |
| **Fix** | The recommended resolution. |

Config scans are stored in the same history as image scans with an `image_ref` of `stack:<name>`, so they appear on the Scan history sheet and can be exported as CSV.

## Misconfig acknowledgements

Some misconfigurations are intentional. A reverse proxy legitimately needs root to bind privileged ports; a network monitor might require host networking; an `--privileged` mount of the Docker socket might be exactly what your janitor service expects. Sencho lets admins acknowledge a rule so it stops triggering alerts on that stack without lowering the policy bar for every other stack.

Acknowledgements are applied at read time and never modify stored findings. Deleting an acknowledgement immediately resurfaces the underlying finding wherever it appears.

### Acknowledging from a scan result

1. Open a stack-config scan that contains the finding.
2. Click the shield-check icon at the right edge of the misconfig row. The dialog opens with the rule id prefilled and the stack pattern set to the current stack name, so a single click acknowledges *only this stack*. That is the narrowest possible scope by default.
3. Add a reason explaining why the misconfiguration is accepted. The reason is stored locally and replicates fleet-wide; it never appears in audit-log summaries to avoid leaking incident-tracker IDs or vendor secrets.
4. Optionally set an expiry in days. After expiry the acknowledgement stops applying and the finding resurfaces.

Acknowledged rows render dimmed with a strikethrough title; hovering surfaces the recorded reason.

### Managing acknowledgements

**Settings → Security** has a Misconfig Acknowledgements panel listing every acknowledgement on this control: rule id, optional stack pattern (glob), creator, expiry date, and a delete button. The same `replicated` badge that appears on CVE suppressions appears here for rows pushed from the control to a replica.

Replicas show the panel read-only; write operations return 403 with a "managed by control" message so configuration drift cannot accumulate on the leaf nodes.

### Scope and matching

An acknowledgement matches a misconfig finding when:

- `rule_id` equals the finding's `rule_id` (exact match), and
- `stack_pattern` is null **or** matches the scan's stack name via the same glob syntax used elsewhere (e.g. `traefik`, `web-*`), and
- the acknowledgement has not expired.

When more than one acknowledgement could match, Sencho picks the most specific: a stack-pinned ack beats a fleet-wide ack for the same rule.

### SARIF emission

Acknowledged misconfigs are emitted in the SARIF export with a `suppressions` entry of kind `external` and status `accepted`, mirroring CVE suppressions. Code-scanning dashboards that respect SARIF suppressions dismiss them with the recorded justification.

## SBOM generation

<Note>
  SBOM generation requires a **Skipper** or **Admiral** license.
</Note>

A Software Bill of Materials (SBOM) is a machine-readable inventory of every package in a container image. SBOMs satisfy security frameworks (SLSA, Executive Order 14028, EU Cyber Resilience Act) and support offline supply-chain analysis.

From the scan results drawer, click **SBOM** below the summary and choose a format:

| Format | Use case |
|--------|----------|
| **SPDX JSON** | Widely supported, best for tooling integration. |
| **CycloneDX** | Richer dependency metadata, better for supply-chain analysis. |

The download starts immediately and uses the image's digest (when available) in the filename.

## SARIF export

<Note>
  SARIF export requires a **Skipper** or **Admiral** license.
</Note>

SARIF (Static Analysis Results Interchange Format) is the standard format supported by GitHub code scanning, Microsoft Defender for Cloud, and most security dashboards. Sencho generates SARIF 2.1.0 documents from the stored scan results, so the download matches what you see in the drawer (same findings, same suppression state) without re-running Trivy.

From the scan drawer header, click **SARIF** to download the report. The file is named after the image reference with a `.sarif.json` extension.

What the export contains:

- **Vulnerabilities**: one SARIF result per CVE, with `security-severity` scored 9.8 (CRITICAL), 7.5 (HIGH), 5.0 (MEDIUM), 2.5 (LOW), or 0.0 (UNKNOWN). The affected package appears as a logical location (`<pkg>@<version>`).
- **Secrets**: rule ids are namespaced as `SECRET:<rule>`. Results point at the file and line number where the match was found.
- **Misconfigs**: rule ids are namespaced as `MISCONFIG:<rule>`. Results point at the Compose file that triggered the check.
- **Suppressions**: CVEs you have suppressed in Sencho are emitted with a SARIF `suppressions` entry of kind `external` and status `accepted`, so code-scanning dashboards dismiss them with the justification you recorded.
- **Acknowledged misconfigs**: misconfigs you have acknowledged in Sencho are emitted with the same `suppressions` shape so dashboards apply the same dismissal logic.

Each finding type is capped at 5000 rows to bound memory and serialisation time on pathological scans. When any type trips the cap, the SARIF run carries `properties.truncated = true`, `properties.row_limit`, and a `properties.totals` object with the original counts so downstream tooling can flag the export as partial.

Typical upload flow for GitHub code scanning:

```yaml
- name: Upload SARIF to GitHub
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: sencho-scan.sarif.json
```

## Scan history

Every scan Sencho runs is stored with its full vulnerability detail. Scan records are pruned automatically after 90 days to keep the database compact. The history powers two things: digest caching (skip re-scanning a digest already scanned within 24 hours) and trend insight (compare a new scan to its predecessor to see what changed).

Click **Scan history** from the top of the Resources Hub to open the scan history sheet over the current page. The sheet lists completed scans grouped by image, lets you search by image reference, and lets you tick two scans to compare. Close the sheet with Escape, by clicking the overlay, or by clicking the close button in the header.

<Frame>
  <img src="/images/vulnerability-scanning/scan-history-sheet.png" alt="Scan history sheet over the Resources Hub showing 361 scans across 29 images, the Compare primary action enabled with two scans ticked, a search box, pagination, and scans grouped by image reference" />
</Frame>

## Comparing scans

Compare any two completed scans for an image to see what changed.

**From the scan history sheet**: tick two scans (one baseline, one newer) and click **Compare**. Selecting a third scan replaces the oldest selection.

**From an open scan**: click **Compare** in the drawer header, then pick a baseline scan from the dropdown. Only completed scans for the same image appear.

The comparison sheet shows:

- A **delta ribbon** summarizing the net change per severity (CRITICAL, HIGH, MEDIUM, LOW). Net-positive deltas on CRITICAL render in destructive red so a regression on the worst tier is immediately visible.
- Filter pills to switch between **Added** (new findings since the baseline), **Removed** (resolved findings), and **Unchanged** (findings present in both).
- A sorted table of CVEs with severity, affected package, and direct links to the upstream advisory. CVE-prefixed identifiers resolve to [cve.org](https://www.cve.org); GHSA identifiers resolve to the [GitHub Advisory Database](https://github.com/advisories). Critical and high rows carry the same left-rail tint as the scan drawer for visual continuity.

Comparisons are scoped to a single node; scans taken on different nodes cannot be compared against each other.

Cross-image comparisons (picking scans from two different image references) are allowed but flagged with a warning, since package-level changes may reflect image differences rather than CVE drift. In that mode, the **Unchanged** pill is relabeled to **Shared** to reflect that same CVE + package matches across different images are not necessarily the same finding.

Up to 1000 findings per scan are loaded for comparison. When a scan exceeds this limit, the sheet shows a truncation banner indicating the comparison may be incomplete.

<Frame>
  <img src="/images/vulnerability-scanning/scan-compare-sheet.png" alt="Compare scans sheet diffing two profilarr scans, with baseline and current row, a truncation banner, a delta ribbon (HIGH -79, MEDIUM +79), Added/Removed/Unchanged filter pills, and a diff table tinted by severity" />
</Frame>

## How it works

1. On startup, Sencho looks for the `trivy` binary on `PATH` (and the path configured by `TRIVY_BIN` if set) and caches its availability.
2. When a scan is triggered, Sencho resolves the image digest via Docker and checks the 24-hour cache. If a completed scan exists for that digest, it is returned instantly.
3. Otherwise, Sencho spawns `trivy image --format json --quiet <image-ref>` and parses the JSON output into the vulnerability database.
4. Private registry credentials are forwarded automatically by writing a temporary `DOCKER_CONFIG` for the Trivy subprocess, then deleting it when the scan completes.
5. Scan status, counts per severity, and the full vulnerability list are persisted for display in the drawer and for policy evaluation.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Scan button is not visible">
    Sencho hides scanning UI when the Trivy binary is not detected. Check **Settings → Security** for the scanner status, then follow [Installing Trivy](/operations/trivy-setup) if it is missing.
  </Accordion>
  <Accordion title="Scans time out">
    The default scan timeout is 5 minutes. Very large images (2 GB or more) over a slow connection may exceed this; pre-pulling the image to the host speeds up the scan significantly because Trivy then works against the local image store.
  </Accordion>
  <Accordion title="Private registry images fail to scan">
    Sencho forwards the same registry credentials configured under **Settings → Registries** to Trivy. If a pull works in Sencho but a scan fails, make sure the image has been pulled at least once so Trivy can work against the cached local image.
  </Accordion>
  <Accordion title="Badge is out of date after an image update">
    Post-deploy scanning only runs on deploy actions. For long-running images that aren't redeployed, schedule a recurring scan (Skipper or Admiral) or click the shield icon in the Resources Hub to re-scan on demand.
  </Accordion>
  <Accordion title="A scan shows in progress for a long time">
    Scans have a 5-minute internal timeout. The scheduler sweeps every tick and marks any scan stuck `in_progress` for more than 15 minutes as failed, so the UI always recovers on its own. Wait for the sweep, then click the shield icon again to trigger a fresh scan.
  </Accordion>
  <Accordion title="Trivy is not detected after installing it">
    Sencho re-checks for the Trivy binary every ten minutes from the scheduler. Install Trivy on the host, wait for the next window, and the scanning UI lights up automatically.
  </Accordion>
  <Accordion title="Collecting diagnostic logs for support">
    Enable **Developer Mode** under **Settings → Developer** and trigger the failing scan again. The backend logs verbose `[Trivy:diag]` entries covering detection timing, cache hits, Trivy invocation, and parse statistics. Attach these lines when filing a support issue. Turn Developer Mode off once you have captured the output.
  </Accordion>
  <Accordion title="Post-deploy scan failure notifications">
    When a post-deploy scan fails for a specific image (for example because Trivy could not resolve a private registry pull), Sencho dispatches a warning-level alert through your configured notification channels. The deploy itself is never blocked by a scan failure.
  </Accordion>
  <Accordion title="A deploy was blocked by a policy I did not expect">
    The block dialog names the policy that fired and lists every image that violated the threshold. Open **Settings → Security** and review the matching policy: check the stack pattern glob and the max severity. If the policy should not apply, tighten the pattern (for example `staging-*` instead of `*`) or turn **Block on deploy** off to keep the evaluation in alert-only mode. Admins can bypass a single deploy with the **Deploy anyway** button; every bypass is recorded in the [Audit Log](/features/audit-log) with the actor, policy, and violation list.
  </Accordion>
  <Accordion title="Trivy is not installed and a deploy with a block policy went through">
    Sencho fails open when Trivy is not installed on the target node, so operators are never locked out by tooling state. A warning alert is dispatched through your configured notification channels with the message `Pre-deploy scan for "<stack>" skipped: Trivy not installed on this node`. Install Trivy from **Settings → Security** to enforce the policy; see [Installing Trivy](/operations/trivy-setup) for options.
  </Accordion>
  <Accordion title="Compare button is disabled in the scan history sheet">
    The Compare primary action enables only after exactly two scans are ticked. Selecting zero, one, or three scans leaves it disabled. If you have only one scan for an image, trigger a second scan from the Resources Hub (or wait for a scheduled scan), then return to Scan history and tick both.
  </Accordion>
  <Accordion title="Comparison shows unexpected results">
    Two common causes:

    - **Cross-image comparison.** When the Baseline and Current rows at the top of the sheet point at different image references, a warning banner appears and the "Unchanged" pill is labeled "Shared". Items in that bucket match on CVE + package name but may not be the same finding across two distinct images. Stick to scans of the same image reference for apples-to-apples drift analysis.
    - **Truncated scans.** When either scan has more than 1000 stored findings, the sheet shows a truncation banner. Items past the 1000-row cap do not contribute to the Added / Removed / Unchanged buckets and totals may be misleading. Re-run the scan with a tighter image or scope the investigation to the most severe findings to avoid truncation.
  </Accordion>
  <Accordion title="An older scan is missing from Scan history">
    The Scan history sheet uses server-driven pagination. If you know the scan exists but cannot see it, use the search box to filter by image reference, or page forward with the arrows in the card header. Scans older than 90 days are pruned automatically to keep the database compact.
  </Accordion>
  <Accordion title="Scan policies are missing on one of my nodes">
    Scan policies are managed from the control Sencho instance and replicate to every remote. On a replica, **Settings → Security** shows a banner explaining that rules are managed upstream. See [Fleet Sync](/features/fleet-sync) for how replication works and how to investigate push failures.
  </Accordion>
  <Accordion title="I suppressed a CVE but the scan badge count is unchanged">
    Badge counts always reflect raw findings so alerting stays accurate. Policy evaluation also uses raw findings by default; if you want suppressed CVEs to stop counting toward block-on-deploy policies, enable **Honor suppressions in deploy blocks** in **Settings → Security**. Open the scan drawer to confirm the row is dimmed with a shield-off icon. See [CVE Suppressions](/features/cve-suppressions) for how the filter is applied across the drawer, compare sheet, and other read surfaces.
  </Accordion>
  <Accordion title="The Secrets tab is empty on an image I expect to contain credentials">
    Secret detection matches against Trivy's built-in rule set, which focuses on well-known provider patterns. Plain text passwords, custom token formats, or values that do not match any published rule will not appear. Make sure you picked **Full scan (vulnerabilities + secrets)** from the shield-icon menu; a plain vulnerability scan does not walk the filesystem.
  </Accordion>
  <Accordion title="Compose misconfiguration scan returns 404">
    The scanner needs a Compose file in the stack directory. If the stack was created outside Sencho and the working directory does not contain a file named `compose.yml`, `compose.yaml`, `docker-compose.yml`, or `docker-compose.yaml`, the scan returns 404. Name the file accordingly or keep the stack under Sencho's managed compose directory.
  </Accordion>
  <Accordion title="SARIF download returns 409 Scan not complete">
    SARIF export requires a completed scan. If a scan failed, timed out, or is still running, the button downloads nothing and the server returns a 409. Trigger a fresh scan from the Resources Hub or the stack page, wait for the drawer to populate, then export again.
  </Accordion>
  <Accordion title="SARIF download is missing findings I see in the drawer">
    Each finding type (vulnerabilities, secrets, misconfigs) is capped at 5000 rows in the SARIF export. When any type trips the cap, the SARIF run includes `properties.truncated: true` along with the original counts, and the server logs a warning. The drawer paginates beyond 5000 so it shows everything, but the export is bounded for memory safety. If you need every row, narrow the scope (per-stack scan, per-image scan) before exporting.
  </Accordion>
  <Accordion title="Acknowledge button is missing on a misconfig finding">
    The button is admin-only and hidden on replica nodes. Replicas read acknowledgements from the control via fleet sync; mutations happen on the control. If you are an admin on the control and still do not see the button, the row is likely already acknowledged. Look for the dimmed strikethrough rendering and hover for the recorded reason.
  </Accordion>
  <Accordion title="Findings reappeared after deleting a CVE suppression or misconfig acknowledgement">
    Suppressions and acknowledgements are applied at read time and never modify stored finding rows. Removing one immediately resurfaces the underlying finding everywhere it appears (drawer, compare sheet, badge counts, SARIF export). The behavior is intentional: deleting an acknowledgement is meant to revert the operator decision, not to mask history.
  </Accordion>
  <Accordion title="Outbound traffic to ghcr.io and aquasecurity from the scanner host">
    Trivy fetches its CVE and secret-rule database from public registries on first scan and refreshes periodically; that egress is required for vulnerability scanning to work. Sencho does not emit telemetry of its own. If your environment forbids egress, see Trivy's [air-gapped scanning guide](https://trivy.dev/latest/docs/advanced/air-gap/) for how to pre-seed the database and run scans with `--offline-scan`.
  </Accordion>
  <Accordion title="Compose stack scan returns 409 Already scanning this stack">
    Sencho deduplicates concurrent scans of the same stack so two simultaneous calls cannot double-process the result. Wait for the in-flight scan to finish (its row appears in Scan history with status `in_progress` and flips to `completed` or `failed` shortly after) and trigger again.
  </Accordion>
</AccordionGroup>
