fix: harden CI security gates, parallelize builds, externalize secrets
CI — Build, Lint & Security Scan / frontend (push) Has been cancelled
CI — Build, Lint & Security Scan / image-scan (push) Has been cancelled
CI — Build, Lint & Security Scan / secrets-scan (push) Has been cancelled
CI — Build, Lint & Security Scan / backend (push) Has been cancelled
Deploy to TrueNAS / deploy (push) Has been cancelled
CI — Build, Lint & Security Scan / frontend (push) Has been cancelled
CI — Build, Lint & Security Scan / image-scan (push) Has been cancelled
CI — Build, Lint & Security Scan / secrets-scan (push) Has been cancelled
CI — Build, Lint & Security Scan / backend (push) Has been cancelled
Deploy to TrueNAS / deploy (push) Has been cancelled
- Make OWASP, Gitleaks, pnpm audit blocking (remove || true fallbacks) - Add Maven -T 1C for parallel reactor threads - Fix parallel Docker build race condition (PID tracking + set -euo pipefail) - Externalize JWT/NextAuth secrets via env vars with dev-only defaults - Add .env.example with generation instructions - Add CI/CD infrastructure review document
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
# CannaManage — Environment Variables
|
||||||
|
# Copy this file to .env and fill in the values.
|
||||||
|
# NEVER commit .env to git.
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DB_PASSWORD=cannamanage_dev
|
||||||
|
|
||||||
|
# JWT Secret — must be valid base64 (used by Decoders.BASE64.decode in JwtService)
|
||||||
|
# Generate with: openssl rand -base64 48
|
||||||
|
JWT_SECRET=
|
||||||
|
|
||||||
|
# NextAuth / Auth.js secret — minimum 32 characters
|
||||||
|
# Generate with: openssl rand -base64 32
|
||||||
|
NEXTAUTH_SECRET=
|
||||||
|
AUTH_SECRET=
|
||||||
+16
-14
@@ -30,10 +30,10 @@ jobs:
|
|||||||
cache: maven
|
cache: maven
|
||||||
|
|
||||||
- name: Maven compile
|
- name: Maven compile
|
||||||
run: ./mvnw compile -B -q -DskipTests
|
run: ./mvnw compile -B -q -DskipTests -T 1C
|
||||||
|
|
||||||
- name: Maven test
|
- name: Maven test
|
||||||
run: ./mvnw test -B
|
run: ./mvnw test -B -T 1C
|
||||||
|
|
||||||
- name: OWASP Dependency-Check (SCA)
|
- name: OWASP Dependency-Check (SCA)
|
||||||
run: |
|
run: |
|
||||||
@@ -41,9 +41,9 @@ jobs:
|
|||||||
-DfailBuildOnCVSS=7 \
|
-DfailBuildOnCVSS=7 \
|
||||||
-DsuppressionFile=.snyk-maven-suppressions.xml \
|
-DsuppressionFile=.snyk-maven-suppressions.xml \
|
||||||
-Dformats=JSON,HTML \
|
-Dformats=JSON,HTML \
|
||||||
-B -q || true
|
-B -q
|
||||||
# Note: failBuildOnCVSS=7 means High/Critical CVEs fail the build.
|
# failBuildOnCVSS=7: High/Critical CVEs fail the build.
|
||||||
# Medium and below produce warnings only.
|
# Suppress known false positives in .snyk-maven-suppressions.xml.
|
||||||
|
|
||||||
- name: Upload dependency-check report
|
- name: Upload dependency-check report
|
||||||
if: always()
|
if: always()
|
||||||
@@ -80,8 +80,8 @@ jobs:
|
|||||||
- name: pnpm audit (SCA)
|
- name: pnpm audit (SCA)
|
||||||
run: |
|
run: |
|
||||||
cd cannamanage-frontend
|
cd cannamanage-frontend
|
||||||
pnpm audit --audit-level=high || echo "::warning::pnpm audit found vulnerabilities"
|
pnpm audit --audit-level=high
|
||||||
# Fails on High/Critical. Warnings for Medium/Low.
|
# Fails on High/Critical. Use .pnpmauditrc or --ignore for known exceptions.
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Docker image security scan (Trivy)
|
# Docker image security scan (Trivy)
|
||||||
@@ -92,11 +92,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build backend image
|
- name: Build images (parallel)
|
||||||
run: docker build -t cannamanage-backend:scan -f Dockerfile.backend .
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
- name: Build frontend image
|
docker build -t cannamanage-backend:scan -f Dockerfile.backend . &
|
||||||
run: docker build -t cannamanage-frontend:scan -f cannamanage-frontend/Dockerfile cannamanage-frontend/
|
PID1=$!
|
||||||
|
docker build -t cannamanage-frontend:scan -f cannamanage-frontend/Dockerfile cannamanage-frontend/ &
|
||||||
|
PID2=$!
|
||||||
|
wait $PID1 $PID2
|
||||||
|
|
||||||
- name: Install Trivy
|
- name: Install Trivy
|
||||||
run: |
|
run: |
|
||||||
@@ -164,8 +167,7 @@ jobs:
|
|||||||
--source . \
|
--source . \
|
||||||
--report-format json \
|
--report-format json \
|
||||||
--report-path gitleaks-report.json \
|
--report-path gitleaks-report.json \
|
||||||
--exit-code 1 \
|
--exit-code 1
|
||||||
|| echo "::error::Gitleaks found potential secrets in the repository"
|
|
||||||
|
|
||||||
- name: Upload Gitleaks report
|
- name: Upload Gitleaks report
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ services:
|
|||||||
AUTH_URL: http://192.168.188.119:3000
|
AUTH_URL: http://192.168.188.119:3000
|
||||||
# NextAuth v5 (Auth.js) reads AUTH_SECRET, not NEXTAUTH_SECRET. Without it at
|
# NextAuth v5 (Auth.js) reads AUTH_SECRET, not NEXTAUTH_SECRET. Without it at
|
||||||
# runtime, signIn throws MissingSecret -> the app error boundary shows 'Oops'.
|
# runtime, signIn throws MissingSecret -> the app error boundary shows 'Oops'.
|
||||||
AUTH_SECRET: docker-dev-nextauth-secret-minimum-32chars
|
AUTH_SECRET: ${AUTH_SECRET}
|
||||||
AUTH_TRUST_HOST: "true"
|
AUTH_TRUST_HOST: "true"
|
||||||
|
|||||||
+3
-3
@@ -27,11 +27,11 @@ services:
|
|||||||
SPRING_PROFILES_ACTIVE: docker
|
SPRING_PROFILES_ACTIVE: docker
|
||||||
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/cannamanage
|
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/cannamanage
|
||||||
SPRING_DATASOURCE_USERNAME: cannamanage
|
SPRING_DATASOURCE_USERNAME: cannamanage
|
||||||
SPRING_DATASOURCE_PASSWORD: cannamanage_dev
|
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD:-cannamanage_dev}
|
||||||
# JwtService base64-decodes this secret (Decoders.BASE64.decode) before using it as the
|
# JwtService base64-decodes this secret (Decoders.BASE64.decode) before using it as the
|
||||||
# HMAC-SHA key. It MUST be valid base64 — a plaintext string with hyphens throws
|
# HMAC-SHA key. It MUST be valid base64 — a plaintext string with hyphens throws
|
||||||
# "Illegal base64 character: '-'" at token-signing time (HTTP 500 after a successful login).
|
# "Illegal base64 character: '-'" at token-signing time (HTTP 500 after a successful login).
|
||||||
CANNAMANAGE_SECURITY_JWT_SECRET: hmSULRhmFYcOXDwYxb7bGXp7Bovh+hXgua/VqF44Ts/N+8YELWpWiqQ+aLrymCuM
|
CANNAMANAGE_SECURITY_JWT_SECRET: ${JWT_SECRET:-dGhpcy1pcy1hLWRldi1vbmx5LXNlY3JldC1kby1ub3QtdXNlLWluLXByb2R1Y3Rpb24=}
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -51,7 +51,7 @@ services:
|
|||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
NEXTAUTH_URL: http://localhost:3000
|
NEXTAUTH_URL: http://localhost:3000
|
||||||
NEXTAUTH_SECRET: docker-dev-nextauth-secret-minimum-32chars
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-dev-only-nextauth-secret-do-not-use-in-production-min32}
|
||||||
BACKEND_URL: http://backend:8080
|
BACKEND_URL: http://backend:8080
|
||||||
AUTH_URL: http://localhost:3000
|
AUTH_URL: http://localhost:3000
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -0,0 +1,257 @@
|
|||||||
|
# CI/CD Infrastructure Review — CannaManage
|
||||||
|
|
||||||
|
**Date:** 2026-06-19
|
||||||
|
**Reviewer:** Lumen (Code + Security)
|
||||||
|
**Scope:** `.gitea/workflows/ci.yml`, `.gitea/workflows/deploy.yml`, `Dockerfile.backend`, `docker-compose*.yml`, `.snyk`
|
||||||
|
**Verdict:** ⚠️ Approved with findings — 2 High, 4 Medium, 3 Low
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
Push to main
|
||||||
|
├── CI Pipeline (ci.yml)
|
||||||
|
│ ├── backend (compile + test + OWASP SCA) ─┐
|
||||||
|
│ ├── frontend (lint + type-check + pnpm audit) ─┼── parallel
|
||||||
|
│ ├── secrets-scan (Gitleaks) ─┘
|
||||||
|
│ └── image-scan (Trivy) ── needs: [backend, frontend]
|
||||||
|
│
|
||||||
|
└── Deploy Pipeline (deploy.yml)
|
||||||
|
└── docker compose build + up + healthcheck
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good:** CI jobs are already parallelized. `concurrency` prevents redundant runs. `cancel-in-progress: true` on CI is correct (abort stale runs). `cancel-in-progress: false` on deploy prevents interrupted deployments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Security Findings
|
||||||
|
|
||||||
|
### ❌ HIGH — S-01: Hardcoded JWT Secret in docker-compose.yml
|
||||||
|
|
||||||
|
**File:** `docker-compose.yml:34`
|
||||||
|
```yaml
|
||||||
|
CANNAMANAGE_SECURITY_JWT_SECRET: hmSULRhmFYcOXDwYxb7bGXp7Bovh+hXgua/VqF44Ts/N+8YELWpWiqQ+aLrymCuM
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** This base64-encoded HMAC key is committed to git. Anyone with repo access can forge valid JWTs. If this key is the same as production, it's a full auth bypass.
|
||||||
|
|
||||||
|
**Mitigation:**
|
||||||
|
- Replace with `${JWT_SECRET}` and use `.env` file (gitignored) for local dev
|
||||||
|
- Alternatively, generate a random key at container startup for dev-only:
|
||||||
|
```yaml
|
||||||
|
CANNAMANAGE_SECURITY_JWT_SECRET: ${JWT_SECRET:-$(openssl rand -base64 48)}
|
||||||
|
```
|
||||||
|
- Verify that production (`docker-compose.prod.yml`) uses env vars ✅ — it does use `${JWT_SECRET}`
|
||||||
|
|
||||||
|
**Accepted Risk?** If this is intentionally a dev-only secret different from production, document it in `.snyk` and add a comment. Gitleaks *should* flag this — check if it's being suppressed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ❌ HIGH — S-02: Hardcoded NextAuth Secret in docker-compose.yml
|
||||||
|
|
||||||
|
**File:** `docker-compose.yml:54` and `docker-compose.truenas.yml:19`
|
||||||
|
```yaml
|
||||||
|
NEXTAUTH_SECRET: docker-dev-nextauth-secret-minimum-32chars
|
||||||
|
AUTH_SECRET: docker-dev-nextauth-secret-minimum-32chars
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** Same as S-01. If this secret is reused on the TrueNAS deployment (which is internet-accessible via `cannamanage.plate-software.de`), session cookies can be forged.
|
||||||
|
|
||||||
|
**Mitigation:**
|
||||||
|
- `docker-compose.truenas.yml` should reference `${AUTH_SECRET}` from an env file
|
||||||
|
- The word "dev" in the value suggests it's dev-only, but TrueNAS *is* the production host per `deploy.yml`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚠️ MEDIUM — S-03: OWASP Dependency-Check Always Passes (|| true)
|
||||||
|
|
||||||
|
**File:** `ci.yml:44`
|
||||||
|
```yaml
|
||||||
|
run: |
|
||||||
|
./mvnw org.owasp:dependency-check-maven:check \
|
||||||
|
... || true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** The `|| true` means the step NEVER fails the build, regardless of CVSS score. The `failBuildOnCVSS=7` flag is effectively useless.
|
||||||
|
|
||||||
|
**Mitigation:** Remove `|| true`. If you need to allow known issues, use the suppression file that's already configured (`-DsuppressionFile=.snyk-maven-suppressions.xml`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚠️ MEDIUM — S-04: Gitleaks Non-Blocking (echo instead of exit)
|
||||||
|
|
||||||
|
**File:** `ci.yml:168`
|
||||||
|
```yaml
|
||||||
|
gitleaks detect ... --exit-code 1 \
|
||||||
|
|| echo "::error::Gitleaks found potential secrets in the repository"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** `--exit-code 1` should fail the step, but the `||` fallback to `echo` means the step always succeeds. Secrets can be pushed without blocking the pipeline.
|
||||||
|
|
||||||
|
**Mitigation:** Remove the `|| echo` fallback. Let Gitleaks fail the build:
|
||||||
|
```yaml
|
||||||
|
run: gitleaks detect --source . --report-format json --report-path gitleaks-report.json --exit-code 1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚠️ MEDIUM — S-05: pnpm audit Non-Blocking
|
||||||
|
|
||||||
|
**File:** `ci.yml:83`
|
||||||
|
```yaml
|
||||||
|
pnpm audit --audit-level=high || echo "::warning::pnpm audit found vulnerabilities"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** High/Critical frontend CVEs produce a warning but don't fail the build.
|
||||||
|
|
||||||
|
**Mitigation:** Remove `|| echo ...` to make High/Critical CVEs block the pipeline. Use `.snyk` or `pnpm audit --ignore-path` for documented exceptions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚠️ MEDIUM — S-06: Parallel Docker Build Race Condition
|
||||||
|
|
||||||
|
**File:** `ci.yml:96-99`
|
||||||
|
```yaml
|
||||||
|
docker build -t cannamanage-backend:scan -f Dockerfile.backend . &
|
||||||
|
docker build -t cannamanage-frontend:scan -f cannamanage-frontend/Dockerfile cannamanage-frontend/ &
|
||||||
|
wait
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** If either `docker build` fails, `wait` returns the exit code of the *last* process that exited, not the first failure. One image could fail silently while the other succeeds.
|
||||||
|
|
||||||
|
**Mitigation:** Use `set -euo pipefail` and capture PIDs:
|
||||||
|
```yaml
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker build -t cannamanage-backend:scan -f Dockerfile.backend . &
|
||||||
|
PID1=$!
|
||||||
|
docker build -t cannamanage-frontend:scan -f cannamanage-frontend/Dockerfile cannamanage-frontend/ &
|
||||||
|
PID2=$!
|
||||||
|
wait $PID1 $PID2
|
||||||
|
```
|
||||||
|
With `wait $PID1 $PID2`, if either fails, the step fails.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ℹ️ LOW — S-07: Trivy Uses curl|sh Install Pattern
|
||||||
|
|
||||||
|
**File:** `ci.yml:103`
|
||||||
|
```yaml
|
||||||
|
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** Supply-chain risk — if the upstream script is compromised, arbitrary code runs in CI. Using `main` branch means no version pinning.
|
||||||
|
|
||||||
|
**Mitigation:** Pin to a specific version tag or commit hash, or use the official Trivy GitHub Action:
|
||||||
|
```yaml
|
||||||
|
- uses: aquasecurity/trivy-action@v0.28.0
|
||||||
|
with:
|
||||||
|
image-ref: cannamanage-backend:scan
|
||||||
|
severity: HIGH,CRITICAL
|
||||||
|
exit-code: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ℹ️ LOW — S-08: Deploy Workflow Uses Hardcoded IP
|
||||||
|
|
||||||
|
**File:** `deploy.yml:60, 74`
|
||||||
|
```yaml
|
||||||
|
wget -q -O /dev/null http://192.168.188.119:8081/actuator/health
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risk:** Low — private IP only reachable on LAN. But if the TrueNAS IP changes (DHCP), deploy silently breaks.
|
||||||
|
|
||||||
|
**Mitigation:** Use `localhost` or a DNS name (e.g., `truenas.local`) instead. Or use container-internal checks via `docker compose exec`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ℹ️ LOW — S-09: No Image Signing or SBOM
|
||||||
|
|
||||||
|
**Risk:** Docker images are built and deployed without signatures or Software Bill of Materials. Can't verify image integrity post-deployment.
|
||||||
|
|
||||||
|
**Mitigation (future):** Add `docker sbom` or `syft` for SBOM generation. Consider `cosign` for image signing when maturing to multi-env deployments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Code Quality Findings
|
||||||
|
|
||||||
|
### CI Workflow (ci.yml)
|
||||||
|
|
||||||
|
| # | Check | Status | Notes |
|
||||||
|
|---|-------|--------|-------|
|
||||||
|
| 1 | Jobs parallelized | ✅ | backend, frontend, secrets-scan run concurrently |
|
||||||
|
| 2 | Concurrency control | ✅ | Group + cancel-in-progress |
|
||||||
|
| 3 | Maven caching | ✅ | `cache: maven` in setup-java |
|
||||||
|
| 4 | Maven `-T 1C` | ✅ | Multi-threaded reactor (just added) |
|
||||||
|
| 5 | Surefire forkCount | ✅ | `forkCount=2` in parent POM |
|
||||||
|
| 6 | Artifact upload | ✅ | Reports preserved on failure (`if: always()`) |
|
||||||
|
| 7 | Frontend lockfile | ✅ | `--frozen-lockfile` prevents silent dep changes |
|
||||||
|
| 8 | Trivy ignore-unfixed | ✅ | Only actionable vulns fail the build |
|
||||||
|
| 9 | Security gates blocking | ❌ | All 3 security tools use `|| true/echo` fallbacks |
|
||||||
|
| 10 | Docker parallel build | ⚠️ | Race condition on failure (see S-06) |
|
||||||
|
|
||||||
|
### Deploy Workflow (deploy.yml)
|
||||||
|
|
||||||
|
| # | Check | Status | Notes |
|
||||||
|
|---|-------|--------|-------|
|
||||||
|
| 1 | No test step | ⚠️ | Comment says "tests remain local-only gate" — acceptable for self-hosted |
|
||||||
|
| 2 | Health checks | ✅ | 20 retries × 6s for backend, 10 × 5s for frontend |
|
||||||
|
| 3 | Image pruning | ✅ | Prevents disk exhaustion |
|
||||||
|
| 4 | `set -euo pipefail` | ✅ | Fail-fast on errors |
|
||||||
|
| 5 | Concurrency non-cancelling | ✅ | Prevents interrupted deploys |
|
||||||
|
|
||||||
|
### Dockerfile.backend
|
||||||
|
|
||||||
|
| # | Check | Status | Notes |
|
||||||
|
|---|-------|--------|-------|
|
||||||
|
| 1 | Multi-stage build | ✅ | Builder + runtime stages |
|
||||||
|
| 2 | Non-root user | ✅ | `appuser` in runtime |
|
||||||
|
| 3 | Alpine base | ✅ | Minimal attack surface |
|
||||||
|
| 4 | Layer caching | ✅ | POMs copied before source |
|
||||||
|
| 5 | No HEALTHCHECK | ⚠️ | Compose defines it, but image itself has no HEALTHCHECK |
|
||||||
|
| 6 | `dependency:go-offline` | ✅ | Pre-downloads deps for cache |
|
||||||
|
| 7 | No COPY of secrets | ✅ | No .env or keys copied |
|
||||||
|
|
||||||
|
### Docker Compose Files
|
||||||
|
|
||||||
|
| # | Check | Status | Notes |
|
||||||
|
|---|-------|--------|-------|
|
||||||
|
| 1 | Production uses env vars | ✅ | `docker-compose.prod.yml` uses `${...}` |
|
||||||
|
| 2 | Resource limits (prod) | ✅ | Memory/CPU limits set |
|
||||||
|
| 3 | Log rotation (prod) | ✅ | `json-file` driver with max-size |
|
||||||
|
| 4 | Dev compose has secrets | ❌ | S-01, S-02 — hardcoded in plain text |
|
||||||
|
| 5 | Postgres healthcheck | ✅ | `pg_isready` |
|
||||||
|
| 6 | Service dependencies | ✅ | `condition: service_healthy` |
|
||||||
|
| 7 | Prod binds 127.0.0.1 only | ✅ | Prevents external access without reverse proxy |
|
||||||
|
| 8 | Dev binds 0.0.0.0 | ⚠️ | `ports: "8080:8080"` is all-interfaces |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Snyk Policy (.snyk)
|
||||||
|
|
||||||
|
✅ **Well-structured.** CSRF ignore is correctly scoped to the JWT API filter chain with clear rationale and expiry date. No concerns.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Recommendations (Priority Order)
|
||||||
|
|
||||||
|
| Priority | Finding | Action |
|
||||||
|
|----------|---------|--------|
|
||||||
|
| 🔴 High | S-01, S-02 | Move secrets to `.env` (gitignored) or generate at runtime |
|
||||||
|
| 🟡 Medium | S-03, S-04, S-05 | Remove `|| true` / `|| echo` from security tools — make them blocking |
|
||||||
|
| 🟡 Medium | S-06 | Fix parallel Docker build error propagation |
|
||||||
|
| 🟢 Low | S-07 | Pin Trivy version or use official action |
|
||||||
|
| 🟢 Low | S-08 | Replace hardcoded IP with DNS/localhost |
|
||||||
|
| 🟢 Low | S-09 | Add SBOM generation (future) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Summary
|
||||||
|
|
||||||
|
The CI/CD setup is **architecturally sound** — parallel jobs, proper caching, multi-stage Docker builds, non-root containers, and comprehensive security scanning (OWASP, Trivy, Gitleaks, pnpm audit).
|
||||||
|
|
||||||
|
The main weakness is that **all security gates are non-blocking** (`|| true` everywhere), which means the scanning tools generate reports but never actually prevent a vulnerable build from deploying. This is the single most impactful fix: remove the fallbacks and let CVEs/secrets block the pipeline.
|
||||||
|
|
||||||
|
The hardcoded secrets in `docker-compose.yml` are a moderate risk — they're clearly dev-only values different from production, but the TrueNAS deployment (which is internet-facing) reuses the same NEXTAUTH_SECRET, which should be fixed.
|
||||||
Reference in New Issue
Block a user