- scripts/verify-pentest-fixes.sh: 26-check automated verification that tests all 21 pentest findings against the live server - loop/plan.md: add permanent post-fix verification section - scripts/overnight-loop.sh: accept plan file arg, run verification after all fixes complete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8.4 KiB
8.4 KiB
I now have complete visibility into all affected code. Here is the remediation plan:
Security Fixes — http://192.168.1.228
- FIX-001 — fix(AUTH-001): add server-side session management to
core/archipelago/src/api/rpc/auth.rshandle_auth_login— on successful password verification, generate a cryptographic random token, hash with SHA-256, store in a server-side session map (Arc<RwLock<HashMap<HashedToken, Session>>>), and return it viaSet-Cookie: session=<token>; HttpOnly; SameSite=Strict; Path=/ - FIX-002 — fix(AUTH-002): add authentication middleware in
core/archipelago/src/api/handler.rshandle_requestandcore/archipelago/src/api/rpc/mod.rshandle— extract and validate session cookie before dispatching to any handler; allowlist onlyauth.login,auth.isOnboardingComplete,health, andechoas unauthenticated; reject all other requests with 401 - FIX-003 — fix(AUTH-005): update frontend auth in
neode-ui/src/api/rpc-client.tsto send credentials with requests (credentials: 'same-origin') and store auth state based on server session cookie presence, not just localStorage - FIX-004 — fix(AUTH-007): add session cookie validation to WebSocket upgrade in
core/archipelago/src/api/handler.rshandle_websocket(line 42-43) — parseCookieheader from the upgrade request, validate the session token against the session store, reject with 401 if invalid - FIX-005 — fix(SSRF-004): restrict
dockerImageincore/archipelago/src/api/rpc/package.rshandle_package_install(line 28) — replaceis_valid_docker_imageblocklist with an allowlist of trusted registries (docker.io/,ghcr.io/,localhost/) and reject all other image sources - FIX-006 — fix(INJ-002): validate
package_idincore/archipelago/src/api/rpc/package.rshandle_package_uninstall(line 564-567) andhandle_package_install(line 15-18) — addvalidate_app_id()helper that enforces^[a-z0-9][a-z0-9-]{0,63}$regex; call it before any filesystem or command usage; also apply inget_data_dirs_for_app(line 763) andget_containers_for_app - FIX-007 — fix(AUTH-003): add rate limiting to
core/archipelago/src/api/rpc/auth.rshandle_auth_login— track failed attempts per IP with a sliding window (max 5 failures per 60 seconds); return 429 withRetry-Afterheader when exceeded; useArc<Mutex<HashMap<IpAddr, Vec<Instant>>>>inRpcHandler - FIX-008 — fix(AUTH-008): validate incoming P2P messages in
core/archipelago/src/api/handler.rshandle_node_message(line 125-145) — verifyfrom_pubkeyis a valid ed25519 public key format (^[0-9a-f]{64}$), add an optional HMAC or ed25519 signature field for message authenticity, and rate-limit by source IP - FIX-009 — fix(AUTH-009): replace
CORS_ANYwildcard incore/archipelago/src/api/handler.rs(lines 15, 108, 118, 142, 154, 173) — remove theconst CORS_ANY: &str = "*"constant; setAccess-Control-Allow-Originto the node's own origin (fromConfig.host_ipor requestOriginheader validated against an allowlist); addVary: Originheader - FIX-010 — fix(AUTH-011): ensure
/proxy/lnd/*route incore/archipelago/src/api/handler.rshandle_lnd_proxy(line 67-68) is gated by the session validation middleware added in FIX-002; additionally forward the LND macaroon only from server-side config, never from client headers - FIX-011 — fix(XSS-004): add security headers to
image-recipe/configs/nginx-archipelago.confin bothserverblocks — addX-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy: camera=(), microphone=(), geolocation=(), and a baselineContent-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'(withframe-srcexceptions for app iframes) - FIX-012 — fix(XSS-007): remove the blanket
Access-Control-Allow-Origin $http_originecho pattern from nginx config if present; ensure nginx does not add its own CORS headers that override the backend's restricted CORS (from FIX-009); confirm only same-origin requests are allowed for/rpc/and/wslocations - FIX-013 — fix(SSRF-001): add
validate_onion()call at the top ofcore/archipelago/src/node_message.rscheck_peer_reachable(line 115) — currently missing, unlikesend_to_peerwhich already validates; this prevents arbitrary host/port injection via theonionparameter - FIX-014 — fix(SSRF-002): ensure
node-send-messageRPC is behind auth middleware (FIX-002); additionally, incore/archipelago/src/api/rpc/peers.rshandle_node_send_message(line 50-67), validate that theonionaddress exists in the node's known peer list (peers::load_peers) before sending — prevent SSRF to arbitrary Tor destinations - FIX-015 — fix(AUTH-006): implement functional logout in
core/archipelago/src/api/rpc/auth.rshandle_auth_logout(line 34-36) — extract session token from request cookie, remove it from the server-side session store, and return aSet-Cookieheader that expires the cookie - FIX-016 — fix(AUTH-012): ensure
/api/container/logsroute incore/archipelago/src/api/handler.rshandle_container_logs_http(line 64-66) is gated by the session validation middleware added in FIX-002; also validateapp_idquery parameter withvalidate_app_id()from FIX-006 - FIX-017 — fix(XSS-001): sanitize P2P message content in
core/archipelago/src/node_message.rsstore_received(line 40-42) — strip or escape HTML entities (<,>,&,",') frommessageandfrom_pubkeybefore storing; also ensure the Vue frontend component rendering messages uses{{ }}text interpolation (notv-html) - FIX-018 — fix(INJ-001): validate
manifest_pathincore/archipelago/src/api/rpc/container.rshandle_container_install(line 17-18) — canonicalize the path and verify it starts with theapps/directory underconfig.data_dir; reject paths containing..segments; reject absolute paths outside the allowed base - FIX-019 — fix(INJ-006): add authentication to
/aiui/api/claude/inimage-recipe/configs/nginx-archipelago.conf(lines 17-28 and 371-382) — addauth_requestdirective pointing to an internal auth-check endpoint on the backend (e.g.,/internal/auth-checkthat validates the session cookie), or restrict access to authenticated sessions via cookie check in thelocationblock - FIX-020 — fix(XSS-005): gate
echo/server.echobehind authentication incore/archipelago/src/api/rpc/mod.rs(lines 87-88) — removeechofrom the unauthenticated allowlist so it requires a valid session; alternatively, strip or limitmessageparam to alphanumeric + basic punctuation - FIX-021 — fix(INJ-007): sanitize log output in
core/archipelago/src/api/handler.rshandle_node_message(line 136) — replace newlines (\n,\r) and ANSI escape sequences infromandmsgwith safe representations before passing totracing::info!; use.replace('\n', "\\n").replace('\r', "\\r") - FIX-022 — fix: harden
image-recipe/configs/archipelago.service— changeUser=roottoUser=archipelago(dedicated non-root service account); setEnvironment="ARCHIPELAGO_DEV_MODE=false"; addNoNewPrivileges=true,ProtectSystem=strict,ReadWritePaths=/var/lib/archipelago; this reduces blast radius for all findings - VERIFY — test: re-run pentest curl probes from the exploitation report against all 21 finding endpoints to confirm: unauthenticated requests return 401, path traversal payloads are rejected, CORS headers are restrictive, security headers are present, WebSocket requires auth, and the service runs as non-root with dev mode disabled
Post-Fix Verification (ALWAYS run as final step)
After all FIX tasks are complete and deployed, run the automated verification script:
./scripts/verify-pentest-fixes.sh
This script tests every pentest finding against the live server:
- Login returns HttpOnly/SameSite session cookie
- All sensitive RPC methods return 401 without auth
- WebSocket, container logs, LND proxy require auth
- Rate limiting triggers on 6th failed login
- Path traversal, untrusted registries, spoofed pubkeys are rejected
- CORS blocks evil origins
- Nginx security headers are present
- Logout invalidates the session
If verification fails (exit code 1), DO NOT mark VERIFY as done. Fix the failing checks and redeploy first.