# Syft - SBOM Generation

Comprehensive guide to Software Bill of Materials (SBOM) generation with Syft for container images, filesystems, and archives.

## Overview

**Container**: `anchore/syft:latest`
**Category**: Supply Chain Security
**Port**: N/A (CLI tool)

Syft is a CLI tool and Go library for generating a Software Bill of Materials (SBOM) from container images and filesystems. It provides detailed package information for vulnerability assessment and compliance.

## Quick Start

```bash
# Generate SBOM from container image
docker exec syft syft alpine:latest

# Generate SBOM from local directory
docker exec syft syft dir:/code

# Output in specific format
docker exec syft syft alpine:latest -o spdx-json > sbom.json

# Scan Docker archive
docker exec syft syft docker-archive:/images/app.tar
```

## Supported Sources

| Source | Syntax | Example |
|--------|--------|---------|
| Container Image | `image:tag` | `syft nginx:latest` |
| Docker Archive | `docker-archive:path` | `syft docker-archive:image.tar` |
| OCI Archive | `oci-archive:path` | `syft oci-archive:image.tar` |
| OCI Directory | `oci-dir:path` | `syft oci-dir:/oci-layout` |
| Podman | `podman:tag` | `syft podman:localhost/app:latest` |
| Directory | `dir:path` | `syft dir:/code` |
| File | `file:path` | `syft file:package.json` |
| Registry | `registry:image` | `syft registry:ghcr.io/app:latest` |

## Output Formats

```bash
# SPDX JSON (ISO standard)
docker exec syft syft alpine:latest -o spdx-json

# SPDX Tag-Value
docker exec syft syft alpine:latest -o spdx-tag-value

# CycloneDX JSON
docker exec syft syft alpine:latest -o cyclonedx-json

# CycloneDX XML
docker exec syft syft alpine:latest -o cyclonedx-xml

# Syft native JSON (most detailed)
docker exec syft syft alpine:latest -o json

# Table (human readable)
docker exec syft syft alpine:latest -o table

# Text
docker exec syft syft alpine:latest -o text

# GitHub dependency format
docker exec syft syft alpine:latest -o github
```

## SBOM Formats Comparison

| Format | Standard | Use Case |
|--------|----------|----------|
| SPDX JSON | ISO/IEC 5962:2021 | Legal/compliance, license tracking |
| CycloneDX | OWASP | Vulnerability management, security |
| Syft JSON | Anchore | Detailed analysis, Grype integration |
| GitHub | GitHub | Dependency graph integration |

## Package Catalogers

Syft detects packages from multiple ecosystems:

| Ecosystem | Detected Packages |
|-----------|-------------------|
| Alpine | apk packages |
| Debian/Ubuntu | dpkg packages |
| Red Hat | rpm packages |
| Python | pip, poetry, pipenv |
| JavaScript | npm, yarn |
| Java | Maven, Gradle, JAR |
| Go | go.mod, go.sum |
| Rust | Cargo.toml, Cargo.lock |
| Ruby | Gemfile, gemspec |
| .NET | NuGet |
| PHP | Composer |
| Swift | Swift Package Manager |
| Haskell | Cabal, Stack |
| R | CRAN packages |

## CLI Commands

```bash
# Basic scan with output file
docker exec syft syft nginx:latest -o spdx-json > nginx-sbom.json

# Scan with multiple outputs
docker exec syft syft nginx:latest -o json=sbom.json -o table

# Scan specific scope (all layers vs squashed)
docker exec syft syft nginx:latest --scope all-layers
docker exec syft syft nginx:latest --scope squashed

# Exclude specific catalogers
docker exec syft syft dir:/code --exclude-catalogers python

# Platform selection for multi-arch images
docker exec syft syft --platform linux/arm64 nginx:latest

# Quiet output (only SBOM)
docker exec syft syft nginx:latest -q -o json

# Verbose output
docker exec syft syft nginx:latest -v

# Scan with attestation
docker exec syft syft nginx:latest -o spdx-json --attest
```

## Configuration File

```yaml
# .syft.yaml
output:
  - "spdx-json"
  - "table"
scope: "squashed"
quiet: false
file: ""
check-for-app-update: false
anchore:
  host: ""
  path: ""
  username: ""
  password: ""
log:
  level: "info"
  file: ""
catalogers:
  exclude: []
package:
  name:
    exclude: []
file:
  metadata:
    selection: "owned-by-package"
    digests:
      - "sha256"
```

## SBOM Analysis

### Count Packages

```bash
# Count total packages
docker exec syft syft nginx:latest -o json | jq '.artifacts | length'

# List packages by type
docker exec syft syft nginx:latest -o json | jq '.artifacts | group_by(.type) | map({type: .[0].type, count: length})'

# List all package names
docker exec syft syft nginx:latest -o json | jq '.artifacts[].name'
```

### Extract License Information

```bash
# Get all licenses
docker exec syft syft nginx:latest -o json | jq '.artifacts[].licenses[]' | sort -u

# Packages with specific license
docker exec syft syft nginx:latest -o json | jq '.artifacts[] | select(.licenses[] | contains("GPL"))'
```

### Compare SBOMs

```bash
# Generate SBOMs for comparison
docker exec syft syft app:v1 -o json > sbom-v1.json
docker exec syft syft app:v2 -o json > sbom-v2.json

# Compare package lists
diff <(jq -r '.artifacts[].name' sbom-v1.json | sort) \
     <(jq -r '.artifacts[].name' sbom-v2.json | sort)
```

## Integration with Grype

Syft SBOMs can be used directly with Grype for vulnerability scanning:

```bash
# Generate SBOM
docker exec syft syft nginx:latest -o json > sbom.json

# Scan SBOM for vulnerabilities
docker exec grype grype sbom:sbom.json

# Single command
docker exec syft syft nginx:latest -o json | docker exec -i grype grype
```

## CI/CD Integration

### GitHub Actions

```yaml
name: SBOM Generation
on: [push]

jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Generate SBOM
        uses: anchore/sbom-action@v0
        with:
          image: myapp:${{ github.sha }}
          format: spdx-json
          output-file: sbom.spdx.json

      - name: Upload SBOM
        uses: actions/upload-artifact@v3
        with:
          name: sbom
          path: sbom.spdx.json

      - name: Scan for vulnerabilities
        uses: anchore/scan-action@v3
        with:
          sbom: sbom.spdx.json
          fail-build: true
          severity-cutoff: high
```

### Pipeline Script

```bash
#!/bin/bash
# generate-sbom.sh

IMAGE=$1
OUTPUT_DIR=${2:-./sboms}

mkdir -p $OUTPUT_DIR

# Generate multiple SBOM formats
docker exec syft syft $IMAGE -o spdx-json > "$OUTPUT_DIR/sbom.spdx.json"
docker exec syft syft $IMAGE -o cyclonedx-json > "$OUTPUT_DIR/sbom.cdx.json"
docker exec syft syft $IMAGE -o json > "$OUTPUT_DIR/sbom.syft.json"

# Generate summary
echo "=== SBOM Summary for $IMAGE ===" > "$OUTPUT_DIR/summary.txt"
echo "Total packages: $(jq '.artifacts | length' $OUTPUT_DIR/sbom.syft.json)" >> "$OUTPUT_DIR/summary.txt"
echo "" >> "$OUTPUT_DIR/summary.txt"
echo "Package types:" >> "$OUTPUT_DIR/summary.txt"
jq -r '.artifacts | group_by(.type) | map("\(.[0].type): \(length)")[]' "$OUTPUT_DIR/sbom.syft.json" >> "$OUTPUT_DIR/summary.txt"

cat "$OUTPUT_DIR/summary.txt"
```

## Attestation & Signing

### Generate Attested SBOM

```bash
# Generate signed attestation
docker exec syft syft nginx:latest -o spdx-json --attest --key cosign.key

# Verify attestation
cosign verify-attestation --key cosign.pub nginx:latest
```

### Cosign Integration

```bash
# Sign SBOM with cosign
cosign sign-blob --key cosign.key sbom.json > sbom.sig

# Verify signature
cosign verify-blob --key cosign.pub --signature sbom.sig sbom.json
```

## Registry Scanning

```bash
# Scan from registry (with auth)
docker exec syft syft registry:ghcr.io/org/app:latest \
  --from registry \
  -o spdx-json

# With credentials
SYFT_REGISTRY_AUTH_USERNAME=user \
SYFT_REGISTRY_AUTH_PASSWORD=pass \
docker exec syft syft registry:private.registry.io/app:latest
```

## Quality Gates

### Package Policy Enforcement

```bash
#!/bin/bash
# check-sbom-policy.sh

SBOM=$1

# Check for banned packages
BANNED_PACKAGES=("lodash@4.17.20" "log4j@2.14.1")

for pkg in "${BANNED_PACKAGES[@]}"; do
    if jq -e ".artifacts[] | select(.name + \"@\" + .version == \"$pkg\")" $SBOM > /dev/null; then
        echo "FAIL: Banned package found: $pkg"
        exit 1
    fi
done

# Check for minimum package versions
MIN_OPENSSL="1.1.1"
OPENSSL_VERSION=$(jq -r '.artifacts[] | select(.name == "openssl") | .version' $SBOM)
if [ -n "$OPENSSL_VERSION" ]; then
    if [ "$(printf '%s\n' "$MIN_OPENSSL" "$OPENSSL_VERSION" | sort -V | head -n1)" != "$MIN_OPENSSL" ]; then
        echo "FAIL: OpenSSL version $OPENSSL_VERSION below minimum $MIN_OPENSSL"
        exit 1
    fi
fi

echo "PASS: SBOM policy checks passed"
```

### License Compliance

```bash
#!/bin/bash
# check-licenses.sh

SBOM=$1
ALLOWED_LICENSES=("MIT" "Apache-2.0" "BSD-3-Clause" "ISC")

# Extract all licenses
ALL_LICENSES=$(jq -r '.artifacts[].licenses[]' $SBOM | sort -u)

for license in $ALL_LICENSES; do
    ALLOWED=false
    for allowed in "${ALLOWED_LICENSES[@]}"; do
        if [[ "$license" == *"$allowed"* ]]; then
            ALLOWED=true
            break
        fi
    done
    if [ "$ALLOWED" = false ]; then
        echo "WARNING: License '$license' not in allowed list"
    fi
done
```

## Integration with Stack

- Generate SBOMs for all container images
- Feed SBOMs to Grype for vulnerability scanning
- Import SBOM data into DefectDojo
- Track dependencies alongside Trivy scans
- Compliance reporting for supply chain security
- License auditing and tracking

## Best Practices

1. **Generate at Build** - Create SBOM during CI/CD build
2. **Multiple Formats** - Generate both SPDX and CycloneDX
3. **Sign SBOMs** - Use cosign for attestation
4. **Store SBOMs** - Archive with build artifacts
5. **Version Tracking** - Include in release artifacts
6. **Regular Scanning** - Regenerate for deployed images
7. **Policy Enforcement** - Check against banned packages
8. **License Compliance** - Audit licenses regularly
9. **Dependency Tracking** - Compare SBOMs across versions
10. **Integration** - Connect to vulnerability management

## Troubleshooting

### Common Issues

**Issue**: Missing packages in SBOM
```bash
# Use all-layers scope
docker exec syft syft nginx:latest --scope all-layers

# Check cataloger coverage
docker exec syft syft nginx:latest -v 2>&1 | grep "cataloger"
```

**Issue**: Slow scanning
```bash
# Use squashed scope for faster scans
docker exec syft syft nginx:latest --scope squashed

# Exclude unnecessary catalogers
docker exec syft syft dir:/code --exclude-catalogers ruby,haskell
```

**Issue**: Registry authentication
```bash
# Set credentials
export SYFT_REGISTRY_AUTH_USERNAME=user
export SYFT_REGISTRY_AUTH_PASSWORD=pass
docker exec -e SYFT_REGISTRY_AUTH_USERNAME -e SYFT_REGISTRY_AUTH_PASSWORD syft syft registry:private/image
```
