Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
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:
@@ -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
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
50
etc/bootstrap/notify/rules/airgap-ops.rule.json
Normal file
50
etc/bootstrap/notify/rules/airgap-ops.rule.json
Normal 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"
|
||||
}
|
||||
@@ -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
5
etc/findings-ledger.yaml
Normal 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
51
etc/notify.airgap.yaml
Normal 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.
|
||||
9
etc/secrets/notify-web-airgap.secret.example
Normal file
9
etc/secrets/notify-web-airgap.secret.example
Normal 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
|
||||
Reference in New Issue
Block a user