# Security Review: Sprint 5 **Date:** 2026-06-12 **Scope:** Full codebase (backend + frontend) after Sprint 5 **Tools:** Snyk Code (SAST), Snyk Open Source (SCA), SonarQube (SAST via MCP) --- ## Summary | Scan | Critical | High | Medium | Low | Status | |------|----------|------|--------|-----|--------| | Snyk Code (Frontend) | 0 | 0 | 2 | 4 | ⚠️ Low risk | | Snyk Code (Backend) | 0 | 1 | 0 | 2 | ⚠️ Review needed | | Snyk SCA (Frontend) | 0 | 5 | 14 | 0 | ⚠️ Upgrade needed | | Snyk SCA (Backend) | 0 | 0 | 0 | 0 | ✅ Clean | | SonarQube (Security) | 0 | 0 | 0 | 0 | ✅ Clean | **Overall Verdict:** ⚠️ No critical blockers. 1 High-severity SAST finding (CSRF — false positive for stateless JWT API). 5 High-severity SCA findings in transitive dependencies (upgrade `next` to fix 2, remaining 3 in eslint tooling). No secrets exposure, no injection vectors, no auth bypass paths. --- ## Findings ### HIGH Severity #### 1. Snyk Code: CSRF Protection Disabled — `SecurityConfig.java:47` **Finding:** `.csrf(csrf -> csrf.disable())` on the `/api/**` filter chain. **Verdict: ✅ FALSE POSITIVE — Intentional design.** This API uses stateless JWT authentication with `SessionCreationPolicy.STATELESS`. CSRF protection is irrelevant for stateless token-based APIs because: - No cookies carry authentication state (Bearer tokens in Authorization header) - The browser never automatically attaches credentials - CSRF attacks rely on cookie-based session state The portal filter chain (`/portal/**`) correctly enables CSRF via `CookieCsrfTokenRepository`. The public endpoints filter chain also disables CSRF — acceptable for Swagger/actuator. **Action:** None required. Add a comment explaining the rationale. --- #### 2. Snyk SCA: Allocation of Resources Without Limits — `next@15.5.18` **Vulnerability:** [SNYK-JS-NEXT-15105315](https://security.snyk.io/vuln/SNYK-JS-NEXT-15105315) (High) **Fix:** Upgrade `next` to `>=16.1.7` **Action:** Evaluate Next.js 16 upgrade feasibility. Pin to latest 15.x patch if 16 is breaking. --- #### 3. Snyk SCA: ReDoS in `ajv@6.12.6` **Vulnerability:** [SNYK-JS-AJV-15274295](https://security.snyk.io/vuln/SNYK-JS-AJV-15274295) (High) **Path:** `@eslint/eslintrc@3.2.0 > ajv@6.12.6` **Fix:** `ajv` ≥ 6.14.0 **Action:** Dev dependency only — not in production bundle. Low real-world risk. --- #### 4. Snyk SCA: Infinite loop in `brace-expansion@1.1.12` **Vulnerability:** [SNYK-JS-BRACEEXPANSION-15789759](https://security.snyk.io/vuln/SNYK-JS-BRACEEXPANSION-15789759) (High) **Path:** `@eslint/eslintrc@3.2.0 > minimatch@3.1.2 > brace-expansion@1.1.12` **Fix:** `brace-expansion` ≥ 1.1.13 **Action:** Dev dependency only (eslint toolchain). Override in `package.json` resolutions. --- #### 5. Snyk SCA: ReDoS in `minimatch@3.1.2` **Vulnerability:** [SNYK-JS-MINIMATCH-15309438](https://security.snyk.io/vuln/SNYK-JS-MINIMATCH-15309438) (High) **Path:** `@eslint/eslintrc@3.2.0 > minimatch@3.1.2` **Fix:** `minimatch` ≥ 3.1.3 **Action:** Dev dependency only. Override in resolutions. --- #### 6. Snyk SCA: Inefficient Algorithmic Complexity in `minimatch@3.1.2` **Vulnerability:** [SNYK-JS-MINIMATCH-15353389](https://security.snyk.io/vuln/SNYK-JS-MINIMATCH-15353389) (High) **Path:** `@eslint/eslintrc@3.2.0 > minimatch@3.1.2` **Fix:** `minimatch` ≥ 3.1.3 **Action:** Same as above — override in resolutions. --- #### 7. Snyk SCA: ReDoS in `picomatch@4.0.3` **Vulnerability:** [SNYK-JS-PICOMATCH-15765511](https://security.snyk.io/vuln/SNYK-JS-PICOMATCH-15765511) (High) **Path:** `next-intl@4.13.0 > @parcel/watcher@2.5.6 > picomatch@4.0.3` **Fix:** `picomatch` ≥ 4.0.4 **Action:** Production dependency via `next-intl`. Override `picomatch` to `4.0.4` in `pnpm.overrides`. --- ### MEDIUM Severity #### 8. Snyk Code: Hardcoded Password — `src/data/user.ts:8` **Finding:** `password: "StrongPass123"` in mock user data. **Verdict: ⚠️ LOW RISK — Placeholder mock data from UI template (Shadboard starter kit).** This file is only used for the sidebar user display component. It does NOT authenticate against the backend. However, shipping mock credentials in production bundles is poor practice. **Action:** Remove the `password` field from `userData` — it is not consumed anywhere in the UI. --- #### 9. Snyk Code: HTTP Cleartext — `e2e/mock-backend.mjs:16` **Finding:** `http.createServer()` uses unencrypted HTTP. **Verdict: ✅ FALSE POSITIVE — Test-only file.** This is a local E2E test mock server used by Playwright. Never deployed. **Action:** None required. --- #### 10. Snyk SCA: Prototype Pollution in `picomatch@4.0.3` **Vulnerability:** [SNYK-JS-PICOMATCH-15765513](https://security.snyk.io/vuln/SNYK-JS-PICOMATCH-15765513) (Medium) **Fix:** `picomatch` ≥ 4.0.4 **Action:** Same override as finding #7. --- #### 11. Snyk SCA: XSS in `postcss@8.4.31` **Vulnerability:** [SNYK-JS-POSTCSS-16189065](https://security.snyk.io/vuln/SNYK-JS-POSTCSS-16189065) (Medium) **Path:** `next@15.5.18 > postcss@8.4.31` **Fix:** `postcss` ≥ 8.5.10 **Action:** Override `postcss` in `pnpm.overrides`, or wait for Next.js upgrade to bundle newer postcss. --- #### 12. Snyk SCA: 10× LGPL-3.0 License Issues — `@img/sharp-libvips-*@1.2.4` **Finding:** Sharp image library bundles LGPL-licensed libvips binaries across platform variants. **Verdict: ⚠️ LICENSE COMPLIANCE — Not a security issue.** LGPL-3.0 allows dynamic linking without copyleft contamination. Sharp uses these as pre-built native binaries (dynamic linking). No source disclosure obligation for the application. **Action:** Document license acceptance in project records. No code change needed. --- ### LOW Severity #### 13. Snyk Code: Hardcoded Passwords in Tests | File | Line | Context | |------|------|---------| | `cannamanage-api/src/test/java/.../AuthControllerIntegrationTest.java` | 48 | Test credential | | `cannamanage-api/src/test/java/.../AuthIntegrationTest.java` | 24 | Test secret | | `cannamanage-frontend/e2e/authenticated-tour.spec.ts` | 152 | E2E test credential | **Verdict: ✅ FALSE POSITIVE — Test-only scope.** These are hardcoded test credentials in test files. They don't match production passwords and are not deployed. **Action:** None required. Standard testing practice. --- #### 14. Snyk Code: Missing Secure Cookie Attribute | File | Line | Context | |------|------|---------| | `src/components/ui/sidebar.tsx` | 93 | UI preference cookie | | `e2e/authenticated-tour.spec.ts` | 52 | E2E test cookie setup | | `e2e/screenshot-tour.spec.ts` | 50 | E2E test cookie setup | **Verdict: ⚠️ MINOR for sidebar.tsx — the cookie stores UI sidebar state (open/closed), not auth data.** **Action:** Add `secure: true` to the sidebar cookie in production builds (good practice, even for non-sensitive data). E2E test files are acceptable — localhost doesn't support Secure flag. --- ## SonarQube Analysis (MCP) | File | Issues | Security Findings | |------|--------|-------------------| | `SecurityConfig.java` | 4 maintainability (duplicate literals) | 0 security | | `AuthService.java` | 1 maintainability (generic exception in SHA-256 catch) | 0 security | | `JwtAuthFilter.java` | 0 (excl. path false positive) | 0 security | | `auth.ts` (NextAuth config) | 0 | 0 security | **SonarQube Verdict: ✅ No security issues detected.** The code follows secure patterns: - Proper token validation before extraction - Token revocation check (JTI blacklist) - TenantContext cleanup in `finally` block - Generic error messages in auth (no info leakage) - SHA-256 for refresh token storage (appropriate for high-entropy tokens) - Proper refresh token rotation on each use --- ## False Positives Summary | # | Tool | File | Finding | Reason | |---|------|------|---------|--------| | 1 | Snyk Code | `SecurityConfig.java:47` | CSRF disabled | Stateless JWT API — CSRF irrelevant | | 2 | Snyk Code | `e2e/mock-backend.mjs:16` | HTTP cleartext | Test-only mock server | | 3 | Snyk Code | `*IntegrationTest.java` | Hardcoded passwords | Test-scope only | | 4 | Snyk Code | `e2e/*.spec.ts` | Missing Secure cookie | E2E test setup on localhost | | 5 | Snyk SCA | `ajv`, `minimatch`, `brace-expansion` | High-severity ReDoS | Dev-only eslint toolchain deps | --- ## Recommendations ### Immediate (Sprint 6) 1. **Remove `password` field from `src/data/user.ts`** — unused mock data that triggers SAST scanners 2. **Add `pnpm.overrides`** for `picomatch@4.0.4` and `postcss@8.5.10` — fixes production-path vulnerabilities 3. **Add `secure: true`** to sidebar cookie in `sidebar.tsx` (line 93) when `window.location.protocol === 'https:'` ### Near-term (Sprint 7-8) 4. **Evaluate Next.js 16 upgrade** — fixes 2 High-severity `next` vulnerabilities (resource throttling) 5. **Upgrade `@eslint/eslintrc`** or add `pnpm.overrides` for `minimatch@3.1.3` and `ajv@6.14.0` — cleans dev toolchain ### Architecture Notes (Already Good) - ✅ JWT auth with refresh token rotation and revocation - ✅ Portal uses session-based auth with CSRF protection (separate filter chain) - ✅ Password hashing via BCrypt - ✅ Generic error messages in auth flow (no user enumeration) - ✅ Invite token flow with expiry and single-use guarantee - ✅ TenantContext isolation per request with `finally` cleanup - ✅ Token blacklist via JTI for immediate revocation - ✅ CORS restricted to known origins - ✅ Method-level security via `@PreAuthorize` --- ## Appendix: Scan Commands ```bash # Snyk Code (SAST) — Frontend cd cannamanage-frontend && snyk code test # Snyk Code (SAST) — Backend (full repo) cd cannamanage && snyk code test # Snyk SCA — Frontend cd cannamanage-frontend && snyk test --all-projects # Snyk SCA — Backend cd cannamanage && snyk test --file=pom.xml # SonarQube — via MCP analyze_code_snippet on key security files ```