* 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.
* fix(compose-doctor): recognize Docker socket proxy topologies
Classify dedicated socket proxies separately from direct docker.sock mounts so Doctor no longer recommends adopting a proxy the stack already uses. Closes#1790.
* fix(compose-doctor): widen socket proxy detection and flag writable proxy sockets
Close the remaining gaps in socket proxy topology handling: a service that
points at a proxy through a tcp:// endpoint on its command line (how Traefik
and friends do it) now gets the client note, proxy API group flags are read
for any truthy value rather than a literal 1, and underscore or dot separated
proxy names are recognized.
Two cases that previously slipped through now surface: a service classified
as a proxy purely by name or image but mounting docker.sock read-write is
reported as high, and a proxy on the implicit default network or on a network
the rendered model does not describe counts as non-internal. A direct socket
mount alongside an existing proxy now names that proxy in its fix.
* fix(compose-doctor): require corroboration before a service name classifies a socket proxy
A service name is free text the author controls, so on its own it could move a
writable docker.sock mount out of the high direct-mount finding. A known proxy
image is an artifact identity and still stands alone; a proxy-shaped name now
counts only alongside an observable fact, a read-only socket or a scoped API
group key.
* fix(compose-doctor): tighten socket-proxy detection against live upstream behavior
Require proxy API flags to be exactly 1 (matching tecnativa and linuxserver
images), count only those enabled flags when classifying a proxy, extract
tcp hosts from DOCKER_HOST instead of treating key presence as a proxy client,
and correlate each client note to one proxy instance by both name and shared
network. Soften the published-port finding so it claims reachability rather
than Docker API exposure for unrelated ports.
* fix(compose-doctor): resolve effective healthcheck coverage
Compose Doctor now classifies healthcheck coverage from the Compose model, running containers, and local images so image-provided HEALTHCHECKs are not false positives. Update Guard shares the same presence helper so test NONE is not treated as active.
* fix(compose-doctor): fix healthcheck project label and empty compose HC
Use the Compose project name for runtime container listing so stacks whose name: differs from the directory still get runtime evidence. Treat empty or timing-only healthcheck objects as absent rather than active.
* fix(compose-doctor): treat inherited healthcheck as All Clear note
Inherited image healthchecks no longer block All Clear; they surface under a notes section and cannot be acknowledged.
* feat(security): surface Compose internet-reachability exposure in posture
Builds a per-stack per-service exposure descriptor from the rendered
effective Compose model, cached at deploy/update time, and joins it into
the Security action posture. A service is publicly exposed when it
publishes a port on a non-loopback host IP or uses host networking.
The exposure cache lives in a new stack_exposure table, refreshed inside
ComposeService.deployStack and updateStack (covering all funneled paths:
manual, scheduler, mesh, templates, labels, App Store, Git, webhooks).
Cleanup runs on stack delete, blueprint withdrawal, and node delete.
The overview route intersects the exposed image set with the existing
per-image suppression-aware Critical/High tally, so a clean public
nginx does not escalate posture. The scan sheet shows a "Published
service" or "Internal only" evidence badge per image.
* fix(test): provide fresh auto-close proc for exposure spawn in stall tests
Two deployStack idle-stall tests used mockSpawn.mockReturnValue(proc)
which returned the same already-closed process for the new config spawn
added by the exposure refresh. The renderConfig promise hung waiting for
a close event that had already fired.
The fix uses mockImplementation to return the controlled proc for the
first spawn (up) and a fresh auto-closing proc for the second spawn
(config via refreshExposureCache).
* fix(security): tighten loopback detection, clarify exposure semantics, drop internal-only badge
- Expand isLoopback to cover full 127.0.0.0/8 range (127.0.0.2 etc)
- Clarify that exposure is configured (Compose model), not live topology
- Remove "Internal only" badge: false is not proof of non-exposure when
other stacks using the same image may lack a cached descriptor