Supply chain security scanner for Node.js and Bun projects.
OpenSentinel scans your full dependency tree — including transitive dependencies — for known CVEs, malicious code patterns, and supply chain risks. Results appear in an interactive terminal UI or can be exported as CycloneDX SBOM, JSON, or HTML.
- Full dependency tree — parses
package-lock.json,yarn.lock,pnpm-lock.yaml,bun.lock - Multi-source advisories — queries OSV, GitHub Security Advisories, and NVD in parallel
- Code pattern detection — credential harvesting, crypto mining, network exfiltration, obfuscated
eval/require - AST analysis — Tree-sitter-based deep inspection of JS/TS source (requires
downloadSource: true) - MITRE ATT&CK mapping — links detections to techniques (T1005, T1071, T1059, …)
- Install script detection — flags packages with
preinstall/postinstallhooks - Typosquatting detection — catches packages with names similar to popular libraries
- Interactive TUI — navigate packages, drill into each CVE, view code snippets, export on the fly
- CI/CD friendly — structured exit codes, non-interactive output formats, severity filters
- Rust 1.75+
- PostgreSQL (optional, for advisory caching) — the scanner works without a database, results are fetched live each run
GITHUB_TOKEN— recommended for higher GitHub Advisory API rate limitsNVD_API_KEY— recommended to avoid NVD rate limiting
The scanning screen shows database connection status in real time. If the database is unreachable OpenSentinel continues without cache.
git clone https://github.com/yourname/opensentinel
cd opensentinel
cargo install --path .The opse binary will be available in your $PATH after installation.
# Scan current directory — opens interactive TUI
opse scan
# Scan a specific project
opse scan ~/projects/my-app
# Scan only Node.js packages
opse scan --ecosystem nodejs
# Non-interactive — print JSON to stdout
opse scan --format json
# Save CycloneDX SBOM to file
opse scan --format sbom --output sbom.cdx.json
# Only report high and critical issues
opse scan --severity high,critical
# Skip dev dependencies
opse scan --exclude devDependencies# Initialize config in your project
opse init
# Follow the interactive wizard to configure:
# - Database connection (PostgreSQL with URL or individual fields)
# - Source analysis settings
# - API credentials
# - Parallelism tuning
# - Output preferencesEach scan is saved to the database automatically. You can review previous results without re-scanning:
# List scans for the current project
opse history
# List scans across all projects
opse history --all
# Re-open a previous scan in the TUI
opse view 3f2a1b4cThe scan ID is displayed at the end of every scan. Use the first 8 characters or the full UUID.
| Key | Action |
|---|---|
↑ ↓ |
Navigate package list or vulnerability list |
Enter |
Open vulnerability list for selected package; then open detail |
Esc |
Go back one panel |
Tab |
Cycle between panels |
I |
Ignore / restore selected package (dimmed at bottom of list) |
/ |
Search packages by name |
D |
Toggle direct dependencies only |
G |
Group by severity |
E |
Export current filtered results to opensentinel-{timestamp}.json |
C |
Copy selected vulnerability ID + fix info to clipboard |
Q |
Quit |
Switch between arrows (default) and vim (hjkl) keybindings via opse init or the --keybindings flag.
The scanning screen shows real-time database connection status. If the database is unreachable, OpenSentinel continues without cache and scan results are not persisted.
| Panel | Content |
|---|---|
| Left (30%) | Full package list with severity labels. Filterable by search, direct deps, severity group. |
| Top-right (30%) | Vulnerability list for the selected package — advisories ([OSV], [GitHub], [NVD]) and code detections ([CODE]). |
| Bottom-right (70%) | Full detail of the selected vulnerability: description, CVSS score, affected/patched versions, publish date, references, code snippet, MITRE mapping, recommendations. |
opse scan --format sbom # CycloneDX (default for --output)
opse scan --format json # Raw JSON — full risk data
opse scan --format table # ASCII table in terminal
opse scan --format html # HTML report
# Re-render a saved JSON report into another format
opse report --source scan.json --format html --output report.htmlConfig is read from ./opensentinel.json (project-level) or ~/.opensentinel/config.json (global). Run opse init to generate it interactively.
{
"version": "1.0",
"database": {
"engine": "postgresql",
"host": "localhost",
"port": 5432,
"database": "opensentinel",
"user": "postgres",
"password": "${DB_PASSWORD}",
"ssl": false,
"poolSize": 10
},
"sourceAnalysis": {
"enabled": true,
"downloadSource": false,
"analyzeAst": true,
"cacheDir": ".opensentinel/cache",
"cacheTtl": 604800,
"maxSourceSizeMb": 100
},
"parallelism": {
"packageConcurrency": 4,
"apiConcurrency": 3,
"osv": { "limit": 10, "delayMs": 100 },
"github": { "limit": 5, "delayMs": 200 },
"nvd": { "limit": 5, "delayMs": 200 },
"mitre": { "limit": 3, "delayMs": 300 }
},
"credentials": {
"githubToken": "${GITHUB_TOKEN}",
"nvdApiKey": "${NVD_API_KEY}",
"storage": "env",
"keyringSupport": false
},
"ecosystems": ["nodejs", "bun"],
"severity": ["high", "critical"],
"excludeDevDeps": false,
"keybindings": "arrows",
"outputFormat": "sbom"
}Credential values starting with
${...}are read from environment variables at runtime. Setstorage: "keyring"to use the OS keyring instead.
For managed PostgreSQL providers that require a full connection string (including SNI parameters), use the url field instead of individual connection fields:
{
"database": {
"url": "${DATABASE_URL}"
}
}The url value supports the same ${ENV_VAR} syntax. When url is set it takes precedence over host, port, user, and password. SSL is enforced automatically for non-localhost hosts even when using individual fields.
By default, OpenSentinel only checks advisory databases (no source download). To enable full code analysis:
"sourceAnalysis": {
"downloadSource": true,
"analyzeAST": true
}With downloadSource: true, OpenSentinel fetches package tarballs from the registry and scans the actual source for malicious patterns and AST-level anomalies. Results appear as [CODE] entries in the TUI with file paths and code snippets.
- OSV — Open Source Vulnerabilities (covers npm, PyPI, Go, Rust, …)
- GitHub Security Advisories — curated advisories from the GitHub Advisory Database
- NVD — National Vulnerability Database (NIST CVE feed)
| Category | Examples |
|---|---|
| Credential harvesting | process.env access, hardcoded secrets, SSH key patterns |
| Crypto mining | Mining pool connections (stratum+tcp://), CoinHive, XMRig |
| Network exfiltration | HTTP POST to external hosts, base64-encoded URLs |
| Obfuscated code | eval(Buffer.from(..., 'base64')), dynamic require via encoded strings |
evalwith encoded payloads- Dynamic
requirewith obfuscated paths - Hardcoded secrets in variable assignments
- Suspicious environment variable access patterns
- Install scripts —
preinstall/postinstall/preparehooks - Typosquatting — names suspiciously similar to well-known packages
- MITRE ATT&CK — technique mapping (T1005 Data from Local System, T1071 Application Layer Protocol, T1059 Command and Scripting Interpreter, …)
OpenSentinel returns structured exit codes suitable for use in pipelines:
| Exit code | Meaning |
|---|---|
0 |
No risks, or only LOW severity |
1 |
MEDIUM severity risks found |
2 |
HIGH severity risks found |
3 |
CRITICAL severity risks found |
- name: Security scan
run: |
opse scan \
--format json \
--output opensentinel-report.json \
--severity high,critical
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: opensentinel-report.jsonopse scan --format json --severity critical --output /dev/null
# exits 0 unless CRITICAL found| Ecosystem | Lock files parsed |
|---|---|
| Node.js | package-lock.json (v1/v2/v3), yarn.lock (classic + berry), pnpm-lock.yaml |
| Bun | bun.lock, bunfig.toml |
Python, Go, and Rust support is planned for a future release.
| Component | Library |
|---|---|
| Language | Rust |
| Async runtime | Tokio |
| Terminal UI | Ratatui + Crossterm |
| HTTP client | Reqwest |
| Database | SQLx (PostgreSQL / SQLite / MySQL) |
| AST parsing | Tree-sitter |
| Serialization | Serde |
| SBOM output | CycloneDX |
| Dependency walking | WalkDir |
Contributions are welcome. Please open an issue before submitting large changes.
# Run tests
cargo test
# Check for warnings
cargo clippy -- -D warnings
# Format
cargo fmtApache 2.0 — see LICENSE.
