Files
mgeeky-decode-spam-headers/Auto Run Docs/SpecKit-web-header-analyzer-Phase-08-Security-Operations.md
2026-02-18 04:27:35 +01:00

4.5 KiB

Phase 08: US6 — Security & Operational Readiness

This phase protects the analysis service from abuse with per-IP rate limiting and CAPTCHA challenge, exposes a health-check endpoint for monitoring, and wires all routers and middleware into the FastAPI application. The frontend displays a CAPTCHA modal when rate-limited. TDD Red-Green approach throughout.

Spec Kit Context

  • Feature: 1-web-header-analyzer
  • Specification: .specify/specs/1-web-header-analyzer/spec.md (NFR-11, NFR-12, NFR-15, NFR-16)
  • Plan: .specify/specs/1-web-header-analyzer/plan.md
  • Tasks: .specify/specs/1-web-header-analyzer/tasks.md
  • API Contract: .specify/specs/1-web-header-analyzer/contracts/api.yaml (GET /api/health, POST /api/captcha/verify)
  • User Story: US6 — Security & Operational Readiness
  • Constitution: .specify/memory/constitution.md (TDD: P6)

Dependencies

  • Requires Phase 05 completed (analysis endpoint for rate limiting to protect)
  • Requires Phase 01 completed (config.py for rate limit thresholds)

Rate Limiting Design

  • Mechanism: Per-IP sliding window counter (async-safe, in-memory)
  • Threshold: Configurable via config.py environment variables
  • Response: HTTP 429 with Retry-After header and CAPTCHA challenge token
  • CAPTCHA: Server-generated visual noise (randomly distorted text rendered as image)
  • Bypass: HMAC-signed token (5-minute expiry) returned on successful CAPTCHA solve
  • Trade-off: Per-instance counters — acceptable for initial release; shared Redis store upgradeable later

Tasks

  • T040 [US6] Write failing tests (TDD Red) in backend/tests/api/test_rate_limiter.py (rate limiting triggers at threshold, 429 response with CAPTCHA challenge), backend/tests/api/test_captcha.py (challenge generation, verification, bypass token), backend/tests/api/test_health.py (health endpoint returns correct status), and frontend/src/__tests__/CaptchaChallenge.test.tsx (render modal, display CAPTCHA image, submit answer, handle success/failure states, keyboard accessibility)
  • T041 [US6] Create backend/app/middleware/rate_limiter.py — per-IP sliding window rate limiter (async-safe in-memory). Configurable limit via config.py. Returns 429 with Retry-After header and CAPTCHA challenge token (NFR-11, NFR-12). Note: per-instance counters — acceptable for initial release; shared store upgradeable later. Verify test_rate_limiter.py passes (TDD Green)
  • T042 [US6] Create backend/app/routers/captcha.pyPOST /api/captcha/verify endpoint. Server-generated visual noise CAPTCHA (randomly distorted text). Returns HMAC-signed bypass token (5-minute expiry) on success. Token exempts IP from rate limiting. Response schema in backend/app/schemas/captcha.py. Verify test_captcha.py passes (TDD Green)
  • T043 [P] [US6] Create frontend/src/components/CaptchaChallenge.tsx — modal on 429 response. Displays CAPTCHA image, on verification stores bypass token and retries original request. FontAwesome lock/unlock icons. Keyboard accessible (NFR-02). Verify CaptchaChallenge.test.tsx passes (TDD Green)
  • T044 [US6] Create backend/app/schemas/health.py and backend/app/routers/health.pyGET /api/health returning status (up/degraded/down), version, uptime, scanner count (NFR-15). Verify test_health.py passes (TDD Green)
  • T045 [US6] Register all routers and middleware in backend/app/main.py — CORS middleware (frontend origin), rate limiter, routers (analysis, tests, health, captcha). Verify stateless operation (NFR-16). Note: rate limiter per-instance state is accepted trade-off (see T041)

Completion

  • pytest backend/tests/api/test_rate_limiter.py backend/tests/api/test_captcha.py backend/tests/api/test_health.py all pass
  • All vitest tests pass: npx vitest run src/__tests__/CaptchaChallenge.test.tsx
  • Exceeding rate limit returns HTTP 429 with Retry-After header and CAPTCHA challenge
  • Solving CAPTCHA returns HMAC-signed bypass token (5-minute expiry)
  • Bypass token exempts IP from rate limiting on subsequent requests
  • GET /api/health returns {status, version, uptime, scannerCount}
  • All routers and CORS middleware are registered in main.py
  • Application starts statelessly — no database, no session management. Verified backend/app/main.py registers only CORS + rate limiter middleware and does not initialize any DB/session services.
  • CAPTCHA modal is keyboard accessible (Tab, Enter, Escape to close)
  • Linting passes on both sides
  • Run /speckit.analyze to verify consistency