7.4 KiB
7.4 KiB
Copper Tone Technologies – Security/Quality Audit
Date: 2025-11-20
Auditor: Codex (manual review per docs/AUDITOR.md guidance)
Scope: Backend Go services, database migrations, frontend auth/XSS posture, infra (Containerfiles, podman-compose), CI workflows.
Method: Static code/config review only. Automated scans (npm audit, govulncheck, trivy, lint/tests) not run because this session uses a restricted, offline sandbox.
Executive Summary
- Overall risk: High. Users can self-assign
ADMINat registration, downstream services do not enforce ownership/role checks, and the Stripe webhook has no signature verification. These allow total compromise of data and payments. - Transport security gaps: All services hardcode
sslmode=disableto Postgres; no way to require TLS in production. - Auth inconsistencies: JWT claims differ between issuer and consumers (
userIdvsuser_id), enabling logic bugs while RBAC already trusts client-chosen roles. - Frontend XSS exposure: Markdown is rendered and injected via
v-htmlwith no sanitization. - DevOps hygiene: Compose ships default secrets; containers run as root (scratch images) without CA certs; no backend
.env.example.
Critical Findings
- Privilege escalation at signup –
handleRegisterEmailPasswordandhandleRegisterBlockchainaccept a user-suppliedroleand insert it directly (backend/functions/auth-service/main.go:168-263). Any new user can register asADMIN, then call other services with administrative privileges. - No authorization / data-owner checks – Work Management and Payment services allow any authenticated token to list/read/update/delete all projects, tasks, work orders, invoices, and payments; no scoping to the caller or role gating beyond minimal wrappers (
backend/functions/work-management-service/main.go:69-360,payment-service/main.go:97-454). Combined with self-assigned admin, this is full data disclosure/modification risk. - Stripe webhook unauthenticated –
/webhooks/stripehas TODOs for signature verification and status updates but currently accepts any POST and returns 200 (backend/functions/payment-service/main.go:682-694). An attacker can spoof payment events. - Database TLS disabled – All services build
sslmode=disableinto the connection string with no override (auth-service/main.go:126-147,work-management-service/main.go:91-114,payment-service/main.go:121-144). Production connections would be unencrypted and unauthenticated.
High Findings
- JWT claim mismatch / brittle RBAC – Auth service issues tokens with
userIdwhile other services expectuser_id(auth-service/main.go:590-612vswork-management-service/main.go:172-178,payment-service/main.go:201-207). Context user IDs becomenil, and RBAC trusts whatever roles are in the token (already user-controlled). Inconsistent claims increase the chance of bypasses/bugs. - Replayable blockchain login –
verifyEthereumSignaturetrusts arbitrarymessageprovided by the client and stores no nonce/timestamp (auth-service/main.go:241-320). A captured signature can be replayed indefinitely to log in. - Over-permissive CORS – All services send
Access-Control-Allow-Origin: *with broad methods/headers (auth-service/main.go:151-164,work-management-service/main.go:116-130,payment-service/main.go:146-159). In production this enables credential theft if tokens are ever stored/sent in browsers across origins. - Default secrets in compose –
podman-compose.ymlshipsyour_super_secret_jwt_keyandDB_PASSWORD=passwordbaked into service env, suitable only for dev but likely to be deployed as-is; no backend.env.exampleenumerating required secrets.
Medium Findings
- Payment/auth amounts handled as floats – API structs use
float64for money (payment-service/main.go:21-68); conversions to cents rely on floating arithmetic (main.go:641-665). Risk of rounding errors; prefer fixed-point integers. - Task/status updates trust clients fully – No server-side validation of allowed status transitions or ownership (e.g., anyone can mark tasks completed or change invoice status).
- Container hardening – Final images run as root (
FROM scratch) with noUSERdirective and no CA certificates; TLS outbound would fail, and root runtime expands attack surface. - CI image builds may fail – Workflows call
podman buildon GitHub-hosted runners without ensuring podman availability/privileged mode; not a security bug but a reliability gap.
Low Findings
- Frontend XSS vector via Markdown –
ServiceDetailView.vueandArticleDetailView.vuerender Markdown to HTML and inject viav-htmlwithout sanitization (frontend/src/views/ServiceDetailView.vue:8-18,ArticleDetailView.vue:8-22). Safe only if content is fully trusted and static; otherwise add sanitization. - JWT secret length not enforced – Auth service only checks non-empty (
auth-service/main.go:93-99); mandate 32–64+ random bytes. - No rate limiting / brute-force protection – Login endpoints have no throttling or lockouts.
Database/Migrations Review
- Migrations are paired and reversible (001–003 up/down present). Types and constraints look sane (ENUMs for status/roles, FK indexes present). No plaintext secrets stored.
- Missing: encryption requirements for sensitive columns (if any added later), and no migrations enforce audited timestamp updates for all tables beyond current set.
Environment & Compliance
- Missing backend
.env.example; secrets and required vars are not documented. .gitignoreproperly excludes.env*. No evidence of secrets committed.- No HTTPS/TLS enforcement discussed for services; production should terminate TLS and set HSTS.
- Accessibility, GDPR, PCI: not evaluated in depth; Stripe used for payments (good), but webhook is insecure.
Recommendations / Next Steps
- Fix auth/RBAC: Remove client-controlled roles from registration; default to least-privileged and add an admin-only path to elevate. Align JWT claims (
userIdvsuser_id) and validate roles server-side per endpoint. Add ownership checks for projects/tasks/invoices/payments. - Secure transport & secrets: Make
sslmodeconfigurable withrequire/verify-fullfor production; document vars in a backend.env.example; replace compose defaults with${VAR}placeholders. - Stripe webhook: Verify signatures (
Stripe-Signature), parse events, and update payment status safely. - CORS & HTTP hardening: Restrict origins/methods/headers in prod; add request timeouts and rate limiting on auth endpoints.
- Blockchain login: Require server-issued nonce/timestamp, bind signatures to that nonce, and expire after single use.
- Frontend sanitization: Sanitize rendered Markdown (e.g., DOMPurify) or ensure content is immutable/trusted.
- Container/CI: Add non-root
USER, CA certificates, and health probes; ensure CI runners support podman or switch to docker/buildx. - Monetary precision: Store/accept currency values as integers of smallest unit; validate currency codes and ranges.
- Automated scans (run outside this sandbox):
npm audit/npm audit --production,go mod verify,go vet,govulncheck,golangci-lint,trivy image, and full test suites.
Testing Performed
- Not run (sandboxed, offline). All findings from static analysis of repository files only.