Teck Cloud implements comprehensive supply chain security measures to ensure the integrity and authenticity of our container images and software artifacts.
All container images are built using SLSA (Supply-chain Levels for Software Artifacts) Build Level 3 standards:
- ✅ Isolated Builds: All builds run in isolated GitHub Actions environments
- ✅ Build Provenance: Cryptographically signed provenance attestations for every build
- ✅ Non-falsifiable: Provenance uses GitHub's OIDC tokens (keyless signing with Sigstore)
- ✅ Hermetic: All GitHub Actions are pinned by SHA256 digest (not tags)
- ✅ Automated: No manual steps in the build/release process
Our build process meets EU CRA requirements:
- ✅ Machine-readable SBOMs: Dual format (SPDX + CycloneDX) for every image
- ✅ 10-year retention: All SBOMs stored in GitHub Releases (permanent storage)
- ✅ Cryptographic signatures: All SBOMs and VEX documents signed with Cosign
- ✅ Vulnerability tracking: VEX (Vulnerability Exploitability eXchange) documents for exploitability status
- ✅ External SBOM observability: CycloneDX SBOMs uploaded to Dependency-Track and SecObserve on releases and internal PRs
We generate SBOMs in three locations for different purposes:
Purpose: Compliance audits, security analysis, long-term archival Formats: SPDX JSON + CycloneDX JSON Signed: Yes (Cosign keyless with Sigstore) Storage: GitHub Releases (10-year retention) Access:
# Download SBOMs from latest release
gh release download v1.0.0 --pattern "sbom-*.json"
# Verify SBOM signature
cosign verify-blob sbom-catalog.spdx.json \
--bundle sbom-catalog.spdx.json.bundle \
--certificate-identity-regexp="^https://github.com/Teck/Teck.Cloud/.*" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.comPurpose: Continuous dependency and vulnerability monitoring across releases and internal PR builds
Format: CycloneDX JSON
Service model: One Dependency-Track project per service (for example catalog, customer, auth)
Version model:
- Releases:
<release-version>(for example1.2.3) - Internal PRs:
pr-<number>-linux-amd64andpr-<number>-linux-arm64(for examplepr-842-linux-amd64,pr-842-linux-arm64)
SecObserve mapping:
- Product: Service/app specific product (for example
catalog,customer,gateway,web,mobile,admin) - Product Group:
Teck.Cloud(configured in SecObserve) - Branch/version: Releases use the release version, while internal PRs use the stable branch
pr-<number> - Service origin:
SO_ORIGIN_SERVICE=<service>
Purpose: Day-to-day operations, quick package lookups, team debugging Format: SPDX JSON (generated by Trivy) Signed: No (auto-generated by Harbor) Storage: OCI accessories (linked to images) Access: Harbor Web UI → Projects → teck-lab → Repositories → [service] → SBOM tab
For compliance purposes, always use GitHub-generated SBOMs.
DEPENDENCYTRACK_HOSTNAMEDEPENDENCYTRACK_PORT(optional, defaults to443)DEPENDENCYTRACK_PROTOCOL(optional, defaults tohttps)SO_API_BASE_URL
DEPENDENCYTRACK_APIKEYSO_API_TOKEN
- Releases fail if SBOM uploads to Dependency-Track or SecObserve fail.
- Internal PRs fail if SBOM uploads to Dependency-Track or SecObserve fail.
- Fork PRs skip external uploads because GitHub does not expose repository secrets to fork workflows.
VEX documents track which CVEs are actually exploitable in our images:
- Format: OpenVEX JSON
- Tool: Grype with reachability analysis
- Signed: Yes (Cosign)
- Storage: GitHub Releases
Download VEX:
gh release download v1.0.0 --pattern "vex-*.json"
# Check exploitability status
jq '.statements[] | select(.status == "not_affected")' vex-catalog.openvex.jsonVEX Status Values:
affected- Vulnerability is present and exploitablenot_affected- Vulnerability present but not exploitable (with justification)under_investigation- Status being determinedfixed- Vulnerability has been patched
All container images are signed using Cosign with keyless (OIDC-based) signatures:
- No long-lived secrets: Uses GitHub Actions OIDC tokens
- Transparency: All signatures recorded in Sigstore's Rekor transparency log
- Non-repudiable: Certificates tied to GitHub workflow identity
Prerequisites:
# Install cosign
brew install cosign # macOS
# or download from https://github.com/sigstore/cosign/releasesVerify an image:
cosign verify \
--certificate-identity-regexp="^https://github.com/Teck/Teck.Cloud/.*" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
harbor.tecklab.dk/teck-lab/catalog:1.0.0Successful output:
Verification for harbor.tecklab.dk/teck-lab/catalog:1.0.0 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified offline
- The code-signing certificate was verified using trusted certificate authority certificates
Harbor is configured to enforce signature verification at pull time:
- Policy: Only signed images can be pulled from
teck-labproject - Verification: Harbor validates Cosign signatures against Sigstore
- Result: Unsigned images are rejected with error
Test enforcement:
# Signed image - should succeed
docker pull harbor.tecklab.dk/teck-lab/catalog:1.0.0
✅ Success
# Unsigned image - should fail
docker pull harbor.tecklab.dk/teck-lab/test-unsigned:latest
❌ Error: image signature verification failedSBOM attestations link SBOMs to specific image digests using in-toto format:
# Verify SBOM attestation
gh attestation verify oci://harbor.tecklab.dk/teck-lab/catalog:1.0.0 \
--owner TeckBuild provenance attestations prove how an image was built:
# Verify build provenance
gh attestation verify oci://harbor.tecklab.dk/teck-lab/catalog:1.0.0 \
--predicate-type https://slsa.dev/provenance/v1 \
--owner TeckProvenance includes:
- Builder ID (GitHub Actions workflow)
- Source repository and commit SHA
- Build parameters and environment
- Materials (dependencies) used during build
- Byproducts (SBOMs, scan results)
We provide a comprehensive verification script that checks all security artifacts:
# Verify all services in a release
./scripts/verify-signatures.sh 1.0.0
# Verify specific service
./scripts/verify-signatures.sh 1.0.0 catalogThe script verifies:
- ✅ Image Cosign signature
- ✅ SPDX SBOM signature
- ✅ CycloneDX SBOM signature
- ✅ VEX document signature
- ✅ SBOM attestations
- ✅ Build provenance attestations (SLSA L3)
Trivy runs in two modes in this repository:
- PR/release image scanning: Built container images are scanned during PR validation and release publishing.
- Repository filesystem scanning: The
.github/workflows/security-gap-scans.ymlworkflow scans the checked-out repository for dependencies, secrets, and misconfigurations.
Container image scans cover the published runtime artifact:
- Scan types: OS packages, language dependencies, configuration issues
- Severities: CRITICAL, HIGH, MEDIUM, LOW
- Output: SARIF (uploaded to GitHub Security) + JSON (in releases)
- Trigger: Automatic on internal PR builds and every release
View scan results:
- GitHub Security tab:
https://github.com/Teck/Teck.Cloud/security/code-scanning - Download JSON:
gh release download v1.0.0 --pattern "trivy-scan-*.json"
Repository filesystem scans cover source-level dependency, secret, and misconfiguration findings:
- Scan types: Vulnerabilities, secrets, misconfigurations
- Severities: CRITICAL, HIGH
- Output: SARIF (uploaded to GitHub Security and SecObserve)
- Trigger: Pull requests, pushes to
main, weekly schedule, and manual runs
Grype provides reachability analysis for VEX generation:
- Reachability: Determines if vulnerable code is actually executed
- Output: OpenVEX format with exploitability status
- Use case: Prioritize remediation based on actual risk
The workflow .github/workflows/security-gap-scans.yml adds additional scanner coverage for categories not fully covered by release/PR container scanning:
- Trivy filesystem: Repository-level vulnerability, secret, and misconfiguration scanning
- CodeQL (SAST): Managed by GitHub Security CodeQL default setup (repository Security settings), not this workflow
- Gitleaks: Secret detection and SARIF upload to GitHub Security
- Semgrep: Multi-language static analysis (OpenGrep-style rule coverage)
- Checkov (IaC/GitOps):
.github/workflows/**for GitHub Actions security posturedeployment/**when present (Terraform/Kubernetes/Dockerfile/secrets)
- Bandit: Python security scanning when Python files exist
- ClamAV: Scheduled/manual malware scanning
- OWASP ZAP baseline: Optional DAST scan for a configured URL
Execution model:
- Push/PR: Trivy filesystem, Gitleaks, Semgrep, Checkov, Bandit (if applicable)
- Schedule (
0 2 * * 1): Same as above, plus ClamAV - Manual (
workflow_dispatch): Includes Trivy filesystem, ClamAV; includes ZAP whenDAST_TARGET_URLis set
DAST configuration requirement:
- ZAP baseline runs only when repository variable
DAST_TARGET_URLis set. - If
DAST_TARGET_URLis empty or missing, thezap-baselinejob is skipped (disabled), including manual runs.
The teck-lab Harbor project has the following security policies enabled:
Deployment Security:
- ✅ Cosign signature verification: Enforced (unsigned images rejected)
- ☐ Notation signature verification: Disabled (not using Notation)
Vulnerability Scanning:
- ✅ Automatically scan images on push: Enabled
- ✅ SBOM generation: Enabled (Trivy-based, SPDX format)
- ☐ Prevent vulnerable images from running: Optional (currently disabled)
To enable stricter vulnerability policy:
- Login to Harbor:
https://harbor.tecklab.dk - Navigate: Projects → teck-lab → Configuration
- Check: "Prevent vulnerable images from running"
- Set: Severity threshold (e.g., block CRITICAL/HIGH)
We use Dependabot to automatically update dependencies:
NuGet packages (daily):
- Scans .NET project dependencies
- Creates PRs for updates
- Ignores test framework updates (stable versions pinned)
GitHub Actions (weekly, Monday 3am):
- Updates pinned action SHAs
- Ensures supply chain security patches applied
- Labeled:
dependencies,github-actions,security
Review Dependabot PRs:
# List open Dependabot PRs
gh pr list --label dependencies
# Review specific PR
gh pr view <pr-number>If an image is suspected to be compromised:
-
Verify signature:
./scripts/verify-signatures.sh <version> <service>
-
Check build provenance:
gh attestation verify oci://harbor.tecklab.dk/teck-lab/<service>:<version> \ --predicate-type https://slsa.dev/provenance/v1
-
Review build logs:
- GitHub Actions:
https://github.com/Teck/Teck.Cloud/actions - Find workflow run for release
- Check for anomalies
- GitHub Actions:
-
Compare SBOM with known good version:
diff <(jq -S '.packages' sbom-catalog-1.0.0.spdx.json) \ <(jq -S '.packages' sbom-catalog-1.0.1.spdx.json)
When a new CVE is announced:
-
Check VEX documents:
jq '.statements[] | select(.vulnerability.name == "CVE-2024-XXXX")' \ vex-catalog.openvex.json -
If affected:
- Determine fix version from VEX
action_statement - Update dependencies
- Trigger new release
- Determine fix version from VEX
-
If not affected:
- VEX justification explains why (code not in execute path, etc.)
- No action needed, but document in security advisories
Use the official SLSA Verifier:
# Install SLSA verifier
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest
# Verify SLSA provenance
slsa-verifier verify-image \
harbor.tecklab.dk/teck-lab/catalog:1.0.0 \
--source-uri github.com/Teck/Teck.Cloud \
--source-tag v1.0.0Expected output:
Verified signature against tlog entry index XXX at URL: ...
Verified build using builder "https://github.com/Teck/Teck.Cloud/.github/workflows/reusable-build-sign-sbom.yml@refs/tags/v1.0.0" at commit abc123...
Verifying provenance: PASSED
SLSA verification: PASSED (Build L3)
- ✅ Machine-readable SBOM in SPDX format
- ✅ SBOM retention for 10 years (GitHub Releases)
- ✅ Vulnerability tracking (Trivy + Grype)
- ✅ Exploitability status (VEX documents)
- ✅ Cryptographic signatures on all artifacts
- ✅ Non-falsifiable provenance (SLSA L3)
- ✅ Automated security updates (Dependabot)
Please DO NOT file public GitHub issues for security vulnerabilities.
To report a security vulnerability:
-
Email: security@teck.dk
-
Include:
- Description of the vulnerability
- Steps to reproduce
- Affected versions/components
- Suggested remediation (if known)
-
Response time:
- Initial response: Within 48 hours
- Status update: Within 7 days
- Fix timeline: Based on severity
We follow responsible disclosure:
- Coordinated disclosure with 90-day embargo
- Security advisories published via GitHub Security Advisories
- CVE requested for critical vulnerabilities
- SLSA Framework
- Sigstore/Cosign
- SPDX Specification
- CycloneDX Specification
- OpenVEX Specification
- Harbor Documentation
- Cosign - Container signing
- Syft - SBOM generation
- Grype - Vulnerability scanning
- Trivy - Security scanner
- SLSA Verifier - Provenance verification
- Security inquiries: security@teck.dk
- General support: support@teck.dk
- Website: https://teck.dk
- Documentation: https://docs.teck.dk
Last Updated: 2026-03-08 Security Policy Version: 1.0.0