Skip to content

Commit 86cba58

Browse files
authored
Enhance vulnerability check documentation
Updated the vulnerability check section for clarity and added formatting. Signed-off-by: Brent Toderash <brent@toderash.net>
1 parent 0c2f77f commit 86cba58

2 files changed

Lines changed: 349 additions & 126 deletions

File tree

tools/sbom-tools/readme.md

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
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

Comments
 (0)