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.
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)…
$ npx skills add pashov/skills --skill x-ray -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install pashov/skills x-ray --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ 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-srcUse ~/.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/
Install the "x-ray" agent skill from https://github.com/pashov/skills/tree/main/x-ray into .claude/skills/x-ray/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "x-ray", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/pashov/skills/tree/main/x-rayType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add pashov/skills --skill x-ray -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install pashov/skills x-ray --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pashov/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/x-ray .agents/skills/x-ray && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "x-ray" agent skill from https://github.com/pashov/skills/tree/main/x-ray into .agents/skills/x-ray/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "x-ray", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pashov/skills --skill x-ray -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install pashov/skills x-ray --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pashov/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/x-ray .cursor/skills/x-ray && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "x-ray" agent skill from https://github.com/pashov/skills/tree/main/x-ray into .cursor/skills/x-ray/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "x-ray", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/pashov/skills.git --path x-ray--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add pashov/skills --skill x-ray -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install pashov/skills x-ray --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pashov/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/x-ray .gemini/skills/x-ray && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "x-ray" agent skill from https://github.com/pashov/skills/tree/main/x-ray into .gemini/skills/x-ray/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "x-ray", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install pashov/skills x-rayInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add pashov/skills --skill x-ray -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/pashov/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/x-ray .github/skills/x-ray && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "x-ray" agent skill from https://github.com/pashov/skills/tree/main/x-ray into .github/skills/x-ray/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "x-ray", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pashov/skills --skill x-ray -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install pashov/skills x-ray --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pashov/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/x-ray .opencode/skills/x-ray && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "x-ray" agent skill from https://github.com/pashov/skills/tree/main/x-ray into .opencode/skills/x-ray/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "x-ray", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
x-rayGenerates 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. 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.
3 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 92b0ea6. It shows what the files ask for, not the result of running them.
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.
Ships 3 files in scripts/ (Python and Shell), which the agent can run.
Shell commands in SKILL.md call:
python3bashnpxcurlnpmFrom the folder's file list and the shell code blocks in SKILL.md.
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.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
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.
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.
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.
The full file from pashov/skills at commit 92b0ea6, republished under its MIT licence (© pashov). 4,758 words, ~10,032 tokens.
.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.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).
Before doing anything else, call TodoWrite with these 3 todos (all pending):
Phase 1: Enumerate & measure codebasePhase 2: Read sources, classify entry points, synthesize invariantsPhase 3: Write x-ray report filesTransitions (update via TodoWrite — never batch):
in_progress immediately, before running enumerate.sh.completed and Phase 2 in_progress.completed and Phase 3 in_progress.completed.Rule: exactly one todo is in_progress at any time. Status updates happen the moment a phase starts or ends.
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):
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):
VERSION file from $SKILL_DIR/VERSIONcurl -sf https://raw.githubusercontent.com/pashov/skills/main/x-ray/VERSION⚠️ 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:
cd [project-root] && forge coverage 2>&1 || (echo "RETRYING_WITH_IR_MINIMUM" && forge coverage --ir-minimum 2>&1)For Hardhat:
cd [project-root] && npx hardhat coverage 2>&1If 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):
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.jsonThe 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 guide4. 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:
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.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.
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).
interfaces/ dirs or filenames I + uppercase letterOne 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).
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()`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:
# 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:]_]|$)'# 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.
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:
require(msg.sender == X) or if (msg.sender != X) revert ...if (msg.sender != X || ...) revert ... (compound conditions)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.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.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:
(user-controlled), (user-signed), (keeper-provided), (protocol-derived)→ Contract.fn() → Contract.fn()in (tokens deposited), out (tokens withdrawn), noneThis data feeds TWO outputs:
x-ray.md (Section 2) — use the permissionless subset onlyUsing 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):
require statements and state variable checks◄── annotationsOutput: 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.
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:
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.
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.):
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.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:
whenNotPaused (or equivalent) is appliedAnti-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.
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).
Use the exact nSLOC TOTAL from the Step 1 enumerate output (no ~ prefix) in the report header and scope table.
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):
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].
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.mapping[from] -= e, mapping[to] += e with no scalar change), confirm the mapping sum is self-conserving.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?"
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: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.
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).
State machine / one-shot scan: For each enum/uint/address variable in require(var == X); ... var = Y patterns, record the transition. Distinguish:
require(var == default); var = concrete with no path back (e.g., setStrategy, setLeverager).require(var == false); var = true but another function flips it back (e.g., freeze/unFreeze, toggleVaultLeverage). NOT a state machine invariant — skip.false → true → false driven by timing/condition (e.g., ongoingVestingPosition). Record as a cycle invariant.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).
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.
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.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):
@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.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).
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:
npm install / forge install not run)Rules:
test_files/test_functions from Step 1 enumeration for ALL test existence claims. Never infer "no tests" from coverage tool failure."[N] test files with [M] test functions detected; coverage metrics unavailable — [failure reason]".test_co_change_rate from git analysis measures file co-modification in commits, not coverage — qualify it as such.Check forge coverage status: include results if done, failure reason if failed, "pending" if still running. Do NOT wait.
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** [[X-4](invariants.md#x-4), [I-17](invariants.md#i-17)] — .... 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):
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).
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:
[branch] at [commit]".squashed_import (1 commit), there is no meaningful evolution to describe — state this and skip fix/hotspot analysis.python3 $SKILL_DIR/scripts/generate_svg.py x-ray/architecture.json x-ray/architecture.svgThen 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).
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.
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
SKILL.md and 7 other files (scripts, references) in x-ray of pashov/skills.
Open the folder on GitHubat commit 92b0ea6
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.
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.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| X Ray this skillpashov/skills | 1.2k | 1 repos | ~10k | Automated safety check: Pass | MIT | |
| Xray Pre Auditccashwell/evm-cortex | 131 | — | ~25k | Automated safety check: Pass | MIT | |
| Repo SentinelMathews-Tom/armory | 328 | — | ~2.2k | Automated safety check: Pass | MIT | |
| Security Ownership Mapdiegosouzapw/awesome-omni-skills | 159 | — | ~4.6k | Automated safety check: Pass | Apache-2.0 | |
| Hardening Windows Endpoint With Cis Benchmarkmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~2.2k | Automated safety check: Pass | Apache-2.0 | |
| Oma Scmfirst-fluke/oh-my-agent | 1.3k | — | ~3.3k | Automated safety check: Notes | MIT |
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.
Mathews-Tom/armory
Full security audit for public repositories across 12 attack surfaces: git history, secrets, CI/CD, containers, dependencies, licenses.
diegosouzapw/awesome-omni-skills
Security Ownership Map workflow skill. An agent skill from diegosouzapw/awesome-omni-skills.
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.
first-fluke/oh-my-agent
SCM (software configuration management) and Git — branching, merges, conflicts, worktrees, baselines, audit readiness, plus Conventional Commits and safe staging.
austintgriffith/ethskills
Solidity security patterns, common vulnerabilities, and pre-deploy audit checklist.
pashov/skills
Generate Echidna/Medusa-compatible Solidity fuzz suites from Foundry or Hardhat projects.
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.
pashov/skills
Reconcile an existing Fizz harness with a changed source tree.
pashov/skills
Security audit of Solidity code while you develop. An agent skill from pashov/skills.
Works with
Categories
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.
X Ray fits situations like: audit readiness; readiness report; pre-audit report; prep this protocol.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.