# Supply Chain Review Lens

Review operational infrastructure for **dependency provenance, pinning, and integrity**.

## What to Look For

### Unpinned Dependencies
- Docker: `FROM image:latest` or no tag (implicit latest)
- npm/pip: Missing lockfiles, `*` or `^` versions in production
- GitHub Actions: `uses: org/action@main` instead of SHA
- Gitea Actions: Same pattern, unpinned branch refs
- Helm: `version: "*"` or missing version constraints

### Nix-Specific Pinning
- Missing `flake.lock` (flakes should always have lockfile)
- `fetchurl`/`fetchzip` without SRI hash (`sha256`, `hash`)
- `builtins.fetchGit` without `rev` (floating HEAD)
- `builtins.fetchTarball` without `sha256` (common nixpkgs fetch)
- `fetchFromGitHub` without `hash` attribute
- IFD (import-from-derivation) fetching unpinned sources

### Terraform/Tofu Pinning
- Providers missing `version` constraint in `required_providers`
- Modules sourcing git URLs without `?ref=<sha>` or `?ref=<tag>`
- Missing `.terraform.lock.hcl` (provider checksums)

### Container Provenance
- Base images from untrusted registries
- Missing digest pinning: `image:tag` vs `image@sha256:...`
- Multi-stage builds losing provenance in final stage
- No signature verification (cosign, Notary)

### CI/CD Pipeline Risks
- Actions from unverified publishers
- Workflow injection via `${{ github.event.* }}` in run blocks
- Secrets exposed to untrusted PRs (pull_request_target)
- Missing OIDC for cloud auth (long-lived credentials instead)
- Missing `permissions:` block (GITHUB_TOKEN has write access by default)

### Build-Time Network Access
- Dockerfile `RUN curl/wget` fetching unpinned resources
- `go get` during Docker build without `go.sum` enforcement
- Nix `__noChroot = true` allowing network during build
- npm/pip install without lockfile enforcement in CI

### Binary/Artifact Integrity
- Downloads without checksum verification
- curl/wget piped to sh without hash check
- Missing GPG signature verification
- Unsigned packages from PPAs/third-party repos

### Substituters & Registries
- Nix: Untrusted substituters without signature verification
- Docker: Pulling from HTTP registries
- npm/pip: Private registries without auth/TLS
- Missing dependency confusion protections (scoped packages)

## Output Format

```
[SUPPLY-CHAIN] <severity:HIGH|MED|LOW> <file:line>
Issue: <what's unpinned or unverified>
Risk: <what could be injected or changed>
Suggest: <pin to SHA/hash, add verification>
Evidence: <unpinned reference found>
```

## Common Fixes

```dockerfile
# Docker: Pin to digest
FROM node:20@sha256:abc123...

# Or at minimum, pin major version
FROM node:20.10.0-alpine
```

```yaml
# GitHub Actions: Pin to SHA
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29  # v4.1.6
```

```nix
# Nix: Always include hash
fetchFromGitHub {
  owner = "...";
  repo = "...";
  rev = "v1.2.3";  # or full SHA
  hash = "sha256-...";
}
```

```bash
# Downloads: Verify before executing
curl -fsSL https://example.com/install.sh -o install.sh
echo "expected_sha256  install.sh" | sha256sum -c -
bash install.sh
```

## False Positive Risks

- Development/CI images where latest is intentional for testing
- Internal trusted registries with controlled update policies
- Nix flakes auto-update workflows with proper review

## Guidelines

- **HIGH** = unpinned production deps, unverified downloads, curl|sh
- **MED** = unpinned CI actions, missing lockfiles, unverified registries
- **LOW** = dev dependencies unpinned, internal tooling
- Every external dependency is a trust decision
- Prefer: SHA > tag > branch > latest
- Nix flakes with `flake.lock` are good; verify lock is committed
