Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

App Stride Report

ASecurity

STRIDE threat model of the project under review — system decomposition, a data-flow diagram with trust boundaries, and every threat per element with its attack path, current status and mitigation, written in the user's language (pt-BR by default). Use when the user runs /app-stride-report, or asks for a threat model, STRIDE analysis, attack surface review, or an assessment of the project's trust boundaries.

2 stars
0 votes
0 copies
0 views
Added 9/19/2026
securityrustgobashrailsgitdatabasesecurity

Works with

terminalcli

Security Analysis

A100/100

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add joaovicdev/claude-appsec --skill app-stride-report --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of App Stride Report?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for App Stride Report
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/joaovicdev-app-stride-report/badge)](https://www.skillsdirectory.com/skills/joaovicdev-app-stride-report)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: app-stride-report
description: STRIDE threat model of the project under review — system decomposition, a data-flow diagram with trust boundaries, and every threat per element with its attack path, current status and mitigation, written in the user's language (pt-BR by default). Use when the user runs /app-stride-report, or asks for a threat model, STRIDE analysis, attack surface review, or an assessment of the project's trust boundaries.
allowed-tools: Read, Glob, Grep, Bash, Write, Agent
---

# App STRIDE report

Produces one artifact: a model of the system under review — its actors,
processes, stores, flows and trust boundaries — with every threat that applies
to each element, its attack path, whether anything currently stops it, and what
would.

This skill is **self-contained**. Its threat material lives in `stride/` beside
this file and its ids (`S.Q1`, `E.Q3`) are its own. It reads no other skill and
depends on none being installed. Any other security material in the project is a
different taxonomy that happens to share a repository — never cite across.

Nothing here modifies the project under review. There is no `Edit`, and the only
file written is the model itself.

## The distinction the whole skill defends

**A threat is a hypothesis with a status. A finding is a confirmed defect.**

A threat whose mitigation is absent has no line of code to point at — that
absence *is* the threat, and it is the main product of this exercise. So this
skill does not require a `file:line` per threat. It requires something harder to
fake: an account of where the agent looked. See the evidence rule in
`references/report-format.md`.

## Arguments

`/app-stride-report [language] [path]` — both positional, both optional.

| Argument | Default | Meaning |
|---|---|---|
| `language` | `pt-BR` | Output language: `pt-BR`, `en`, `es`, … Anything that is not a recognized language tag is treated as `path`. |
| `path` | repository root | Restrict the model to a subdirectory. Stated in the header, and in **Limits**, when set. |

## Manifest

| ID | File | Load when analysing |
|---|---|---|
| S | `stride/S-spoofing.md` | actors and processes — identity, credentials, tokens, webhook senders, internal calls, anything that answers "who is this" |
| T | `stride/T-tampering.md` | processes, flows and stores — input binding, server-authoritative values, transport, write scope, query and path construction, deserialization, audit trails of data |
| R | `stride/R-repudiation.md` | actors, processes and stores — audit records, attributed identity, log durability, correlation across hops |
| I | `stride/I-information-disclosure.md` | processes, flows and stores — response shape, caller-scoped lookups, error detail, debug surfaces, data at rest, logs, enumerable identifiers |
| D | `stride/D-denial-of-service.md` | processes, flows and stores — limits, unbounded work, shared finite resources, timeouts and retries, cost per invocation |
| E | `stride/E-elevation-of-privilege.md` | processes — where the authorization decision is made, entitlement in the lookup, caller-influenced roles, internal trust, admin surfaces, process privilege |
| — | `references/decomposition.md` | always, in Step 2 — element shapes, the boundary catalogue, the STRIDE-per-element matrix, discovery recipes |
| — | `references/report-format.md` | always, in Steps 4 and 6 — the return contract, the evidence rule, risk, the output template |

Every path is relative to this skill's own directory, so the same bytes work
whether this was installed as a plugin, committed into a project's
`.claude/skills/`, or linked into the user's global skills directory.

## Step 1 — Resolve the roots

Everything downstream is addressed by absolute path. Establish both before
anything else and reuse them verbatim.

1. **`STRIDE_ROOT`** — this skill's own directory, made absolute from wherever
   this `SKILL.md` was loaded. `STRIDE_ROOT/stride/` and
   `STRIDE_ROOT/references/` are what the subagents read.

   If `STRIDE_ROOT/stride/` does not exist, **stop and say so**, naming the path
   you tried. Never continue without the material: a model built from memory
   looks exactly like one built from the files, and is worth nothing.

2. **`SCAN_ROOT`** — the root of the project under review, absolute; the `path`
   argument if given, otherwise the repository root.

3. Detect the stack — `nest-cli.json`, `artisan`/`composer.json`,
   `pom.xml`/`build.gradle`, or none. This selects a **discovery recipe** in
   `references/decomposition.md` and nothing else. No threat question in this
   skill is framework-specific, so "no stack detected" is a normal run.

4. Read these at `SCAN_ROOT` if they exist, and say which you found:
   - **`SECURITY-NOTES.md`** — anything under **Accepted risks** goes to its own
     section, not into the threat list.
   - **`appsec/security-report.md`** — a code-level audit of the same project, if
     the user has run one. Threats it already confirmed get marked in Step 5,
     cited by that report's durable `SEC-<n>` id.
   - **`appsec/profile.md`** — a description of this project's own
     architecture, generated by `/appsec-profile`: numbered claims, each with a
     mechanism, an evidence anchor, and the exceptions it does not cover. It
     carries no threat vocabulary and none of this skill's ids — it says what the
     project *does*, never which category that answers. Read `## Trust boundaries`
     first and **name your boundaries from it, verbatim**. A claim with no
     `Does not apply to:` line, or one under `## Stale`, applies to nothing;
     re-grep the anchor of any claim you rely on, and put a vanished anchor in
     **Premissas**. No profile means this run works exactly as it did before the
     file existed — say so once, name `/appsec-profile`, and continue.

5. **`MODEL`** — `SCAN_ROOT/appsec/stride-report.md`, and **`LEDGER`** — the
   `appsec-ledger` comment on its last lines. If the model exists, read the
   ledger now: it carries every threat id this project has issued, what element
   and ref each one was, whether it was open or fixed, and which ones a
   regression test already pins down. Step 5 reconciles against it and Step 6
   rewrites it.

   No model yet means no ledger: every threat is `[new]` and ids start at `TM-1`.
   A model whose ledger comment is missing or unparseable is **not** a fresh
   start — fall back to `max(id present in the document)` and say so. Restarting
   at `TM-1` would hand an old id to a new threat, and a committed test citing
   that id would then point at the wrong one.

## Step 2 — Decompose the system

Follow `references/decomposition.md`. Produce all five lists — actors,
processes, stores, flows, boundaries — before enumerating a single threat.

This decomposition is the spine of the document: the element inventory and the
STRIDE matrix are built from it, not from whatever the analysis happened to
notice. Run the **Cross-check** at the end of that file before continuing.

Every judgment call made here — what counts as one process, where a boundary
sits, what a store holds — is written down now and reproduced under
**Premissas** in the output. A wrong assumption invalidates every threat resting
on it, so it has to be visible enough to be corrected.

**A boundary the profile already names keeps that name, verbatim.** Two runs that
decompose the same system correctly but call the same boundary by two names cannot
be compared, and a threat model nobody can compare across runs is a document
rather than a practice.

**The same holds for every element, and now it is load-bearing.** A threat's
identity is `(element name, ref)`, so an element the previous model at
`appsec/stride-report.md` already names keeps that name verbatim — read the
`## Inventário de elementos` of that document before writing your own. Renaming
`OrdersController` to `Orders controller` between two runs does not produce a
clearer model; it produces one threat that vanished and one that appeared, and a
regression test pointing at an id that no longer means anything. The table in the profile is the shared vocabulary; extend
it where the decomposition finds more, and never rename what it already has.

## Step 3 — Draw it, and show it

Emit the Mermaid data-flow diagram from the template in
`references/report-format.md`: one `subgraph` per trust boundary, every
element inside the boundary it belongs to, every flow labelled with what it
carries.

Print the diagram and the element counts to the user **before** fanning out, and
continue without waiting. A wrong decomposition is cheap to correct now and
expensive to correct after eight agents have reasoned on top of it — but the
**Premissas** section catches it either way, so this does not block.

## Step 4 — Fan out

Dispatch **`threat-modeler`** subagents — the agent shipped alongside this
skill. Under a plugin install its name carries the plugin's namespace as a
prefix; the bare name resolves otherwise. It has no `Write` and no `Edit`, so
read-only is enforced by its definition rather than requested in a prompt.

If neither form resolves, the agent was not installed. Fall back to
`general-purpose`, **tell the user** the model is running without the
enforced-read-only agent, and paste the full set of rules from the agent's
definition into every prompt by hand.

**Partition by trust boundary.** One subagent per boundary, owning the elements
on its inner side. Split a boundary whose inner side exceeds ~12 elements, and
give stores and flows that belong to no single boundary — a shared database, a
log sink, an outbound integration — their own agent. Cap at 8 concurrent, run
the remainder in further batches.

**Assign categories from the matrix, not by hand.** An agent owning only stores
and flows is never asked about `S` or `E`, and its prompt does not name those
files. An agent owning processes carries all six.

Every subagent prompt states, explicitly:

- **`STRIDE_ROOT`** and **`SCAN_ROOT`**, both absolute. The `stride/` and
  `references/` paths are relative to `STRIDE_ROOT` — interpolate the absolute
  path rather than pasting the relative one.
- **The whole decomposition**, including the boundaries and elements this agent
  does *not* own. A threat that crosses a boundary is only visible to an agent
  that knows the other side is there.
- **The slice it owns**, and that everything outside it belongs to another agent.
- **Which `stride/` files to read**, from the matrix.
- The return contract from `references/report-format.md`, in **English**.
- **The profile sections this agent's slice needs — never the whole file.**
  `## Trust boundaries` and `## Module map` always; the claim sections the matrix
  makes relevant to the elements it owns, and no others (an agent owning only
  stores and flows gets no `## Authorization`). Say that a claim is the developer's
  word and is not to be re-derived, and state the tagging rule: **an agent never
  stays silent.** It emits the block with one extra field, `profile: P<n>`, naming
  the claim it relied on, and consolidation decides. An agent that suppressed on
  its own would make the count this model owes **Limites** impossible to produce.

## Step 5 — Consolidate

- **Deduplicate** by `(element, ref)`. A threat that is a property of the system
  rather than of one element — no rate limiting anywhere, no audit trail at all —
  is filed once against the element that would own the fix, never repeated on
  every element it touches.
- **Drop** any `ref` that does not exist in `stride/`, any `ref` the matrix says
  that element type cannot carry, and any `status: unmitigated` with no account
  of where the agent searched. A threat nobody can check is a threat nobody will
  act on.
- **Rate** each surviving threat with the impact × likelihood matrix. Order by
  risk, then by boundary.
- **Reconcile against `LEDGER`** and assign ids, following section 3 of
  `references/report-format.md`: match by `(element, ref)`, carry the id of every
  match, mark an unmatched ledger row `fixed`, give a rematched `fixed` row its
  own id back as `reopened`, and issue a new id at the next number for anything
  left. Still `TM-<n>` and never `T-<n>` — `T` is Tampering — but no zero
  padding, and **never renumbered, never reused**: the order of this list is by
  risk and says nothing about identity.
- **Remove, and count.** A threat tagged `profile: P<n>` against a well-formed
  claim does not enter the model — not as a threat, not as a note on the element.
  It is counted, and that count plus the claim ids is the line this run owes
  **Limites**. A tag against a claim with no `Does not apply to:` line, one under
  `## Stale`, or one whose anchor did not re-grep is **not** a removal: the threat
  stands, and the account of the search says why.
- **Cross-check the three project files.** An accepted risk from
  `SECURITY-NOTES.md` moves to its own section. A threat that
  `appsec/security-report.md` already confirmed on the same element or route gets
  a **Confirmado em código** line citing that finding's **durable id** (`SEC-7`)
  — never the taxonomy it uses. The two documents share a project, not a
  vocabulary. Both ids are durable, so the citation survives a regeneration of
  either document.

## Step 6 — Emit

Everything this skill writes lives under `SCAN_ROOT/appsec/` — never in this
skill's own repository. Create the directory if it is absent.

1. **Archive the previous run, do not overwrite it.** If `MODEL` exists, read its
   `**Data:**` and `**Commit:**` header and move it to
   `appsec/history/stride-<that date>-<that sha>.md`. Same date and same sha as a
   file already there → append `-2`, `-3`. A model with no `**Commit:**` header
   predates this layout: name it `stride-<that date>-nogit.md`.
2. **Write the new model** to `appsec/stride-report.md`, ledger comment last.
3. **Print it to the terminal** as well.

`appsec/stride-report.md` is the current run and `appsec/history/` is every run
before it. A run never copies itself into the history — it moves its predecessor
there.

Capture the sha for this run's header with
`git -C "$SCAN_ROOT" rev-parse --short HEAD`; a dirty worktree gets `<sha>-dirty`
and a directory that is not a git repository gets `nogit`.

**This document maps the attack surface, including the parts nobody has tried
yet, and it is committed.** That is deliberate: comparing this run against the
last is the practice this document exists to support, and a local-only file
nobody shares does not support it. Say so once, plainly, on a first run — a
repository whose read access is wider than the team should decide that knowingly.
Never add an ignore rule on the user's behalf.

Use the template in `references/report-format.md`. Translate the prose and
the labels into the requested language. Never translate: ids (`S.Q1`, `TM-04`),
file paths, route paths, HTTP methods, identifiers, or code.

Close with **Premissas** and **Limites**: what was assumed, what was not
decomposed, how many elements were actually read, and that no threat is not
proof of no risk.

Attribution

joaovicdevjoaovicdev
View sourceMore from joaovicdev →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Springboot Security

Java Spring Boot 服务中关于身份验证/授权、验证、CSRF、密钥、标头、速率限制和依赖安全的 Spring Security 最佳实践。

2456590 votes

Security Review

Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.

2456590 votes

Summarize Status

Write a short, colloquial summary for a Paperclip summary slot: open with the 1–3 specific, concrete actions the reader needs to take right now to unblock the work, then a brief plain-language status, streaming progress as it works.

798220 votes

Paperclip Task Bridge

Create, comment on, update, and list Paperclip tasks from Hermes using scoped Paperclip API credentials.

798220 votes

V3 Security Overhaul

Complete security architecture overhaul for claude-flow v3. Addresses critical CVEs (CVE-1, CVE-2, CVE-3) and implements secure-by-default patterns. Use for security-first v3 implementation.

701370 votes
View all in security →