mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-13 04:06:59 +00:00
fix(security): tie fixable CVE posture to image-update evidence (#1815)
* fix(security): tie fixable CVE posture to image-update evidence Stop treating Trivy fixed_version alone as an Update affected images CTA. Reuse persisted ImageUpdateService status so Security only offers Review update when an applicable image update is confirmed, and otherwise surfaces waiting or uncertain remediation with truthful affordances. * fix(security): move image-update recheck helper out of OverviewTab Satisfy react-refresh/only-export-components so Frontend lint passes. * fix(security): preserve posture reason image targets in Images drill-down Carry affected image refs on overview reasons so public exposure and related CTAs open a clearable targeted Images list instead of an unfiltered hunt. * fix(security): attach Networking exposure intent to posture targets Preserve stack/service context and intentional classification on network-exposed Security reasons without suppressing risk or claiming Internet reachability. * fix(security): persist Images exposure intent and triage scope Standing image summaries carry Networking intent context with cap-safe aggregates, Anatomy Networking links, and scan-sheet triage that defaults to the current image. * fix(security): clear CI lint errors for exposure helpers * fix(security): stop intentional exposure from forcing Action needed Separate exposure fact, intent correctness, and vulnerability drivers so package fixed_version cannot recreate a permanent public_exposure blocker. * fix(security): define Monitoring residual-risk narrative * fix(security): define Secure via residual Crit/High triage Replace triage-blind raw Crit/High Secure gating with residual material risk so accepted and ignored stay Monitoring, while not_affected, false positive, and fixed can clear residual without claiming no detections. * fix(security): exclude rollback-hold images from Security scans Hold-only sencho-rb tags are recovery state; keep them out of Trivy node scans, Security inventory, and Overview posture while dual-tagged images remain under their registry tag. * fix(security): keep authoritative no-update rows after preview Opening a stack page must not delete ok+false stack_update_status evidence; Security treats a missing row as uncertain and would flip waiting-upstream to unknown.
This commit is contained in:
@@ -32,7 +32,7 @@ The dialog has the following fields:
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **CVE or advisory ID** | Required. Accepts both `CVE-YYYY-NNNN` and `GHSA-xxxx-xxxx-xxxx`. |
|
||||
| **Triage decision** | How the finding was triaged: accepted risk (default), not affected, false positive, affected, fixed, ignored, or needs review. A decided state (accepted, not affected, false positive, fixed, ignored) stops the finding from driving the action posture; "needs review" and "affected" keep it counted but still actionable. |
|
||||
| **Triage decision** | How the finding was triaged: accepted risk (default), not affected, false positive, affected, fixed, ignored, or needs review. Not affected, false positive, and fixed clear residual Crit/High so Secure can become reachable. Accepted and ignored residual risk stay on Monitoring and do not turn Secure green. Needs review and affected stay actionable. |
|
||||
| **OpenVEX justification** | Required when the triage decision is not affected or false positive. Explains why the vulnerable code is not exploitable, for example vulnerable code not present, vulnerable code not in the execute path, component not present, or inline mitigations already exist. Carried into the OpenVEX export for that decision. |
|
||||
| **Package (optional)** | Leave blank to suppress every occurrence of this CVE across every package, or pin a specific package name (e.g. `openssl`) to narrow the scope. |
|
||||
| **Image pattern (optional)** | Glob applied to image references (`*` matches any sequence, case-sensitive). For example, `lscr.io/linuxserver/*` matches every LinuxServer image, and `*alpine*` matches anything containing `alpine`. Leave blank to apply fleet-wide. |
|
||||
@@ -45,7 +45,14 @@ The dialog has the following fields:
|
||||
|
||||
### Suppressing directly from a scan result
|
||||
|
||||
The panel's empty state hints at the faster path: from any vulnerability scan, click the small shield icon at the right edge of a finding's row. The dialog opens pre-filled with the CVE ID and the package name from that row (both read-only in this flow), leaving you to set the Triage decision (accepted risk by default), add a Reason, an optional Image pattern, and an optional Expiry. Choosing not affected or false positive also requires an OpenVEX justification. This is the recommended workflow for everyday triage, because it keeps the scope as narrow as the originating finding. To broaden the scope (for example, to suppress across every package), create the rule from the **Security** page → **Suppressions** tab instead.
|
||||
The panel's empty state hints at the faster path: from any vulnerability scan, click **Triage finding**
|
||||
on a finding's row. The dialog opens pre-filled with the CVE ID, the package name, and the **Image
|
||||
pattern** set to the current image reference so the decision stays scoped to that image by default.
|
||||
You set the Triage decision (accepted risk by default), add a Reason, and an optional Expiry. You can
|
||||
clear or broaden the image pattern if you intend a wider match. Choosing not affected or false
|
||||
positive also requires an OpenVEX justification. This is the recommended workflow for everyday
|
||||
triage. To start from a blank image pattern for fleet-wide matching, create the rule from the
|
||||
**Security** page → **Suppressions** tab instead.
|
||||
|
||||
### How specificity is resolved
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ When a deploy or update fails, Sencho classifies the failure from the compose ou
|
||||
The sidebar's per-stack **Update** action runs the same path as the editor toolbar, so it shows the same readiness dialog and deploy progress. One click on **Update now** proceeds. On nodes that do not advertise the capability, updates run directly without the dialog.
|
||||
</Accordion>
|
||||
<Accordion title="Why do I see sencho-rb/... images in docker images on the host">
|
||||
Those are automatic rollback images: an opaque copy of a service's prior image, held so Sencho can restore it if a full-stack update fails. They are not leftovers. Sencho keeps them out of **Resources → Images** on purpose (they are recovery state, not image inventory) and lists them in **Resources → Rollback** instead, showing which stack and generation each one belongs to and how soon it clears on its own. If one still carries a normal registry tag too, it also stays visible in the Images tab with a **Rollback protected** badge.
|
||||
Those are automatic rollback images: an opaque copy of a service's prior image, held so Sencho can restore it if a full-stack update fails. They are not leftovers. Sencho keeps them out of **Resources → Images** and **Security** on purpose (they are recovery state, not image inventory or scan targets) and lists them in **Resources → Rollback** instead, showing which stack and generation each one belongs to and how soon it clears on its own. If one still carries a normal registry tag too, it also stays visible in the Images tab with a **Rollback protected** badge, and Security continues to scan that registry tag.
|
||||
</Accordion>
|
||||
<Accordion title="Deleting a rollback-protected image fails">
|
||||
That failure is intentional: the image is protected by an active or recently superseded rollback generation. Open **Resources → Rollback**, find the matching generation, and use **Release** there if you are sure you do not need it. Releasing the current generation means Sencho cannot automatically roll that stack back until its next successful full-stack update.
|
||||
|
||||
+62
-21
@@ -9,7 +9,7 @@ center, so you can answer "what should I look at first?" without hunting through
|
||||
scoped to the active node: the findings and scanner status you see reflect whichever node is selected.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/security/overview.png" alt="Security page Overview tab with an Action needed masthead reading '3 actions: fixable findings, detected secrets' and CRITICAL/HIGH/LAST SCAN stat tiles, a Why Action needed review-queue card, a 30-day risk trend chart, an Action posture breakdown, and a Scan this node button." />
|
||||
<img src="/images/security/overview.png" alt="Security page Overview tab with an Action needed masthead, CRITICAL/HIGH/LAST SCAN stat tiles, a Why Action needed review-queue card, a 30-day risk trend chart, an Action posture breakdown, and a Scan this node button." />
|
||||
</Frame>
|
||||
|
||||
The page is organized into tabs: Overview, Images, Compose risks, Secrets, Policies, Suppressions,
|
||||
@@ -20,17 +20,35 @@ History, and Scanner setup.
|
||||
The overview opens with a status masthead that reads your **action posture** at a glance, the answer
|
||||
to "what can and should I do right now?":
|
||||
|
||||
- **Action needed**: something concrete to act on, such as a fixable Critical or High finding, a
|
||||
detected secret, a dangerous Compose setting, or a known-exploited (CISA KEV) CVE.
|
||||
- **Monitoring**: Critical or High findings exist, but none are currently actionable (no fix
|
||||
available, or already triaged).
|
||||
- **Secure**: nothing actionable right now. This is never a claim that no vulnerabilities exist.
|
||||
- **Action needed**: something concrete to act on that Sencho can support right now, such as a
|
||||
newer image available to review for Critical or High findings, a detected secret, a dangerous
|
||||
Compose setting, a known-exploited (CISA KEV) CVE, elevated exploit risk on a network-exposed
|
||||
workload, or exposure that conflicts with declared Networking intent.
|
||||
- **Monitoring**: no Action needed blocker, but material residual risk, a pending review, or
|
||||
relevant uncertainty remains. Residual Critical/High include undecided findings and accepted or
|
||||
ignored residual risk. Package fixes may exist without an applicable container-image update,
|
||||
workloads may be intentionally network-exposed without an independent Security driver, and
|
||||
waiting-upstream or update-check-uncertain rows appear under **Why Monitoring** without a red
|
||||
masthead.
|
||||
- **Secure**: no Action needed blocker and no residual material Critical/High or pending
|
||||
security-review condition under current evidence. Not affected, false positive, and fixed can
|
||||
clear residual risk for those findings; accepting residual risk does not. This is never a claim
|
||||
that no scanner detections exist.
|
||||
- **Unknown**: the scanner is not installed, or no scan has completed yet.
|
||||
|
||||
Monitoring keeps residual risk visible (raw detections, accepted risk, waiting-upstream rows,
|
||||
intentional exposure context). Secure is stricter: residual Critical/High and review reasons must
|
||||
be cleared, not merely the absence of an immediate action.
|
||||
|
||||
Raw Critical and High counts stay visible next to the posture as **scanner detections**, not as the
|
||||
posture itself: a vulnerable component being present is not the same as a reachable, exploitable risk.
|
||||
The masthead carries a standing note to that effect, and posture weighs fix availability, exploit
|
||||
intelligence, and triage decisions rather than raw severity alone.
|
||||
The masthead carries a standing note to that effect, and posture weighs package-fix evidence,
|
||||
image-update availability, exploit intelligence, exposure, and triage decisions rather than raw
|
||||
severity alone. A Trivy package fix does not by itself mean Sencho can update the image; when a
|
||||
newer image is confirmed available the review queue offers **Review update**, and when no applicable
|
||||
update is identified it surfaces **Waiting for upstream image** instead of an impossible update
|
||||
instruction. Deploy policies remain orthogonal: they may still block admission on package-fix
|
||||
Critical/High even when Security posture is Monitoring.
|
||||
|
||||
An admin on a node with a ready scanner sees a **Scan this node** button above the review queue. It
|
||||
opens a popover to pick any combination of image vulnerabilities, image secrets, and Compose
|
||||
@@ -40,11 +58,18 @@ clean.
|
||||
|
||||
Below the masthead, a **Review queue** card leads the overview when actions or review items exist.
|
||||
When the posture is Action needed the card is titled **Why Action needed** and lists each concrete
|
||||
action with a count and a tab-shortcut button: fixable findings, known-exploited CVEs, detected
|
||||
secrets, unacknowledged Compose risks, and publicly exposed affected images. When only monitoring
|
||||
items remain (exposed images with no fix or known exploit, findings awaiting triage, stale or failed
|
||||
scans) the card is titled **Review queue** and lists them without the red masthead, so the operator
|
||||
can see what to keep an eye on without a permanent alarm.
|
||||
action with a count and a tab-shortcut button: newer images available to review, known-exploited
|
||||
CVEs, elevated exploit risk on network-exposed workloads, detected secrets, unacknowledged Compose
|
||||
risks, and exposure that conflicts with declared Networking intent. Intentional network exposure is
|
||||
Security context, not an independent Action needed reason by itself. When only monitoring items
|
||||
remain (waiting for an upstream image, update availability unknown, network-exposed images not yet
|
||||
classified, findings awaiting triage, stale or failed scans) the card is titled **Why Monitoring**
|
||||
and lists them with View findings shortcuts (and Check again when image update availability could
|
||||
not be established and you can manage the node), so the operator can see what to keep an eye on
|
||||
without a permanent alarm. Shortcuts that carry affected image identities open Images already
|
||||
narrowed to those images, with a clearable banner so you can return to the full list. When the
|
||||
reason includes exact driving findings, opening an image filters the scan report to that contributing
|
||||
set.
|
||||
|
||||
The charts then lead with prioritization rather than raw severity: a **risk trend** for context,
|
||||
an **action posture** breakdown (fixable, known-exploited, needs-review, accepted, not-affected), a
|
||||
@@ -64,11 +89,26 @@ clear "overview unavailable" state and the other tabs keep working.
|
||||
<img src="/images/security/images-tab.png" alt="Security page Images tab listing scanned images with Image, Findings, Last scan, Severity, and Actions columns; findings show critical/high counts and fixable totals, and one row shows a Clean badge." />
|
||||
</Frame>
|
||||
|
||||
Image findings list every scanned image on the active node with its highest severity. Selecting an
|
||||
image opens the full scan report, where you can review vulnerabilities, triage a CVE, compare against
|
||||
another scan, and export a CSV, a SARIF file, or an SBOM. Each finding carries evidence tags so you can
|
||||
tell scary from exploitable at a glance: known-exploited (KEV), EPSS exploitation probability, the
|
||||
CVSS score, and vendor "will not fix" status, alongside whether a fix is available.
|
||||
Image findings list every scanned image on the active node with its highest severity. Sencho
|
||||
rollback-hold tags (`sencho-rb/...:hold`) are recovery state, not Security inventory: node-wide
|
||||
scans skip them, and existing hold-only scan rows stay out of this list and the Overview posture.
|
||||
When you arrive
|
||||
from a Security posture action that named specific images, the list opens already filtered to those
|
||||
images and shows a clearable banner with the reason and count. Images configured beyond loopback (or
|
||||
with host networking) carry a **Network exposed** evidence label on the row during ordinary browsing,
|
||||
not only after a posture drill-down. That label means Compose declares non-loopback reachability; it
|
||||
is not a claim that the service is reachable from the Internet. When Networking exposure intent is
|
||||
available, the row also shows compact intent evidence (for example Intent: public, Intent mismatch,
|
||||
or Intent: not classified). Open Networking from that evidence to review or correct classification
|
||||
for the matching stack. Intentional exposure raises the security relevance of vulnerable findings
|
||||
but does not by itself keep the masthead on Action needed. Open the image, remediate, or triage
|
||||
individual findings (Accepted risk and related decisions). There is no image-level "accept residual
|
||||
risk" control. Compose Doctor acknowledgements are a separate triage surface and do not clear
|
||||
Security exposure context. Selecting
|
||||
an image opens the full scan report, where you can review vulnerabilities, triage a finding, compare
|
||||
against another scan, and export a CSV, a SARIF file, or an SBOM. Each finding carries evidence tags
|
||||
so you can tell scary from exploitable at a glance: known-exploited (KEV), EPSS exploitation
|
||||
probability, the CVSS score, and vendor "will not fix" status, alongside whether a fix is available.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/security/scan-report.png" alt="Vulnerability scan report sheet for nginx:latest showing 340 vulnerabilities, a Critical/High/Medium/Low summary, Compare/CSV/SARIF/SBOM export buttons, and a findings table with CVE, package, severity, installed version, and per-row EPSS and CVSS evidence tags." />
|
||||
@@ -99,7 +139,7 @@ the secret findings for a scan.
|
||||
## Policies
|
||||
|
||||
<Frame>
|
||||
<img src="/images/security/add-policy.png" alt="New policy dialog with a Name field, an optional glob-style Stack pattern field, Block conditions toggles for Severity threshold, Known-exploited (KEV), and Fixable Critical/High, a Block on deploy toggle, and an Enabled toggle." />
|
||||
<img src="/images/security/add-policy.png" alt="New policy dialog with a Name field, an optional glob-style Stack pattern field, Block conditions toggles for Severity threshold, Known-exploited (KEV), and Package fix available (Critical/High), a Block on deploy toggle, and an Enabled toggle." />
|
||||
</Frame>
|
||||
|
||||
The Policies tab manages deploy-enforcement scan policies: a policy names one or more block
|
||||
@@ -121,8 +161,9 @@ in Settings. These are governed by the local instance, so this tab is shown when
|
||||
node; switch to the local node to manage them.
|
||||
|
||||
Suppressing a CVE records a **triage decision**: accepted risk, not affected, false positive, fixed,
|
||||
ignored, or needs review, with an optional OpenVEX justification. Decided findings stop driving the
|
||||
action posture; a "needs review" decision stays counted but keeps the finding actionable. You can
|
||||
ignored, or needs review, with an optional OpenVEX justification. Not affected, false positive, and
|
||||
fixed clear residual Crit/High for Secure. Accepted and ignored residual risk stay on Monitoring and
|
||||
do not turn the page green. Needs review and affected stay actionable. You can
|
||||
export the fleet's triage decisions as an OpenVEX document for use with other tooling.
|
||||
|
||||
## History
|
||||
|
||||
+4
-3
@@ -1890,9 +1890,10 @@ paths:
|
||||
description: |
|
||||
Computes the same preview as GET. When every image reports
|
||||
`check_status: ok` and `has_update` is false (mixed `ok` +
|
||||
`not_checkable` does not clear), clears sticky confirmed update rows
|
||||
for the stack and sets `reconciled: true`. Requires `stack:read`
|
||||
permission.
|
||||
`not_checkable` does not clear), deletes sticky partial, failed,
|
||||
and ok+true rows and sets `reconciled: true` only when a row was
|
||||
deleted. Existing ok+false rows are kept (`reconciled` stays false).
|
||||
Requires `stack:read` permission.
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/stackName"
|
||||
- $ref: "#/components/parameters/nodeId"
|
||||
|
||||
Reference in New Issue
Block a user