Add compliance frameworks, gap analysis, audits, and remediation features#134
Merged
Conversation
…T 800-53, RGPD, DORA, NIS2, SOX) Complete the "5. Conformité" target framework list. Each catalog is a plugin file (init()+register()) that auto-surfaces in GET /compliance/catalogs and the import modal — no handler or frontend change needed. - ISO/IEC 27005:2022 (19) — infosec risk management process activities - ISO 31000:2018 (22) — 8 principles + framework + process - NIST SP 800-53 Rev.5 (20) — the 20 control families - RGPD / EU 2016/679 (22) — key operational articles (FR descriptions) - DORA / EU 2022/2554 (19) — 5 pillars, key articles (FR) - NIS2 / EU 2022/2555 (12) — governance + art.21 measures + notification (FR) - SOX 2002 (10) — statutory sections + ITGC domains IDs/codes are each framework's public structure; descriptions are original summaries with a source citation per control. TestExpectedControlCounts locks each count against truncation.
GET /compliance/gap-analysis returns every unsatisfied control (not implemented / in progress) across the tenant's frameworks, with per-framework and overall roll-ups. Optional ?framework_id= scopes to one framework. - GetGapAnalysisUseCase reuses existing repo ports (ListFrameworks, ListControlsByFramework, CountEvidencesByFramework) — no new persistence. - Fully tenant-scoped; unknown/other-tenant framework → ErrNotFound. - Evidence counts folded in via one grouped query per framework (no N+1). - 3 use-case tests: all-frameworks roll-up, single-framework, unknown→404.
The "Voir les écarts" CTA on ComplianceScreen was a dead button — it now navigates to a real gap analysis page (/compliance/gap-analysis). - GapAnalysisPage: coverage gauge hero, per-framework filter chips, gaps grouped by framework with status badge + evidence count + source citation; 3 UI states (loading/error/empty = "100% covered"). - complianceService.getGapAnalysis + useGapAnalysis hook + typed GapAnalysis interfaces (no `any`; follow-up: add schema to openapi.yaml). - Route registered before compliance/:frameworkId so "gap-analysis" is never parsed as a framework id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the two missing "5. Conformité" sub-features as tenant-scoped aggregates
in Clean Architecture (domain → repo → use cases → handler → routes).
Audits ("Audits" — plan/execute/history):
- domain.ComplianceAudit (type: internal/external/certification/surveillance;
status: planned→in_progress→completed/cancelled; optional framework scope;
auditor, scope, summary, compliance-score snapshot, scheduled/completed dates).
- CRUD use cases; completing stamps completed_at once, reopening clears it.
- Routes /compliance/audits[/:id] gated by compliance:audits:read|write.
Remediation plans ("Plans de remédiation" — close a gap, assign, track):
- domain.RemediationPlan linked to a compliance control (the gap) and,
optionally, the audit that surfaced it; priority + status lifecycle,
assignee, due date. DISTINCT from risk Mitigations.
- Create validates the linked control belongs to the tenant (double
cross-tenant guard) and derives the framework from it; list enriches each
plan with its control code/name (cached, no N+1).
- Routes /compliance/remediations[/:id] gated by compliance:remediations:read|write.
One GormComplianceAuditRepository backs both; tenant_id filtered on every
query (forged id from another tenant → ErrNotFound). Both entities added to
AutoMigrate. 7 sqlite repo tests: create/get/list/update/delete + cross-tenant
isolation on both aggregates + control filter.
Surfaces the two new backend aggregates and turns Compliance into a hub. - AuditsPage (/compliance/audits): schedule an audit (type/framework/auditor/ dates/scope), inline status lifecycle (planned→in progress→completed/ cancelled), status filter chips, delete. Gated by compliance:audits:write. - RemediationPage (/compliance/remediations): plans with linked control code, priority + status badges, overdue highlighting, inline status change, delete. Gated by compliance:remediations:write. - CreateAuditDialog + CreateRemediationDialog reuse the dc.html modal primitives (ModalShell/Field/SelectField/TextArea/FooterButtons, now exported). - GapAnalysisPage: each gap gets a "Remédier" button that opens a remediation plan pre-linked to that control — closes the loop gap → remediation. - ComplianceScreen: hub nav row (Gap analysis / Audits / Remediation plans). - Service methods + useAudits/useRemediations hooks + typed interfaces (no any). tsc -b + vite build green.
writeAppError → HTTPStatusFromError only maps *AppError to a status; the bare domain.ErrNotFound sentinel fell through to 500. Return domain.NewNotFoundError (a *AppError, Code 404) from the audit/remediation Get/Update use cases and the repo RowsAffected==0 paths, and from gap-analysis on an unknown framework. Verified live: GET/DELETE a non-existent audit, PATCH a non-existent remediation, and gap-analysis with an unknown framework_id now all return 404. Repo tests still pass (errors.Is unwraps the AppError to ErrNotFound).
…ediation Update ROADMAP.md Module 12 (16 importable frameworks now; gap/audits/ remediation OK) and add CLAUDE.md sprint item 27 with the full "5. Conformité" build and live-verification notes.
New one-click action: POST /compliance/audits/:id/generate-remediations opens a remediation plan for every open gap under the audit's framework. - GenerateRemediationsFromAuditUseCase (deps: audit + remediation + compliance repos). Requires the audit to be framework-scoped (program-wide → 400). Priority derived from the gap: not_implemented → high, in_progress → medium. Idempotent: a control that already has a non-cancelled plan is skipped, so re-running never duplicates (returns created/skipped counts). - Route gated by compliance:remediations:write; 404 on unknown audit. - 4 use-case tests (gaps-only, skip active, program-wide 400, unknown 404). - Frontend: "Remédier" button per framework-scoped audit on AuditsPage → toast (created/skipped) → navigates to the remediation register.
Link a control to an equivalent control in another framework so satisfying one
signals coverage in the other ("cross-mapping entre référentiels").
Backend:
- domain.ControlMapping — tenant-scoped, undirected crosswalk between two
controls + MappingRelation (equivalent/partial/related). In AutoMigrate.
- ControlMappingRepository + Gorm impl: Exists checks BOTH directions,
tenant-scoped on every query, forged-id delete → not found.
- Use cases (application/compliance): Create (double cross-tenant guard — both
controls must be the tenant's; refuses self-link + duplicate either
direction), List (optionally by control, enriched with each side's
code/name/framework), Delete.
- Handler + routes GET/POST /compliance/control-mappings,
DELETE /compliance/control-mappings/:mappingId (static path before
/controls/:id — Fiber ordering). Gated read/update by control perms.
- Tests: repo (symmetric Exists, list all/by-control, tenant isolation + delete).
Frontend:
- types + service + useControlMappings hook.
- ControlMappingsSection in the control drawer: lists the other side of each
link, plus an add form (pick target framework → control → relation).
…PI + make frontend types contract-first The gap-analysis, audits, remediation and cross-mapping endpoints were live but only described by hand-written frontend types. Now they're in docs/openapi.yaml (schemas + paths, with required arrays so generated fields aren't all-optional), regenerated into src/types/openapi.generated.ts, and src/types/compliance.ts re-points every one of those types onto the generated schemas (deriving the enum aliases AuditType/AuditStatus/RemediationPriority/RemediationStatus/ MappingRelation from them) — matching the file's existing contract-first aliases. Only RemediationFilter (a query filter, not a body) stays hand-written. tsc -b + vite build green.
…drifted risks DDL) Two pre-existing test-only bugs, unrelated to production code: - payloads sent probability:4 (old 1–5 scale) which fails the handler's min=0,max=1 validation (Score Engine 0.0–1.0) → 400. Now 0.4. - the hand-written sqlite `risks` table had drifted: missing tenant_id plus ~15 columns the current domain.Risk INSERT/RETURNING touches (criticality, lifecycle_phase, slexaf, aro, source_cve_id, control_ids…) → "table risks has no column named tenant_id". DDL now matches the model. go test ./internal/handler/... green.
…, OpenAPI contract-first, TestRiskCRUDFlow fix Update ROADMAP.md §12 and CLAUDE.md (item 28) with the 5 attention points from item 27, all done + live-verified on 17/07: audit→remediation auto-generation, cross-framework control mappings, OpenAPI schemas/paths + regenerated contract- first frontend types, and the repaired long-failing TestRiskCRUDFlow.
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.
No description provided.