audit-system 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +351 -0
  3. package/agents/AGENT_REGISTRY.md +150 -0
  4. package/agents/assumption-analyzer.json +7 -0
  5. package/agents/assumption-analyzer.md +37 -0
  6. package/agents/composition-attacker.json +7 -0
  7. package/agents/composition-attacker.md +46 -0
  8. package/agents/economic-attacker.json +7 -0
  9. package/agents/economic-attacker.md +43 -0
  10. package/agents/exploit-writer.json +7 -0
  11. package/agents/exploit-writer.md +48 -0
  12. package/agents/orchestrator.json +16 -0
  13. package/agents/orchestrator.md +46 -0
  14. package/agents/report-writer.json +7 -0
  15. package/agents/report-writer.md +52 -0
  16. package/agents/state-machine-hacker.json +7 -0
  17. package/agents/state-machine-hacker.md +43 -0
  18. package/agents/test-generator.json +7 -0
  19. package/agents/test-generator.md +49 -0
  20. package/cli.js +93 -0
  21. package/config.json +74 -0
  22. package/lib/detect-lang.js +109 -0
  23. package/lib/install.js +229 -0
  24. package/lib/utils.js +41 -0
  25. package/obsidian-vault/README.md +103 -0
  26. package/obsidian-vault/attack-patterns/state-inconsistency.md +90 -0
  27. package/obsidian-vault/exploits/_index.md +109 -0
  28. package/obsidian-vault/exploits/beanstalk-2022.md +334 -0
  29. package/obsidian-vault/exploits/nomad-2022.md +295 -0
  30. package/obsidian-vault/exploits/ronin-2022.md +251 -0
  31. package/obsidian-vault/exploits/wormhole-2022.md +284 -0
  32. package/obsidian-vault/failed-hypotheses/_template.md +77 -0
  33. package/obsidian-vault/hypotheses/_template.md +43 -0
  34. package/obsidian-vault/hypotheses/bridge-protocol-template.md +254 -0
  35. package/obsidian-vault/hypotheses/dex-protocol-template.md +185 -0
  36. package/obsidian-vault/hypotheses/governance-protocol-template.md +263 -0
  37. package/obsidian-vault/hypotheses/lending-protocol-template.md +218 -0
  38. package/obsidian-vault/hypotheses/staking-protocol-template.md +223 -0
  39. package/obsidian-vault/invariant-catalog/defi-invariants.md +307 -0
  40. package/obsidian-vault/invariant-catalog/solana-invariants.md +213 -0
  41. package/obsidian-vault/novel-patterns/pattern-mutation-framework.md +316 -0
  42. package/obsidian-vault/reports/_template.md +92 -0
  43. package/obsidian-vault/research/cross-protocol-analysis/.gitkeep +0 -0
  44. package/obsidian-vault/research/emerging-threats/.gitkeep +0 -0
  45. package/obsidian-vault/research/protocol-specific/.gitkeep +0 -0
  46. package/obsidian-vault/test-strategies/fuzzing.md +75 -0
  47. package/obsidian-vault/vulnerabilities/access-control.md +122 -0
  48. package/obsidian-vault/vulnerabilities/flash-loan-attack.md +66 -0
  49. package/obsidian-vault/vulnerabilities/oracle-manipulation.md +135 -0
  50. package/obsidian-vault/vulnerabilities/reentrancy.md +141 -0
  51. package/obsidian-vault/vulnerabilities/rust-unsafe-deserialization.md +128 -0
  52. package/obsidian-vault/vulnerabilities/solana-account-confusion.md +125 -0
  53. package/obsidian-vault/vulnerabilities/solana-close-account.md +141 -0
  54. package/obsidian-vault/vulnerabilities/solana-cpi-attacks.md +131 -0
  55. package/obsidian-vault/vulnerabilities/solana-signer-authorization.md +119 -0
  56. package/package.json +56 -0
  57. package/skills/audit-connect.md +385 -0
  58. package/skills/auditor.md +280 -0
  59. package/skills/exploit-generator.md +394 -0
  60. package/skills/novel-discovery.md +551 -0
  61. package/skills/test-generator.md +511 -0
@@ -0,0 +1,511 @@
1
+ # Test Generator Skill
2
+
3
+ ## Role
4
+ Smart Contract Test Architect — generates comprehensive test suites covering edge cases, invariants, and fuzzing scenarios that surface hidden vulnerabilities.
5
+ - **Solidity:** Foundry (`forge test`)
6
+ - **Rust/Solana:** Anchor (`anchor test`) + Rust integration tests
7
+ - **Rust/ink!:** `cargo contract test`
8
+
9
+ ## Objective
10
+ Transform a contract into a full test suite that automatically discovers vulnerabilities through systematic coverage, fuzzing, and invariant checking.
11
+
12
+ ---
13
+
14
+ ## Test Categories
15
+
16
+ ### 1. Unit Tests — Function by Function
17
+ Test each function in isolation with boundary values.
18
+
19
+ ```
20
+ - Zero values
21
+ - Max uint256
22
+ - Zero address
23
+ - Empty arrays
24
+ - Single element arrays
25
+ - Caller = contract itself
26
+ ```
27
+
28
+ ### 2. Integration Tests — Function Sequences
29
+ Test realistic interaction sequences.
30
+
31
+ ```
32
+ - deposit → withdraw
33
+ - approve → transferFrom
34
+ - stake → unstake → claim
35
+ - mint → transfer → burn
36
+ ```
37
+
38
+ ### 3. Fuzz Tests — Random Input Discovery
39
+ Let Foundry's fuzzer find edge cases you can't imagine.
40
+
41
+ ```
42
+ - Fuzz all numerical inputs
43
+ - Fuzz addresses
44
+ - Fuzz sequences
45
+ - Fuzz amounts
46
+ ```
47
+
48
+ ### 4. Invariant Tests — Properties That Must Always Hold
49
+ Define mathematical truths the contract must never violate.
50
+
51
+ ```
52
+ - totalSupply = sum of all balances
53
+ - contract ETH balance >= total deposits
54
+ - no user balance exceeds totalSupply
55
+ - owner cannot be zero address
56
+ ```
57
+
58
+ ### 5. Stateful Tests — Sequence of Operations
59
+ Simulate realistic user journeys and look for inconsistencies.
60
+
61
+ ---
62
+
63
+ ## Foundry Test Templates
64
+
65
+ ### Fuzz Test Template
66
+ ```solidity
67
+ // SPDX-License-Identifier: MIT
68
+ pragma solidity ^0.8.0;
69
+
70
+ import "forge-std/Test.sol";
71
+ import "../src/TargetContract.sol";
72
+
73
+ contract FuzzTest is Test {
74
+ TargetContract target;
75
+
76
+ function setUp() public {
77
+ target = new TargetContract();
78
+ }
79
+
80
+ // Foundry automatically fuzzes the inputs
81
+ function testFuzz_deposit(uint256 amount) public {
82
+ // Bound to realistic values
83
+ amount = bound(amount, 1, 1_000_000 ether);
84
+
85
+ deal(address(this), amount);
86
+ target.deposit{value: amount}();
87
+
88
+ assertEq(target.balanceOf(address(this)), amount);
89
+ }
90
+
91
+ function testFuzz_withdrawDoesNotExceedDeposit(
92
+ uint256 depositAmount,
93
+ uint256 withdrawAmount
94
+ ) public {
95
+ depositAmount = bound(depositAmount, 1, 1000 ether);
96
+ withdrawAmount = bound(withdrawAmount, 1, depositAmount);
97
+
98
+ deal(address(this), depositAmount);
99
+ target.deposit{value: depositAmount}();
100
+ target.withdraw(withdrawAmount);
101
+
102
+ assertGe(target.balanceOf(address(this)), 0);
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### Invariant Test Template
108
+ ```solidity
109
+ contract InvariantTest is Test {
110
+ TargetContract target;
111
+ Handler handler;
112
+
113
+ function setUp() public {
114
+ target = new TargetContract();
115
+ handler = new Handler(target);
116
+
117
+ // Tell Foundry to use the handler for calls
118
+ targetContract(address(handler));
119
+ }
120
+
121
+ // This invariant is checked after EVERY sequence of handler calls
122
+ function invariant_balanceSolvency() public view {
123
+ assertGe(
124
+ address(target).balance,
125
+ target.totalDeposits(),
126
+ "Contract is insolvent!"
127
+ );
128
+ }
129
+
130
+ function invariant_totalSupplyMatchesSum() public view {
131
+ assertEq(
132
+ target.totalSupply(),
133
+ handler.sumOfAllBalances(),
134
+ "Total supply mismatch!"
135
+ );
136
+ }
137
+ }
138
+
139
+ contract Handler is Test {
140
+ TargetContract target;
141
+ address[] public actors;
142
+ uint256 public sumDeposited;
143
+
144
+ constructor(TargetContract _target) {
145
+ target = _target;
146
+ // Create test actors
147
+ for (uint i = 0; i < 3; i++) {
148
+ actors.push(makeAddr(string(abi.encodePacked("actor", i))));
149
+ }
150
+ }
151
+
152
+ function deposit(uint256 actorSeed, uint256 amount) external {
153
+ address actor = actors[actorSeed % actors.length];
154
+ amount = bound(amount, 1, 100 ether);
155
+
156
+ deal(actor, amount);
157
+ vm.prank(actor);
158
+ target.deposit{value: amount}();
159
+ sumDeposited += amount;
160
+ }
161
+
162
+ function withdraw(uint256 actorSeed, uint256 amount) external {
163
+ address actor = actors[actorSeed % actors.length];
164
+ uint256 balance = target.balanceOf(actor);
165
+ if (balance == 0) return;
166
+
167
+ amount = bound(amount, 1, balance);
168
+ vm.prank(actor);
169
+ target.withdraw(amount);
170
+ }
171
+
172
+ function sumOfAllBalances() external view returns (uint256 sum) {
173
+ for (uint i = 0; i < actors.length; i++) {
174
+ sum += target.balanceOf(actors[i]);
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ ### State Transition Test Template
181
+ ```solidity
182
+ contract StateTransitionTest is Test {
183
+ TargetContract target;
184
+
185
+ enum State { Idle, Active, Paused, Finalized }
186
+
187
+ function test_invalidStateTransition() public {
188
+ // Try to move to invalid state
189
+ vm.expectRevert();
190
+ target.finalize(); // Should fail if not active
191
+ }
192
+
193
+ function test_stateSequence() public {
194
+ assertEq(uint(target.state()), uint(State.Idle));
195
+
196
+ target.activate();
197
+ assertEq(uint(target.state()), uint(State.Active));
198
+
199
+ target.pause();
200
+ assertEq(uint(target.state()), uint(State.Paused));
201
+
202
+ target.resume();
203
+ assertEq(uint(target.state()), uint(State.Active));
204
+
205
+ target.finalize();
206
+ assertEq(uint(target.state()), uint(State.Finalized));
207
+
208
+ // Cannot go back
209
+ vm.expectRevert();
210
+ target.activate();
211
+ }
212
+ }
213
+ ```
214
+
215
+ ### Boundary Value Test Template
216
+ ```solidity
217
+ contract BoundaryTest is Test {
218
+ TargetContract target;
219
+
220
+ function test_zeroAmount() public {
221
+ vm.expectRevert("Amount must be > 0");
222
+ target.deposit{value: 0}();
223
+ }
224
+
225
+ function test_maxUint() public {
226
+ // Test with maximum possible value
227
+ uint256 maxVal = type(uint256).max;
228
+ // Should either work correctly or revert cleanly (no overflow)
229
+ try target.someFunction(maxVal) {
230
+ // Check result is correct
231
+ } catch {
232
+ // Revert is acceptable, silent overflow is NOT
233
+ }
234
+ }
235
+
236
+ function test_zeroAddress() public {
237
+ vm.expectRevert();
238
+ target.transfer(address(0), 100);
239
+ }
240
+ }
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Rust/Solana Test Templates
246
+
247
+ ### Anchor TypeScript Test Template
248
+
249
+ ```typescript
250
+ import * as anchor from "@coral-xyz/anchor";
251
+ import { Program } from "@coral-xyz/anchor";
252
+ import { expect } from "chai";
253
+ import { MyProgram } from "../target/types/my_program";
254
+
255
+ describe("my-program", () => {
256
+ anchor.setProvider(anchor.AnchorProvider.env());
257
+ const program = anchor.workspace.MyProgram as Program<MyProgram>;
258
+
259
+ it("Is initialized!", async () => {
260
+ const tx = await program.methods.initialize().rpc();
261
+ console.log("Transaction signature:", tx);
262
+ });
263
+
264
+ it("Deposit and withdraw", async () => {
265
+ const user = anchor.web3.Keypair.generate();
266
+ const amount = new anchor.BN(1_000_000);
267
+
268
+ // Airdrop
269
+ await anchor.getProvider().connection.confirmTransaction(
270
+ await anchor.getProvider().connection.requestAirdrop(
271
+ user.publicKey,
272
+ 10 * anchor.web3.LAMPORTS_PER_SOL
273
+ )
274
+ );
275
+
276
+ // Deposit
277
+ await program.methods
278
+ .deposit(amount)
279
+ .accounts({ user: user.publicKey })
280
+ .signers([user])
281
+ .rpc();
282
+
283
+ // Withdraw
284
+ await program.methods
285
+ .withdraw(amount)
286
+ .accounts({ user: user.publicKey })
287
+ .signers([user])
288
+ .rpc();
289
+ });
290
+
291
+ it("Cannot withdraw more than deposited", async () => {
292
+ const user = anchor.web3.Keypair.generate();
293
+ const depositAmount = new anchor.BN(500_000);
294
+ const withdrawAmount = new anchor.BN(1_000_000);
295
+
296
+ // Airdrop and deposit
297
+ await anchor.getProvider().connection.confirmTransaction(
298
+ await anchor.getProvider().connection.requestAirdrop(
299
+ user.publicKey,
300
+ 10 * anchor.web3.LAMPORTS_PER_SOL
301
+ )
302
+ );
303
+ await program.methods.deposit(depositAmount).accounts({ user: user.publicKey }).signers([user]).rpc();
304
+
305
+ // Attempt to withdraw more — should fail
306
+ try {
307
+ await program.methods.withdraw(withdrawAmount).accounts({ user: user.publicKey }).signers([user]).rpc();
308
+ expect.fail("Should have thrown");
309
+ } catch (e) {
310
+ expect(e).to.be.an("error");
311
+ }
312
+ });
313
+ });
314
+ ```
315
+
316
+ ### Rust Integration Test Template (Solana)
317
+
318
+ ```rust
319
+ use solana_program_test::*;
320
+ use solana_sdk::{
321
+ signature::{Keypair, Signer},
322
+ transaction::Transaction,
323
+ pubkey::Pubkey,
324
+ };
325
+ use anchor_lang::AccountDeserialize;
326
+
327
+ #[tokio::test]
328
+ async fn test_exploit_prevention() {
329
+ let program_id = Pubkey::new_unique();
330
+ let mut program_test = ProgramTest::new(
331
+ "my_program",
332
+ program_id,
333
+ processor!(my_program::entry),
334
+ );
335
+
336
+ let (mut banks_client, payer, recent_blockhash) =
337
+ program_test.start().await;
338
+
339
+ let user = Keypair::new();
340
+ let amount = 1_000_000u64;
341
+
342
+ // Create transaction
343
+ let mut tx = Transaction::new_signed_with_payer(
344
+ &[/* instructions */],
345
+ Some(&payer.pubkey()),
346
+ &[&payer, &user],
347
+ recent_blockhash,
348
+ );
349
+
350
+ // Execute
351
+ let result = banks_client.process_transaction(tx).await;
352
+ assert!(result.is_ok());
353
+ }
354
+ ```
355
+
356
+ ### Fuzz Testing Template (Rust/Solana — Trident)
357
+
358
+ ```rust
359
+ use trident_client::fuzzing::*;
360
+ use my_program::*;
361
+
362
+ #[derive(Arbitrary, Debug, Clone)]
363
+ struct MyFuzzInstruction {
364
+ amount: u64,
365
+ user_index: u8,
366
+ }
367
+
368
+ impl FuzzTestExecutor for MyFuzzInstruction {
369
+ type AccountStorage = MyAccounts;
370
+
371
+ fn execute(
372
+ &self,
373
+ accounts: &mut Self::AccountStorage,
374
+ _runtime: &mut FuzzingRuntime,
375
+ ) -> Result<(), FuzzError> {
376
+ let user = accounts.users[self.user_index as usize % accounts.users.len()];
377
+
378
+ let ix = my_program::instruction::deposit(
379
+ user.pubkey(),
380
+ self.amount,
381
+ );
382
+
383
+ let tx = Transaction::new_signed_with_payer(
384
+ &[ix],
385
+ Some(&user.pubkey()),
386
+ &[user],
387
+ accounts.recent_blockhash,
388
+ );
389
+
390
+ let result = accounts.banks_client.process_transaction(tx);
391
+
392
+ // Invariant: total deposits should never overflow
393
+ assert!(result.is_ok() || result.unwrap_err().to_string().contains("overflow"));
394
+
395
+ Ok(())
396
+ }
397
+ }
398
+ ```
399
+
400
+ ### ink!/Substrate Test Template
401
+
402
+ ```rust
403
+ #[ink::test]
404
+ fn test_transfer_works() {
405
+ let mut contract = MyContract::new(1000);
406
+ assert_eq!(contract.total_supply(), 1000);
407
+
408
+ contract.transfer(AccountId::from([0x02; 32]), 500);
409
+ assert_eq!(contract.balance_of(AccountId::from([0x01; 32])), 500);
410
+ assert_eq!(contract.balance_of(AccountId::from([0x02; 32])), 500);
411
+ }
412
+
413
+ #[ink::test]
414
+ #[should_panic(expected = "insufficient balance")]
415
+ fn test_transfer_fails_insufficient_balance() {
416
+ let mut contract = MyContract::new(100);
417
+ contract.transfer(AccountId::from([0x02; 32]), 200);
418
+ }
419
+ ```
420
+
421
+ ---
422
+
423
+ ## Prompt Templates
424
+
425
+ ### Generate Full Test Suite (Solidity/Foundry)
426
+ ```
427
+ You are a Foundry test architect.
428
+
429
+ Contract: [PASTE CONTRACT]
430
+
431
+ Generate a comprehensive test suite including:
432
+ 1. Unit tests for every public/external function
433
+ 2. Fuzz tests for all numerical inputs
434
+ 3. Invariant tests defining 3-5 critical properties
435
+ 4. State transition tests
436
+ 5. Boundary tests (0, max, zero address)
437
+ 6. Integration tests for realistic user flows
438
+
439
+ Format as a complete Foundry test file ready to run.
440
+ ```
441
+
442
+ ### Generate Full Test Suite (Rust/Solana/Anchor)
443
+ ```
444
+ You are an Anchor/Solana test architect using mocha+chai TypeScript tests or Rust integration tests.
445
+
446
+ Program: [PASTE RUST PROGRAM or IDL]
447
+
448
+ Generate a comprehensive test suite including:
449
+ 1. Unit tests for every instruction handler
450
+ 2. Account validation tests (wrong accounts, missing signers)
451
+ 3. Fuzz-like parameter bound testing
452
+ 4. State invariant checks (balance sums, authority checks)
453
+ 5. CPI integration tests
454
+ 6. Edge case tests (zero amounts, max values, close+reinit)
455
+
456
+ Format as complete `describe/it` test blocks ready to run with `anchor test`.
457
+ ```
458
+
459
+ ### Generate Invariants Only
460
+ ```
461
+ Analyze this contract and identify:
462
+ 1. All state invariants that must ALWAYS hold
463
+ 2. Economic invariants (solvency, supply)
464
+ 3. Access invariants (ownership, roles)
465
+ 4. Logic invariants (sequence, state machine)
466
+
467
+ For each invariant write:
468
+ - Natural language description
469
+ - Foundry invariant_ test function
470
+
471
+ Contract: [PASTE CONTRACT]
472
+ ```
473
+
474
+ ### Generate Fuzz Targets
475
+ ```
476
+ Identify the 5 most valuable functions to fuzz in this contract.
477
+ For each, write a Foundry fuzz test that:
478
+ 1. Bounds inputs to realistic ranges
479
+ 2. Tests for correct behavior
480
+ 3. Checks no funds are lost
481
+ 4. Verifies no invariants broken
482
+
483
+ Contract: [PASTE CONTRACT]
484
+ ```
485
+
486
+ ---
487
+
488
+ ## Foundry Commands Reference
489
+
490
+ ```bash
491
+ # Run all tests
492
+ forge test
493
+
494
+ # Run with verbosity (see logs)
495
+ forge test -vvvv
496
+
497
+ # Run specific test
498
+ forge test --match-test test_exploit
499
+
500
+ # Run fuzz tests (more runs = better coverage)
501
+ forge test --fuzz-runs 10000
502
+
503
+ # Run invariant tests
504
+ forge test --match-contract InvariantTest
505
+
506
+ # Coverage report
507
+ forge coverage
508
+
509
+ # Gas snapshot
510
+ forge snapshot
511
+ ```