feat(reports): vulnerability_management evidence report (#5816) (#5784 W04) - #6027
Conversation
…W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…naged registry and config schema (#5784 W04) The two reportGenerationService.test.ts arms for the new type stay red until the generator module lands in the next commit - that is the red-first order. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nd feed freshness (#5784 W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… arithmetic (#5784 W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… KEV/EPSS callouts (#5784 W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…f arm (#5784 W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…elf-serve generate (#5784 W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…p, end to end (#5784 W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ight-locale keys (#5784 W04) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ity_management PDF arm (#5784 W04) Also flips W01's auto-evidence empty-state assertion now that the registry has its first member. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Deploying breeze with
|
| Latest commit: |
c81c939
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://da76f0f2.breeze-9te.pages.dev |
| Branch Preview URL: | https://feature-5812-service-plan-ev-dqdk.breeze-9te.pages.dev |
… result, as measured (#5784 W04) Two review findings: - `closedThisPeriod.count` was always a real integer, so a run where no feed had ever synced printed "0 finding(s) were remediated" right beside "N/A - not measured" for the same data source. It now follows the same unmeasured gate as `open`. - The restricted-to-zero-sites short circuit returned `emptyRowsReport()`, which carries no `summary` — so buildReportPdf fell through to renderGenericReport and printed "No data available for the selected filters.", indistinguishable from "we checked every device and found none". Both that arm and the generator's own branch now share one `emptyVulnerabilityManagementSummary`. Plus the test-coverage gaps the review named: mitigated-vs-accepted exception classification, the missing-cadence fallback disclosure, the below-floor patchable finding still ranking for remediation, and the topN schema bounds. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… a spliced one (#5784 W04) groupExceptions set `status` from whichever device's finding was read first, then later overwrote `acceptedBy`/`acceptedUntil` from a different device's finding when that one expired sooner — regardless of ITS status. A CVE accepted on device A until December and mitigated on device B until October printed as "accepted, by Bob, until October": a record neither device holds, in the very section a vCISO reads to decide what to re-approve. Status, approver and expiry now move together. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Review run: Findings: 3 consequential raised → all 3 addressed in
Also closed the test-coverage gaps the analyzer named: Clean on review: per-branch site scoping (verified non-vacuous — the assertion's needle is a UUID, not a matchable enum value); Tests (re-run in full after the fixes): Status: review-clean, awaiting maintainer merge. Note the rollout item in the PR body — this release should run |
…ced for c9ea189/f5845d8) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ByUsCYMsa2c4EDG797LTqf
…dpoint_management_review (W03) + vulnerability_management (W04) registrations Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ByUsCYMsa2c4EDG797LTqf
|
Merged |
W04 of #5812 / #5784 — the
vulnerability_managementreport type, end to end.The spec's own worked example (#5573 §15 points a vulnerability-management
deliverable at "the org's vulnerability report definition") now has a report to
point at.
Closes #5816
Spec:
docs/superpowers/specs/billing/2026-09-14-service-plan-evidence-reports-spec.md§3.4Plan:
docs/superpowers/plans/billing/2026-09-14-service-plan-evidence-reports-w04-vulnerability-management.mdPlan index:
docs/superpowers/plans/billing/2026-09-14-service-plan-evidence-reports.mdDepends on W01 only (#5813, merged as
eabb9eb48). Independent of W02/W03/W06.Relationship to
security_compliance_posturesecurity_compliance_posturekeeps its single vulnerability control line;this is the detail artifact — findings, exceptions and a remediation
ranking. Neither replaces the other.
The shared loader
securityComplianceReportVulnerabilities.tswas extended,not forked: the catalog join and the throw-on-incomplete-catalog rule now live
in one private
loadVulnerabilityCatalog, which bothloadOpenVulnerabilityCounts(posture, signature and behaviour unchanged — its tests pass unedited) and
the new
loadOpenFindingscall. Integration Case 4 generates a posture run andthis report for the same seeded org and asserts the critical/high numbers agree;
if a later change forks the loader, that case goes red. (verified)
What the artifact leads with
last_sync_atis on the cover. A reportthat under-counts because MSRC has not synced in a week says so instead of
printing a reassuring number.
null,the PDF prints
N/A - not measured, and the reason lands indataGaps.Integration Case 3 asserts
null, not0. (verified)filtered by
severityFloor— an actively exploited medium outranks atheoretical critical, and the options-form help line says so explicitly.
for. The next period is derived from the deliverable's own cadence
(
recurrence.tsmonth-end semantics), never "period end + 30 days"; aquarterly deliverable is pinned by a unit test, a monthly one by integration
Case 2. An ad-hoc staff run with no occurrence falls back to one calendar
month and discloses that in
dataGaps. (verified)Portal
Label only — no generate button. The type stays out of
PORTAL_REPORT_TYPESand out of both portal-user allowlists in
reportGenerationService.ts(OD-10 = A).PortalRunDto['type']and the portalReportRunListunion are widened (the typelie the compiler could not see, since
portalRunListPredicatehas no typefilter);
ReportRunListnow separatesReportType(what a row can carry) fromGeneratableReportType(what has a button), so a later wave has to choose a siderather than inherit a button by accident. OD-12 gates visibility on delivery —
integration Case 5 asserts invisible +
renderRunPdfrefusal before delivery,listed + downloadable after.
The silent-failure this wave specifically guards
A type with no
buildReportPdfarm falls through torenderGenericReport,producing a plausible-looking PDF with the entire designed summary — exceptions
section included — dropped, and no unit test catches it. All three render
paths are now proven against the one stored snapshot: (verified)
renderRunPdf)vulnerabilityManagementEvidence.integration.test.tsCase 5 asserts the produced PDF's text contains the exceptions sectionexportReport)reportExport.vulnerabilityManagement.test.tsxbuildReportPdfopts —reportPdf.vulnerabilityManagement.test.tscovers the arm and the generic fall-throughRollout
M365_TENANT_SYNC_ENABLEDdependency. Unlike W03, W05 and W06 this wavereads no M365 table;
device_vulnerabilities/vulnerabilitiesare populatedby the agent and the NVD/MSRC/Apple/OSV feeds regardless of that flag. Do not
gate this release on a flag it does not need.
pnpm --filter @breeze/api reports:reprovision-portal-definitions(dry run, then
--apply) — orgs that enabled portal reports earlier willotherwise lack the new definition. (Not required for correctness of the
evidence path:
resolveManagedEvidenceDefinitioncreates it on first templateapply.) (inferred — the script was not run here)
2026-10-17-110600-report-type-vulnerability-management.sqlis anALTER TYPE ... ADD VALUE IF NOT EXISTSalone in its file, writes no rows, andis not added to
migrationRlsScope.test.ts's baseline. Applied twiceagainst a live DB: second run a clean no-op,
db:check-driftclean. (verified)registrations are untouched (
tenantCascade+tenant-export-policyintegration suites re-run green). (verified)
Localization caveat
The eight locale files localize the web UI only; the PDF renderer is English.
Verification
All run in the foreground on this branch. (verified)
apps/apifull unit suitepackages/sharedfull suiteapps/webfull suiteapps/portalfull suitetsc --noEmit— api, shared, web, portalastro check(web)pnpm lintpnpm db:check-driftvulnerabilityManagementEvidence.integration.test.tstenantCascade+tenant-export-policy+orgLifecycleFoundations+managedEvidenceFoundations+portalReportSelfServiceintegrationsecurityComplianceReportVulnerabilities.test.ts+ posture suitesTest stack torn down (
pnpm test-stack down, verified no Breeze compose project left running).Shared-file edits (all additive, one entry each)
db/schema/reports.ts,reportGenerationService.ts(+test),managedEvidenceRegistry.ts,validators/deliverableTemplates.ts,routes/reports/schemas.ts(+test),securityComplianceReportVulnerabilities.ts(+test),reportPdf.ts+index.ts,types/index.ts,utils/index.ts,portal/reportsSelfService.ts(+test),types/portalVisibility.ts, portalReportRunList.tsx(+test), six web reportcomponents,
apps/web/src/locales/*/reports.json.Three pre-existing assertions were bumped by exactly this wave's addition, not
loosened: the portal provisioning list (3 → 4 definitions), the curated
template-card count (6 → 7 buttons), and W01's deliberate
auto-evidence-emptyempty-state assertion, which W01 wrote for the first waveto flip.
🤖 Generated with Claude Code