|
| 1 | +# 📦 SBOM Tools |
| 2 | + |
| 3 | +A set of production-hardened Bash utilities for generating Software Bill of Materials (SBOMs) and performing vulnerability scanning. These scripts are designed for CI/CD environments, emphasizing error handling, input sanitization, and automated cleanup. |
| 4 | + |
| 5 | +**The Scripts:** |
| 6 | +1. SBOM Generator -- Generates SPDX & CycloneDX SBOMs from a target directory or archive |
| 7 | +2. SBOM Analyzer -- Finds & Analyzes SBOMs within a target directory |
| 8 | +3. SBOM CVE Scanner -- Scans packages listed in an SBOM for known CVEs |
| 9 | + |
| 10 | +## 📥 Installation |
| 11 | + |
| 12 | +### 🛠️ Dependencies |
| 13 | + |
| 14 | +Ensure the following tools are installed in your environment (local or CI runner): |
| 15 | +- jq 1.6+ -- used for JSON processing |
| 16 | +- Syft v0.60.0+ -- used for SBOM Generation |
| 17 | +- Grype -- used for vulerability scanning of SBOM content |
| 18 | +- `bash`, with support for `sed`, `grep`, `basename`, `dirname`, `mktemp`, `realpath`, `timeout` (standard with `coreutils`) |
| 19 | +- External HTTP access is required for Grype to retrieve the vulnerability database. In air-gapped environments, this step must be done manually. Syft does not require an external connection except when scanning an external Docker file. |
| 20 | + |
| 21 | +#### 📥 Install Dependencies |
| 22 | + |
| 23 | +**Ubuntu/Debian:** `sudo apt-get update && sudo apt-get install -y jq` |
| 24 | + |
| 25 | +If necessary: `sudo apt-get install coreutils` (Should be pre-installed) |
| 26 | + |
| 27 | +(Adjust to suit your distro or package manager if not apt; _e.g._, `yum`, `rpm`, _etc._) |
| 28 | + |
| 29 | +Install Syft & Grype from their repos: |
| 30 | + |
| 31 | +`curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin` |
| 32 | + |
| 33 | +`curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin` |
| 34 | + |
| 35 | +**macOS, using Homebrew:** `brew install jq syft grype` |
| 36 | + |
| 37 | +### 📥 Install the SBOM Scripts |
| 38 | + |
| 39 | +1. Save the scripts to a working directory (unless you're adding them to your PATH). |
| 40 | +2. Make them executable: |
| 41 | + |
| 42 | +```bash |
| 43 | +chmod +x sbom-gen.sh analyze-sbom.sh vuln-scan.sh |
| 44 | + |
| 45 | +``` |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | + |
| 50 | +## 🚀 SBOM Generator |
| 51 | + |
| 52 | +`sbom-gen.sh` |
| 53 | + |
| 54 | +**Purpose:** Generates standardized SBOMs from a source target (Docker image, directory, or archive) using **Syft**. |
| 55 | +**Outputs:** Automatically creates two `.json files` for SBOMs in both `SPDX` and `CycloneDX` formats. |
| 56 | +**Safety:** Sanitizes filenames, enforces relative paths for privacy, and includes timeouts to prevent stalls. |
| 57 | + |
| 58 | +This script will generate two `.json` SBOM files (one SPDX and one CyclondDX format) from a target directory or archive of a type supported by Syft, including `.zip`, `.tar`, `.tar.gz`, `.7z`, `.tgz`, `.xz`, and others, or from Docker images. The script sanitizes filenames, enforces relative paths for privacy, and includes timeouts to prevent stalls. |
| 59 | + |
| 60 | +Usage: `./sbom-gen.sh {target}` |
| 61 | + |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | +## 🚀 Vulnerability Check |
| 72 | + |
| 73 | +`vulncheck-sbom.sh` |
| 74 | + |
| 75 | +This script will review a provided SBOM and check the listed packages against a current vulnerability database. The script is recommended to be run on a CycloneDX SBOM, but will work equally with other standard SBOM formats. The script will output an updated SBOM including vulnerability information, with a file name based on the target input, _e.g._, `sbom-vulns-{target}.cyclonedx.json`. |
| 76 | + |
| 77 | +Usage: `./vulncheck-sbom.sh sbom-{target}.cyclonedx.json` |
| 78 | + |
| 79 | +Note: in an air-gapped environment, the Grype DB must be imported manually or the script will fail. |
| 80 | + |
| 81 | +Import the database manually with `grype db import` |
| 82 | + |
| 83 | +## License: MIT |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | + |
| 98 | +# SBOM Analyzer |
| 99 | + |
| 100 | +A robust Bash utility that scans directories for Software Bill of Materials (SBOM) sources (such as `package-lock.json` or existing SBOM files), generates standardized SBOMs on-the-fly using **Syft**, and performs a deep variance analysis against a baseline if multiple SBOMs are found. It identifies added, removed, and version-shifted packages while intelligently filtering out development dependencies to reduce noise. |
| 101 | + |
| 102 | +## 🚀 Features |
| 103 | + |
| 104 | +* **Auto-Discovery:** Recursively finds SBOM-compatible files (default depth: 6), skipping `node_modules` and hidden directories for speed. |
| 105 | +* **Intelligent Baseline:** Automatically sorts found files by depth, prioritizing files with `*bom*`, `*spdx*`, `*cyclonedx*, and `*json*` in the file name. The highest-priority file is selected as the "Source of Truth" baseline for comparisons. |
| 106 | +* **Noise Reduction:** Identifies and omits `devDependencies` from the comparison report to focus on production risks. |
| 107 | +* **Security Hardened:** Includes timeouts, relative path masking (privacy), and secure filename handling to prevent injection attacks on the script itself. |
| 108 | +* **CI/CD Ready:** Offers a `--json` flag for machine-parsable output for build pipelines. |
| 109 | + |
| 110 | +## 🛠️ Dependencies |
| 111 | + |
| 112 | +- jq 1.6+ |
| 113 | +- Syft v0.60.0+ |
| 114 | +- Grype |
| 115 | +- `bash`, |
| 116 | +- `awk`, `sed`, `grep`, `basename`, `dirname`, `mktemp`, `realpath`, which are all standard with the `coreutils` package. |
| 117 | +- External HTTP access is required for Grype only to retrieve the vulnerability database. This must be done manually for an air-gapped environment. Syft does not require an external connection except when scanning an external Docker file. |
| 118 | + |
| 119 | +This script relies on standard Unix utilities and two specific tools: **Syft** and **jq**. |
| 120 | + |
| 121 | +### Required Tools |
| 122 | + |
| 123 | +| Tool | Purpose | Installation (macOS) | Installation (Ubuntu/Debian) | |
| 124 | +| --- | --- | --- | --- | |
| 125 | +| **Syft** | Generating SBOMs from lockfiles | `brew install syft` | `curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | |
| 126 | +| **jq** | Processing JSON data | `brew install jq` | `sudo apt-get install jq` | |
| 127 | +| **awk** | Text processing & diffing | Pre-installed | Pre-installed | |
| 128 | +| **timeout** | Anti-DoS security | Pre-installed | `sudo apt-get install coreutils` | |
| 129 | + |
| 130 | +* Obviously, adapt the install commands for your package installer on other Linux distros or if not using `apt`. |
| 131 | + |
| 132 | +## 📥 Installation |
| 133 | + |
| 134 | +1. Ensure dependencies are met. |
| 135 | +2. Save the script to your selected directory. |
| 136 | +3. Make it executable: |
| 137 | + |
| 138 | +```bash |
| 139 | +chmod +x analyze-sbom.sh |
| 140 | + |
| 141 | +``` |
| 142 | + |
| 143 | +## 💻 Usage |
| 144 | + |
| 145 | +### Basic Syntax |
| 146 | + |
| 147 | +```bash |
| 148 | +./analyze-sbom.sh [DIRECTORY] [OPTIONS] |
| 149 | + |
| 150 | +``` |
| 151 | + |
| 152 | +*If no directory is provided, it defaults to the current directory (`.`).* |
| 153 | + |
| 154 | +### Options & Flags |
| 155 | + |
| 156 | +| Flag | Long Flag | Description | |
| 157 | +| --- | --- | --- | |
| 158 | +| `-v` | `--verbose` | **Detailed Mode:** Lists the specific names of added, removed, or changed packages. | |
| 159 | +| `-j` | `--json` | **JSON Output:** Outputs raw JSON for piping into other tools. Suppresses all log messages. | |
| 160 | +| `-n` | `--no-diff` | **Discovery Only:** Finds potential SBOMs but skips the comparison step. | |
| 161 | +| `-d` | `--depth INT` | **Search Depth:** How deep to search for files (Default: 6). | |
| 162 | +| `-f` | `--filter TYPE` | **Filter:** Limit analysis to specific package types (e.g., `npm`, `python`, `binary`). | |
| 163 | +| `-h` | `--help` | **Help:** Displays usage information. | |
| 164 | + |
| 165 | +## 🔍 Examples |
| 166 | + |
| 167 | +### 1. Drift Check |
| 168 | + |
| 169 | +Compare all lockfiles in the current directory against the highest-priority baseline. |
| 170 | + |
| 171 | +```bash |
| 172 | +./analyze-sbom.sh |
| 173 | + |
| 174 | +``` |
| 175 | + |
| 176 | +### 2. Verbose Audit |
| 177 | + |
| 178 | +See exactly *which* library versions have changed. |
| 179 | + |
| 180 | +```bash |
| 181 | +./analyze-sbom.sh ./backend --verbose |
| 182 | + |
| 183 | +``` |
| 184 | + |
| 185 | +*Output snippet:* |
| 186 | + |
| 187 | +```text |
| 188 | + Variances: [+] 0 new, [-] 0 removed, [Δ] 1 version changes |
| 189 | + [Δ] react: 17.0.2 ➔ 18.2.0 |
| 190 | +
|
| 191 | +``` |
| 192 | + |
| 193 | +### 3. CI/CD Integration (JSON) |
| 194 | + |
| 195 | +Generate a JSON report to fail a build if drift is detected. |
| 196 | + |
| 197 | +```bash |
| 198 | +./analyze-sbom.sh --json > sbom-report.json |
| 199 | + |
| 200 | +``` |
| 201 | + |
| 202 | +### 4. Deep Search |
| 203 | + |
| 204 | +If your `package-lock.json` is deeply nested (_e.g._, inside a monorepo structure). |
| 205 | + |
| 206 | +```bash |
| 207 | +./analyze-sbom.sh --depth 8 |
| 208 | + |
| 209 | +``` |
| 210 | + |
| 211 | +## ⚙️ How It Works |
| 212 | + |
| 213 | +1. **Discovery:** The script runs `find` to locate files matching `*bom*` or `*.json`. It deliberately prunes (ignores) `node_modules`, `.git`, `dist`, and `.venv` to ensure performance. |
| 214 | +2. **Baseline Selection:** When multiple SBOM files are found, list is sorted by priority, calculated as (1) the depth it is found in the directory tree (_e.g._, 0 in the target-root directory, 1 for the first subdirectory, and so on). If the filename includes any the SBOM-indicators (`spdx`, `cyclonedx`, `bom`), 0.5 is subtracted from its depth score. The highest-priority file will have the lowest number, becoming the Baseline to which any other files are compared. |
| 215 | +3. **Parsing:** |
| 216 | +* Each file is passed to `syft` to generate a standardized JSON SBOM. |
| 217 | +* `jq` filters out artifacts marked as `dev: true` or having a `dev` property. |
| 218 | + |
| 219 | +4. **Comparison:** |
| 220 | +* `awk` compares the production dependencies of the Target vs. the Baseline. |
| 221 | +* It calculates **Additions** (in Target, not Baseline), **Removals** (in Baseline, not Target), and **Version Shifts**. |
| 222 | + |
| 223 | + |
| 224 | + |
| 225 | +## 🛡️ Security Notes |
| 226 | + |
| 227 | +* **Read-Only:** This script is read-only; it does not modify your lockfiles or project structure. |
| 228 | +* **Privacy:** Output paths are relative to the execution directory. Absolute system paths (_e.g._, `/home/user/...`) are masked. |
| 229 | +* **Timeouts:** Parsing operations are capped at 30 seconds per file to prevent "Zip bomb" or "JSON bomb" denial-of-service scenarios. |
| 230 | + |
| 231 | + |
| 232 | +## License |
| 233 | + |
| 234 | +These scripts are licensed under the MIT License. |
| 235 | + |
| 236 | +Documentation **CC BY 4.0** https://creativecommons.org/licenses/by/4.0/ |
| 237 | + |
| 238 | + |
| 239 | + |
| 240 | + |
| 241 | + |
| 242 | + |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | + |
| 247 | + |
| 248 | + |
| 249 | + |
| 250 | + |
| 251 | + |
| 252 | + |
| 253 | + |
| 254 | + |
| 255 | + |
| 256 | + |
| 257 | +### 2. `vuln-scan.sh` SBOM CVE Scanner |
| 258 | + |
| 259 | +**Purpose:** Consumes an existing SBOM and scans it for vulnerabilities using **Grype**. |
| 260 | + |
| 261 | +* **Outputs:** A unified JSON SBOM file containing the original SBOM merged with the vulnerability report. |
| 262 | +* **Safety:** Redacts internal tool paths (_e.g._, `.cache/grype`) to prevent information leakage and validates JSON integrity before saving. |
| 263 | + |
| 264 | + |
| 265 | + |
| 266 | +## 💻 Usage |
| 267 | + |
| 268 | +### Generating SBOMs |
| 269 | + |
| 270 | +Run the generator against a local directory, supported archive (`.zip`, `.tar.gz`, _etc._), or Docker image. |
| 271 | + |
| 272 | +```bash |
| 273 | +./sbom-gen.sh <TARGET> |
| 274 | + |
| 275 | +``` |
| 276 | + |
| 277 | +**Examples:** |
| 278 | + |
| 279 | +```bash |
| 280 | +# Scan a local directory |
| 281 | +./sbom-gen.sh ./app-source |
| 282 | + |
| 283 | +# Scan a Docker image |
| 284 | +./sbom-gen.sh nginx:latest |
| 285 | + |
| 286 | +``` |
| 287 | + |
| 288 | +**Environment Variables:** |
| 289 | +You can pass flags directly to Syft using `SYFT_ARGS`: |
| 290 | + |
| 291 | +```bash |
| 292 | +# Scan only the squash filesystem of an image |
| 293 | +SYFT_ARGS='--scope squash' ./sbom-gen.sh alpine:latest |
| 294 | + |
| 295 | +``` |
| 296 | + |
| 297 | +**Output:** |
| 298 | +The script creates two files in the current directory: |
| 299 | + |
| 300 | +* `sbom-<sanitized_name>.spdx.json` |
| 301 | +* `sbom-<sanitized_name>.cyclonedx.json` |
| 302 | + |
| 303 | +--- |
| 304 | + |
| 305 | +### Scanning SBOM for Vulnerable Packages |
| 306 | + |
| 307 | +Give the scanner a valid SBOM as its target file to scan. |
| 308 | + |
| 309 | +```bash |
| 310 | +./vuln-scan.sh <INPUT_SBOM_FILE> |
| 311 | + |
| 312 | +``` |
| 313 | + |
| 314 | +**Example:** |
| 315 | + |
| 316 | +```bash |
| 317 | +./vuln-scan.sh sbom-nginx_latest.spdx.json |
| 318 | + |
| 319 | +``` |
| 320 | + |
| 321 | +**Output:** |
| 322 | + |
| 323 | +* Creates: `sbom-vulns-<sanitized_name>.json` |
| 324 | +* This file contains the full SBOM **plus** a new top-level `vulnerabilities` object containing the Grype findings. |
| 325 | + |
| 326 | +## 🛡️ Security & Hardening Features |
| 327 | + |
| 328 | +These scripts include several protections suitable for enterprise pipelines: |
| 329 | + |
| 330 | +1. **Write Verification:** Both scripts explicitly check if the current directory is writable before wasting CPU cycles on scanning. |
| 331 | +2. **Anti-DoS Timeouts:** |
| 332 | +* `syft` is capped at **60s**. |
| 333 | +* `grype` is capped at **120s** (to allow for database updates). |
| 334 | + |
| 335 | + |
| 336 | +3. **Privacy Scrubbing:** |
| 337 | +* **Generator:** Forces relative path scanning to prevent leaking absolute server paths (e.g., `/home/jenkins/workspace`). |
| 338 | +* **Scanner:** Redacts internal Grype cache paths from the final JSON report. |
| 339 | + |
| 340 | + |
| 341 | +4. **Atomic Writes:** Results are written to temporary files first and only moved to the final filename upon successful completion and validation. |
| 342 | +5. **Strict Cleanup:** A `trap` function ensures temporary files are deleted regardless of whether the script succeeds, fails, or is terminated by `CTRL+C`. |
| 343 | + |
| 344 | + |
| 345 | +## License |
| 346 | + |
| 347 | +These scripts are licensed under the MIT License. |
| 348 | + |
| 349 | +Documentation **CC BY 4.0** https://creativecommons.org/licenses/by/4.0/ |
0 commit comments