d4df1e01df
Vertical slice over the Compliance Execution Graph: obligation_id -> accepted
controls -> required evidence -> status. NEVER auto-asserts fulfillment - with
no evidence collection wired (MVP), a mapped obligation is "not_assessed" and
every required evidence is "missing". Fail-closed: no id -> 400; unknown id ->
unknown_obligation; mapped-but-no-control -> unmapped; graph not loaded -> 503.
- ComplianceGraphHandlers (separate from the DB-backed ObligationsHandlers):
loads Registry join keys + accepted control mappings + evidence once at start.
- LoadComplianceGraph: candidate-path resolution across dev/container/test.
- Data plumbing: Dockerfile now COPYs data/{control_mappings,evidence_requirements,
obligations}; data/obligations/obligation_join_keys.json is a SYNCED COPY of the
repo-root Registry contract (re-sync on Registry growth).
- Table-driven handler test (mapped/unmapped/unknown/400 + no-fulfillment-claim).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
90 lines
3.3 KiB
Go
90 lines
3.3 KiB
Go
package ucca
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
// graphCallerRel resolves a path relative to THIS source file (build-time location), so the
|
|
// graph data is findable under `go test` (cwd = package dir) regardless of working directory.
|
|
// In a built container the source is gone, so cwd-relative candidates carry the load instead.
|
|
func graphCallerRel(rel string) string {
|
|
_, file, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return filepath.Join(filepath.Dir(file), rel)
|
|
}
|
|
|
|
// firstExisting returns the first candidate path that exists with the requested kind (dir vs
|
|
// file). Empty candidates (e.g. unset env overrides) are skipped.
|
|
func firstExisting(candidates []string, wantDir bool) string {
|
|
for _, p := range candidates {
|
|
if p == "" {
|
|
continue
|
|
}
|
|
info, err := os.Stat(p)
|
|
if err != nil || info.IsDir() != wantDir {
|
|
continue
|
|
}
|
|
return p
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// LoadComplianceGraph loads the file-backed Compliance Execution Graph: the Registry join-key
|
|
// contract (obligations/obligation_join_keys.json — owned by the Obligation session) + our
|
|
// curated, accepted control mappings + evidence requirements. Locations are resolved across
|
|
// three layouts: dev (cwd = ai-compliance-sdk/, canonical contract at ../obligations), container
|
|
// (WORKDIR /app, data/ copied in incl. a synced data/obligations/ copy) and `go test`
|
|
// (cwd = package dir, via graphCallerRel). Fail-closed: a missing/invalid source returns an
|
|
// error so the handler serves 503 — never a half-built graph.
|
|
//
|
|
// NOTE: data/obligations/obligation_join_keys.json is a SYNCED COPY of the repo-root contract
|
|
// (the canonical owner is the Obligation session). Re-sync it when the Registry grows; dev/test
|
|
// prefer the canonical repo-root path, only the container falls back to the copy.
|
|
func LoadComplianceGraph() (*ObligationJoinKeys, *ControlMappingSet, *EvidenceRequirementSet, error) {
|
|
joinPath := firstExisting([]string{
|
|
os.Getenv("BP_OBLIGATION_JOIN_KEYS"),
|
|
"../obligations/obligation_join_keys.json",
|
|
graphCallerRel("../../../obligations/obligation_join_keys.json"),
|
|
"data/obligations/obligation_join_keys.json",
|
|
graphCallerRel("../../data/obligations/obligation_join_keys.json"),
|
|
}, false)
|
|
if joinPath == "" {
|
|
return nil, nil, nil, fmt.Errorf("obligation_join_keys.json not found in any candidate path")
|
|
}
|
|
mapDir := firstExisting([]string{
|
|
os.Getenv("BP_CONTROL_MAPPINGS_DIR"),
|
|
"data/control_mappings",
|
|
graphCallerRel("../../data/control_mappings"),
|
|
}, true)
|
|
if mapDir == "" {
|
|
return nil, nil, nil, fmt.Errorf("control_mappings dir not found in any candidate path")
|
|
}
|
|
evDir := firstExisting([]string{
|
|
os.Getenv("BP_EVIDENCE_DIR"),
|
|
"data/evidence_requirements",
|
|
graphCallerRel("../../data/evidence_requirements"),
|
|
}, true)
|
|
if evDir == "" {
|
|
return nil, nil, nil, fmt.Errorf("evidence_requirements dir not found in any candidate path")
|
|
}
|
|
|
|
joins, err := LoadObligationJoinKeys(joinPath)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("load join keys (%s): %w", joinPath, err)
|
|
}
|
|
mappings, err := LoadControlMappings(mapDir)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("load control mappings (%s): %w", mapDir, err)
|
|
}
|
|
evidence, err := LoadEvidenceRequirements(evDir)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("load evidence (%s): %w", evDir, err)
|
|
}
|
|
return joins, mappings, evidence, nil
|
|
}
|