Review-driven fixes, hardening, docs + risk-based prioritization (KEV/EPSS) - #4
Merged
Merged
Conversation
…C tests From a multi-agent review of the codebase: - ScanReconciler: an empty/errored scan no longer auto-resolves every open finding (which reported false 100% compliance); dedup identity matching is now case-insensitive to match Azure SQL collation (fixes duplicate-INSERT 500s on e.g. Newtonsoft.Json vs newtonsoft.json). - IngestionService: retry on the dedup unique-index collision so two concurrent scans of a project reconcile instead of 500-ing. - Csv.Field: neutralize spreadsheet formula-injection triggers (=,+,-,@) in exported, untrusted scan fields. - Client: TokenHandler now clears the token and redirects to login on a 401 (expired/revoked session) instead of leaking a raw error string; Login guards returnUrl against open-redirect. - Tests: RBAC authorization-matrix integration tests + a role-seeding/JWT fixture helper (the biggest coverage gap for a security tool).
- docs/THREAT-MODEL.md: full STRIDE model with a Mermaid data-flow diagram, trust boundaries, each threat mapped to its in-code mitigation, and an honest residual-risks section. - docs/architecture.md: Mermaid system diagram + walkthrough. - README: fix stale drift (Key Vault is live, not 'on the roadmap'; accurate stage table), add a live-demo + API-docs section and a 'What's inside' feature list.
…ed SLA) Severity/CVSS alone is a weak triage signal. Findings are now enriched with CISA KEV (known actively exploited) and FIRST EPSS (predicted exploitation probability), and those signals drive both a risk-adjusted SLA and prioritized ordering. - Domain: RiskEvaluator (rank + label), SlaEvaluator.ComputeDueDate risk-aware overload (KEV/high-EPSS tighten the deadline, never loosen; also gives an exploited Unknown-severity finding a deadline), CveIdentifier. - Infrastructure: VulnerabilityIntelligenceClient (public KEV feed + EPSS API, pure testable parsers, failures swallowed), FindingEnrichmentService, wired into the hourly SLA re-evaluation; config-gated + inert by default (VulnIntel:Enabled), so local/dev/test make no external calls. - API/UI: Finding gains KEV/EPSS columns (+migration); FindingDto/DashboardDto carry the signals; risk-first sort on findings; GET /api/risk-policy and POST /api/admin/enrich; dashboard 'Actively exploited' count + finding risk badges; VulnIntel__Enabled=true added to the Container App (Terraform). - Tests: risk math, risk-adjusted SLA, CVE extraction, KEV/EPSS parsing (unit suite 76 -> 107 across the branch). Program.cs and RavelinEndpoints.cs in this commit also carry same-review hardening that shares these files: JWT fail-closed on a missing/weak signing key (no more all-zeros fallback) + tighter ClockSkew, and read-path AsNoTracking + SQL-side breach filtering + additive pagination.
Ravelin's own SCA gate flagged the transitive Microsoft.OpenApi 2.0.0 (pulled by Microsoft.AspNetCore.OpenApi/Scalar) as vulnerable to GHSA-v5pm-xwqc-g5wc (circular-schema parsing DoS; patched in 2.7.5). Pin it directly so the patched version resolves across the whole solution. The dogfood loop working as intended.
The Azure DevOps pipeline ran 'dotnet test Ravelin.slnx' (whole solution), which pulls in the Testcontainers integration tier and the Playwright E2E tier on an agent that provisions neither — so the test step failed. Those tiers run in GitHub Actions (security.yml), which sets them up. Run only the fast unit project here, as the build/containerize/deploy gate.
A PR validation build (triggered by branch policy) shouldn't push a production image to ACR or deploy to Container Apps — it should just build and unit-test. Gate the Image and Deploy stages on Build.Reason != PullRequest so PR checks stop at the Build stage; the full build->image->deploy still runs on main.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A review-driven improvement pass over Ravelin: a multi-agent code review surfaced correctness bugs and hardening gaps, I fixed the high-value ones, then added the biggest maturity feature — risk-based prioritization (CISA KEV + FIRST EPSS → risk-adjusted SLA). Also ships the STRIDE threat model and architecture docs that were promised but missing.
Build is clean; the unit suite grows 76 → 107, all green. Integration/E2E tiers (which need Docker/real SQL) run in CI.
The branch is organized into three commits — fixes, docs, and the feature — grouped below.
🐛 Correctness & hardening (
fix:commit)OrdinalIgnoreCaseto match Azure SQL's collation, fixing duplicate-INSERT 500s (e.g.Newtonsoft.Jsonvsnewtonsoft.json; NuGet ids are case-insensitive).IngestionServiceretries on the dedup unique-index collision so two overlapping scans reconcile instead of returning a 500 (arowversionconcurrency token is added alongside).= + - @are neutralized so they can't execute in an auditor's spreadsheet.returnUrlagainst open redirects.Program.cs): removed the all-zeros signing-key fallback — Production refuses to start without a ≥32-byte key; dev/test use an explicit dev key. TightenedClockSkewto 30s.RavelinEndpoints.cs):AsNoTracking+ SQL-side breach filtering + additive (non-breaking) pagination on the hot read queries.📄 Docs (
docs:commit)docs/THREAT-MODEL.md— full STRIDE model: Mermaid DFD, trust boundaries, each threat mapped to its in-code mitigation, and an honest residual-risks section.docs/architecture.md— Mermaid system diagram + walkthrough.✨ Risk-based prioritization (
feat:commit)Severity/CVSS alone is a weak triage signal — real programs prioritize by exploitation in the wild.
POST /api/admin/enrich). Config-gated and inert by default (VulnIntel:Enabled) — no external calls locally; feed outages are swallowed.GET /api/risk-policy,POST /api/admin/enrich, newFindingcolumns (+ migration), enriched DTOs, KEV/EPSS in CSV export.Deploy / activation
VulnIntel__Enabled=trueis added to the Container App (Terraform). To activate live:terraform apply→ deploy (migrations apply on boot) → triggerPOST /api/admin/enrichor wait for the hourly re-eval → the dashboard's "Actively exploited" count and finding badges populate.Verification
dotnet buildclean;dotnet testunit tier 107 passing.Notes for the reviewer
feat:commit'sProgram.cs/RavelinEndpoints.csalso carry the JWT fail-closed and read-path scaling changes, because those hardening edits share files with the feature (called out above and in the commit body).terraform applyagainst live Azure; aTimeProviderrefactor for deterministic time-tests; typed results for richer OpenAPI schemas.