- Spec for fixing 13 Critical + High code audit issues - requirements.md (14 requirements, EARS format) - design.md (component fixes + 8 correctness properties) - tasks.md (8 tasks, parallel execution waves) - CloudFormation test deploy template planned - code-audit.md steering file (all 40 issues documented) Co-authored-by: Samuel James <ssamjame@amazon.com> Co-authored-by: Kiro <noreply@kiro.dev>
7.9 KiB
Implementation Plan: Code Audit Fixes
Overview
Surgical fixes for 13 Critical + High audit issues across the ArchNest backend (Fastify 5 + TypeScript) and frontend (React 19 + TypeScript), plus a CloudFormation test deploy template. Each task targets a specific file with a minimal, correct patch. No new dependencies introduced.
Tasks
-
1. Backend security hardening — input validation and auth
-
1.1 Add path traversal prevention to
backend/src/routes/files.ts- Add
validatePath()function usingposix.normalize()to reject absolute paths and..traversal - Apply validation at the top of every handler that accepts a user-supplied path (list, content read, content write, mkdir, rename, delete, chmod, download, upload)
- Return HTTP 400 with descriptive error on rejection
- Requirements: 6.1, 6.2, 6.3, 6.4
- Add
-
1.2 Fix agent token timing-safe comparison in
backend/src/routes/agents.ts- Replace early-return on length mismatch with padded constant-time comparison
- Pad both buffers to
Math.max(a.length, b.length)beforetimingSafeEqual - Ensure
ok: trueonly when both length AND content match - Same 401 response for all rejection cases
- Requirements: 7.1, 7.2, 7.3
-
1.3 Add session log integrationId validation in
backend/src/routes/terminal.ts- Update
sessionLogPath()to returnnullwhen integrationId is not a positive integer - Callers skip logging on
nullreturn - Requirements: 13.1, 13.2
- Update
-
1.4 Add body size limit to data import in
backend/src/routes/data.ts- Add
bodyLimit: 10 * 1024 * 1024to the POST/api/data/importroute registration - Fastify returns 413 automatically when exceeded
- Requirements: 8.1, 8.2
- Add
-
1.5 Set CORS origin to fail-closed default in
backend/src/server.ts- Change fallback from
truetofalsewhenARCHNEST_CORS_ORIGINis not set - Log a warning at startup when env var is missing
- Requirements: 10.1, 10.2, 10.3
- Change fallback from
-
* 1.6 Write property tests for path traversal, agent token, and integrationId validation
- Property 3: Path Traversal Prevention — verify
validatePathrejects all..escape patterns and accepts valid relative paths - Property 4: Agent Token Comparison Correctness — verify
ok: trueiffpresented === expected, never throws - Property 8: Integration ID Numeric Validation — verify
sessionLogPathreturns null for non-positive-integer values - Validates: Requirements 6.1, 6.2, 7.1, 7.2, 7.3, 13.1, 13.2
- Property 3: Path Traversal Prevention — verify
-
-
2. Checkpoint — Backend security
- Ensure all tests pass, ask the user if questions arise.
-
3. WebSocket authentication refactor
-
3.1 Implement first-message auth gate in
backend/src/routes/terminal.ts- Add
authenticatedflag per connection - Require
{ type: 'auth', token }as first message; verify JWT - Reject all other message types before auth with error frame + close
- Remove
req.query.tokenusage from connect/list_tmux handlers - Requirements: 5.3, 5.4
- Add
-
3.2 Implement first-message auth gate in
backend/src/routes/docker.ts- Same pattern as terminal: auth-first protocol for Docker exec WebSocket
- Verify existing JSON parse error handling remains intact (already correct per design)
- Requirements: 5.3, 5.4, 12.1, 12.2
-
3.3 Update frontend WebSocket connections in
src/lib/TerminalSessionContext.tsx- Remove
?token=from WebSocket URL query string - Send
{ type: 'auth', token }as first message onws.onopen - Apply same change to
fetchTmuxSessions()WebSocket - Requirements: 5.1, 5.2
- Remove
-
3.4 Fix WebSocket session leak in
src/lib/TerminalSessionContext.tsx- Guard
ws.close()with readyState check (OPEN or CONNECTING) - Capture
thisWsreference; inonclose, bail ifs.ws !== thisWs - Requirements: 1.1, 1.2, 1.3
- Guard
-
* 3.5 Write property test for WebSocket auth gate
- Property 5: WebSocket Auth Gate Rejects Unauthenticated Messages
- Verify no message type other than
authis processed before authentication - Validates: Requirements 5.3, 5.4
-
-
4. Checkpoint — WebSocket auth
- Ensure all tests pass, ask the user if questions arise.
-
5. Backend — tmux validation and SSH cleanup verification
-
5.1 Harden tmux session name usage in
backend/src/routes/terminal.ts- Ensure validated name is single-quoted in shell command construction
- Defense-in-depth: strip any
'(impossible given regex, but belt+suspenders) - Requirements: 2.1, 2.2, 2.3
-
5.2 Verify container ref validation in
backend/src/ssh/docker.ts- Confirm
CONTAINER_REF_REregex is^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$ - Confirm
shQuote()is applied to all container ref interpolations - Confirm SSH cleanup in
withSshClientfinally block is present - If already correct (per design), add a code comment noting audit verification
- Requirements: 3.1, 3.2, 3.3, 9.1, 9.2
- Confirm
-
* 5.3 Write property tests for tmux name validation and container ref validation
- Property 1: tmux Session Name Validation Prevents Injection — verify only
[A-Za-z0-9_-]{1,64}passes - Property 2: Container Reference Validation and Safe Escaping — verify regex and
shQuotesafety - Property 6: SSH Connection Cleanup Guarantee — verify
withSshClientalways calls.end() - Validates: Requirements 2.1, 2.3, 3.1, 3.3, 9.1, 9.2
- Property 1: tmux Session Name Validation Prevents Injection — verify only
-
-
6. Frontend stability fixes
-
6.1 Add
.catch()to Sidebar promise insrc/components/Sidebar.tsx- Append
.catch(() => {})to theapi.listIntegrations()call - Ensure integrations state remains null on failure (shows "Checking…")
- Requirements: 4.1, 4.2
- Append
-
6.2 Create Error Boundary component at
src/components/ErrorBoundary.tsx- React class component with
getDerivedStateFromError+componentDidCatch - Fallback UI: centered message + gold "Reload Page" button on dark background
- Log error to console
- Requirements: 11.2, 11.3, 11.4
- React class component with
-
6.3 Wrap Dashboard in ErrorBoundary in
src/App.tsx- Import and wrap the top-level Dashboard component tree
- Requirements: 11.1
-
* 6.4 Write unit tests for ErrorBoundary and Sidebar error handling
- Verify ErrorBoundary renders fallback on child throw
- Verify Sidebar swallows rejected promise without crashing
- Validates: Requirements 4.1, 4.2, 11.1, 11.2, 11.3, 11.4
-
-
7. CloudFormation test deploy template
-
7.1 Create
infra/test-deploy.ymlCloudFormation template- Parameters: KeyPairName (KeyPair type), NotificationEmail (String)
- Resources: SecurityGroup (SSH + HTTP/HTTPS), EC2 Instance (t4g.small, AL2023 ARM64, Docker + Compose via UserData), Budget alarm ($30/month, 80% threshold)
- Outputs: PublicIP, InstanceId
- Requirements: 14.1, 14.2, 14.3, 14.4, 14.5, 14.6
-
* 7.2 Write smoke test validating CloudFormation template structure
- Verify required resource types, parameters, and outputs exist
- Validate YAML parses correctly
- Validates: Requirements 14.1–14.6
-
-
8. Final checkpoint
- Ensure all tests pass, ask the user if questions arise.
Notes
- Tasks marked with
*are optional and can be skipped for faster MVP - Each task references specific requirements for traceability
- Components 3 (container ref), 9 (SSH cleanup), and 12 (JSON parse) are verified-already-correct per design — task 5.2 confirms with a code comment
- The project uses TypeScript throughout (Fastify 5 backend, React 19 frontend)
- No new dependencies are introduced; all fixes use Node.js built-ins and existing patterns
Task Dependency Graph
{
"waves": [
{ "id": 0, "tasks": ["1.1", "1.2", "1.3", "1.4", "1.5", "6.1", "6.2", "7.1"] },
{ "id": 1, "tasks": ["1.6", "6.3", "6.4", "7.2"] },
{ "id": 2, "tasks": ["3.1", "3.2", "5.1", "5.2"] },
{ "id": 3, "tasks": ["3.3", "3.4", "3.5", "5.3"] }
]
}