|
| 1 | +# Repository Audit Report |
| 2 | + |
| 3 | +**Date:** 2026-03-19 |
| 4 | +**Branch:** sani-review |
| 5 | +**Scope:** Bugs, security issues, architecture problems, Docker/DevOps issues, CI/CD risks (Jenkins) |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## CRITICAL |
| 10 | + |
| 11 | +### C1 — Missing `path` import in `result/server.js` (Runtime Crash) |
| 12 | +**File:** `result/server.js:71` |
| 13 | +`res.sendFile(path.resolve(__dirname + "/views/index.html"))` — `path` is used but never imported with `require('path')`. Every GET `/` will throw a `ReferenceError`, making the result service completely broken in production. |
| 14 | + |
| 15 | +### C2 — Hardcoded Database Credentials Everywhere |
| 16 | +**Files:** |
| 17 | +- `docker-compose.yml:67-68` — `POSTGRES_USER/PASSWORD: "postgres"` |
| 18 | +- `worker/Program.cs:19` — `"Server=db;Username=postgres;Password=postgres;"` |
| 19 | +- `result/server.js:21` — `"postgres://postgres:postgres@db/postgres"` |
| 20 | +- `k8s-specifications/db-deployment.yaml:23-24` — plain-text in YAML |
| 21 | +- `result/docker-compose.test.yml:49-51` |
| 22 | + |
| 23 | +Credentials are in source control, visible to anyone. Should use environment variables + Kubernetes Secrets / Docker secrets. |
| 24 | + |
| 25 | +### C3 — Docker Socket Mounted into Containers |
| 26 | +**Files:** `trivy-scan.sh:15-37`, `docker-compose.trivy.yml:9` |
| 27 | +`-v /var/run/docker.sock:/var/run/docker.sock` gives the container full control of the Docker daemon. A compromised container becomes a full host compromise. |
| 28 | + |
| 29 | +### C4 — Jenkinsfile `|| true` Suppresses All Failures |
| 30 | +**File:** `Jenkinsfile` (lines ~46, 64, 73, 82, 92) |
| 31 | +Every critical step — linting, static analysis, security scanning — is suffixed with `|| true`. The pipeline will show green even when checks fail. Defeats the entire purpose of CI gates. |
| 32 | + |
| 33 | +### C5 — PostgreSQL `emptyDir` in Kubernetes (Data Loss) |
| 34 | +**Files:** `k8s-specifications/db-deployment.yaml:33`, `k8s-specifications/redis-deployment.yaml:28` |
| 35 | +```yaml |
| 36 | +volumes: |
| 37 | +- name: db-data |
| 38 | + emptyDir: {} # wiped on every pod restart |
| 39 | +``` |
| 40 | +All vote data is permanently lost on any pod restart or eviction. Needs a `PersistentVolumeClaim`. |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## MEDIUM |
| 45 | + |
| 46 | +### M1 — Flask Debug Mode in Production |
| 47 | +**File:** `vote/app.py:53` |
| 48 | +```python |
| 49 | +app.run(host='0.0.0.0', port=80, debug=True, threaded=True) |
| 50 | +``` |
| 51 | +`debug=True` enables the Werkzeug interactive debugger — arbitrary code execution from the browser. Should use gunicorn (already in `requirements.txt`). |
| 52 | + |
| 53 | +### M2 — No USER Directive in Any Dockerfile |
| 54 | +**Files:** `vote/Dockerfile`, `result/Dockerfile`, `worker/Dockerfile`, `seed-data/Dockerfile` |
| 55 | +All containers run as root. A container escape grants full host access. Add `USER nobody` or create a dedicated user. |
| 56 | + |
| 57 | +### M3 — Unpinned Base Images |
| 58 | +**Files:** All Dockerfiles use tags like `python:3.11-slim`, `node:18-slim`, `mcr.microsoft.com/dotnet/sdk:7.0` — no SHA digest pinning. Builds are non-reproducible and silently pick up upstream changes. |
| 59 | + |
| 60 | +### M4 — `result/tests/Dockerfile` Uses Node 8 (EOL 2019) |
| 61 | +**File:** `result/tests/Dockerfile:1` — `node:8.9-slim`. Critical CVEs, no patches. Upgrade to Node 18+. |
| 62 | + |
| 63 | +### M5 — Test PostgreSQL Version Mismatch |
| 64 | +**File:** `result/docker-compose.test.yml:48` uses `postgres:9.4` (EOL 2021). |
| 65 | +**Production:** `docker-compose.yml` uses `postgres:15-alpine`. Tests don't reflect production behavior. |
| 66 | + |
| 67 | +### M6 — Jenkinsfile Artifacts Reference Non-Existent Paths |
| 68 | +**File:** `Jenkinsfile:134-135` |
| 69 | +```groovy |
| 70 | +'result/dist/**/*' // no dist/ directory in result service |
| 71 | +'vote/build/**/*' // no build/ directory in vote service |
| 72 | +``` |
| 73 | +Archive steps silently do nothing. No build output is preserved. |
| 74 | + |
| 75 | +### M7 — JUnit Report Format Mismatch |
| 76 | +**File:** `Jenkinsfile:137` — `junit testResults: 'result/tests/test-report.txt'` |
| 77 | +`result/tests/tests.sh` writes plain text (`echo "Tests passed" >> test-report.txt`), not JUnit XML. Jenkins will fail or silently skip the report. |
| 78 | + |
| 79 | +### M8 — Worker Has No Graceful Shutdown |
| 80 | +**File:** `worker/Program.cs:29-61` — `while(true)` loop with no `CancellationToken` or SIGTERM handler. Rolling deployments will SIGKILL the worker mid-vote, potentially losing votes in transit. |
| 81 | + |
| 82 | +### M9 — No Resource Limits Anywhere |
| 83 | +**Files:** All `docker-compose.yml` services, all `k8s-specifications/*.yaml` |
| 84 | +No memory or CPU limits. A single misbehaving container can starve the host. |
| 85 | + |
| 86 | +### M10 — No Liveness/Readiness Probes in Kubernetes |
| 87 | +**Files:** All `k8s-specifications/*-deployment.yaml` |
| 88 | +Kubernetes cannot detect unhealthy pods; broken instances stay in rotation and receive traffic. |
| 89 | + |
| 90 | +### M11 — Nginx Has No HTTPS, No Security Headers, No Rate Limiting |
| 91 | +**File:** `nginx/nginx.conf` |
| 92 | +- HTTP only (port 80), all traffic in plaintext |
| 93 | +- Missing: `X-Frame-Options`, `X-Content-Type-Options`, `Content-Security-Policy`, HSTS |
| 94 | +- No `limit_req` — open to abuse/DDoS |
| 95 | + |
| 96 | +### M12 — Duplicate Build Stages in Jenkinsfile |
| 97 | +**File:** `Jenkinsfile` — images are built once serially (lines ~30-38) then rebuilt again in a "parallel" stage (lines ~94-115). Wasted CI time and ambiguity about which artifact is tested. |
| 98 | + |
| 99 | +### M13 — No `.dockerignore` Files |
| 100 | +No `.dockerignore` found in any service directory. `.git`, `node_modules`, test files, and local configs are copied into images — larger images and potential secret leakage. |
| 101 | + |
| 102 | +### M14 — Dependabot Only Covers GitHub Actions |
| 103 | +**File:** `.github/dependabot.yml` — only `github-actions` ecosystem monitored. `npm`, `pip`, and `nuget` packages are unmonitored for CVEs. |
| 104 | + |
| 105 | +--- |
| 106 | + |
| 107 | +## LOW |
| 108 | + |
| 109 | +### L1 — Hardcoded Service Hostnames |
| 110 | +`result/server.js:21` and `worker/Program.cs:19` hardcode the hostname `db`. These are not configurable via environment variables, making multi-environment deployments fragile. |
| 111 | + |
| 112 | +### L2 — `nodemon` in Production Result Image |
| 113 | +**File:** `result/Dockerfile:11` — `nodemon` installed globally in the production image. Development tool adds unnecessary attack surface. |
| 114 | + |
| 115 | +### L3 — Git Short SHA as Image Tag |
| 116 | +**File:** `Jenkinsfile:22-26` — 7-character short SHAs have collision risk in large repos and are not immutable identifiers for production traceability. |
| 117 | + |
| 118 | +### L4 — Single Replica for All Kubernetes Deployments |
| 119 | +All `k8s-specifications/*-deployment.yaml` set `replicas: 1`. Any pod failure causes downtime. Vote and Result services should be at ≥2. |
| 120 | + |
| 121 | +### L5 — No Kubernetes Namespace |
| 122 | +All K8s specs deploy to `default` namespace — no isolation, no RBAC scoping possible. |
| 123 | + |
| 124 | +### L6 — `chmod +x` on Every Pipeline Run |
| 125 | +**File:** `Jenkinsfile` — scripts have `chmod +x` applied each run rather than committing the executable bit with `git update-index --chmod=+x`. |
| 126 | + |
| 127 | +### L7 — Redis and PostgreSQL Are Single Points of Failure |
| 128 | +Both have no clustering, no replication, no backup strategy. Disk failure = data loss. |
| 129 | + |
| 130 | +### L8 — No Observability Stack |
| 131 | +No log aggregation, no metrics (Prometheus), no alerting, no distributed tracing. Failures will go undetected until users report them. |
| 132 | + |
| 133 | +--- |
| 134 | + |
| 135 | +## Summary |
| 136 | + |
| 137 | +| Severity | Count | |
| 138 | +|----------|-------| |
| 139 | +| Critical | 5 | |
| 140 | +| Medium | 14 | |
| 141 | +| Low | 8 | |
| 142 | + |
| 143 | +**Top priority fixes in order:** |
| 144 | +1. Add `const path = require('path')` to `result/server.js` — service is currently broken |
| 145 | +2. Remove hardcoded DB credentials, inject via env vars |
| 146 | +3. Replace `emptyDir` with `PersistentVolumeClaim` for Postgres in K8s |
| 147 | +4. Remove `|| true` from all Jenkins pipeline steps |
| 148 | +5. Remove Docker socket mount from Trivy container |
| 149 | +6. Set `debug=False` in `vote/app.py` and switch to gunicorn |
0 commit comments