Repository navigation
feat(watchdog): daemon logs every attack for triage; threatcrush watchdog to verify, fix, PR - #283
Merged
Merged
Conversation
… a PR A ban says who attacked, not whether it worked. Watchdog mode keeps every attack the dashboard sees so each one can be verified afterwards and, when it is a real hole, fixed in a PR. - `threatcrush tui --watchdog`, or `w` in the dashboard, appends attacks to <state dir>/watchdog/attacks.jsonl (owner-only, buffered, rotated at 50 MB, deduped across reconnect backfills). Turning it on also sweeps what is already on screen. Header badge: `◉ WATCHDOG 42 · 3 answered · 1 5xx`. Demo mode refuses, so canned events never reach the real log. - An attack is any CRS-matched web request (even below the ban threshold), or any medium+ detection from another module; firewall notices, heartbeats and plain 4xx/5xx are not. - log-watcher now puts the HTTP status, method and full request target in web event details. Status is what separates a probe that was refused (4xx) from one the app answered (2xx) or crashed on (5xx). - `threatcrush watchdog [list|show|brief|mark|path]`: findings grouped by vhost+method+path+attack type, worst outcome first; `brief` prints a Markdown hand-off (samples, repro curl, verify-then-fix steps) for an issue, PR body or agent; `mark <id> hole|fp|fixed --note --pr` records the verdict in triage.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ThreatCrush Security Scan17 finding(s) HIGH/CRITICAL: 1 | MEDIUM: 8 | LOW: 8
Snippets are redacted; ThreatCrush never prints matched credential material. |
…e-system-race) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The TUI only kept attacks while someone had a dashboard open with `w` on.
The daemon now logs every attack from its event bus, always:
- daemon/watchdog-service.ts subscribes to the bus, flushes each second
and on shutdown, and reports {enabled, dir, logged, answered, errored,
error?} in the IPC `status` reply. `[watchdog] enabled = false` turns it
off; `[watchdog] dir` moves it. An unwritable dir is reported, not fatal.
- A root daemon shares /var/lib/threatcrush/watchdog with `adm` (the group
its socket already trusts): setgid 2770, log and triage.json 660, so log
readers can `threatcrush watchdog list` and `mark` without sudo. A
user-mode daemon keeps it 700/600.
- The TUI no longer writes anything: the header badge shows the daemon's
counts, and the `w` key and `tui --watchdog` flags are gone (unreleased).
- `threatcrush watchdog` reads the system daemon's directory first, like
the client picks the system socket.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A ban says who attacked, not whether it worked. Watchdog mode keeps every attack on disk so each one can be verified afterwards and, when it is a real hole, fixed in a PR.
What's in it
<state dir>/watchdog/attacks.jsonl(daemon/watchdog-service.ts): buffered, flushed each second and on shutdown, rotated at 50 MB, deduped. On by default;[watchdog] enabled = falseturns it off,[watchdog] dirmoves it. An unwritable dir is reported in status and the daemon log, never fatal.admwhen root:/var/lib/threatcrush/watchdogis setgid2770 root:adm, the log andtriage.jsonare660, the same group the IPC socket trusts. Log readers can list and mark without sudo. A user-mode daemon keeps it700/600.statusgainswatchdog: {enabled, dir, logged, answered, errored, error?}. The TUI header shows◉ WATCHDOG 42 · 3 answered · 1 5xxfrom it; the TUI writes nothing itself.status,methodand full request target (url) in web event details. Status is what separates refused (4xx) from answered (2xx, a possible hole) or errored (5xx).threatcrush watchdog [list|show|brief|mark|path]: findings grouped by vhost+method+path+attack type, worst outcome first; refused/triaged hidden unless--all.brief <id>prints a Markdown hand-off (samples, repro curl, verify-then-fix steps) for an issue, PR body or agent.mark <id> hole|fp|fixed --note --prstores the verdict. Reads the system daemon's directory first, like the client prefers the system socket.Tested
tsc --noEmitclean; CLI vitest 49 files / 497 tests pass (new:core/__tests__/watchdog.test.ts,daemon/__tests__/watchdog-service.test.ts, view badge tests).[watchdog] logging attacks to …, IPCstatusreturns{"enabled":true,…}, dir created700in user mode./wp-login.phpas one row),brief,mark fixed --prdropping it from the queue,--dirbefore and after the subcommand.Daemons need upgrading and restarting to start logging (and for web events to carry
status).🤖 Generated with Claude Code