Skip to content

Add compliance frameworks, gap analysis, audits, and remediation features#134

Merged
alex-dembele merged 12 commits into
masterfrom
feature/compliance-management
Jul 17, 2026
Merged

Add compliance frameworks, gap analysis, audits, and remediation features#134
alex-dembele merged 12 commits into
masterfrom
feature/compliance-management

Conversation

@alex-dembele

Copy link
Copy Markdown
Member

No description provided.

alex-dembele and others added 12 commits July 16, 2026 19:03
…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.
@alex-dembele
alex-dembele merged commit f16f5f4 into master Jul 17, 2026
9 of 14 checks passed
@alex-dembele
alex-dembele deleted the feature/compliance-management branch July 17, 2026 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant