Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .deepsec/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules/
.env*.local

# Scan output — regenerated by `deepsec scan` / `process`. INFO.md
# and SETUP.md (manually edited) stay tracked.
data/*/files/
data/*/runs/
data/*/reports/
data/*/project.json
data/*/tech.json
23 changes: 23 additions & 0 deletions .deepsec/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Agent setup

This is a deepsec scanning workspace. Each registered project has its
own setup prompt at `data/<id>/SETUP.md` — open the relevant one when
asked to set a project up.

## Common tasks

- **Set up a project for scanning**: read `data/<id>/SETUP.md` and
follow it (read `node_modules/deepsec/SKILL.md`, then fill
`data/<id>/INFO.md` from the target codebase).
- **Add a new project**: run `deepsec init-project <root>` — it
scaffolds `data/<id>/` and prints/writes the setup prompt for the
new project.
- **Write a custom matcher** (only after a real true-positive shows you
a pattern worth keeping): read
`node_modules/deepsec/dist/docs/writing-matchers.md`.

## Reference

The deepsec skill is at `node_modules/deepsec/SKILL.md` (after
`pnpm install`). The full docs ship at
`node_modules/deepsec/dist/docs/`.
74 changes: 74 additions & 0 deletions .deepsec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# deepsec

This directory holds the [deepsec](https://www.npmjs.com/package/deepsec)
config for the parent repo. Checked into git so teammates inherit
project context (auth shape, threat model, custom matchers); generated
scan output is gitignored.

Currently configured project: `aavegotchi-geist` (target: `..`).

## Setup

1. `pnpm install` — installs deepsec.
2. Add an AI Gateway / Anthropic / OpenAI token to `.env.local`. If
you already have `claude` or `codex` CLI logged in on this
machine, you can skip the token for non-sandbox runs (`process` /
`revalidate` / `triage`); deepsec auto-detects and reuses the
subscription. See
`node_modules/deepsec/dist/docs/vercel-setup.md` after install.
3. Open the parent repo in your coding agent (Claude Code, Cursor, …)
and have it follow `data/aavegotchi-geist/SETUP.md` to fill in
`data/aavegotchi-geist/INFO.md`.

## Daily commands

```bash
pnpm deepsec scan
pnpm deepsec process --concurrency 5
pnpm deepsec revalidate --concurrency 5 # cuts FP rate
pnpm deepsec export --format md-dir --out ./findings
```

`--project-id` is auto-resolved while there's only one project in
`deepsec.config.ts`. Once you've added a second project, pass
`--project-id aavegotchi-geist` (or whichever id you want) explicitly.

`scan` is free (regex only). `process` is the AI stage (≈$0.30/file
on Opus by default). Run state goes to `data/aavegotchi-geist/`.

## Adding another project

To scan another codebase from this same `.deepsec/`:

```bash
pnpm deepsec init-project ../some-other-package # path relative to .deepsec/
```

Appends an entry to `deepsec.config.ts` and writes
`data/<id>/{INFO.md,SETUP.md,project.json}`. Open the new SETUP.md
in your agent to fill in INFO.md.

## Layout

```
deepsec.config.ts Project list (one entry per scanned repo)
data/aavegotchi-geist/
INFO.md Repo context — checked into git, hand-curated
SETUP.md Agent setup prompt — checked in, deletable
project.json Generated (gitignored)
files/ One JSON per scanned source file (gitignored)
runs/ Run metadata (gitignored)
reports/ Generated markdown reports (gitignored)
AGENTS.md Pointer for coding agents
.env.local Tokens (gitignored)
```

## Docs

After `pnpm install`:

- Skill: `node_modules/deepsec/SKILL.md`
- Full docs: `node_modules/deepsec/dist/docs/{getting-started,configuration,models,writing-matchers,plugins,architecture,data-layout,vercel-setup,faq}.md`

Or browse on
[GitHub](https://github.com/vercel/deepsec/tree/main/docs).
33 changes: 33 additions & 0 deletions .deepsec/data/aavegotchi-geist/INFO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# aavegotchi-geist

## What this codebase does

Solidity/TypeScript contracts for Aavegotchi on Base, centered on EIP-2535 diamonds. The main Aavegotchi diamond delegates selector calls into facets under `contracts/Aavegotchi/facets/` and stores protocol state in a single slot-0 `AppStorage`; related contracts include GHST/WGHST, wearables/forge surfaces, escrow contracts, Chainlink/Proof-of-Play VRF adapters, Hardhat tasks, and deployment/migration scripts.

## Auth shape

- Diamond upgrade and ownership paths use `LibDiamond.enforceIsContractOwner()` and `LibDiamond.contractOwner()`.
- Most Aavegotchi facets inherit `Modifiers` from `LibAppStorage`; important guards include `onlyOwner`, `onlyDao`, `onlyDaoOrOwner`, `onlyOwnerOrDaoOrGameManager`, `onlyItemManager`, `onlyOwnerOrItemManager`, `onlyPeriphery`, `onlyBaseRelayer`, and `whenNotPaused`.
- Meta-transaction-aware user flows should usually use `LibMeta.msgSender()` rather than raw `msg.sender`; `MetaTransactionsFacet.executeMetaTransaction` appends the signed user address to calldata.
- Token ownership and operator checks are split across ERC721/ERC1155 helpers, marketplace libraries, `s.operators`, `s.petOperators`, escrow ownership, and whitelist/buy-order state.
- Operational scripts and Hardhat network config read secrets/RPC/private-key material from `.env`; they must not broadcast to live networks unless intentionally invoked with a network and funded credentials.

## Threat model

Highest-impact attacks are unauthorized diamond cuts, owner/DAO/item-manager/game-manager privilege escalation, or delegatecall/init abuse that changes shared diamond state. Next are theft or locking of GHST, collateral, ERC721 gotchis/portals, ERC1155 wearables, marketplace proceeds, escrow assets, or reward/XP allocations. Also important: replay or spoofing in meta-transactions/signatures, bypassing pause/relayer restrictions, stale/misrouted VRF fulfillment, and scripts/tasks accidentally sending privileged live-network transactions.

## Project-specific patterns to flag

- Facet write functions that mutate `AppStorage` without the expected `Modifiers` guard or without a local ownership/operator check.
- Any storage-layout changes to `AppStorage` that reorder, remove, or type-change existing fields rather than append new fields at the end.
- Permissioned paths that use raw `msg.sender` where meta-tx-compatible `LibMeta.msgSender()` is required, or `LibMeta.msgSender()` where the direct caller must be enforced.
- Diamond cut/init flows that allow untrusted `_init`, `_calldata`, facet addresses, selector replacement, or delegatecall side effects.
- Marketplace, lending, escrow, bridge, and VRF flows where token transfer order, escrow ownership transfer, whitelist checks, fee splits, or callback authorization can be bypassed or griefed.

## Known false-positives

- `contracts/Aavegotchi/Diamond.sol` fallback delegatecall is the intended EIP-2535 dispatch path; risk depends on selector/facet registration and upgrade authorization.
- `contracts/shared/libraries/LibDiamond.sol` uses inline assembly and delegatecall for standard diamond storage and initialization mechanics.
- `contracts/Aavegotchi/facets/MetaTransactionsFacet.sol` intentionally calls `address(this).call(abi.encodePacked(functionSignature, userAddress))` after signature and nonce validation.
- `contracts/WGHST/TestVault.sol`, test fixtures, mocks, and scripts may look custodial or privileged but are not production protocol entry points unless deployed intentionally.
- Hardhat tasks under `tasks/` and scripts under `scripts/` may reference live networks and private-key env vars; treat as operational risk, not on-chain vulnerability, unless a task can be triggered by untrusted users.
55 changes: 55 additions & 0 deletions .deepsec/data/aavegotchi-geist/SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Agent setup for `aavegotchi-geist`

This is a deepsec scanning workspace. Project `aavegotchi-geist` was just registered
(target: `..`). Setup is incomplete — `data/aavegotchi-geist/INFO.md`
still has placeholder sections.

## What to do

1. **Read the deepsec skill.** After `pnpm install`, the file is at
`node_modules/deepsec/SKILL.md`. It maps every doc topic to a file
under `node_modules/deepsec/dist/docs/`. Read `getting-started.md`,
`configuration.md`, and `writing-matchers.md` (skim the rest).

2. **Fill in `data/aavegotchi-geist/INFO.md`.** It's auto-injected into the AI
prompt for every batch — keep it short and selective.

**Length budget: 50–100 lines total.** Verbose context dilutes
signal in the scanner's prompt window. The goal is "what would a
reviewer miss if they didn't read this?", not exhaustive enumeration.

**Per-section rubric**:
- Pick 3–5 representative items per section. **Don't list every
file, helper, or callsite** — pick the patterns.
- Name primitives by their public name (e.g. `withAuthentication`,
`auth.can()`, `isTeamAdmin`). **No line numbers.** Don't enumerate
more than 5 paths in any list.
- Skip generic CWE categories — built-in matchers already cover
"SSRF", "SQL injection", "XSS". Cover what's *project-specific*:
internal auth helpers, custom middleware names, fork-specific
stubs, intended-public endpoints.
- One short paragraph or 3–5 short bullets per section. Not both.

Source material (read in this order, stop when you have enough):
- `../README.md`
- any `AGENTS.md` / `CLAUDE.md` in `..`
- `../package.json` (or `go.mod`, `pyproject.toml`, etc.)
- 5–10 representative code files (entry points, auth helpers) — not
a full code tour.

3. **(Optional) Add custom matchers** for repo-specific patterns the
built-in matchers won't catch. Read
`node_modules/deepsec/dist/docs/writing-matchers.md` first; the
workflow there starts from a confirmed finding and grows the matcher
from it. Don't add matchers speculatively — wait for a real TP.

## When you're done

The user will run:

```bash
pnpm deepsec scan --project-id aavegotchi-geist
pnpm deepsec process --project-id aavegotchi-geist
```

You can delete this file once setup is complete.
29 changes: 29 additions & 0 deletions .deepsec/deepsec.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { defineConfig, type DeepsecPlugin } from "deepsec/config";
import { solidityDiamondCore } from "./matchers/solidity-diamond-core.js";
import { solidityFacetEntrypoint } from "./matchers/solidity-facet-entrypoint.js";
import { soliditySensitiveOperation } from "./matchers/solidity-sensitive-operation.js";

const aavegotchiPlugin: DeepsecPlugin = {
name: "aavegotchi-solidity",
matchers: [solidityDiamondCore, solidityFacetEntrypoint, soliditySensitiveOperation],
};

export default defineConfig({
defaultAgent: "codex",
projects: [
{
id: "aavegotchi-geist",
root: "..",
priorityPaths: [
"contracts/Aavegotchi/",
"contracts/shared/",
"contracts/GHST/",
"contracts/WGHST/",
"tasks/",
"scripts/",
],
},
// <deepsec:projects-insert-above>
],
plugins: [aavegotchiPlugin],
});
28 changes: 28 additions & 0 deletions .deepsec/matchers/solidity-diamond-core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { CandidateMatch, MatcherPlugin } from "deepsec/config";
import { regexMatcher } from "deepsec/config";

const SKIP_SOLIDITY = /(^|\/)(test|interfaces)\//;

export const solidityDiamondCore: MatcherPlugin = {
slug: "solidity-diamond-core",
description: "Diamond storage, selector routing, upgrade, and initialization primitives",
noiseTier: "normal",
filePatterns: ["contracts/**/*.sol"],
match(content, filePath): CandidateMatch[] {
if (SKIP_SOLIDITY.test(filePath)) return [];

return regexMatcher(
"solidity-diamond-core",
[
{ regex: /\bstruct\s+AppStorage\b/, label: "slot-0 AppStorage schema" },
{ regex: /\bcontract\s+Modifiers\b/, label: "Aavegotchi modifier/auth surface" },
{ regex: /\bDIAMOND_STORAGE_POSITION\b/, label: "diamond storage position" },
{ regex: /\bdiamondStorage\s*\(/, label: "manual diamond storage accessor" },
{ regex: /\bdiamondCut\s*\(/, label: "diamond cut selector mutation" },
{ regex: /\bsetContractOwner\s*\(/, label: "diamond owner mutation" },
{ regex: /\bdelegatecall\s*\(/, label: "delegatecall initialization or dispatch" },
],
content,
);
},
};
40 changes: 40 additions & 0 deletions .deepsec/matchers/solidity-facet-entrypoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { CandidateMatch, MatcherPlugin } from "deepsec/config";

const ENTRYPOINT_RE = /\bfunction\s+\w+\s*\([^;{]*\)\s*(?:external|public)\b/;

export const solidityFacetEntrypoint: MatcherPlugin = {
slug: "solidity-facet-entrypoint",
description: "Production Solidity files exposing public/external protocol entrypoints",
noiseTier: "noisy",
filePatterns: [
"contracts/Aavegotchi/facets/*.sol",
"contracts/Aavegotchi/ForgeDiamond/facets/*.sol",
"contracts/Aavegotchi/WearableDiamond/facets/*.sol",
"contracts/Aavegotchi/vrf/*.sol",
"contracts/GHST/facets/*.sol",
"contracts/WGHST/*.sol",
"contracts/periphery/*.sol",
"contracts/raffle/*.sol",
"contracts/shared/facets/*.sol",
],
match(content): CandidateMatch[] {
const lines = content.split("\n");

for (let i = 0; i < lines.length; i++) {
if (!ENTRYPOINT_RE.test(lines[i])) continue;

const start = Math.max(0, i - 3);
const end = Math.min(lines.length, i + 10);
return [
{
vulnSlug: "solidity-facet-entrypoint",
lineNumbers: [i + 1],
snippet: lines.slice(start, end).join("\n"),
matchedPattern: "production Solidity file with public/external entrypoint",
},
];
}

return [];
},
};
29 changes: 29 additions & 0 deletions .deepsec/matchers/solidity-sensitive-operation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { CandidateMatch, MatcherPlugin } from "deepsec/config";
import { regexMatcher } from "deepsec/config";

const SKIP_SOLIDITY = /(^|\/)(test|interfaces)\//;

export const soliditySensitiveOperation: MatcherPlugin = {
slug: "solidity-sensitive-operation",
description: "Solidity auth, meta-transaction, token-flow, callback, and privileged-state primitives",
noiseTier: "normal",
filePatterns: ["contracts/**/*.sol"],
match(content, filePath): CandidateMatch[] {
if (SKIP_SOLIDITY.test(filePath)) return [];

return regexMatcher(
"solidity-sensitive-operation",
[
{ regex: /\bLibMeta\.msgSender\s*\(/, label: "meta-transaction-aware sender" },
{ regex: /\bmsg\.sender\b/, label: "raw caller identity" },
{ regex: /\becrecover\s*\(/, label: "signature recovery" },
{ regex: /\.(?:call|delegatecall)\s*(?:\{|\()/, label: "low-level external call" },
{ regex: /\b(?:safeTransferFrom|transferFrom)\s*\(/, label: "token transfer from another account" },
{ regex: /\b(?:safeTransfer|transfer)\s*\(/, label: "token transfer out" },
{ regex: /\bs\.(?:dao|daoTreasury|daoDirectorTreasury|itemManagers|gameManagers|baseRelayer|relayerPetter|VRFSystem|diamondPaused)\b/, label: "privileged config state" },
{ regex: /\bs\.(?:aavegotchis|erc721Listings|erc1155Listings|gotchiLendings|erc721BuyOrders|erc1155BuyOrders|whitelists)\b/, label: "funds or ownership accounting state" },
],
content,
);
},
};
12 changes: 12 additions & 0 deletions .deepsec/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "deepsec-workspace",
"version": "0.1.0",
"private": true,
"description": "deepsec scanning workspace",
"type": "module",
"workspaces": [],
"packageManager": "pnpm@9.15.4",
"dependencies": {
"deepsec": "^2.0.4"
}
}
Loading
Loading