fix(security): sovereign-audit hardening pass — RCE, multi-tenant, reliability Reasoning-based audit fixes (all verified by typecheck, attack paths re-traced): - build-time RCE: validate spec.dependencies to npm-registry semver only (no git/url/file specifiers) + --ignore-scripts in runner Dockerfile. - container hardening fail-CLOSED: harden unless RUNNER_DISABLE_HARDENING=1, no longer gated on a fragile NODE_ENV string compare. - secret env keys validated (UPPER_SNAKE, reject NODE_*/PATH/LD_*). - cross-org image-tag collision: qualify tag with serverId. - /iterate now enforces suspension + daily-build limits like /servers. - preview SSE: clear keepalive in finally + on client close (timer/FD leak). - SMS OTP: atomic attempt counter (lt(attempts,MAX) in UPDATE) — brute-force race. - getSession orders membership by createdAt (deterministic primary org). - template scopes aggregated from real tool scopes (was hardcoded mcp:read). - template category filter pushed into WHERE (was applied after LIMIT). - support admin reply/status: 404 on unknown ticket; status change now audited. - build worker: queue defaultJobOptions, docker build/run/stop timeouts, old-container teardown in finally (no orphan on post-deploy DB failure). - nginx: HSTS, X-Frame-Options DENY, nosniff, Referrer-Policy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
22 lines
1001 B
Docker
22 lines
1001 B
Docker
FROM node:20-alpine AS deps
|
|
WORKDIR /app
|
|
COPY package.json ./
|
|
# --ignore-scripts: generated package.json carries LLM/user-chosen dependencies.
|
|
# Without this, a malicious dependency's postinstall lifecycle script would run
|
|
# at `docker build` time on the shared host. Specifiers are also validated to
|
|
# registry semver ranges at the API boundary (DependencyMap). (GEN-001)
|
|
RUN npm install --omit=dev --ignore-scripts --no-audit --no-fund && npm install --no-save --ignore-scripts tsx@4.19.2 typescript@5.7.2
|
|
|
|
FROM node:20-alpine AS runtime
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY package.json tsconfig.json ./
|
|
COPY src ./src
|
|
EXPOSE 3000
|
|
# 127.0.0.1, not localhost: busybox wget resolves localhost to ::1 first and
|
|
# the server binds IPv4 only, so a localhost check would wrongly fail.
|
|
HEALTHCHECK --interval=15s --timeout=3s --start-period=10s --retries=3 \
|
|
CMD wget -qO- http://127.0.0.1:3000/health || exit 1
|
|
CMD ["npx", "tsx", "src/server.ts"]
|