Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ RUST_BACKTRACE=full
#STACKDOG_SSHOLE_ENABLED=true
# Accounts that should never accept a password login. Defaults to root,alpine.
#STACKDOG_SSHOLE_KEYONLY_USERS=root,alpine
# CVE watch: match every package on the host and in containers against the
# public feeds (OSV + CISA's KEV catalog) and alert once per package and CVE.
# Refreshed at least daily, which is what catches newly published CVEs.
#STACKDOG_CVE_WATCH_ENABLED=true

# ---------------------------------------------------------------------------
# Dashboard container (docker-compose.yml / docker-compose.app.yml)
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- CVE watch (`vuln.cve-watch`): matches every package on the host and in
running containers against the public feeds. OSV answers "does this exact
version have known vulnerabilities" (Debian and Ubuntu versions server-side,
Alpine versions without the `-rN` package revision), CISA's KEV catalog says
which are exploited in the wild — those are Critical, the rest carry the
advisory's severity label. Alerts once per package and CVE, 50 per pass
worst first, caches answers for 24h. A match means a fix is missing, not
that a box is exploitable: alerts say to check the vendor's advisory.
`STACKDOG_CVE_WATCH_ENABLED=false` turns it off.
- Configuration is also read from `~/.config/stackdog/.env`
(`$XDG_CONFIG_HOME/stackdog/.env` when set), behind the working-directory
`.env`: the nearest file wins and real environment variables win over both.
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,30 @@ syslog sources the brute-force bans come from — on Alpine, sshd logs to
`STACKDOG_SSHOLE_KEYONLY_USERS`. Stackdog cannot patch OpenSSH: upgrading is
the fix.

#### CVE watch (every new CVE, from the public feeds)

The detectors above hardcode what they know. `vuln.cve-watch` asks the public
databases instead, so a CVE published tonight is checked on the next refresh
without a Stackdog release. Every package on the host and in running
containers is matched against [OSV](https://osv.dev) (Debian and Ubuntu
versions server-side; Alpine's without the `-rN` package revision), and the
results are cross-referenced with CISA's KEV catalog: known-exploited findings
are Critical, the rest carry the advisory's own severity label.

| Finding | When | Action |
|---|---|---|
| `vuln.cve-watch` | a package version that OSV reports vulnerabilities for, newly matched since the last alert | alert once per package and CVE, KEV entries first. Never bans: a version names no attacker |

Each package is re-queried at least every 24 hours, which is what catches
newly published CVEs; answers are cached in the database so a restart does not
re-query everything. Alerts are capped at 50 per pass, worst first. Honest
limits: a match says a fix is missing, not that a machine is exploitable —
distributions backport fixes, and configuration-dependent CVEs do not survive
being flattened into a version range, so treat findings as "check your
vendor's advisory". Very fresh CVEs may not be in the feeds yet; the
specialized detectors above cover the ones we know in detail.
`STACKDOG_CVE_WATCH_ENABLED=false` turns it off.

---

## 📦 Installation
Expand Down
25 changes: 25 additions & 0 deletions src/database/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,31 @@ pub fn init_database(pool: &DbPool) -> Result<()> {
[],
)?;

conn.execute(
"CREATE TABLE IF NOT EXISTS cve_watch_cache (
ecosystem TEXT NOT NULL,
package TEXT NOT NULL,
version TEXT NOT NULL,
fetched_at TEXT NOT NULL,
cve_ids TEXT NOT NULL,
PRIMARY KEY (ecosystem, package, version)
)",
[],
)?;

conn.execute(
"CREATE TABLE IF NOT EXISTS cve_watch_alerted (
owner TEXT NOT NULL,
ecosystem TEXT NOT NULL,
package TEXT NOT NULL,
version TEXT NOT NULL,
osv_id TEXT NOT NULL,
alerted_at TEXT NOT NULL,
PRIMARY KEY (owner, ecosystem, package, version, osv_id)
)",
[],
)?;

conn.execute(
"CREATE TABLE IF NOT EXISTS ip_offenses (
id TEXT PRIMARY KEY,
Expand Down
Loading
Loading