crucible-mcp 0.5.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

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 (46) hide show
  1. crucible/cli.py +109 -2
  2. crucible/enforcement/bundled/error-handling.yaml +84 -0
  3. crucible/enforcement/bundled/security.yaml +123 -0
  4. crucible/enforcement/bundled/smart-contract.yaml +110 -0
  5. crucible/hooks/claudecode.py +388 -0
  6. crucible/hooks/precommit.py +117 -25
  7. crucible/knowledge/loader.py +186 -0
  8. crucible/knowledge/principles/API_DESIGN.md +176 -0
  9. crucible/knowledge/principles/COMMITS.md +127 -0
  10. crucible/knowledge/principles/DATABASE.md +138 -0
  11. crucible/knowledge/principles/DOCUMENTATION.md +201 -0
  12. crucible/knowledge/principles/ERROR_HANDLING.md +157 -0
  13. crucible/knowledge/principles/FP.md +162 -0
  14. crucible/knowledge/principles/GITIGNORE.md +218 -0
  15. crucible/knowledge/principles/OBSERVABILITY.md +147 -0
  16. crucible/knowledge/principles/PRECOMMIT.md +201 -0
  17. crucible/knowledge/principles/SECURITY.md +136 -0
  18. crucible/knowledge/principles/SMART_CONTRACT.md +153 -0
  19. crucible/knowledge/principles/SYSTEM_DESIGN.md +153 -0
  20. crucible/knowledge/principles/TESTING.md +129 -0
  21. crucible/knowledge/principles/TYPE_SAFETY.md +170 -0
  22. crucible/skills/accessibility-engineer/SKILL.md +71 -0
  23. crucible/skills/backend-engineer/SKILL.md +69 -0
  24. crucible/skills/customer-success/SKILL.md +69 -0
  25. crucible/skills/data-engineer/SKILL.md +70 -0
  26. crucible/skills/devops-engineer/SKILL.md +69 -0
  27. crucible/skills/fde-engineer/SKILL.md +69 -0
  28. crucible/skills/formal-verification/SKILL.md +86 -0
  29. crucible/skills/gas-optimizer/SKILL.md +89 -0
  30. crucible/skills/incident-responder/SKILL.md +91 -0
  31. crucible/skills/mev-researcher/SKILL.md +87 -0
  32. crucible/skills/mobile-engineer/SKILL.md +70 -0
  33. crucible/skills/performance-engineer/SKILL.md +68 -0
  34. crucible/skills/product-engineer/SKILL.md +68 -0
  35. crucible/skills/protocol-architect/SKILL.md +83 -0
  36. crucible/skills/security-engineer/SKILL.md +63 -0
  37. crucible/skills/tech-lead/SKILL.md +92 -0
  38. crucible/skills/uiux-engineer/SKILL.md +70 -0
  39. crucible/skills/web3-engineer/SKILL.md +79 -0
  40. crucible_mcp-1.0.0.dist-info/METADATA +198 -0
  41. crucible_mcp-1.0.0.dist-info/RECORD +66 -0
  42. crucible_mcp-0.5.0.dist-info/METADATA +0 -161
  43. crucible_mcp-0.5.0.dist-info/RECORD +0 -30
  44. {crucible_mcp-0.5.0.dist-info → crucible_mcp-1.0.0.dist-info}/WHEEL +0 -0
  45. {crucible_mcp-0.5.0.dist-info → crucible_mcp-1.0.0.dist-info}/entry_points.txt +0 -0
  46. {crucible_mcp-0.5.0.dist-info → crucible_mcp-1.0.0.dist-info}/top_level.txt +0 -0
crucible/cli.py CHANGED
@@ -578,6 +578,27 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
578
578
  # Deduplicate
579
579
  all_findings = deduplicate_findings(all_findings)
580
580
 
581
+ # Run enforcement assertions
582
+ from crucible.review.core import run_enforcement
583
+
584
+ compliance_config = _build_compliance_config(
585
+ config,
586
+ cli_token_budget=getattr(args, "token_budget", None),
587
+ cli_model=getattr(args, "compliance_model", None),
588
+ cli_no_compliance=getattr(args, "no_compliance", False),
589
+ )
590
+
591
+ # Use current directory as repo root for enforcement
592
+ enforcement_findings, enforcement_errors, assertions_checked, assertions_skipped, budget_state = (
593
+ run_enforcement(
594
+ ".",
595
+ changed_files=files_to_analyze,
596
+ repo_root=".",
597
+ compliance_config=compliance_config,
598
+ )
599
+ )
600
+ tool_errors.extend(enforcement_errors)
601
+
581
602
  # Compute severity summary
582
603
  severity_counts = compute_severity_counts(all_findings)
583
604
 
@@ -607,6 +628,21 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
607
628
  }
608
629
  for f in all_findings
609
630
  ],
631
+ "enforcement": {
632
+ "findings": [
633
+ {
634
+ "assertion_id": f.assertion_id,
635
+ "severity": f.severity,
636
+ "message": f.message,
637
+ "location": f.location,
638
+ "source": f.source,
639
+ }
640
+ for f in enforcement_findings
641
+ ],
642
+ "assertions_checked": assertions_checked,
643
+ "assertions_skipped": assertions_skipped,
644
+ "tokens_used": budget_state.tokens_used if budget_state else 0,
645
+ },
610
646
  "severity_counts": severity_counts,
611
647
  "passed": passed,
612
648
  "threshold": default_threshold.value if default_threshold else None,
@@ -616,7 +652,7 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
616
652
  else:
617
653
  # Text output
618
654
  if all_findings:
619
- print(f"\nFound {len(all_findings)} issue(s):\n")
655
+ print(f"\nFound {len(all_findings)} static analysis issue(s):\n")
620
656
  for f in all_findings:
621
657
  sev_icon = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "🔵", "info": "⚪"}.get(
622
658
  f.severity.value, "⚪"
@@ -626,7 +662,18 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
626
662
  if f.suggestion:
627
663
  print(f" 💡 {f.suggestion}")
628
664
  print()
629
- else:
665
+
666
+ # Enforcement findings
667
+ if enforcement_findings:
668
+ print(f"\nEnforcement Assertions ({len(enforcement_findings)}):")
669
+ for f in enforcement_findings:
670
+ sev_icon = {"error": "🔴", "warning": "🟠", "info": "⚪"}.get(f.severity, "⚪")
671
+ source_tag = "[LLM]" if f.source == "llm" else "[Pattern]"
672
+ print(f" {sev_icon} [{f.severity.upper()}] {source_tag} {f.assertion_id}: {f.location}")
673
+ print(f" {f.message}")
674
+ print()
675
+
676
+ if not all_findings and not enforcement_findings:
630
677
  print("\n✅ No issues found.")
631
678
 
632
679
  # Summary
@@ -634,6 +681,11 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
634
681
  counts_str = ", ".join(f"{k}: {v}" for k, v in severity_counts.items() if v > 0)
635
682
  print(f"Summary: {counts_str}")
636
683
 
684
+ if assertions_checked or assertions_skipped:
685
+ print(f"Assertions: {assertions_checked} checked, {assertions_skipped} skipped")
686
+ if budget_state and budget_state.tokens_used > 0:
687
+ print(f" LLM tokens used: {budget_state.tokens_used}")
688
+
637
689
  if tool_errors and not args.quiet:
638
690
  print(f"\n⚠️ {len(tool_errors)} tool error(s)")
639
691
  for err in tool_errors[:5]:
@@ -1682,11 +1734,30 @@ include_context: false
1682
1734
  if not gitignore_path.exists():
1683
1735
  gitignore_path.write_text("# Local overrides (optional)\n*.local.md\n")
1684
1736
 
1737
+ # Create minimal CLAUDE.md if requested
1738
+ if args.with_claudemd:
1739
+ claudemd_path = project_path / "CLAUDE.md"
1740
+ if claudemd_path.exists() and not args.force:
1741
+ print(f"Warning: {claudemd_path} exists, skipping (use --force to overwrite)")
1742
+ else:
1743
+ project_name = project_path.name
1744
+ claudemd_content = f"""# {project_name}
1745
+
1746
+ Use Crucible for code review: `crucible review`
1747
+
1748
+ For full engineering principles and patterns, run:
1749
+ - `crucible knowledge list` - see available knowledge
1750
+ - `crucible skills list` - see available review personas
1751
+ """
1752
+ claudemd_path.write_text(claudemd_content)
1753
+ print(f"Created {claudemd_path}")
1754
+
1685
1755
  print(f"\nInitialized {crucible_dir}")
1686
1756
  print("\nNext steps:")
1687
1757
  print(" 1. Customize skills: crucible skills init <skill>")
1688
1758
  print(" 2. Customize knowledge: crucible knowledge init <file>")
1689
1759
  print(" 3. Install git hooks: crucible hooks install")
1760
+ print(" 4. Claude Code hooks: crucible hooks claudecode init")
1690
1761
  return 0
1691
1762
 
1692
1763
 
@@ -1950,6 +2021,28 @@ def main() -> int:
1950
2021
  "path", nargs="?", default=".", help="Repository path"
1951
2022
  )
1952
2023
 
2024
+ # hooks claudecode
2025
+ hooks_claudecode_parser = hooks_sub.add_parser(
2026
+ "claudecode",
2027
+ help="Claude Code hooks integration"
2028
+ )
2029
+ hooks_claudecode_sub = hooks_claudecode_parser.add_subparsers(dest="claudecode_command")
2030
+
2031
+ # hooks claudecode init
2032
+ hooks_cc_init_parser = hooks_claudecode_sub.add_parser(
2033
+ "init",
2034
+ help="Initialize Claude Code hooks for project"
2035
+ )
2036
+ hooks_cc_init_parser.add_argument(
2037
+ "path", nargs="?", default=".", help="Project path"
2038
+ )
2039
+
2040
+ # hooks claudecode hook (called by Claude Code)
2041
+ hooks_claudecode_sub.add_parser(
2042
+ "hook",
2043
+ help="Run hook (reads JSON from stdin)"
2044
+ )
2045
+
1953
2046
  # === review command ===
1954
2047
  review_parser = subparsers.add_parser(
1955
2048
  "review",
@@ -2044,6 +2137,10 @@ def main() -> int:
2044
2137
  "--minimal", action="store_true",
2045
2138
  help="Create minimal config without copying skills"
2046
2139
  )
2140
+ init_proj_parser.add_argument(
2141
+ "--with-claudemd", action="store_true",
2142
+ help="Generate minimal CLAUDE.md that points to Crucible"
2143
+ )
2047
2144
  init_proj_parser.add_argument(
2048
2145
  "path", nargs="?", default=".",
2049
2146
  help="Project path (default: current directory)"
@@ -2161,6 +2258,15 @@ def main() -> int:
2161
2258
  return cmd_hooks_uninstall(args)
2162
2259
  elif args.hooks_command == "status":
2163
2260
  return cmd_hooks_status(args)
2261
+ elif args.hooks_command == "claudecode":
2262
+ from crucible.hooks.claudecode import main_init, run_hook
2263
+ if args.claudecode_command == "init":
2264
+ return main_init(args.path)
2265
+ elif args.claudecode_command == "hook":
2266
+ return run_hook()
2267
+ else:
2268
+ hooks_claudecode_parser.print_help()
2269
+ return 0
2164
2270
  else:
2165
2271
  hooks_parser.print_help()
2166
2272
  return 0
@@ -2211,6 +2317,7 @@ def main() -> int:
2211
2317
  print(" crucible hooks install Install pre-commit hook to .git/hooks/")
2212
2318
  print(" crucible hooks uninstall Remove pre-commit hook")
2213
2319
  print(" crucible hooks status Show hook installation status")
2320
+ print(" crucible hooks claudecode init Initialize Claude Code hooks")
2214
2321
  print()
2215
2322
  print(" crucible assertions list List assertion files from all sources")
2216
2323
  print(" crucible assertions validate Validate assertion files")
@@ -0,0 +1,84 @@
1
+ version: "1.0"
2
+ name: error-handling
3
+ description: Error handling best practices to prevent silent failures
4
+ linked_knowledge: ERROR_HANDLING.md
5
+
6
+ assertions:
7
+ # High: Silent failures
8
+ - id: no-bare-except
9
+ type: pattern
10
+ pattern: "except\\s*:"
11
+ message: "Bare except catches everything including SystemExit/KeyboardInterrupt - catch specific exceptions"
12
+ severity: warning
13
+ priority: high
14
+ languages: [python]
15
+
16
+ - id: no-pass-in-except
17
+ type: pattern
18
+ pattern: "except[^:]*:\\s*\\n\\s*pass\\s*$"
19
+ message: "Empty except block silently swallows errors - log or re-raise"
20
+ severity: warning
21
+ priority: high
22
+ languages: [python]
23
+
24
+ - id: no-empty-catch
25
+ type: pattern
26
+ pattern: "catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}"
27
+ message: "Empty catch block silently swallows errors - log or re-throw"
28
+ severity: warning
29
+ priority: high
30
+ languages: [javascript, typescript]
31
+
32
+ # Medium: Pokemon exception handling
33
+ - id: no-catch-exception
34
+ type: pattern
35
+ pattern: "except\\s+Exception\\s*:"
36
+ message: "Catching Exception is too broad - catch specific exception types"
37
+ severity: info
38
+ priority: medium
39
+ languages: [python]
40
+
41
+ - id: no-catch-base-exception
42
+ type: pattern
43
+ pattern: "except\\s+BaseException\\s*:"
44
+ message: "Catching BaseException includes SystemExit/KeyboardInterrupt - use Exception or specific types"
45
+ severity: warning
46
+ priority: medium
47
+ languages: [python]
48
+
49
+ # Medium: Error suppression
50
+ - id: no-ignore-errors-flag
51
+ type: pattern
52
+ pattern: "errors\\s*=\\s*[\"']ignore[\"']"
53
+ message: "errors='ignore' silently drops data - use 'replace' or handle explicitly"
54
+ severity: warning
55
+ priority: medium
56
+ languages: [python]
57
+
58
+ # Info: Best practices
59
+ - id: prefer-contextlib-suppress
60
+ type: pattern
61
+ pattern: "except\\s+\\w+\\s*:\\s*\\n\\s*pass\\s*$"
62
+ message: "Consider contextlib.suppress() for intentionally ignoring specific exceptions"
63
+ severity: info
64
+ priority: low
65
+ languages: [python]
66
+
67
+ # LLM assertion for complex error handling patterns
68
+ - id: error-handling-semantic
69
+ type: llm
70
+ compliance: |
71
+ Check that error handling follows best practices:
72
+ 1. Errors are not silently swallowed (no empty except/catch blocks that do nothing)
73
+ 2. Exceptions are logged or reported before being suppressed
74
+ 3. Error messages provide enough context to debug issues
75
+ 4. Errors at API/system boundaries are handled (not just propagated)
76
+ message: "Error handling may need improvement"
77
+ severity: warning
78
+ priority: medium
79
+ languages: [python, javascript, typescript]
80
+ applicability:
81
+ exclude:
82
+ - "**/test_*.py"
83
+ - "**/*_test.py"
84
+ - "**/tests/**"
@@ -0,0 +1,123 @@
1
+ version: "1.0"
2
+ name: security
3
+ description: Security assertions to catch common vulnerabilities
4
+ linked_knowledge: SECURITY.md
5
+
6
+ assertions:
7
+ # Critical: Code execution risks
8
+ - id: no-eval
9
+ type: pattern
10
+ pattern: "\\beval\\s*\\("
11
+ message: "eval() is dangerous - use ast.literal_eval() for data or safer alternatives"
12
+ severity: error
13
+ priority: critical
14
+ languages: [python, javascript, typescript]
15
+
16
+ - id: no-exec
17
+ type: pattern
18
+ pattern: "\\bexec\\s*\\("
19
+ message: "exec() allows arbitrary code execution - avoid or sandbox carefully"
20
+ severity: error
21
+ priority: critical
22
+ languages: [python]
23
+
24
+ # Critical: Shell injection
25
+ - id: no-shell-true
26
+ type: pattern
27
+ pattern: "shell\\s*=\\s*True"
28
+ message: "shell=True enables shell injection - use shell=False with argument list"
29
+ severity: error
30
+ priority: critical
31
+ languages: [python]
32
+
33
+ - id: no-os-system
34
+ type: pattern
35
+ pattern: "os\\.system\\s*\\("
36
+ message: "os.system() is vulnerable to shell injection - use subprocess with shell=False"
37
+ severity: error
38
+ priority: critical
39
+ languages: [python]
40
+
41
+ # Critical: Deserialization
42
+ - id: no-pickle-load
43
+ type: pattern
44
+ pattern: "pickle\\.load|pickle\\.loads|cPickle\\.load"
45
+ message: "pickle can execute arbitrary code - use json or msgpack for untrusted data"
46
+ severity: error
47
+ priority: critical
48
+ languages: [python]
49
+
50
+ - id: no-yaml-unsafe-load
51
+ type: pattern
52
+ pattern: "yaml\\.load\\s*\\([^)]*\\)(?!.*Loader)"
53
+ message: "yaml.load() without Loader is unsafe - use yaml.safe_load()"
54
+ severity: error
55
+ priority: critical
56
+ languages: [python]
57
+
58
+ # High: Hardcoded secrets
59
+ - id: no-hardcoded-password
60
+ type: pattern
61
+ pattern: "(?i)(password|passwd|pwd)\\s*=\\s*[\"'][^\"']{4,}[\"']"
62
+ message: "Possible hardcoded password - use environment variables or secrets manager"
63
+ severity: error
64
+ priority: high
65
+ applicability:
66
+ exclude:
67
+ - "**/test_*.py"
68
+ - "**/*_test.py"
69
+ - "**/tests/**"
70
+ - "**/*.md"
71
+
72
+ - id: no-hardcoded-api-key
73
+ type: pattern
74
+ pattern: "(?i)(api[_-]?key|apikey|secret[_-]?key)\\s*=\\s*[\"'][a-zA-Z0-9]{16,}[\"']"
75
+ message: "Possible hardcoded API key - use environment variables or secrets manager"
76
+ severity: error
77
+ priority: high
78
+ applicability:
79
+ exclude:
80
+ - "**/test_*.py"
81
+ - "**/*_test.py"
82
+ - "**/tests/**"
83
+ - "**/*.md"
84
+
85
+ # High: SQL injection
86
+ - id: no-string-sql
87
+ type: pattern
88
+ pattern: "execute\\s*\\(\\s*[\"']\\s*SELECT|execute\\s*\\(\\s*f[\"']|execute\\s*\\([^)]*%\\s*\\("
89
+ message: "Possible SQL injection - use parameterized queries"
90
+ severity: error
91
+ priority: high
92
+ languages: [python]
93
+
94
+ # Medium: Weak crypto
95
+ - id: no-md5-security
96
+ type: pattern
97
+ pattern: "hashlib\\.md5|MD5\\s*\\(|md5\\.new"
98
+ message: "MD5 is cryptographically broken - use SHA-256 or better for security"
99
+ severity: warning
100
+ priority: medium
101
+ languages: [python]
102
+
103
+ - id: no-sha1-security
104
+ type: pattern
105
+ pattern: "hashlib\\.sha1|SHA1\\s*\\(|sha1\\.new"
106
+ message: "SHA-1 is deprecated for security - use SHA-256 or better"
107
+ severity: warning
108
+ priority: medium
109
+ languages: [python]
110
+
111
+ # Medium: Insecure random
112
+ - id: no-random-for-security
113
+ type: pattern
114
+ pattern: "\\brandom\\.(choice|randint|random|uniform)\\s*\\("
115
+ message: "random module is not cryptographically secure - use secrets module"
116
+ severity: warning
117
+ priority: medium
118
+ languages: [python]
119
+ applicability:
120
+ exclude:
121
+ - "**/test_*.py"
122
+ - "**/*_test.py"
123
+ - "**/tests/**"
@@ -0,0 +1,110 @@
1
+ version: "1.0"
2
+ name: smart-contract
3
+ description: Smart contract security patterns for Solidity/Vyper
4
+ linked_knowledge: SMART_CONTRACT.md
5
+
6
+ assertions:
7
+ # Critical: Reentrancy
8
+ - id: state-after-external-call
9
+ type: pattern
10
+ pattern: "\\.call\\{[^}]*\\}\\([^)]*\\)[^;]*;[^}]*\\b(balances|_balances|userBalances|amounts)\\s*\\["
11
+ message: "State change after external call - potential reentrancy vulnerability (CEI violation)"
12
+ severity: error
13
+ priority: critical
14
+ languages: [solidity]
15
+
16
+ - id: transfer-without-reentrancy-guard
17
+ type: llm
18
+ compliance: |
19
+ Check for reentrancy vulnerabilities:
20
+ 1. External calls (.call, .transfer, .send) should come AFTER state changes (CEI pattern)
21
+ 2. Functions making external calls should have reentrancy guards (nonReentrant modifier)
22
+ 3. State variables should be updated before external calls, not after
23
+ 4. Check-effects-interactions pattern: checks first, then effects (state changes), then interactions (external calls)
24
+ message: "Potential reentrancy vulnerability - apply CEI pattern and/or nonReentrant modifier"
25
+ severity: error
26
+ priority: critical
27
+ model: opus
28
+ languages: [solidity]
29
+
30
+ # Critical: Access control
31
+ - id: missing-access-control
32
+ type: pattern
33
+ pattern: "function\\s+\\w+\\s*\\([^)]*\\)\\s+(external|public)(?![^{]*onlyOwner|[^{]*onlyRole|[^{]*require\\s*\\(\\s*msg\\.sender)"
34
+ message: "Public/external function may need access control"
35
+ severity: warning
36
+ priority: high
37
+ languages: [solidity]
38
+
39
+ - id: tx-origin-auth
40
+ type: pattern
41
+ pattern: "require\\s*\\(\\s*tx\\.origin\\s*==|tx\\.origin\\s*==\\s*msg\\.sender"
42
+ message: "tx.origin for auth is vulnerable to phishing - use msg.sender"
43
+ severity: error
44
+ priority: critical
45
+ languages: [solidity]
46
+
47
+ # High: Integer overflow (pre-0.8.0)
48
+ - id: unchecked-arithmetic
49
+ type: pattern
50
+ pattern: "unchecked\\s*\\{[^}]*[+\\-*/]"
51
+ message: "Unchecked arithmetic - ensure overflow/underflow is intentional and safe"
52
+ severity: warning
53
+ priority: high
54
+ languages: [solidity]
55
+
56
+ # High: Front-running
57
+ - id: block-timestamp-comparison
58
+ type: pattern
59
+ pattern: "block\\.timestamp\\s*[<>=]|[<>=]\\s*block\\.timestamp"
60
+ message: "block.timestamp can be manipulated by miners (~15 sec) - avoid for critical logic"
61
+ severity: warning
62
+ priority: medium
63
+ languages: [solidity]
64
+
65
+ # High: Denial of service
66
+ - id: unbounded-loop
67
+ type: pattern
68
+ pattern: "for\\s*\\([^;]*;[^;]*\\.length\\s*;"
69
+ message: "Loop bounded by dynamic array length - may cause out-of-gas DoS"
70
+ severity: warning
71
+ priority: high
72
+ languages: [solidity]
73
+
74
+ # Medium: Best practices
75
+ - id: missing-zero-address-check
76
+ type: pattern
77
+ pattern: "address\\s+\\w+\\s*=[^;]*;(?![^}]*require\\s*\\([^)]*!=\\s*address\\(0\\))"
78
+ message: "Consider checking for zero address on address parameters"
79
+ severity: info
80
+ priority: medium
81
+ languages: [solidity]
82
+
83
+ - id: hardcoded-gas
84
+ type: pattern
85
+ pattern: "\\.call\\{[^}]*gas:\\s*\\d+[^}]*\\}"
86
+ message: "Hardcoded gas values may break with EVM changes - use gasleft() or remove gas limit"
87
+ severity: warning
88
+ priority: medium
89
+ languages: [solidity]
90
+
91
+ # LLM: Complex security patterns
92
+ - id: smart-contract-security-review
93
+ type: llm
94
+ compliance: |
95
+ Perform a security review of this smart contract code:
96
+ 1. Check for reentrancy vulnerabilities (external calls before state changes)
97
+ 2. Verify access control on privileged functions
98
+ 3. Check for integer overflow/underflow risks
99
+ 4. Look for front-running vulnerabilities
100
+ 5. Check for DoS vectors (unbounded loops, block gas limit issues)
101
+ 6. Verify proper validation of external inputs
102
+ 7. Check for unsafe delegatecall usage
103
+ 8. Look for flash loan attack vectors
104
+ message: "Smart contract security issue detected"
105
+ severity: error
106
+ priority: critical
107
+ model: opus
108
+ languages: [solidity, vyper]
109
+ applicability:
110
+ glob: "**/*.sol"