Changelog
This is a mirror of the project's
CHANGELOG.md
for the post-v0.5.0 entries that the rest of this site references.
For the canonical list back to v0.1.0, follow the link above.
The format is based on Keep a Changelog, and the project adheres to Semantic Versioning.
[0.10.0] - 2026-04-30
A non-feature minor release: full product documentation website, Apache 2.0 relicensing, and a NOTICE file crediting the third-party scanners SecureScan orchestrates.
Added
- Documentation website at https://metbcy.github.io/securescan/.
41 pages covering install, all features (v0.6.0 → v0.9.0), API
reference, deployment, security, and CLI usage. Built with mdBook,
auto-deployed on every push to
mainthat touchesdocs/**via a new.github/workflows/docs.ymlworkflow. LICENSEfile (Apache 2.0, full text) andNOTICEfile crediting the third-party scanners which are invoked as subprocesses, not redistributed.
Changed
- Project relicensed to Apache 2.0. Previously declared MIT in
README.mdandbackend/pyproject.tomlbut noLICENSEfile existed in the repo. Apache 2.0 fits a security-tooling project better — explicit patent grant, NOTICE convention.
No code changes
This release ships zero changes to the API, database schema, scanner behavior, or dashboard UX. v0.9.0 callers can upgrade safely.
0.9.0 - 2026-04-29
A workflow / observability release: dashboards now have a notification bell, operators can issue outbound webhooks to Slack, Discord, or any HTTP receiver with HMAC-signed deliveries and a durable retry queue, and the v0.7.0 SSE live-progress stream now works in authenticated deployments via short-lived signed event tokens (the v0.8.0 deferral is closed).
Added
- SSE event tokens. New
POST /api/v1/scans/{id}/event-token(read scope) returns a 5-minute HMAC-signed token bound toscan_id+ the caller'skey_id. The SSE endpointGET /api/v1/scans/{id}/eventsnow accepts?event_token=…as an alternative toX-API-Keyso EventSource can authenticate. Token verification rehydrates the principal at connect time so a revoked DB key invalidates outstanding tokens immediately. Frontend rotates tokens at half-life with a single re-mint on error before falling back to polling. Signing secret fromSECURESCAN_EVENT_TOKEN_SECRET; required whenSECURESCAN_AUTH_REQUIRED=1. → SSE event tokens. - Outbound webhooks.
POST /api/v1/webhooks(admin) creates durable subscriptions to scan lifecycle events. Each delivery is persisted inwebhook_deliveriesBEFORE the HTTP call, so a backend restart resumes any pending retries. Retry policy: full- jitter exponential backoff capped at 5 minutes, max delivery age 30 minutes. Payloads are HMAC-SHA256 signed viaX-SecureScan-Signature: t=<unix-ts>,v1=<hex-hmac>overf"{t}.{raw_body}"(Stripe-style). FIFO ordering per webhook (different webhooks dispatch concurrently). Slack and Discord URLs auto-detected and reshaped; generic JSON otherwise. New/settings/webhookspage lists webhooks with a delivery log drawer that auto-refreshes every 5s.POST /webhooks/{id}/testfires a synthetic event through the exact dispatcher path so users can verify receivers end-to-end. → Webhooks. - In-app notifications. New
notificationstable; bell icon in the topbar with unread count badge (poll every 30s); 360px popover showing 10 most recent with severity dots. Notifications are auto-created onscan.complete(only whenfindings_count > 0— successful zero-finding scans don't spam the bell),scan.failed,scanner.failed. New/notificationspage lists everything with All / Unread / Read filter chips. Read notifications older than 30 days are pruned at backend startup. → Notifications.
Changed
_log_scan_eventnow triggers three side-effects per emission: the v0.6.1 logger line, the v0.7.0 ScanEventBus publish, and (new) two side-effect hooks for webhook enqueue + notification create. Hooks run viaasyncio.create_taskand swallow DB errors so a failed side-effect can't break a live scan.require_api_keynow accepts?event_token=for the SSE route specifically. The path-match check makes a leaked token only usable on/scans/{id}/events; any other route falls through to strictX-API-Keyvalidation.
Bug fixed during integration
- Dev-mode SSE event tokens were being minted with
key_id="env", then verification rejected them because no env-var was actually configured. Tokens minted in dev mode now use a"dev"sentinel that's accepted only while the system remains in dev mode; if credentials are added later, dev-mode tokens are invalidated. Regression tests intest_sse.py::test_dev_mode_token_round_tripsandtest_dev_mode_token_invalidated_when_auth_enabled.
Tests
- 790 → 863 (+73): 9 event-token unit + 12 SSE token integration (including 2 dev-mode regression), 31 webhooks, 21 notifications.
Deployment notes
- The webhook dispatcher runs as an asyncio task on the same uvicorn worker as the API. Single-worker constraint from v0.7.0 still applies (multi-worker pubsub backplane is a future feature). → Single-worker constraint.
0.8.0 - 2026-04-29
A production-readiness release: API authentication is no longer a
single shared env-var key. Operators can now issue, scope, and revoke
hashed API keys through the dashboard or the API, and the
existing endpoints are gated behind explicit read / write / admin
scopes. The legacy SECURESCAN_API_KEY env var still works as a
break-glass / dev-mode fallback.
Added
- DB-backed API keys with scopes. New
api_keystable stores salted-sha256 hashes — plaintext keys are returned exactly once at creation. Key format:ssk_<10-char id>_<32-char secret>(~250 bits of entropy). Three scopes:read,write,admin. Default new-key scopes are["read", "write"];adminmust be explicitly granted. → API keys.POST /api/v1/keys(admin) —{name, scopes}→ 201ApiKeyCreated(the only response that includes the full secret)GET /api/v1/keys(admin) →ApiKeyView[](no secret)GET /api/v1/keys/me(any authenticated DB key) → caller's own key infoDELETE /api/v1/keys/{id}(admin) → 204; 409 if revoking the target would leave the system with zero admin credentials andSECURESCAN_AUTH_REQUIRED=1is set (lockout protection)
- Per-route scope enforcement. Every
/api/*route now declares a required scope viaDepends(require_scope(...)). A new regression-guard test (test_all_routes_have_explicit_scope) enumeratesapp.routesand fails if any non-public route is missing a scope — preventing future scope-coverage holes. → Scopes. SECURESCAN_AUTH_REQUIRED=1startup safety. When set with no configured credentials, the backend logs CRITICAL and exits with code 2. Catches misconfigured deploys before they accept their first request unauthenticated.- Lockout protection. Revoking the last admin DB key when
AUTH_REQUIRED=1and no env-var key is set returns 409 with a human-readable message. Operators can still delete admin keys freely when an env-var fallback exists. /settings/keysdashboard page. Lists keys (name, prefix, scopes, created, last used, status), with a "New key" modal that enforces the one-shot secret reveal contract.
Changed
auth.pywas rewritten to support both the legacy env-var path and DB keys. Bug fix: an explicit-but-bogus key now always fails with 401 — even when no DB keys remain unrevoked. The previous logic would fall back to dev mode in that scenario, letting a revoked key keep working until at least one other key was created. Caught during integration; regression test intest_revoked_db_key_rejected_when_no_env_var.- The
Principalresolved byrequire_api_keyis stashed onrequest.state.principal, so per-route scope checks don't re-trigger DB writes.
Tests
- 738 → 790 (+52).
Backward compatibility
SECURESCAN_API_KEYenv var still works exactly as before; treated as a synthetic principal with all scopes.- Dev mode (no env, no DB keys) is unchanged: every request passes through and scope checks fail-open.
0.7.0 - 2026-04-29
A workflow + observability release: the dashboard now lets you triage individual findings (status + comments) with verdicts that survive across scans, and replaces 2-second polling with a live event stream so a running scan shows real-time per-scanner progress instead of a frozen "running" badge.
Added
- Findings triage workflow. Each finding now has an optional
triage state (
new,triaged,false_positive,accepted_risk,fixed,wont_fix) and a per-finding comments thread. State is keyed on the stablefingerprintso a "false positive" verdict on a finding survives subsequent rescans of the same target — and even survivesDELETE /scans/{id}. New API:PATCH /api/v1/findings/{fingerprint}/stateGET /api/v1/findings/{fingerprint}/commentsPOST /api/v1/findings/{fingerprint}/commentsDELETE /api/v1/findings/{fingerprint}/comments/{comment_id}→ Triage workflow.
- Triage UI. New compact "Status" column in the findings table.
Default-hide set is
{false_positive, accepted_risk, wont_fix}.fixedis intentionally NOT default-hidden so a "fixed" finding reappearing in a later scan stays visible. - Real-time scan progress (SSE).
GET /api/v1/scans/{scan_id}/eventsstreams scan + per-scanner lifecycle events. Late subscribers get a 200-event replay buffer. Terminal events are never dropped on subscriber backpressure. → Real-time scan progress. - Scan-detail page goes live. New
<ScanProgressPanel>above the StatLine while a scan isrunning/pending.EventSourcereplaces the 2-second poll, with a polling fallback when an API key is configured (the v0.9.0 SSE-with-auth path closes that gap).
Changed
GET /api/v1/scans/{id}/findingsnow returnsFindingWithStateobjects — every existing field, plus an optionalstatepayload. The bareFindingmodel is unchanged, so SARIF / JSON / baseline / CLI exporters keep their existing contract.
Deployment notes
- The SSE event bus is a module-level singleton. SecureScan now
requires
--workers 1for/api/v1/scans/{id}/eventsto work correctly. → Single-worker constraint.
Tests
- 709 → 738 (+29).
0.6.1 - 2026-04-29
A polish release focused on production readiness on real (large)
scans. The 20k-finding scan that shipped during v0.6.0 testing
exposed three issues — a stale-running UI badge, a janky search box,
and a missing delete-scan endpoint — all fixed here. We also added
structured scan lifecycle logs, a user-scoped .env loader so
credentials persist across restarts, and a smarter ZAP install hint.
Added
DELETE /api/v1/scans/{id}removes a scan and cascades its findings. Returns 204 on success, 409 if the scan isrunning/pending(cancel first), 404 otherwise.- Structured INFO logging for the scan lifecycle on the
securescan.scanlogger:scan.start,scanner.start,scanner.complete(withduration_sandfindings_count),scanner.skipped,scanner.failed,scan.complete,scan.failed,scan.cancelled. Tail/tmp/securescan-backend.logto debug a scan in flight. ~/.config/securescan/.env(or$XDG_CONFIG_HOME/securescan/.env) is auto-loaded at backend startup. → Local config (.env).
Changed
frontend/src/app/scan/[id]/page.tsxpolling no longer refetches the entire findings array every 2 seconds. While a scan is running, only the lightweight scan-status record is polled; findings and summary load once on mount and once when status flips tocompleted.FindingsTableis responsive again on 20k-finding scans. Search input uses React 19'suseDeferredValue; a single memoized projection replaces per-keystroke string normalization.
Tests
- 690 → 709 (+19).
0.6.0 - 2026-04-29
This release pairs an end-to-end frontend redesign with two backend
durability features. The dashboard moves off neon traffic-light
colors and ad-hoc card grids onto an OKLCH design system with a
single-hue severity ramp, dense data-table layouts, a new app shell
(sidebar + sticky topbar + ⌘K command palette), and a brand-new
/diff page for PR-style scan comparison. On the API side, all
routes are now mounted under /api/v1/... (legacy /api/... paths
still work, with Deprecation / Sunset response headers), and
POST /scans is protected by an in-memory per-key token-bucket rate
limiter.
Added
/api/v1versioning prefix; legacy/api/*paths returnDeprecation,Link, andSunset(Dec 31 2026) response headers. → Versioning & deprecation.- In-memory rate limiting on
POST /api/scansandPOST /api/v1/scans; per-API-key token-bucket, configurable viaSECURESCAN_RATE_LIMIT_PER_MIN/_BURST/_ENABLED. → Rate limits. - New
/diffdashboard page — PR-style scan-vs-scan diff with base/head pickers, summary chips, and tabbed findings. → Diff & compare. - Command palette (⌘K) for navigation, recent scans, and quick actions.
- Theme toggle and
next-themesintegration; dark default with light theme support.
Changed
- Frontend redesigned end-to-end. New OKLCH design tokens, single-hue
severity ramp (replaces neon traffic-light coloring), Geist
Sans/Mono typography, restrained color strategy per the new
DESIGN.md. → Dashboard tour.
Earlier releases
For v0.5.0, v0.4.0, v0.3.0, v0.2.0, and v0.1.0, see
CHANGELOG.md
on GitHub. Highlights:
- 0.5.0 — API key auth, structured JSON logging, request-ID
correlation,
/readydistinct from/health,Scan.scanners_run/scanners_skippedpersisted. - 0.4.0 —
pr-mode: inlineGitHub Action mode with one inline comment per finding,\``suggestion` blocks, idempotent re-runs via fingerprint markers. - 0.3.0 —
.securescan.ymlconfig, inline# securescan: ignorecomments, baselines,securescan compare,severity_overrides,--show-suppressed/--no-suppress,--ai/--no-ai. - 0.2.0 —
securescan diff, deterministic SARIF output, GitHub Action, container image, signed releases (cosign + sigstore-python), per-finding fingerprints. - 0.1.0 — Initial public release. 14 scanners, FastAPI backend, Next.js dashboard, SBOM (CycloneDX + SPDX), AI enrichment, OWASP / CIS / PCI-DSS / SOC 2 compliance mapping.