Agent skill

X Ray

by pashov in pashov/skills

Generates an x-ray.md pre-audit report covering overview, enhanced threat model (protocol-type profiling, git-weighted attack surfaces, temporal risk analysis, composability dependency mapping)…

MITAuto-check passedSecurity

Install X Ray

skills CLI
$ npx skills add pashov/skills --skill x-ray -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install pashov/skills x-ray --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/pashov/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/x-ray .claude/skills/x-ray && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
x-ray
GitHub stars
1.2k
Used in
1 other repo
Token cost
~10k tokens
SKILL.md length
4,758 words
Files
8 (incl. scripts, references)
Skills in repo
5
Repo updated
First seen
Licence
MIT

At a glance

Generates an x-ray.md pre-audit report covering overview, enhanced threat model (protocol-type profiling, git-weighted attack surfaces, temporal risk analysis, composability dependency mapping)…

  • Works in 3 steps: Enumerate & Measure → Read Source Files + Entry Point Scan… → Write Output
  • Audit readiness
  • SKILL.md covers Progress tracking (MANDATORY), Step 1: Enumerate & Measure, Step 2: Read Source Files +… and Step 3: Write Output, plus 1 more section
  • Runs Python and Shell scripts from its folder; calls python3, bash and npx

What it does

X Ray is an agent skill from pashov/skills. Generates an x-ray.md pre-audit report covering overview, enhanced threat model (protocol-type profiling, git-weighted attack surfaces, temporal risk analysis, composability dependency mapping), invariants, integrations, docs quality, test analysis, and developer/git history. Triggers on 'x-ray', 'audit readiness', 'readiness report', 'pre-audit report', 'prep this protocol', 'protocol prep', 'summarize this protocol'.

Its SKILL.md is about 10k tokens, which your agent loads only when the skill is triggered. The skill folder holds 9 other files, including scripts and reference files (for example `README.md`, `references/templates.md` and `references/threats.md`).

It sits in Security, covering Threat modeling, Git workflow and Smart contract auditing. It works with Git. The repository describes itself as: Pashov Audit Group Skills. The licence is MIT.

When your agent uses it

  • Audit readiness
  • Readiness report
  • Pre-audit report
  • Prep this protocol

Example prompts

  • “audit readiness”
  • “readiness report”
  • “pre-audit report”
  • “/x-ray”

Requirements

  • Python 3
  • Node.js
  • A Bash shell

Workflow steps

3 steps, taken from the step headings in SKILL.md.

  1. Enumerate & Measure
  2. Read Source Files + Entry Point Scan (SINGLE message, ALL tool calls parallel)
  3. Write Output

What it can do on your machine

Read from SKILL.md and the folder at commit 92b0ea6. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships 3 files in scripts/ (Python and Shell), which the agent can run.

    Shell commands in SKILL.md call:

    • python3
    • bash
    • npx
    • curl
    • npm

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npx, curl and npm, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

X Ray loads about 10k tokens when it runs, and up to ~34k if it reads all its reference files. Until then it costs about 107 tokens; SKILL.md has 4,758 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~107
When it runs · the whole SKILL.md, loaded when a task matches
~10k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~34k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.

SKILL.md

The full file from pashov/skills at commit 92b0ea6, republished under its MIT licence (© pashov). 4,758 words, ~10,032 tokens.

Download SKILL.mdSave it as .claude/skills/x-ray/SKILL.md (or your agent's skills folder). This skill also uses 7 other files; get the full folder from GitHub.
name
x-ray
description
Generates an x-ray.md pre-audit report covering overview, enhanced threat model (protocol-type profiling, git-weighted attack surfaces, temporal risk analysis, composability dependency mapping), invariants, integrations, docs quality, test analysis, and developer/git history. Triggers on 'x-ray', 'audit readiness', 'readiness report', 'pre-audit report', 'prep this protocol', 'protocol prep', 'summarize this protocol'.

X-Ray

Generate an x-ray/ folder at the project root containing all output files. Pipeline: 3 steps, always sequential.

$SKILL_DIR = the directory containing this SKILL.md file. Resolve it from the path you loaded this skill from (e.g. if this file is at /path/to/x-ray/SKILL.md, then $SKILL_DIR = /path/to/x-ray).

Progress tracking (MANDATORY)

Before doing anything else, call TodoWrite with these 3 todos (all pending):

  1. Phase 1: Enumerate & measure codebase
  2. Phase 2: Read sources, classify entry points, synthesize invariants
  3. Phase 3: Write x-ray report files

Transitions (update via TodoWrite — never batch):

  • Mark Phase 1 in_progress immediately, before running enumerate.sh.
  • When Step 1's parallel batch returns, in ONE TodoWrite call mark Phase 1 completed and Phase 2 in_progress.
  • When Step 2 (including 2b–2g) finishes, in ONE TodoWrite call mark Phase 2 completed and Phase 3 in_progress.
  • After all Step 3 output files are written, mark Phase 3 completed.

Rule: exactly one todo is in_progress at any time. Status updates happen the moment a phase starts or ends.

Step 1: Enumerate & Measure

If the user specifies a path, use it as project root. Otherwise use cwd. If no .sol files or foundry.toml/hardhat.config.* at root, check one level deep.

Source directory detection: Auto-detect from foundry.toml (src = "...") or hardhat.config.*. If no config, try both src/ and contracts/. Read foundry.toml first if present.

Run enumeration (single Bash call — includes output directory creation):

bash
mkdir -p [project-root]/x-ray && bash $SKILL_DIR/scripts/enumerate.sh [project-root] [src-dir]

Immediately after, launch ALL of the following in a single message (parallel):

0. Version check (foreground):

  • Read the local VERSION file from $SKILL_DIR/VERSION
  • Bash curl -sf https://raw.githubusercontent.com/pashov/skills/main/x-ray/VERSION
  • If the remote VERSION fetch succeeds and differs from local, print ⚠️ You are not using the latest version. Please upgrade for best security coverage. See https://github.com/pashov/skills. If it fails, skip silently.

1. Coverage (run_in_background: true):

For Foundry:

bash
cd [project-root] && forge coverage 2>&1 || (echo "RETRYING_WITH_IR_MINIMUM" && forge coverage --ir-minimum 2>&1)

For Hardhat:

bash
cd [project-root] && npx hardhat coverage 2>&1

If toolchain is not installed (e.g., forge: command not found, npx: command not found, missing node_modules/), the coverage command will fail. This is expected — test existence is already captured by enumeration in the step above. Coverage failure does NOT mean tests are absent.

2. Git security analysis + JSON read (foreground, single Bash call):

bash
cd [project-root] && python3 $SKILL_DIR/scripts/analyze_git_security.py --repo . --src-dir [src-dir] --json x-ray/git-security-analysis.json 2>&1 && cat x-ray/git-security-analysis.json

The JSON has 7 sections: repo_shape, fix_candidates, dangerous_area_changes, late_changes, forked_deps, tech_debt, dev_patterns.

3. Preload reference files (2 parallel Read calls — these must be in context before Step 2d/3a):

  • $SKILL_DIR/references/threats.md — threat profiles, temporal threats, composability threats
  • $SKILL_DIR/references/templates.md — output template, entry points template, architecture guide

4. Spec/whitepaper detection (1 Glob: **/{whitepaper,spec,design,protocol,architecture,overview,README}*.{pdf,md} excluding node_modules/, lib/, x-ray/, test/). Skip user-facing docs (tutorials, API refs, changelogs, contribution guides). Then apply size-aware handling:

  • Path A (≤5 docs, each ≤300 lines): Include them as Read calls in Step 2's parallel message. Direct reads — no subagent needed.
  • Path B (>5 docs OR any doc >300 lines): Launch a single subagent (model: "sonnet") that reads ALL doc files and returns a structured extraction (max 200 lines). Subagent prompt:
    Read each doc file listed below. Extract ONLY security-relevant information into this format:
    Files: [list of doc file paths]
    
    Return this exact structure:
    ### Doc-Stated Global Invariants
    [Bullet list of every invariant, constraint, or guarantee the docs claim must hold globally across calls. Treated like NatSpec-stated invariants by Step 2g — routed directly to §2 / §3 / §4 of invariants.md by shape, NOT into §1 (Enforced Guards, which is per-call preconditions only).]
    ### Actor Definitions
    [Each actor/role with stated permissions and trust level]
    ### Trust Assumptions
    [What the protocol assumes about external systems, oracles, admins, users]
    ### Cross-System Flows
    [How value/data moves between contracts or external systems]
    ### Economic Properties
    [Fee structures, reward mechanisms, tokenomics, bounded parameters]
    ### Key Design Decisions
    [Explicit "we chose X over Y because Z" statements]
    
    Rules: Quote the source doc for each claim. Omit sections with no relevant content. Max 200 lines total.
    Include this subagent in Step 1's parallel message. Its output feeds Step 3 report writing.

For both paths, extract only: doc-stated global invariants, actor definitions, cross-system flows, trust assumptions, economic properties, key design decisions. Tag all spec-derived claims in the report with (per spec) so auditors know what is code-verified vs spec-stated. Doc-stated global invariants feed Step 2g's NatSpec routing step — they route to §2 / §3 / §4 of invariants.md by shape, NOT to §1 (Enforced Guards).

ALL calls (coverage, git analysis, reference reads, spec glob) MUST appear in the same message. Proceed to Step 2 without waiting for forge coverage.

Step 2: Read Source Files + Entry Point Scan (SINGLE message, ALL tool calls parallel)

CRITICAL: Every tool call — Bash, Agent, Read, Grep — MUST be issued in ONE message so they run concurrently. This includes source file reads, the entry point grep scan, and any spec doc detected in Step 1 (they are all independent).

Scope Filtering
  • Skip interfaces: interfaces/ dirs or filenames I + uppercase letter
  • Skip vendored libs: Uniswap FullMath/TickMath, OZ copies
  • When uncertain, include it but exclude from scope table
Path A: ≤20 source files (direct reads)

One Read call per file. Do NOT read README, docs, or foundry.toml (already read in Step 1).

Extract per file: contract type & inheritance, roles & access control, value-holding state vars, external calls, fund flows, invariant comments, assert/require, backwards-compatibility indicators (see below), delta writes (per function: storage variables and the symbolic delta applied — e.g. Δ(totalSupply) = +shares, Δ(balanceOf[msg.sender]) = +shares — same-basic-block only, no cross-function inference; inherited helpers like OZ _mint/_burn may be resolved only when their effect on balanceOf/_totalSupply is semantically unambiguous), guard predicates (every require/assert/if-revert that references a storage variable, quoted verbatim with line number; skip guards that reference only function parameters), enum/one-shot transitions (every require(state == X); ...; state = Y pair, recorded as X@Lx → Y@Ly — include one-shot latches like require(addr == address(0)); addr = concrete).

Path B: >20 source files (parallel subagents)

Tier 1 — Small files (≤120 lines): Batch into single Bash cat call.

Tier 2 — Large files (>120 lines): Group by subsystem. Launch one subagent per subsystem (model: "sonnet", up to 5, max ~10 files each). Subagent prompt:

Read each file listed below and return a structured summary. Do NOT analyze — just extract facts.
Files: [list of file paths]
For EACH file, return this exact format:
### [filename]
- **Type**: contract | library | abstract
- **Inherits**: [parent contracts]
- **Imports**: [imported libraries/contracts]
- **Roles/Access**: [onlyOwner, role constants, modifiers]
- **State vars (value-holding)**: [mappings/vars that hold balances, collateral, etc.]
- **External calls**: [calls to other contracts, ERC20 transfers, etc.]
- **Fund flows**: [deposit/withdraw/mint/burn/transfer paths]
- **Invariants**: [require/assert statements, NatSpec invariant comments]
- **Delta writes**: For EACH non-view non-pure function, list storage variables that change and the symbolic delta applied. Use format `Δ(var) = +expr` or `Δ(var) = -expr`. Only report pairs where BOTH writes appear in the same function body with no intervening call to an unknown external contract. Do NOT chase writes through inherited/imported functions unless the semantic effect is unambiguous (OZ `_mint` → `balanceOf` + `_totalSupply` is fine; custom internal helpers are NOT — list those deltas only in the internal helper's own entry). Example:
  - `deposit()`: `Δ(totalSupply) = +shares`, `Δ(balanceOf[msg.sender]) = +shares`
  - `borrow()`: `Δ(totalBorrows) = +amount`, `Δ(underlyingBalance) = -amount`
- **Guard predicates**: Every `require`/`assert`/`if-revert` in the file that references a storage variable. Quote verbatim with line number. Skip guards that reference only function parameters.
  - `Vault.sol:206`: `require(_fee <= 10, "fee is capped at 0.1%")`
- **Enum/one-shot transitions**: Every pattern of `require(var == X); ...; var = Y` where `var` is a storage enum, uint, or address. Record as `X@Lx → Y@Ly`. Include one-shot latches like `require(addr == address(0)); addr = concrete`.
- **Key logic**: [1-2 sentences on what the contract does]
- **Function-level access map** (REQUIRED for contracts, skip for libraries):
  List every public/external non-view non-pure function with its access control:
  - `functionName()` — [modifier name, e.g. `onlyRole(OCT_KEEPER)`] or [NONE — permissionless]
  For functions with NO modifier, also list which external calls they make:
  - `functionName()` — NONE — calls `ContractName.method()`
Entry Point Grep Scan (INCLUDED in the same parallel message as source reads)

Launch these two Bash calls in the SAME message as the source file reads above — they are independent and can run concurrently. Commands use only POSIX ERE + POSIX character classes (no -P / PCRE, no GNU-only escapes like \s \w \b), so they work identically on GNU grep (Linux/WSL), BSD grep (macOS default /usr/bin/grep, FreeBSD), and ripgrep:

bash
# 1. Single-line signatures: function name and visibility on same line
grep -rnE 'function[[:space:]]+[[:alnum:]_]+[[:space:]]*\([^)]*\)[[:space:]]+(external|public)' [src-dir]/ --include='*.sol' \
  | grep -v '/interfaces/' | grep -v '/mock/' \
  | grep -Ev '(^|[^[:alnum:]_])(view|pure)([^[:alnum:]_]|$)'
bash
# 2. Multiline signatures: visibility keyword on the closing-paren line (covers 90%+ of multiline cases)
grep -rnE '^[[:space:]]*\)[[:space:]]+(external|public)' [src-dir]/ --include='*.sol' -B5 \
  | grep -v '/interfaces/' | grep -v '/mock/' \
  | grep -Ev '(^|[^[:alnum:]_])(view|pure)([^[:alnum:]_]|$)'

Combine results from both. The multiline grep is critical — Solidity functions often split parameters across lines, putting external/public on the ) line while function name( is lines above. The trailing grep -Ev '(^|[^[:alnum:]_])(view|pure)([^[:alnum:]_]|$)' is the POSIX-portable substitute for \b(view|pure)\b: it drops any line where view or pure appears as a standalone identifier (surrounded by non-identifier chars or line boundaries), while preserving lines that merely contain view_param / pure_x identifier substrings.

Portability guarantees:

  • -E, -v, -r, -n, -B → POSIX (2001+) / supported by macOS, FreeBSD, Linux GNU grep, busybox grep, ripgrep
  • [[:space:]], [[:alnum:]_] → POSIX character classes, supported by all above
  • --include='*.sol' → GNU + macOS BSD grep + ripgrep. Not supported by busybox grep (niche; Alpine minimal); if the skill ever needs to run there, replace --include='*.sol' [src]/ with $(find [src]/ -name '*.sol') passed as arguments.

ALL tool calls (source reads/Bash/subagents, BOTH grep scans) MUST be in ONE message.

Do NOT read test files or documentation files.

Step 2b: Entry Point Classification

Using the grep results already returned from Step 2's parallel message, classify ALL entry points. Do NOT rely solely on subagent summaries — subagents extract facts at the contract level and can misattribute which function makes which external call or which function has which modifier.

Exclude from entry points: view/pure functions, interface-only declarations, library internal functions (they're downstream calls, not entry points), mock contracts.

For each result, classify into one of three categories:

  1. Permissionless — no access-control modifier AND no internal caller restriction in the function body. You MUST verify the function body before classifying as permissionless. For Path A (≤20 files), the bodies are already in context from Step 2 reads — classify directly without additional Read calls. For Path B, batch all candidate body reads into a SINGLE parallel message. Look for ANY of these patterns that restrict the caller:
    • require(msg.sender == X) or if (msg.sender != X) revert ...
    • if (msg.sender != X || ...) revert ... (compound conditions)
    • Calls to internal functions that check msg.sender A function without a modifier but WITH an internal msg.sender check is role-gated, not permissionless. Common examples: acceptOwnership(), acceptMsig(), confirmX() — these often have no modifier but restrict the caller to a specific pending address via if (msg.sender != pendingX) revert.
  2. Role-gated — has a role modifier (onlyRole(X), onlyOwner, onlyRouter, etc.) OR an internal msg.sender restriction (via require, if/revert, or delegated check). Record which role or address is required.
  3. Admin-only — gated by DEFAULT_ADMIN_ROLE, onlyOwner pointing to the protocol admin, or similar top-level authority.

Note: nonReentrant alone is NOT access control. initializer/reinitializer are one-time deployment functions — track separately.

For each entry point, record:

  • Contract name and function name
  • Access level (permissionless / role name / admin)
  • Caller (User, Keeper, Admin, LP, etc.)
  • Parameters with trust level: (user-controlled), (user-signed), (keeper-provided), (protocol-derived)
  • Call chain: trace downstream calls using subagent summaries + function-level access maps. Format: → Contract.fn() → Contract.fn()
  • State modified: which storage vars/mappings change
  • Value flow: in (tokens deposited), out (tokens withdrawn), none
  • Reentrancy guard: yes/no

This data feeds TWO outputs:

  • The permissionless entry points list in x-ray.md (Section 2) — use the permissionless subset only
  • The full entry-points.md file (Step 3c) — uses all categories
Step 2b-flow: Protocol Flow Path Construction

Using the entry point data already collected in Step 2b, construct flow paths for entry-points.md. This is NOT a separate analysis pass — it reorganizes data you already have.

For each major user-facing entry point (permissionless and role-gated functions that move value):

  1. Identify its require statements and state variable checks
  2. For each check, find which function WRITES that state variable (already known from the "State modified" field of other entry points)
  3. Chain these backwards: destination ← writer of its precondition ← writer of THAT precondition ← ... ← deployment
  4. Note non-function preconditions (time passage, market conditions, external state) with ◄── annotations

Output: Simple arrow chains grouped by actor flow. Reference earlier flows instead of repeating. 15-30 lines total. See the Protocol Flow Paths section in the entry-points.md template for exact format.

The grep scan is a hard gate: the permissionless entry points section in the report must match this grep-verified list, not the subagent summaries. If there is a conflict, the grep + code reading result wins.

Step 2c: Backwards-Compatibility Code Detection

While reading source files, watch for code that appears to be remnants of a removed mechanism kept so the remaining codebase does not break. Common signals: empty or trivial function bodies, state variables declared but never meaningfully read or written, comments containing "deprecated" / "legacy" / "backwards compat" / "no longer used", functions that implement an interface but always return a default, and storage variables preserved solely for proxy storage layout compatibility.

After reading ALL source files, cross-reference candidates against these mandatory verification checks before classifying anything as backwards-compatibility. Batch ALL caller-check Grep calls for all candidates into a SINGLE parallel message — do not verify them one-by-one:

  1. Caller check (REQUIRED): Use Grep to confirm the function/variable has NO active callers in the current codebase. If it IS called from active code paths, it is NOT backwards-compatibility — it is the current design, regardless of whether it returns defaults or zeros.
  2. NatSpec/comment check (REQUIRED): If the code has NatSpec or inline comments explaining WHY it behaves a certain way (e.g., "simplified for X mode", "by design", "intentionally zero"), this is documented intentional design, NOT backwards-compatibility code. Do not override explicit developer documentation with heuristic pattern matching.
  3. Interface obligation check: A function that returns default values but exists because an interface requires it AND is actively called is part of the current architecture, not a remnant.

Only classify code as backwards-compatibility when ALL of: (a) no active callers exist, (b) no NatSpec/comments document the behavior as intentional, and (c) git history shows the mechanism it belonged to was removed.

Do not describe backwards-compatibility code as active features in the report. Instead, note them explicitly in Section 1 (see output template) so auditors know which parts of the codebase are retained for compatibility rather than being live functionality. If no backwards-compatibility code survives the verification checks above, omit the subsection entirely.

Step 2d: Centralization & Pause Coverage Analysis

After reading source files and classifying entry points, perform two analyses that feed into the Actors table, Trust Boundaries, and Key Attack Surfaces (Section 2). These are NOT standalone sections — the results integrate into existing report sections.

Centralization analysis — For each privileged role (admin, owner, operator, keeper, service, etc.):

  1. List every operational action the role can take (from the function-level access map)
  2. For each action, note whether a timelock, multi-sig, or delay exists. Distinguish between role transfer delays (e.g., AccessControlDefaultAdminRules 1-day delay) and operational action delays — they are not the same. A role transfer delay does NOT protect against a compromised holder using instant operational functions.
  3. Identify which actions can extract or redirect user funds (e.g., emergencyWithdraw, setTreasury, transferFee)

Integrate into: Actors table (Capabilities column should be specific about what's instant vs timelocked), Trust Boundaries (describe what each boundary actually protects vs what bypasses it), Key Attack Surfaces (frame as "Admin operational powers" or "[Role] compromise" — the attack surface is the role compromise, not individual functions).

Pause coverage analysis — For each critical state-changing function:

  1. Check whether whenNotPaused (or equivalent) is applied
  2. Note which functions are pausable vs not
  3. If a function that should logically be pausable is not (e.g., a function callable by a bounded role that operates on user funds), integrate this finding into the relevant attack surface for that role. The missing pause is not itself an attack surface — it's a detail that worsens the relevant role's compromise scenario.

Anti-pattern: Do NOT create a standalone "Centralization Risks" subsection. Centralization details belong distributed across Actors, Trust Boundaries, Key Attack Surfaces, and Protocol-Type Concerns. A dedicated section duplicates information already present in those sections. The same applies to pause coverage — integrate into the relevant role's attack surface description.

Step 2e: Protocol Classification

After reading source, classify the protocol following the procedures in references/threats.md (type detection + hybrid classification, phase detection, and external call classification — all in one file).

Step 2f: nSLOC

Use the exact nSLOC TOTAL from the Step 1 enumerate output (no ~ prefix) in the report header and scope table.

Show full SKILL.md (2,541 more words)Show less
Step 2g: Invariant Synthesis

Using the delta writes, guard predicates, enum/one-shot transitions, and invariant comments extracted in Step 2 (from direct reads in Path A, or subagent output in Path B), systematically walk the following taxonomy to produce invariant candidates. This is a reasoning pass — no new tool calls needed (except the Grep batch in step 2 Pass B — see below).

Terminology: A guard is a per-call precondition enforced at a single callsite (e.g., require(amount >= MIN)). It is NOT a falsifiable invariant — the code itself guarantees it at that callsite. An invariant is a property that must hold globally across any sequence of calls (e.g., "every active position ≥ MIN"). Guards feed §1 of invariants.md (Enforced Guards reference) only. Invariants that are lifted from guards (see step 2 below) or stated in NatSpec feed §2 / §3 / §4.

NatSpec routing (run before the structural walk): For each NatSpec @invariant tag or inline comment asserting a global property (e.g., "totalSupply always equals Σ balances", "fee never exceeds MAX_BP", "only one active epoch at a time"), route DIRECTLY to §2 (or §3/§4 if the property spans contracts or derives from multiple primitives) by category shape (Conservation / Bound / Ratio / StateMachine / Temporal). Source tag: NatSpec: Contract.sol:LN. Do NOT place developer-stated global invariants in §1 — §1 is per-call guard predicates only. After routing, still run the structural scans below — they may confirm (On-chain=Yes) or contradict (On-chain=No) the NatSpec claim.

Walk order (each step uses the raw extraction data, not prior-step conclusions):

  1. Conservation scan: For each function, find delta-write pairs where Δ(A) = +expr and Δ(B) = -expr (or Δ(B) = +expr for a mapping counterpart) in the same function body. Each matched pair is a conservation candidate: A + B = const or A == Σ B[key].

    • For mapping writes (mapping[key] += e paired with scalar += e), infer scalar == Σ mapping[key]. Verify the pattern holds across ALL functions that write to either variable — if ANY function writes to one without the other, note the gap as "partial conservation" and split into Yes/No rows.
    • For transfer patterns (mapping[from] -= e, mapping[to] += e with no scalar change), confirm the mapping sum is self-conserving.
    • Negative conservation (important): If a function that ought to track a flow (e.g., flashloan pull/push, receive/forward) has zero storage Δ, record this as a Conservation-negative finding. Absence of Δ is itself an invariant observation.
  2. Guard extraction and lift (two passes over each require/assert/if-revert):

    Pass A — Extract verbatim (Enforced Guards reference): Every require/assert/if-revert becomes a G-N row in §1 of invariants.md. Quote the predicate verbatim with source location. This is a mechanical dump of per-call preconditions — not falsifiable, not fuzzed. Skip guards that only reference function parameters with no storage tie-back AND have no global implication (pure local input validation with no audit value beyond Pass B).

    Pass B — Lift to global property, then check all write sites: For each guard, ask: "does this imply a property that must hold across any sequence of calls, not just at this callsite?"

    • If NO (the guard only constrains a transient parameter that is consumed by the function and does not tie to persistent storage) → leave in §1 only. Do not promote.
    • If YES (the guard implies a persistent property — e.g., require(amount >= MIN) at deposit implies "every active position ≥ MIN"; require(_fee <= 10) at setter implies "fee ∈ [0, 10]") → rewrite the guard as a global property, then locate ALL write sites of the constrained storage variable using Grep on the variable name across scope files. Batch ALL write-site Greps for all lifted guards into a SINGLE parallel message — do not verify one at a time:
      • If ALL write sites enforce an equivalent guard → promote to §2 as a Bound invariant with On-chain=Yes. Derivation: cite the guard + confirm all write sites.
      • If ANY write site writes the variable without an equivalent guard → promote to §2 as a Bound invariant with On-chain=No, and cite the unguarded write site(s) as the gap. This is the high-signal output — the gap is simultaneously an invariant and a potential bug.

    Include setter-level bounds where a setter writes to a storage variable constrained by its own parameter check. Run the same all-write-site check — if multiple setters write the same variable but only some enforce the bound, the property is On-chain=No.

  3. Ratio scan: For each storage write of form A = B * C / D where B, C, D are storage variables or function-scoped snapshots of storage, record the ratio. Note whether the snapshot is taken before or after other state changes in the same function (ordering matters — e.g., totalSupply snapshotted before _burn vs after).

  4. State machine / one-shot scan: For each enum/uint/address variable in require(var == X); ... var = Y patterns, record the transition. Distinguish:

    • One-shot latch: require(var == default); var = concrete with no path back (e.g., setStrategy, setLeverager).
    • Togglable flag: require(var == false); var = true but another function flips it back (e.g., freeze/unFreeze, toggleVaultLeverage). NOT a state machine invariant — skip.
    • Cyclic state: false → true → false driven by timing/condition (e.g., ongoingVestingPosition). Record as a cycle invariant.
  5. Temporal scan: For each block.timestamp or block.number comparison involving a storage variable (deadline, lastUpdate, lockPeriod, interval), extract the temporal constraint. Note whether the constraint is checked-then-updated (safe) or updated-then-checked (potential stale read).

  6. Cross-contract scan: For each external call where the return value is used in arithmetic or a storage write, record what the caller assumes. Then find the callee's write sites for that state. If the callee can change it independently (via another function), the assumption is unvalidated — record as a cross-contract invariant with On-chain=No. ONLY include rows where BOTH sides (caller assumption + callee write sites) are inside the scope files. Do not speculate about out-of-scope contracts.

    • Also include: setter-vs-invariant mismatches — where an admin setter writes a storage value without checking that existing invariants still hold (e.g., setReserveCapacity without checking against current liquidity). These are cross-contract in the sense that the setter is one contract/function and the invariant is enforced elsewhere.
  7. Economic derivation: After steps 1-6, check if any combination of single-contract + cross-contract invariants implies a higher-order property. Each economic invariant must cite the specific I-N / X-N IDs it derives from. If the derivation chain has a gap (one of the source invariants is On-chain=No), the economic invariant is also On-chain=No.

Verification gate (MANDATORY before including any inferred invariant):

  • Conservation: confirm the Δ-pair exists at the cited lines (same function body).
  • Guard (Pass A, §1 row): confirm the require/assert/if-revert is verbatim from code.
  • Guard lift (Pass B, §2 row): confirm the lifted global property references persistent storage (not just a transient parameter restatement). Confirm all write sites of the constrained variable have been enumerated via Grep, and the On-chain=Yes/No verdict matches the enumeration — if any write site lacks the guard and the row says On-chain=Yes, the row is invalid.
  • NatSpec: confirm the @invariant tag or comment exists verbatim at the cited location AND asserts a global property (not a per-call note). If it's a per-call note, drop — do not route to §2.
  • Ratio: confirm the formula is exact and the snapshot ordering (before/after other writes in the same function) is noted.
  • StateMachine: confirm both sides of the edge exist AND confirm no reverse path. If there IS a reverse path, it's a togglable flag — drop.
  • Temporal: confirm the comparison involves a storage variable, not just block.timestamp vs parameters.
  • Cross-contract: confirm both caller usage AND callee write site exist in scope.
  • Economic: confirm all referenced I-N / X-N IDs are themselves verified.
  • If you cannot verify → drop the row. "Could not verify" is not a valid row.

Output: Invariant candidates feed directly into invariants.md (Step 3a). x-ray.md Section 3 gets Enforced Guards (Reference) + top 3-5 inferred (prioritize On-chain=No from Conservation, Cross-Contract, and lifted-guard gaps; include one high-signal Yes row for structural coverage like a ratio or state-machine latch).

Step 3: Write Output

Test existence vs. coverage execution (CRITICAL)

Test presence is determined by Step 1 enumeration (test_files, test_functions, stateless_fuzz, foundry_invariant, echidna, medusa, hardhat_fuzz, fork, certora, halmos, hevm counts). These are file-scan results and are ALWAYS reliable regardless of whether the toolchain can compile or run. Multi-signal categories (echidna, medusa, certora, halmos) output as functions:configs — e.g., 5:1 means 5 test functions + 1 config file detected.

Coverage metrics (line/branch %) come from forge coverage or hardhat coverage which require installed dependencies, successful compilation, and passing tests. Coverage can fail for many reasons unrelated to test quality:

  • Dependencies not installed (npm install / forge install not run)
  • Compiler errors (stack-too-deep, version mismatch)
  • Test execution failures (missing RPC, fork config)

Rules:

  1. Use test_files/test_functions from Step 1 enumeration for ALL test existence claims. Never infer "no tests" from coverage tool failure.
  2. If coverage fails but enumeration shows tests exist, report: "[N] test files with [M] test functions detected; coverage metrics unavailable — [failure reason]".
  3. In "Gaps" subsection, only flag missing test categories (stateless_fuzz=0, foundry_invariant=0, echidna=0, medusa=0, certora=0, halmos=0, hevm=0, fork=0), never flag "no tests" when enumeration shows they exist. Prioritize gaps by audit impact: missing stateful fuzz and formal verification for math-heavy/financial logic is higher priority than missing fork tests.
  4. In git history "Security Observations", never claim "commits without tests" based on coverage failure. The test_co_change_rate from git analysis measures file co-modification in commits, not coverage — qualify it as such.
  5. If coverage fails, do NOT let the failure cascade into threat model or risk assessments. Test presence (from enumeration) and coverage metrics (from tooling) are independent signals.

Check forge coverage status: include results if done, failure reason if failed, "pending" if still running. Do NOT wait.

3a. Write ALL output files (4 parallel Write calls in ONE message)

All output files go into the x-ray/ directory. Write ALL FOUR files in a SINGLE message so they are created concurrently:

1. x-ray/architecture.json — Follow format and rules in the architecture guide section of references/templates.md (already loaded in Step 1).

2. x-ray/x-ray.md — Follow template in the output template section of references/templates.md (already loaded in Step 1). Under 500 lines. No fabrication. Section 3 (Invariants) is a POINTER ONLY to invariants.md — do NOT include a Guards table, do NOT list top inferred invariants. One blockquote callout with counts (guards / single-contract / cross-contract / economic) and a strong link to the invariants.md file is the entire §3. The invariant catalog lives exclusively in invariants.md; duplicating it in x-ray.md was old V2 behavior and is no longer correct.

Key Attack Surfaces cross-link requirement: When writing Section 2 Key Attack Surfaces, cross-reference each surface against the invariants.md blocks you just produced. If the surface's cited file:line falls within the Location / Derivation / Caller side / Callee side window of any G-N / I-N / X-N / E-N block, append the matching IDs as bracketed markdown links immediately after the surface title using LOWERCASE slug fragments: - **Surface name** &nbsp;&#91;[X-4](invariants.md#x-4), [I-17](invariants.md#i-17)&#93; — .... Separate each surface bullet with a blank line. Surfaces that are purely access-control or upgrade-ability concerns may be left unlinked — that is a healthy signal, not a gap. Typical hit rate on non-trivial protocols: ≥70% of surfaces link to at least one invariant.

3. x-ray/entry-points.md — Using the full entry point data collected in Step 2b and the flow paths from Step 2b-flow, follow the entry points template section of references/templates.md (already loaded in Step 1). Start with the Protocol Flow Paths section (arrow chains showing prerequisite sequences for each major entry point), then the access-level detail sections. Factual only — no threat analysis (that stays in x-ray.md). If the protocol has >30 entry points, use compact tables for role-gated and admin sections instead of per-function detail blocks. Only permissionless entry points get the full detail block treatment regardless of count.

4. x-ray/invariants.md — Follow the invariant map template section of references/templates.md (already loaded in Step 1). Four sections: Enforced Guards (Reference), Inferred (Single-Contract), Inferred (Cross-Contract), Economic. Use #### G-N / #### I-N / #### X-N / #### E-N heading blocks — NOT tables. Heading anchors (slug #g-1, #i-17, …) are the target of cross-file markdown links from x-ray.md attack surfaces; inline <a id> anchors inside table cells do NOT work cross-file in VS Code. Each G-N block must include a Purpose line explaining what the guard protects (not just what it checks). Every inferred block MUST cite a concrete Δ-pair, guard-lift + write-sites, edge, temporal predicate, or NatSpec claim — drop blocks that cannot. Every cross-contract block must cite BOTH caller-side assumption AND callee-side write sites (both must be inside the scope files). Every economic block must derive from specific I-N / X-N IDs. No cap on block count. Factual only — no threat analysis.

Writing Section 2 (Threat & Trust Model) — Follow the structure in the output template. Use references/threats.md for threat profiles, temporal threats, and composability threats content (all in one file, already loaded in Step 1). For hybrids, merge: primary adversary list first, then unique secondary threats (de-duplicate overlapping ones).

Verification rules (apply during Section 2 writing):

  • Permissionless entry points: Use only the grep-verified list from Step 2b. The Step 2b procedure is the source of truth — do not rely on subagent summaries.
  • Security claims: Before writing any claim that a security check is missing, incomplete, or bypassable, you MUST trace the actual data flow by reading the relevant code. Specifically: (1) identify all write sites for the variable under question (use Grep), (2) confirm your claim holds against those write sites. Subagent summaries are not sufficient. If you cannot verify, qualify the claim with "could not confirm" rather than stating it as fact.

Section 7 (Git History): Integrate x-ray/git-security-analysis.json into: Contributors, Review Signals, Hotspots, Security-Relevant Commits (score >= 5), Dangerous Area Evolution, Forked Dependencies, Tech Debt, Cross-Reference Synthesis (2-4 bullets connecting git signals to Sections 2-3).

Branch scoping (CRITICAL)

The git analysis is scoped to the current branch only (HEAD). The git_branch field in the JSON meta tells you which branch was analyzed. All git signals (fix candidates, hotspots, dangerous areas, late changes) reflect ONLY commits reachable from HEAD — not other branches.

Rules:

  1. State the analyzed branch in the report header or git history section: "Analyzed branch: [branch] at [commit]".
  2. When describing fix commits or code changes from git history, always describe them as what the current branch code does — not what a fix "changed" if you cannot see the before/after on this branch.
  3. Never describe code state from other branches. The source files you read in Step 2 are the current branch's files — git history describes how those files evolved on this branch only.
  4. If the repo shape is squashed_import (1 commit), there is no meaningful evolution to describe — state this and skip fix/hotspot analysis.
3b. Generate & Validate Architecture SVG
bash
python3 $SKILL_DIR/scripts/generate_svg.py x-ray/architecture.json x-ray/architecture.svg

Then follow the rendering, audit checklist, and fix loop in the architecture guide section of references/templates.md. Max 3 iterations. Cleanup temp files after (including x-ray/git-security-analysis.json).

3c. Terminal Verdict

After all files are written and cleanup is done, read the ## X-Ray Verdict section from the generated x-ray/x-ray.md and print it verbatim to the terminal. Do NOT paraphrase, summarize, or rewrite — copy the exact tier, justification, and key observations as they appear in the file.

Constraints

  • Under 500 lines. Protect threat model, invariants, test gaps, git analysis, verdict — compress other sections if needed.
  • No fabrication. Say "could not determine" when uncertain.
  • Steps 1-3 fully autonomous. No user interaction required.
  • Always group contracts by subsystem in scope table.
  • Single pass. No partial outputs.
  • Never reference audit platforms, contest rules, or bounty program framing — keep the report vendor-neutral.
  • If git security analysis script fails, fall back to bash-only git stats. Never block on a missing script.

Before doing anything else, print this exactly:

██████╗  █████╗ ███████╗██╗  ██╗ ██████╗ ██╗   ██╗     ███████╗██╗  ██╗██╗██╗     ██╗     ███████╗
██╔══██╗██╔══██╗██╔════╝██║  ██║██╔═══██╗██║   ██║     ██╔════╝██║ ██╔╝██║██║     ██║     ██╔════╝
██████╔╝███████║███████╗███████║██║   ██║██║   ██║     ███████╗█████╔╝ ██║██║     ██║     ███████╗
██╔═══╝ ██╔══██║╚════██║██╔══██║██║   ██║╚██╗ ██╔╝     ╚════██║██╔═██╗ ██║██║     ██║     ╚════██║
██║     ██║  ██║███████║██║  ██║╚██████╔╝ ╚████╔╝      ███████║██║  ██╗██║███████╗███████╗███████║
╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝ ╚═════╝   ╚═══╝       ╚══════╝╚═╝  ╚═╝╚═╝╚══════╝╚══════╝╚══════╝

© pashov, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 7 other files (scripts, references) in x-ray of pashov/skills.

  • SKILL.md
  • README.md
  • VERSION
  • references/templates.md
  • references/threats.md
  • scripts/analyze_git_security.py
  • scripts/enumerate.sh
  • scripts/generate_svg.py

Open the folder on GitHubat commit 92b0ea6

Used in 1 other repository

We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in pashov/skills, which our catalogue first saw on October 7, 2026.

Compare with similar skills

X Ray next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

X Ray compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
X Ray this skillpashov/skills1.2k1 repos~10kAutomated safety check: PassMIT
Xray Pre Auditccashwell/evm-cortex131—~25kAutomated safety check: PassMIT
Repo SentinelMathews-Tom/armory328—~2.2kAutomated safety check: PassMIT
Security Ownership Mapdiegosouzapw/awesome-omni-skills159—~4.6kAutomated safety check: PassApache-2.0
Hardening Windows Endpoint With Cis Benchmarkmukul975/Anthropic-Cybersecurity-Skills34k—~2.2kAutomated safety check: PassApache-2.0
Oma Scmfirst-fluke/oh-my-agent1.3k—~3.3kAutomated safety check: NotesMIT

Similar skills

  • Xray Pre Audit

    ccashwell/evm-cortex

    A skill your agent uses when preparing for a security audit, performing reconnaissance on a new codebase, or creating a protocol overview.

    131 GitHub stars~25k tokensUpdated 9 days ago
    SecurityAuto-check passed
  • Repo Sentinel

    Mathews-Tom/armory

    Full security audit for public repositories across 12 attack surfaces: git history, secrets, CI/CD, containers, dependencies, licenses.

    328 GitHub stars~2.2k tokensUpdated 3 days ago
    SecurityAuto-check passed
  • Security Ownership Map

    diegosouzapw/awesome-omni-skills

    Security Ownership Map workflow skill. An agent skill from diegosouzapw/awesome-omni-skills.

    159 GitHub stars~4.6k tokensUpdated 3 mo ago
    SecurityAuto-check passed
  • Hardening Windows Endpoint With Cis Benchmark

    mukul975/Anthropic-Cybersecurity-Skills

    Hardens Windows endpoints using CIS (Center for Internet Security) Benchmark recommendations to reduce attack surface, enforce security baselines, and meet compliance requirements.

    34k GitHub stars~2.2k tokensUpdated 1 mo ago
    SecurityAuto-check passed
  • Oma Scm

    first-fluke/oh-my-agent

    SCM (software configuration management) and Git — branching, merges, conflicts, worktrees, baselines, audit readiness, plus Conventional Commits and safe staging.

    1.3k GitHub stars~3.3k tokensUpdated today
    DevelopmentAuto-check: notes
  • Security

    austintgriffith/ethskills

    Solidity security patterns, common vulnerabilities, and pre-deploy audit checklist.

    295 GitHub stars~4.9k tokensUpdated 1 mo ago
    SecurityAuto-check passed

More from pashov/skills

  • Fizz

    pashov/skills

    Generate Echidna/Medusa-compatible Solidity fuzz suites from Foundry or Hardhat projects.

    1.2k GitHub starsUsed in 2 repos~11k tokens
    Auto-check passed
  • Fizz Convert

    pashov/skills

    Convert English-language properties in PROPERTIES.md (produced by the Fizz skill) into Solidity assertions inside the existing fuzz harness, then flip their checkboxes.

    1.2k GitHub starsUsed in 2 repos~3.7k tokens
    Auto-check passed
  • Fizz Sync

    pashov/skills

    Reconcile an existing Fizz harness with a changed source tree.

    1.2k GitHub starsUsed in 2 repos~3.9k tokens
    Auto-check passed
  • Solidity Auditor

    pashov/skills

    Security audit of Solidity code while you develop. An agent skill from pashov/skills.

    1.2k GitHub stars~9.9k tokensUpdated 4 days ago
    Auto-check passed

Works with

Questions about X Ray

What does X Ray do?

Generates an x-ray.md pre-audit report covering overview, enhanced threat model (protocol-type profiling, git-weighted attack surfaces, temporal risk analysis, composability dependency mapping)…. X Ray is an agent skill from pashov/skills.md pre-audit report covering overview, enhanced threat model (protocol-type profiling, git-weighted attack surfaces, temporal risk analysis, composability dependency mapping), invariants, integrations, docs quality, test analysis, and developer/git history.

When should I use X Ray?

X Ray fits situations like: audit readiness; readiness report; pre-audit report; prep this protocol.

How do I install X Ray in Claude Code?

Run `npx skills add pashov/skills --skill x-ray -a claude-code`. Or copy the skill folder (x-ray in pashov/skills) into .claude/skills/x-ray in your project. Claude Code loads it when a task matches its description.

How do I install X Ray in Codex?

Run `npx skills add pashov/skills --skill x-ray -a codex`. Or copy the skill folder (x-ray in pashov/skills) into .agents/skills/x-ray in your project. Codex loads it when a task matches its description.

Can I use X Ray in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add pashov/skills --skill x-ray -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/x-ray, .gemini/skills/x-ray, .github/skills/x-ray and .opencode/skills/x-ray in your project.

What does X Ray need to run?

Going by SKILL.md and its folder, X Ray needs Python and a shell for the scripts in its folder and the command-line tools its instructions call (python3, bash, npx, curl and npm). Our summary lists: Python 3; Node.js; A Bash shell.

Does X Ray access the network?

SKILL.md contains no URLs. Its commands use npx, curl and npm, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is X Ray safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.

What licence does X Ray use?

X Ray is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does X Ray use?

About 10k tokens (SKILL.md is roughly 40k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 24k tokens, read only when the agent opens those files.

What are the alternatives to X Ray?

Skills that share tags, products or a category with X Ray: Xray Pre Audit (ccashwell/evm-cortex, 131 stars), Repo Sentinel (Mathews-Tom/armory, 328 stars), Security Ownership Map (diegosouzapw/awesome-omni-skills, 159 stars) and Hardening Windows Endpoint With Cis Benchmark (mukul975/Anthropic-Cybersecurity-Skills, 34k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains X Ray?

pashov (a GitHub user) maintains it in pashov/skills, which has 1,234 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on October 5, 2026.

Source: pashov/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.