Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly.
- Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps.
- Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges.
- Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges.
- Set up project file for the test project with necessary dependencies and configurations.
- Include JSON fixture files for testing purposes.
This commit is contained in:
master
2025-11-04 07:49:39 +02:00
parent f72c5c513a
commit 2eb6852d34
491 changed files with 39445 additions and 3917 deletions

View File

@@ -0,0 +1,20 @@
%% Standard plug-in bootstrap sequence (Mermaid)
sequenceDiagram
autonumber
participant Operator as Operator / DevOps
participant Host as Authority Host
participant Registrar as StandardPluginRegistrar
participant Store as Credential Store
participant Audit as Audit Sink
participant Telemetry as Telemetry Pipeline
Operator->>Host: Deploy plugin manifest + offline secrets bundle
Host->>Registrar: Load options + validate capabilities
Registrar->>Store: Ensure collections + indexes
Registrar->>Store: Seed bootstrap principals (hashed passwords, roles)
Store-->>Registrar: Acknowledge deterministic bootstrap state
Registrar-->>Host: Register IIdentityProviderPlugin + capability metadata
Host->>Registrar: Invoke WarmupAsync (health checks, secret validation)
Registrar->>Audit: Emit authority.plugin.load event
Registrar->>Telemetry: Emit structured logs and counters
Host-->>Operator: Report readiness (plugin lifecycle complete)

View File

@@ -0,0 +1,112 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 360" role="img">
<title>Standard plug-in bootstrap sequence</title>
<defs>
<style>
.actor { fill: #1e293b; stroke: #334155; stroke-width: 2; rx: 12; ry: 12; }
.actor text { fill: #f8fafc; font: 14px "Segoe UI", sans-serif; }
.lifeline { stroke: #475569; stroke-width: 2; stroke-dasharray: 6 6; }
.arrow { fill: none; stroke: #38bdf8; stroke-width: 2.5; marker-end: url(#arrow); }
.label { fill: #e2e8f0; font: 13px "Segoe UI", sans-serif; }
.step { fill: #94a3b8; font: 12px "Segoe UI", sans-serif; }
</style>
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M0,0 L12,6 L0,12 z" fill="#38bdf8" />
</marker>
</defs>
<g>
<rect class="actor" x="40" y="20" width="140" height="40" />
<text x="110" y="45" text-anchor="middle">Operator / DevOps</text>
<line class="lifeline" x1="110" y1="60" x2="110" y2="350" />
</g>
<g>
<rect class="actor" x="210" y="20" width="140" height="40" />
<text x="280" y="45" text-anchor="middle">Authority Host</text>
<line class="lifeline" x1="280" y1="60" x2="280" y2="350" />
</g>
<g>
<rect class="actor" x="380" y="20" width="150" height="40" />
<text x="455" y="45" text-anchor="middle">Standard Registrar</text>
<line class="lifeline" x1="455" y1="60" x2="455" y2="350" />
</g>
<g>
<rect class="actor" x="560" y="20" width="140" height="40" />
<text x="630" y="45" text-anchor="middle">Credential Store</text>
<line class="lifeline" x1="630" y1="60" x2="630" y2="350" />
</g>
<g>
<rect class="actor" x="720" y="20" width="120" height="40" />
<text x="780" y="45" text-anchor="middle">Audit Sink</text>
<line class="lifeline" x1="780" y1="60" x2="780" y2="350" />
</g>
<g>
<rect class="actor" x="870" y="20" width="120" height="40" />
<text x="930" y="45" text-anchor="middle">Telemetry</text>
<line class="lifeline" x1="930" y1="60" x2="930" y2="350" />
</g>
<g>
<text class="step" x="80" y="90">1</text>
<path class="arrow" d="M110 90 H270" />
<text class="label" x="190" y="82" text-anchor="middle">Deploy manifest + secrets bundle</text>
</g>
<g>
<text class="step" x="250" y="120">2</text>
<path class="arrow" d="M280 120 H440" />
<text class="label" x="360" y="112" text-anchor="middle">Load options and validate capabilities</text>
</g>
<g>
<text class="step" x="420" y="150">3</text>
<path class="arrow" d="M455 150 H630" />
<text class="label" x="540" y="142" text-anchor="middle">Ensure collections and indexes</text>
</g>
<g>
<text class="step" x="420" y="180">4</text>
<path class="arrow" d="M455 180 H630" />
<text class="label" x="540" y="172" text-anchor="middle">Seed bootstrap principals</text>
</g>
<g>
<text class="step" x="600" y="210">5</text>
<path class="arrow" d="M630 210 H460" />
<text class="label" x="540" y="202" text-anchor="middle">Return deterministic bootstrap state</text>
</g>
<g>
<text class="step" x="420" y="240">6</text>
<path class="arrow" d="M455 240 H280" />
<text class="label" x="360" y="232" text-anchor="middle">Registrar registers plugin + metadata</text>
</g>
<g>
<text class="step" x="250" y="270">7</text>
<path class="arrow" d="M280 270 H455" />
<text class="label" x="365" y="262" text-anchor="middle">Invoke WarmupAsync checks</text>
</g>
<g>
<text class="step" x="420" y="300">8</text>
<path class="arrow" d="M455 300 H780" />
<text class="label" x="620" y="292" text-anchor="middle">Emit authority.plugin.load audit event</text>
</g>
<g>
<text class="step" x="420" y="310">9</text>
<path class="arrow" d="M455 310 H930" />
<text class="label" x="700" y="302" text-anchor="middle">Forward structured logs and counters</text>
</g>
<g>
<text class="step" x="250" y="340">10</text>
<path class="arrow" d="M280 340 H110" />
<text class="label" x="200" y="332" text-anchor="middle">Report readiness to operator</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,50 @@
%% Standard Authority plug-in component overview (Mermaid)
flowchart LR
subgraph Host["Authority Host"]
config[AuthorityPluginConfigurationLoader
(bind + validate options)]
pluginHost[PluginHost Registrar Loader
(IAuthorityPluginRegistrar)]
api[Minimal API Endpoints
/token, /device/code, /internal/*]
telemetry[Structured Telemetry
(logs - metrics - traces)]
end
subgraph StandardPlugin["Standard Identity Provider Plug-in"]
registrar[StandardPluginRegistrar
(registers services, capabilities)]
options[StandardPluginOptions
(offline YAML input)]
identity[IIdentityProviderPlugin
(password & bootstrap flows)]
store[StandardUserCredentialStore
(Mongo collections)]
capability[Capability Metadata
(password, bootstrap, clientProvisioning)]
end
subgraph External["External Systems"]
mongo[(MongoDB cluster
credential + lockout state)]
audit[(Audit Sink / Event Bus)]
secrets[Offline Secrets Bundle
(keys, salts, bootstrap users)]
opsRepo[(Offline Kit Assets)]
end
config --> registrar
pluginHost --> registrar
registrar --> options
registrar --> capability
registrar --> identity
identity --> store
identity --> audit
store --> mongo
options --> secrets
secrets --> registrar
api --> identity
telemetry --> opsRepo
pluginHost --> telemetry
capability --> pluginHost
audit --> telemetry

View File

@@ -0,0 +1,106 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 420" role="img">
<title>Authority Standard plug-in component topology</title>
<defs>
<style>
.cluster { fill: #0b1120; stroke: #1e293b; stroke-width: 2; rx: 20; ry: 20; }
.cluster-title { fill: #e2e8f0; font: 16px "Segoe UI", sans-serif; font-weight: 600; }
.node { fill: #1e293b; stroke: #334155; stroke-width: 2; rx: 12; ry: 12; }
.node text { fill: #f8fafc; font: 14px "Segoe UI", sans-serif; }
.node-small { fill: #0f172a; stroke: #334155; stroke-width: 2; rx: 12; ry: 12; }
.node-small text { fill: #e2e8f0; font: 13px "Segoe UI", sans-serif; }
.annotation { fill: #94a3b8; font: 12px "Segoe UI", sans-serif; }
.arrow { fill: none; stroke: #38bdf8; stroke-width: 2.5; marker-end: url(#arrow); }
</style>
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M0,0 L12,6 L0,12 z" fill="#38bdf8" />
</marker>
</defs>
<rect class="cluster" x="30" y="60" width="260" height="320" rx="20" ry="20" />
<text class="cluster-title" x="65" y="88">Authority Host</text>
<g class="node">
<rect x="50" y="110" width="220" height="48" rx="12" ry="12" />
<text x="60" y="138">Configuration Loader</text>
<text class="annotation" x="60" y="156">AuthorityPluginConfigurationLoader</text>
</g>
<g class="node">
<rect x="50" y="175" width="220" height="52" rx="12" ry="12" />
<text x="60" y="205">PluginHost registrar loader</text>
<text class="annotation" x="60" y="223">IAuthorityPluginRegistrar</text>
</g>
<g class="node">
<rect x="50" y="245" width="220" height="52" rx="12" ry="12" />
<text x="60" y="275">Minimal API endpoints</text>
<text class="annotation" x="60" y="293">/token, /device/code, /internal/*</text>
</g>
<g class="node">
<rect x="50" y="315" width="220" height="52" rx="12" ry="12" />
<text x="60" y="345">Structured telemetry</text>
<text class="annotation" x="60" y="363">logs - metrics - traces</text>
</g>
<rect class="cluster" x="350" y="40" width="260" height="360" rx="20" ry="20" />
<text class="cluster-title" x="378" y="68">Standard plug-in</text>
<g class="node">
<rect x="370" y="90" width="220" height="50" rx="12" ry="12" />
<text x="380" y="118">StandardPluginRegistrar</text>
<text class="annotation" x="380" y="136">bind services &amp; capabilities</text>
</g>
<g class="node">
<rect x="370" y="155" width="220" height="50" rx="12" ry="12" />
<text x="380" y="183">StandardPluginOptions</text>
<text class="annotation" x="380" y="201">offline YAML configuration</text>
</g>
<g class="node">
<rect x="370" y="220" width="220" height="50" rx="12" ry="12" />
<text x="380" y="248">Capability metadata</text>
<text class="annotation" x="380" y="266">password - bootstrap - clientProvisioning</text>
</g>
<g class="node">
<rect x="370" y="285" width="220" height="48" rx="12" ry="12" />
<text x="380" y="313">IIdentityProviderPlugin</text>
<text class="annotation" x="380" y="331">password &amp; bootstrap flows</text>
</g>
<g class="node">
<rect x="370" y="345" width="220" height="48" rx="12" ry="12" />
<text x="380" y="373">StandardUserCredentialStore</text>
<text class="annotation" x="380" y="391">Mongo-backed state</text>
</g>
<rect class="cluster" x="670" y="60" width="260" height="320" rx="20" ry="20" />
<text class="cluster-title" x="715" y="88">External systems</text>
<g class="node-small">
<rect x="690" y="110" width="220" height="46" rx="12" ry="12" />
<text x="700" y="138">Offline secrets bundle</text>
<text class="annotation" x="700" y="156">keys - salts - bootstrap users</text>
</g>
<g class="node-small">
<rect x="690" y="170" width="220" height="46" rx="12" ry="12" />
<text x="700" y="198">MongoDB cluster</text>
<text class="annotation" x="700" y="216">credential &amp; lockout state</text>
</g>
<g class="node-small">
<rect x="690" y="230" width="220" height="46" rx="12" ry="12" />
<text x="700" y="258">Audit/event sink</text>
<text class="annotation" x="700" y="276">authority.security.* stream</text>
</g>
<g class="node-small">
<rect x="690" y="290" width="220" height="46" rx="12" ry="12" />
<text x="700" y="318">Offline kit exports</text>
<text class="annotation" x="700" y="336">docs/assets and config bundles</text>
</g>
<path class="arrow" d="M270 134 H340" />
<path class="arrow" d="M270 204 H340" />
<path class="arrow" d="M270 256 H340" />
<path class="arrow" d="M270 326 H340" />
<path class="arrow" d="M470 140 L470 176" />
<path class="arrow" d="M470 205 L470 236" />
<path class="arrow" d="M470 270 L470 308" />
<path class="arrow" d="M470 330 L470 362" />
<path class="arrow" d="M592 120 C630 104, 650 104, 670 120" />
<path class="arrow" d="M592 190 C630 180, 650 182, 670 196" />
<path class="arrow" d="M592 248 C630 246, 650 246, 670 250" />
<path class="arrow" d="M592 308 C630 308, 650 318, 670 326" />
<path class="arrow" d="M592 360 L670 360" />
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB