Skip to content

fix: resolve remaining LOW security-edge items P3-3 and SC-14 - #77

Merged
tyler-rich merged 2 commits into
devfrom
claude/session-9d5ra6
Jul 20, 2026
Merged

fix: resolve remaining LOW security-edge items P3-3 and SC-14#77
tyler-rich merged 2 commits into
devfrom
claude/session-9d5ra6

Conversation

@tyler-rich

@tyler-rich tyler-rich commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

Resolves the two still-open LOW items with a security edge from the 2026-07 review-remediation backlog (docs/reviews/STATUS.md § "Remaining work"). They touch disjoint files and land as two separate commits.

P3-3 — surface credential/filter option-fetch failures (frontend)

NewScanPage.tsx and ScansPage.tsx each wrapped their option fetch in an empty catch, so a failed load left the credential/filter pickers silently empty — indistinguishable from "none configured." An operator could then launch a private-image or -repository scan anonymously, believing no credential was saved, only for the scan to fail minutes later with an opaque auth error.

  • On failure, set an optionsError state and reset the lists (rather than swallowing the error).
  • New Scan shows a yellow warning Alert with a Retry action above the registry/git-credential picker, telling the operator saved credentials couldn't load and launching now would scan anonymously.
  • Scan history shows a non-blocking inline warning (with Retry) that the initiator/tag lists may be incomplete.
  • A successful (re)load clears the warning.

No frontend test added: the Vitest suite runs in the Node environment and covers only pure src/lib/ helpers — there is no jsdom/React-Testing-Library harness to render a page component, and adding one is out of scope for a minimal LOW fix (consistent with the existing untested page-effect posture noted in STATUS.md § 1).

SC-14 — keep the backend test suite and dev scripts out of the runtime image (docker)

The final image stage copies the backend tree wholesale (COPY … backend/ /app/backend/), shipping backend/tests/ and backend/scripts/ into the published scanner image — needless bloat and attack surface.

  • Excluded backend/tests/ and backend/scripts/ from the build context via the root .dockerignore (the only ignore file Docker honors for a normal build; a backend/.dockerignore would be a silent no-op), leaving the layer ordering of the final stage untouched.
  • The runtime COPY backend/ now brings in only alembic/, alembic.ini, app/, pyproject.toml, and requirements.lock — everything Alembic migrations + uvicorn app.main:app need.
  • Verified no runtime code imports tests/scripts (app and the Alembic env.py import only from app), so the image still runs and imports unchanged.
  • Guarded two ways: a static check in test_dockerfile_supply_chain.py, plus a content assertion in the CI dogfood job ("Image — build + dogfood self-scan") that runs against the real built image and fails if /app/backend/tests or /app/backend/scripts is present (or if the runtime app/alembic trees go missing). The exclusion was also pre-confirmed by a faithful .dockerignore pattern-match simulation.

Testing

  • Frontend: ESLint, Prettier, tsc, and Vitest (21 tests) all green.
  • Backend: ruff and black --check clean; test_dockerfile_supply_chain.py (incl. the new SC-14 guard) passes.
  • CI dogfood image build + self-scan green, and the new content-assertion step confirms the built image excludes backend/tests/backend/scripts.

Docs

  • Dated docs/ARCHIVE.md § 14 entries added for both fixes.
  • docs/reviews/STATUS.md moves P3-3 and SC-14 from "Remaining work" to "Resolved."

An empty catch around the registry/git-credential and history filter
option fetches left the pickers silently empty on failure, which is
indistinguishable from 'none configured' — an operator could launch a
private-image or -repository scan anonymously believing no credential was
saved. Set an optionsError state and reset the lists on failure, and show
a non-blocking warning with a Retry action so a failed load is no longer
mistaken for 'none configured'.

See docs/ARCHIVE.md § 14 (Deviations) for this fix.
…ge (SC-14)

The final image stage copies the backend tree wholesale, shipping the
pytest suite and the dev env-example script into the published image —
needless bloat and attack surface on a security tool's own image. Exclude
backend/tests/ and backend/scripts/ from the build context via the root
.dockerignore (the only ignore file Docker honors for the context), so the
runtime COPY brings in only alembic/, alembic.ini, app/, pyproject.toml,
and requirements.lock. No runtime code imports those trees, so the image
still runs and imports unchanged.

Guarded two ways: a static check in test_dockerfile_supply_chain.py, and a
content assertion in the CI dogfood job that runs against the real built
image and fails if /app/backend/tests or /app/backend/scripts is present
(or if the runtime app/alembic trees go missing).

See docs/ARCHIVE.md § 14 (Deviations) for this fix.
@tyler-rich
tyler-rich force-pushed the claude/session-9d5ra6 branch from 2199c38 to 74cf78a Compare July 20, 2026 06:30
@tyler-rich
tyler-rich merged commit 8eb5ca3 into dev Jul 20, 2026
4 checks passed
@tyler-rich
tyler-rich deleted the claude/session-9d5ra6 branch July 31, 2026 04:59
tyler-rich added a commit that referenced this pull request Jul 31, 2026
The [0.2.0] section carried only what had been written into [Unreleased] since
roughly 2026-07-24. Everything promoted in #70 (2026-07-13, i.e. #53-#67) and
the #77-#88 batch that followed had never been changelogged at all — v0.1.0 was
tagged 2026-07-09 and #70 landed four days later — so about two dozen PRs of
security and correctness work that ships in 0.2.0 was absent.

Backfilled from the #70 commit range and the §14 entries for that batch, merged
into the existing Added/Fixed/Changed/Security sections rather than added as a
separate block: 0.2.0 is one release, and a changelog-within-a-changelog would
make a reader track which half applies to them.

Three of the release's upgrade-affecting items live here and were invisible
before: the SSRF egress guard (SCRYE_ALLOW_INTERNAL_EGRESS, default off), the
remote-clone-URL requirement for repository targets, and the master-key entropy
floor — which refuses to start a v0.1.0 deployment whose key file holds a raw
passphrase, and whose remedy is the boot-and-rotate escape hatch plus a
backup/restore cycle, not a fresh key.

Also records three contract-visible API changes narrower than the envelope:
timestamps serialize with an explicit Z, /api/audit renamed entries -> items,
and scan list rows dropped options/error in favour of has_error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant