/** * pop org probe-access — burner-callStatic access-control probe for any contract. * * Codifies the static-analysis methodology vigil_01 used across HB#153-157 * to map the access-control gates of HybridVoting, Executor, EligibilityModule, * PaymentManager, and QuickJoin. The methodology: * * 1. For every external/payable function in the ABI: * 2. Construct a callStatic invocation with realistic-shape zero arguments * (zero address for `address`, 0 for `uint*`, false for `bool`, * empty bytes for `bytes`, empty arrays for `*[]`, etc). * 3. Spoof `from` to a fresh burner wallet address (no hats, no balances). * 4. Catch the revert. Decode it via Interface.parseError(). Record the * error name + selector + any args. * 5. Print a verification table: function name | error | likely access * control gate. * * Result: a contract's full external surface mapped to its access control * model in <5 minutes, zero gas, zero on-chain footprint. Two distinct * gating libraries handled by the same decode path (OpenZeppelin Ownable * via `OwnableUnauthorizedAccount`, custom errors via `NotSuperAdmin` / * `Unauthorized` / `OnlyMasterDeploy` / etc). * * Limitations explicitly handled and surfaced in --help: * - Functions that revert with `require(string)` instead of a custom * error fall through to the raw revert message. Still reportable. * - Some access checks happen mid-function after state reads, so a * well-shaped argument may pass the access gate and revert later on * a state check. Those are reported as "passed access check, reverted * downstream" and the command notes the function name. * - Reentrancy guards count as access controls for our purposes — a * `ReentrancyGuardReentrantCall` revert means the gate is sound. * - The probe never sends a transaction. It is read-only by construction. */ import type { Argv, ArgumentsCamelCase } from 'yargs'; import { ethers } from 'ethers'; interface ProbeAccessArgs { address: string; abi?: string; selectors?: string; chain?: number; rpc?: string; skipCodeCheck?: boolean; expectedName?: string; } /** * HB#385 task #390 — pre-probe identity check. * * HB#384 discovered that the HB#362 "Gitcoin Governor Bravo" audit was * actually probing Uniswap Governor Bravo — same address, wrong label. * The prevention rule documented in `reports/audits/corrections-hb384.md` * is "verify contract name() before probing." This helper makes that * check a first-class part of every probe run. * * Returns: * - contractName: the string returned by the target's `name()` accessor, * or null if the contract doesn't expose one / the call reverts * - nameCheck: when expectedName is supplied, a { expected, actual, match } * record. match is case-insensitive substring — "Compound" matches * "Compound Governor Bravo", "Uniswap" does not. * * Never throws. name() reverts are silently tolerated — plenty of * contracts don't expose name() and the check is best-effort. */ /** * Pure helper for the substring name-match logic. Exported for unit testing * without needing to mock an RPC provider. * * Match semantics (HB#387+, task #395): * 1. Case-insensitive substring of the raw expected string against the * on-chain `name()` return value. This is the original HB#385 behavior. * 2. If step 1 fails, consult LABEL_ALIASES: split the expected label into * words, look up aliases for each word, and accept a match if any alias * appears as a case-insensitive substring of the actual name. This * closes the HB#386 sweep false-positive class where "curve" doesn't * literally appear in "Vote-escrowed CRV". * * An empty expected string matches everything (String.includes semantic). * A null actual never matches. */ export declare function matchContractName(actual: string | null, expected: string): boolean; export declare function fetchContractNameAndCheck(provider: ethers.providers.JsonRpcProvider, address: string, expectedName: string | undefined): Promise<{ contractName: string | null; nameCheck: { expected: string; actual: string | null; match: boolean; } | null; }>; /** * HB#382 task #384 — probe-reliability heuristic. * * Detect contract patterns that make burner-callStatic probe results * unreliable BEFORE running the probe, so operators can interpret the * output correctly instead of treating a passed result as a security * finding. * * Two patterns detected: * * 1. **ds-auth** (Dappsys library, used by MakerDAO and many older * Ethereum contracts). The permission check is an EXTERNAL CALL to * a separate Authority contract, which is expensive. Contracts * economize by running cheap parameter validation first, so * burner-callStatic with default parameters hits early-return paths * BEFORE the Authority check. Detected by the presence of BOTH * `setUserRole(address,uint8,bool)` (0x67aff484) and * `setAuthority(address)` (0x7a9e5e4b) selectors in the runtime code. * See HB#379 Maker Chief audit for the empirical finding. * * 2. **Vyper compiler** (used by Curve, Yearn, and most veToken DAOs). * Vyper orders parameter loading + cheap parameter validation before * the `assert msg.sender == self.admin` statement in each function * body. Same symptom as ds-auth — default-parameter burner calls hit * early-returns before the permission check. Detected by the * presence of Curve's signature `commit_transfer_ownership(address)` * (0x6b441a40) + `apply_transfer_ownership()` (0x6a1c05ae) selectors, * which is the canonical 2-step Vyper admin transfer pattern. * See HB#380 Curve VotingEscrow + GaugeController audits. * * False positive tolerance: we'd rather flag one extra contract as * probe-limited than miss a real one. Operators can ignore the warning * if they know the contract uses inline modifiers despite matching the * selector pattern. * * False negative concern: any contract using ds-auth or Vyper WITHOUT * also exposing these selectors won't be detected. That's acceptable * because (a) ds-auth without setAuthority is unusual, and (b) Vyper * contracts without the transfer_ownership pattern are rare in DAO * governance. Both would require per-contract case-by-case analysis * anyway, which is outside the scope of the heuristic. */ export declare function detectProbeReliabilityPatterns(codeLower: string | null): { dsAuth: boolean; vyper: boolean; voteEscrow: boolean; warnings: string[]; }; export declare const probeAccessHandler: { builder: (yargs: Argv) => Argv<{ address: string; } & { abi: string | undefined; } & { selectors: string | undefined; } & { "skip-code-check": boolean; } & { "expected-name": string | undefined; }>; handler: (argv: ArgumentsCamelCase) => Promise; }; export {};