mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 12:51:11 +00:00
09449bda2c
ci/woodpecker/push/build-arm Pipeline failed
Wrap up cycle 3 across the autodev existing-code Phase B steps that follow Implement (Steps 12-15), plus the cross-workspace prerequisite ticket filed for AZ-512. Step 12 - Test-Spec Sync: - Un-quarantine FT-P-01 in traceability-matrix (closed by AZ-510) - Add AZ-510 chained /users/me failure-path test reference under AC-23 - Note AZ-512 deferral status under O9 (P12 Phase B target) Step 13 - Update Docs (task mode): - Refresh src__auth__AuthContext module doc with AZ-510 wire shape (POST refresh + chained /users/me + bootstrapInflight guard) - Add usersMe() to src__api__endpoints module doc + consumer note - Rename src__features__annotations__classColors module doc to src__class-colors__classColors (matches AZ-511 git mv); refresh header - Refresh src__components__DetectionClasses + src__features__annotations module group doc for the new class-colors barrel import path - Update components/11_class-colors Module Inventory to point at the renamed module doc filename - Rewrite system-flows.md Flow F2 (Bearer auto-refresh) with the AZ-510 POST + chained /users/me sequence; close Finding B3 references - Generate ripple_log_cycle3 documenting all changed source files, their reverse-dependency search results, and the docs touched Step 14 - Security Audit (cycle-3 delta): - Resume mode against cycle-2 baseline; cycle-2 artifacts untouched - Re-run bun audit on both roots: clean (cycle-2 inline fix held) - Re-rate OWASP A06: FAIL -> PASS; A07: PASS_WITH_KNOWN -> PASS (B3 closed by AZ-510) - New finding F-SAST-CY3-1 (LOW): __resetBootstrapInflightForTests exposed via src/auth public barrel; defer to hygiene cycle - Verdict: FAIL -> PASS_WITH_WARNINGS; one HIGH (F-SAST-1 mission-planner git-history key, unchanged) remains - Add amendment banner to cycle-2 security_report.md Step 15 - Performance Test: - Static profile NFT-PERF-01 PASS (290 575 B gzipped vs 2 MB budget; ~14% of budget; no regression from AZ-510 surface additions) - E2E profile SKIP (Playwright perf project still pending AZ-457..AZ-482); legitimate skip per test-run skill, gap acknowledged in report - AZ-510 200ms p95 chain NFR verified at spec level only - no CI gate yet (covered by future AZ-457..AZ-482 work) Cross-workspace prerequisite (AZ-513 just filed): - Updated _docs/_process_leftovers/2026-05-13_az-512-admin-classes-prereq.md to reflect AZ-513 filing on admin/ workspace (parent epic AZ-509, Blocks link to AZ-512). Companion task spec added in admin/ repo (separate commit there, owned by admin/ workspace). State file: advanced to Step 16 (Deploy) per autodev existing-code flow. Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
12 KiB
Markdown
157 lines
12 KiB
Markdown
# Security Audit Report — Azaion UI
|
||
|
||
> **AMENDMENT 2026-05-13 — verdict superseded by cycle-3 delta report.** See `_docs/05_security/security_report_cycle3_delta.md`. Current verdict (post AZ-510 + cycle-2-tail `bun update vite`): **PASS_WITH_WARNINGS** (was FAIL). All HIGH-severity dependency advisories closed; OWASP A06 → PASS, A07 → PASS. The HIGH-severity F-SAST-1 (`mission-planner/` Google Geocode API key in git history) remains open but does not affect the production browser bundle. The cycle-2 evidence below is preserved verbatim as the audit history of record.
|
||
|
||
**Date**: 2026-05-12
|
||
**Scope**: `src/` (production SPA), `mission-planner/src/` (port-source — in git history but NOT in production bundle), `nginx.conf`, `Dockerfile`, `.woodpecker/build-arm.yml`, `e2e/` harness, `.env.example` files
|
||
**Cycle**: Phase B / Cycle 2 (post AZ-498, AZ-499)
|
||
**Verdict**: **FAIL** — 1 HIGH-severity secret leak in port-source (`F-SAST-1` Google Geocode API key) plus 1 HIGH-severity dependency advisory (`F-DEP-1` vite — dev-server only, no prod exposure)
|
||
|
||
---
|
||
|
||
## Summary
|
||
|
||
| Severity | Count | Notes |
|
||
|----------|-------|-------|
|
||
| Critical | 0 | — |
|
||
| High | 2 | F-SAST-1 (production-bundle exposure: NONE today; git-history exposure: HIGH); F-DEP-1 (production exposure: NONE; dev-server: HIGH) |
|
||
| Medium | 7 | F-SAST-2, F-SAST-3, F-DEP-2, F-DEP-3, F-INF-1, F-INF-2, F-INF-3, F-INF-4 |
|
||
| Low | 2 | F-SAST-4, F-INF-5 |
|
||
|
||
**Production browser bundle is clean** — no exploitable findings. All HIGH-severity items are concentrated in (a) port-source code that does not ship and (b) dev-time tooling (Vite dev server). The audit's FAIL verdict reflects:
|
||
1. The port-source key is a real secret in real git history → must be revoked + externalized following the AZ-499 pattern.
|
||
2. CI does not run `bun audit`, so the High Vite advisory shipped through Cycle 2 unflagged → procedural gap to close.
|
||
|
||
## OWASP Top 10 (2021) Assessment
|
||
|
||
| # | Category | Status | Findings |
|
||
|---|----------|--------|----------|
|
||
| A01 | Broken Access Control | PASS_WITH_KNOWN | 1 known UX gap (`/admin` route, F2/AC-22 — pre-existing) |
|
||
| A02 | Cryptographic Failures | PASS_WITH_KNOWN | 1 accepted trade-off (SSE bearer-in-query, ADR-008) |
|
||
| A03 | Injection | PASS | — |
|
||
| A04 | Insecure Design | PASS | — |
|
||
| A05 | Security Misconfiguration | FAIL | F-INF-2 (nginx headers + log redaction missing) |
|
||
| A06 | Vulnerable & Outdated Components | FAIL | F-DEP-1, F-DEP-2, F-DEP-3 |
|
||
| A07 | Identification & Authentication Failures | PASS_WITH_KNOWN | 1 known cold-load refresh bug (F2 — pre-existing) |
|
||
| A08 | Software & Data Integrity Failures | FAIL | F-INF-1, F-INF-3, F-INF-4 |
|
||
| A09 | Security Logging & Monitoring Failures | N/A | Server-side concern (operator-internal SPA) |
|
||
| A10 | Server-Side Request Forgery | N/A | Browser SPA has no server-side request surface |
|
||
|
||
## Findings (severity-ranked)
|
||
|
||
| # | Severity | Category | Location | Title |
|
||
|---|----------|----------|----------|-------|
|
||
| F-SAST-1 | **HIGH** | Secrets in code | `mission-planner/src/config.ts:2` | Hardcoded Google Geocode API key in port-source |
|
||
| F-DEP-1 | **HIGH** | Vulnerable component | `vite@6.4.1` (both roots) | Vite Arbitrary File Read via Dev Server WebSocket (GHSA-p9ff-h696-f583) — dev-server only |
|
||
| F-INF-1 | MEDIUM | CI/CD | `.woodpecker/build-arm.yml` | `bun audit` not gated in CI pipeline |
|
||
| F-INF-2 | MEDIUM | Misconfiguration | `nginx.conf` | Missing CSP, X-Frame-Options, HSTS, Referrer-Policy, X-Content-Type-Options, log-redaction |
|
||
| F-INF-3 | MEDIUM | Supply chain | `.woodpecker/build-arm.yml` | No image vulnerability scan (Trivy/Grype) |
|
||
| F-INF-4 | MEDIUM | Supply chain | `.woodpecker/build-arm.yml` | No SBOM emission, no image signing (cosign) |
|
||
| F-DEP-2 | MEDIUM | Vulnerable component | `vite@6.4.1` | Vite Path Traversal in Optimized Deps `.map` (GHSA-4w7w-66w2-5vf9) — dev-server only |
|
||
| F-DEP-3 | MEDIUM | Vulnerable component | `postcss@8.5.8` (transitive) | PostCSS XSS via Unescaped `</style>` (GHSA-qx2v-qp2m-jg93) — low surface |
|
||
| F-SAST-2 | MEDIUM | Supply chain | `mission-planner/src/icons/PointIcons.tsx:7` | `unpkg.com` CDN reference in port-source |
|
||
| F-SAST-3 | MEDIUM | Coverage gap | `scripts/run-tests.sh` (`STC-SEC2`) | No-CDN gate does not scan `mission-planner/` |
|
||
| F-SAST-4 | LOW | Future risk | `mission-planner/src/constants/tileUrls.ts:2-3` | Port-source still uses third-party tile fallbacks |
|
||
| F-INF-5 | LOW | Container hardening | `Dockerfile` | nginx runs as root master process; no `HEALTHCHECK` directive |
|
||
|
||
### Finding Details
|
||
|
||
#### F-SAST-1 — Hardcoded Google Geocode API key — HIGH
|
||
|
||
- **Location**: `mission-planner/src/config.ts:2`
|
||
- **Value**: `AIzaSyAhvDeYukuyWVrQYbRhuv91bsi_jj5_Iys`
|
||
- **Description**: The Google Geocode API key is committed in `mission-planner/` (port-source). Used by `mission-planner/src/flightPlanning/LeftBoard.tsx:114` for address-to-coords lookups.
|
||
- **Production-bundle exposure**: NONE today. `src/` does not import from `mission-planner/`; `Dockerfile` builds only `src/`-rooted Vite. The key is NOT in `dist/`.
|
||
- **Git-history exposure**: HIGH. Anyone with repo read access can extract the key. Same threat class as the OWM key resolved by AZ-499.
|
||
- **Impact**: Quota theft, billing-account abuse, accelerated risk if `mission-planner/` is later ported into the SPA without remediation.
|
||
- **Remediation** (mirror AZ-499 / AC-42 pattern):
|
||
1. **Revoke** the key at https://console.cloud.google.com/google/maps-apis/credentials (manual, OUT-OF-BAND, USER ACTION). Capture evidence.
|
||
2. Externalize: `import.meta.env.VITE_GOOGLE_GEOCODE_KEY` in `mission-planner/src/config.ts` with fail-soft if unset.
|
||
3. Update `mission-planner/.env.example` with placeholder.
|
||
4. Extend `tests/security/banned-deps.json` `owm_key_in_source` (or add a sibling `google_key_in_source`) section to also block the literal Google key.
|
||
5. Long-term: route geocoding via suite-side proxy when the SPA needs it.
|
||
- See: `static_analysis.md` F-SAST-1.
|
||
|
||
#### F-DEP-1 — Vite Arbitrary File Read via Dev Server WebSocket — HIGH
|
||
|
||
- **Location**: `vite@6.4.1` (resolved in `bun.lock`, both `ui/` and `mission-planner/` roots)
|
||
- **Advisory**: [GHSA-p9ff-h696-f583](https://github.com/advisories/GHSA-p9ff-h696-f583)
|
||
- **Description**: WebSocket endpoint exposed by `vite dev` allows arbitrary local-file read via path traversal.
|
||
- **Production-bundle exposure**: NONE. The Vite dev server is never present in production (`Dockerfile` final stage is `nginx:alpine` serving static `dist/`).
|
||
- **Developer-machine exposure**: HIGH if `bun run dev --host` is ever used (binding to `0.0.0.0`); MODERATE for the default `localhost` binding (still a browser-side script attack vector via DNS rebinding).
|
||
- **Remediation**: `bun update vite` in both roots → `vite >= 6.4.2`. Verify build + fast tests still pass.
|
||
- See: `dependency_scan.md` F-DEP-1.
|
||
|
||
(Full detail for F-INF-1 .. F-INF-5 in `infrastructure_review.md`; for F-DEP-2/F-DEP-3 in `dependency_scan.md`; for F-SAST-2/F-SAST-3/F-SAST-4 in `static_analysis.md`. Not duplicated here.)
|
||
|
||
---
|
||
|
||
## Dependency Vulnerabilities
|
||
|
||
| Package | GHSA / Advisory | Severity | Installed | Fix |
|
||
|---------|-----------------|----------|-----------|-----|
|
||
| `vite` | [GHSA-p9ff-h696-f583](https://github.com/advisories/GHSA-p9ff-h696-f583) | HIGH | 6.4.1 | `>= 6.4.2` (bun update vite) |
|
||
| `vite` | [GHSA-4w7w-66w2-5vf9](https://github.com/advisories/GHSA-4w7w-66w2-5vf9) | MODERATE | 6.4.1 | `>= 6.4.2` (same upgrade) |
|
||
| `postcss` | [GHSA-qx2v-qp2m-jg93](https://github.com/advisories/GHSA-qx2v-qp2m-jg93) | MODERATE | 8.5.8 | `>= 8.5.10` (transitive — flows through Vite upgrade) |
|
||
|
||
A single `bun update vite` in each root fixes all three.
|
||
|
||
---
|
||
|
||
## Recommendations
|
||
|
||
### Immediate (HIGH — block deploys until done)
|
||
|
||
- [ ] **F-SAST-1 (USER ACTION + CODE)**: Revoke the Google Geocode API key at the Google Cloud Console, then externalize per AZ-499 pattern. Mirror the manual evidence-capture protocol used for AZ-499 AC-7. Recommended ticket: `AZ-NEW — Externalize Google Geocode key in mission-planner port-source` (3 SP — same shape as AZ-499 minus AC-8 misattribution).
|
||
- [ ] **F-DEP-1 / F-DEP-2 / F-DEP-3 (CODE)**: `bun update vite` in `ui/` and `mission-planner/`. Re-run `bun audit` to confirm zero findings. Recommended ticket: `AZ-NEW — Update Vite to fix CVE-2026 advisories` (1 SP).
|
||
|
||
### Short-term (MEDIUM — Phase B)
|
||
|
||
- [ ] **F-INF-1**: Add `bun audit --severity high` step to `.woodpecker/build-arm.yml` so future advisory regressions fail CI (1 SP).
|
||
- [ ] **F-INF-2**: Add CSP, X-Frame-Options, Referrer-Policy, X-Content-Type-Options + bearer-redaction log format to `nginx.conf` (2 SP). Coordinate HSTS decision with suite ingress.
|
||
- [ ] **F-INF-3**: Add Trivy image-scan step to `.woodpecker/build-arm.yml` after `docker build` (2 SP).
|
||
- [ ] **F-SAST-2**: Bundle Leaflet marker icon locally instead of `unpkg.com` CDN reference (covered by the same port-source cleanup as F-SAST-1).
|
||
- [ ] **F-SAST-3**: Widen no-CDN static gate to scan `mission-planner/` — move pattern into `tests/security/banned-deps.json` and use the existing `check-banned-deps.mjs` widening (2 SP).
|
||
|
||
### Long-term (Suite-wide / Hardening)
|
||
|
||
- [ ] **F-INF-4**: SBOM (Syft/cyclonedx) + cosign image signing — coordinate registry capability with suite team (3-5 SP).
|
||
- [ ] **F-SAST-4**: Mission-planner port-source modernization will resolve the third-party tile fallbacks naturally — no separate ticket needed.
|
||
- [ ] **F-INF-5**: `nginxinc/nginx-unprivileged` migration + `HEALTHCHECK` directive (1 SP, low priority).
|
||
|
||
### Pre-existing (not introduced by this audit; tracked elsewhere)
|
||
|
||
- F2 / AC-01 — bootstrap refresh missing `credentials:'include'` (`src/auth/AuthContext.tsx:24`). Quarantined-test acknowledged. Phase B fix.
|
||
- AC-22 — `/admin` route lacks client-side role-gate. Server-authoritative, no exploit. Phase B UX fix.
|
||
- ADR-008 — SSE bearer-in-query-string. Accepted trade-off; mitigation lives in F-INF-2 (nginx log redaction).
|
||
- AZ-499 AC-7 — OWM key revocation manual deliverable. **Pending USER action.**
|
||
|
||
---
|
||
|
||
## Cycle 2 — security regression check
|
||
|
||
No security regressions introduced by AZ-498 or AZ-499. Both changes pass static + fast test suites; the cookie-credentialed tile fetch is correctly scoped to `SameSite=Strict` and same-origin; the OWM env hardening closes the previously quarantined `NFT-SEC-09` source check.
|
||
|
||
`STC-SEC1C` is now part of the static gate and would catch any future re-introduction of the literal OWM key in either `src/` or `mission-planner/`.
|
||
|
||
---
|
||
|
||
## Verdict justification
|
||
|
||
The verdict is **FAIL** because:
|
||
1. F-SAST-1 is a real third-party API key in real git history. The same finding class as AZ-499 — same remediation pattern, same urgency, same need for out-of-band revocation.
|
||
2. F-DEP-1 is a HIGH advisory against a current direct dependency. Even with no production exposure, OWASP A06 categorically fails on any actionable HIGH advisory.
|
||
|
||
Both findings have one-line remediations. Once F-SAST-1 is revoked + externalized and F-DEP-1 is upgraded, a follow-up audit cycle should re-rate the verdict to PASS_WITH_WARNINGS pending the MEDIUM infrastructure tickets.
|
||
|
||
The production browser bundle itself is **not vulnerable** — the SPA is well-architected (server-authoritative auth, bearer-in-memory + HttpOnly cookie, no eval/injection surface, no client-side persistence). The deficiencies are at the supply-chain, infrastructure, and port-source layers.
|
||
|
||
## Self-verification
|
||
|
||
- [x] All findings from Phases 1–4 included
|
||
- [x] No duplicate findings (cross-references used instead)
|
||
- [x] Every finding has remediation guidance
|
||
- [x] Verdict matches severity logic (FAIL on any HIGH)
|
||
- [x] Production-vs-dev impact distinguished for each HIGH finding
|
||
- [x] Cycle 2 deltas (AZ-498, AZ-499) explicitly reviewed for regressions
|