Tạo SBOM bằng Syft — CycloneDX & SPDX
🎯 Mục tiêu: Tạo SBOM đầy đủ cho container image và filesystem bằng Syft, xuất định dạng CycloneDX (JSON) và SPDX, phân tích nội dung SBOM.
🧰 Công cụ / nền tảng: Syft CLI, Docker, Python 3.
📦 Chuẩn bị:
# Cài Syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh \
| sh -s -- -b /usr/local/bin
syft version
▶️ Các bước (CLI):
# 1. Tạo Python app nhỏ để tạo image
mkdir sbom-lab && cd sbom-lab
cat > requirements.txt << 'EOF'
flask==3.0.3
requests==2.31.0
gunicorn==21.2.0
EOF
cat > app.py << 'EOF'
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return {"status": "ok", "version": "1.0.0"}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
EOF
cat > Dockerfile << 'EOF'
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "app:app"]
EOF
# 2. Build image
docker build -t sbom-demo:1.0.0 .
# 3. Tạo SBOM từ image — format CycloneDX JSON
syft sbom-demo:1.0.0 -o cyclonedx-json=sbom-cyclonedx.json
echo "CycloneDX SBOM components:"
python3 -c "
import json
with open('sbom-cyclonedx.json') as f:
sbom = json.load(f)
components = sbom.get('components', [])
print(f' Total components: {len(components)}')
# Tìm Python packages
pkgs = [c for c in components if c.get('type') == 'library']
print(f' Libraries: {len(pkgs)}')
for p in sorted(pkgs, key=lambda x: x.get('name',''))[:10]:
print(f' {p.get(\"name\",\"?\")} {p.get(\"version\",\"?\")}')
"
# 4. Tạo SBOM format SPDX JSON
syft sbom-demo:1.0.0 -o spdx-json=sbom-spdx.json
echo ""
echo "SPDX SBOM packages:"
python3 -c "
import json
with open('sbom-spdx.json') as f:
sbom = json.load(f)
pkgs = sbom.get('packages', [])
print(f' Total packages: {len(pkgs)}')
for p in pkgs[:8]:
print(f' {p.get(\"name\",\"?\")} {p.get(\"versionInfo\",\"?\")} [{p.get(\"licenseConcluded\",\"?\")}]')
"
# 5. Scan SBOM filesystem (không cần image — air-gap friendly)
syft dir:. -o cyclonedx-json=sbom-fs.json
echo "Filesystem SBOM: $(python3 -c \"import json; d=json.load(open('sbom-fs.json')); print(len(d.get('components',[])), 'components')\")"
# 6. Attach SBOM vào image như OCI attestation
# (yêu cầu Cosign — xem LAB-128)
syft sbom-demo:1.0.0 -o syft-json=sbom-syft.json
echo "Syft JSON SBOM: $(wc -l < sbom-syft.json) lines"
# 7. In license summary từ CycloneDX
python3 << 'PYEOF'
import json
from collections import Counter
with open('sbom-cyclonedx.json') as f:
sbom = json.load(f)
licenses = []
for comp in sbom.get('components', []):
for lic in comp.get('licenses', []):
if 'license' in lic:
licenses.append(lic['license'].get('id', lic['license'].get('name', 'Unknown')))
counts = Counter(licenses)
print("\nLicense summary:")
for lic, count in counts.most_common(10):
print(f" {lic}: {count}")
PYEOF
✅ Kết quả mong đợi: 3 file SBOM tạo thành công: sbom-cyclonedx.json, sbom-spdx.json, sbom-syft.json. CycloneDX report ≥ 50 components (Python packages + OS packages). License summary hiển thị MIT, BSD, Apache 2.0 chiếm đa số. SPDX có field spdxVersion, packages array.
🧹 Cleanup: docker rmi sbom-demo:1.0.0; cd .. && rm -rf sbom-lab.