7 Proven Software Supply Chain Security Tactics

Engineering leaders: if software supply chain security is the mandate, this is your copy-paste plan. Below you’ll wire SBOM, VEX, and SLSA into CI so every build ships with signed build provenance, developer-owned triage, and deploy fail-gates that block exploitable risk—without slowing velocity.

You’ll get:

  • Auto-SBOM per build, attached to the artifact.
  • VEX that separates exploitable vulnerabilities from noise.
  • SLSA provenance to prove what built what, when.
  • Fail-gates + exception workflows that keep velocity high.
Software Supply Chain Security Tactics

If you manage Android fleets, don’t miss our step-by-step guide to Android patch automation to ship the November 2025 update in 48 hours with staged rollouts, MDM gates, and device-posture telemetry.

keep learning on our Bloghttps://www.cybersrely.com/blog/.


TL;DR: Software Supply Chain Security checklist

  1. Generate an SBOM on every build (CycloneDX or SPDX).
  2. Emit a VEX per release to mark exploitable vs not_affected.
  3. Produce SLSA provenance and sign both artifact & attestations.
  4. Gate deploys on signature + provenance + “no exploitable criticals.”
  5. Allow time-boxed exceptions with expiration and compensating controls.

GitHub Actions for software supply chain security (SBOM → VEX → SLSA)

1) Build + SBOM (CycloneDX or SPDX)

# .github/workflows/supply-chain.yml
name: supply-chain-security
on: [push]

permissions:
  contents: read
  id-token: write     # for keyless signing
  packages: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 1 }

      - name: Build (Node example)
        run: |
          set -euo pipefail
          npm ci
          npm run build

  sbom:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4

      # CycloneDX for Node (swap for your stack)
      - name: Install CycloneDX
        run: npm i -g @cyclonedx/cyclonedx-npm

      - name: Generate SBOM (CycloneDX JSON)
        run: cyclonedx-npm --output-file sbom.cdx.json --spec-version 1.5

      - uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.cdx.json

Python example (per-service SBOM):

pip install cyclonedx-bom
cyclonedx-py --format json --outfile sbom.cdx.json

Java/Maven example (module SBOM):

<!-- pom.xml -->
<plugin>
  <groupId>org.cyclonedx</groupId>
  <artifactId>cyclonedx-maven-plugin</artifactId>
  <version>2.8.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals><goal>makeAggregateBom</goal></goals>
      <configuration><schemaVersion>1.5</schemaVersion></configuration>
    </execution>
  </executions>
</plugin>

2) Scan + produce VEX (downgrade the noise)

  scan-and-vex:
    runs-on: ubuntu-latest
    needs: sbom
    steps:
      - uses: actions/download-artifact@v4
        with: { name: sbom }

      # Use a maintained scanner Action to avoid curl scripts
      - name: Scan SBOM for known vulns
        uses: anchore/scan-action@v3
        with:
          sbom: 'sbom.cdx.json'
          fail-build: false
        id: scan

      - name: Generate OpenVEX from scan output (simple policy)
        run: |
          echo '${{ steps.scan.outputs.scan-results }}' > grype.json
          jq -n --argfile g grype.json '
          {
            "@context":"https://openvex.dev/ns/v0.2.0",
            "statements": (
              $g.matches
              | group_by(.vulnerability.id)
              | map({
                  vulnerability: (.[0].vulnerability.id),
                  products: [ { "@id": "pkg:app/${{ github.repository }}@${{ github.sha }}" } ],
                  status: (
                    if any(.artifact.locations[]?.path; test("dev|test|examples")) then "not_affected"
                    else "affected" end
                  ),
                  justification: "inlineMitigationsAlreadyExist"
                })
            )
          }' > vex.openvex.json

      - uses: actions/upload-artifact@v4
        with: { name: vex, path: vex.openvex.json }

As your policy matures, enrich the VEX generator with runtime context, feature flags, OS packages, and compensating controls so your VEX reflects exploitability in your environment.

3) Sign artifact + attach SLSA provenance (keyless)

  sign-and-provenance:
    runs-on: ubuntu-latest
    needs: build
    env:
      IMAGE: ghcr.io/${{ github.repository }}/app:${{ github.sha }}
    steps:
      - uses: actions/checkout@v4

      - name: Build container
        run: docker build -t "$IMAGE" .

      - name: Push to GHCR
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin
          docker push "$IMAGE"

      - name: Install cosign (action)
        uses: sigstore/cosign-installer@v3

      - name: Keyless sign
        run: cosign sign "$IMAGE" --yes

      - name: Create SLSA provenance predicate (minimal)
        run: |
          cat > provenance.json <<'JSON'
          {
            "_type": "https://in-toto.io/Statement/v0.1",
            "subject": [{"name": "IMAGE_PLACEHOLDER"}],
            "predicateType": "https://slsa.dev/provenance/v1",
            "predicate": {
              "builder": { "id": "github-actions" },
              "buildType": "container",
              "invocation": {
                "parameters": { "sha": "${{ github.sha }}", "ref": "${{ github.ref_name }}" }
              },
              "metadata": { "buildStartedOn": "${{ github.run_started_at }}" }
            }
          }
          JSON
          sed -i "s/IMAGE_PLACEHOLDER/${IMAGE//\//\\/}/" provenance.json

      - name: Attach provenance attestation
        run: cosign attest --type slsaprovenance --predicate provenance.json "$IMAGE" --yes

      - name: Attach SBOM and VEX as attestations
        run: |
          cosign attest --type cyclonedx --predicate sbom.cdx.json "$IMAGE" --yes
          cosign attest --type openvex --predicate vex.openvex.json "$IMAGE" --yes

4) Gate deploys: signatures + provenance + exploitable-critical=0

# .github/workflows/deploy.yml
name: deploy
on: { workflow_dispatch: {} }

jobs:
  verify-and-deploy:
    runs-on: ubuntu-latest
    env:
      IMAGE: ghcr.io/${{ github.repository }}/app:${{ github.sha }}
    steps:
      - uses: sigstore/cosign-installer@v3

      - name: Verify signature & provenance
        run: |
          cosign verify "$IMAGE" \
           --certificate-oidc-issuer https://token.actions.githubusercontent.com \
           --certificate-identity-regexp "https://github.com/${{ github.repository }}.+"
          cosign verify-attestation "$IMAGE" --type slsaprovenance

      - name: Pull SBOM & VEX attestations and enforce policy
        run: |
          cosign verify-attestation "$IMAGE" --type cyclonedx --output-json > sbom_att.json
          cosign verify-attestation "$IMAGE" --type openvex   --output-json > vex_att.json

          # Block if any statement remains affected at Critical severity (toy logic; adapt!)
          if jq -e '
              .payload | @base64d | fromjson | .predicate.statements[]
              | select(.status=="affected")
            ' vex_att.json; then
            echo "Exploitable issues remain — blocking deploy"; exit 1
          fi

      - name: Deploy
        run: ./scripts/deploy.sh "$IMAGE"

Bonus: add a tiny OPA/Rego policy for declarative gates

# policy/vex.rego
package policy.vex

deny[msg] {
  some s
  s := input.vex.statements[_]
  s.status == "affected"
  msg := sprintf("Exploitable vuln blocked: %v", [s.vulnerability])
}

Free Website Vulnerability Scanner (landing)

Screenshot of the free tools webpage where you can access security assessment tools for different vulnerability detection.
Screenshot of the free tools webpage where you can access security assessment tools for different vulnerability detection.

GitLab CI: replicate software supply chain security controls

# .gitlab-ci.yml
stages: [build, sbom, scan, attest, deploy]
variables:
  IMAGE: $CI_REGISTRY_IMAGE/app:$CI_COMMIT_SHA

build:
  stage: build
  image: docker:24
  services: [docker:24-dind]
  script:
    - docker build -t "$IMAGE" .
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker push "$IMAGE"

sbom:
  stage: sbom
  image: node:20
  script:
    - npm i -g @cyclonedx/cyclonedx-npm
    - cyclonedx-npm --output-file sbom.cdx.json --spec-version 1.5
  artifacts: { paths: [sbom.cdx.json] }

scan:
  stage: scan
  image: alpine:3.20
  script:
    - apk add --no-cache jq
    - echo "Run your preferred SBOM scanner here; export JSON to grype.json"
    - echo '{"matches":[]}' > grype.json || true
  artifacts: { paths: [grype.json] }

attest:
  stage: attest
  image: alpine:3.20
  script:
    - apk add --no-cache cosign jq
    - cosign sign "$IMAGE" --yes
    - cat > provenance.json <<'JSON'
      { "_type":"https://in-toto.io/Statement/v0.1",
        "subject":[{"name":"IMAGE_PLACEHOLDER"}],
        "predicateType":"https://slsa.dev/provenance/v1",
        "predicate":{"builder":{"id":"gitlab-ci"},"buildType":"container",
          "invocation":{"parameters":{"sha":"$CI_COMMIT_SHA","pipeline":"$CI_PIPELINE_ID"}}}}
      JSON
    - sed -i "s/IMAGE_PLACEHOLDER/${IMAGE//\//\\/}/" provenance.json
    - cosign attest --type slsaprovenance --predicate provenance.json "$IMAGE" --yes

deploy:
  stage: deploy
  rules: [ { if: '$MANUAL_DEPLOY == "true"', when: manual } ]
  image: alpine:3.20
  script:
    - apk add --no-cache cosign jq
    - cosign verify "$IMAGE"
    - cosign verify-attestation "$IMAGE" --type slsaprovenance
    - ./deploy.sh "$IMAGE"

Time-boxed exceptions (keep velocity high)

Create a VEX override file checked into the repo; merge it before attesting.

// exceptions/vex-overrides.json
{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "statements": [{
    "vulnerability": "CVE-2025-12345",
    "products": [{ "@id": "pkg:app/myapp@${GIT_SHA}" }],
    "status": "under_investigation",
    "justification": "compensatingControls",
    "timestamp": "2025-11-04T12:00:00Z",
    "expires": "2025-12-04T12:00:00Z",
    "impact_statement": "Feature X disabled; WAF rule in place; monitored."
  }]
}

Merge step:

jq -s '
  reduce .[] as $v ({"@context":"https://openvex.dev/ns/v0.2.0","statements":[]};
    .statements += ($v.statements // [])
  )
' vex.openvex.json exceptions/vex-overrides.json > vex.merged.json

Operational tips for software supply chain security

  • One artifact, many attestations. Attach SBOM, VEX, SLSA to the digest, not the tag.
  • Evidence bucket. Archive SBOM, VEX, provenance, and the release decision JSON in immutable storage.
  • Guardrails, not gates. Start with warnings; flip to blocking once exceptions and ownership are clear.
  • Shift left on policy. Keep OPA/Rego and VEX mapping in-repo so engineers can iterate via PRs.

Sample report snippet by the tool to check Website Vulnerability

An example of a vulnerability assessment report generated using our free tool provides valuable insights into potential vulnerabilities.
An example of a vulnerability assessment report generated using our free tool provides valuable insights into potential vulnerabilities.

Related services & how we help next


Recent on the Cyber Rely blog


Move from talk to proof

Get the SBOM/VEX CI Starter Pack and book a Supply Chain Risk Assessment & Remediation Workshop.


Optional JSON-LD

<script type="application/ld+json">
{
  "@context":"https://schema.org",
  "@type":"Article",
  "headline":"7 Proven Software Supply Chain Security Tactics",
  "keywords":"software supply chain security, SBOM, VEX, SLSA, build provenance, CI/CD security",
  "mainEntityOfPage":{"@type":"WebPage","@id":"https://cybersrely.com/software-supply-chain-security-sbom-vex-slsa"}
}
</script>

Free Consultation

If you have any questions or need expert assistance, feel free to schedule a Free consultation with one of our security engineers>>

🔐 Frequently Asked Questions (FAQs)

Find answers to commonly asked questions about Software Supply Chain Security Tactics.

Get a Quote

Leave a Comment

Your email address will not be published. Required fields are marked *

Cyber Rely Logo cyber security
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.