From 7029a3cfd73c6f3410b15db7b3b4c5486b15f001 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 3 Feb 2026 15:15:12 +0100 Subject: [PATCH 01/52] [WIP] ADR EC integration --- .../00012-enterprise-contract-integration.md | 500 ++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 docs/adrs/00012-enterprise-contract-integration.md diff --git a/docs/adrs/00012-enterprise-contract-integration.md b/docs/adrs/00012-enterprise-contract-integration.md new file mode 100644 index 000000000..568ef7b31 --- /dev/null +++ b/docs/adrs/00012-enterprise-contract-integration.md @@ -0,0 +1,500 @@ +# 00012. Enterprise Contract Integration + +Date: 2026-02-03 + +## Status + +PROPOSED + +## Context + +Organizations need to enforce security and compliance policies across their software supply chain to ensure that all software components meet specific criteria for licensing, vulnerabilities, and provenance. Currently, Trustify provides SBOM storage, analysis, and vulnerability tracking, but lacks automated policy enforcement capabilities. + +Manual validation of SBOMs against organizational policies is: + +- Time-consuming and error-prone +- Inconsistent across teams and projects +- Difficult to scale across large numbers of SBOMs +- Lacks audit trails and historical compliance tracking +- Cannot provide real-time feedback on policy violations + +### Requirements + +Users need the ability to: + +1. Automatically validate SBOMs against organizational policies +2. Define and manage multiple policy configurations +3. View compliance status and violation details for each SBOM +4. Track compliance history over time +5. Generate detailed compliance reports for auditing +6. Receive actionable feedback on policy violations + +### Acceptance Criteria + +For this integration to be considered successful, the following criteria must be met: + +1. **Conforma Integration**: Trustify backend integrates with Conforma CLI to execute policy checks against stored SBOMs +2. **Policy Configuration**: Users/administrators can define or reference specific Enterprise Contract policies to enforce via the UI or API +3. **Result Persistence**: Conforma check output (Pass/Fail status and specific violations) is parsed and saved as structured properties on the corresponding SBOM record in the database +4. **Error Handling**: System gracefully handles execution failures (e.g., malformed SBOMs, policy timeouts, Conforma unavailable) and returns appropriate error messages +5. **Visibility**: Users can retrieve the compliance status of an SBOM via the Trustify API or UI, including historical validation results + +### Available Solutions + +**Enterprise Contract (Conforma)** is an open-source policy enforcement tool that: + +- Validates SBOMs against configurable policies +- Supports policies for licensing, vulnerabilities, and provenance +- Provides structured output (JSON) for programmatic consumption +- Integrates with CI/CD pipelines +- Is open source +- Is actively maintained by Red Hat + +**Current state**: Conforma provides a CLI tool but no REST API yet. Future API availability is expected but timeline is undetermined. + +## Decision + +We will integrate Enterprise Contract (Conforma) into Trustify as an optional validation service that: + +1. **Executes Conforma CLI** via async process spawning using `tokio::process::Command` +2. **Stores validation results** as structured data in PostgreSQL with foreign key relationship to SBOMs +3. **Persists detailed reports** in object storage (S3/Minio) for audit trails +4. **Exposes REST API endpoints** for triggering validation and retrieving results +5. **Supports policy configuration** through database-backed policy references + +### System Architecture + +```mermaid +C4Context + title Enterprise Contract Integration - System Context + + Person(user, "Trustify User", "Analyst validating SBOMs") + + System(trustify, "Trustify", "RHTPA") + + System_Ext(conforma, "Conforma", "Enterprise Contract policy validation tool") + System_Ext(s3, "S3", "S3/Minio Storage") + System_Ext(policyRepo, "Policy Repository", "Git repository or storage containing EC policies") + + Rel(user, trustify, "Request Compliance
View compliance status", "API/GUI") + Rel(trustify, conforma, "Executes policy validation", "Spawn Process") + Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") + Rel(trustify, s3, "I3s", S3/Minio Storager, trustify, $offsetX="-30", $offsetY="+20") + + UpdateRelStyle(trustify, conforma, $offsetX="-40") + UpdateRelStyle(user, trustify, $offsetX="-50", $offsetY="20") + + UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1") +``` + +### Component Diagram - EC Validation Module + +```mermaid +C4Container + title Enterprise Contract Integration - Container Diagram + + Person(user, "Trustify User", "Software engineer or security analyst") + + + Container_Boundary(trustify, "Trustify System") { + Container(webui, "Web UI", "React/TypeScript", "Trustify GUI") + Container(api, "API Gateway", "Actix-web", "REST API endpoints for SBOM
and compliance operations") + ContainerDb(postgres, "PostgreSQL", "DBMS", "Stores SBOM metadata, relationships,
and EC validation results") + Container(ecModule, "EC Validation Module", "Rust", "Orchestrates Conforma CLI
execution and result persistence") + ContainerDb(s3, "Object Storage", "S3/Minio", "Stores SBOM documents and EC reports") + Container(storage, "Storage Service", "Rust", "Manages document storage
(SBOMs, policies results)") + } + + Container_Boundary(conforma, "Conforma System") { + System_Ext(conforma, "Conforma CLI", "External policy validation tool") + System_Ext(policyRepo, "Policy Repository", "Git repository with EC policies") + } + + Rel(user, webui, "Views compliance status", "HTTPS") + Rel(user, api, "Views compliance status", "RESTful") + Rel(webui, api, "API calls", "JSON/HTTPS") + Rel(api, ecModule, "Triggers validation", "Function call") + Rel(ecModule, conforma, "Executes validation", "CLI/Process") + Rel(ecModule, postgres, "Saves validation
results", "SQL") + Rel(ecModule, storage, "Stores EC reports", "Function call") + Rel(storage, s3, "Persists reports", "S3 API") + Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") + + UpdateRelStyle(user, webui, $offsetX="-60", $offsetY="30") + UpdateRelStyle(user, api, $offsetX="-60", $offsetY="-50") + UpdateRelStyle(webui, api, $offsetX="-40", $offsetY="10") + UpdateRelStyle(ecModule, postgres, $offsetX="-40", $offsetY="10") + UpdateRelStyle(storage, s3, $offsetX="-40", $offsetY="10") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3") +``` + +### Container Diagram + +```mermaid +C4Component + title EC Validation Module - Component Diagram + + Container(api, "API Gateway", "Actix-web", "REST API for EC operations") + + Container_Boundary(ecModule, "EC Validation Module") { + Component(ecEndpoints, "EC Endpoints", "Actix-web handlers", "REST endpoints for
validation operations") + Component(conformaExecutor, "Conforma Executor", "Async Process", "Executes Conforma CLI
and captures output") + Component(policyManager, "Policy Manager", "Configuration", "Manages EC policy
references and configuration") + Component(ecService, "EC Service", "Business logic", "Orchestrates validation workflow") + Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output
into structured data") + Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results to database") + Component(SBOMModel, "SBOM Model", "Data structure", "API models for validation
requests/responses") + } + + Container_Boundary(external, "External Systems") { + System_Ext(conforma, "Conforma CLI", "Enterprise Contract
validation tool") + System_Ext(s3, "S3 Object Storage", "Stores SBOM documents and reports") + } + + Container_Boundary(postgres, "database") { + ContainerDb(postgres, "PostgreSQL", "Database", "Stores validation results") + } + + + Rel(api, ecEndpoints, "POST /sboms/{id}/ec-validate,
GET /sbms/{id}/ec-report", "JSON/HTTPS") + Rel(ecEndpoints, ecService, "validate_sbom()
get_ec_report()", "Function call") + Rel(ecService, policyManager, "get_policy_config()", "Function call") + Rel(ecService, conformaExecutor, "request_validation()", "Function call") + Rel(conformaExecutor, conforma, "Runs CLI command", "Process spawn") + Rel(resultParser, SBOMModel, "Creates models", "Data mapping") + Rel(ecService, resultPersistence, "save_results()", "Function call") + Rel(resultPersistence, postgres, "INSERT validation_results", "SQL") + Rel(ecService, s3, "Store EC report", "S3 API") + + UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") +``` + +### Data Model + +Two new tables: + +**`ec_policies`** - Store policy references and metadata (not the policies themselves) + +> **Note**: This table stores references to external policies (Git URLs, OCI registries, etc.) and local configuration metadata. The actual policy definitions remain in their external repositories (GitHub, GitLab, etc.) and are fetched by Conforma at validation time. + +- `id` (UUID, PK) +- `name` (VARCHAR, unique) - User-friendly name for this policy configuration +- `description` (TEXT) - Description of what this policy enforces +- `policy_ref` (VARCHAR) - **External reference**: Git URL, OCI registry, or file path where policy is stored +- `policy_type` (VARCHAR) - 'git', 'oci', 'local' (indicates how Conforma should fetch the policy) +- `configuration` (JSONB) - Additional Conforma parameters (branch, tag, auth credentials, etc.) +- `created_at`, `updated_at` (TIMESTAMP) + +**`ec_validation_results`** - Store validation outcomes + +- `id` (UUID, PK) +- `sbom_id` (UUID, FK → sbom) +- `policy_id` (UUID, FK → ec_policies) +- `status` (VARCHAR) - 'pass', 'fail', 'error' +- `violations` (JSONB) - Structured violation data +- `summary` (JSONB) - Statistics (total checks, passed, failed, warnings) +- `report_url` (VARCHAR) - S3 URL to detailed report +- `executed_at` (TIMESTAMP) +- `execution_duration_ms` (INTEGER) +- `conforma_version` (VARCHAR) +- `error_message` (TEXT) + +### API Endpoints + +``` +POST /api/v2/sboms/{id}/ec-validate # Trigger validation +GET /api/v2/sboms/{id}/ec-report # Get latest validation result +GET /api/v2/sboms/{id}/ec-report/history # Get validation history +GET /api/v2/ec/report/{result_id} # Download detailed report +POST /api/v2/ec/policies # Create policy reference (admin) +GET /api/v2/ec/policies # List policy references +GET /api/v2/ec/policies/{id} # Get policy reference details +PUT /api/v2/ec/policies/{id} # Update policy reference (admin) +DELETE /api/v2/ec/policies/{id} # Delete policy reference (admin) +``` + +### Implementation Approach + +**Phase 1: Backend - CLI Integration** + +- Use async process spawning with proper timeout handling +- Stream stdout/stderr for error capture +- Parse Conforma JSON output into Rust structs +- Handle exit codes (0=pass, 1=fail, 2=error) +- Implement REST API endpoints for validation operations +- Add database schema and migrations +- Create service layer for orchestration + +**Phase 2: Frontend - GUI Development** + +- **SBOM Details View Enhancements**: + - Add "Validate with EC" button/action in SBOM detail page + - Display compliance status badge (Pass/Fail/Error) prominently + - Show latest validation timestamp and policy used +- **Validation Results Display**: + - Create dedicated compliance tab/section in SBOM view + - Display validation summary (total checks, passed, failed, warnings) + - List violations with severity, description, and remediation hints + - Provide expandable/collapsible violation details +- **Validation History**: + - Show historical validation results in timeline view + - Allow filtering by policy, status, and date range + - Display trend charts for compliance over time +- **Policy Management UI** (Admin): + - Policy reference configuration page (store Git URLs, OCI refs, etc.) + - List view of available policy references with descriptions and types + - Policy selection dialog when triggering validation + - Form validation for policy references (validate Git URL format, test connectivity) + - Display policy source (GitHub, GitLab, OCI registry) with clickable links +- **Report Download**: + - Download button for detailed JSON/HTML reports + - Preview detailed report in modal or new page + - Export options (JSON, PDF, HTML) +- **Notifications & Feedback**: + - Loading indicators during validation execution + - Toast notifications for validation completion + - Error messages with actionable guidance + - Progress tracking for long-running validations + +**Phase 3: Future API Migration** + +When Conforma provides a REST API: + +- Implement adapter pattern to switch between CLI and API +- Add feature flag `ec-api-mode` for gradual migration +- Maintain backward compatibility with CLI mode +- Deprecate CLI integration after stable API adoption +- No GUI changes required (transparent backend switch) + +### Module Structure + +``` +modules/ec/ +├── Cargo.toml +└── src/ + ├── lib.rs + ├── endpoints/ + │ └── mod.rs # REST endpoints + ├── model/ + │ ├── mod.rs + │ ├── policy.rs # Policy API models + │ └── validation.rs # Validation result models + ├── service/ + │ ├── mod.rs + │ ├── ec_service.rs # Main orchestration + │ ├── policy_manager.rs # Policy configuration + │ ├── executor.rs # Conforma CLI execution + │ └── result_parser.rs # Output parsing + └── error.rs # Error types +``` + +### Technical Considerations + +**Conforma CLI Execution** + +- Use `tokio::process::Command` for async execution to avoid blocking the runtime +- Stream stdout/stderr for real-time monitoring and logging +- Set execution timeouts (default: 5 minutes, configurable per policy) +- Handle large SBOM files efficiently by streaming to temporary files +- Capture exit codes for error handling (0=pass, 1=fail, 2+=error) +- Sanitize all CLI arguments to prevent injection attacks (use process args array, not shell strings) + +**Policy Management** + +- Support multiple policy types: Git repositories, OCI registries, local file paths +- Cache policy files locally to avoid repeated fetches from external sources +- Validate policy references before execution (check URL format, test connectivity) +- Track policy version/commit when storing validation results for reproducibility +- Support authentication for private policy repositories (tokens, SSH keys) + +**Result Storage** + +- Store structured violations in JSONB for efficient querying (filter by violation type, severity) +- Keep detailed reports in object storage (S3/Minio) to minimize database size +- Implement retention policies for old validation results (configurable, e.g., 90 days) +- Index frequently queried fields (sbom_id, status, executed_at) for performance +- Consider result aggregation for analytics dashboards + +**Error Handling** + +- Distinguish between validation failures (policy violations) and execution errors (CLI crashes) +- Provide actionable error messages to users (e.g., "Policy file not found at URL") +- Implement retry logic for transient failures (network timeouts, temporary service unavailability) +- Log all execution details for debugging (command, arguments, duration, exit code) +- Gracefully degrade when Conforma is unavailable (show cached results, queue for retry) + +**Security** + +- Validate and sanitize all user inputs (policy URLs, SBOM IDs) +- Restrict policy sources to trusted domains (configurable allowlist) +- Store authentication credentials securely (encrypted, never in logs) +- Implement proper authorization checks (only admins can modify policies) +- Audit log all validation executions and policy modifications + +**Performance** + +- Limit concurrent Conforma executions using semaphore (default: 5 concurrent) +- Implement queueing for validation requests during high load +- Cache policy files to reduce external network calls +- Use connection pooling for database operations +- Monitor execution times and resource usage for capacity planning +- Consider horizontal scaling for validation workloads + +## Consequences + +### Positive + +1. **Automated Policy Enforcement**: Organizations can automatically validate SBOMs without manual review +2. **Audit Trail**: Complete history of compliance checks stored in database +3. **Flexibility**: Support multiple policy configurations for different requirements +4. **Integration Ready**: REST API enables integration with CI/CD pipelines +5. **Scalability**: Async execution prevents blocking on long-running validations +6. **Extensibility**: Module design allows future enhancement (webhooks, notifications, etc.) +7. **Open Source**: Conforma is open-source and actively maintained + +### Negative + +1. **External Dependency**: Requires Conforma CLI to be installed on Trustify servers +2. **Process Overhead**: Spawning external processes has performance implications +3. **Error Handling Complexity**: Must handle CLI failures, timeouts, and malformed output +4. **Version Management**: Need to track Conforma version compatibility +5. **Resource Usage**: Multiple concurrent validations may consume significant resources +6. **No Native API**: Until Conforma provides an API the CLI integration is less efficient than native API integration. + +### Risks and Mitigations + +| Risk | Mitigation | +| ----------------------------------- | --------------------------------------------------------------- | +| Conforma CLI unavailable/crashes | Implement health checks, graceful error handling, retry logic | +| Long execution times block requests | Use async execution with configurable timeouts (default: 5 min) | +| Large SBOMs cause memory issues | Stream SBOM to temp file, pass file path to Conforma | +| CLI injection attacks | Sanitize all inputs, use process args array (not shell strings) | +| Version incompatibility | Document required Conforma version, validate on startup | +| Storage costs for reports | Implement retention policies, compress reports | + +### Migration Path + +When Conforma REST API becomes available: + +1. Implement API client alongside CLI executor +2. Add configuration flag to select execution mode +3. Gradually migrate workloads to API mode +4. Deprecate CLI mode after stability period +5. Remove CLI executor in future major version + +### Performance Considerations + +- **Concurrent Limits**: Implement semaphore to limit parallel Conforma executions (default: 5) +- **Timeout**: Default 5-minute timeout, configurable per policy +- **Caching**: Cache policy files to avoid repeated Git fetches +- **Async**: All operations non-blocking using Tokio runtime +- **Streaming**: Stream results incrementally for large reports + +## Alternatives Considered + +### 1. In-Process Policy Engine + +**Pros**: No external dependencies, faster execution, native Rust integration + +**Cons**: Requires reimplementing Enterprise Contract logic, maintenance burden, divergence from upstream + +**Verdict**: Rejected - maintaining policy engine parity with EC would be significant effort + +### 2. Webhook-based Integration + +**Pros**: Decoupled, scalable, easier to manage separate service + +**Cons**: Additional infrastructure, network latency, complexity for simple use case + +**Verdict**: Deferred - could be future enhancement for large-scale deployments + +### 3. Embedded WASM Module + +**Pros**: Sandboxed, portable, no process spawning + +**Cons**: Conforma not available as WASM, would require major upstream changes + +**Verdict**: Rejected - not feasible with current Conforma implementation + +### 4. Batch Processing Queue + +**Pros**: Better resource management, retry logic, priority handling + +**Cons**: Adds complexity, requires queue infrastructure (Redis/RabbitMQ) + +**Verdict**: Deferred - implement if demand increases, start with simple async execution + +## References + +- [Enterprise Contract (Conforma) GitHub](https://github.com/enterprise-contract/ec-cli) +- [Conforma CLI Documentation](https://github.com/enterprise-contract/ec-cli) +- [Design Document](../design/enterprise-contract-integration.md) +- [ADR-00005: Upload API for UI](./00005-ui-upload.md) - Similar async processing pattern +- [ADR-00001: Graph Analytics](./00001-graph-analytics.md) - Database query patterns + +## Implementation Tracking + +### Backend Tasks + +- [ ] Create module structure under `modules/ec` +- [ ] Implement database migrations for new tables (`ec_policies`, `ec_validation_results`) +- [ ] Build Conforma CLI executor with async process handling +- [ ] Create result parser for JSON output +- [ ] Implement policy manager service +- [ ] Implement EC service orchestration layer +- [ ] Add REST API endpoints (validation, results, policies) +- [ ] Write unit and integration tests +- [ ] Add OpenAPI documentation +- [ ] Write deployment documentation (Conforma installation) +- [ ] Add monitoring and metrics (execution times, success rates) +- [ ] Implement error handling and retry logic +- [ ] Add authentication/authorization checks + +### Frontend Tasks + +- [ ] Add "Validate with EC" button to SBOM detail page +- [ ] Create compliance status badge component (Pass/Fail/Error) +- [ ] Implement validation results display with summary statistics +- [ ] Build violations list component with expandable details +- [ ] Create validation history timeline view +- [ ] Add policy selectreference management UI (admin pages) + - [ ] Policy reference list view with search/filter (shows name, external URL, type) + - [ ] Policy reference create/edit form (Git URL, OCI ref, auth config) + - [ ] Policy reference delete confirmation + - [ ] Test policy connectivity button (validate URL is reachable) + - [ ] Policy delete confirmation +- [ ] Add report download functionality (JSON/HTML) +- [ ] Create detailed report preview modal +- [ ] Implement loading indicators for validation execution +- [ ] Add toast notifications for validation completion/errors +- [ ] Create compliance trend charts (optional, if analytics desired) +- [ ] Add help tooltips and documentation links +- [ ] Ensure responsive design for mobile/tablet views +- [ ] Add accessibility features (ARIA labels, keyboard navigation) + +## Open Questions + +1. **Policy Storage & Caching**: Should policies be pulled from external sources (OCI registry, Git repository) at runtime, or should Trustify cache them locally? Should we rely on Conforma's built-in caching or implement our own? How to handle cache invalidation and TTL for cached policies? + +2. **Trigger Mechanism**: Should the EC validation run automatically immediately upon SBOM upload, or will this be a manually triggered/scheduled process? Should users be able to configure auto-validation per SBOM or globally? + +3. **Data Granularity**: Do we store the full raw JSON output from Conforma, or only a summary (Pass/Fail) and a list of specific policy violations to save database space? What's the trade-off between storage cost and audit completeness? + +4. **Conforma Versioning**: Which version of the Conforma CLI are we targeting initially, and how will we handle updates to the policy engine? Should we support multiple Conforma versions concurrently or enforce a single version? + +5. **Policy Source Security**: Should we validate/verify Git repository signatures for policy sources? How to handle private repositories requiring authentication (tokens, SSH keys)? + +6. **Multi-tenancy**: How to isolate policy references per organization in shared Trustify deployments? Should each organization have separate policy namespaces? + +7. **Rate Limiting**: Should we limit validation requests per user/organization to prevent resource exhaustion? What are reasonable limits? + +8. **Notifications**: Should validation results trigger notifications (email, webhook, Slack)? Should notifications be configurable per policy or per SBOM? + +9. **Automatic Re-validation**: Should we re-validate SBOMs when policy definitions are updated in their external repositories? How to detect policy changes (polling, webhooks)? + +10. **UI/UX**: Where in the UI should compliance status be most prominently displayed? Should we show compliance badges on SBOM list views or only in detail views? + +11. **Default Policies**: Should there be system-wide default policy references that apply to all SBOMs unless overridden? How to handle precedence (user-level vs org-level vs system-level)? From 716b95759eaec87736c029c5dcf01af1cb92d99a Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 4 Feb 2026 15:22:17 +0100 Subject: [PATCH 02/52] Fix typos --- .../00012-enterprise-contract-integration.md | 100 +++++++++++++++++- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/docs/adrs/00012-enterprise-contract-integration.md b/docs/adrs/00012-enterprise-contract-integration.md index 568ef7b31..7cf7dd544 100644 --- a/docs/adrs/00012-enterprise-contract-integration.md +++ b/docs/adrs/00012-enterprise-contract-integration.md @@ -79,7 +79,7 @@ C4Context Rel(user, trustify, "Request Compliance
View compliance status", "API/GUI") Rel(trustify, conforma, "Executes policy validation", "Spawn Process") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") - Rel(trustify, s3, "I3s", S3/Minio Storager, trustify, $offsetX="-30", $offsetY="+20") + Rel(trustify, s3, "3s", S3/Minio Storager, trustify, $offsetX="-30", $offsetY="+20") UpdateRelStyle(trustify, conforma, $offsetX="-40") UpdateRelStyle(user, trustify, $offsetX="-50", $offsetY="20") @@ -102,7 +102,7 @@ C4Container ContainerDb(postgres, "PostgreSQL", "DBMS", "Stores SBOM metadata, relationships,
and EC validation results") Container(ecModule, "EC Validation Module", "Rust", "Orchestrates Conforma CLI
execution and result persistence") ContainerDb(s3, "Object Storage", "S3/Minio", "Stores SBOM documents and EC reports") - Container(storage, "Storage Service", "Rust", "Manages document storage
(SBOMs, policies results)") + Container(storage, "Storage Service", "Rust", "Manages document storage
(SBOMs, policy results)") } Container_Boundary(conforma, "Conforma System") { @@ -157,7 +157,7 @@ C4Component } - Rel(api, ecEndpoints, "POST /sboms/{id}/ec-validate,
GET /sbms/{id}/ec-report", "JSON/HTTPS") + Rel(api, ecEndpoints, "POST /sboms/{id}/ec-validate,
GET /sboms/{id}/ec-report", "JSON/HTTPS") Rel(ecEndpoints, ecService, "validate_sbom()
get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") Rel(ecService, conformaExecutor, "request_validation()", "Function call") @@ -172,6 +172,96 @@ C4Component UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` +```mermaid +sequenceDiagram + autonumber + actor User + participant UI as Trustify UI + participant API as Trustify API + participant VS as Validation Service + participant PM as Policy Manager + participant DB as PostgreSQL + participant S3 as Object Storage + participant Conf as Conforma CLI + + User->>UI: Request SBOM validation for policy + UI->>API: POST /api/v2/sbom/{sbom_id}/validate + Note over UI,API: Request body: {policy_id} + + API->>VS: validate_sbom_against_policy(sbom_id, policy_id) + + rect rgb(42, 48, 53) + Note over VS,PM: Policy Resolution Phase + VS->>PM: get_policy_configuration(policy_id) + PM->>DB: SELECT * FROM ec_policies WHERE id = ? + DB-->>PM: Policy configuration + alt Policy not found + PM-->>VS: Error: PolicyNotFound + VS-->>API: 404 Not Found + API-->>UI: Policy not found error + UI-->>User: Display error: "Policy does not exist" + end + PM-->>VS: PolicyConfig {name, policy_ref, version} + end + + rect rgb(68, 66, 62) + Note over VS,S3: SBOM Retrieval Phase + VS->>DB: SELECT * FROM sbom WHERE id = ? + DB-->>VS: SBOM metadata + alt SBOM not found + VS-->>API: 404 Not Found + API-->>UI: SBOM not found error + UI-->>User: Display error: "SBOM does not exist" + end + + VS->>S3: retrieve_sbom_document(sbom_id) + S3-->>VS: SBOM document (JSON/XML) + end + + rect rgb(42, 48, 53) + Note over VS,Conf: Validation Execution Phase + VS->>VS: Create temp files for SBOM and policy + VS->>Conf: spawn: conforma validate
--policy={policy_ref}
--sbom={sbom_file}
--output=json + + alt Validation passes + Conf-->>VS: Exit code: 0
JSON: {result: "PASS", violations: []} + VS->>VS: Parse validation results + VS->>DB: INSERT INTO ec_validation_results
(sbom_id, policy_id, status='passed',
violations=[], timestamp) + DB-->>VS: result_id + VS->>S3: store_validation_report(result_id, full_json) + S3-->>VS: report_url + VS->>DB: UPDATE ec_validation_results
SET report_url = ? + DB-->>VS: Updated + VS-->>API: ValidationResult {status: "passed",
violations: [], report_url} + API-->>UI: 200 OK {passed: true, violations: 0} + UI-->>User: ✓ SBOM passes policy validation + + else Validation fails with violations + Conf-->>VS: Exit code: 1
JSON: {result: "FAIL",
violations: [{rule, severity, message}]} + VS->>VS: Parse validation results + VS->>DB: INSERT INTO ec_validation_results
(sbom_id, policy_id, status='failed',
violations=json, timestamp) + DB-->>VS: result_id + VS->>S3: store_validation_report(result_id, full_json) + S3-->>VS: report_url + VS->>DB: UPDATE ec_validation_results
SET report_url = ? + DB-->>VS: Updated + VS-->>API: ValidationResult {status: "failed",
violations: [...], report_url} + API-->>UI: 200 OK {passed: false, violations: [...]} + UI-->>User: ✗ SBOM violates policy
Show violation details + + else Conforma execution error + Conf-->>VS: Exit code: 2
stderr: "Policy file not found" + VS->>DB: INSERT INTO ec_validation_results
(sbom_id, policy_id, status='error',
error_message=stderr) + DB-->>VS: result_id + VS-->>API: Error: ValidationExecutionFailed + API-->>UI: 500 Internal Server Error + UI-->>User: Display error: "Validation failed to execute" + end + end + + VS->>VS: Cleanup temp files +``` + ### Data Model Two new tables: @@ -409,7 +499,7 @@ When Conforma REST API becomes available: **Cons**: Additional infrastructure, network latency, complexity for simple use case -**Verdict**: Deferred - could be future enhancement for large-scale deployments +**Verdict**: Deferred - could be future enhancements for large-scale deployments ### 3. Embedded WASM Module @@ -460,7 +550,7 @@ When Conforma REST API becomes available: - [ ] Implement validation results display with summary statistics - [ ] Build violations list component with expandable details - [ ] Create validation history timeline view -- [ ] Add policy selectreference management UI (admin pages) +- [ ] Add policy reference management UI (admin pages) - [ ] Policy reference list view with search/filter (shows name, external URL, type) - [ ] Policy reference create/edit form (Git URL, OCI ref, auth config) - [ ] Policy reference delete confirmation From 8a4d3f8aabe2002b6b2c61ebe2f7a16d642b0b94 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Mon, 9 Feb 2026 15:39:33 +0100 Subject: [PATCH 03/52] Fix broken/missing/redundant links;more details --- ...-integration.md => 00014-enterprise-contract-integration.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/adrs/{00012-enterprise-contract-integration.md => 00014-enterprise-contract-integration.md} (99%) diff --git a/docs/adrs/00012-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md similarity index 99% rename from docs/adrs/00012-enterprise-contract-integration.md rename to docs/adrs/00014-enterprise-contract-integration.md index 7cf7dd544..5ca3ea1a2 100644 --- a/docs/adrs/00012-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -1,4 +1,4 @@ -# 00012. Enterprise Contract Integration +# 00014. Enterprise Contract Integration Date: 2026-02-03 From 3d649d90c7a699cb7e495e7fee825c0d4e3b74cd Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Mon, 9 Feb 2026 16:09:54 +0100 Subject: [PATCH 04/52] User realistic parameters for conforma CLI --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 5ca3ea1a2..64f463576 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -221,7 +221,7 @@ sequenceDiagram rect rgb(42, 48, 53) Note over VS,Conf: Validation Execution Phase VS->>VS: Create temp files for SBOM and policy - VS->>Conf: spawn: conforma validate
--policy={policy_ref}
--sbom={sbom_file}
--output=json + VS->>Conf: spawn: conforma validate
input --file "$1" --policy
--output json --show-successes
--info alt Validation passes Conf-->>VS: Exit code: 0
JSON: {result: "PASS", violations: []} From 6a5069b4ccd2b29996fc0e57dda6b6421e9d8405 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Mon, 9 Feb 2026 16:12:29 +0100 Subject: [PATCH 05/52] Offset link text --- docs/adrs/00014-enterprise-contract-integration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 64f463576..cfd361368 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -168,6 +168,7 @@ C4Component Rel(ecService, s3, "Store EC report", "S3 API") UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") + UpdateRelStyle(ecService, policyManager, $offsetX="-50", $offsetY="-50") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` From 935f3a98976a17a03bf65c7f44b408e4444f2183 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Mon, 16 Feb 2026 10:10:31 +0100 Subject: [PATCH 06/52] Consolidate trade-offs --- .../00014-enterprise-contract-integration.md | 51 +++++-------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index cfd361368..dfd4ca703 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -79,7 +79,7 @@ C4Context Rel(user, trustify, "Request Compliance
View compliance status", "API/GUI") Rel(trustify, conforma, "Executes policy validation", "Spawn Process") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") - Rel(trustify, s3, "3s", S3/Minio Storager, trustify, $offsetX="-30", $offsetY="+20") + Rel(trustify, s3, "Stores reports", "S3 API") UpdateRelStyle(trustify, conforma, $offsetX="-40") UpdateRelStyle(user, trustify, $offsetX="-50", $offsetY="20") @@ -446,43 +446,19 @@ modules/ec/ 6. **Extensibility**: Module design allows future enhancement (webhooks, notifications, etc.) 7. **Open Source**: Conforma is open-source and actively maintained -### Negative +### Trade-offs and Risks -1. **External Dependency**: Requires Conforma CLI to be installed on Trustify servers -2. **Process Overhead**: Spawning external processes has performance implications -3. **Error Handling Complexity**: Must handle CLI failures, timeouts, and malformed output -4. **Version Management**: Need to track Conforma version compatibility -5. **Resource Usage**: Multiple concurrent validations may consume significant resources -6. **No Native API**: Until Conforma provides an API the CLI integration is less efficient than native API integration. - -### Risks and Mitigations - -| Risk | Mitigation | -| ----------------------------------- | --------------------------------------------------------------- | -| Conforma CLI unavailable/crashes | Implement health checks, graceful error handling, retry logic | -| Long execution times block requests | Use async execution with configurable timeouts (default: 5 min) | -| Large SBOMs cause memory issues | Stream SBOM to temp file, pass file path to Conforma | -| CLI injection attacks | Sanitize all inputs, use process args array (not shell strings) | -| Version incompatibility | Document required Conforma version, validate on startup | -| Storage costs for reports | Implement retention policies, compress reports | - -### Migration Path - -When Conforma REST API becomes available: - -1. Implement API client alongside CLI executor -2. Add configuration flag to select execution mode -3. Gradually migrate workloads to API mode -4. Deprecate CLI mode after stability period -5. Remove CLI executor in future major version - -### Performance Considerations - -- **Concurrent Limits**: Implement semaphore to limit parallel Conforma executions (default: 5) -- **Timeout**: Default 5-minute timeout, configurable per policy -- **Caching**: Cache policy files to avoid repeated Git fetches -- **Async**: All operations non-blocking using Tokio runtime -- **Streaming**: Stream results incrementally for large reports +| Trade-off / Risk | Impact | Mitigation | +| ------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------- | +| External CLI dependency | Requires Conforma installed on servers | Health checks, graceful error handling, retry logic | +| Process spawning overhead | Performance implications per validation | Async execution with configurable timeouts (default: 5 min) | +| Error handling complexity | CLI failures, timeouts, malformed output | Distinguish validation failures from execution errors; actionable messages | +| Version management | Conforma version compatibility | Document required version, validate on startup | +| Resource usage under load | Concurrent validations consume resources | Semaphore limits (default: 5), queueing, monitoring | +| No native API yet | CLI less efficient than REST integration | Adapter pattern for future API migration (see Phase 3) | +| Large SBOMs cause memory issues | Out-of-memory during validation | Stream SBOM to temp file, pass file path to Conforma | +| CLI injection attacks | Security vulnerability | Sanitize all inputs, use process args array (not shell strings) | +| Storage costs for reports | Growing storage over time | Retention policies, report compression | ## Alternatives Considered @@ -556,7 +532,6 @@ When Conforma REST API becomes available: - [ ] Policy reference create/edit form (Git URL, OCI ref, auth config) - [ ] Policy reference delete confirmation - [ ] Test policy connectivity button (validate URL is reachable) - - [ ] Policy delete confirmation - [ ] Add report download functionality (JSON/HTML) - [ ] Create detailed report preview modal - [ ] Implement loading indicators for validation execution From cd0a49dd63bc2a24f360a0459e84da3bcb2e6b7c Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Feb 2026 10:20:48 +0100 Subject: [PATCH 07/52] Consolidate, remove redundancy, more focus on details --- .../00014-enterprise-contract-integration.md | 549 ++++++------------ 1 file changed, 175 insertions(+), 374 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index dfd4ca703..5036577b5 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -8,15 +8,9 @@ PROPOSED ## Context -Organizations need to enforce security and compliance policies across their software supply chain to ensure that all software components meet specific criteria for licensing, vulnerabilities, and provenance. Currently, Trustify provides SBOM storage, analysis, and vulnerability tracking, but lacks automated policy enforcement capabilities. +Trustify provides SBOM storage, analysis, and vulnerability tracking but lacks automated policy enforcement. Organizations need to validate SBOMs against security and compliance policies (licensing, vulnerabilities, provenance) without relying on manual, inconsistent review processes. -Manual validation of SBOMs against organizational policies is: - -- Time-consuming and error-prone -- Inconsistent across teams and projects -- Difficult to scale across large numbers of SBOMs -- Lacks audit trails and historical compliance tracking -- Cannot provide real-time feedback on policy violations +Enterprise Contract (Conforma) is an open-source policy enforcement tool actively maintained by Red Hat. It validates SBOMs against configurable policies and produces structured JSON output. Currently it provides only a CLI; a REST API is planned but with no committed timeline. ### Requirements @@ -29,38 +23,81 @@ Users need the ability to: 5. Generate detailed compliance reports for auditing 6. Receive actionable feedback on policy violations -### Acceptance Criteria +## Decision + +We will integrate Conforma into Trustify as an optional validation service by spawning the Conforma CLI asynchronously. +Validation is manually triggered — not automatic on SBOM upload. +Validation on upload is deferred to a follow-up version. -For this integration to be considered successful, the following criteria must be met: +What is stored where -1. **Conforma Integration**: Trustify backend integrates with Conforma CLI to execute policy checks against stored SBOMs -2. **Policy Configuration**: Users/administrators can define or reference specific Enterprise Contract policies to enforce via the UI or API -3. **Result Persistence**: Conforma check output (Pass/Fail status and specific violations) is parsed and saved as structured properties on the corresponding SBOM record in the database -4. **Error Handling**: System gracefully handles execution failures (e.g., malformed SBOMs, policy timeouts, Conforma unavailable) and returns appropriate error messages -5. **Visibility**: Users can retrieve the compliance status of an SBOM via the Trustify API or UI, including historical validation results +- PostgreSQL: validation status, structured violations (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, status, executed_at. +- S3/Minio: full raw Conforma JSON report, linked from the DB row via report_url. Keeps DB rows small while preserving audit completeness. +- Not stored: the policy definitions themselves. ec_policies stores references (URLs, OCI refs) that Conforma fetches at runtime. -### Available Solutions +Storing full JSON in S3 rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB violations JSONB holds enough structure for filtering and dashboards without duplicating the full payload. -**Enterprise Contract (Conforma)** is an open-source policy enforcement tool that: +### Deferred Validation -- Validates SBOMs against configurable policies -- Supports policies for licensing, vulnerabilities, and provenance -- Provides structured output (JSON) for programmatic consumption -- Integrates with CI/CD pipelines -- Is open source -- Is actively maintained by Red Hat +I has been decided that uploaded SBOMs start in "Pending" status and are not discoverable until validated. +EC validation is one mechanism by which an SBOM can move from "Pending" to "Accepted" or "Rejected". -**Current state**: Conforma provides a CLI tool but no REST API yet. Future API availability is expected but timeline is undetermined. +Concretely: -## Decision + An SBOM in "Pending" can be submitted for EC validation. + A passing EC result transitions the SBOM to "Accepted". + A failing EC result transitions it to "Rejected", with the violation details linked. + An EC execution error (CLI crash, policy fetch failure) does not change the SBOM's policy status — the SBOM stays Pending and the error is surfaced separately, so it doesn't silently block an SBOM. + +## Consequences + +Integrating via CLI spawning rather than a native API introduces an external process dependency that adds operational overhead (Conforma must be installed and version-pinned on every server) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma API exists yet. The executor is built behind an adapter interface so the implementation can be swapped for a REST client in Phase 3 without changes to the service layer or API. + +### Trade-off/Risk & Mitigation + +- Conforma must be installed on servers + - Health check on startup + - graceful degradation showing cached results +- Process spawn overhead per validation + - Async execution; configurable timeout (default 5 min) +- CLI injection attacks + - Args array only, never shell strings; all user inputs sanitized +- Version compatibility + - Document required Conforma version; validate on startup +- Concurrent load exhausting resources + - Semaphore (default: 5) + - 429 on exhaustion +- No native API yet + - Adapter pattern for future migration (Phase 3) +- Large SBOMs causing OOM + - Stream to temp file + - pass path to Conforma +- Growing S3 storage costs + - Retention policy (90-day default) + +### Alternatives Considered + +#### In-Process Policy Engine: Rejected + +Reimplementing Enterprise Contract logic in Rust would diverge from upstream and create significant maintenance burden. + +#### Webhook-based Integration: Deferred -We will integrate Enterprise Contract (Conforma) into Trustify as an optional validation service that: +Decouples validation into a separate service, which is better for large-scale deployments but adds infrastructure complexity premature for initial scope. -1. **Executes Conforma CLI** via async process spawning using `tokio::process::Command` -2. **Stores validation results** as structured data in PostgreSQL with foreign key relationship to SBOMs -3. **Persists detailed reports** in object storage (S3/Minio) for audit trails -4. **Exposes REST API endpoints** for triggering validation and retrieving results -5. **Supports policy configuration** through database-backed policy references +#### Embedded WASM Module: Rejected + +Conforma is not available as WASM and would require major upstream changes. + +#### Batch Processing Queue: Deferred + +A Redis/RabbitMQ queue would improve retry handling and priority management; implement if the 429-based rejection approach proves insufficient under real load. + +### Future API Migration + +When Conforma provides a REST API, the executor.rs adapter is replaced with an HTTP client. A feature flag (ec-api-mode) allows gradual migration. No changes to service layer, API endpoints, or UI are required. + +## The solution ### System Architecture @@ -138,227 +175,167 @@ C4Component Container(api, "API Gateway", "Actix-web", "REST API for EC operations") Container_Boundary(ecModule, "EC Validation Module") { - Component(ecEndpoints, "EC Endpoints", "Actix-web handlers", "REST endpoints for
validation operations") - Component(conformaExecutor, "Conforma Executor", "Async Process", "Executes Conforma CLI
and captures output") - Component(policyManager, "Policy Manager", "Configuration", "Manages EC policy
references and configuration") + Component(ecEndpoints, "EC Endpoints", "Actix-web handlers", "REST endpoints for validation operations") + Component(conformaExecutor, "Conforma Executor", "Async Process", "Spawns Conforma CLI and captures output") Component(ecService, "EC Service", "Business logic", "Orchestrates validation workflow") - Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output
into structured data") - Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results to database") - Component(SBOMModel, "SBOM Model", "Data structure", "API models for validation
requests/responses") + Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output into structured data") + Component(policyManager, "Policy Manager", "Configuration", "Manages EC policy references") + Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results") } - Container_Boundary(external, "External Systems") { - System_Ext(conforma, "Conforma CLI", "Enterprise Contract
validation tool") - System_Ext(s3, "S3 Object Storage", "Stores SBOM documents and reports") + Container_Boundary(external, "External System") { + System_Ext(conforma, "Conforma CLI", "Enterprise Contract validation tool") } - Container_Boundary(postgres, "database") { - ContainerDb(postgres, "PostgreSQL", "Database", "Stores validation results") + Container_Boundary(dbms, "Database") { + ContainerDb(postgres, "PostgreSQL", "Database", "Stores validation results and policy references") + } + Container_Boundary(storage, "S3 System") { + System_Ext(s3, "S3 Object Storage", "Stores SBOM documents and reports") } - - Rel(api, ecEndpoints, "POST /sboms/{id}/ec-validate,
GET /sboms/{id}/ec-report", "JSON/HTTPS") - Rel(ecEndpoints, ecService, "validate_sbom()
get_ec_report()", "Function call") + Rel(api, ecEndpoints, "POST /sboms/{id}/ec-validate,\nGET /sboms/{id}/ec-report", "JSON/HTTPS") + Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") + Rel(policyManager, postgres, "SELECT ec_policies", "SQL") Rel(ecService, conformaExecutor, "request_validation()", "Function call") Rel(conformaExecutor, conforma, "Runs CLI command", "Process spawn") - Rel(resultParser, SBOMModel, "Creates models", "Data mapping") + Rel(ecService, resultParser, "parse_output()", "Function call") Rel(ecService, resultPersistence, "save_results()", "Function call") - Rel(resultPersistence, postgres, "INSERT validation_results", "SQL") + Rel(resultPersistence, postgres, "INSERT ec_validation_results", "SQL") Rel(ecService, s3, "Store EC report", "S3 API") UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") - UpdateRelStyle(ecService, policyManager, $offsetX="-50", $offsetY="-50") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") + ``` +### The main sequence Diagram + ```mermaid sequenceDiagram autonumber actor User - participant UI as Trustify UI participant API as Trustify API - participant VS as Validation Service + participant VS as EC Service participant PM as Policy Manager participant DB as PostgreSQL participant S3 as Object Storage participant Conf as Conforma CLI - User->>UI: Request SBOM validation for policy - UI->>API: POST /api/v2/sbom/{sbom_id}/validate - Note over UI,API: Request body: {policy_id} - - API->>VS: validate_sbom_against_policy(sbom_id, policy_id) - - rect rgb(42, 48, 53) - Note over VS,PM: Policy Resolution Phase - VS->>PM: get_policy_configuration(policy_id) - PM->>DB: SELECT * FROM ec_policies WHERE id = ? - DB-->>PM: Policy configuration - alt Policy not found - PM-->>VS: Error: PolicyNotFound - VS-->>API: 404 Not Found - API-->>UI: Policy not found error - UI-->>User: Display error: "Policy does not exist" - end - PM-->>VS: PolicyConfig {name, policy_ref, version} - end + User->>API: POST /api/v2/sbom/{sbom_id}/ec-validate {policy_id} - rect rgb(68, 66, 62) - Note over VS,S3: SBOM Retrieval Phase - VS->>DB: SELECT * FROM sbom WHERE id = ? - DB-->>VS: SBOM metadata - alt SBOM not found - VS-->>API: 404 Not Found - API-->>UI: SBOM not found error - UI-->>User: Display error: "SBOM does not exist" - end - - VS->>S3: retrieve_sbom_document(sbom_id) - S3-->>VS: SBOM document (JSON/XML) + VS->>PM: get_policy_configuration(policy_id) + PM->>DB: SELECT * FROM ec_policies WHERE id = ? + alt Policy not found + PM-->>VS: Error: PolicyNotFound + VS-->>API: 404 Not Found end + PM-->>VS: PolicyConfig {name, policy_ref, type} - rect rgb(42, 48, 53) - Note over VS,Conf: Validation Execution Phase - VS->>VS: Create temp files for SBOM and policy - VS->>Conf: spawn: conforma validate
input --file "$1" --policy
--output json --show-successes
--info - - alt Validation passes - Conf-->>VS: Exit code: 0
JSON: {result: "PASS", violations: []} - VS->>VS: Parse validation results - VS->>DB: INSERT INTO ec_validation_results
(sbom_id, policy_id, status='passed',
violations=[], timestamp) - DB-->>VS: result_id - VS->>S3: store_validation_report(result_id, full_json) - S3-->>VS: report_url - VS->>DB: UPDATE ec_validation_results
SET report_url = ? - DB-->>VS: Updated - VS-->>API: ValidationResult {status: "passed",
violations: [], report_url} - API-->>UI: 200 OK {passed: true, violations: 0} - UI-->>User: ✓ SBOM passes policy validation - - else Validation fails with violations - Conf-->>VS: Exit code: 1
JSON: {result: "FAIL",
violations: [{rule, severity, message}]} - VS->>VS: Parse validation results - VS->>DB: INSERT INTO ec_validation_results
(sbom_id, policy_id, status='failed',
violations=json, timestamp) - DB-->>VS: result_id - VS->>S3: store_validation_report(result_id, full_json) - S3-->>VS: report_url - VS->>DB: UPDATE ec_validation_results
SET report_url = ? - DB-->>VS: Updated - VS-->>API: ValidationResult {status: "failed",
violations: [...], report_url} - API-->>UI: 200 OK {passed: false, violations: [...]} - UI-->>User: ✗ SBOM violates policy
Show violation details - - else Conforma execution error - Conf-->>VS: Exit code: 2
stderr: "Policy file not found" - VS->>DB: INSERT INTO ec_validation_results
(sbom_id, policy_id, status='error',
error_message=stderr) - DB-->>VS: result_id - VS-->>API: Error: ValidationExecutionFailed - API-->>UI: 500 Internal Server Error - UI-->>User: Display error: "Validation failed to execute" - end + VS->>DB: SELECT * FROM sbom WHERE id = ? + alt SBOM not found + VS-->>API: 404 Not Found + end + VS->>S3: retrieve_sbom_document(sbom_id) + S3-->>VS: SBOM document (JSON/XML) + + VS->>VS: Write SBOM to temp file + VS->>Conf: conforma validate input --file "$SBOM_PATH" --policy --output json --show-successes --info + + alt Exit code 0 — pass + Conf-->>VS: {result: "PASS", violations: []} + VS->>DB: INSERT ec_validation_results (status='pass', violations=[], ...) + VS->>S3: store_validation_report(result_id, full_json) + VS->>DB: UPDATE SET report_url = ? + VS->>DB: UPDATE sbom SET policy_status = 'Accepted' + VS-->>API: 200 {passed: true, violations: 0, report_url} + else Exit code 1 — fail (policy violations) + Conf-->>VS: {result: "FAIL", violations: [{rule, severity, message}]} + VS->>DB: INSERT ec_validation_results (status='fail', violations=json, ...) + VS->>S3: store_validation_report(result_id, full_json) + VS->>DB: UPDATE SET report_url = ? + VS->>DB: UPDATE sbom SET policy_status = 'Rejected' + VS-->>API: 200 {passed: false, violations: [...], report_url} + else Exit code 2+ — execution error + Conf-->>VS: stderr: "Policy file not found" + VS->>DB: INSERT ec_validation_results (status='error', error_message=stderr) + Note over VS,DB: SBOM policy_status unchanged — stays Pending + VS-->>API: 500 {error: "Validation failed to execute", detail: stderr} end VS->>VS: Cleanup temp files ``` -### Data Model - -Two new tables: +### The Data Model -**`ec_policies`** - Store policy references and metadata (not the policies themselves) - -> **Note**: This table stores references to external policies (Git URLs, OCI registries, etc.) and local configuration metadata. The actual policy definitions remain in their external repositories (GitHub, GitLab, etc.) and are fetched by Conforma at validation time. +**`ec_policies`** - Stores references to external policies, not the policies themselves - `id` (UUID, PK) -- `name` (VARCHAR, unique) - User-friendly name for this policy configuration -- `description` (TEXT) - Description of what this policy enforces -- `policy_ref` (VARCHAR) - **External reference**: Git URL, OCI registry, or file path where policy is stored -- `policy_type` (VARCHAR) - 'git', 'oci', 'local' (indicates how Conforma should fetch the policy) -- `configuration` (JSONB) - Additional Conforma parameters (branch, tag, auth credentials, etc.) +- `name` (VARCHAR, unique) - User-friendly name label +- `description` (TEXT) - What this policy enforces +- `policy_ref` (VARCHAR) - Git URL, OCI registry, or file path +- `policy_type` (VARCHAR) - 'git', 'oci', 'local' +- `configuration` (JSONB) - Branch, tag, auth credentials, etc. - `created_at`, `updated_at` (TIMESTAMP) -**`ec_validation_results`** - Store validation outcomes +**`ec_validation_results`** - one row per validation execution - `id` (UUID, PK) - `sbom_id` (UUID, FK → sbom) - `policy_id` (UUID, FK → ec_policies) - `status` (VARCHAR) - 'pass', 'fail', 'error' -- `violations` (JSONB) - Structured violation data -- `summary` (JSONB) - Statistics (total checks, passed, failed, warnings) +- `violations` (JSONB) - Structured violation data for querying +- `summary` (JSONB) - Total checks, passed, failed, warnings - `report_url` (VARCHAR) - S3 URL to detailed report - `executed_at` (TIMESTAMP) - `execution_duration_ms` (INTEGER) -- `conforma_version` (VARCHAR) -- `error_message` (TEXT) +- `conforma_version` (VARCHAR) - For reproducibility +- `error_message` (TEXT) - Populated only on error status ### API Endpoints ``` -POST /api/v2/sboms/{id}/ec-validate # Trigger validation -GET /api/v2/sboms/{id}/ec-report # Get latest validation result -GET /api/v2/sboms/{id}/ec-report/history # Get validation history -GET /api/v2/ec/report/{result_id} # Download detailed report -POST /api/v2/ec/policies # Create policy reference (admin) -GET /api/v2/ec/policies # List policy references -GET /api/v2/ec/policies/{id} # Get policy reference details -PUT /api/v2/ec/policies/{id} # Update policy reference (admin) -DELETE /api/v2/ec/policies/{id} # Delete policy reference (admin) +POST /api/v2/sboms/{id}/ec-validate # Trigger validation +GET /api/v2/sboms/{id}/ec-report # Get latest validation result +GET /api/v2/sboms/{id}/ec-report/history # Get validation history +GET /api/v2/ec/report/{result_id} # Download detailed report from S3 + +POST /api/v2/ec/policies # Create policy reference (admin) +GET /api/v2/ec/policies # List policy references +GET /api/v2/ec/policies/{id} # Get policy reference +PUT /api/v2/ec/policies/{id} # Update policy reference (admin) +DELETE /api/v2/ec/policies/{id} # Delete policy reference (admin) ``` -### Implementation Approach - -**Phase 1: Backend - CLI Integration** - -- Use async process spawning with proper timeout handling -- Stream stdout/stderr for error capture -- Parse Conforma JSON output into Rust structs -- Handle exit codes (0=pass, 1=fail, 2=error) -- Implement REST API endpoints for validation operations -- Add database schema and migrations -- Create service layer for orchestration - -**Phase 2: Frontend - GUI Development** - -- **SBOM Details View Enhancements**: - - Add "Validate with EC" button/action in SBOM detail page - - Display compliance status badge (Pass/Fail/Error) prominently - - Show latest validation timestamp and policy used -- **Validation Results Display**: - - Create dedicated compliance tab/section in SBOM view - - Display validation summary (total checks, passed, failed, warnings) - - List violations with severity, description, and remediation hints - - Provide expandable/collapsible violation details -- **Validation History**: - - Show historical validation results in timeline view - - Allow filtering by policy, status, and date range - - Display trend charts for compliance over time -- **Policy Management UI** (Admin): - - Policy reference configuration page (store Git URLs, OCI refs, etc.) - - List view of available policy references with descriptions and types - - Policy selection dialog when triggering validation - - Form validation for policy references (validate Git URL format, test connectivity) - - Display policy source (GitHub, GitLab, OCI registry) with clickable links -- **Report Download**: - - Download button for detailed JSON/HTML reports - - Preview detailed report in modal or new page - - Export options (JSON, PDF, HTML) -- **Notifications & Feedback**: - - Loading indicators during validation execution - - Toast notifications for validation completion - - Error messages with actionable guidance - - Progress tracking for long-running validations - -**Phase 3: Future API Migration** - -When Conforma provides a REST API: - -- Implement adapter pattern to switch between CLI and API -- Add feature flag `ec-api-mode` for gradual migration -- Maintain backward compatibility with CLI mode -- Deprecate CLI integration after stable API adoption -- No GUI changes required (transparent backend switch) +### Technical Considerations + +#### Conforma CLI Execution + +Conforma is invoked via tokio::process::Command to avoid blocking the async runtime. All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are streamed to a temp file and passed by path rather than piped via stdin, which avoids OOM issues with the parent process. + +Exit codes are treated as follows: 0 = pass, 1 = policy violations (expected failure, not an error), 2+ = execution error. It is important to distinguish 1 from 2+ in error handling — a policy violation is a valid result that should be surfaced to the user, not treated as a system failure. + +Temp files (SBOM, any cached policy material) are cleaned up in a finally-equivalent block regardless of execution outcome, including on timeout. + +#### Concurrency and Backpressure + +Concurrent Conforma processes are bounded by a semaphore (default: 5). When the semaphore is exhausted, incoming validation requests return 429 Too Many Requests immediately rather than queuing or blocking indefinitely. This makes the capacity limit explicit to callers (e.g. CI pipelines can implement their own retry with backoff). If demand grows to warrant it, a proper queue (Redis/RabbitMQ) is the deferred alternative considered below. + +#### Policy Management + +ec_policies stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and must be encrypted at rest; they are never logged. + +Policy version/commit hash is recorded in the conforma_version field of each result row, enabling reproducibility and audit. + +#### Multi-tenancy + +Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. + +#### Data Retention + +Validation results accumulate over time. A retention policy (default: 90 days, configurable) should be implemented to prune old ec_validation_results rows and their corresponding S3 reports. This is deferred to the implementation phase but must be included before production rollout. ### Module Structure @@ -382,185 +359,9 @@ modules/ec/ └── error.rs # Error types ``` -### Technical Considerations - -**Conforma CLI Execution** - -- Use `tokio::process::Command` for async execution to avoid blocking the runtime -- Stream stdout/stderr for real-time monitoring and logging -- Set execution timeouts (default: 5 minutes, configurable per policy) -- Handle large SBOM files efficiently by streaming to temporary files -- Capture exit codes for error handling (0=pass, 1=fail, 2+=error) -- Sanitize all CLI arguments to prevent injection attacks (use process args array, not shell strings) - -**Policy Management** - -- Support multiple policy types: Git repositories, OCI registries, local file paths -- Cache policy files locally to avoid repeated fetches from external sources -- Validate policy references before execution (check URL format, test connectivity) -- Track policy version/commit when storing validation results for reproducibility -- Support authentication for private policy repositories (tokens, SSH keys) - -**Result Storage** - -- Store structured violations in JSONB for efficient querying (filter by violation type, severity) -- Keep detailed reports in object storage (S3/Minio) to minimize database size -- Implement retention policies for old validation results (configurable, e.g., 90 days) -- Index frequently queried fields (sbom_id, status, executed_at) for performance -- Consider result aggregation for analytics dashboards - -**Error Handling** - -- Distinguish between validation failures (policy violations) and execution errors (CLI crashes) -- Provide actionable error messages to users (e.g., "Policy file not found at URL") -- Implement retry logic for transient failures (network timeouts, temporary service unavailability) -- Log all execution details for debugging (command, arguments, duration, exit code) -- Gracefully degrade when Conforma is unavailable (show cached results, queue for retry) - -**Security** - -- Validate and sanitize all user inputs (policy URLs, SBOM IDs) -- Restrict policy sources to trusted domains (configurable allowlist) -- Store authentication credentials securely (encrypted, never in logs) -- Implement proper authorization checks (only admins can modify policies) -- Audit log all validation executions and policy modifications - -**Performance** - -- Limit concurrent Conforma executions using semaphore (default: 5 concurrent) -- Implement queueing for validation requests during high load -- Cache policy files to reduce external network calls -- Use connection pooling for database operations -- Monitor execution times and resource usage for capacity planning -- Consider horizontal scaling for validation workloads - -## Consequences - -### Positive - -1. **Automated Policy Enforcement**: Organizations can automatically validate SBOMs without manual review -2. **Audit Trail**: Complete history of compliance checks stored in database -3. **Flexibility**: Support multiple policy configurations for different requirements -4. **Integration Ready**: REST API enables integration with CI/CD pipelines -5. **Scalability**: Async execution prevents blocking on long-running validations -6. **Extensibility**: Module design allows future enhancement (webhooks, notifications, etc.) -7. **Open Source**: Conforma is open-source and actively maintained - -### Trade-offs and Risks - -| Trade-off / Risk | Impact | Mitigation | -| ------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------- | -| External CLI dependency | Requires Conforma installed on servers | Health checks, graceful error handling, retry logic | -| Process spawning overhead | Performance implications per validation | Async execution with configurable timeouts (default: 5 min) | -| Error handling complexity | CLI failures, timeouts, malformed output | Distinguish validation failures from execution errors; actionable messages | -| Version management | Conforma version compatibility | Document required version, validate on startup | -| Resource usage under load | Concurrent validations consume resources | Semaphore limits (default: 5), queueing, monitoring | -| No native API yet | CLI less efficient than REST integration | Adapter pattern for future API migration (see Phase 3) | -| Large SBOMs cause memory issues | Out-of-memory during validation | Stream SBOM to temp file, pass file path to Conforma | -| CLI injection attacks | Security vulnerability | Sanitize all inputs, use process args array (not shell strings) | -| Storage costs for reports | Growing storage over time | Retention policies, report compression | - -## Alternatives Considered - -### 1. In-Process Policy Engine - -**Pros**: No external dependencies, faster execution, native Rust integration - -**Cons**: Requires reimplementing Enterprise Contract logic, maintenance burden, divergence from upstream - -**Verdict**: Rejected - maintaining policy engine parity with EC would be significant effort - -### 2. Webhook-based Integration - -**Pros**: Decoupled, scalable, easier to manage separate service - -**Cons**: Additional infrastructure, network latency, complexity for simple use case - -**Verdict**: Deferred - could be future enhancements for large-scale deployments - -### 3. Embedded WASM Module - -**Pros**: Sandboxed, portable, no process spawning - -**Cons**: Conforma not available as WASM, would require major upstream changes - -**Verdict**: Rejected - not feasible with current Conforma implementation - -### 4. Batch Processing Queue - -**Pros**: Better resource management, retry logic, priority handling - -**Cons**: Adds complexity, requires queue infrastructure (Redis/RabbitMQ) - -**Verdict**: Deferred - implement if demand increases, start with simple async execution - -## References +### References - [Enterprise Contract (Conforma) GitHub](https://github.com/enterprise-contract/ec-cli) -- [Conforma CLI Documentation](https://github.com/enterprise-contract/ec-cli) - [Design Document](../design/enterprise-contract-integration.md) - [ADR-00005: Upload API for UI](./00005-ui-upload.md) - Similar async processing pattern - [ADR-00001: Graph Analytics](./00001-graph-analytics.md) - Database query patterns - -## Implementation Tracking - -### Backend Tasks - -- [ ] Create module structure under `modules/ec` -- [ ] Implement database migrations for new tables (`ec_policies`, `ec_validation_results`) -- [ ] Build Conforma CLI executor with async process handling -- [ ] Create result parser for JSON output -- [ ] Implement policy manager service -- [ ] Implement EC service orchestration layer -- [ ] Add REST API endpoints (validation, results, policies) -- [ ] Write unit and integration tests -- [ ] Add OpenAPI documentation -- [ ] Write deployment documentation (Conforma installation) -- [ ] Add monitoring and metrics (execution times, success rates) -- [ ] Implement error handling and retry logic -- [ ] Add authentication/authorization checks - -### Frontend Tasks - -- [ ] Add "Validate with EC" button to SBOM detail page -- [ ] Create compliance status badge component (Pass/Fail/Error) -- [ ] Implement validation results display with summary statistics -- [ ] Build violations list component with expandable details -- [ ] Create validation history timeline view -- [ ] Add policy reference management UI (admin pages) - - [ ] Policy reference list view with search/filter (shows name, external URL, type) - - [ ] Policy reference create/edit form (Git URL, OCI ref, auth config) - - [ ] Policy reference delete confirmation - - [ ] Test policy connectivity button (validate URL is reachable) -- [ ] Add report download functionality (JSON/HTML) -- [ ] Create detailed report preview modal -- [ ] Implement loading indicators for validation execution -- [ ] Add toast notifications for validation completion/errors -- [ ] Create compliance trend charts (optional, if analytics desired) -- [ ] Add help tooltips and documentation links -- [ ] Ensure responsive design for mobile/tablet views -- [ ] Add accessibility features (ARIA labels, keyboard navigation) - -## Open Questions - -1. **Policy Storage & Caching**: Should policies be pulled from external sources (OCI registry, Git repository) at runtime, or should Trustify cache them locally? Should we rely on Conforma's built-in caching or implement our own? How to handle cache invalidation and TTL for cached policies? - -2. **Trigger Mechanism**: Should the EC validation run automatically immediately upon SBOM upload, or will this be a manually triggered/scheduled process? Should users be able to configure auto-validation per SBOM or globally? - -3. **Data Granularity**: Do we store the full raw JSON output from Conforma, or only a summary (Pass/Fail) and a list of specific policy violations to save database space? What's the trade-off between storage cost and audit completeness? - -4. **Conforma Versioning**: Which version of the Conforma CLI are we targeting initially, and how will we handle updates to the policy engine? Should we support multiple Conforma versions concurrently or enforce a single version? - -5. **Policy Source Security**: Should we validate/verify Git repository signatures for policy sources? How to handle private repositories requiring authentication (tokens, SSH keys)? - -6. **Multi-tenancy**: How to isolate policy references per organization in shared Trustify deployments? Should each organization have separate policy namespaces? - -7. **Rate Limiting**: Should we limit validation requests per user/organization to prevent resource exhaustion? What are reasonable limits? - -8. **Notifications**: Should validation results trigger notifications (email, webhook, Slack)? Should notifications be configurable per policy or per SBOM? - -9. **Automatic Re-validation**: Should we re-validate SBOMs when policy definitions are updated in their external repositories? How to detect policy changes (polling, webhooks)? - -10. **UI/UX**: Where in the UI should compliance status be most prominently displayed? Should we show compliance badges on SBOM list views or only in detail views? - -11. **Default Policies**: Should there be system-wide default policy references that apply to all SBOMs unless overridden? How to handle precedence (user-level vs org-level vs system-level)? From 54c83ce787299398e3c821898f69c945a0e21f89 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Feb 2026 10:24:33 +0100 Subject: [PATCH 08/52] Remove S3 from system Architecture --- docs/adrs/00014-enterprise-contract-integration.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 5036577b5..51f915016 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -110,13 +110,11 @@ C4Context System(trustify, "Trustify", "RHTPA") System_Ext(conforma, "Conforma", "Enterprise Contract policy validation tool") - System_Ext(s3, "S3", "S3/Minio Storage") System_Ext(policyRepo, "Policy Repository", "Git repository or storage containing EC policies") Rel(user, trustify, "Request Compliance
View compliance status", "API/GUI") Rel(trustify, conforma, "Executes policy validation", "Spawn Process") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") - Rel(trustify, s3, "Stores reports", "S3 API") UpdateRelStyle(trustify, conforma, $offsetX="-40") UpdateRelStyle(user, trustify, $offsetX="-50", $offsetY="20") From 8f04d2643e50dafa27b35e627358d4e26947d745 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Feb 2026 10:26:17 +0100 Subject: [PATCH 09/52] Offset link --- docs/adrs/00014-enterprise-contract-integration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 51f915016..21de73a02 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -108,7 +108,6 @@ C4Context Person(user, "Trustify User", "Analyst validating SBOMs") System(trustify, "Trustify", "RHTPA") - System_Ext(conforma, "Conforma", "Enterprise Contract policy validation tool") System_Ext(policyRepo, "Policy Repository", "Git repository or storage containing EC policies") @@ -116,8 +115,9 @@ C4Context Rel(trustify, conforma, "Executes policy validation", "Spawn Process") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") - UpdateRelStyle(trustify, conforma, $offsetX="-40") UpdateRelStyle(user, trustify, $offsetX="-50", $offsetY="20") + UpdateRelStyle(trustify, conforma, $offsetX="-40") + UpdateRelStyle(conforma, policyRepo,$offsetX="-40") UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1") ``` From f77628e40bd4e5c52333a4e8efeb8627da84ef21 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Feb 2026 10:29:29 +0100 Subject: [PATCH 10/52] Component diagram: standalone policy system --- docs/adrs/00014-enterprise-contract-integration.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 21de73a02..e0ef90d88 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -142,6 +142,9 @@ C4Container Container_Boundary(conforma, "Conforma System") { System_Ext(conforma, "Conforma CLI", "External policy validation tool") + } + + Container_Boundary(policyRepo, "Policy System") { System_Ext(policyRepo, "Policy Repository", "Git repository with EC policies") } @@ -161,7 +164,7 @@ C4Container UpdateRelStyle(ecModule, postgres, $offsetX="-40", $offsetY="10") UpdateRelStyle(storage, s3, $offsetX="-40", $offsetY="10") - UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3") + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` ### Container Diagram From 928d4ca3854ad630808503280ee5d9c1cf79b2e1 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Feb 2026 11:29:06 +0100 Subject: [PATCH 11/52] Component details --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index e0ef90d88..88ea02ca0 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -180,7 +180,7 @@ C4Component Component(conformaExecutor, "Conforma Executor", "Async Process", "Spawns Conforma CLI and captures output") Component(ecService, "EC Service", "Business logic", "Orchestrates validation workflow") Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output into structured data") - Component(policyManager, "Policy Manager", "Configuration", "Manages EC policy references") + Component(policyManager, "Policy Manager", "Business logic", "Manages EC policy references and configuration") Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results") } From e854d1337f2b08533d301c3c6c58f4fc2830d5ca Mon Sep 17 00:00:00 2001 From: arpcv Date: Mon, 23 Feb 2026 14:57:02 +0100 Subject: [PATCH 12/52] Refine SBOM validation process details and update terminology in enterprise contract integration documentation --- .../00014-enterprise-contract-integration.md | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 88ea02ca0..c63c03386 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -16,7 +16,7 @@ Enterprise Contract (Conforma) is an open-source policy enforcement tool activel Users need the ability to: -1. Automatically validate SBOMs against organizational policies +1. Validate SBOMs against organizational policies 2. Define and manage multiple policy configurations 3. View compliance status and violation details for each SBOM 4. Track compliance history over time @@ -29,6 +29,13 @@ We will integrate Conforma into Trustify as an optional validation service by sp Validation is manually triggered — not automatic on SBOM upload. Validation on upload is deferred to a follow-up version. +Uploaded SBOMs start in "Pending" status and are not discoverable until validated. EC validation is one mechanism by which an SBOM can move from "Pending" to "Accepted" or "Rejected": + +- An SBOM in "Pending" can be submitted for EC validation. +- A passing EC result transitions the SBOM to "Accepted". +- A failing EC result transitions it to "Rejected", with the violation details linked. +- An EC execution error (CLI crash, policy fetch failure) does not change the SBOM's policy status — the SBOM stays Pending and the error is surfaced separately, so it doesn't silently block an SBOM. + What is stored where - PostgreSQL: validation status, structured violations (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, status, executed_at. @@ -37,18 +44,6 @@ What is stored where Storing full JSON in S3 rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB violations JSONB holds enough structure for filtering and dashboards without duplicating the full payload. -### Deferred Validation - -I has been decided that uploaded SBOMs start in "Pending" status and are not discoverable until validated. -EC validation is one mechanism by which an SBOM can move from "Pending" to "Accepted" or "Rejected". - -Concretely: - - An SBOM in "Pending" can be submitted for EC validation. - A passing EC result transitions the SBOM to "Accepted". - A failing EC result transitions it to "Rejected", with the violation details linked. - An EC execution error (CLI crash, policy fetch failure) does not change the SBOM's policy status — the SBOM stays Pending and the error is surfaced separately, so it doesn't silently block an SBOM. - ## Consequences Integrating via CLI spawning rather than a native API introduces an external process dependency that adds operational overhead (Conforma must be installed and version-pinned on every server) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma API exists yet. The executor is built behind an adapter interface so the implementation can be swapped for a REST client in Phase 3 without changes to the service layer or API. @@ -271,7 +266,7 @@ sequenceDiagram ### The Data Model -**`ec_policies`** - Stores references to external policies, not the policies themselves +**`ec_policy`** - Stores references to external policies, not the policies themselves - `id` (UUID, PK) - `name` (VARCHAR, unique) - User-friendly name label From 444814d3ec9fcefcb743af2e2d6e998f5924778f Mon Sep 17 00:00:00 2001 From: arpcv Date: Mon, 23 Feb 2026 15:03:02 +0100 Subject: [PATCH 13/52] Integrated the EC result status; Fix plural for db table; Remove irrelevant requirement --- docs/adrs/00014-enterprise-contract-integration.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index c63c03386..6e676a9ae 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -329,10 +329,6 @@ Policy version/commit hash is recorded in the conforma_version field of each res Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. -#### Data Retention - -Validation results accumulate over time. A retention policy (default: 90 days, configurable) should be implemented to prune old ec_validation_results rows and their corresponding S3 reports. This is deferred to the implementation phase but must be included before production rollout. - ### Module Structure ``` From f9d83ccd3742b8afb7400869b1588a44887928fa Mon Sep 17 00:00:00 2001 From: arpcv Date: Wed, 25 Feb 2026 16:32:41 +0100 Subject: [PATCH 14/52] External HTTP API/Webhook Wraper --- .../00014-enterprise-contract-integration.md | 71 ++++++++----------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 6e676a9ae..b1d7333be 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -25,10 +25,13 @@ Users need the ability to: ## Decision -We will integrate Conforma into Trustify as an optional validation service by spawning the Conforma CLI asynchronously. +We will integrate Conforma into Trustify as a user triggered validation service by interacting with Conforma CLI. Validation is manually triggered — not automatic on SBOM upload. Validation on upload is deferred to a follow-up version. +Conforma CLI is deployed separately from Trustify as either a standalone container or equivalent. +A HTTP wrapper will act as a proxy between Trustify EC service and Conforma CLI. + Uploaded SBOMs start in "Pending" status and are not discoverable until validated. EC validation is one mechanism by which an SBOM can move from "Pending" to "Accepted" or "Rejected": - An SBOM in "Pending" can be submitted for EC validation. @@ -46,29 +49,11 @@ Storing full JSON in S3 rather than only a summary was chosen explicitly to pres ## Consequences -Integrating via CLI spawning rather than a native API introduces an external process dependency that adds operational overhead (Conforma must be installed and version-pinned on every server) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma API exists yet. The executor is built behind an adapter interface so the implementation can be swapped for a REST client in Phase 3 without changes to the service layer or API. +Using a HTTP API wrapper decouples the validation process into a external service. +This will better catter for large-scale deployments as EC validation has its own constraint. +Meanwhile it adds infrastructure complexity as the Webhook will need to be deployed alongside the EC system -### Trade-off/Risk & Mitigation - -- Conforma must be installed on servers - - Health check on startup - - graceful degradation showing cached results -- Process spawn overhead per validation - - Async execution; configurable timeout (default 5 min) -- CLI injection attacks - - Args array only, never shell strings; all user inputs sanitized -- Version compatibility - - Document required Conforma version; validate on startup -- Concurrent load exhausting resources - - Semaphore (default: 5) - - 429 on exhaustion -- No native API yet - - Adapter pattern for future migration (Phase 3) -- Large SBOMs causing OOM - - Stream to temp file - - pass path to Conforma -- Growing S3 storage costs - - Retention policy (90-day default) +Integrating via CLI spawning rather than a native API introduces an external process dependency that adds operational overhead (Conforma must be installed and version-pinned on every server) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma API exists yet. The executor is built behind an adapter interface so the implementation can be swapped for a REST client in Phase 3 without changes to the service layer or API. ### Alternatives Considered @@ -76,9 +61,9 @@ Integrating via CLI spawning rather than a native API introduces an external pro Reimplementing Enterprise Contract logic in Rust would diverge from upstream and create significant maintenance burden. -#### Webhook-based Integration: Deferred +#### Direct Integration: Rejected -Decouples validation into a separate service, which is better for large-scale deployments but adds infrastructure complexity premature for initial scope. +Couple validation integrated within Trustify service through a directly controlled component was simpler but worse for large-scale deployments. #### Embedded WASM Module: Rejected @@ -168,19 +153,23 @@ C4Container C4Component title EC Validation Module - Component Diagram - Container(api, "API Gateway", "Actix-web", "REST API for EC operations") - - Container_Boundary(ecModule, "EC Validation Module") { - Component(ecEndpoints, "EC Endpoints", "Actix-web handlers", "REST endpoints for validation operations") - Component(conformaExecutor, "Conforma Executor", "Async Process", "Spawns Conforma CLI and captures output") - Component(ecService, "EC Service", "Business logic", "Orchestrates validation workflow") - Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output into structured data") - Component(policyManager, "Policy Manager", "Business logic", "Manages EC policy references and configuration") - Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results") + Deployment_Node(trustifySystem, "Trustify System") { + Container(api, "API Gateway", "Actix-web", "REST API for EC operations") + Container_Boundary(ecModule, "EC Validation Module") { + Container(ecEndpoints, "EC Endpoints", "Actix-web handlers", "REST endpoints for validation operations") + Component(ecService, "EC Service", "Business logic", "Orchestrates validation workflow") + Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output into structured data") + Component(policyManager, "Policy Manager", "Business logic", "Manages EC policy references and
configuration") + Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results") + } } - - Container_Boundary(external, "External System") { - System_Ext(conforma, "Conforma CLI", "Enterprise Contract validation tool") + Deployment_Node(external, "External System") { + Deployment_Node(trustifyPod, "Trustify Pod") { + Component(conformaECWrapper, "EC Wrapper", "HTTP API/Webhook") + } + Deployment_Node(conformaPod, "Conforma Pod") { + System_Ext(conforma, "Conforma CLI", "Enterprise Contract validation tool") + } } Container_Boundary(dbms, "Database") { @@ -194,17 +183,19 @@ C4Component Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") Rel(policyManager, postgres, "SELECT ec_policies", "SQL") - Rel(ecService, conformaExecutor, "request_validation()", "Function call") - Rel(conformaExecutor, conforma, "Runs CLI command", "Process spawn") + Rel(ecService, conformaECWrapper, "POST /api/v1/validation", "HTTP") + Rel(conformaECWrapper, conforma, "ec validate", "Process spawn") Rel(ecService, resultParser, "parse_output()", "Function call") Rel(ecService, resultPersistence, "save_results()", "Function call") Rel(resultPersistence, postgres, "INSERT ec_validation_results", "SQL") Rel(ecService, s3, "Store EC report", "S3 API") UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") + UpdateRelStyle(ecEndpoints, ecService, $offsetX="-60", $offsetY="+40") + UpdateRelStyle(ecService, resultParser, $offsetX="-60", $offsetY="+0") + UpdateRelStyle(ecService, resultPersistence, $offsetX="-60", $offsetY="+80") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") - ``` ### The main sequence Diagram From 99e85c84e027cb830a7d57f4deb53144c5881801 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 3 Mar 2026 13:56:18 +0100 Subject: [PATCH 15/52] Adjust styles for components and containers --- .../00014-enterprise-contract-integration.md | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index b1d7333be..41920406c 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -91,7 +91,7 @@ C4Context System_Ext(conforma, "Conforma", "Enterprise Contract policy validation tool") System_Ext(policyRepo, "Policy Repository", "Git repository or storage containing EC policies") - Rel(user, trustify, "Request Compliance
View compliance status", "API/GUI") + Rel(user, trustify, "Request compliance
View compliance status", "API/GUI") Rel(trustify, conforma, "Executes policy validation", "Spawn Process") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") @@ -112,7 +112,7 @@ C4Container Container_Boundary(trustify, "Trustify System") { - Container(webui, "Web UI", "React/TypeScript", "Trustify GUI") + Container(webui, "Web UI", "Rust/Actix", "Trustify GUI") Container(api, "API Gateway", "Actix-web", "REST API endpoints for SBOM
and compliance operations") ContainerDb(postgres, "PostgreSQL", "DBMS", "Stores SBOM metadata, relationships,
and EC validation results") Container(ecModule, "EC Validation Module", "Rust", "Orchestrates Conforma CLI
execution and result persistence") @@ -121,6 +121,7 @@ C4Container } Container_Boundary(conforma, "Conforma System") { + Container(ecWrapper, "EC Wrapper", "Rust/Actix", "HTTP Wrapper") System_Ext(conforma, "Conforma CLI", "External policy validation tool") } @@ -128,11 +129,12 @@ C4Container System_Ext(policyRepo, "Policy Repository", "Git repository with EC policies") } - Rel(user, webui, "Views compliance status", "HTTPS") - Rel(user, api, "Views compliance status", "RESTful") - Rel(webui, api, "API calls", "JSON/HTTPS") + Rel(user, webui, "Views compliance status", "HTTP API") + Rel(user, api, "Views compliance status", "HTTP API") + Rel(webui, api, "API calls", "JSON/HTTP API") Rel(api, ecModule, "Triggers validation", "Function call") - Rel(ecModule, conforma, "Executes validation", "CLI/Process") + Rel(ecModule, ecWrapper, "POST /validate {SBOM} {policy}", "HTTP API") + Rel(ecWrapper, conforma, "ec validate input {SBOM} {policy}", "Spawned command") Rel(ecModule, postgres, "Saves validation
results", "SQL") Rel(ecModule, storage, "Stores EC reports", "Function call") Rel(storage, s3, "Persists reports", "S3 API") @@ -141,6 +143,7 @@ C4Container UpdateRelStyle(user, webui, $offsetX="-60", $offsetY="30") UpdateRelStyle(user, api, $offsetX="-60", $offsetY="-50") UpdateRelStyle(webui, api, $offsetX="-40", $offsetY="10") + UpdateRelStyle(ecModule, ecWrapper, $offsetX="-50", $offsetY="-20") UpdateRelStyle(ecModule, postgres, $offsetX="-40", $offsetY="10") UpdateRelStyle(storage, s3, $offsetX="-40", $offsetY="10") @@ -165,7 +168,7 @@ C4Component } Deployment_Node(external, "External System") { Deployment_Node(trustifyPod, "Trustify Pod") { - Component(conformaECWrapper, "EC Wrapper", "HTTP API/Webhook") + Component(ecWrapper, "EC Wrapper", "Actix-web handlers", "HTTP API/Webhook") } Deployment_Node(conformaPod, "Conforma Pod") { System_Ext(conforma, "Conforma CLI", "Enterprise Contract validation tool") @@ -183,8 +186,9 @@ C4Component Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") Rel(policyManager, postgres, "SELECT ec_policies", "SQL") - Rel(ecService, conformaECWrapper, "POST /api/v1/validation", "HTTP") - Rel(conformaECWrapper, conforma, "ec validate", "Process spawn") + Rel(ecService, ecWrapper, "POST /api/v1/validation", "HTTP + callback URL") + Rel(ecWrapper, conforma, "ec validate", "Process spawn") + Rel(ecWrapper, ecEndpoints, "POST /validate/job/{id}", "JSON/HTTPS") Rel(ecService, resultParser, "parse_output()", "Function call") Rel(ecService, resultPersistence, "save_results()", "Function call") Rel(resultPersistence, postgres, "INSERT ec_validation_results", "SQL") @@ -192,6 +196,8 @@ C4Component UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") UpdateRelStyle(ecEndpoints, ecService, $offsetX="-60", $offsetY="+40") + UpdateRelStyle(ecService, ecWrapper, $offsetX="-20", $offsetY="10") + UpdateRelStyle(ecWrapper, ecEndpoints, $offsetX="20", $offsetY="-40") UpdateRelStyle(ecService, resultParser, $offsetX="-60", $offsetY="+0") UpdateRelStyle(ecService, resultPersistence, $offsetX="-60", $offsetY="+80") From 26956ca89630aa7c4490afd27b655b734e6e233b Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 3 Mar 2026 14:58:13 +0100 Subject: [PATCH 16/52] Swap titles --- docs/adrs/00014-enterprise-contract-integration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 41920406c..88721bcba 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -102,7 +102,7 @@ C4Context UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1") ``` -### Component Diagram - EC Validation Module +### Container Diagram - EC Validation Module ```mermaid C4Container @@ -150,7 +150,7 @@ C4Container UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` -### Container Diagram +### Component Diagram ```mermaid C4Component From cceec6855a7f355f3b0f4ff87a04683122515110 Mon Sep 17 00:00:00 2001 From: gildub Date: Tue, 3 Mar 2026 19:09:17 +0100 Subject: [PATCH 17/52] Conforma JSON output with HTTP Wrapper feed back --- .../00014-enterprise-contract-integration.md | 204 +++++++++++++----- 1 file changed, 148 insertions(+), 56 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 88721bcba..22062fe03 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -32,12 +32,15 @@ Validation on upload is deferred to a follow-up version. Conforma CLI is deployed separately from Trustify as either a standalone container or equivalent. A HTTP wrapper will act as a proxy between Trustify EC service and Conforma CLI. -Uploaded SBOMs start in "Pending" status and are not discoverable until validated. EC validation is one mechanism by which an SBOM can move from "Pending" to "Accepted" or "Rejected": +Each SBOM + policy pair has a validation state that follows this lifecycle: -- An SBOM in "Pending" can be submitted for EC validation. -- A passing EC result transitions the SBOM to "Accepted". -- A failing EC result transitions it to "Rejected", with the violation details linked. -- An EC execution error (CLI crash, policy fetch failure) does not change the SBOM's policy status — the SBOM stays Pending and the error is surfaced separately, so it doesn't silently block an SBOM. +- **Pending** — default state when an SBOM is uploaded. No validation has been triggered yet for this SBOM against this policy. +- **In Progress** — a user has triggered validation; the request is being processed. Other users can see this state, preventing duplicate validation runs for the same SBOM + policy pair. +- **Pass** — Conforma validation succeeded; the SBOM satisfies the policy. +- **Fail** — Conforma validation found policy violations; violation details are linked. +- **Error** — an execution error occurred (CLI crash, policy fetch failure, timeout). The error is surfaced separately, and the validation can be re-triggered. + +The "In Progress" state serves as a concurrency guard: if a validation is already running for a given SBOM + policy pair, subsequent requests are rejected (409 Conflict), preventing duplicate work. What is stored where @@ -133,7 +136,8 @@ C4Container Rel(user, api, "Views compliance status", "HTTP API") Rel(webui, api, "API calls", "JSON/HTTP API") Rel(api, ecModule, "Triggers validation", "Function call") - Rel(ecModule, ecWrapper, "POST /validate {SBOM} {policy}", "HTTP API") + Rel(ecModule, ecWrapper, "POST /api/v1/validation {SBOM, policy_ref}
← returns validation_id", "HTTP API") + Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "HTTP callback") Rel(ecWrapper, conforma, "ec validate input {SBOM} {policy}", "Spawned command") Rel(ecModule, postgres, "Saves validation
results", "SQL") Rel(ecModule, storage, "Stores EC reports", "Function call") @@ -186,9 +190,9 @@ C4Component Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") Rel(policyManager, postgres, "SELECT ec_policies", "SQL") - Rel(ecService, ecWrapper, "POST /api/v1/validation", "HTTP + callback URL") + Rel(ecService, ecWrapper, "POST /api/v1/validation → returns validation_id", "HTTP") Rel(ecWrapper, conforma, "ec validate", "Process spawn") - Rel(ecWrapper, ecEndpoints, "POST /validate/job/{id}", "JSON/HTTPS") + Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "JSON/HTTPS") Rel(ecService, resultParser, "parse_output()", "Function call") Rel(ecService, resultPersistence, "save_results()", "Function call") Rel(resultPersistence, postgres, "INSERT ec_validation_results", "SQL") @@ -197,68 +201,108 @@ C4Component UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") UpdateRelStyle(ecEndpoints, ecService, $offsetX="-60", $offsetY="+40") UpdateRelStyle(ecService, ecWrapper, $offsetX="-20", $offsetY="10") - UpdateRelStyle(ecWrapper, ecEndpoints, $offsetX="20", $offsetY="-40") + UpdateRelStyle(ecWrapper, api, $offsetX="20", $offsetY="-40") UpdateRelStyle(ecService, resultParser, $offsetX="-60", $offsetY="+0") UpdateRelStyle(ecService, resultPersistence, $offsetX="-60", $offsetY="+80") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` -### The main sequence Diagram +### Sequence Diagram — User Request (synchronous) ```mermaid sequenceDiagram autonumber actor User participant API as Trustify API + participant EP as EC Endpoints participant VS as EC Service participant PM as Policy Manager participant DB as PostgreSQL participant S3 as Object Storage - participant Conf as Conforma CLI + participant Wrapper as EC Wrapper (HTTP) User->>API: POST /api/v2/sbom/{sbom_id}/ec-validate {policy_id} + API->>EP: dispatch request + EP->>VS: validate_sbom(sbom_id, policy_id) VS->>PM: get_policy_configuration(policy_id) PM->>DB: SELECT * FROM ec_policies WHERE id = ? alt Policy not found PM-->>VS: Error: PolicyNotFound - VS-->>API: 404 Not Found + VS-->>EP: 404 Not Found + EP-->>API: 404 Not Found end PM-->>VS: PolicyConfig {name, policy_ref, type} VS->>DB: SELECT * FROM sbom WHERE id = ? alt SBOM not found - VS-->>API: 404 Not Found + VS-->>EP: 404 Not Found + EP-->>API: 404 Not Found end + + VS->>DB: SELECT * FROM ec_validation_results WHERE sbom_id = ? AND policy_id = ? AND status = 'in_progress' + alt Validation already in progress + VS-->>EP: 409 Conflict {existing job_id} + EP-->>API: 409 Conflict + API-->>User: 409 Conflict — validation already in progress + end + VS->>S3: retrieve_sbom_document(sbom_id) S3-->>VS: SBOM document (JSON/XML) - VS->>VS: Write SBOM to temp file - VS->>Conf: conforma validate input --file "$SBOM_PATH" --policy --output json --show-successes --info + VS->>Wrapper: POST /api/v1/validation {SBOM, policy_ref} + Wrapper-->>VS: 202 Accepted {validation_id} + + VS->>DB: INSERT ec_validation_results (status='in_progress', sbom_id, policy_id, validation_id, ...) + VS-->>EP: 202 Accepted {validation_id} + EP-->>API: 202 Accepted {validation_id} + API-->>User: 202 Accepted {validation_id} +``` + +### Sequence Diagram — Async Validation Processing + +```mermaid +sequenceDiagram + autonumber + participant API as Trustify API + participant EP as EC Endpoints + participant VS as EC Service + participant DB as PostgreSQL + participant S3 as Object Storage + participant Wrapper as EC Wrapper (HTTP) + participant Conf as Conforma CLI + + Note over Wrapper: Wrapper holds validation_id from the initial request + + Wrapper->>Wrapper: Write SBOM to temp file + Wrapper->>Conf: ec validate input --file "$SBOM_PATH" --policy --output json --show-successes --info alt Exit code 0 — pass - Conf-->>VS: {result: "PASS", violations: []} - VS->>DB: INSERT ec_validation_results (status='pass', violations=[], ...) + Conf-->>Wrapper: {result: "PASS", violations: []} + else Exit code 1 — fail (policy violations) + Conf-->>Wrapper: {result: "FAIL", violations: [{rule, severity, message}]} + else Exit code 2+ — execution error + Conf-->>Wrapper: stderr: "Policy file not found" + end + + Wrapper->>Wrapper: Cleanup temp files + Wrapper->>API: POST /api/v2/ec/validation/{validation_id}/result {conforma JSON output} + API->>EP: dispatch callback + EP->>VS: process_validation_result(validation_id, result) + + alt Pass + VS->>DB: UPDATE ec_validation_results SET status='pass', violations=[] VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_url = ? - VS->>DB: UPDATE sbom SET policy_status = 'Accepted' - VS-->>API: 200 {passed: true, violations: 0, report_url} - else Exit code 1 — fail (policy violations) - Conf-->>VS: {result: "FAIL", violations: [{rule, severity, message}]} - VS->>DB: INSERT ec_validation_results (status='fail', violations=json, ...) + else Fail + VS->>DB: UPDATE ec_validation_results SET status='fail', violations=json VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_url = ? - VS->>DB: UPDATE sbom SET policy_status = 'Rejected' - VS-->>API: 200 {passed: false, violations: [...], report_url} - else Exit code 2+ — execution error - Conf-->>VS: stderr: "Policy file not found" - VS->>DB: INSERT ec_validation_results (status='error', error_message=stderr) - Note over VS,DB: SBOM policy_status unchanged — stays Pending - VS-->>API: 500 {error: "Validation failed to execute", detail: stderr} + else Error + VS->>DB: UPDATE ec_validation_results SET status='error', error_message=detail + Note over VS,DB: Validation can be re-triggered (new request will create a new row with status='in_progress') end - - VS->>VS: Cleanup temp files ``` ### The Data Model @@ -276,9 +320,10 @@ sequenceDiagram **`ec_validation_results`** - one row per validation execution - `id` (UUID, PK) +- `validation_id` (VARCHAR, unique) - ID returned by the EC Wrapper, used for callback correlation - `sbom_id` (UUID, FK → sbom) - `policy_id` (UUID, FK → ec_policies) -- `status` (VARCHAR) - 'pass', 'fail', 'error' +- `status` (VARCHAR) - 'pending', 'in_progress', 'pass', 'fail', 'error' - `violations` (JSONB) - Structured violation data for querying - `summary` (JSONB) - Total checks, passed, failed, warnings - `report_url` (VARCHAR) - S3 URL to detailed report @@ -290,10 +335,11 @@ sequenceDiagram ### API Endpoints ``` -POST /api/v2/sboms/{id}/ec-validate # Trigger validation -GET /api/v2/sboms/{id}/ec-report # Get latest validation result -GET /api/v2/sboms/{id}/ec-report/history # Get validation history -GET /api/v2/ec/report/{result_id} # Download detailed report from S3 +POST /api/v2/sboms/{id}/ec-validate # Trigger validation +GET /api/v2/sboms/{id}/ec-report # Get latest validation result +GET /api/v2/sboms/{id}/ec-report/history # Get validation history +GET /api/v2/ec/report/{result_id} # Download detailed report from S3 +POST /api/v2/ec/validation/{validation_id}/result # Callback: EC Wrapper posts Conforma result POST /api/v2/ec/policies # Create policy reference (admin) GET /api/v2/ec/policies # List policy references @@ -302,6 +348,28 @@ PUT /api/v2/ec/policies/{id} # Update policy reference (admin) DELETE /api/v2/ec/policies/{id} # Delete policy reference (admin) ``` +### Module Structure + +``` +modules/ec/ +├── Cargo.toml +└── src/ + ├── lib.rs + ├── endpoints/ + │ └── mod.rs # REST endpoints + ├── model/ + │ ├── mod.rs + │ ├── policy.rs # Policy API models + │ └── validation.rs # Validation result models + ├── service/ + │ ├── mod.rs + │ ├── ec_service.rs # Main orchestration + │ ├── policy_manager.rs # Policy configuration + │ ├── executor.rs # Conforma CLI execution + │ └── result_parser.rs # Output parsing + └── error.rs # Error types +``` + ### Technical Considerations #### Conforma CLI Execution @@ -326,26 +394,50 @@ Policy version/commit hash is recorded in the conforma_version field of each res Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. -### Module Structure - -``` -modules/ec/ -├── Cargo.toml -└── src/ - ├── lib.rs - ├── endpoints/ - │ └── mod.rs # REST endpoints - ├── model/ - │ ├── mod.rs - │ ├── policy.rs # Policy API models - │ └── validation.rs # Validation result models - ├── service/ - │ ├── mod.rs - │ ├── ec_service.rs # Main orchestration - │ ├── policy_manager.rs # Policy configuration - │ ├── executor.rs # Conforma CLI execution - │ └── result_parser.rs # Output parsing - └── error.rs # Error types +### Structure of JSON returned from Conformat CLI validation request (from an example) + +```json +{ + "success": false, + "filepaths": [ + { + "filepath": "sboms/registry.redhat.io__rhtas__ec-rhel9__sha256__ea49a30eef5a2948b04540666a12048dd082625ce9f755acd3ece085c7d7937e.json", + "violations": [ + { + "msg": "There are 2942 packages which is more than the permitted maximum of 510.", + "metadata": { + "code": "hello_world.minimal_packages", + "description": "Just an example... To exclude this rule add \"hello_world.minimal_packages\" to the `exclude` section of the policy configuration.", + "solution": "You need to reduce the number of dependencies in this artifact.", + "title": "Check we don't have too many packages" + } + } + ], + "warnings": [], + "successes": [ + { + "msg": "Pass", + "metadata": { + "code": "hello_world.valid_spdxid", + "description": "Make sure that the SPDXID value found in the SBOM matches a list of allowed values.", + "title": "Check for valid SPDXID value" + } + } + ], + "success": false, + "success-count": 1 + } + ], + "policy": { + "sources": [ + { + "policy": ["github.com/conforma/policy//policy/lib", "./policy"] + } + ] + }, + "ec-version": "v0.8.83", + "effective-time": "2026-03-03T14:36:55.807826709Z" +} ``` ### References From 6d18e16e3a8374a81a368aec1798136c3b1bf688 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 3 Mar 2026 20:32:04 +0100 Subject: [PATCH 18/52] Fix inconsitencies --- .../00014-enterprise-contract-integration.md | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 22062fe03..6847a6fb5 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -30,11 +30,11 @@ Validation is manually triggered — not automatic on SBOM upload. Validation on upload is deferred to a follow-up version. Conforma CLI is deployed separately from Trustify as either a standalone container or equivalent. -A HTTP wrapper will act as a proxy between Trustify EC service and Conforma CLI. +An EC Wrapper (HTTP service) acts as a proxy between Trustify's EC service and Conforma CLI. Each SBOM + policy pair has a validation state that follows this lifecycle: -- **Pending** — default state when an SBOM is uploaded. No validation has been triggered yet for this SBOM against this policy. +- **Pending** — initial state, set when an SBOM is associated with a policy (e.g., a default policy assigned at SBOM upload time; upload itself is outside the scope of this ADR). Indicates no validation has been triggered yet for this SBOM against this policy. - **In Progress** — a user has triggered validation; the request is being processed. Other users can see this state, preventing duplicate validation runs for the same SBOM + policy pair. - **Pass** — Conforma validation succeeded; the SBOM satisfies the policy. - **Fail** — Conforma validation found policy violations; violation details are linked. @@ -52,11 +52,9 @@ Storing full JSON in S3 rather than only a summary was chosen explicitly to pres ## Consequences -Using a HTTP API wrapper decouples the validation process into a external service. -This will better catter for large-scale deployments as EC validation has its own constraint. -Meanwhile it adds infrastructure complexity as the Webhook will need to be deployed alongside the EC system +Using an EC Wrapper decouples the validation process into an external service. This better caters for large-scale deployments as EC validation has its own resource constraints. Meanwhile it adds infrastructure complexity as the EC Wrapper must be deployed and maintained alongside the Conforma CLI. -Integrating via CLI spawning rather than a native API introduces an external process dependency that adds operational overhead (Conforma must be installed and version-pinned on every server) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma API exists yet. The executor is built behind an adapter interface so the implementation can be swapped for a REST client in Phase 3 without changes to the service layer or API. +Within the EC Wrapper, Conforma is invoked via CLI spawning rather than a native API. This introduces an operational dependency (Conforma must be installed and version-pinned on every EC Wrapper instance) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma REST API exists yet. On the Trustify side, the EC service interacts with the EC Wrapper over HTTP and is built behind an adapter interface, so the implementation can be swapped for a direct Conforma REST client when one becomes available, without changes to the service layer or API. ### Alternatives Considered @@ -78,7 +76,7 @@ A Redis/RabbitMQ queue would improve retry handling and priority management; imp ### Future API Migration -When Conforma provides a REST API, the executor.rs adapter is replaced with an HTTP client. A feature flag (ec-api-mode) allows gradual migration. No changes to service layer, API endpoints, or UI are required. +When Conforma provides a REST API, the EC Wrapper can be replaced by pointing Trustify's adapter directly at the Conforma REST endpoint. A feature flag (`ec-api-mode`) allows gradual migration. No changes to the service layer, API endpoints, or UI are required. ## The solution @@ -95,7 +93,7 @@ C4Context System_Ext(policyRepo, "Policy Repository", "Git repository or storage containing EC policies") Rel(user, trustify, "Request compliance
View compliance status", "API/GUI") - Rel(trustify, conforma, "Executes policy validation", "Spawn Process") + Rel(trustify, conforma, "Triggers policy validation", "HTTP API") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") UpdateRelStyle(user, trustify, $offsetX="-50", $offsetY="20") @@ -118,17 +116,17 @@ C4Container Container(webui, "Web UI", "Rust/Actix", "Trustify GUI") Container(api, "API Gateway", "Actix-web", "REST API endpoints for SBOM
and compliance operations") ContainerDb(postgres, "PostgreSQL", "DBMS", "Stores SBOM metadata, relationships,
and EC validation results") - Container(ecModule, "EC Validation Module", "Rust", "Orchestrates Conforma CLI
execution and result persistence") + Container(ecModule, "EC Validation Module", "Rust", "Orchestrates validation via
EC Wrapper and persists results") ContainerDb(s3, "Object Storage", "S3/Minio", "Stores SBOM documents and EC reports") Container(storage, "Storage Service", "Rust", "Manages document storage
(SBOMs, policy results)") } - Container_Boundary(conforma, "Conforma System") { + Container_Boundary(conformaSystem, "Conforma System") { Container(ecWrapper, "EC Wrapper", "Rust/Actix", "HTTP Wrapper") System_Ext(conforma, "Conforma CLI", "External policy validation tool") } - Container_Boundary(policyRepo, "Policy System") { + Container_Boundary(policySystem, "Policy System") { System_Ext(policyRepo, "Policy Repository", "Git repository with EC policies") } @@ -172,7 +170,7 @@ C4Component } Deployment_Node(external, "External System") { Deployment_Node(trustifyPod, "Trustify Pod") { - Component(ecWrapper, "EC Wrapper", "Actix-web handlers", "HTTP API/Webhook") + Component(ecWrapper, "EC Wrapper", "Actix-web handlers", "HTTP API") } Deployment_Node(conformaPod, "Conforma Pod") { System_Ext(conforma, "Conforma CLI", "Enterprise Contract validation tool") @@ -307,7 +305,7 @@ sequenceDiagram ### The Data Model -**`ec_policy`** - Stores references to external policies, not the policies themselves +**`ec_policies`** - Stores references to external policies, not the policies themselves - `id` (UUID, PK) - `name` (VARCHAR, unique) - User-friendly name label @@ -329,7 +327,8 @@ sequenceDiagram - `report_url` (VARCHAR) - S3 URL to detailed report - `executed_at` (TIMESTAMP) - `execution_duration_ms` (INTEGER) -- `conforma_version` (VARCHAR) - For reproducibility +- `conforma_version` (VARCHAR) - Conforma CLI version used (e.g., `v0.8.83`), for reproducibility +- `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status ### API Endpoints @@ -365,16 +364,16 @@ modules/ec/ │ ├── mod.rs │ ├── ec_service.rs # Main orchestration │ ├── policy_manager.rs # Policy configuration - │ ├── executor.rs # Conforma CLI execution + │ ├── executor.rs # EC Wrapper HTTP client (adapter) │ └── result_parser.rs # Output parsing └── error.rs # Error types ``` ### Technical Considerations -#### Conforma CLI Execution +#### Conforma CLI Execution (EC Wrapper) -Conforma is invoked via tokio::process::Command to avoid blocking the async runtime. All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are streamed to a temp file and passed by path rather than piped via stdin, which avoids OOM issues with the parent process. +The EC Wrapper invokes Conforma via process spawning (e.g., `tokio::process::Command`). All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are written to a temp file and passed by path rather than piped via stdin, which avoids OOM issues. Exit codes are treated as follows: 0 = pass, 1 = policy violations (expected failure, not an error), 2+ = execution error. It is important to distinguish 1 from 2+ in error handling — a policy violation is a valid result that should be surfaced to the user, not treated as a system failure. @@ -382,19 +381,19 @@ Temp files (SBOM, any cached policy material) are cleaned up in a finally-equiva #### Concurrency and Backpressure -Concurrent Conforma processes are bounded by a semaphore (default: 5). When the semaphore is exhausted, incoming validation requests return 429 Too Many Requests immediately rather than queuing or blocking indefinitely. This makes the capacity limit explicit to callers (e.g. CI pipelines can implement their own retry with backoff). If demand grows to warrant it, a proper queue (Redis/RabbitMQ) is the deferred alternative considered below. +On the EC Wrapper side, concurrent Conforma processes are bounded by a semaphore (default: 5). When the semaphore is exhausted, the EC Wrapper returns 429 Too Many Requests to Trustify, which propagates the status to the caller. This makes the capacity limit explicit to callers (e.g., CI pipelines can implement their own retry with backoff). On the Trustify side, the "In Progress" concurrency guard (409 Conflict) prevents duplicate validation runs for the same SBOM + policy pair. If demand grows to warrant it, a proper queue (Redis/RabbitMQ) is the deferred alternative considered below. #### Policy Management ec_policies stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and must be encrypted at rest; they are never logged. -Policy version/commit hash is recorded in the conforma_version field of each result row, enabling reproducibility and audit. +The Conforma CLI version and the policy commit hash/tag resolved at validation time are recorded in each result row (`conforma_version` and `policy_version` respectively), enabling reproducibility and audit. #### Multi-tenancy Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. -### Structure of JSON returned from Conformat CLI validation request (from an example) +### Structure of JSON returned from Conforma CLI validation request (from an example) ```json { From 3ff66ed09d518e6983040a43ead7fbda082021a7 Mon Sep 17 00:00:00 2001 From: arpcv Date: Thu, 5 Mar 2026 09:43:23 +0100 Subject: [PATCH 19/52] ec_validation_result is Singular --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 6847a6fb5..546552e63 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -315,7 +315,7 @@ sequenceDiagram - `configuration` (JSONB) - Branch, tag, auth credentials, etc. - `created_at`, `updated_at` (TIMESTAMP) -**`ec_validation_results`** - one row per validation execution +**`ec_validation_result`** - one row per validation execution - `id` (UUID, PK) - `validation_id` (VARCHAR, unique) - ID returned by the EC Wrapper, used for callback correlation From acf5df737e4dc592dd00be3a63815107ea1a81e3 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 11 Mar 2026 10:24:54 +0100 Subject: [PATCH 20/52] Adding more details --- .../00014-enterprise-contract-integration.md | 73 ++++++++++++------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 546552e63..a94b7cb25 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -34,7 +34,7 @@ An EC Wrapper (HTTP service) acts as a proxy between Trustify's EC service and C Each SBOM + policy pair has a validation state that follows this lifecycle: -- **Pending** — initial state, set when an SBOM is associated with a policy (e.g., a default policy assigned at SBOM upload time; upload itself is outside the scope of this ADR). Indicates no validation has been triggered yet for this SBOM against this policy. +- **Pending** — initial state, set when an SBOM is associated with a policy. Indicates no validation has been triggered yet for this SBOM against this policy. - **In Progress** — a user has triggered validation; the request is being processed. Other users can see this state, preventing duplicate validation runs for the same SBOM + policy pair. - **Pass** — Conforma validation succeeded; the SBOM satisfies the policy. - **Fail** — Conforma validation found policy violations; violation details are linked. @@ -74,10 +74,6 @@ Conforma is not available as WASM and would require major upstream changes. A Redis/RabbitMQ queue would improve retry handling and priority management; implement if the 429-based rejection approach proves insufficient under real load. -### Future API Migration - -When Conforma provides a REST API, the EC Wrapper can be replaced by pointing Trustify's adapter directly at the Conforma REST endpoint. A feature flag (`ec-api-mode`) allows gradual migration. No changes to the service layer, API endpoints, or UI are required. - ## The solution ### System Architecture @@ -108,10 +104,7 @@ C4Context ```mermaid C4Container title Enterprise Contract Integration - Container Diagram - Person(user, "Trustify User", "Software engineer or security analyst") - - Container_Boundary(trustify, "Trustify System") { Container(webui, "Web UI", "Rust/Actix", "Trustify GUI") Container(api, "API Gateway", "Actix-web", "REST API endpoints for SBOM
and compliance operations") @@ -130,24 +123,33 @@ C4Container System_Ext(policyRepo, "Policy Repository", "Git repository with EC policies") } + Container_Boundary(oidc, "OIDC") { + System_Ext(oidc, "OIDC", "OPenID Connect OAuth 2.0 + ", "") + } + Rel(user, webui, "Views compliance status", "HTTP API") Rel(user, api, "Views compliance status", "HTTP API") Rel(webui, api, "API calls", "JSON/HTTP API") Rel(api, ecModule, "Triggers validation", "Function call") - Rel(ecModule, ecWrapper, "POST /api/v1/validation {SBOM, policy_ref}
← returns validation_id", "HTTP API") - Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "HTTP callback") + Rel(ecModule, ecWrapper, "POST /validation", "HTTP API formData") + Rel(ecWrapper, api, "POST /validation/{id}/result", "HTTP API") Rel(ecWrapper, conforma, "ec validate input {SBOM} {policy}", "Spawned command") Rel(ecModule, postgres, "Saves validation
results", "SQL") Rel(ecModule, storage, "Stores EC reports", "Function call") Rel(storage, s3, "Persists reports", "S3 API") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") + Rel(ecWrapper, oidc, "Authenticate", "OAuth API") UpdateRelStyle(user, webui, $offsetX="-60", $offsetY="30") UpdateRelStyle(user, api, $offsetX="-60", $offsetY="-50") UpdateRelStyle(webui, api, $offsetX="-40", $offsetY="10") UpdateRelStyle(ecModule, ecWrapper, $offsetX="-50", $offsetY="-20") + UpdateRelStyle(ecWrapper, api, $offsetX="-60", $offsetY="-10") UpdateRelStyle(ecModule, postgres, $offsetX="-40", $offsetY="10") UpdateRelStyle(storage, s3, $offsetX="-40", $offsetY="10") + UpdateRelStyle(conforma, policyRepo, $offsetX="-40", $offsetY="100") + UpdateRelStyle(ecWrapper, oidc, $offsetX="30", $offsetY="40") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` @@ -180,7 +182,7 @@ C4Component Container_Boundary(dbms, "Database") { ContainerDb(postgres, "PostgreSQL", "Database", "Stores validation results and policy references") } - Container_Boundary(storage, "S3 System") { + Container_Boundary(storage, "S3 System") { System_Ext(s3, "S3 Object Storage", "Stores SBOM documents and reports") } @@ -188,7 +190,7 @@ C4Component Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") Rel(policyManager, postgres, "SELECT ec_policies", "SQL") - Rel(ecService, ecWrapper, "POST /api/v1/validation → returns validation_id", "HTTP") + Rel(ecService, ecWrapper, "POST /api/v1/validation → returns {id}", "HTTP") Rel(ecWrapper, conforma, "ec validate", "Process spawn") Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "JSON/HTTPS") Rel(ecService, resultParser, "parse_output()", "Function call") @@ -220,7 +222,7 @@ sequenceDiagram participant S3 as Object Storage participant Wrapper as EC Wrapper (HTTP) - User->>API: POST /api/v2/sbom/{sbom_id}/ec-validate {policy_id} + User->>API: POST /api/v2/sbom/{sbom_id}/ec-validate/{policy_id} API->>EP: dispatch request EP->>VS: validate_sbom(sbom_id, policy_id) @@ -318,36 +320,46 @@ sequenceDiagram **`ec_validation_result`** - one row per validation execution - `id` (UUID, PK) -- `validation_id` (VARCHAR, unique) - ID returned by the EC Wrapper, used for callback correlation - `sbom_id` (UUID, FK → sbom) - `policy_id` (UUID, FK → ec_policies) -- `status` (VARCHAR) - 'pending', 'in_progress', 'pass', 'fail', 'error' +- `status` (ENUM) - 'pending', 'in_progress', 'pass', 'fail', 'error' - `violations` (JSONB) - Structured violation data for querying - `summary` (JSONB) - Total checks, passed, failed, warnings - `report_url` (VARCHAR) - S3 URL to detailed report -- `executed_at` (TIMESTAMP) -- `execution_duration_ms` (INTEGER) +- `start_time` (TIMESTAMP) +- `end_time` (TIMESTAMP) - `conforma_version` (VARCHAR) - Conforma CLI version used (e.g., `v0.8.83`), for reproducibility - `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status -### API Endpoints +### Trustify API Endpoints ``` -POST /api/v2/sboms/{id}/ec-validate # Trigger validation +POST /api/v2/sboms/{id}/ec-validate # Trigger validation GET /api/v2/sboms/{id}/ec-report # Get latest validation result GET /api/v2/sboms/{id}/ec-report/history # Get validation history GET /api/v2/ec/report/{result_id} # Download detailed report from S3 POST /api/v2/ec/validation/{validation_id}/result # Callback: EC Wrapper posts Conforma result -POST /api/v2/ec/policies # Create policy reference (admin) -GET /api/v2/ec/policies # List policy references -GET /api/v2/ec/policies/{id} # Get policy reference -PUT /api/v2/ec/policies/{id} # Update policy reference (admin) -DELETE /api/v2/ec/policies/{id} # Delete policy reference (admin) +POST /api/v2/ec/policy # Create policy reference (admin) +GET /api/v2/ec/policy # List policy references +GET /api/v2/ec/policy/{id} # Get policy reference +PUT /api/v2/ec/policy/{id} # Update policy reference (admin) +DELETE /api/v2/ec/policy/{id} # Delete policy reference (admin) ``` -### Module Structure +## Conforma HTTP Wrapper API Endpoints + +### POST `/api/v1/validate` + +Validate the uploaded SBOM file against the provided rule URL. + +#### Response + +- 200 - if the validation request was accepted +- 401 - if the user was not authenticated + +### Trustify Module Structure ``` modules/ec/ @@ -369,6 +381,17 @@ modules/ec/ └── error.rs # Error types ``` +### HTTP Wrapper Module Structure + +``` +├── Cargo.toml +└── server + ├── lib.rs + ├── endpoints/ + │ └── mod.rs # REST endpoints + └── error.rs # Error types +``` + ### Technical Considerations #### Conforma CLI Execution (EC Wrapper) From a1fef11d91196d6ba9938a7b10c10ea436dbe0b4 Mon Sep 17 00:00:00 2001 From: arpcv Date: Thu, 12 Mar 2026 12:12:05 +0100 Subject: [PATCH 21/52] Use of a default Policy; Storage system and report_path --- docs/adrs/00014-enterprise-contract-integration.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index a94b7cb25..d0362bd58 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -34,7 +34,7 @@ An EC Wrapper (HTTP service) acts as a proxy between Trustify's EC service and C Each SBOM + policy pair has a validation state that follows this lifecycle: -- **Pending** — initial state, set when an SBOM is associated with a policy. Indicates no validation has been triggered yet for this SBOM against this policy. +- **Pending** — initial state, indicates no validation has been triggered yet for this SBOM against this policy. - **In Progress** — a user has triggered validation; the request is being processed. Other users can see this state, preventing duplicate validation runs for the same SBOM + policy pair. - **Pass** — Conforma validation succeeded; the SBOM satisfies the policy. - **Fail** — Conforma validation found policy violations; violation details are linked. @@ -45,10 +45,10 @@ The "In Progress" state serves as a concurrency guard: if a validation is alread What is stored where - PostgreSQL: validation status, structured violations (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, status, executed_at. -- S3/Minio: full raw Conforma JSON report, linked from the DB row via report_url. Keeps DB rows small while preserving audit completeness. +- Storage system: full raw Conforma JSON report, linked from the DB row via report_path. Keeps DB rows small while preserving audit completeness. - Not stored: the policy definitions themselves. ec_policies stores references (URLs, OCI refs) that Conforma fetches at runtime. -Storing full JSON in S3 rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB violations JSONB holds enough structure for filtering and dashboards without duplicating the full payload. +Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB violations JSONB holds enough structure for filtering and dashboards without duplicating the full payload. ## Consequences @@ -294,11 +294,11 @@ sequenceDiagram alt Pass VS->>DB: UPDATE ec_validation_results SET status='pass', violations=[] VS->>S3: store_validation_report(result_id, full_json) - VS->>DB: UPDATE SET report_url = ? + VS->>DB: UPDATE SET ureport_path = ? else Fail VS->>DB: UPDATE ec_validation_results SET status='fail', violations=json VS->>S3: store_validation_report(result_id, full_json) - VS->>DB: UPDATE SET report_url = ? + VS->>DB: UPDATE SET report_part = ? else Error VS->>DB: UPDATE ec_validation_results SET status='error', error_message=detail Note over VS,DB: Validation can be re-triggered (new request will create a new row with status='in_progress') @@ -325,7 +325,7 @@ sequenceDiagram - `status` (ENUM) - 'pending', 'in_progress', 'pass', 'fail', 'error' - `violations` (JSONB) - Structured violation data for querying - `summary` (JSONB) - Total checks, passed, failed, warnings -- `report_url` (VARCHAR) - S3 URL to detailed report +- `report_path` (VARCHAR) - File system or S3 path to detailed report - `start_time` (TIMESTAMP) - `end_time` (TIMESTAMP) - `conforma_version` (VARCHAR) - Conforma CLI version used (e.g., `v0.8.83`), for reproducibility From 8aaf28ddd6a53bd19f1a1235f302d240161a7330 Mon Sep 17 00:00:00 2001 From: arpcv Date: Thu, 12 Mar 2026 12:19:15 +0100 Subject: [PATCH 22/52] Explaining the Policy handling and the default one --- docs/adrs/00014-enterprise-contract-integration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index d0362bd58..c1aff0e87 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -28,6 +28,8 @@ Users need the ability to: We will integrate Conforma into Trustify as a user triggered validation service by interacting with Conforma CLI. Validation is manually triggered — not automatic on SBOM upload. Validation on upload is deferred to a follow-up version. +Trustify stores information to identify (id, name, URL) of Policies. +A defaut Policy is defined at the application level which will be the Policy used for validation if a SBOM doesn't have a Policy attached to it. Conforma CLI is deployed separately from Trustify as either a standalone container or equivalent. An EC Wrapper (HTTP service) acts as a proxy between Trustify's EC service and Conforma CLI. From bdf3ac2aac8442593ea0877783fbe211a8399189 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 12 Mar 2026 17:49:35 +0100 Subject: [PATCH 23/52] Remove conforma_version --- docs/adrs/00014-enterprise-contract-integration.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index c1aff0e87..a1c4b92a1 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -330,7 +330,6 @@ sequenceDiagram - `report_path` (VARCHAR) - File system or S3 path to detailed report - `start_time` (TIMESTAMP) - `end_time` (TIMESTAMP) -- `conforma_version` (VARCHAR) - Conforma CLI version used (e.g., `v0.8.83`), for reproducibility - `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status @@ -410,9 +409,9 @@ On the EC Wrapper side, concurrent Conforma processes are bounded by a semaphore #### Policy Management -ec_policies stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and must be encrypted at rest; they are never logged. +ec_policies stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and will be encrypted using AES crate; they are never logged. -The Conforma CLI version and the policy commit hash/tag resolved at validation time are recorded in each result row (`conforma_version` and `policy_version` respectively), enabling reproducibility and audit. +The policy commit hash/tag (`policy_version`) resolved at validation time are recorded in each result row, enabling reproducibility and audit. #### Multi-tenancy From 22c49e8ff89a8986cb2ceb37a013033ed9619d02 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 12 Mar 2026 17:50:28 +0100 Subject: [PATCH 24/52] Replace with http wrapper --- docs/adrs/00014-enterprise-contract-integration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index a1c4b92a1..8a8e931c0 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -395,9 +395,9 @@ modules/ec/ ### Technical Considerations -#### Conforma CLI Execution (EC Wrapper) +#### Conforma CLI Execution (HTTP Wrapper) -The EC Wrapper invokes Conforma via process spawning (e.g., `tokio::process::Command`). All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are written to a temp file and passed by path rather than piped via stdin, which avoids OOM issues. +The HTTP Wrapper invokes Conforma via process spawning (e.g., `tokio::process::Command`). All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are written to a temp file and passed by path rather than piped via stdin, which avoids OOM issues. Exit codes are treated as follows: 0 = pass, 1 = policy violations (expected failure, not an error), 2+ = execution error. It is important to distinguish 1 from 2+ in error handling — a policy violation is a valid result that should be surfaced to the user, not treated as a system failure. From ff99e3a3c971f07f161ed1577ff0f78665c5598d Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 12 Mar 2026 18:22:23 +0100 Subject: [PATCH 25/52] HTTP Wrapper API --- docs/adrs/00014-enterprise-contract-integration.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 8a8e931c0..7ba45e535 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -351,14 +351,9 @@ DELETE /api/v2/ec/policy/{id} # Delete policy reference (admin) ## Conforma HTTP Wrapper API Endpoints -### POST `/api/v1/validate` - -Validate the uploaded SBOM file against the provided rule URL. - -#### Response - -- 200 - if the validation request was accepted -- 401 - if the user was not authenticated +``` +POST /api/v1/validate # Validate uploaded SBOM file against the provided Policy URL +``` ### Trustify Module Structure @@ -395,7 +390,7 @@ modules/ec/ ### Technical Considerations -#### Conforma CLI Execution (HTTP Wrapper) +#### Conforma CLI Execution (HTTPEC Wrapper) The HTTP Wrapper invokes Conforma via process spawning (e.g., `tokio::process::Command`). All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are written to a temp file and passed by path rather than piped via stdin, which avoids OOM issues. From 8e21a08299c0e256f71d19ce05fe4f1562aadeb1 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 17 Mar 2026 15:17:39 +0100 Subject: [PATCH 26/52] Two states --- .../00014-enterprise-contract-integration.md | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 7ba45e535..3be4d16a4 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -29,26 +29,35 @@ We will integrate Conforma into Trustify as a user triggered validation service Validation is manually triggered — not automatic on SBOM upload. Validation on upload is deferred to a follow-up version. Trustify stores information to identify (id, name, URL) of Policies. -A defaut Policy is defined at the application level which will be the Policy used for validation if a SBOM doesn't have a Policy attached to it. +A default Policy is defined at the application level (global policy) which is used for validation when an SBOM does not have any Policy explicitly attached to it. +An SBOM can also carry references to one or more Policies. This one-to-many relationship between SBOM and Policy is modeled through a join table (`sbom_ec_policy`). Conforma CLI is deployed separately from Trustify as either a standalone container or equivalent. An EC Wrapper (HTTP service) acts as a proxy between Trustify's EC service and Conforma CLI. -Each SBOM + policy pair has a validation state that follows this lifecycle: +Each SBOM + policy pair has two validation states . + +The validation process state of the EC Wrapper follows this lifecycle: + +- **Queued** — a user has triggered validation; the request is being processed. Other users can see this state, preventing duplicate validation runs for the same SBOM + policy pair. +- **In Progress** — the request has been submitted to EC Wrapper. +- **Completed** — the outcome of the request has been received back from EC Wrapper. +- **Failed** — an execution error occurred (CLI crash, policy fetch failure, timeout). The error is surfaced separately, and the validation can be re-triggered. + +The Policy validation outcome follows this lifecycle: - **Pending** — initial state, indicates no validation has been triggered yet for this SBOM against this policy. -- **In Progress** — a user has triggered validation; the request is being processed. Other users can see this state, preventing duplicate validation runs for the same SBOM + policy pair. -- **Pass** — Conforma validation succeeded; the SBOM satisfies the policy. - **Fail** — Conforma validation found policy violations; violation details are linked. -- **Error** — an execution error occurred (CLI crash, policy fetch failure, timeout). The error is surfaced separately, and the validation can be re-triggered. +- **Pass** — Conforma validation succeeded; the SBOM satisfies the policy. +- **Error** — The Conforma validation has generated an error. -The "In Progress" state serves as a concurrency guard: if a validation is already running for a given SBOM + policy pair, subsequent requests are rejected (409 Conflict), preventing duplicate work. +The `ec_status` "In Progress" state serves as a concurrency guard: if a validation is already running for a given SBOM + policy pair, subsequent requests are rejected (409 Conflict), preventing duplicate work. What is stored where -- PostgreSQL: validation status, structured violations (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, status, executed_at. +- PostgreSQL: validation process state (`ec_status`), validation outcome (`status`), structured violations (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, ec_status, status, start_time. - Storage system: full raw Conforma JSON report, linked from the DB row via report_path. Keeps DB rows small while preserving audit completeness. -- Not stored: the policy definitions themselves. ec_policies stores references (URLs, OCI refs) that Conforma fetches at runtime. +- Not stored: the policy definitions themselves. ec_policy stores references (URLs, OCI refs) that Conforma fetches at runtime. Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB violations JSONB holds enough structure for filtering and dashboards without duplicating the full payload. @@ -191,13 +200,13 @@ C4Component Rel(api, ecEndpoints, "POST /sboms/{id}/ec-validate,\nGET /sboms/{id}/ec-report", "JSON/HTTPS") Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") - Rel(policyManager, postgres, "SELECT ec_policies", "SQL") + Rel(policyManager, postgres, "SELECT ec_policy", "SQL") Rel(ecService, ecWrapper, "POST /api/v1/validation → returns {id}", "HTTP") Rel(ecWrapper, conforma, "ec validate", "Process spawn") Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "JSON/HTTPS") Rel(ecService, resultParser, "parse_output()", "Function call") Rel(ecService, resultPersistence, "save_results()", "Function call") - Rel(resultPersistence, postgres, "INSERT ec_validation_results", "SQL") + Rel(resultPersistence, postgres, "INSERT ec_validation_result", "SQL") Rel(ecService, s3, "Store EC report", "S3 API") UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") @@ -229,7 +238,7 @@ sequenceDiagram EP->>VS: validate_sbom(sbom_id, policy_id) VS->>PM: get_policy_configuration(policy_id) - PM->>DB: SELECT * FROM ec_policies WHERE id = ? + PM->>DB: SELECT * FROM ec_policy WHERE id = ? alt Policy not found PM-->>VS: Error: PolicyNotFound VS-->>EP: 404 Not Found @@ -243,7 +252,7 @@ sequenceDiagram EP-->>API: 404 Not Found end - VS->>DB: SELECT * FROM ec_validation_results WHERE sbom_id = ? AND policy_id = ? AND status = 'in_progress' + VS->>DB: SELECT * FROM ec_validation_result WHERE sbom_id = ? AND policy_id = ? AND ec_status IN ('queued', 'in_progress') alt Validation already in progress VS-->>EP: 409 Conflict {existing job_id} EP-->>API: 409 Conflict @@ -256,7 +265,7 @@ sequenceDiagram VS->>Wrapper: POST /api/v1/validation {SBOM, policy_ref} Wrapper-->>VS: 202 Accepted {validation_id} - VS->>DB: INSERT ec_validation_results (status='in_progress', sbom_id, policy_id, validation_id, ...) + VS->>DB: INSERT ec_validation_result (ec_status='in_progress', status='pending', sbom_id, policy_id, validation_id, ...) VS-->>EP: 202 Accepted {validation_id} EP-->>API: 202 Accepted {validation_id} API-->>User: 202 Accepted {validation_id} @@ -294,22 +303,22 @@ sequenceDiagram EP->>VS: process_validation_result(validation_id, result) alt Pass - VS->>DB: UPDATE ec_validation_results SET status='pass', violations=[] + VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='pass', violations=[] VS->>S3: store_validation_report(result_id, full_json) - VS->>DB: UPDATE SET ureport_path = ? + VS->>DB: UPDATE SET report_path = ? else Fail - VS->>DB: UPDATE ec_validation_results SET status='fail', violations=json + VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='fail', violations=json VS->>S3: store_validation_report(result_id, full_json) - VS->>DB: UPDATE SET report_part = ? + VS->>DB: UPDATE SET report_path = ? else Error - VS->>DB: UPDATE ec_validation_results SET status='error', error_message=detail - Note over VS,DB: Validation can be re-triggered (new request will create a new row with status='in_progress') + VS->>DB: UPDATE ec_validation_result SET ec_status='failed', status='error', error_message=detail + Note over VS,DB: Validation can be re-triggered (new row with ec_status='in_progress', status='pending') end ``` ### The Data Model -**`ec_policies`** - Stores references to external policies, not the policies themselves +**`ec_policy`** - Stores references to external policies, not the policies themselves - `id` (UUID, PK) - `name` (VARCHAR, unique) - User-friendly name label @@ -319,12 +328,19 @@ sequenceDiagram - `configuration` (JSONB) - Branch, tag, auth credentials, etc. - `created_at`, `updated_at` (TIMESTAMP) +**`sbom_ec_policy`** - Join table linking SBOMs to their attached policies (one SBOM → many policies) + +- `sbom_id` (UUID, FK → sbom, PK) +- `policy_id` (UUID, FK → ec_policy, PK) +- `created_at` (TIMESTAMP) + **`ec_validation_result`** - one row per validation execution - `id` (UUID, PK) - `sbom_id` (UUID, FK → sbom) -- `policy_id` (UUID, FK → ec_policies) -- `status` (ENUM) - 'pending', 'in_progress', 'pass', 'fail', 'error' +- `policy_id` (UUID, FK → ec_policy) +- `ec_status` (ENUM) - 'queued', 'in_progress', 'completed', 'failed' +- `status` (ENUM) - 'pending', 'pass', 'fail', 'error' - `violations` (JSONB) - Structured violation data for querying - `summary` (JSONB) - Total checks, passed, failed, warnings - `report_path` (VARCHAR) - File system or S3 path to detailed report @@ -336,9 +352,10 @@ sequenceDiagram ### Trustify API Endpoints ``` -POST /api/v2/sboms/{id}/ec-validate # Trigger validation -GET /api/v2/sboms/{id}/ec-report # Get latest validation result -GET /api/v2/sboms/{id}/ec-report/history # Get validation history +POST /api/v2/sbom/{id}/ec-validate # Trigger validation +GET /api/v2/sbom/{id}/ec-report # Get latest validation result +GET /api/v2/sbom/{id}/ec-report/history # Get validation history + GET /api/v2/ec/report/{result_id} # Download detailed report from S3 POST /api/v2/ec/validation/{validation_id}/result # Callback: EC Wrapper posts Conforma result @@ -400,11 +417,11 @@ Temp files (SBOM, any cached policy material) are cleaned up in a finally-equiva #### Concurrency and Backpressure -On the EC Wrapper side, concurrent Conforma processes are bounded by a semaphore (default: 5). When the semaphore is exhausted, the EC Wrapper returns 429 Too Many Requests to Trustify, which propagates the status to the caller. This makes the capacity limit explicit to callers (e.g., CI pipelines can implement their own retry with backoff). On the Trustify side, the "In Progress" concurrency guard (409 Conflict) prevents duplicate validation runs for the same SBOM + policy pair. If demand grows to warrant it, a proper queue (Redis/RabbitMQ) is the deferred alternative considered below. +On the EC Wrapper side, concurrent Conforma processes are bounded by a semaphore (default: 5). When the semaphore is exhausted, the EC Wrapper returns 429 Too Many Requests to Trustify, which propagates the status to the caller. This makes the capacity limit explicit to callers (e.g., CI pipelines can implement their own retry with backoff). On the Trustify side, the `ec_status` "In Progress" concurrency guard (409 Conflict) prevents duplicate validation runs for the same SBOM + policy pair. If demand grows to warrant it, a proper queue (Redis/RabbitMQ) is the deferred alternative considered below. #### Policy Management -ec_policies stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and will be encrypted using AES crate; they are never logged. +ec_policy stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and will be encrypted using AES crate; they are never logged. The policy commit hash/tag (`policy_version`) resolved at validation time are recorded in each result row, enabling reproducibility and audit. From ddfa48f48224b1f80060bbd3f55a1314e5b5bbf9 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 17 Mar 2026 15:52:56 +0100 Subject: [PATCH 27/52] Remove no used table --- .../00014-enterprise-contract-integration.md | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 3be4d16a4..2443006e9 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -30,7 +30,6 @@ Validation is manually triggered — not automatic on SBOM upload. Validation on upload is deferred to a follow-up version. Trustify stores information to identify (id, name, URL) of Policies. A default Policy is defined at the application level (global policy) which is used for validation when an SBOM does not have any Policy explicitly attached to it. -An SBOM can also carry references to one or more Policies. This one-to-many relationship between SBOM and Policy is modeled through a join table (`sbom_ec_policy`). Conforma CLI is deployed separately from Trustify as either a standalone container or equivalent. An EC Wrapper (HTTP service) acts as a proxy between Trustify's EC service and Conforma CLI. @@ -328,12 +327,6 @@ sequenceDiagram - `configuration` (JSONB) - Branch, tag, auth credentials, etc. - `created_at`, `updated_at` (TIMESTAMP) -**`sbom_ec_policy`** - Join table linking SBOMs to their attached policies (one SBOM → many policies) - -- `sbom_id` (UUID, FK → sbom, PK) -- `policy_id` (UUID, FK → ec_policy, PK) -- `created_at` (TIMESTAMP) - **`ec_validation_result`** - one row per validation execution - `id` (UUID, PK) @@ -352,24 +345,24 @@ sequenceDiagram ### Trustify API Endpoints ``` -POST /api/v2/sbom/{id}/ec-validate # Trigger validation -GET /api/v2/sbom/{id}/ec-report # Get latest validation result -GET /api/v2/sbom/{id}/ec-report/history # Get validation history - -GET /api/v2/ec/report/{result_id} # Download detailed report from S3 -POST /api/v2/ec/validation/{validation_id}/result # Callback: EC Wrapper posts Conforma result - POST /api/v2/ec/policy # Create policy reference (admin) GET /api/v2/ec/policy # List policy references GET /api/v2/ec/policy/{id} # Get policy reference PUT /api/v2/ec/policy/{id} # Update policy reference (admin) DELETE /api/v2/ec/policy/{id} # Delete policy reference (admin) + +POST /api/v2/ec/validate?sbom_id={id}&policy_id={id} # Trigger validation +GET /api/v2/ec/report?sbom_id={id}&policy_id={id} # Get latest validation result +GET /api/v2/ec/report/history?sbom_id={id}&policy_id={id} # Get validation history +GET /api/v2/ec/report/{result_id} # Download detailed report from S3 + +POST /api/v2/ec/validation/{validation_id}/result # Callback: EC Wrapper posts Conforma result ``` -## Conforma HTTP Wrapper API Endpoints +## Conforma EC Wrapper API Endpoints ``` -POST /api/v1/validate # Validate uploaded SBOM file against the provided Policy URL +POST /api/v1/validate # Validate uploaded SBOM file against the provided Policy URL (multipart form) ``` ### Trustify Module Structure From b68e7959018b8c40010bd34afb36547394e0cde0 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 17 Mar 2026 19:38:46 +0100 Subject: [PATCH 28/52] Add ec_policy.configuration model --- .../00014-enterprise-contract-integration.md | 70 +++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 2443006e9..f60f44ccb 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -322,11 +322,41 @@ sequenceDiagram - `id` (UUID, PK) - `name` (VARCHAR, unique) - User-friendly name label - `description` (TEXT) - What this policy enforces -- `policy_ref` (VARCHAR) - Git URL, OCI registry, or file path -- `policy_type` (VARCHAR) - 'git', 'oci', 'local' -- `configuration` (JSONB) - Branch, tag, auth credentials, etc. +- `policy_type` (VARCHAR) - 'Git URL' +- `configuration` (JSONB) - See model below - `created_at`, `updated_at` (TIMESTAMP) +**`ec_policy.configuration` JSONB model:** + +| Field | Type | Required | Description | +| ---------------------- | -------- | --------------- | -------------------------------------------------------------------------------- | +| `policy_ref` | string | yes | Policy source URL, e.g. `"git://github.com/org/policy-repo?ref=main"` | +| `auth` | object | no | Credentials for private repos; sensitive values encrypted via AES (never logged) | +| `auth.type` | string | yes (if `auth`) | `"token"`, `"ssh_key"`, or `"none"` | +| `auth.token_encrypted` | string | no | AES-encrypted bearer/PAT token, prefixed with encryption scheme | +| `policy_paths` | string[] | no | Sub-paths within the repo to evaluate (maps to Conforma `--policy` source paths) | +| `exclude` | string[] | no | Rule codes to skip during validation | +| `include` | string[] | no | If non-empty, only these rule codes are evaluated | +| `timeout_seconds` | integer | no | Per-policy override of the default 5-minute execution timeout | +| `extra_args` | string[] | no | Additional CLI flags forwarded verbatim to Conforma | + +Example of configuration field : + +```json +{ + "policy_ref": "git://github.com/org/policy-repo?ref=main", + "auth": { + "type": "token", + "token_encrypted": "AES256:" + }, + "policy_paths": ["policy/lib", "policy/release"], + "exclude": ["hello_world.minimal_packages"], + "include": [], + "timeout_seconds": 300, + "extra_args": ["--strict"] +} +``` + **`ec_validation_result`** - one row per validation execution - `id` (UUID, PK) @@ -334,7 +364,7 @@ sequenceDiagram - `policy_id` (UUID, FK → ec_policy) - `ec_status` (ENUM) - 'queued', 'in_progress', 'completed', 'failed' - `status` (ENUM) - 'pending', 'pass', 'fail', 'error' -- `violations` (JSONB) - Structured violation data for querying +- `violations` (JSONB) - See model below - `summary` (JSONB) - Total checks, passed, failed, warnings - `report_path` (VARCHAR) - File system or S3 path to detailed report - `start_time` (TIMESTAMP) @@ -342,6 +372,38 @@ sequenceDiagram - `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status +**`ec_validation_result.violations` JSONB model:** + +```json +[ + { + "severity": "violation", + "msg": "There are 2942 packages which is more than the permitted maximum of 510.", + "code": "hello_world.minimal_packages", + "title": "Check we don't have too many packages", + "description": "Just an example... To exclude this rule add \"hello_world.minimal_packages\" to the `exclude` section of the policy configuration.", + "solution": "You need to reduce the number of dependencies in this artifact." + }, + { + "severity": "warning", + "msg": "Deprecated license format detected.", + "code": "license.format_check", + "title": "License format validation", + "description": "Checks that license identifiers follow the SPDX specification.", + "solution": "Update license identifiers to valid SPDX expressions." + } +] +``` + +| Field | Type | Required | Description | +|---|---|---|---| +| `severity` | string | yes | `"violation"` or `"warning"` (derived from Conforma's `violations` vs `warnings` arrays) | +| `msg` | string | yes | Human-readable message describing the finding | +| `code` | string | yes | Rule identifier (e.g. `"hello_world.minimal_packages"`), useful for filtering and deduplication | +| `title` | string | yes | Short rule title | +| `description` | string | no | Longer explanation of what the rule checks | +| `solution` | string | no | Suggested remediation | + ### Trustify API Endpoints ``` From c0fc5e4f7f570070d1c6160db8b6d9b8a8ec1e08 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 17 Mar 2026 19:55:15 +0100 Subject: [PATCH 29/52] Add ec_policy.results model --- .../00014-enterprise-contract-integration.md | 102 +++++++----------- 1 file changed, 36 insertions(+), 66 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index f60f44ccb..9aef3de9a 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -340,7 +340,7 @@ sequenceDiagram | `timeout_seconds` | integer | no | Per-policy override of the default 5-minute execution timeout | | `extra_args` | string[] | no | Additional CLI flags forwarded verbatim to Conforma | -Example of configuration field : +`ec_policy.configuration` example : ```json { @@ -364,7 +364,7 @@ Example of configuration field : - `policy_id` (UUID, FK → ec_policy) - `ec_status` (ENUM) - 'queued', 'in_progress', 'completed', 'failed' - `status` (ENUM) - 'pending', 'pass', 'fail', 'error' -- `violations` (JSONB) - See model below +- `results` (JSONB) - See model below - `summary` (JSONB) - Total checks, passed, failed, warnings - `report_path` (VARCHAR) - File system or S3 path to detailed report - `start_time` (TIMESTAMP) @@ -372,38 +372,54 @@ Example of configuration field : - `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status -**`ec_validation_result.violations` JSONB model:** +**`ec_validation_result.results` JSONB model:** + +| Field | Type | Required | Description | +| ---------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------- | +| `severity` | string | yes | `"violation"`, `"warning"`, or `"success"` (derived from Conforma's `violations`, `warnings`, `successes` arrays) | +| `msg` | string | yes | Human-readable message describing the check outcome | +| `metadata` | object | yes | Rule metadata, preserved as-is from Conforma CLI output | +| `metadata.code` | string | yes | Rule identifier (e.g. `"hello_world.minimal_packages"`), useful for filtering and deduplication | +| `metadata.title` | string | yes | Short rule title | +| `metadata.description` | string | no | Longer explanation of what the rule checks | +| `metadata.solution` | string | no | Suggested remediation (typically absent for successes) | + +`ec_validation_result.results` example: ```json [ { "severity": "violation", "msg": "There are 2942 packages which is more than the permitted maximum of 510.", - "code": "hello_world.minimal_packages", - "title": "Check we don't have too many packages", - "description": "Just an example... To exclude this rule add \"hello_world.minimal_packages\" to the `exclude` section of the policy configuration.", - "solution": "You need to reduce the number of dependencies in this artifact." + "metadata": { + "code": "hello_world.minimal_packages", + "title": "Check we don't have too many packages", + "description": "Just an example... To exclude this rule add \"hello_world.minimal_packages\" to the `exclude` section of the policy configuration.", + "solution": "You need to reduce the number of dependencies in this artifact." + } }, { "severity": "warning", "msg": "Deprecated license format detected.", - "code": "license.format_check", - "title": "License format validation", - "description": "Checks that license identifiers follow the SPDX specification.", - "solution": "Update license identifiers to valid SPDX expressions." + "metadata": { + "code": "license.format_check", + "title": "License format validation", + "description": "Checks that license identifiers follow the SPDX specification.", + "solution": "Update license identifiers to valid SPDX expressions." + } + }, + { + "severity": "success", + "msg": "Pass", + "metadata": { + "code": "hello_world.valid_spdxid", + "title": "Check for valid SPDXID value", + "description": "Make sure that the SPDXID value found in the SBOM matches a list of allowed values." + } } ] ``` -| Field | Type | Required | Description | -|---|---|---|---| -| `severity` | string | yes | `"violation"` or `"warning"` (derived from Conforma's `violations` vs `warnings` arrays) | -| `msg` | string | yes | Human-readable message describing the finding | -| `code` | string | yes | Rule identifier (e.g. `"hello_world.minimal_packages"`), useful for filtering and deduplication | -| `title` | string | yes | Short rule title | -| `description` | string | no | Longer explanation of what the rule checks | -| `solution` | string | no | Suggested remediation | - ### Trustify API Endpoints ``` @@ -484,52 +500,6 @@ The policy commit hash/tag (`policy_version`) resolved at validation time are re Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. -### Structure of JSON returned from Conforma CLI validation request (from an example) - -```json -{ - "success": false, - "filepaths": [ - { - "filepath": "sboms/registry.redhat.io__rhtas__ec-rhel9__sha256__ea49a30eef5a2948b04540666a12048dd082625ce9f755acd3ece085c7d7937e.json", - "violations": [ - { - "msg": "There are 2942 packages which is more than the permitted maximum of 510.", - "metadata": { - "code": "hello_world.minimal_packages", - "description": "Just an example... To exclude this rule add \"hello_world.minimal_packages\" to the `exclude` section of the policy configuration.", - "solution": "You need to reduce the number of dependencies in this artifact.", - "title": "Check we don't have too many packages" - } - } - ], - "warnings": [], - "successes": [ - { - "msg": "Pass", - "metadata": { - "code": "hello_world.valid_spdxid", - "description": "Make sure that the SPDXID value found in the SBOM matches a list of allowed values.", - "title": "Check for valid SPDXID value" - } - } - ], - "success": false, - "success-count": 1 - } - ], - "policy": { - "sources": [ - { - "policy": ["github.com/conforma/policy//policy/lib", "./policy"] - } - ] - }, - "ec-version": "v0.8.83", - "effective-time": "2026-03-03T14:36:55.807826709Z" -} -``` - ### References - [Enterprise Contract (Conforma) GitHub](https://github.com/enterprise-contract/ec-cli) From 2c45fa450162d14c57a062436175e4319ac49ffb Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 17 Mar 2026 20:02:57 +0100 Subject: [PATCH 30/52] Add ec_validation.summary model --- .../00014-enterprise-contract-integration.md | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 9aef3de9a..0e3d17d98 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -367,22 +367,19 @@ sequenceDiagram - `results` (JSONB) - See model below - `summary` (JSONB) - Total checks, passed, failed, warnings - `report_path` (VARCHAR) - File system or S3 path to detailed report -- `start_time` (TIMESTAMP) -- `end_time` (TIMESTAMP) -- `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status **`ec_validation_result.results` JSONB model:** -| Field | Type | Required | Description | -| ---------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------- | -| `severity` | string | yes | `"violation"`, `"warning"`, or `"success"` (derived from Conforma's `violations`, `warnings`, `successes` arrays) | -| `msg` | string | yes | Human-readable message describing the check outcome | -| `metadata` | object | yes | Rule metadata, preserved as-is from Conforma CLI output | -| `metadata.code` | string | yes | Rule identifier (e.g. `"hello_world.minimal_packages"`), useful for filtering and deduplication | -| `metadata.title` | string | yes | Short rule title | -| `metadata.description` | string | no | Longer explanation of what the rule checks | -| `metadata.solution` | string | no | Suggested remediation (typically absent for successes) | +| Field | Type | Required | Description | +| ---------------------- | ------ | -------- | ------------------------------------------------------- | +| `severity` | string | yes | `"violation"`, `"warning"`, or `"success"` | +| `msg` | string | yes | Human-readable message describing the check outcome | +| `metadata` | object | yes | Rule metadata, preserved as-is from Conforma CLI output | +| `metadata.code` | string | yes | Rule identifier for filtering and deduplication | +| `metadata.title` | string | yes | Short rule title | +| `metadata.description` | string | no | Detailed explanation of what the rule checks | +| `metadata.solution` | string | no | Suggested remediation (absent for successes) | `ec_validation_result.results` example: @@ -420,6 +417,32 @@ sequenceDiagram ] ``` +**`ec_validation_result.summary` JSONB model:** + +| Field | Type | Required | Description | +| ---------------- | ------- | -------- | ------------------------------------------------------------------------ | +| `success` | boolean | yes | Overall pass/fail outcome (mirrors Conforma's top-level `success` field) | +| `total` | integer | yes | Total number of checks evaluated | +| `violations` | integer | yes | Count of checks with violation severity | +| `warnings` | integer | yes | Count of checks with warning severity | +| `successes` | integer | yes | Count of checks that passed | +| `ec_version` | string | yes | Conforma version used (e.g. `"v0.8.83"`) | +| `effective_time` | string | yes | ISO 8601 timestamp of evaluation from Conforma | + +`ec_validation_result.summary` example: + +```json +{ + "success": false, + "total": 3, + "violations": 1, + "warnings": 1, + "successes": 1, + "ec_version": "v0.8.83", + "effective_time": "2026-03-03T14:36:55.807826709Z" +} +``` + ### Trustify API Endpoints ``` From 06e09b5f8b0c4ce9b11989ea4cd9dbb663abffb5 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 17 Mar 2026 20:08:44 +0100 Subject: [PATCH 31/52] Fix inconsistencies --- .../00014-enterprise-contract-integration.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 0e3d17d98..d6f46746d 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -54,11 +54,11 @@ The `ec_status` "In Progress" state serves as a concurrency guard: if a validati What is stored where -- PostgreSQL: validation process state (`ec_status`), validation outcome (`status`), structured violations (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, ec_status, status, start_time. +- PostgreSQL: validation process state (`ec_status`), validation outcome (`status`), structured results (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, ec_status, status, start_time. - Storage system: full raw Conforma JSON report, linked from the DB row via report_path. Keeps DB rows small while preserving audit completeness. - Not stored: the policy definitions themselves. ec_policy stores references (URLs, OCI refs) that Conforma fetches at runtime. -Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB violations JSONB holds enough structure for filtering and dashboards without duplicating the full payload. +Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB results JSONB holds enough structure for filtering and dashboards without duplicating the full payload. ## Consequences @@ -196,7 +196,7 @@ C4Component System_Ext(s3, "S3 Object Storage", "Stores SBOM documents and reports") } - Rel(api, ecEndpoints, "POST /sboms/{id}/ec-validate,\nGET /sboms/{id}/ec-report", "JSON/HTTPS") + Rel(api, ecEndpoints, "POST /ec/validate,\nGET /ec/report", "JSON/HTTPS") Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") Rel(policyManager, postgres, "SELECT ec_policy", "SQL") @@ -232,7 +232,7 @@ sequenceDiagram participant S3 as Object Storage participant Wrapper as EC Wrapper (HTTP) - User->>API: POST /api/v2/sbom/{sbom_id}/ec-validate/{policy_id} + User->>API: POST /api/v2/ec/validate (multipart form: sbom_id, policy_id) API->>EP: dispatch request EP->>VS: validate_sbom(sbom_id, policy_id) @@ -261,7 +261,7 @@ sequenceDiagram VS->>S3: retrieve_sbom_document(sbom_id) S3-->>VS: SBOM document (JSON/XML) - VS->>Wrapper: POST /api/v1/validation {SBOM, policy_ref} + VS->>Wrapper: POST /api/v1/validate {SBOM, policy_ref} Wrapper-->>VS: 202 Accepted {validation_id} VS->>DB: INSERT ec_validation_result (ec_status='in_progress', status='pending', sbom_id, policy_id, validation_id, ...) @@ -302,11 +302,11 @@ sequenceDiagram EP->>VS: process_validation_result(validation_id, result) alt Pass - VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='pass', violations=[] + VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='pass', results=[] VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_path = ? else Fail - VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='fail', violations=json + VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='fail', results=json VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_path = ? else Error @@ -367,6 +367,9 @@ sequenceDiagram - `results` (JSONB) - See model below - `summary` (JSONB) - Total checks, passed, failed, warnings - `report_path` (VARCHAR) - File system or S3 path to detailed report +- `start_time` (TIMESTAMP) +- `end_time` (TIMESTAMP) +- `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status **`ec_validation_result.results` JSONB model:** @@ -452,7 +455,7 @@ GET /api/v2/ec/policy/{id} # Get policy reference PUT /api/v2/ec/policy/{id} # Update policy reference (admin) DELETE /api/v2/ec/policy/{id} # Delete policy reference (admin) -POST /api/v2/ec/validate?sbom_id={id}&policy_id={id} # Trigger validation +POST /api/v2/ec/validate # Trigger validation (multipart form: sbom_id, policy_id) GET /api/v2/ec/report?sbom_id={id}&policy_id={id} # Get latest validation result GET /api/v2/ec/report/history?sbom_id={id}&policy_id={id} # Get validation history GET /api/v2/ec/report/{result_id} # Download detailed report from S3 From f09d5fb8c3d0b09dc9bd39fb63706001be436c4e Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 17 Mar 2026 20:20:15 +0100 Subject: [PATCH 32/52] Flesh out Consequences of EC Wrapper --- .../00014-enterprise-contract-integration.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index d6f46746d..251679d76 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -62,7 +62,21 @@ Storing full JSON in storage system rather than only a summary was chosen explic ## Consequences -Using an EC Wrapper decouples the validation process into an external service. This better caters for large-scale deployments as EC validation has its own resource constraints. Meanwhile it adds infrastructure complexity as the EC Wrapper must be deployed and maintained alongside the Conforma CLI. +### Why EC runs externally + +EC validation can be be very resource-intensive (especially for large SBOMs with thousands of packages) and it should not compete with Trustify. +A dedicated EC Wrapper running alonside EC instance (Conforma CLI, etc) provides : + +- **Resource isolation** — A long-running or memory-heavy Conforma process cannot degrade Trustify's responsiveness. +- **Independent scaling** — The EC Wrapper can be scaled horizontally (more replicas) based on validation demand without scaling the entire Trustify deployment. Conversely, Trustify can scale for query load without provisioning excess capacity for validation. +- **Failure containment** — An EC instance crash (OOM kill, policy fetch timeout, unexpected CLI error) is isolated to the wrapper. Trustify records the failure in `ec_status` and remains fully operational; the validation can be re-triggered. +- **Version independence** — The EC Wrapper and EC instance (Conforma CLI) can be upgraded or rolled back on their own release cadence, without redeploying Trustify. This is important given Conforma's active development pace. + +The trade-off is added infrastructure complexity: the EC Wrapper must be deployed separatly with EC instance, monitored, and maintained as a separate component alongside the Conforma CLI binary. + +In Kubernetes or standalone machine deployments, the EC Wrapper pod has its own resource requests/limits, independent of the Trustify pod. + +### CLI spawning Within the EC Wrapper, Conforma is invoked via CLI spawning rather than a native API. This introduces an operational dependency (Conforma must be installed and version-pinned on every EC Wrapper instance) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma REST API exists yet. On the Trustify side, the EC service interacts with the EC Wrapper over HTTP and is built behind an adapter interface, so the implementation can be swapped for a direct Conforma REST client when one becomes available, without changes to the service layer or API. From 30e668aef003214d01a1e3073dc8730f89c7891f Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 17 Mar 2026 20:23:44 +0100 Subject: [PATCH 33/52] Rephrase --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 251679d76..05a9b71fb 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -62,7 +62,7 @@ Storing full JSON in storage system rather than only a summary was chosen explic ## Consequences -### Why EC runs externally +### EC Wrapper runs externally EC validation can be be very resource-intensive (especially for large SBOMs with thousands of packages) and it should not compete with Trustify. A dedicated EC Wrapper running alonside EC instance (Conforma CLI, etc) provides : From e07ad0cad3fdd38487b7c9d5c8e8f9b5e58ce619 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Mar 2026 15:32:57 +0100 Subject: [PATCH 34/52] Create explicit futur work section --- docs/adrs/00014-enterprise-contract-integration.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 05a9b71fb..d7d130f5e 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -60,6 +60,12 @@ What is stored where Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB results JSONB holds enough structure for filtering and dashboards without duplicating the full payload. +### Futur work + +#### Multi-tenancy + +Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. + ## Consequences ### EC Wrapper runs externally @@ -536,10 +542,6 @@ ec_policy stores external references only. Conforma fetches the actual policy at The policy commit hash/tag (`policy_version`) resolved at validation time are recorded in each result row, enabling reproducibility and audit. -#### Multi-tenancy - -Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. - ### References - [Enterprise Contract (Conforma) GitHub](https://github.com/enterprise-contract/ec-cli) From 085e6ce823bedfc2aca84b694ae677fc08b4863a Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Mar 2026 15:48:59 +0100 Subject: [PATCH 35/52] Rename ec_validation table --- .../00014-enterprise-contract-integration.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index d7d130f5e..01be0221d 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -225,7 +225,7 @@ C4Component Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "JSON/HTTPS") Rel(ecService, resultParser, "parse_output()", "Function call") Rel(ecService, resultPersistence, "save_results()", "Function call") - Rel(resultPersistence, postgres, "INSERT ec_validation_result", "SQL") + Rel(resultPersistence, postgres, "INSERT ec_validation", "SQL") Rel(ecService, s3, "Store EC report", "S3 API") UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") @@ -271,7 +271,7 @@ sequenceDiagram EP-->>API: 404 Not Found end - VS->>DB: SELECT * FROM ec_validation_result WHERE sbom_id = ? AND policy_id = ? AND ec_status IN ('queued', 'in_progress') + VS->>DB: SELECT * FROM ec_validation WHERE sbom_id = ? AND policy_id = ? AND ec_status IN ('queued', 'in_progress') alt Validation already in progress VS-->>EP: 409 Conflict {existing job_id} EP-->>API: 409 Conflict @@ -284,7 +284,7 @@ sequenceDiagram VS->>Wrapper: POST /api/v1/validate {SBOM, policy_ref} Wrapper-->>VS: 202 Accepted {validation_id} - VS->>DB: INSERT ec_validation_result (ec_status='in_progress', status='pending', sbom_id, policy_id, validation_id, ...) + VS->>DB: INSERT ec_validation (ec_status='in_progress', status='pending', sbom_id, policy_id, validation_id, ...) VS-->>EP: 202 Accepted {validation_id} EP-->>API: 202 Accepted {validation_id} API-->>User: 202 Accepted {validation_id} @@ -322,15 +322,15 @@ sequenceDiagram EP->>VS: process_validation_result(validation_id, result) alt Pass - VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='pass', results=[] + VS->>DB: UPDATE ec_validation SET ec_status='completed', status='pass', results=[] VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_path = ? else Fail - VS->>DB: UPDATE ec_validation_result SET ec_status='completed', status='fail', results=json + VS->>DB: UPDATE ec_validation SET ec_status='completed', status='fail', results=json VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_path = ? else Error - VS->>DB: UPDATE ec_validation_result SET ec_status='failed', status='error', error_message=detail + VS->>DB: UPDATE ec_validation SET ec_status='failed', status='error', error_message=detail Note over VS,DB: Validation can be re-triggered (new row with ec_status='in_progress', status='pending') end ``` @@ -377,7 +377,7 @@ sequenceDiagram } ``` -**`ec_validation_result`** - one row per validation execution +**`ec_validation`** - one row per validation execution - `id` (UUID, PK) - `sbom_id` (UUID, FK → sbom) @@ -392,7 +392,7 @@ sequenceDiagram - `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status -**`ec_validation_result.results` JSONB model:** +**`ec_validation.results` JSONB model:** | Field | Type | Required | Description | | ---------------------- | ------ | -------- | ------------------------------------------------------- | @@ -404,7 +404,7 @@ sequenceDiagram | `metadata.description` | string | no | Detailed explanation of what the rule checks | | `metadata.solution` | string | no | Suggested remediation (absent for successes) | -`ec_validation_result.results` example: +`ec_validation.results` example: ```json [ @@ -440,7 +440,7 @@ sequenceDiagram ] ``` -**`ec_validation_result.summary` JSONB model:** +**`ec_validation.summary` JSONB model:** | Field | Type | Required | Description | | ---------------- | ------- | -------- | ------------------------------------------------------------------------ | @@ -452,7 +452,7 @@ sequenceDiagram | `ec_version` | string | yes | Conforma version used (e.g. `"v0.8.83"`) | | `effective_time` | string | yes | ISO 8601 timestamp of evaluation from Conforma | -`ec_validation_result.summary` example: +`ec_validation.summary` example: ```json { From ae34653c505a41b34e10d7f09839ff173ed00302 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Mar 2026 15:52:48 +0100 Subject: [PATCH 36/52] ec_policy more generic with policy_type ENUM --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 01be0221d..3ee466475 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -342,7 +342,7 @@ sequenceDiagram - `id` (UUID, PK) - `name` (VARCHAR, unique) - User-friendly name label - `description` (TEXT) - What this policy enforces -- `policy_type` (VARCHAR) - 'Git URL' +- `policy_type` (ENUM) - 'Conforma' - `configuration` (JSONB) - See model below - `created_at`, `updated_at` (TIMESTAMP) From c6f5e7fbbc6ed960895b5c3834c59ba04a502d29 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Mar 2026 15:54:31 +0100 Subject: [PATCH 37/52] ec_policy.revision replace start/end datetime --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 3ee466475..9def16130 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -344,7 +344,7 @@ sequenceDiagram - `description` (TEXT) - What this policy enforces - `policy_type` (ENUM) - 'Conforma' - `configuration` (JSONB) - See model below -- `created_at`, `updated_at` (TIMESTAMP) +- `revision`(UUID) - Conditional UPDATE filtering both the primary key and the current revision **`ec_policy.configuration` JSONB model:** From 1003de54986b47d3f07c8f8906dafea9374b974a Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Mar 2026 15:59:38 +0100 Subject: [PATCH 38/52] ec_validation: clarify status names --- docs/adrs/00014-enterprise-contract-integration.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 9def16130..0f2f706ff 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -382,10 +382,10 @@ sequenceDiagram - `id` (UUID, PK) - `sbom_id` (UUID, FK → sbom) - `policy_id` (UUID, FK → ec_policy) -- `ec_status` (ENUM) - 'queued', 'in_progress', 'completed', 'failed' -- `status` (ENUM) - 'pending', 'pass', 'fail', 'error' +- `processing_status` (ENUM) - 'queued', 'in_progress', 'completed', 'failed' +- `validation_status` (ENUM) - 'pending', 'pass', 'fail', 'error' - `results` (JSONB) - See model below -- `summary` (JSONB) - Total checks, passed, failed, warnings +- `summary` (JSONB) - Total checks, passed, failed, warnings, see model below - `report_path` (VARCHAR) - File system or S3 path to detailed report - `start_time` (TIMESTAMP) - `end_time` (TIMESTAMP) @@ -450,7 +450,7 @@ sequenceDiagram | `warnings` | integer | yes | Count of checks with warning severity | | `successes` | integer | yes | Count of checks that passed | | `ec_version` | string | yes | Conforma version used (e.g. `"v0.8.83"`) | -| `effective_time` | string | yes | ISO 8601 timestamp of evaluation from Conforma | +| `effective_time` | string | yes | ISO 8601 timestamp of evaluation provided by Conforma | `ec_validation.summary` example: From 667d008b488f65217a9049c010800cbe9f27bb5c Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Mar 2026 16:47:28 +0100 Subject: [PATCH 39/52] EC service: is now Policy verifier; Conforma used instead of EC name --- .../00014-enterprise-contract-integration.md | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 0f2f706ff..100922b7e 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -10,7 +10,7 @@ PROPOSED Trustify provides SBOM storage, analysis, and vulnerability tracking but lacks automated policy enforcement. Organizations need to validate SBOMs against security and compliance policies (licensing, vulnerabilities, provenance) without relying on manual, inconsistent review processes. -Enterprise Contract (Conforma) is an open-source policy enforcement tool actively maintained by Red Hat. It validates SBOMs against configurable policies and produces structured JSON output. Currently it provides only a CLI; a REST API is planned but with no committed timeline. +Conforma (former Enterprise Contract) is an open-source policy enforcement tool actively maintained by Red Hat. It validates SBOMs against configurable policies and produces structured JSON output. Currently it provides only a CLI; a REST API is planned but with no committed timeline. ### Requirements @@ -32,15 +32,15 @@ Trustify stores information to identify (id, name, URL) of Policies. A default Policy is defined at the application level (global policy) which is used for validation when an SBOM does not have any Policy explicitly attached to it. Conforma CLI is deployed separately from Trustify as either a standalone container or equivalent. -An EC Wrapper (HTTP service) acts as a proxy between Trustify's EC service and Conforma CLI. +A Conforma Wrapper (HTTP service) acts as a proxy between Trustify's Policy Verifier service and Conforma CLI. Each SBOM + policy pair has two validation states . -The validation process state of the EC Wrapper follows this lifecycle: +The validation process state of the Conforma Wrapper follows this lifecycle: - **Queued** — a user has triggered validation; the request is being processed. Other users can see this state, preventing duplicate validation runs for the same SBOM + policy pair. -- **In Progress** — the request has been submitted to EC Wrapper. -- **Completed** — the outcome of the request has been received back from EC Wrapper. +- **In Progress** — the request has been submitted to Conforma Wrapper. +- **Completed** — the outcome of the request has been received back from Conforma Wrapper. - **Failed** — an execution error occurred (CLI crash, policy fetch failure, timeout). The error is surfaced separately, and the validation can be re-triggered. The Policy validation outcome follows this lifecycle: @@ -50,13 +50,13 @@ The Policy validation outcome follows this lifecycle: - **Pass** — Conforma validation succeeded; the SBOM satisfies the policy. - **Error** — The Conforma validation has generated an error. -The `ec_status` "In Progress" state serves as a concurrency guard: if a validation is already running for a given SBOM + policy pair, subsequent requests are rejected (409 Conflict), preventing duplicate work. +The `processing_status` "In Progress" state serves as a concurrency guard: if a validation is already running for a given SBOM + policy pair, subsequent requests are rejected (409 Conflict), preventing duplicate work. What is stored where -- PostgreSQL: validation process state (`ec_status`), validation outcome (`status`), structured results (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, ec_status, status, start_time. +- PostgreSQL: validation process state (`processing_status`), validation outcome (`status`), structured results (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, processing_status, status, start_time. - Storage system: full raw Conforma JSON report, linked from the DB row via report_path. Keeps DB rows small while preserving audit completeness. -- Not stored: the policy definitions themselves. ec_policy stores references (URLs, OCI refs) that Conforma fetches at runtime. +- Not stored: the policy definitions themselves. policy stores references (URLs, OCI refs) that Conforma fetches at runtime. Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB results JSONB holds enough structure for filtering and dashboards without duplicating the full payload. @@ -68,23 +68,23 @@ Policy references are global (shared across all users) in this initial implement ## Consequences -### EC Wrapper runs externally +### The Conforma Wrapper runs externally EC validation can be be very resource-intensive (especially for large SBOMs with thousands of packages) and it should not compete with Trustify. -A dedicated EC Wrapper running alonside EC instance (Conforma CLI, etc) provides : +A dedicated Conforma Wrapper running alonside EC instance (Conforma CLI, etc) provides : - **Resource isolation** — A long-running or memory-heavy Conforma process cannot degrade Trustify's responsiveness. -- **Independent scaling** — The EC Wrapper can be scaled horizontally (more replicas) based on validation demand without scaling the entire Trustify deployment. Conversely, Trustify can scale for query load without provisioning excess capacity for validation. -- **Failure containment** — An EC instance crash (OOM kill, policy fetch timeout, unexpected CLI error) is isolated to the wrapper. Trustify records the failure in `ec_status` and remains fully operational; the validation can be re-triggered. -- **Version independence** — The EC Wrapper and EC instance (Conforma CLI) can be upgraded or rolled back on their own release cadence, without redeploying Trustify. This is important given Conforma's active development pace. +- **Independent scaling** — The Conforma Wrapper can be scaled horizontally (more replicas) based on validation demand without scaling the entire Trustify deployment. Conversely, Trustify can scale for query load without provisioning excess capacity for validation. +- **Failure containment** — An EC instance crash (OOM kill, policy fetch timeout, unexpected CLI error) is isolated to the wrapper. Trustify records the failure in `processing_status` and remains fully operational; the validation can be re-triggered. +- **Version independence** — The Conforma Wrapper and EC instance (Conforma CLI) can be upgraded or rolled back on their own release cadence, without redeploying Trustify. This is important given Conforma's active development pace. -The trade-off is added infrastructure complexity: the EC Wrapper must be deployed separatly with EC instance, monitored, and maintained as a separate component alongside the Conforma CLI binary. +The trade-off is added infrastructure complexity: the Conforma Wrapper must be deployed separatly with EC instance, monitored, and maintained as a separate component alongside the Conforma CLI binary. -In Kubernetes or standalone machine deployments, the EC Wrapper pod has its own resource requests/limits, independent of the Trustify pod. +In Kubernetes or standalone machine deployments, the Conforma Wrapper pod has its own resource requests/limits, independent of the Trustify pod. ### CLI spawning -Within the EC Wrapper, Conforma is invoked via CLI spawning rather than a native API. This introduces an operational dependency (Conforma must be installed and version-pinned on every EC Wrapper instance) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma REST API exists yet. On the Trustify side, the EC service interacts with the EC Wrapper over HTTP and is built behind an adapter interface, so the implementation can be swapped for a direct Conforma REST client when one becomes available, without changes to the service layer or API. +Within the Conforma Wrapper, Conforma is invoked via CLI spawning rather than a native API. This introduces an operational dependency (Conforma must be installed and version-pinned on every Conforma Wrapper instance) and per-validation process spawning overhead. These are accepted trade-offs given that no Conforma REST API exists yet. On the Trustify side, the Policy Verifier service interacts with the Conforma Wrapper over HTTP and is built behind an adapter interface, so the implementation can be swapped for a direct Conforma REST client when one becomes available, without changes to the service layer or API. ### Alternatives Considered @@ -139,13 +139,13 @@ C4Container Container(webui, "Web UI", "Rust/Actix", "Trustify GUI") Container(api, "API Gateway", "Actix-web", "REST API endpoints for SBOM
and compliance operations") ContainerDb(postgres, "PostgreSQL", "DBMS", "Stores SBOM metadata, relationships,
and EC validation results") - Container(ecModule, "EC Validation Module", "Rust", "Orchestrates validation via
EC Wrapper and persists results") + Container(ecModule, "EC Validation Module", "Rust", "Orchestrates validation via
Conforma Wrapper and persists results") ContainerDb(s3, "Object Storage", "S3/Minio", "Stores SBOM documents and EC reports") Container(storage, "Storage Service", "Rust", "Manages document storage
(SBOMs, policy results)") } Container_Boundary(conformaSystem, "Conforma System") { - Container(ecWrapper, "EC Wrapper", "Rust/Actix", "HTTP Wrapper") + Container(ecWrapper, "Conforma Wrapper", "Rust/Actix", "HTTP Wrapper") System_Ext(conforma, "Conforma CLI", "External policy validation tool") } @@ -194,7 +194,7 @@ C4Component Container(api, "API Gateway", "Actix-web", "REST API for EC operations") Container_Boundary(ecModule, "EC Validation Module") { Container(ecEndpoints, "EC Endpoints", "Actix-web handlers", "REST endpoints for validation operations") - Component(ecService, "EC Service", "Business logic", "Orchestrates validation workflow") + Component(ecService, "Policy Verifier service", "Business logic", "Orchestrates validation workflow") Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output into structured data") Component(policyManager, "Policy Manager", "Business logic", "Manages EC policy references and
configuration") Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results") @@ -202,7 +202,7 @@ C4Component } Deployment_Node(external, "External System") { Deployment_Node(trustifyPod, "Trustify Pod") { - Component(ecWrapper, "EC Wrapper", "Actix-web handlers", "HTTP API") + Component(ecWrapper, "Conforma Wrapper", "Actix-web handlers", "HTTP API") } Deployment_Node(conformaPod, "Conforma Pod") { System_Ext(conforma, "Conforma CLI", "Enterprise Contract validation tool") @@ -219,13 +219,13 @@ C4Component Rel(api, ecEndpoints, "POST /ec/validate,\nGET /ec/report", "JSON/HTTPS") Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") Rel(ecService, policyManager, "get_policy_config()", "Function call") - Rel(policyManager, postgres, "SELECT ec_policy", "SQL") + Rel(policyManager, postgres, "SELECT policy", "SQL") Rel(ecService, ecWrapper, "POST /api/v1/validation → returns {id}", "HTTP") Rel(ecWrapper, conforma, "ec validate", "Process spawn") Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "JSON/HTTPS") Rel(ecService, resultParser, "parse_output()", "Function call") Rel(ecService, resultPersistence, "save_results()", "Function call") - Rel(resultPersistence, postgres, "INSERT ec_validation", "SQL") + Rel(resultPersistence, postgres, "INSERT policy_validation", "SQL") Rel(ecService, s3, "Store EC report", "S3 API") UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") @@ -246,18 +246,18 @@ sequenceDiagram actor User participant API as Trustify API participant EP as EC Endpoints - participant VS as EC Service + participant VS as Policy Verifier service participant PM as Policy Manager participant DB as PostgreSQL participant S3 as Object Storage - participant Wrapper as EC Wrapper (HTTP) + participant Wrapper as Conforma Wrapper (HTTP) User->>API: POST /api/v2/ec/validate (multipart form: sbom_id, policy_id) API->>EP: dispatch request EP->>VS: validate_sbom(sbom_id, policy_id) VS->>PM: get_policy_configuration(policy_id) - PM->>DB: SELECT * FROM ec_policy WHERE id = ? + PM->>DB: SELECT * FROM policy WHERE id = ? alt Policy not found PM-->>VS: Error: PolicyNotFound VS-->>EP: 404 Not Found @@ -271,7 +271,7 @@ sequenceDiagram EP-->>API: 404 Not Found end - VS->>DB: SELECT * FROM ec_validation WHERE sbom_id = ? AND policy_id = ? AND ec_status IN ('queued', 'in_progress') + VS->>DB: SELECT * FROM policy_validation WHERE sbom_id = ? AND policy_id = ? AND processing_status IN ('queued', 'in_progress') alt Validation already in progress VS-->>EP: 409 Conflict {existing job_id} EP-->>API: 409 Conflict @@ -284,7 +284,7 @@ sequenceDiagram VS->>Wrapper: POST /api/v1/validate {SBOM, policy_ref} Wrapper-->>VS: 202 Accepted {validation_id} - VS->>DB: INSERT ec_validation (ec_status='in_progress', status='pending', sbom_id, policy_id, validation_id, ...) + VS->>DB: INSERT policy_validation (processing_status='in_progress', status='pending', sbom_id, policy_id, validation_id, ...) VS-->>EP: 202 Accepted {validation_id} EP-->>API: 202 Accepted {validation_id} API-->>User: 202 Accepted {validation_id} @@ -297,10 +297,10 @@ sequenceDiagram autonumber participant API as Trustify API participant EP as EC Endpoints - participant VS as EC Service + participant VS as Policy Verifier service participant DB as PostgreSQL participant S3 as Object Storage - participant Wrapper as EC Wrapper (HTTP) + participant Wrapper as Conforma Wrapper (HTTP) participant Conf as Conforma CLI Note over Wrapper: Wrapper holds validation_id from the initial request @@ -322,22 +322,22 @@ sequenceDiagram EP->>VS: process_validation_result(validation_id, result) alt Pass - VS->>DB: UPDATE ec_validation SET ec_status='completed', status='pass', results=[] + VS->>DB: UPDATE policy_validation SET verification_status='completed', status='pass', results=[] VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_path = ? else Fail - VS->>DB: UPDATE ec_validation SET ec_status='completed', status='fail', results=json + VS->>DB: UPDATE policy_validation SET verification_status='completed', status='fail', results=json VS->>S3: store_validation_report(result_id, full_json) VS->>DB: UPDATE SET report_path = ? else Error - VS->>DB: UPDATE ec_validation SET ec_status='failed', status='error', error_message=detail - Note over VS,DB: Validation can be re-triggered (new row with ec_status='in_progress', status='pending') + VS->>DB: UPDATE policy_validation SET verification_status='failed', status='error', error_message=detail + Note over VS,DB: Validation can be re-triggered (new row with processing_status='in_progress', status='pending') end ``` ### The Data Model -**`ec_policy`** - Stores references to external policies, not the policies themselves +**`policy`** - Stores references to external policies, not the policies themselves - `id` (UUID, PK) - `name` (VARCHAR, unique) - User-friendly name label @@ -346,7 +346,7 @@ sequenceDiagram - `configuration` (JSONB) - See model below - `revision`(UUID) - Conditional UPDATE filtering both the primary key and the current revision -**`ec_policy.configuration` JSONB model:** +**`policy.configuration` JSONB model:** | Field | Type | Required | Description | | ---------------------- | -------- | --------------- | -------------------------------------------------------------------------------- | @@ -360,7 +360,7 @@ sequenceDiagram | `timeout_seconds` | integer | no | Per-policy override of the default 5-minute execution timeout | | `extra_args` | string[] | no | Additional CLI flags forwarded verbatim to Conforma | -`ec_policy.configuration` example : +`policy.configuration` example : ```json { @@ -377,13 +377,13 @@ sequenceDiagram } ``` -**`ec_validation`** - one row per validation execution +**`policy_validation`** - one row per validation execution - `id` (UUID, PK) - `sbom_id` (UUID, FK → sbom) -- `policy_id` (UUID, FK → ec_policy) +- `policy_id` (UUID, FK → policy) - `processing_status` (ENUM) - 'queued', 'in_progress', 'completed', 'failed' -- `validation_status` (ENUM) - 'pending', 'pass', 'fail', 'error' +- `verification_status` (ENUM) - 'pending', 'pass', 'fail', 'error' - `results` (JSONB) - See model below - `summary` (JSONB) - Total checks, passed, failed, warnings, see model below - `report_path` (VARCHAR) - File system or S3 path to detailed report @@ -392,7 +392,7 @@ sequenceDiagram - `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time - `error_message` (TEXT) - Populated only on error status -**`ec_validation.results` JSONB model:** +**`policy_validation.results` JSONB model:** | Field | Type | Required | Description | | ---------------------- | ------ | -------- | ------------------------------------------------------- | @@ -404,7 +404,7 @@ sequenceDiagram | `metadata.description` | string | no | Detailed explanation of what the rule checks | | `metadata.solution` | string | no | Suggested remediation (absent for successes) | -`ec_validation.results` example: +`policy_validation.results` example: ```json [ @@ -440,7 +440,7 @@ sequenceDiagram ] ``` -**`ec_validation.summary` JSONB model:** +**`policy_validation.summary` JSONB model:** | Field | Type | Required | Description | | ---------------- | ------- | -------- | ------------------------------------------------------------------------ | @@ -452,7 +452,7 @@ sequenceDiagram | `ec_version` | string | yes | Conforma version used (e.g. `"v0.8.83"`) | | `effective_time` | string | yes | ISO 8601 timestamp of evaluation provided by Conforma | -`ec_validation.summary` example: +`policy_validation.summary` example: ```json { @@ -469,21 +469,21 @@ sequenceDiagram ### Trustify API Endpoints ``` -POST /api/v2/ec/policy # Create policy reference (admin) -GET /api/v2/ec/policy # List policy references -GET /api/v2/ec/policy/{id} # Get policy reference -PUT /api/v2/ec/policy/{id} # Update policy reference (admin) -DELETE /api/v2/ec/policy/{id} # Delete policy reference (admin) - -POST /api/v2/ec/validate # Trigger validation (multipart form: sbom_id, policy_id) -GET /api/v2/ec/report?sbom_id={id}&policy_id={id} # Get latest validation result -GET /api/v2/ec/report/history?sbom_id={id}&policy_id={id} # Get validation history -GET /api/v2/ec/report/{result_id} # Download detailed report from S3 - -POST /api/v2/ec/validation/{validation_id}/result # Callback: EC Wrapper posts Conforma result +POST /api/v2/policy # Create policy reference (admin) +GET /api/v2/policy # List policy references +GET /api/v2/policy/{id} # Get policy reference +PUT /api/v2/policy/{id} # Update policy reference (admin) +DELETE /api/v2/policy/{id} # Delete policy reference (admin) + +POST /api/v2/policy/validate # Trigger validation (multipart form: sbom_id, policy_id) +GET /api/v2/policy/report?sbom_id={id}&policy_id={id} # Get latest validation result +GET /api/v2/policy/report/history?sbom_id={id}&policy_id={id} # Get validation history +GET /api/v2/policy/report/{result_id} # Download detailed report from S3 + +POST /api/v2/ec/validation/{validation_id}/result # Callback: Conforma Wrapper posts Conforma result ``` -## Conforma EC Wrapper API Endpoints +## Conforma Wrapper API Endpoints ``` POST /api/v1/validate # Validate uploaded SBOM file against the provided Policy URL (multipart form) @@ -492,7 +492,7 @@ POST /api/v1/validate # Validate uploaded SBOM file agains ### Trustify Module Structure ``` -modules/ec/ +modules/policy/ ├── Cargo.toml └── src/ ├── lib.rs @@ -506,12 +506,12 @@ modules/ec/ │ ├── mod.rs │ ├── ec_service.rs # Main orchestration │ ├── policy_manager.rs # Policy configuration - │ ├── executor.rs # EC Wrapper HTTP client (adapter) + │ ├── executor.rs # Conforma Wrapper HTTP client (adapter) │ └── result_parser.rs # Output parsing └── error.rs # Error types ``` -### HTTP Wrapper Module Structure +### Conforma Wrapper Module Structure ``` ├── Cargo.toml @@ -524,9 +524,9 @@ modules/ec/ ### Technical Considerations -#### Conforma CLI Execution (HTTPEC Wrapper) +#### Conforma CLI Execution -The HTTP Wrapper invokes Conforma via process spawning (e.g., `tokio::process::Command`). All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are written to a temp file and passed by path rather than piped via stdin, which avoids OOM issues. +The Conforma Wrapper invokes Conforma CLI via process spawning (e.g., `tokio::process::Command`). All arguments are passed as an array — never as a shell string — to prevent CLI injection. Execution has a configurable timeout (default 5 minutes); large SBOMs are written to a temp file and passed by path rather than piped via stdin, which avoids OOM issues. Exit codes are treated as follows: 0 = pass, 1 = policy violations (expected failure, not an error), 2+ = execution error. It is important to distinguish 1 from 2+ in error handling — a policy violation is a valid result that should be surfaced to the user, not treated as a system failure. @@ -534,11 +534,11 @@ Temp files (SBOM, any cached policy material) are cleaned up in a finally-equiva #### Concurrency and Backpressure -On the EC Wrapper side, concurrent Conforma processes are bounded by a semaphore (default: 5). When the semaphore is exhausted, the EC Wrapper returns 429 Too Many Requests to Trustify, which propagates the status to the caller. This makes the capacity limit explicit to callers (e.g., CI pipelines can implement their own retry with backoff). On the Trustify side, the `ec_status` "In Progress" concurrency guard (409 Conflict) prevents duplicate validation runs for the same SBOM + policy pair. If demand grows to warrant it, a proper queue (Redis/RabbitMQ) is the deferred alternative considered below. +On the Conforma Wrapper side, concurrent Conforma processes are bounded by a semaphore (default: 5). When the semaphore is exhausted, the Conforma Wrapper returns 429 Too Many Requests to Trustify, which propagates the status to the caller. This makes the capacity limit explicit to callers (e.g., CI pipelines can implement their own retry with backoff). On the Trustify side, the `processing_status` "In Progress" concurrency guard (409 Conflict) prevents duplicate validation runs for the same SBOM + policy pair. If demand grows to warrant it, a proper queue (Redis/RabbitMQ) is the deferred alternative considered below. #### Policy Management -ec_policy stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and will be encrypted using AES crate; they are never logged. +policy stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and will be encrypted using AES crate; they are never logged. The policy commit hash/tag (`policy_version`) resolved at validation time are recorded in each result row, enabling reproducibility and audit. From db5785d873d36665fcf2fb32dc67bbbd249abe37 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 18 Mar 2026 16:56:20 +0100 Subject: [PATCH 40/52] Diagrams: Replaced EC --- .../00014-enterprise-contract-integration.md | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 100922b7e..077c9d130 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -129,7 +129,7 @@ C4Context UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1") ``` -### Container Diagram - EC Validation Module +### Container Diagram - Policy Validation Module ```mermaid C4Container @@ -139,13 +139,13 @@ C4Container Container(webui, "Web UI", "Rust/Actix", "Trustify GUI") Container(api, "API Gateway", "Actix-web", "REST API endpoints for SBOM
and compliance operations") ContainerDb(postgres, "PostgreSQL", "DBMS", "Stores SBOM metadata, relationships,
and EC validation results") - Container(ecModule, "EC Validation Module", "Rust", "Orchestrates validation via
Conforma Wrapper and persists results") + Container(policyValidationModule, "Policy Validation Module", "Rust", "Orchestrates validation via
Conforma Wrapper and persists results") ContainerDb(s3, "Object Storage", "S3/Minio", "Stores SBOM documents and EC reports") Container(storage, "Storage Service", "Rust", "Manages document storage
(SBOMs, policy results)") } Container_Boundary(conformaSystem, "Conforma System") { - Container(ecWrapper, "Conforma Wrapper", "Rust/Actix", "HTTP Wrapper") + Container(conformaWrapper, "Conforma Wrapper", "Rust/Actix", "HTTP Wrapper") System_Ext(conforma, "Conforma CLI", "External policy validation tool") } @@ -161,25 +161,25 @@ C4Container Rel(user, webui, "Views compliance status", "HTTP API") Rel(user, api, "Views compliance status", "HTTP API") Rel(webui, api, "API calls", "JSON/HTTP API") - Rel(api, ecModule, "Triggers validation", "Function call") - Rel(ecModule, ecWrapper, "POST /validation", "HTTP API formData") - Rel(ecWrapper, api, "POST /validation/{id}/result", "HTTP API") - Rel(ecWrapper, conforma, "ec validate input {SBOM} {policy}", "Spawned command") - Rel(ecModule, postgres, "Saves validation
results", "SQL") - Rel(ecModule, storage, "Stores EC reports", "Function call") + Rel(api, policyValidationModule, "Triggers validation", "Function call") + Rel(policyValidationModule, conformaWrapper, "POST /validation", "HTTP API formData") + Rel(conformaWrapper, api, "POST /validation/{id}/result", "HTTP API") + Rel(conformaWrapper, conforma, "ec validate input {SBOM} {policy}", "Spawned command") + Rel(policyValidationModule, postgres, "Saves validation
results", "SQL") + Rel(policyValidationModule, storage, "Stores EC reports", "Function call") Rel(storage, s3, "Persists reports", "S3 API") Rel(conforma, policyRepo, "Fetches policies", "Git/HTTPS") - Rel(ecWrapper, oidc, "Authenticate", "OAuth API") + Rel(conformaWrapper, oidc, "Authenticate", "OAuth API") UpdateRelStyle(user, webui, $offsetX="-60", $offsetY="30") UpdateRelStyle(user, api, $offsetX="-60", $offsetY="-50") UpdateRelStyle(webui, api, $offsetX="-40", $offsetY="10") - UpdateRelStyle(ecModule, ecWrapper, $offsetX="-50", $offsetY="-20") - UpdateRelStyle(ecWrapper, api, $offsetX="-60", $offsetY="-10") - UpdateRelStyle(ecModule, postgres, $offsetX="-40", $offsetY="10") + UpdateRelStyle(policyValidationModule, conformaWrapper, $offsetX="-50", $offsetY="-20") + UpdateRelStyle(conformaWrapper, api, $offsetX="-60", $offsetY="-10") + UpdateRelStyle(policyValidationModule, postgres, $offsetX="-40", $offsetY="10") UpdateRelStyle(storage, s3, $offsetX="-40", $offsetY="10") UpdateRelStyle(conforma, policyRepo, $offsetX="-40", $offsetY="100") - UpdateRelStyle(ecWrapper, oidc, $offsetX="30", $offsetY="40") + UpdateRelStyle(conformaWrapper, oidc, $offsetX="30", $offsetY="40") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` @@ -188,13 +188,13 @@ C4Container ```mermaid C4Component - title EC Validation Module - Component Diagram + title Policy Validation Module - Component Diagram Deployment_Node(trustifySystem, "Trustify System") { Container(api, "API Gateway", "Actix-web", "REST API for EC operations") - Container_Boundary(ecModule, "EC Validation Module") { - Container(ecEndpoints, "EC Endpoints", "Actix-web handlers", "REST endpoints for validation operations") - Component(ecService, "Policy Verifier service", "Business logic", "Orchestrates validation workflow") + Container_Boundary(policyValidationModule, "Policy Validation Module") { + Container(policyEndpoints, "Policy Endpoints", "Actix-web handlers", "REST endpoints for validation operations") + Component(policyVeriferService, "Policy Verifier service", "Business logic", "Orchestrates validation workflow") Component(resultParser, "Result Parser", "JSON parser", "Parses Conforma output into structured data") Component(policyManager, "Policy Manager", "Business logic", "Manages EC policy references and
configuration") Component(resultPersistence, "Result Persistence", "Database layer", "Saves validation results") @@ -202,7 +202,7 @@ C4Component } Deployment_Node(external, "External System") { Deployment_Node(trustifyPod, "Trustify Pod") { - Component(ecWrapper, "Conforma Wrapper", "Actix-web handlers", "HTTP API") + Component(conformaWrapper, "Conforma Wrapper", "Actix-web handlers", "HTTP API") } Deployment_Node(conformaPod, "Conforma Pod") { System_Ext(conforma, "Conforma CLI", "Enterprise Contract validation tool") @@ -216,24 +216,24 @@ C4Component System_Ext(s3, "S3 Object Storage", "Stores SBOM documents and reports") } - Rel(api, ecEndpoints, "POST /ec/validate,\nGET /ec/report", "JSON/HTTPS") - Rel(ecEndpoints, ecService, "validate_sbom() / get_ec_report()", "Function call") - Rel(ecService, policyManager, "get_policy_config()", "Function call") + Rel(api, policyEndpoints, "POST /ec/validate,\nGET /ec/report", "JSON/HTTPS") + Rel(policyEndpoints, policyVeriferService, "validate_sbom() / get_ec_report()", "Function call") + Rel(policyVeriferService, policyManager, "get_policy_config()", "Function call") Rel(policyManager, postgres, "SELECT policy", "SQL") - Rel(ecService, ecWrapper, "POST /api/v1/validation → returns {id}", "HTTP") - Rel(ecWrapper, conforma, "ec validate", "Process spawn") - Rel(ecWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "JSON/HTTPS") - Rel(ecService, resultParser, "parse_output()", "Function call") - Rel(ecService, resultPersistence, "save_results()", "Function call") + Rel(policyVeriferService, conformaWrapper, "POST /api/v1/validation → returns {id}", "HTTP") + Rel(conformaWrapper, conforma, "ec validate", "Process spawn") + Rel(conformaWrapper, api, "POST /api/v2/ec/validation/{validation_id}/result", "JSON/HTTPS") + Rel(policyVeriferService, resultParser, "parse_output()", "Function call") + Rel(policyVeriferService, resultPersistence, "save_results()", "Function call") Rel(resultPersistence, postgres, "INSERT policy_validation", "SQL") - Rel(ecService, s3, "Store EC report", "S3 API") + Rel(policyVeriferService, s3, "Store EC report", "S3 API") - UpdateRelStyle(api, ecEndpoints, $offsetX="-50", $offsetY="-50") - UpdateRelStyle(ecEndpoints, ecService, $offsetX="-60", $offsetY="+40") - UpdateRelStyle(ecService, ecWrapper, $offsetX="-20", $offsetY="10") - UpdateRelStyle(ecWrapper, api, $offsetX="20", $offsetY="-40") - UpdateRelStyle(ecService, resultParser, $offsetX="-60", $offsetY="+0") - UpdateRelStyle(ecService, resultPersistence, $offsetX="-60", $offsetY="+80") + UpdateRelStyle(api, policyEndpoints, $offsetX="-50", $offsetY="-50") + UpdateRelStyle(policyEndpoints, policyVeriferService, $offsetX="-60", $offsetY="+40") + UpdateRelStyle(policyVeriferService, conformaWrapper, $offsetX="-20", $offsetY="10") + UpdateRelStyle(conformaWrapper, api, $offsetX="20", $offsetY="-40") + UpdateRelStyle(policyVeriferService, resultParser, $offsetX="-60", $offsetY="+0") + UpdateRelStyle(policyVeriferService, resultPersistence, $offsetX="-60", $offsetY="+80") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` @@ -245,7 +245,7 @@ sequenceDiagram autonumber actor User participant API as Trustify API - participant EP as EC Endpoints + participant EP as Policy Endpoints participant VS as Policy Verifier service participant PM as Policy Manager participant DB as PostgreSQL @@ -296,7 +296,7 @@ sequenceDiagram sequenceDiagram autonumber participant API as Trustify API - participant EP as EC Endpoints + participant EP as Policy Endpoints participant VS as Policy Verifier service participant DB as PostgreSQL participant S3 as Object Storage @@ -475,12 +475,12 @@ GET /api/v2/policy/{id} # Get policy reference PUT /api/v2/policy/{id} # Update policy reference (admin) DELETE /api/v2/policy/{id} # Delete policy reference (admin) -POST /api/v2/policy/validate # Trigger validation (multipart form: sbom_id, policy_id) -GET /api/v2/policy/report?sbom_id={id}&policy_id={id} # Get latest validation result -GET /api/v2/policy/report/history?sbom_id={id}&policy_id={id} # Get validation history -GET /api/v2/policy/report/{result_id} # Download detailed report from S3 +POST /api/v2/policy/validate # Trigger validation (multipart form: sbom_id, policy_id) +GET /api/v2/policy/report?sbom_id={id}&policy_id={id} # Get latest validation result +GET /api/v2/policy/report/history?sbom_id={id}&policy_id={id} # Get validation history +GET /api/v2/policy/report/{result_id} # Download detailed report from S3 -POST /api/v2/ec/validation/{validation_id}/result # Callback: Conforma Wrapper posts Conforma result +POST /api/v2/policy/validation/{validation_id}/result # Callback: Conforma Wrapper posts Conforma result ``` ## Conforma Wrapper API Endpoints From ee7ddbab3f83dd42b96fd1f29b29c74b4554deb7 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 24 Mar 2026 12:07:50 +0100 Subject: [PATCH 41/52] Use JSON parameter --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 077c9d130..efc5a5d00 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -475,7 +475,7 @@ GET /api/v2/policy/{id} # Get policy reference PUT /api/v2/policy/{id} # Update policy reference (admin) DELETE /api/v2/policy/{id} # Delete policy reference (admin) -POST /api/v2/policy/validate # Trigger validation (multipart form: sbom_id, policy_id) +POST /api/v2/policy/validate?sbom_id={id}&policy_id={id} # Trigger validation GET /api/v2/policy/report?sbom_id={id}&policy_id={id} # Get latest validation result GET /api/v2/policy/report/history?sbom_id={id}&policy_id={id} # Get validation history GET /api/v2/policy/report/{result_id} # Download detailed report from S3 From 201ceb8b77d1b2236bcaba879a93aa1486cd027f Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 24 Mar 2026 12:08:14 +0100 Subject: [PATCH 42/52] Adr must be approved --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index efc5a5d00..e986c59c8 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -4,7 +4,7 @@ Date: 2026-02-03 ## Status -PROPOSED +APPROVED ## Context From 4264c088ba5e7830578f3486d99535b8554e37d1 Mon Sep 17 00:00:00 2001 From: arpcv Date: Tue, 24 Mar 2026 14:35:34 +0100 Subject: [PATCH 43/52] Actionable feedback is future work --- docs/adrs/00014-enterprise-contract-integration.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index e986c59c8..f49610ace 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -66,6 +66,10 @@ Storing full JSON in storage system rather than only a summary was chosen explic Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. +#### Receive actionable feedback on policy violations + +This is out of scope of this ADR. + ## Consequences ### The Conforma Wrapper runs externally From bd77beef7779d224ea963ac47149df21fdf263ea Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 24 Mar 2026 14:39:11 +0100 Subject: [PATCH 44/52] Future work --- docs/adrs/00014-enterprise-contract-integration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index f49610ace..ecf61b4e7 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -27,7 +27,6 @@ Users need the ability to: We will integrate Conforma into Trustify as a user triggered validation service by interacting with Conforma CLI. Validation is manually triggered — not automatic on SBOM upload. -Validation on upload is deferred to a follow-up version. Trustify stores information to identify (id, name, URL) of Policies. A default Policy is defined at the application level (global policy) which is used for validation when an SBOM does not have any Policy explicitly attached to it. @@ -62,6 +61,8 @@ Storing full JSON in storage system rather than only a summary was chosen explic ### Futur work +#### Validation on SBOM upload + #### Multi-tenancy Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. From 82890c6aaebca5bb5aa820044bc3622df3d6bcc5 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 24 Mar 2026 15:08:18 +0100 Subject: [PATCH 45/52] Use source_document.id --- docs/adrs/00014-enterprise-contract-integration.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index ecf61b4e7..eb0d7fa6a 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -54,7 +54,7 @@ The `processing_status` "In Progress" state serves as a concurrency guard: if a What is stored where - PostgreSQL: validation process state (`processing_status`), validation outcome (`status`), structured results (JSONB), summary statistics, foreign keys to SBOM and policy. Indexed on sbom_id, processing_status, status, start_time. -- Storage system: full raw Conforma JSON report, linked from the DB row via report_path. Keeps DB rows small while preserving audit completeness. +- Storage system: full raw Conforma JSON report, linked from the DB row via `source_document.id`. Keeps DB rows small while preserving audit completeness. - Not stored: the policy definitions themselves. policy stores references (URLs, OCI refs) that Conforma fetches at runtime. Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB results JSONB holds enough structure for filtering and dashboards without duplicating the full payload. @@ -329,11 +329,11 @@ sequenceDiagram alt Pass VS->>DB: UPDATE policy_validation SET verification_status='completed', status='pass', results=[] VS->>S3: store_validation_report(result_id, full_json) - VS->>DB: UPDATE SET report_path = ? + VS->>DB: UPDATE SET source_document.id = ? else Fail VS->>DB: UPDATE policy_validation SET verification_status='completed', status='fail', results=json VS->>S3: store_validation_report(result_id, full_json) - VS->>DB: UPDATE SET report_path = ? + VS->>DB: UPDATE SET source_document.id = ? else Error VS->>DB: UPDATE policy_validation SET verification_status='failed', status='error', error_message=detail Note over VS,DB: Validation can be re-triggered (new row with processing_status='in_progress', status='pending') @@ -391,7 +391,7 @@ sequenceDiagram - `verification_status` (ENUM) - 'pending', 'pass', 'fail', 'error' - `results` (JSONB) - See model below - `summary` (JSONB) - Total checks, passed, failed, warnings, see model below -- `report_path` (VARCHAR) - File system or S3 path to detailed report +- `source_document_id` (VARCHAR) - File system or S3 path to detailed report - `start_time` (TIMESTAMP) - `end_time` (TIMESTAMP) - `policy_version` (VARCHAR) - Policy commit hash or tag resolved at validation time From 8731773a8258218b1038da455fac58ef2aeabcae Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 24 Mar 2026 17:15:15 +0100 Subject: [PATCH 46/52] Move future section at bottom --- .../00014-enterprise-contract-integration.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index eb0d7fa6a..2b89dbc42 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -59,18 +59,6 @@ What is stored where Storing full JSON in storage system rather than only a summary was chosen explicitly to preserve audit completeness — callers can always fetch the raw report. The DB results JSONB holds enough structure for filtering and dashboards without duplicating the full payload. -### Futur work - -#### Validation on SBOM upload - -#### Multi-tenancy - -Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. - -#### Receive actionable feedback on policy violations - -This is out of scope of this ADR. - ## Consequences ### The Conforma Wrapper runs externally @@ -547,6 +535,18 @@ policy stores external references only. Conforma fetches the actual policy at va The policy commit hash/tag (`policy_version`) resolved at validation time are recorded in each result row, enabling reproducibility and audit. +### Futur work + +#### Validation on SBOM upload + +#### Multi-tenancy + +Policy references are global (shared across all users) in this initial implementation. Per-organization policy namespacing is out of scope here and should be addressed in a dedicated multi-tenancy ADR when Trustify adds org-level isolation more broadly. + +#### Receive actionable feedback on policy violations + +This is out of scope of this ADR. + ### References - [Enterprise Contract (Conforma) GitHub](https://github.com/enterprise-contract/ec-cli) From 93376e08044f23328683774d86b5175d39c25193 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Tue, 24 Mar 2026 17:34:51 +0100 Subject: [PATCH 47/52] Use Conforma name instead of EC --- .../00014-enterprise-contract-integration.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 2b89dbc42..ef3a809c7 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -435,15 +435,15 @@ sequenceDiagram **`policy_validation.summary` JSONB model:** -| Field | Type | Required | Description | -| ---------------- | ------- | -------- | ------------------------------------------------------------------------ | -| `success` | boolean | yes | Overall pass/fail outcome (mirrors Conforma's top-level `success` field) | -| `total` | integer | yes | Total number of checks evaluated | -| `violations` | integer | yes | Count of checks with violation severity | -| `warnings` | integer | yes | Count of checks with warning severity | -| `successes` | integer | yes | Count of checks that passed | -| `ec_version` | string | yes | Conforma version used (e.g. `"v0.8.83"`) | -| `effective_time` | string | yes | ISO 8601 timestamp of evaluation provided by Conforma | +| Field | Type | Required | Description | +| ------------------ | ------- | -------- | ------------------------------------------------------------------------ | +| `success` | boolean | yes | Overall pass/fail outcome (mirrors Conforma's top-level `success` field) | +| `total` | integer | yes | Total number of checks evaluated | +| `violations` | integer | yes | Count of checks with violation severity | +| `warnings` | integer | yes | Count of checks with warning severity | +| `successes` | integer | yes | Count of checks that passed | +| `conforma_version` | string | yes | Conforma version used (e.g. `"v0.8.83"`) | +| `effective_time` | string | yes | ISO 8601 timestamp of evaluation provided by Conforma | `policy_validation.summary` example: @@ -454,7 +454,7 @@ sequenceDiagram "violations": 1, "warnings": 1, "successes": 1, - "ec_version": "v0.8.83", + "conforma_version": "v0.8.83", "effective_time": "2026-03-03T14:36:55.807826709Z" } ``` From 8afd623882b0c21fbd38071c7d9a1541b3e0fb76 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Wed, 25 Mar 2026 10:13:54 +0100 Subject: [PATCH 48/52] Move summary into table --- docs/adrs/00014-enterprise-contract-integration.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index ef3a809c7..c1ea989b1 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -378,7 +378,13 @@ sequenceDiagram - `processing_status` (ENUM) - 'queued', 'in_progress', 'completed', 'failed' - `verification_status` (ENUM) - 'pending', 'pass', 'fail', 'error' - `results` (JSONB) - See model below -- `summary` (JSONB) - Total checks, passed, failed, warnings, see model below +- `success` (BOOLEAN) - Overall pass/fail outcome (mirrors Conforma's top-level `success` field) +- `total` (SMALLINT) - Total number of checks evaluated +- `violations` (SMALLINT) - Count of checks with violation severity +- `warnings` (SMALLINT) - Count of checks with warning severity +- `successes` (SMALLINT) - Count of checks that passed +- `conforma_version`(VARCHAR) - Conforma version used (e.g. `"v0.8.83"`) +- `effective_time` (VARCHAR) - ISO 8601 timestamp of evaluation provided by Conforma - `source_document_id` (VARCHAR) - File system or S3 path to detailed report - `start_time` (TIMESTAMP) - `end_time` (TIMESTAMP) From 8ac603caca90a7c07a96ab129d05f05de5ec7109 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 26 Mar 2026 15:31:07 +0100 Subject: [PATCH 49/52] Policy managemt better phrasing; Wrapper is included in trustd --- .../00014-enterprise-contract-integration.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index c1ea989b1..cf49787f6 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -508,17 +508,13 @@ modules/policy/ │ ├── executor.rs # Conforma Wrapper HTTP client (adapter) │ └── result_parser.rs # Output parsing └── error.rs # Error types -``` - -### Conforma Wrapper Module Structure - -``` -├── Cargo.toml -└── server - ├── lib.rs - ├── endpoints/ - │ └── mod.rs # REST endpoints - └── error.rs # Error types +└── conforma_wrapper + ├── build.rs + ├── Cargo.toml + └── src/ + ├── endpoints/ + │ └── mod.rs # REST endpoints + └── lib.rs ``` ### Technical Considerations @@ -537,7 +533,9 @@ On the Conforma Wrapper side, concurrent Conforma processes are bounded by a sem #### Policy Management -policy stores external references only. Conforma fetches the actual policy at validation time, which means Trustify does not cache policy content by default. The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and will be encrypted using AES crate; they are never logged. +`policy` stores external references only as the policy is fetched by Conforma at validation time, therefore Trustify does not cache policy content. + +The trade-off: validation always uses the latest policy version, but network failures or policy repo outages will cause execution errors. For private policy repositories, authentication credentials are stored in the configuration JSONB column and will be encrypted using AES crate; they are never logged. The policy commit hash/tag (`policy_version`) resolved at validation time are recorded in each result row, enabling reproducibility and audit. From 46b48643657f0315046cc69a2176766bb6887ac7 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 26 Mar 2026 15:39:44 +0100 Subject: [PATCH 50/52] Renamed executor.rs to client/conforma.rs --- docs/adrs/00014-enterprise-contract-integration.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index cf49787f6..71a101f31 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -494,6 +494,7 @@ POST /api/v1/validate # Validate uploaded SBOM file agains modules/policy/ ├── Cargo.toml └── src/ + ├── error.rs # Error types ├── lib.rs ├── endpoints/ │ └── mod.rs # REST endpoints @@ -505,9 +506,9 @@ modules/policy/ │ ├── mod.rs │ ├── ec_service.rs # Main orchestration │ ├── policy_manager.rs # Policy configuration - │ ├── executor.rs # Conforma Wrapper HTTP client (adapter) │ └── result_parser.rs # Output parsing - └── error.rs # Error types + └── client/ + └── conforma.rs # Conforma client adapter └── conforma_wrapper ├── build.rs ├── Cargo.toml From eaeba2962b1063f82a844e23fcb70f96eb7cba97 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 26 Mar 2026 15:41:32 +0100 Subject: [PATCH 51/52] Fix tree --- .../00014-enterprise-contract-integration.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index 71a101f31..adba9f183 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -493,22 +493,22 @@ POST /api/v1/validate # Validate uploaded SBOM file agains ``` modules/policy/ ├── Cargo.toml -└── src/ - ├── error.rs # Error types - ├── lib.rs - ├── endpoints/ - │ └── mod.rs # REST endpoints - ├── model/ - │ ├── mod.rs - │ ├── policy.rs # Policy API models - │ └── validation.rs # Validation result models - ├── service/ - │ ├── mod.rs - │ ├── ec_service.rs # Main orchestration - │ ├── policy_manager.rs # Policy configuration - │ └── result_parser.rs # Output parsing - └── client/ - └── conforma.rs # Conforma client adapter +├── src/ +│ ├── error.rs # Error types +│ ├── lib.rs +│ ├── endpoints/ +│ │ └── mod.rs # REST endpoints +│ ├── model/ +│ │ ├── mod.rs +│ │ ├── policy.rs # Policy API models +│ │ └── validation.rs # Validation result models +│ ├── service/ +│ │ ├── mod.rs +│ │ ├── ec_service.rs # Main orchestration +│ │ ├── policy_manager.rs # Policy configuration +│ │ └── result_parser.rs # Output parsing +│ └── client/ +│ └── conforma.rs # Conforma client adapter └── conforma_wrapper ├── build.rs ├── Cargo.toml From 0c8567d50a59f305ddba08349a404025640870e6 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 26 Mar 2026 15:42:56 +0100 Subject: [PATCH 52/52] File structure --- docs/adrs/00014-enterprise-contract-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adrs/00014-enterprise-contract-integration.md b/docs/adrs/00014-enterprise-contract-integration.md index adba9f183..a37edc99d 100644 --- a/docs/adrs/00014-enterprise-contract-integration.md +++ b/docs/adrs/00014-enterprise-contract-integration.md @@ -488,7 +488,7 @@ POST /api/v2/policy/validation/{validation_id}/result # Callback: Con POST /api/v1/validate # Validate uploaded SBOM file against the provided Policy URL (multipart form) ``` -### Trustify Module Structure +### Trustify File Structure ``` modules/policy/