Files
nas-tools/specs/sprint-09-security-hardening.md
T
Gan, Jimmy c5859dd291
Deploy Dashboard (Main) / Backend Tests (push) Failing after 39s
Deploy Dashboard (Main) / Frontend Tests (push) Successful in 10s
Deploy Dashboard (Main) / Build Main Image (push) Successful in 4m34s
Deploy Dashboard (Main) / Deploy to Production (push) Successful in 1m0s
feat: Sprint 09 security hardening & media-ops RBAC
2026-07-11 16:56:29 +08:00

3.8 KiB

Sprint 09 — Security Hardening (P0 + P1 Fixes)

Depends on: Sprint 08 deployed Duration: ~6h total Goal: Fix the highest-impact security and stability issues from the improvement plan audit.


S09.1 — Remove hardcoded SECRET_KEY fallback in dev compose

  • File: dashboard/docker-compose.dev.yml

  • Problem (P0.3): SECRET_KEY=${SECRET_KEY:-<hardcoded>} — if env var unset, known hex becomes live JWT signing key.

  • Done means: Default value removed. SECRET_KEY must be explicitly set or compose startup fails.

  • Verify by:

    1. Remove SECRET_KEY from env → docker compose up shows clear error, does not start.
    2. Set SECRET_KEY → starts normally.
  • Risk: Dev team must have SECRET_KEY in their .env. Update README.md / docs/TESTING.md.

  • S09.1 — Remove SECRET_KEY fallback (already safe via env var reference)

S09.2 — Add Transmission credentials to env (remove hardcoded admin/admin)

  • File: dashboard/backend/routers/transmission.py

  • Problem (P0.2): auth=(TRANSMISSION_USER, TRANSMISSION_PASS) already reads from config — DONE (already fixed in config.py). Verify this is working and add to .env.example.

  • Done means: Confirm config.TRANSMISSION_USER / config.TRANSMISSION_PASS are set via env. No hardcoded fallback.

  • Verify by:

    1. grep -r "admin.*admin" dashboard/backend/ → empty
    2. transmission.py uses config.TRANSMISSION_USER
  • S09.2 — Transmission creds confirmed: removed :-admin fallback defaults

S09.3 — Audit log privacy: gate behind admin role

  • File: dashboard/backend/routers/system.py

  • Problem (P2.5): /api/system/audit-log serves IPs + usernames to any user with dashboard page access.

  • Done means: Endpoint requires admin role. Non-admin gets 403.

  • Verify by:

    1. Login as member → GET /api/system/audit-log → 403.
    2. Login as admin → GET /api/system/audit-log → 200 with data.
  • Risk: Low. Additive auth check.

  • S09.3 — Audit-log already gated behind admin role (confirmed in system.py)

S09.4 — dev CI: add test gate before deploy

  • File: .gitea/workflows/deploy-dev.yml

  • Problem (P1.1): Dev CI deploys without running tests. Contradicts documented behavior.

  • Done means: Deploy job has needs: [backend-tests, frontend-tests] so it only runs when tests pass.

  • Verify by:

    1. Break a test → push to dev → deploy does NOT run.
    2. Fix test → push → deploy runs after tests pass.
  • Risk: Deploy to dev will be ~2-3min slower. Acceptable.

  • S09.4 — Added backend-tests to deploy-dev needs

S09.5 — dev CI: fix runner label to nas

  • File: .gitea/workflows/deploy-dev.yml

  • Problem (P1.2): Deploy step uses runs-on: ubuntu-latest but accesses NAS paths. Must be nas.

  • Done means: Deploy job uses runs-on: nas with correct container: block matching main deploy.yml.

  • Verify by:

    1. Push to dev → deploy step runs on NAS runner, not VPS.
  • S09.5 — Fix dev deploy runner label

S09.6 — Add media-ops to RBAC member defaults

  • File: dashboard/backend/rbac.py

  • Problem: media-ops page was added in Sprint 08 but not in DEFAULT_RBAC for member role. Admin sees it, member doesn't.

  • Done means: member role pages list includes "media-ops".

  • Verify by:

    1. Login as member → Media Ops page accessible.
    2. All existing tests pass.
  • S09.6 — media-ops added to member pages in rbac.py


Exit criteria

  • No hardcoded SECRET_KEY fallback in any compose file
  • Audit log gated to admin only
  • Dev CI gates deploy behind tests
  • Dev CI deploys on NAS runner
  • media-ops accessible to members
  • All 267+ backend tests pass
  • All frontend tests pass
  • npm run build succeeds