| name | exploitation-watch |
| allowed-tools | Read, Grep, Glob, Write, Bash(secaudit*), Bash(python3 -m secaudit_core.cli*), Bash(python -m secaudit_core.cli*) |
| description | Watch the dependencies a project already ships and report the ones that BECOME actively exploited — the EU Cyber Resilience Act's 24-hour early-warning trigger, which fires when the world changes and the repository does not. Use when the user asks to be alerted about newly exploited CVEs, to set up continuous or scheduled vulnerability monitoring, to track CISA KEV / EPSS for their dependencies, or asks "am I affected by the new CVE", "did anything become exploitable", "monitor my dependencies", "CRA 24 hour reporting", and Turkish equivalents ("yeni açık çıktı mı", "bağımlılıklarımı izle", "sürekli izleme"). NOT for a one-off audit — that is the `security-audit` skill. This one answers a question about time, not about code. |
| license | MIT |
Exploitation watch
A scan answers "what is wrong with this code now." This answers the other question, the one
with a legal deadline on it:
A dependency you already ship became actively exploited overnight. Nothing in the
repository changed.
From 2026-09-11 that starts a 24-hour early-warning obligation to ENISA and the relevant
CSIRT under the EU CRA. The trigger is actively exploited, not has a CVE — which is why this
is built on CISA KEV rather than on advisory counts.
Running it
secaudit <path> --watch .secaudit/watch.json
First run records the advisories the project ships, with the reachability verdict the VEX pass
gave each one. Every later run re-asks CISA KEV and FIRST EPSS about exactly those CVE ids and
reports the transitions. Exit code is non-zero when something newly became exploited and when
a feed could not be read.
Only CVE ids leave the machine. Nothing about the code, the paths or the hostnames is sent.
Reading the result
| What you see | What it means | Act? |
|---|
| "N advisories became actively exploited" | The CRA 24-hour class. Reachable ones listed first. | Yes — now |
| "Could not be established" | Neither feed answered. No comparison was made. | Yes — re-run with network |
| "Nothing became actively exploited" | Both feeds answered and nothing moved | No |
| "still exploited, already reported" | Carried, not re-alerted; the clock started when it first appeared | No |
The second row is the one to explain to the user if it appears. A monitoring loop that prints
a clean report when it failed to check is worse than no monitoring, so this refuses to produce a
comparison at all rather than reporting no change. Never summarise a blocked run as "all clear".
Setting it up as a loop
Point them at docs/continuous-mode.md for the scheduled
workflow. Two things to tell them, because both are easy to get wrong:
- Commit the state file. It carries
exploited_since per advisory — the date their 24-hour
clock started. A fresh baseline erases it.
- Re-scan on the same schedule. The watch tracks CVE ids a scan already found; it does not
discover new advisories on its own, so a dependency that gains one only enters the list when
the scan runs again. The command above does both in one pass.
What it does not do
It watches dependencies. A newly published weakness in the user's own code is what the
security-audit skill is for. And an advisory the VEX pass called not_affected is still
watched and still reported when it becomes exploited — that verdict is an import-level inference
that can be wrong, and the reporting obligation attaches to the product rather than to our
confidence about it.