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

@@ -1,17 +1,63 @@
# Placeholder configuration for the LDAP identity provider plug-in.
# Replace values with your directory settings before enabling the plug-in.
# Example configuration for the LDAP identity provider plug-in.
# Adjust values to match your directory deployment before enabling the plugin.
connection:
host: "ldap.example.com"
host: "ldaps://ldap.example.internal"
port: 636
useTls: true
bindDn: "cn=service,dc=example,dc=com"
bindPassword: "CHANGE_ME"
useStartTls: false
validateCertificates: true
clientCertificate:
pfxPath: "file:/etc/stellaops/certs/ldap-client.pfx"
passwordSecret: "file:/etc/stellaops/secrets/ldap-client-pfx.txt"
sendChain: true
trustStore:
mode: system # system | bundle
bundlePath: "file:/etc/stellaops/trust/ldap-root.pem"
searchBase: "ou=people,dc=example,dc=internal"
usernameAttribute: "uid"
userDnFormat: "uid={username},ou=people,dc=example,dc=internal"
bindDn: "cn=stellaops-bind,ou=service,dc=example,dc=internal"
bindPasswordSecret: "file:/etc/stellaops/secrets/ldap-bind.txt"
security:
requireTls: true
allowInsecureWithEnvToggle: false # set STELLAOPS_LDAP_ALLOW_INSECURE=true to permit TLS downgrade
allowedCipherSuites:
- "TLS_AES_256_GCM_SHA384"
- "TLS_AES_128_GCM_SHA256"
referralChasing: false
lockout:
useAuthorityPolicies: true
directoryLockoutAttribute: "pwdAccountLockedTime"
claims:
groupAttribute: "memberOf"
groupToRoleMap:
"cn=stellaops-admins,ou=groups,dc=example,dc=internal": "operators"
"cn=stellaops-read,ou=groups,dc=example,dc=internal": "auditors"
regexMappings:
- pattern: "^cn=stellaops-(?P<role>[a-z-]+),ou=groups,dc=example,dc=internal$"
roleFormat: "{role}"
extraAttributes:
displayName: "displayName"
email: "mail"
queries:
userFilter: "(uid={username})"
groupFilter: "(member={distinguishedName})"
groupAttribute: "cn"
userFilter: "(&(objectClass=person)(uid={username}))"
attributes:
- "displayName"
- "mail"
- "memberOf"
capabilities:
supportsPassword: true
supportsMfa: false
clientProvisioning:
enabled: false
containerDn: "ou=service,dc=example,dc=internal"
secretAttribute: "userPassword"
auditMirror:
enabled: true
collectionName: "ldap_client_provisioning"
health:
probeIntervalSeconds: 60
timeoutSeconds: 5

View File

@@ -65,6 +65,24 @@ notifications:
scope: "notify.escalate"
requireAdminScope: true
vulnerabilityExplorer:
workflow:
antiForgery:
enabled: true
audience: "stellaops:vuln-workflow"
defaultLifetime: "00:10:00"
maxLifetime: "00:30:00"
maxContextEntries: 16
maxContextValueLength: 256
attachments:
enabled: true
payloadType: "application/vnd.stellaops.vuln-attachment-token+json"
defaultLifetime: "00:30:00"
maxLifetime: "04:00:00"
maxMetadataEntries: 16
maxMetadataValueLength: 512
# Authority signs attachment tokens; Policy/UI must call verify before honouring downloads.
delegation:
quotas:
# Maximum concurrent delegated (service account) tokens per tenant.
@@ -81,6 +99,7 @@ delegation:
authorizedClients:
- "export-center-worker"
attributes:
# Keys map to vulnerability ABAC parameters (vuln_env / vuln_owner / vuln_business_tier).
env: [ "prod", "stage" ]
owner: [ "secops" ]
business_tier: [ "tier-1" ]

View File

@@ -0,0 +1,50 @@
{
"schemaVersion": "notify.rule@1",
"ruleId": "rule-airgap-ops",
"tenantId": "bootstrap",
"name": "Air-gap operations alerts",
"description": "Send time-drift, bundle import, and portable export notifications with remediation steps.",
"enabled": true,
"match": {
"eventKinds": [
"airgap.time.drift",
"airgap.bundle.import",
"airgap.portable.export.completed"
],
"minSeverity": "medium",
"labels": [],
"namespaces": [],
"repositories": [],
"digests": [],
"componentPurls": [],
"verdicts": [],
"kevOnly": false,
"vex": {
"includeAcceptedJustifications": true,
"includeRejectedJustifications": true,
"includeUnknownJustifications": true,
"justificationKinds": []
}
},
"actions": [
{
"actionId": "email-airgap-ops",
"channel": "email:airgap-ops",
"template": "airgap-ops",
"enabled": true,
"metadata": {
"locale": "en-us"
}
}
],
"labels": {
"category": "airgap"
},
"metadata": {
"source": "bootstrap-pack"
},
"createdBy": "bootstrap-pack",
"createdAt": "2025-11-03T08:00:00Z",
"updatedBy": "bootstrap-pack",
"updatedAt": "2025-11-03T08:00:00Z"
}

View File

@@ -0,0 +1,16 @@
{
"schemaVersion": "notify.template@1",
"templateId": "tmpl-airgap-ops-email",
"tenantId": "bootstrap",
"channelType": "email",
"key": "airgap-ops",
"locale": "en-us",
"renderMode": "html",
"format": "email",
"description": "Air-gapped operations alert for time drift, bundle imports, and portable exports.",
"body": "<p><strong>Air-gap status:</strong> {{payload.status}} (severity {{payload.severity}})</p>\n{{#if payload.driftSeconds}}<p>Current drift: {{payload.driftSeconds}} seconds. Remaining budget: {{payload.remainingSeconds}} seconds (anchor issued {{payload.anchorIssuedAt}}).</p>{{/if}}\n{{#if payload.bundleId}}<p><strong>Portable bundle:</strong> <code>{{payload.bundleId}}</code> (profile {{payload.profile}}) exported at {{payload.exportedAt}}.</p>{{/if}}\n{{#if payload.sizeBytes}}<p>Bundle size: {{payload.sizeBytes}} bytes.</p>{{/if}}\n{{#if payload.checksum.sha256}}<p>Checksum (SHA-256): <code>{{payload.checksum.sha256}}</code></p>{{/if}}\n{{#if payload.checksum.sha512}}<p>Checksum (SHA-512): <code>{{payload.checksum.sha512}}</code></p>{{/if}}\n{{#if payload.locations}}<p><strong>Locations</strong></p><ul>{{#each payload.locations}}<li>{{#if path}}File: <code>{{path}}</code>{{/if}}{{#if reference}}OCI: <code>{{reference}}</code>{{/if}}{{#if availableUntil}} (available until {{availableUntil}}){{/if}}</li>{{/each}}</ul>{{/if}}\n{{#if payload.warnings}}<p><strong>Warnings</strong></p><ul>{{#each payload.warnings}}<li>{{message}}</li>{{/each}}</ul>{{/if}}\n<p>{{payload.remediation}}</p>\n{{#if payload.links.docs}}<p>{{link \"Review guidance\" payload.links.docs}}</p>{{/if}}\n{{#if payload.links.manifest}}<p>{{link \"View manifest\" payload.links.manifest}}</p>{{/if}}",
"metadata": {
"author": "bootstrap-pack",
"version": "2025-11-03"
}
}

5
etc/findings-ledger.yaml Normal file
View File

@@ -0,0 +1,5 @@
# Sample configuration for StellaOps Findings Ledger service
findings:
ledger:
database:
connectionString: Host=postgres

51
etc/notify.airgap.yaml Normal file
View File

@@ -0,0 +1,51 @@
# Notify WebService configuration — air-gapped bootstrap profile
#
# This template ships inside the Bootstrap Pack so operators can stage
# deterministic notifier settings without reaching external services. The
# values align with the docker-compose.airgap.yaml profile and the defaults
# produced by the Offline Kit builder. Update the connection string and
# Authority endpoints if your environment uses different hosts.
storage:
driver: mongo
connectionString: "mongodb://stellaops:airgap-password@mongo:27017"
database: "stellaops_notify_airgap"
commandTimeoutSeconds: 45
authority:
enabled: true
issuer: "https://authority.airgap.local"
metadataAddress: "https://authority.airgap.local/.well-known/openid-configuration"
requireHttpsMetadata: true
allowAnonymousFallback: false
backchannelTimeoutSeconds: 30
tokenClockSkewSeconds: 60
audiences:
- notify
viewerScope: notify.viewer
operatorScope: notify.operator
adminScope: notify.admin
api:
basePath: "/api/v1/notify"
internalBasePath: "/internal/notify"
tenantHeader: "X-StellaOps-Tenant"
plugins:
baseDirectory: "/opt/stellaops"
directory: "plugins/notify"
searchPatterns:
- "StellaOps.Notify.Connectors.*.dll"
orderedPlugins:
- StellaOps.Notify.Connectors.Email
- StellaOps.Notify.Connectors.Webhook
telemetry:
enableRequestLogging: true
minimumLogLevel: Information
# In sealed/air-gapped mode, outbound connectors are constrained by the
# shared EgressPolicy facade. Channels that point to loopback services (SMTP
# relay, syslog forwarder, file sink) are permitted; external webhooks are
# denied until the host is unsealed or allow-listed. Review
# docs/modules/notify/bootstrap-pack.md for the full bootstrap workflow.

View File

@@ -0,0 +1,9 @@
# Replace this file with the site-specific client secret for the notify-web
# Authority client when running in sealed / air-gapped environments.
#
# Keep the secret outside version control. When building the Bootstrap Pack or
# Offline Kit, copy the populated file alongside notify.yaml so the container
# can load it via environment injection (for example `env_file` or Kubernetes
# Secret mounts).
NOTIFY_WEB_CLIENT_SECRET=change-me-airgap