| name | show-threat-model |
| description | Print the standard summary block of the current threat model — scan identity, findings by severity, the remediation backlog (P1/P2/P3) and mitigation coverage, the worst-case scenarios, control posture, and whether the model is still up to date. A DISPLAY command, not a question-answering surface: use it only for an explicit request to see that block ("show me the threat model summary", "zeig mir die threat-model-uebersicht", "print the overview"). Reuses the same change detection that decides if an incremental scan is needed. Does not analyze code, write files, or compose prose — it prints a rendered block verbatim and cannot answer anything the block does not already contain. Do NOT route a natural-language question here, not even "is there a threat model?" / "gibt es hier ein bedrohungsmodell?" — every question goes to ask-threat-model, which answers directly and can produce this block too. |
You are printing a human-facing overview of the threat model in the target
repository. This skill is read-only — it does not analyze code, does
not spawn agents, and does not write files. It reads the committed
threat-model.yaml and reports.
This is a display command, not a Q&A surface
Your entire job is to emit the rendered block. You do not answer questions
about the model here, and you do not compose prose around the block.
If the user's request was actually a question — "is there a threat model?",
"does it cover SSRF?", "what's the fix for F-003?", "was ist am schlimmsten?" —
routing landed on the wrong skill. Print the block (it is still useful context),
then hand off in one line: "Für Fragen zum Modell:
/appsec-advisor:ask-threat-model". Do not attempt the answer yourself from
the block — it holds a fixed set of facts and cannot answer beyond them, and a
plausible-sounding answer assembled from a summary is exactly the ungrounded
output ask-threat-model exists to prevent.
The two skills are asymmetric on purpose: ask-threat-model can also produce
this block, so a misroute in that direction costs nothing — a misroute here
is a dead end unless you hand off.
The freshness verdict comes from threat_model_health.py --json, which wraps
baseline_state.py check-changes + dirty-set — the same change detection
the pipeline uses to decide whether an incremental scan is needed. This skill
does not re-implement that logic; it folds the verdict into the overview.
--help — inline help (early exit)
If the user's arguments contain --help or -h, print this block verbatim
and exit.
/appsec-advisor:show-threat-model — Read-only threat model overview.
USAGE
/appsec-advisor:show-threat-model [--repo <path>] [--output <path>] [--all] [--json]
FLAGS
--repo <path> Repository to inspect (default: current working dir)
--output <path> Output directory to inspect (default: <repo>/docs/security)
--all List every threat grouped by severity (default: top Critical only)
--json Emit the overview as machine-readable JSON
WHAT IT SHOWS
* Project + scan identity (commit, branch, model, depth, scan date)
* Freshness: is the model still current, or has security-relevant code changed?
(same change detection that drives the incremental-scan decision)
* Findings by severity (Critical / High / Medium / Low)
* Remediation backlog by mitigation priority (P1 / P2 / P3) and how many
findings have a proposed mitigation vs. are uncovered
* The top "worst case if nothing changes" scenarios (from the model's
curated critical findings, with the covering mitigation)
* Top Critical threats (or all threats with --all)
* Control posture: effectiveness mix (Missing / Weak / Partial / Adequate)
and the weakest control domains
* Mitigation and control counts, plus the rendered report path
RELATED
/appsec-advisor:ask-threat-model Ask anything about the model (any question)
/appsec-advisor:threat-model-health Ops/CI probe: freshness + cleanup + run state
/appsec-advisor:create-threat-model Generate or update the threat model
After printing the help block, exit. Do not proceed.
Step 1 — Parse arguments
Recognized flags (and the values consumed by --repo / --output):
--repo <path> --output <path> --all --json --help | -h
Parse these and set REPO_ROOT, OUTPUT_DIR, ALL_MODE, JSON_MODE.
- Default
REPO_ROOT to the current working directory.
- Default
OUTPUT_DIR to $REPO_ROOT/docs/security.
--repo <path> overrides REPO_ROOT; --output <path> overrides OUTPUT_DIR.
--all and --json are boolean toggles that consume no value.
Reject unknown arguments (hard fail)
If the invocation contains any token that is not one of the recognized
flags above — or is not the value consumed by --repo / --output — DO NOT
proceed. Do not resolve CLAUDE_PLUGIN_ROOT, do not invoke any helper, do not
touch any file. Print the following block verbatim to stderr, substituting
<TOKEN> with the first unknown token, then exit with status 2:
Error: unknown argument '<TOKEN>'
/appsec-advisor:show-threat-model accepts only:
--repo <path> Repository to inspect (default: current working dir)
--output <path> Output directory to inspect (default: <repo>/docs/security)
--all List every threat grouped by severity
--json Emit the overview as machine-readable JSON
--help, -h Show full help and exit
Run `/appsec-advisor:show-threat-model --help` for details.
A flag that takes a value (e.g. --repo or --output) counts as unknown
when its value is missing — treat the flag itself as the offending token.
Repeated occurrences of the same flag are allowed; the last value wins.
Step 2 — Resolve CLAUDE_PLUGIN_ROOT
if [ -z "$CLAUDE_PLUGIN_ROOT" ]; then
CLAUDE_PLUGIN_ROOT=$(find /root /home /opt -maxdepth 6 \
-path "*/appsec-advisor/skills/show-threat-model/SKILL.md" \
2>/dev/null | head -1 | xargs -r dirname | xargs -r dirname | xargs -r dirname)
fi
export CLAUDE_PLUGIN_ROOT
if [ -z "$CLAUDE_PLUGIN_ROOT" ] || [ ! -d "$CLAUDE_PLUGIN_ROOT" ]; then
echo "Error: CLAUDE_PLUGIN_ROOT could not be resolved." >&2
exit 2
fi
Step 3 — Dispatch to the helpers
Run the freshness probe in JSON mode and pipe it into the summary renderer so
the freshness verdict is folded into one deterministic block. Do not set
pipefail: the meaningful exit code is the renderer's (0 = model present,
1 = no model), not the health probe's CI exit code.
EXTRA=""
[ "$ALL_MODE" = "true" ] && EXTRA="$EXTRA --all"
[ "$JSON_MODE" = "true" ] && EXTRA="$EXTRA --json"
python3 "$CLAUDE_PLUGIN_ROOT/scripts/threat_model_health.py" \
--repo-root "$REPO_ROOT" --output-dir "$OUTPUT_DIR" --json 2>/dev/null \
| python3 "$CLAUDE_PLUGIN_ROOT/scripts/summarize_threat_model.py" \
--output-dir "$OUTPUT_DIR" --repo-root "$REPO_ROOT" --health-json - $EXTRA
EXIT=$?
Print the renderer's output as the complete deliverable — do not add
commentary. If the renderer exits 1 (no model found), it already prints the
hint to run /appsec-advisor:create-threat-model; surface that as-is.
Exit-code reference (for shell callers):
0 — threat model present, overview rendered
1 — no threat model found at <output>/threat-model.yaml
2 — error (unreadable / unparseable model, or unknown argument)