Skip to content
virge-ioPublic

About

Local security vulnerability scanner CLI

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

28 Commits

Folders and files

Repository files navigation

Security Watch CLI

Security Watch CLI scans local project files for security signals on Linux workstations.

It reports:

  • local project dependency vulnerabilities from Trivy or OSV Scanner
  • direct vs transitive (indirect) classification for each vulnerability
  • the direct dependency chain that pulls in each transitive vulnerable package
  • optional registry lookups (PyPI, npm) to check if a newer version of that direct dep resolves the issue
  • optional public watch-feed matches from CISA KEV and NVD Recent

Binaries

sec-watch is the core scanner. It scans your projects directory, fetches public vulnerability feeds, caches results with a TTL, and prints a status line. It is designed to run continuously — for example, triggered by a shell prompt or status bar widget.

sec-watch-local is a one-shot wrapper around sec-watch. Use it when you want to scan on demand, point at a specific directory, or scan a local Git branch without touching your working tree. It:

  • accepts a PATH argument (defaults to the same projects dir as sec-watch)
  • with --branch <name>, clones a local mirror of the repo and checks the branch out into a temporary worktree before scanning
  • runs sec-watch in an isolated per-run cache directory so it never touches the persistent cache
  • supports --json output for scripting

Install

Install Trivy:

curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b ~/.local/bin

Download the latest binaries from GitHub Releases:

TAG=$(curl -fsSL https://api.github.com/repos/virge-io/sec-watch/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
for b in sec-watch sec-watch-local; do
  curl -fLo ~/.local/bin/$b \
    "https://github.com/virge-io/sec-watch/releases/download/${TAG}/${b}-${OS}-${ARCH}"
done
chmod +x ~/.local/bin/sec-watch ~/.local/bin/sec-watch-local

Verify the MD5 checksums (optional):

SUMS=$(curl -fsSL "https://github.com/virge-io/sec-watch/releases/download/${TAG}/md5sums.txt")
for b in sec-watch sec-watch-local; do
  expected=$(printf '%s\n' "$SUMS" | grep "${b}-${OS}-${ARCH}" | awk '{print $1}')
  actual=$(md5sum ~/.local/bin/$b | awk '{print $1}')
  [ "$expected" = "$actual" ] && echo "$b: OK" || echo "$b: MISMATCH"
done

Make sure ~/.local/bin is on your PATH. Verify:

sec-watch defaults-env

Optionally install a keyword watch config:

mkdir -p ~/.config/sec-watch
cp config/watch.example.json ~/.config/sec-watch/watch.json
# edit ~/.config/sec-watch/watch.json to add your keywords

Usage

Run a scan from the default projects directory (~/Projects):

sec-watch-local

Scan a specific local directory:

sec-watch-local /path/to/project

Scan a local Git branch without changing your working tree:

sec-watch-local /path/to/repo --branch main

Scan a remote GitHub repo (clone first):

git clone --depth=1 https://github.com/org/repo /tmp/repo-scan
sec-watch-local /tmp/repo-scan

Machine-readable JSON output:

sec-watch-local --json

Debug mode — prints effective scanner settings and internal scan milestones:

sec-watch-local --debug --progress-interval 1

Check defaults and current configuration:

sec-watch defaults-env

Transitive dependency analysis

For each vulnerability, sec-watch classifies the affected package as direct or indirect (transitive). Indirect findings include:

  • which direct dependency or dependencies pull in the vulnerable package (the "via" chain, resolved by BFS through the lockfile dependency graph)
  • a registry lookup against PyPI or npm for each direct parent to find whether its latest release constrains the transitive dep to a fixed version

Example text report output:

1. CRITICAL CVE-2026-42208 (CVSS 9.8)
   Package: litellm 1.83.0 [indirect]
   Fix: needs litellm >= 1.83.7
   Via: orchestrator-core 5.0.1 — latest 5.0.2 still allows unfixed litellm (>=1.80.0)
   Target: uv.lock
   ...

If the latest release of the direct dep does require a fixed version, the advice will say so and name the version to upgrade to. If the package is private and not on the public registry, that is noted too.

Registry lookups add a few seconds to each scan. Set SEC_WATCH_REGISTRY_LOOKUP=0 to disable them for offline or faster runs.

Configuration

All defaults can be overridden via environment variables:

Variable Default Description
SEC_WATCH_PROJECTS_DIR ~/Projects Root directory to scan
SEC_WATCH_PROJECTS (all) Comma-separated project name filter
SEC_WATCH_ECOSYSTEMS npm,yarn,pnpm,pip,poetry,uv,python-pkg Dependency ecosystems to include
SEC_WATCH_PUBLIC_FEEDS cisa-kev,nvd-recent Public CVE feeds to query
SEC_WATCH_RECENT_DAYS 7 Lookback window for "recent" changes
SEC_WATCH_TTL 1800 Cache TTL in seconds
SEC_WATCH_CONFIG ~/.config/sec-watch/watch.json Watch config path
SEC_WATCH_REGISTRY_LOOKUP 1 Set to 0 to skip PyPI/npm lookups
SEC_WATCH_FORCE 0 Set to 1 to bypass the cache
SEC_WATCH_DEBUG 0 Set to 1 for debug output

The sec-watch-local wrapper also accepts flags and SEC_WATCH_LOCAL_* overrides:

# Scan a different projects root
SEC_WATCH_PROJECTS_DIR=~/Code sec-watch-local

# Filter to specific ecosystems
sec-watch-local --ecosystems npm,pip

# Disable public feeds
sec-watch-local --public-feeds ''

# Override recent-days lookback
sec-watch-local --recent-days 14

Watch config

Public-feed keyword matching is configured in ~/.config/sec-watch/watch.json:

{
  "lookback_days": 14,
  "keywords": [
    "openssh",
    "openssl",
    "linux kernel",
    "node.js"
  ]
}

The keywords list is matched case-insensitively against CVE IDs, vendor names, product names, and descriptions in both CISA KEV and NVD. A starter template is at config/watch.example.json.

Reports

Each scan writes reports to ~/.cache/sec-watch/:

File Description
dependency-report.txt Terminal-formatted findings with CVSS details and via chain
dependency-report.html Sortable HTML table with indirect badge and fix/via column
dependency-report.json Raw Trivy output

Both the text and HTML reports mark transitive packages with an [indirect] label. The HTML report's Fix / Via column shows the fix version and the direct dep chain inline. Tables can be sorted by clicking any column header.

sec-watch-local run artefacts are kept under ~/.cache/sec-watch-local/jobs/ for post-scan inspection.

Development

Build and verify:

go build ./...
go vet ./...

To test without touching the real cache:

XDG_CACHE_HOME=$(mktemp -d) SEC_WATCH_FORCE=1 sec-watch status

About

Local security vulnerability scanner CLI

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages