Go Container Security Scanning: 6 Key Practices for Trivy SBOM Supply Chain Security
When Supply Chain Attacks Meet Image Vulnerabilities: Container Security's Darkest Hour
3 AM, alerts are screaming. Your production container is running a crypto miner, CPU at 99%. Investigation reveals: the base image golang:1.22-alpine contains an unpatched CVE-2024-1234, attackers injected malicious code through transitive dependencies, and your CI/CD has zero security gates. Worse — you have no idea what's actually inside your image. No SBOM, no signature verification, and compliance audits draw a blank.
This isn't hypothetical. The XZ Utils backdoor, Log4Shell, and the SolarWinds supply chain attack all prove that container security isn't optional — it's a survival baseline. You need vulnerability scanning to find risks, SBOM to make dependencies transparent, image signing for trust, and CI/CD gates to block threats. This article covers 6 key practices to build your Go container supply chain security defense.
Core Concepts Reference
| Concept | Core Idea | Key Tool | Typical Scenario |
|---|---|---|---|
| Container Security | Full lifecycle protection from build to runtime | Trivy, Clair | Image vulnerability scanning, runtime protection |
| SBOM (Software Bill of Materials) | List all software components and dependencies | syft, trivy sbom |
Dependency transparency, compliance audit |
| CVE (Common Vulnerabilities and Exposures) | Standardized vulnerability identification and rating | NVD, OSV | Vulnerability tracking and fix prioritization |
| Trivy | All-in-one container security scanner | trivy |
Image/filesystem/Git repo scanning |
| Cosign | Container image signing and verification | cosign |
Image provenance trust verification |
| SLSA (Supply-chain Levels for Software Artifacts) | Software supply chain integrity framework | SLSA spec | Build process trust assurance |
| Sigstore | Free open-source code signing platform | cosign, rekor |
Image/artifact signing and auditing |
| Image Signing | Sign image digest with key pair | cosign sign |
Prevent image tampering |
| Vulnerability Severity | CRITICAL > HIGH > MEDIUM > LOW | CVSS score | Fix prioritization decisions |
| CIS Benchmark | Container/orchestration security configuration standards | trivy misconfig |
Docker/K8s security hardening |
Table of Contents
- Problem Analysis: 5 Container Security Challenges
- Practice 1: Trivy Image Vulnerability Scanning Integration
- Practice 2: SBOM Generation and Verification
- Practice 3: Cosign Image Signing and Verification
- Practice 4: CI/CD Security Gate Configuration
- Practice 5: Go Module Dependency Auditing
- Practice 6: Runtime Security Monitoring
- 5 Common Pitfalls
- 10 Error Troubleshooting
- Advanced Optimization Tips
- Scanner Comparison
- Recommended Online Tools
- Summary and Outlook
Problem Analysis: 5 Container Security Challenges
Challenge 1: Lagging Image Vulnerability Fixes — The Go base image golang:1.22 contains hundreds of system packages. CVEs take an average of 97 days from disclosure to fix. Your images may run with known vulnerabilities for months.
Challenge 2: Transitive Dependency Risks — Indirect dependencies pulled by go mod tidy may contain malicious code. The XZ Utils incident proved that even widely-used libraries can be backdoored, and go.sum only verifies integrity, not security.
Challenge 3: Missing Image Signature Verification — Without signature verification, anyone can push tampered images to your registry. K8s doesn't verify image provenance by default — it runs whatever it pulls.
Challenge 4: SBOM Generation and Management — Without an SBOM, you can't answer "what's in this image?" Compliance audits require SBOMs, but most teams have never generated one.
Challenge 5: Compliance Audit Automation — SOC2 and similar frameworks require continuous security scanning records. Manual audits are inefficient and prone to gaps.
Practice 1: Trivy Image Vulnerability Scanning Integration
Installation and Basic Scanning
# Install Trivy
brew install trivy
# Or use Docker
docker run aquasec/trivy:latest version
Scan Go Container Images
# Scan local image
trivy image myapp:latest
# Show only CRITICAL and HIGH severity
trivy image --severity CRITICAL,HIGH myapp:latest
# Output JSON format for integration
trivy image --format json --output result.json myapp:latest
# Scan specific architecture
trivy image --platform linux/amd64 myapp:latest
Go Project Integration
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
)
type TrivyReport struct {
Results []TrivyResult `json:"Results"`
}
type TrivyResult struct {
Target string `json:"Target"`
Type string `json:"Type"`
Vulnerabilities []Vuln `json:"Vulnerabilities"`
}
type Vuln struct {
VulnerabilityID string `json:"VulnerabilityID"`
Severity string `json:"Severity"`
PkgName string `json:"PkgName"`
InstalledVersion string `json:"InstalledVersion"`
FixedVersion string `json:"FixedVersion"`
Title string `json:"Title"`
}
func scanImage(imageRef string) (*TrivyReport, error) {
cmd := exec.Command("trivy", "image",
"--format", "json",
"--severity", "CRITICAL,HIGH",
imageRef,
)
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("trivy scan failed: %w", err)
}
var report TrivyReport
if err := json.Unmarshal(output, &report); err != nil {
return nil, fmt.Errorf("parse report failed: %w", err)
}
return &report, nil
}
func main() {
imageRef := os.Getenv("SCAN_IMAGE")
if imageRef == "" {
imageRef = "myapp:latest"
}
report, err := scanImage(imageRef)
if err != nil {
fmt.Fprintf(os.Stderr, "scan error: %v\n", err)
os.Exit(1)
}
criticalCount := 0
for _, result := range report.Results {
for _, v := range result.Vulnerabilities {
if v.Severity == "CRITICAL" {
criticalCount++
fmt.Printf("[CRITICAL] %s: %s (%s -> %s)\n",
v.VulnerabilityID, v.Title,
v.InstalledVersion, v.FixedVersion)
}
}
}
if criticalCount > 0 {
fmt.Printf("\nFound %d CRITICAL vulnerabilities. Blocking deploy.\n", criticalCount)
os.Exit(1)
}
fmt.Println("No CRITICAL vulnerabilities found. Safe to deploy.")
}
Trivy Configuration File
# trivy.yaml
severity:
- CRITICAL
- HIGH
skipDirs:
- /tmp
- /var/cache
ignorefile: .trivyignore
db:
skipUpdate: false
vulnerability:
type:
- os
- library
misconfiguration:
type:
- dockerfile
- kubernetes
Practice 2: SBOM Generation and Verification
Generate SBOM with Trivy
# Generate SPDX format SBOM
trivy image --format spdx-json --output sbom.spdx.json myapp:latest
# Generate CycloneDX format SBOM
trivy image --format cyclonedx --output sbom.cyclonedx.json myapp:latest
Generate SBOM with Syft
# Install Syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Generate SBOM
syft myapp:latest -o spdx-json > sbom.spdx.json
syft myapp:latest -o cyclonedx-json > sbom.cyclonedx.json
Programmatic SBOM Management in Go
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"time"
)
type SBOMMetadata struct {
ImageRef string `json:"imageRef"`
GeneratedAt time.Time `json:"generatedAt"`
Tool string `json:"tool"`
Format string `json:"format"`
ComponentCount int `json:"componentCount"`
}
func generateSBOM(imageRef, format, outputPath string) error {
args := []string{"image", "--format", format, "--output", outputPath, imageRef}
cmd := exec.Command("trivy", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func validateSBOM(sbomPath string) error {
data, err := os.ReadFile(sbomPath)
if err != nil {
return fmt.Errorf("read sbom failed: %w", err)
}
if !json.Valid(data) {
return fmt.Errorf("invalid SBOM JSON format")
}
var sbom map[string]interface{}
if err := json.Unmarshal(data, &sbom); err != nil {
return fmt.Errorf("parse SBOM failed: %w", err)
}
if _, ok := sbom["components"]; !ok {
if _, ok2 := sbom["packages"]; !ok2 {
return fmt.Errorf("SBOM missing components/packages field")
}
}
fmt.Printf("SBOM validation passed: %s\n", sbomPath)
return nil
}
func main() {
imageRef := "myapp:latest"
sbomPath := fmt.Sprintf("sbom-%s.json", time.Now().Format("20060102"))
fmt.Printf("Generating SBOM for %s...\n", imageRef)
if err := generateSBOM(imageRef, "cyclonedx", sbomPath); err != nil {
fmt.Fprintf(os.Stderr, "generate SBOM failed: %v\n", err)
os.Exit(1)
}
if err := validateSBOM(sbomPath); err != nil {
fmt.Fprintf(os.Stderr, "validate SBOM failed: %v\n", err)
os.Exit(1)
}
meta := SBOMMetadata{
ImageRef: imageRef,
GeneratedAt: time.Now(),
Tool: "trivy",
Format: "cyclonedx",
ComponentCount: 0,
}
metaData, _ := json.MarshalIndent(meta, "", " ")
fmt.Println(string(metaData))
}
Practice 3: Cosign Image Signing and Verification
Key Pair Generation and Signing
# Generate key pair
cosign generate-key-pair
# Sign image (using private key)
cosign sign --key cosign.key myregistry/myapp:latest
# Verify signature (using public key)
cosign verify --key cosign.pub myregistry/myapp:latest
# Keyless signing (Sigstore)
cosign sign myregistry/myapp:latest
# Keyless verification
cosign verify myregistry/myapp:latest
Go Image Verification Integration
package main
import (
"fmt"
"os"
"os/exec"
"strings"
)
func verifyImageSignature(imageRef, publicKey string) error {
args := []string{"verify", "--key", publicKey, imageRef}
cmd := exec.Command("cosign", args...)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("signature verification failed: %s", string(output))
}
if !strings.Contains(string(output), "Verified OK") {
return fmt.Errorf("signature not verified for %s", imageRef)
}
fmt.Printf("Image verified: %s\n", imageRef)
return nil
}
func signImage(imageRef, keyPath string) error {
args := []string{"sign", "--key", keyPath, imageRef}
cmd := exec.Command("cosign", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func main() {
action := os.Args[1]
imageRef := os.Args[2]
switch action {
case "sign":
if err := signImage(imageRef, "cosign.key"); err != nil {
fmt.Fprintf(os.Stderr, "sign failed: %v\n", err)
os.Exit(1)
}
case "verify":
if err := verifyImageSignature(imageRef, "cosign.pub"); err != nil {
fmt.Fprintf(os.Stderr, "verify failed: %v\n", err)
os.Exit(1)
}
default:
fmt.Fprintf(os.Stderr, "unknown action: %s\n", action)
os.Exit(1)
}
}
K8s Admission Controller Verification Policy
apiVersion: policies.kubewarden.io/v1
kind: ClusterAdmissionPolicy
metadata:
name: verify-image-signatures
spec:
module: registry://ghcr.io/kubewarden/policies/verify-image-signatures:v0.2.5
rules:
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
operations: ["CREATE", "UPDATE"]
settings:
signatures:
- image: "myregistry.io/*"
pubKeys:
- |
-----BEGIN PUBLIC KEY-----
YOUR_PUBLIC_KEY_HERE
-----END PUBLIC KEY-----
Practice 4: CI/CD Security Gate Configuration
GitHub Actions Security Pipeline
name: Security Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy Vulnerability Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: "myapp:${{ github.sha }}"
format: "sarif"
output: "trivy-results.sarif"
severity: "CRITICAL,HIGH"
exit-code: "1"
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: "myapp:${{ github.sha }}"
format: cyclonedx-json
output-file: sbom.json
- name: Sign Image
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY myapp:${{ github.sha }}
env:
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
- name: Go Module Audit
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
- name: Upload Trivy Results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
GitLab CI Security Gate
stages:
- build
- scan
- sign
- deploy
build-image:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
trivy-scan:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- trivy image --format cyclonedx --output sbom.json $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
artifacts:
paths:
- sbom.json
cosign-verify:
stage: sign
script:
- cosign sign --key cosign.key $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main
deploy:
stage: deploy
script:
- cosign verify --key cosign.pub $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- kubectl set image deployment/myapp myapp=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main
Practice 5: Go Module Dependency Auditing
govulncheck Deep Scanning
# Install govulncheck
go install golang.org/x/vuln/cmd/govulncheck@latest
# Scan current project
govulncheck ./...
# Output JSON format
govulncheck -json ./...
# Show only called vulnerabilities (more precise)
govulncheck -mode binary ./...
Go Dependency Audit Program
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
)
type VulnEntry struct {
OSV string `json:"osv"`
Module string `json:"module"`
Version string `json:"version"`
Package string `json:"package"`
Call string `json:"call"`
Severity string `json:"severity"`
}
func auditGoModules(projectPath string) ([]VulnEntry, error) {
cmd := exec.Command("govulncheck", "-json", "./...")
cmd.Dir = projectPath
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("govulncheck failed: %w", err)
}
var vulns []VulnEntry
decoder := json.NewDecoder(strings.NewReader(string(output)))
for decoder.More() {
var entry map[string]interface{}
if err := decoder.Decode(&entry); err != nil {
continue
}
if vulnData, ok := entry["vulnerability"]; ok {
v := VulnEntry{}
if m, ok := vulnData.(map[string]interface{}); ok {
if id, ok := m["id"].(string); ok {
v.OSV = id
}
if mod, ok := m["module"].(string); ok {
v.Module = mod
}
}
vulns = append(vulns, v)
}
}
return vulns, nil
}
func checkGoModTidy(projectPath string) error {
cmd := exec.Command("go", "mod", "tidy", "-diff")
cmd.Dir = projectPath
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("go.mod needs tidy: %s", string(output))
}
return nil
}
func main() {
projectPath := "."
if len(os.Args) > 1 {
projectPath = os.Args[1]
}
fmt.Println("Checking go.mod tidiness...")
if err := checkGoModTidy(projectPath); err != nil {
fmt.Fprintf(os.Stderr, "[WARN] %v\n", err)
}
fmt.Println("Running govulncheck...")
vulns, err := auditGoModules(projectPath)
if err != nil {
fmt.Fprintf(os.Stderr, "audit failed: %v\n", err)
os.Exit(1)
}
if len(vulns) > 0 {
fmt.Printf("Found %d vulnerabilities:\n", len(vulns))
for _, v := range vulns {
fmt.Printf(" - %s: %s@%s\n", v.OSV, v.Module, v.Version)
}
os.Exit(1)
}
fmt.Println("No known vulnerabilities found.")
}
go.mod Security Configuration
// go.mod
module github.com/myorg/myapp
go 1.22.0
require (
github.com/gin-gonic/gin v1.9.1
github.com/redis/go-redis/v9 v9.5.1
)
// Exclude known vulnerable versions
exclude (
github.com/example/vulnerable v1.0.0
)
// Specify safe version replacement
replace github.com/example/vulnerable => github.com/example/vulnerable v1.0.1
Practice 6: Runtime Security Monitoring
Falco Runtime Detection Rules
# falco-rules/go-container.yaml
- rule: Unexpected Process in Go Container
desc: Detect unexpected processes running in Go containers
condition: >
container and container.image contains "myapp" and
proc.name not in (myapp, sh, cat, ls)
output: >
Unexpected process in Go container
(user=%user.name container=%container.name
image=%container.image.proc=%proc.name)
priority: WARNING
tags: [container, go, runtime]
- rule: Crypto Mining Detected
desc: Detect crypto mining activity in containers
condition: >
container and
(proc.name in (xmrig, minerd, cpuminer) or
proc.cmdline contains "stratum+tcp")
output: >
Crypto mining detected
(user=%user.name container=%container.name
image=%container.image.proc=%proc.name)
priority: CRITICAL
tags: [crypto, container, runtime]
- rule: Container Drift Detected
desc: Detect new files created in running container
condition: >
container and evt.type = openat and
evt.arg.flags contains O_CREAT and
not fd.name startswith /tmp and
not fd.name startswith /var/log
output: >
Container drift detected
(user=%user.name container=%container.name
file=%fd.name)
priority: WARNING
tags: [drift, container, runtime]
Go Runtime Security Checks
package main
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"time"
)
type SecurityCheck struct {
Name string
Status string
Detail string
}
func checkRunningAsRoot() SecurityCheck {
if os.Getuid() == 0 {
return SecurityCheck{
Name: "Root User Check",
Status: "FAIL",
Detail: "Container running as root",
}
}
return SecurityCheck{
Name: "Root User Check",
Status: "PASS",
Detail: fmt.Sprintf("Running as UID %d", os.Getuid()),
}
}
func checkReadOnlyFilesystem() SecurityCheck {
if err := os.WriteFile("/tmp/.security-test", []byte("test"), 0644); err != nil {
return SecurityCheck{
Name: "Read-Only FS Check",
Status: "PASS",
Detail: "Filesystem is read-only",
}
}
os.Remove("/tmp/.security-test")
return SecurityCheck{
Name: "Read-Only FS Check",
Status: "WARN",
Detail: "Filesystem is writable",
}
}
func checkDistroless() SecurityCheck {
_, err := exec.LookPath("sh")
if err != nil {
return SecurityCheck{
Name: "Distroless Check",
Status: "PASS",
Detail: "No shell available (distroless)",
}
}
return SecurityCheck{
Name: "Distroless Check",
Status: "WARN",
Detail: "Shell available in image",
}
}
func runSecurityAudit() []SecurityCheck {
return []SecurityCheck{
checkRunningAsRoot(),
checkReadOnlyFilesystem(),
checkDistroless(),
}
}
func main() {
fmt.Printf("Runtime Security Audit - %s\n", time.Now().Format(time.RFC3339))
fmt.Printf("Platform: %s/%s\n", runtime.GOOS, runtime.GOARCH)
fmt.Println(strings.Repeat("-", 60))
checks := runSecurityAudit()
failCount := 0
for _, c := range checks {
status := c.Status
if status == "FAIL" {
failCount++
}
fmt.Printf("[%s] %s: %s\n", status, c.Name, c.Detail)
}
fmt.Println(strings.Repeat("-", 60))
if failCount > 0 {
fmt.Printf("Audit FAILED: %d checks failed\n", failCount)
os.Exit(1)
}
fmt.Println("Audit PASSED")
}
Hardened Dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /myapp .
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /myapp /myapp
USER nonroot:nonroot
HEALTHCHECK --interval=30s --timeout=3s \
CMD ["/myapp", "healthcheck"]
ENTRYPOINT ["/myapp"]
5 Common Pitfalls
| # | Pitfall | Wrong Approach | Right Approach |
|---|---|---|---|
| 1 | ❌ Scan but don't block | Archive scan results, CI/CD continues deploying | ✅ Use --exit-code 1 to block deployments with CRITICAL vulnerabilities |
| 2 | ❌ Ignore indirect dependencies | Only check go.mod direct dependencies |
✅ Use govulncheck ./... to scan the full dependency tree |
| 3 | ❌ Use latest tag | FROM golang:latest is not reproducible |
✅ Pin versions: FROM golang:1.22.3-alpine3.19 |
| 4 | ❌ Run containers as root | Dockerfile defaults to root user | ✅ USER nonroot:nonroot + distroless base image |
| 5 | ❌ Separate signing from deployment | Sign but don't verify before deploying | ✅ K8s Admission Controller enforces signature verification |
10 Error Troubleshooting
| # | Error Message | Cause | Solution |
|---|---|---|---|
| 1 | FATAL: error in DB download |
Trivy vulnerability DB download failed | Run trivy image --download-db-only then retry; configure HTTP_PROXY |
| 2 | unsupported format: cyclonedx |
Trivy version too old | Upgrade to v0.40+: brew upgrade trivy |
| 3 | cosign: signing failed: KEY_REF |
Key environment variable not set | export COSIGN_PRIVATE_KEY=... or use --key cosign.key |
| 4 | verification failed: no signatures |
Image not signed | Sign first with cosign sign then verify |
| 5 | govulncheck: module not found |
Go module cache corrupted | go clean -modcache && go mod download |
| 6 | trivy: permission denied |
Docker socket permission denied | sudo usermod -aG docker $USER or use rootless mode |
| 7 | SBOM: empty components |
Scanned builder stage instead of final image | Scan the final image, not the builder stage |
| 8 | cosign verify: REKOR error |
Rekor transparency log unreachable | Check network; or use --insecure-ignore-tlog (test only) |
| 9 | trivy: image not found locally |
Image doesn't exist locally | docker pull first or specify remote registry |
| 10 | Falco: rule syntax error |
YAML indentation or field name error | Validate with falco -V |
Advanced Optimization Tips
1. Trivy Operator for K8s Cluster-Wide Auto-Scanning — Deploy Trivy Operator to your K8s cluster to automatically scan all Pod images and generate VulnerabilityReports, combined with Prometheus alerts for continuous monitoring.
2. SBOM Attestation Attached to Images — Use cosign attest to attach SBOMs as attachments to image manifests, ensuring SBOMs travel with images and never get lost.
3. VEX Documents to Suppress False Positives — Generate VEX (Vulnerability Exploitability eXchange) documents to mark vulnerabilities assessed as non-exploitable, reducing scan noise and focusing on real risks.
4. Multi-Arch Image Unified Signing — Use cosign sign on manifest lists rather than individual manifests, ensuring unified verification across linux/amd64 and linux/arm64.
5. SLSA Level 3 Build Assurance — Use Sigstore signing + Rekor transparency log + SLSA provenance in CI/CD to achieve SLSA Level 3, ensuring auditable and verifiable build processes.
Scanner Comparison
| Feature | Trivy | Grype | Clair | Snyk |
|---|---|---|---|---|
| Scan Speed | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Vulnerability DB Coverage | Comprehensive (OS+language) | Good | OS-layer focused | Comprehensive |
| SBOM Generation | ✅ SPDX/CycloneDX | ❌ Needs Syft | ❌ | ✅ |
| Image Signing | ❌ Use with Cosign | ❌ | ❌ | ❌ |
| IaC Scanning | ✅ | ❌ | ❌ | ✅ |
| Secret Detection | ✅ | ❌ | ❌ | ✅ |
| CI/CD Integration | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Open Source Free | ✅ Apache 2.0 | ✅ Apache 2.0 | ✅ Apache 2.0 | ❌ Commercial |
| K8s Operator | ✅ | ❌ | ✅ | ✅ |
| Go Module Support | ✅ | ✅ | ❌ | ✅ |
| Best For | All-around first choice | Lightweight scanning | K8s cluster-level | Enterprise compliance |
Recommendation: Small-to-mid teams should start with Trivy (open source, full-featured); large K8s clusters consider Trivy Operator + Clair combo; enterprise compliance scenarios evaluate Snyk.
Recommended Online Tools
| Tool | Purpose | Link |
|---|---|---|
| JSON Formatter | SBOM/scan result formatting | /en/json/format |
| Hash Calculator | Image digest/file integrity verification | /en/encode/hash |
| Curl to Code | Security scan API call generation | /en/dev/curl-to-code |
| Base64 Codec | Key/certificate encoding/decoding | /en/encode/base64 |
| Regex Tester | Falco rule/log matching debug | /en/dev/regex |
Summary and Outlook
Container security scanning isn't a one-time action — it's a full lifecycle practice from build to runtime. The core logic of the 6 key practices: Trivy scanning discovers vulnerabilities → SBOM makes dependencies transparent → Cosign ensures image trust → CI/CD gates auto-block → govulncheck audits Go dependencies → Falco monitors runtime anomalies.
2026 trends: The SLSA framework is becoming the de facto standard for supply chain security, Sigstore Keyless signing lowers the signing barrier, SBOM attestation binds bills of materials to images, and AI-assisted vulnerability fix prioritization. Container security is shifting from "reactive patching" to "shift-left built-in" — the earlier you integrate security practices, the lower the fix cost.
Remember: Security isn't a wall blocking delivery — it's a gate ensuring delivery quality. Start with Trivy scanning and progressively build your container security defense line.
Try these browser-local tools — no sign-up required →