# DevSecOps and the software supply chain

Show which code was checked, by which tool and through which artifact, rather than relying on a green pipeline. SAST, DAST, SCA and SBOM produce different evidence.

Original AI-assisted SecNet educational content. Fictional local examples; not a standards provider publication, endorsement or compliance evidence. Page exercises do not create account progress.

Sources checked: 2026-10-21

## The role of scanning tools

SAST examines possible flows in source or compiled representations, DAST observes a running system, and SCA examines dependency risks. Secret scanning looks for credential patterns. None fully proves business logic. Record tool version, rules, exclusions and actual exit status; a job swallowing failure with || true may be green while its control failed.

## Dependencies and SBOM

Checking direct packages alone misses transitive dependencies. Review lockfile changes and verify package origin, maintainer and license policy. An SBOM is an inventory, not a security approval: without an artifact digest its described release is ambiguous. Assess a CVE match against actual use, reachability and remediation options rather than silently suppressing it.

## Release what was tested

A mutable release tag does not guarantee identical contents. Bind artifact digest, verified provenance, test results and approval to the same content. Do not expose production secrets to untrusted PR jobs; restrict runner identity and privileges per job. This workshop does not modify organizational CI. Run the fixture locally and adapt it through a controlled pipeline change.

## Release record example: one artifact

For a Harbor Desk change, bind source revision, test command and outcomes in one record. When an organizational pipeline builds a package, capture its digest and require matching SBOM and provenance. If the tested package is rebuilt, do not assume old outcomes apply to the new bytes: dependencies or build environments can differ even with identical source. Stop the decision gate when a security job did not run, or use the authorized exception process. The downloadable model demonstrates the effect of evidence fields; it does not verify provenance signatures or generate an SBOM. Obtain those outputs from the actual pipeline.

## Working sequence

1. Choose scans against risks and requirements.
2. Constrain job identity, secrets and runners.
3. Review dependency and lockfile changes.
4. Bind SBOM, tests and approval to the artifact digest.
5. Define rollback and exception expiry.

## 1. Green but unreviewed

A green job may skip scanning or leave findings unreviewed. The model requires execution, same-revision and review evidence; it does not run scanners.

This code is a local policy model; it does not replace target/device testing.

### Weak implementation
```js
s=>s.jobGreen
```

### Corrected implementation
```js
s=>s.jobGreen&&s.scannerRan&&s.revisionMatched&&s.findingsReviewed
```

### Synthetic input and actual model output
```json
{
  "before": [
    {
      "name": "ignored-exit",
      "input": {
        "jobGreen": true,
        "scannerRan": true,
        "revisionMatched": true,
        "findingsReviewed": false
      },
      "expected": false,
      "observed": true,
      "matched": false
    },
    {
      "name": "reviewed-scan",
      "input": {
        "jobGreen": true,
        "scannerRan": true,
        "revisionMatched": true,
        "findingsReviewed": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "skipped-scan",
      "input": {
        "jobGreen": true,
        "scannerRan": false,
        "revisionMatched": true,
        "findingsReviewed": true
      },
      "expected": false,
      "observed": true,
      "matched": false
    }
  ],
  "after": [
    {
      "name": "ignored-exit",
      "input": {
        "jobGreen": true,
        "scannerRan": true,
        "revisionMatched": true,
        "findingsReviewed": false
      },
      "expected": false,
      "observed": false,
      "matched": true
    },
    {
      "name": "reviewed-scan",
      "input": {
        "jobGreen": true,
        "scannerRan": true,
        "revisionMatched": true,
        "findingsReviewed": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "skipped-scan",
      "input": {
        "jobGreen": true,
        "scannerRan": false,
        "revisionMatched": true,
        "findingsReviewed": true
      },
      "expected": false,
      "observed": false,
      "matched": true
    }
  ]
}
```

## 2. An incomplete inventory

Clean direct dependencies do not cover transitive packages. Binding the SBOM to the artifact prevents release decisions based on stale inventories.

This code is a local policy model; it does not replace target/device testing.

### Weak implementation
```js
s=>s.directDependenciesClean
```

### Corrected implementation
```js
s=>s.directDependenciesClean&&s.transitiveReviewed&&s.lockReviewed&&s.sbomBoundToArtifact
```

### Synthetic input and actual model output
```json
{
  "before": [
    {
      "name": "direct-only",
      "input": {
        "directDependenciesClean": true,
        "transitiveReviewed": false,
        "lockReviewed": true,
        "sbomBoundToArtifact": false
      },
      "expected": false,
      "observed": true,
      "matched": false
    },
    {
      "name": "bound-inventory",
      "input": {
        "directDependenciesClean": true,
        "transitiveReviewed": true,
        "lockReviewed": true,
        "sbomBoundToArtifact": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "stale-sbom",
      "input": {
        "directDependenciesClean": true,
        "transitiveReviewed": true,
        "lockReviewed": true,
        "sbomBoundToArtifact": false
      },
      "expected": false,
      "observed": true,
      "matched": false
    }
  ],
  "after": [
    {
      "name": "direct-only",
      "input": {
        "directDependenciesClean": true,
        "transitiveReviewed": false,
        "lockReviewed": true,
        "sbomBoundToArtifact": false
      },
      "expected": false,
      "observed": false,
      "matched": true
    },
    {
      "name": "bound-inventory",
      "input": {
        "directDependenciesClean": true,
        "transitiveReviewed": true,
        "lockReviewed": true,
        "sbomBoundToArtifact": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "stale-sbom",
      "input": {
        "directDependenciesClean": true,
        "transitiveReviewed": true,
        "lockReviewed": true,
        "sbomBoundToArtifact": false
      },
      "expected": false,
      "observed": false,
      "matched": true
    }
  ]
}
```

## 3. A mutable tag

Verify digest and provenance rather than a release label. Tests for another artifact or stale approval are not valid evidence; the model checks these assumptions independently.

This code is a local policy model; it does not replace target/device testing.

### Weak implementation
```js
s=>s.tag==='release'
```

### Corrected implementation
```js
s=>s.digestMatched&&s.provenanceVerified&&s.testsOnSameArtifact&&s.approvalCurrent
```

### Synthetic input and actual model output
```json
{
  "before": [
    {
      "name": "mutable-tag",
      "input": {
        "tag": "release",
        "digestMatched": false,
        "provenanceVerified": false,
        "testsOnSameArtifact": false,
        "approvalCurrent": true
      },
      "expected": false,
      "observed": true,
      "matched": false
    },
    {
      "name": "verified-digest",
      "input": {
        "tag": "release",
        "digestMatched": true,
        "provenanceVerified": true,
        "testsOnSameArtifact": true,
        "approvalCurrent": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "different-tested-artifact",
      "input": {
        "tag": "release",
        "digestMatched": true,
        "provenanceVerified": true,
        "testsOnSameArtifact": false,
        "approvalCurrent": true
      },
      "expected": false,
      "observed": true,
      "matched": false
    }
  ],
  "after": [
    {
      "name": "mutable-tag",
      "input": {
        "tag": "release",
        "digestMatched": false,
        "provenanceVerified": false,
        "testsOnSameArtifact": false,
        "approvalCurrent": true
      },
      "expected": false,
      "observed": false,
      "matched": true
    },
    {
      "name": "verified-digest",
      "input": {
        "tag": "release",
        "digestMatched": true,
        "provenanceVerified": true,
        "testsOnSameArtifact": true,
        "approvalCurrent": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "different-tested-artifact",
      "input": {
        "tag": "release",
        "digestMatched": true,
        "provenanceVerified": true,
        "testsOnSameArtifact": false,
        "approvalCurrent": true
      },
      "expected": false,
      "observed": false,
      "matched": true
    }
  ]
}
```

## Exit evidence

- Tool/rule/version/scope and exit codes
- Digest-bound SBOM, provenance and approval

## References

- [OWASP CI/CD Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/CI_CD_Security_Cheat_Sheet.html)
- [NIST SSDF publications: 1.1 final; 1.2 draft](https://csrc.nist.gov/projects/ssdf/publications)
- [OWASP ASVS 5.0.0](https://asvs.owasp.org/)
