23 Commits

Author SHA1 Message Date
Christopher Fahlin e0372eb081 ci: gate workflow to GitHub Actions only, skip on Gitea mirror
CI / mix precommit (push) Has been skipped
CI / All checks passed (push) Has been skipped
CI / Build and push to Harbor (push) Has been skipped
2026-05-30 20:34:39 -07:00
Christopher Fahlin 6aa878a8ab feat: add app-level auth with RBAC and per-user tenancy
CI / mix precommit (push) Failing after 22s
CI / Build and push to Harbor (push) Has been skipped
CI / All checks passed (push) Failing after 2s
Add password-only phx.gen.auth (argon2) with closed, admin-managed
registration: admins create accounts with a temporary password and users
must change it on first login. Strip email/magic-link flows and remove the
unused Swoosh mailer.

Add two roles (admin/user) enforced via on_mount guards, and per-user data
tenancy: scans, assets, and findings carry a user_id; vulnerabilities stay
global with visibility derived through findings via a correlated EXISTS.
Cross-tenant detail URLs 404; admins see all rows.

Merge the account password page into /settings. Add an admin user-management
dashboard, a seeded bootstrap admin (fixed dev creds via seeds.exs, random
password via the new seed-admin release task), and cross-tenant isolation
tests. Bundle the root layout noindex/theme-color SEO change.
2026-05-30 20:30:46 -07:00
Christopher Fahlin 3c7f28cc67 fix(docker): apply security updates in runtime stage to clear CVE gate
The pinned Debian snapshot shipped libgnutls30 3.7.9-2+deb12u6, which Trivy
flagged for 5 fixed HIGH/CRITICAL CVEs (deb12u7 available), blocking the
publish gate. Add apt-get upgrade to the runtime stage so security fixes land
without chasing a newer base snapshot on every advisory.

Verified locally: Trivy HIGH/CRITICAL fixed-only scan reports 0 findings.
2026-05-30 19:38:55 -07:00
Christopher Fahlin cb93063ad6 fix(docker): correct base image tag and compile/assets ordering
- Pin builder + runtime to a real, matching Debian snapshot (bookworm-20260518);
  the previous 20250630 tag was never published for OTP 27.3.4, so buildx
  couldn't resolve the builder image and CI failed
- Run mix compile BEFORE mix assets.deploy: the colocated-hooks extractor
  generates phoenix-colocated/bulwark at compile time and esbuild resolves it
  via NODE_PATH, so the prior order broke the asset build
- Production hardening: --no-install-recommends, RELEASE_DISTRIBUTION=none,
  crash dumps to /dev/null, CMD is server-only (migrations run as an
  initContainer per the deploy runbook)

Verified: image builds (153MB) and the release boots.
2026-05-30 19:34:22 -07:00
Christopher Fahlin 2f5587aaec ci: add Harbor publish pipeline with supply-chain gates
GitHub Actions workflow: mix precommit gate against a postgres:17 service
container on PRs and pushes; on pushes to main, build the release image,
gate on a Trivy HIGH/CRITICAL scan, emit an SBOM, push to Harbor, and sign
the pushed tags with Cosign.

- Image: harbor.icecoldchris.dev/bulwark/bulwark (tags: sha-<sha>, latest)
- Commit the Cosign public key (cosign.pub) for verification; gitignore the
  private key (cosign.key / *.key)
2026-05-30 19:27:05 -07:00
Christopher Fahlin ea814c5125 build: add release image, compose backing services, and env scaffolding
- Multi-stage Dockerfile (mix release, embedded ERTS, runs as nobody) with
  migrate/server release overlays for an initContainer migration flow
- docker-compose: standalone-friendly upstream images (postgres:17,
  valkey/valkey:8) for the local dev loop. The cluster's operator images
  (cloudnative-pg, hyperspike) don't run standalone — DSN-from-env already
  gives local<->cluster parity, so image identity is local-only
- .env(.local).example templates + .envrc (direnv) loader
- .dockerignore; .gitignore fixups for release artifacts and env files
2026-05-30 19:07:21 -07:00
Christopher Fahlin 0827168616 feat: add Valkey cache, health probes, and 12-factor config; fix ingestion bugs
Cache (Bulwark.Cache): Redix-pooled Valkey facade that degrades gracefully
when VALKEY_URL is unset or the server is unreachable. Backs the dashboard
KPI cache (5m TTL, invalidated on triage/cancel/parse) and a fail-open
fixed-window rate limiter on uploads. Oban stays on durable Postgres.

Config: dev and test Repos honor DATABASE_URL (12-factor parity) with
zero-config localhost fallbacks. Test config reads TEST_DATABASE_URL and
TEST_VALKEY_URL so a dev .env.local loaded via direnv can't leak into the
hermetic suite.

Health: /health (liveness, always 200) and /ready (readiness, 503 if
Postgres is down; cache-down reported degraded but still 200).

Fixes:
- Detector no longer crashes on top-level JSON arrays (non-map catch-all)
- ParseJob isolates each finding in its own transaction; synthesizes stable
  external IDs (SHA-256 fingerprint) when a tool omits one
- Vulnerability severity normalized centrally (aliases -> canonical levels)
- Upload path traversal closed (Path.basename); peer IP captured for limiter
2026-05-30 19:06:56 -07:00
Christopher Fahlin bd73dc781b docs: update README and CLAUDE.md for current feature set
Add Grype/Snyk to supported formats, settings route, filter bar
component, accent theming. Fix dark mode description.
2026-05-13 21:22:46 -07:00
Christopher Fahlin 303e2c9c60 refactor: rename sec_dashboard to bulwark
Rename the entire project from SecDashboard to Bulwark:
- Module prefixes: SecDashboard -> Bulwark, SecDashboardWeb -> BulwarkWeb
- OTP app atom: :sec_dashboard -> :bulwark
- Directory structure: lib/sec_dashboard -> lib/bulwark, etc
- Config, assets, tests, migrations all updated
- Database names: bulwark_dev, bulwark_test
2026-05-13 21:15:40 -07:00
Christopher Fahlin 89ce5a29d5 feat: add cmd+k filter bar to vulnerabilities, findings, and assets
- Add FilterBar JS hook for global cmd+K keyboard shortcut
- Add filter_bar component to CoreComponents with search input and pill slots
- Vulnerabilities: text search across CVE ID/title (Flop compound fields) + severity pills
- Findings: status quick-filter pills (open, acknowledged, resolved, false positive)
- Assets: text search on identifier + type pills (container image, package, etc)
- Add compound_fields to Vulnerability and Asset Flop.Schema derivations
- Filter state persisted in URL params, compatible with Flop pagination/sorting
- Filter bar accent glow on focus when accents enabled
2026-05-13 21:11:42 -07:00
Christopher Fahlin 72b60da3ac feat: add accent theming across app shell and components
- Active nav accent bar (2px sky left border on current page)
- Section card header gradient line (sky-to-transparent top border)
- Status bar connected dot glow
- Severity indicator dots (colored dot before badge label via CSS ::before)
- Processing scan badge pulse glow animation
- Focus ring accent (sky-500 outline on :focus-visible)
- Table row hover highlight
- Add sev-dot/data-sev attrs to severity_badge component
- Add scan-processing class to processing scan status badge
- Add status-dot class to status bar indicator
- Add card-header class to dashboard section headers
- All effects gated behind body[data-accents] toggle
2026-05-13 20:59:13 -07:00
Christopher Fahlin ac5c17c66d feat: add settings page with accent color toggle
- Add /settings route with SettingsLive view
- Settings stored in localStorage via Settings JS hook
- Accent colors (stat card borders, integrity glow, row hover)
  gated behind body[data-accents] CSS attribute
- Add data-accent attr to stat_card component (replaces inline
  border classes with CSS-driven theming)
- Add Settings nav item to sidebar bottom section
- FOUC prevention: inline script in root layout applies saved
  preference before first paint
2026-05-13 20:55:51 -07:00
Christopher Fahlin de7a3c6c2c fix: ingestion pipeline, add parsers/samples, scan cancel, design cleanup
- Fix Finding changeset: add scan_id/vulnerability_id/asset_id to cast
  whitelist (was silently dropping foreign keys, blocking all ingestion)
- Add SPDX dispatch as no-op (BOM format, no vulnerabilities)
- Add Grype parser for Anchore container scan JSON output
- Add Snyk parser for npm/pip test JSON output
- Add scan cancellation (pending/processing scans only)
- Add cancelled status to Scan schema and scan_status component
- Fix stat card accent colors (use border-l-* directional utilities)
- Replace design/ folder with DESIGN.md reference doc
- Expand all sample files to production-grade size (8 tools, 82 findings)
- Update Detector for Grype and Snyk format detection
- Update CLAUDE.md with new parser inventory
2026-05-13 20:50:46 -07:00
Christopher Fahlin 79a0e749f8 feat: migrate to dark-mode SOC dashboard design
Rewrite entire frontend to match the Auditor Professional design
reference. Dark-mode-only zinc palette with Inter/JetBrains Mono
typography, collapsible sidebar (52px/180px), persistent header
with breadcrumbs and system integrity pulse, status bar footer
with uptime and sync timers.

- app.css: dark-only base, @theme for fonts/text-xxs, dot-grid
  background, sticky table headers with backdrop blur, custom
  scrollbar, integrity-pulse animation
- root.html.heex: Google Fonts, remove theme toggle JS
- app.js: add Sidebar and StatusBar hooks, merge with colocated
- layouts.ex: collapsible sidebar, header with breadcrumbs,
  status bar with JS-driven timers
- core_components.ex: dark-only palette, 3-letter severity
  badges (CRT/HI/MED/LOW), short finding status labels
  (Open/Ack/Fixed/FP), compact mono-font badges
- All 8 LiveViews: remove dark: variants, use dark palette
  directly, monospace data classes, compact density
- CLAUDE.md: document new design system conventions
2026-05-13 20:24:41 -07:00
Christopher Fahlin e55495fb37 docs: add SOC dashboard design reference (standalone HTML) 2026-05-13 20:05:37 -07:00
Christopher Fahlin 8c93112a3a feat: replace daisyUI with zinc/slate design system on bare Tailwind v4
- Remove daisyUI plugin and vendor files (daisyui.js, daisyui-theme.js)
- Add custom zinc/slate palette with semantic colors for severity only
- Add data-table CSS class for dense Flop-paginated tables
- Rewrite sidebar from daisyUI drawer to flex-based peer-state layout
- Replace all daisyUI classes across 8 LiveViews and CoreComponents
- Extract finding_status_badge to CoreComponents (was duplicated in 4 files)
- Add JS dropdown with toggle/hide for triage actions (replaces daisyUI dropdown)
- Update theme JS to resolve system preference to concrete data-theme
- Track theme setting separately via data-theme-setting attribute
- Update pagination to use Flop.Phoenix class attr instead of removed opts
- Rewrite README with project overview and getting started guide
- Update CLAUDE.md to reflect design system changes
2026-05-13 19:57:23 -07:00
Christopher Fahlin f6405a4d32 docs: update CLAUDE.md with multi-page architecture and route table 2026-05-13 19:33:03 -07:00
Christopher Fahlin 2da7296d27 docs: add @moduledoc, @doc, @spec across all domain and ingestion modules 2026-05-13 19:32:18 -07:00
Christopher Fahlin 66f5a7ef5e feat: multi-page app with scan, vulnerability, finding, and asset LiveViews
- Restructure router: dashboard at /, dedicated pages for each domain entity
- Simplify DashboardLive to KPI overview with recent scans/vulns
- Add ScanLive.Index (upload + list) and ScanLive.Show (detail + findings)
- Add VulnerabilityLive.Index (list) and VulnerabilityLive.Show (detail)
- Add FindingLive.Index with inline triage actions
- Add AssetLive.Index (list) and AssetLive.Show (detail + metadata)
- Delete PageController and default Phoenix home page
2026-05-13 19:29:56 -07:00
Christopher Fahlin b4be125712 feat: redesign app shell with sidebar navigation and daisyUI drawer 2026-05-13 19:25:29 -07:00
Christopher Fahlin a4ef73a49e refactor: extract severity_badge, scan_status, stat_card to CoreComponents 2026-05-13 19:24:23 -07:00
Christopher Fahlin 2626aff050 feat: expand Security context API with lookup, scoped listing, and triage functions 2026-05-13 19:23:32 -07:00
Christopher Fahlin e1fdaeb074 feat: initial security dashboard with ingestion pipeline 2026-05-13 19:02:47 -07:00