codejury 1.3.0__tar.gz → 1.4.1__tar.gz

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 (168) hide show
  1. {codejury-1.3.0 → codejury-1.4.1}/PKG-INFO +7 -5
  2. {codejury-1.3.0 → codejury-1.4.1}/README.md +6 -4
  3. {codejury-1.3.0 → codejury-1.4.1}/codejury/cli.py +61 -33
  4. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/base.py +1 -1
  5. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/__init__.py +10 -3
  6. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/facts/slither.py +1 -1
  7. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/index.md +3 -2
  8. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/access-control.md +10 -1
  9. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/accounting-precision.md +1 -0
  10. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/bad-randomness.md +7 -0
  11. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/denial-of-service.md +2 -1
  12. codejury-1.4.1/codejury/domains/evm/knowledge/vulnerabilities/front-running.md +50 -0
  13. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/oracle-price-manipulation.md +1 -0
  14. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/proxy-delegatecall.md +1 -0
  15. codejury-1.4.1/codejury/domains/evm/knowledge/vulnerabilities/reentrancy.md +61 -0
  16. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/signature-replay.md +1 -0
  17. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/unchecked-low-level-call.md +8 -1
  18. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/unsafe-math.md +1 -0
  19. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/vulnerabilities/weird-erc20.md +1 -0
  20. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/playbook/slash-command.md +36 -2
  21. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/playbook/unit-review.md +5 -0
  22. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/poc.py +2 -2
  23. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/__init__.py +32 -4
  24. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/python/django.md +1 -1
  25. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/python/flask.md +6 -1
  26. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/languages/go.md +6 -0
  27. codejury-1.4.1/codejury/domains/web/knowledge/guides/protocols/graphql.md +62 -0
  28. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/index.md +4 -1
  29. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/business-logic.md +1 -0
  30. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/code-injection.md +1 -0
  31. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/command-injection.md +11 -1
  32. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/cors-misconfiguration.md +1 -0
  33. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/cross-site-request-forgery.md +1 -0
  34. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/cross-site-scripting.md +1 -0
  35. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/hardcoded-secrets.md +1 -0
  36. codejury-1.4.1/codejury/domains/web/knowledge/vulnerabilities/http-request-smuggling.md +49 -0
  37. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/http-response-splitting.md +1 -0
  38. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/improper-authentication.md +1 -0
  39. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/information-exposure.md +1 -0
  40. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/insecure-cryptography.md +1 -0
  41. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/insecure-deserialization.md +1 -0
  42. codejury-1.4.1/codejury/domains/web/knowledge/vulnerabilities/insecure-direct-object-reference.md +32 -0
  43. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/insecure-session-management.md +1 -0
  44. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/insecure-transport.md +1 -0
  45. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/jwt-validation.md +1 -0
  46. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/mass-assignment.md +1 -0
  47. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/missing-authorization.md +1 -0
  48. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/nosql-injection.md +1 -0
  49. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/open-redirect.md +1 -0
  50. codejury-1.4.1/codejury/domains/web/knowledge/vulnerabilities/path-traversal.md +41 -0
  51. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/prototype-pollution.md +2 -1
  52. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/race-condition.md +7 -1
  53. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/replay-attack.md +1 -0
  54. codejury-1.4.1/codejury/domains/web/knowledge/vulnerabilities/resource-exhaustion.md +45 -0
  55. codejury-1.4.1/codejury/domains/web/knowledge/vulnerabilities/security-misconfiguration.md +48 -0
  56. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/server-side-request-forgery.md +13 -1
  57. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/server-side-template-injection.md +1 -0
  58. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/sql-injection.md +1 -0
  59. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/unrestricted-file-upload.md +1 -0
  60. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/vulnerabilities/xml-external-entity.md +6 -1
  61. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/playbook/slash-command.md +36 -2
  62. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/playbook/unit-review.md +6 -0
  63. {codejury-1.3.0 → codejury-1.4.1}/codejury/guides.py +2 -2
  64. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/diff/adversarial.py +1 -1
  65. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/diff/engine.py +1 -1
  66. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/engine.py +61 -58
  67. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/model.py +59 -0
  68. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/reviewer.py +1 -1
  69. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/scaffold.py +51 -17
  70. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/union.py +1 -1
  71. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/verifier.py +2 -2
  72. {codejury-1.3.0 → codejury-1.4.1}/codejury.egg-info/PKG-INFO +7 -5
  73. {codejury-1.3.0 → codejury-1.4.1}/codejury.egg-info/SOURCES.txt +5 -0
  74. {codejury-1.3.0 → codejury-1.4.1}/pyproject.toml +1 -1
  75. {codejury-1.3.0 → codejury-1.4.1}/tests/test_anthropic_provider.py +1 -1
  76. {codejury-1.3.0 → codejury-1.4.1}/tests/test_claude_agent_provider.py +1 -1
  77. {codejury-1.3.0 → codejury-1.4.1}/tests/test_cli.py +6 -3
  78. {codejury-1.3.0 → codejury-1.4.1}/tests/test_diff_adversarial.py +1 -1
  79. {codejury-1.3.0 → codejury-1.4.1}/tests/test_domains.py +84 -0
  80. {codejury-1.3.0 → codejury-1.4.1}/tests/test_evals.py +171 -5
  81. {codejury-1.3.0 → codejury-1.4.1}/tests/test_litellm_provider.py +1 -1
  82. {codejury-1.3.0 → codejury-1.4.1}/tests/test_openai_provider.py +1 -1
  83. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_engine.py +52 -6
  84. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_scaffold.py +19 -0
  85. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_union.py +1 -1
  86. {codejury-1.3.0 → codejury-1.4.1}/tests/test_vulnerabilities.py +5 -3
  87. codejury-1.3.0/codejury/domains/evm/knowledge/vulnerabilities/reentrancy.md +0 -38
  88. codejury-1.3.0/codejury/domains/web/knowledge/vulnerabilities/insecure-direct-object-reference.md +0 -25
  89. codejury-1.3.0/codejury/domains/web/knowledge/vulnerabilities/path-traversal.md +0 -35
  90. {codejury-1.3.0 → codejury-1.4.1}/LICENSE +0 -0
  91. {codejury-1.3.0 → codejury-1.4.1}/codejury/__init__.py +0 -0
  92. {codejury-1.3.0 → codejury-1.4.1}/codejury/__main__.py +0 -0
  93. {codejury-1.3.0 → codejury-1.4.1}/codejury/detection.py +0 -0
  94. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/__init__.py +0 -0
  95. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/detection.yaml +0 -0
  96. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/facts/__init__.py +0 -0
  97. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/facts/call_path.py +0 -0
  98. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/guides/languages/solidity.md +0 -0
  99. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/guides/protocols/defi-primitives.md +0 -0
  100. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/knowledge/guides/protocols/erc-standards.md +0 -0
  101. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/playbook/false-positive-traps.md +0 -0
  102. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/playbook/methodology.md +0 -0
  103. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/evm/playbook/severity-rubric.md +0 -0
  104. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/registry.py +0 -0
  105. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/detection.yaml +0 -0
  106. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/go/echo.md +0 -0
  107. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/go/gin.md +0 -0
  108. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/javascript/express.md +0 -0
  109. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/javascript/nestjs.md +0 -0
  110. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/python/celery.md +0 -0
  111. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/frameworks/python/fastapi.md +0 -0
  112. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/languages/javascript.md +0 -0
  113. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/languages/python.md +0 -0
  114. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/languages/typescript.md +0 -0
  115. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/knowledge/guides/protocols/oauth.md +0 -0
  116. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/playbook/false-positive-traps.md +0 -0
  117. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/playbook/methodology.md +0 -0
  118. {codejury-1.3.0 → codejury-1.4.1}/codejury/domains/web/playbook/severity-rubric.md +0 -0
  119. {codejury-1.3.0 → codejury-1.4.1}/codejury/finding.py +0 -0
  120. {codejury-1.3.0 → codejury-1.4.1}/codejury/json_parse.py +0 -0
  121. {codejury-1.3.0 → codejury-1.4.1}/codejury/markdown_docs.py +0 -0
  122. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/__init__.py +0 -0
  123. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/anthropic.py +0 -0
  124. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/base.py +0 -0
  125. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/chat_format.py +0 -0
  126. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/claude_agent.py +0 -0
  127. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/factory.py +0 -0
  128. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/litellm.py +0 -0
  129. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/mock.py +0 -0
  130. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/openai.py +0 -0
  131. {codejury-1.3.0 → codejury-1.4.1}/codejury/providers/retry.py +0 -0
  132. {codejury-1.3.0 → codejury-1.4.1}/codejury/report.py +0 -0
  133. {codejury-1.3.0 → codejury-1.4.1}/codejury/resources.py +0 -0
  134. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/__init__.py +0 -0
  135. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/diff/__init__.py +0 -0
  136. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/diff/audit.py +0 -0
  137. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/diff/filter.py +0 -0
  138. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/diff/prompts.py +0 -0
  139. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/diff/vulnerabilities.py +0 -0
  140. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/__init__.py +0 -0
  141. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/agent.py +0 -0
  142. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/gate.py +0 -0
  143. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/pass_loop.py +0 -0
  144. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/paths.py +0 -0
  145. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/severity.py +0 -0
  146. {codejury-1.3.0 → codejury-1.4.1}/codejury/review/repo/shapes.py +0 -0
  147. {codejury-1.3.0 → codejury-1.4.1}/codejury/severity.py +0 -0
  148. {codejury-1.3.0 → codejury-1.4.1}/codejury.egg-info/dependency_links.txt +0 -0
  149. {codejury-1.3.0 → codejury-1.4.1}/codejury.egg-info/entry_points.txt +0 -0
  150. {codejury-1.3.0 → codejury-1.4.1}/codejury.egg-info/requires.txt +0 -0
  151. {codejury-1.3.0 → codejury-1.4.1}/codejury.egg-info/top_level.txt +0 -0
  152. {codejury-1.3.0 → codejury-1.4.1}/setup.cfg +0 -0
  153. {codejury-1.3.0 → codejury-1.4.1}/tests/test_chat_format.py +0 -0
  154. {codejury-1.3.0 → codejury-1.4.1}/tests/test_detection.py +0 -0
  155. {codejury-1.3.0 → codejury-1.4.1}/tests/test_diff_audit.py +0 -0
  156. {codejury-1.3.0 → codejury-1.4.1}/tests/test_guides.py +0 -0
  157. {codejury-1.3.0 → codejury-1.4.1}/tests/test_json_parse.py +0 -0
  158. {codejury-1.3.0 → codejury-1.4.1}/tests/test_markdown_docs.py +0 -0
  159. {codejury-1.3.0 → codejury-1.4.1}/tests/test_provider_factory.py +0 -0
  160. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_agent.py +0 -0
  161. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_gate.py +0 -0
  162. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_model.py +0 -0
  163. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_pass_loop.py +0 -0
  164. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_paths.py +0 -0
  165. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_severity.py +0 -0
  166. {codejury-1.3.0 → codejury-1.4.1}/tests/test_repo_verifier.py +0 -0
  167. {codejury-1.3.0 → codejury-1.4.1}/tests/test_report.py +0 -0
  168. {codejury-1.3.0 → codejury-1.4.1}/tests/test_retry_provider.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codejury
3
- Version: 1.3.0
3
+ Version: 1.4.1
4
4
  Summary: AI-assisted security review for code diffs and whole repositories.
5
5
  Author: AISecLabs
6
6
  License-Expression: MIT
@@ -84,8 +84,10 @@ codejury install-slash-command # Claude Code
84
84
  codejury install-slash-command --agent codex # Codex
85
85
  ```
86
86
 
87
- `install-slash-command` copies `/codejury-review-repo` into the selected agent's command
88
- directory. Pass `--dir` to install it somewhere else.
87
+ `install-slash-command` copies `/codejury-review` into the selected agent's command
88
+ directory. The one command dispatches by argument: a directory runs the Repo Review
89
+ fan-out, a diff file or git range runs the coded Diff Review. Pass `--dir` to install it
90
+ somewhere else.
89
91
 
90
92
  ## Configure a Model Backend
91
93
 
@@ -232,7 +234,7 @@ _pocs.md PoC reconciliation, planned versus delivered
232
234
  Then run the interactive slash command in Claude Code or Codex:
233
235
 
234
236
  ```text
235
- /codejury-review-repo /path/to/repo
237
+ /codejury-review /path/to/repo
236
238
  ```
237
239
 
238
240
  The agent maps the attack surface, fills the authorization model, runs one focused
@@ -294,7 +296,7 @@ Current guide coverage in the web domain includes:
294
296
  - Python: Django, Flask, FastAPI, Celery
295
297
  - Go: Gin, Echo
296
298
  - JavaScript and TypeScript: Express, NestJS
297
- - Protocols: OAuth and OIDC
299
+ - Protocols: OAuth, OIDC, and GraphQL
298
300
 
299
301
  The evm domain ships a Solidity guide and the smart contract vulnerability classes above.
300
302
  Unguided stacks still work, but the agent relies more on general methodology and model
@@ -42,8 +42,10 @@ codejury install-slash-command # Claude Code
42
42
  codejury install-slash-command --agent codex # Codex
43
43
  ```
44
44
 
45
- `install-slash-command` copies `/codejury-review-repo` into the selected agent's command
46
- directory. Pass `--dir` to install it somewhere else.
45
+ `install-slash-command` copies `/codejury-review` into the selected agent's command
46
+ directory. The one command dispatches by argument: a directory runs the Repo Review
47
+ fan-out, a diff file or git range runs the coded Diff Review. Pass `--dir` to install it
48
+ somewhere else.
47
49
 
48
50
  ## Configure a Model Backend
49
51
 
@@ -190,7 +192,7 @@ _pocs.md PoC reconciliation, planned versus delivered
190
192
  Then run the interactive slash command in Claude Code or Codex:
191
193
 
192
194
  ```text
193
- /codejury-review-repo /path/to/repo
195
+ /codejury-review /path/to/repo
194
196
  ```
195
197
 
196
198
  The agent maps the attack surface, fills the authorization model, runs one focused
@@ -252,7 +254,7 @@ Current guide coverage in the web domain includes:
252
254
  - Python: Django, Flask, FastAPI, Celery
253
255
  - Go: Gin, Echo
254
256
  - JavaScript and TypeScript: Express, NestJS
255
- - Protocols: OAuth and OIDC
257
+ - Protocols: OAuth, OIDC, and GraphQL
256
258
 
257
259
  The evm domain ships a Solidity guide and the smart contract vulnerability classes above.
258
260
  Unguided stacks still work, but the agent relies more on general methodology and model
@@ -366,8 +366,10 @@ def main(argv: list[str] | None = None) -> int:
366
366
  _add_role_backend_args(roles, role)
367
367
 
368
368
  tuning = repo.add_argument_group("run tuning (advanced)", "only affect --run, sane defaults otherwise")
369
- tuning.add_argument("--max-passes", type=int, default=24, dest="max_passes",
370
- help="cap on diverse passes before stopping")
369
+ tuning.add_argument("--max-passes", type=int, default=None, dest="max_passes",
370
+ help="cap on diverse passes before stopping, default scales to the domain: "
371
+ "(min-lens-shots + 1) * number of lenses, so every lens meets its shot "
372
+ "floor with a cycle of headroom for convergence")
371
373
  tuning.add_argument("--converge-after", type=int, default=2, dest="converge_after",
372
374
  help="stop once this many consecutive passes add no new finding")
373
375
  tuning.add_argument("--min-lens-shots", type=int, default=2, dest="min_lens_shots",
@@ -382,12 +384,12 @@ def main(argv: list[str] | None = None) -> int:
382
384
  _add_domain_arg(repo)
383
385
 
384
386
  inst = sub.add_parser("install-slash-command",
385
- help="install the /codejury-review-repo slash command for an agent")
387
+ help="install the /codejury-review slash command for an agent")
386
388
  inst.add_argument("--agent", choices=("claude", "codex"), default="claude",
387
389
  help="which agent's command directory to install into")
388
390
  inst.add_argument("--dir", default=None, help="explicit target directory, overrides --agent")
389
391
  inst.add_argument("--force", action="store_true",
390
- help="overwrite an existing codejury-review-repo.md at the destination")
392
+ help="overwrite an existing codejury-review.md at the destination")
391
393
  inst.add_argument("--domain", default="web", metavar="DOMAIN",
392
394
  help="which domain's slash command to install, one of: " + ", ".join(available_domains()))
393
395
 
@@ -411,6 +413,49 @@ def _diff_provider(args, spec, kind: str):
411
413
  return _role_provider(args, spec)
412
414
 
413
415
 
416
+ def build_diff_providers(args):
417
+ """Resolve the diff seats into providers exactly as `review diff` does, so a non-CLI caller
418
+ such as the eval runs a case through the same wiring a user gets. Returns the base provider
419
+ and model the audit needs plus the per-role finder, challenger, and judge providers and
420
+ models, the role fields None in standard mode. The single source the CLI and the eval share,
421
+ so the probe cannot drift from the product on which model or seat reviews a diff."""
422
+ base = _base_spec(args)
423
+ if args.mode == "adversarial":
424
+ roles = {r: _role_spec(args, r, base) for r in ("finder", "challenger", "judge")}
425
+ kinds = {r: _seat_backend(s, args.executor) for r, s in roles.items()}
426
+ agent_roles = [r for r, k in kinds.items() if k == "agent"]
427
+ _warn_roles_under_agent(args, agent_roles)
428
+ if args.executor == "auto":
429
+ _note_subscription_fallback(agent_roles)
430
+ fp = _diff_provider(args, roles["finder"], kinds["finder"])
431
+ cp = _diff_provider(args, roles["challenger"], kinds["challenger"])
432
+ jp = _diff_provider(args, roles["judge"], kinds["judge"])
433
+ return (fp, roles["finder"]["model"], fp, roles["finder"]["model"],
434
+ cp, roles["challenger"]["model"], jp, roles["judge"]["model"])
435
+ base_kind = _seat_backend(base, args.executor)
436
+ if args.executor == "auto" and base_kind == "agent":
437
+ _note_subscription_fallback(("audit",))
438
+ return (_diff_provider(args, base, base_kind), base["model"], None, None, None, None, None, None)
439
+
440
+
441
+ def diff_args_from_env(mode: str, *, executor: str = "auto", rounds: int = 3):
442
+ """A diff args namespace from the environment defaults, the same values `review diff` reads
443
+ when no flag is passed, so `build_diff_providers` builds the user's real wiring. Lets the eval
444
+ drive the audit through the product path rather than a hardcoded provider."""
445
+ from types import SimpleNamespace
446
+ ns = dict(provider=DEFAULT_PROVIDER, model=DEFAULT_MODEL, api_key=DEFAULT_API_KEY,
447
+ api_base=DEFAULT_API_BASE, retries=DEFAULT_RETRIES, timeout=DEFAULT_TIMEOUT,
448
+ executor=executor, mode=mode, rounds=rounds)
449
+ for role in ROLES:
450
+ d = DEFAULT_ROLE_BACKENDS[role]
451
+ ns[f"{role}_provider"] = d["provider"]
452
+ ns[f"{role}_model"] = d["model"]
453
+ ns[f"{role}_api_key"] = d["api_key"]
454
+ ns[f"{role}_api_base"] = d["api_base"]
455
+ ns[f"{role}_wire_api"] = d["wire_api"]
456
+ return SimpleNamespace(**ns)
457
+
458
+
414
459
  def _dispatch(args, parser) -> int:
415
460
  scope = getattr(args, "scope", None)
416
461
  if args.command == "review" and scope == "diff":
@@ -424,29 +469,8 @@ def _dispatch(args, parser) -> int:
424
469
  else:
425
470
  diff = _read_diff(args)
426
471
  domain = resolve_domain(args.domain, _diff_paths(diff))
427
- base = _base_spec(args)
428
- if args.mode == "adversarial":
429
- # each seat resolves on its own key, so a keyless Claude finder and judge ride the
430
- # subscription while an OpenAI challenger uses its own key. The finder backs the base
431
- # default audit_diff needs, the per-role providers below override it field by field
432
- roles = {r: _role_spec(args, r, base) for r in ("finder", "challenger", "judge")}
433
- kinds = {r: _seat_backend(s, args.executor) for r, s in roles.items()}
434
- agent_roles = [r for r, k in kinds.items() if k == "agent"]
435
- _warn_roles_under_agent(args, agent_roles)
436
- if args.executor == "auto":
437
- _note_subscription_fallback(agent_roles)
438
- finder_provider = _diff_provider(args, roles["finder"], kinds["finder"])
439
- challenger_provider = _diff_provider(args, roles["challenger"], kinds["challenger"])
440
- judge_provider = _diff_provider(args, roles["judge"], kinds["judge"])
441
- finder_model = roles["finder"]["model"]
442
- challenger_model = roles["challenger"]["model"]
443
- judge_model = roles["judge"]["model"]
444
- provider, model = finder_provider, roles["finder"]["model"]
445
- else:
446
- base_kind = _seat_backend(base, args.executor)
447
- if args.executor == "auto" and base_kind == "agent":
448
- _note_subscription_fallback(("audit",))
449
- provider, model = _diff_provider(args, base, base_kind), base["model"]
472
+ (provider, model, finder_provider, finder_model,
473
+ challenger_provider, challenger_model, judge_provider, judge_model) = build_diff_providers(args)
450
474
  kept, _, degraded = audit_diff(
451
475
  diff, provider=provider, model=model,
452
476
  mode=args.mode, max_rounds=args.rounds, filter_findings=not args.no_filter,
@@ -457,7 +481,7 @@ def _dispatch(args, parser) -> int:
457
481
  print(render(args.fmt, kept))
458
482
  if degraded:
459
483
  # the adversarial judge was unusable and the result fell back to the
460
- # unjudged set, so this is a failed audit, not a clean pass, invariant 3
484
+ # unjudged set, so this is a failed audit, not a clean pass, invariant 4
461
485
  print("error: the adversarial audit degraded on an unusable judge reply, "
462
486
  "the result is incomplete and not a clean pass", file=sys.stderr)
463
487
  return 1 if degraded or gate(kept, args.fail_on) else 0
@@ -518,13 +542,17 @@ def _dispatch(args, parser) -> int:
518
542
  f"PoC reconciliation in {fr.workspace}/_pocs.md")
519
543
  if fr.verify and fr.verify.errors:
520
544
  print(f"WARNING: {fr.verify.errors} verification calls failed. Re-run to resume.", file=sys.stderr)
521
- return 1 # fail loud: an incomplete verification is not a clean finalize, invariant 3
545
+ return 1 # fail loud: an incomplete verification is not a clean finalize, invariant 4
522
546
  return 0
523
547
 
524
548
  if args.command == "review" and scope == "repo" and args.run:
525
549
  from codejury.review.repo.engine import run_repo_review
526
550
  from codejury.review.repo.verifier import ModelVerifier
527
551
  domain = resolve_domain(args.domain, _repo_file_names(args.directory))
552
+ # scale the pass cap to the domain, so the min-lens-shots floor is always meetable and the
553
+ # convergence early-stop can fire, with one lens cycle of headroom above the floor
554
+ if args.max_passes is None:
555
+ args.max_passes = (args.min_lens_shots + 1) * len(domain.lenses)
528
556
  _warn_secondary_env()
529
557
  base = _base_spec(args)
530
558
  finder = _role_spec(args, "finder", base)
@@ -600,7 +628,7 @@ def _dispatch(args, parser) -> int:
600
628
  file=sys.stderr)
601
629
  print(f"Findings written to {res.scaffold.workspace}/findings/ and {res.scaffold.workspace}/findings.json")
602
630
  # fail loud: a partial run or a run still finding issues at the cap must not exit clean,
603
- # invariant 3 and the stability red line, so a non-converged run is not reported as done
631
+ # invariant 4 and the stability red line, so a non-converged run is not reported as done
604
632
  return 1 if failures or not acc.converged else 0
605
633
 
606
634
  if args.command == "review" and scope == "repo":
@@ -633,7 +661,7 @@ def _dispatch(args, parser) -> int:
633
661
  print(
634
662
  "This command sets up the review, it does not find anything itself. Next, have an "
635
663
  f"interactive agent follow {res.workspace}/METHODOLOGY.md to run the review, or use the "
636
- "/codejury-review-repo command in Claude Code or Codex. The agent proposes findings in "
664
+ "/codejury-review command in Claude Code or Codex. The agent proposes findings in "
637
665
  f"{res.workspace}/candidates/, finalize confirms them into {res.workspace}/findings/."
638
666
  )
639
667
  return 0
@@ -645,14 +673,14 @@ def _dispatch(args, parser) -> int:
645
673
  "codex": Path.home() / ".codex" / "prompts",
646
674
  }
647
675
  target_dir = Path(args.dir) if args.dir else agent_dirs[args.agent]
648
- dst = target_dir / "codejury-review-repo.md"
676
+ dst = target_dir / "codejury-review.md"
649
677
  if dst.exists() and not args.force:
650
678
  print(f"{dst} already exists. Re-run with --force to overwrite it.", file=sys.stderr)
651
679
  return 1
652
680
  target_dir.mkdir(parents=True, exist_ok=True)
653
681
  dst.write_text(slash_command_file.read_text(encoding="utf-8"), encoding="utf-8")
654
682
  print(f"Installed slash command to {dst}")
655
- print("Run it in the agent with: /codejury-review-repo <repository>")
683
+ print("Run it in the agent with: /codejury-review <repository or diff>")
656
684
  return 0
657
685
 
658
686
  if args.command == "review":
@@ -97,7 +97,7 @@ class Domain:
97
97
  class BackendUnavailable(RuntimeError):
98
98
  """A tool-backed seam was asked to work but its external tool is not installed. Raised,
99
99
  never swallowed into an empty result, so a missing toolchain is a loud failure and not a
100
- silently clean review, invariant 3."""
100
+ silently clean review, invariant 4."""
101
101
 
102
102
 
103
103
  @dataclass(frozen=True, kw_only=True)
@@ -13,15 +13,22 @@ from pathlib import Path
13
13
  from codejury.domains.base import Domain
14
14
  from codejury.domains.evm.facts.slither import SlitherFacts
15
15
 
16
- # the repo-review pass lenses for contracts: each pass leads with one, the empty lens
17
- # reviews every class
16
+ # the repo-review pass lenses for contracts: each pass leads with one class, the empty lens
17
+ # reviews every class. A named lens is a reliable focused pass and the empty catch-all is not,
18
+ # so every shipped contract class gets its own lens rather than relying on the catch-all.
18
19
  EVM_LENSES = (
19
20
  "access-control",
20
21
  "reentrancy",
21
- "oracle-manipulation",
22
+ "oracle-price-manipulation",
22
23
  "accounting-precision",
23
24
  "signature-replay",
24
25
  "denial-of-service",
26
+ "bad-randomness",
27
+ "front-running",
28
+ "proxy-delegatecall",
29
+ "unchecked-low-level-call",
30
+ "unsafe-math",
31
+ "weird-erc20",
25
32
  "",
26
33
  )
27
34
 
@@ -4,7 +4,7 @@ Solidity compiler, availability is lazy-checked so importing the domain never ne
4
4
  heavy dependency, and slither itself is imported only inside extract.
5
5
 
6
6
  A backend that cannot run fails loud rather than returning empty facts that would read as a
7
- clean review, invariant 3. A missing toolchain raises BackendUnavailable, a compile error
7
+ clean review, invariant 4. A missing toolchain raises BackendUnavailable, a compile error
8
8
  in the target propagates as the native slither error.
9
9
  """
10
10
 
@@ -19,6 +19,7 @@ the target. A finding's `category` is one of these ids.
19
19
 
20
20
  ### Economic and Accounting
21
21
  - `oracle-price-manipulation` spot price or balance as price, flash-loan assisted
22
+ - `front-running` no minOut or deadline, sandwich, profitable action front-run
22
23
  - `accounting-precision` rounding, division before multiplication, ERC-4626 first-depositor inflation
23
24
  - `weird-erc20` fee-on-transfer, rebasing, ERC777 callback, received below the requested amount
24
25
  - `unsafe-math` unchecked-block overflow, narrowing downcast truncation
@@ -33,5 +34,5 @@ Report only real, exploitable, high-confidence issues with a concrete exploit pa
33
34
  fund or control impact. Do not report gas-optimization or style notes, floating-pragma or
34
35
  compiler advisories, dependency CVEs, or 0.8 checked-arithmetic overflow outside an
35
36
  `unchecked` block. The set is data: add a class by dropping a new
36
- `vulnerabilities/<id>.md` with the same frontmatter of id, title, impact, tags, and
37
- triggers, plus vulnerable and secure examples.
37
+ `vulnerabilities/<id>.md` with the same frontmatter of id, title, impact, tags,
38
+ triggers, and lens, plus vulnerable and secure examples.
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  id: access-control
3
3
  title: Missing or Broken Access Control
4
+ lens: access-control
4
5
  impact: CRITICAL
5
6
  tags: [swc-105, swc-115, access-control, fund-loss]
6
7
  aliases: [missing-access-control, broken-access-control]
7
- triggers: ["onlyOwner", "function mint", "function burn", "function withdraw", "selfdestruct", "tx.origin", "require(msg.sender", "_mint", "setOwner", "transferOwnership", "external", "public"]
8
+ triggers: ["onlyOwner", "function mint", "function burn", "function withdraw", "selfdestruct", "tx.origin", "require(msg.sender", "_mint", "setOwner", "transferOwnership", "function approve", "blacklist", "isBlacklisted", "whenNotPaused", "external", "public"]
8
9
  ---
9
10
 
10
11
  ## Missing or Broken Access Control
@@ -16,6 +17,14 @@ The cause is a missing modifier, a modifier on the wrong function, an authorizat
16
17
  but not the right one. Gate every state-changing privileged function to the exact role,
17
18
  and use `msg.sender`.
18
19
 
20
+ A security gate that some entrypoints enforce and a sibling omits is a broken invariant,
21
+ report the gap even when the omitted path does not itself move value. A token with a
22
+ blacklist, pause, or sanctions check that runs on `transfer` and `transferFrom` but not on
23
+ `approve`, `permit`, or `increaseAllowance` lets a barred account still take part in the
24
+ flow, so the control is not the invariant it claims. Enumerate the sibling entrypoints a
25
+ stated invariant should cover and name the one missing the check, do not clear it because a
26
+ later step looks gated.
27
+
19
28
  ### Vulnerable
20
29
  ```solidity
21
30
  function mint(address to, uint256 amount) external { // no access control
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: accounting-precision
3
3
  title: Accounting and Precision Error
4
+ lens: accounting-precision
4
5
  impact: HIGH
5
6
  tags: [accounting, rounding, precision, erc4626, fund-loss]
6
7
  aliases: [accounting, precision]
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: bad-randomness
3
3
  title: Insecure Randomness
4
+ lens: bad-randomness
4
5
  impact: HIGH
5
6
  tags: [swc-120, randomness, fund-loss]
6
7
  aliases: [weak-randomness, predictable-random]
@@ -16,6 +17,12 @@ a validator can influence or withhold it, so the result is gamed and the payout
16
17
  Use a verifiable randomness source such as a VRF, or a commit-reveal scheme where the seed
17
18
  is fixed before it can be known.
18
19
 
20
+ The timestamp-dependence variant is the same root cause applied to time, not selection: a
21
+ proposer can nudge `block.timestamp` by a few seconds, so logic that gates an auction close,
22
+ a vesting unlock, a deadline, or a reward rate on an exact `block.timestamp` comparison is
23
+ manipulable at the margin. Use a tolerance band, a block-number gate, or an oracle time
24
+ rather than trusting `block.timestamp` to the second.
25
+
19
26
  ### Vulnerable
20
27
  ```solidity
21
28
  function drawWinner(address[] calldata players) external {
@@ -1,8 +1,9 @@
1
1
  ---
2
2
  id: denial-of-service
3
3
  title: Denial of Service
4
+ lens: denial-of-service
4
5
  impact: HIGH
5
- tags: [swc-113, swc-128, dos, griefing, gas]
6
+ tags: [swc-113, swc-128, dos, griefing, gas, fund-loss]
6
7
  aliases: [dos]
7
8
  triggers: ["for (", "while (", ".length", "push(", "transfer(", "revert", "external call", "unbounded", "for(uint", "selfdestruct"]
8
9
  ---
@@ -0,0 +1,50 @@
1
+ ---
2
+ id: front-running
3
+ title: Front-Running and Slippage
4
+ lens: front-running
5
+ impact: HIGH
6
+ tags: [swc-114, front-running, mev, sandwich, slippage, fund-loss]
7
+ aliases: [mev, sandwich, slippage, frontrunning, front-run]
8
+ triggers: ["swap", "minOut", "amountOutMin", "minAmountOut", "amountOutMinimum", "deadline", "getAmountOut", "addLiquidity", "removeLiquidity", "swapExactTokensForTokens", "0, path", "claim", "harvest"]
9
+ ---
10
+
11
+ ## Front-Running and Slippage
12
+
13
+ A state-changing action is ordered against the victim in the mempool. A swap, deposit,
14
+ or redeem that enforces no minimum output and no deadline executes at an attacker-chosen
15
+ price, or a profitable action is observed and front-run. The classic shape is a sandwich,
16
+ the attacker buys ahead of the victim, lets the victim's trade move the price, then sells
17
+ into it. Bind every trade with a caller-supplied `minOut` and a `deadline`, and treat any
18
+ value the transaction derives from a movable on-chain source as untrusted, see the
19
+ oracle-price-manipulation class.
20
+
21
+ ### Vulnerable
22
+ ```solidity
23
+ function zapIn(uint256 amountIn) external {
24
+ // minOut hardcoded to 0 and block.timestamp as the deadline, so a sandwich sets the price
25
+ uint256[] memory out = router.swapExactTokensForTokens(
26
+ amountIn, 0, path, address(this), block.timestamp
27
+ );
28
+ deposit(out[out.length - 1]);
29
+ }
30
+ ```
31
+
32
+ ### Secure
33
+ ```solidity
34
+ function zapIn(uint256 amountIn, uint256 minOut, uint256 deadline) external {
35
+ uint256[] memory out = router.swapExactTokensForTokens(
36
+ amountIn, minOut, path, address(this), deadline
37
+ );
38
+ deposit(out[out.length - 1]);
39
+ }
40
+ ```
41
+
42
+ ### Not a Finding
43
+
44
+ A trade that passes the caller's `minOut` and `deadline` straight through to the router
45
+ is the expected control and is not reportable. Report it only when the slippage bound is
46
+ hardcoded to 0, the deadline is replaced with `block.timestamp` so it can never expire,
47
+ `minOut` is computed from a spot price read in the same call, or a privileged action's
48
+ ordering grants a concrete and quantifiable fund advantage. A pure view function, an
49
+ action with no economic ordering advantage, or generic mempool-visibility commentary is
50
+ not a finding.
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: oracle-price-manipulation
3
3
  title: Oracle and Price Manipulation
4
+ lens: oracle-price-manipulation
4
5
  impact: CRITICAL
5
6
  tags: [oracle, price-manipulation, flash-loan, fund-loss]
6
7
  aliases: [oracle, oracle-manipulation, oracle-validation, price-manipulation]
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: proxy-delegatecall
3
3
  title: Proxy, Delegatecall, and Initializer Flaws
4
+ lens: proxy-delegatecall
4
5
  impact: CRITICAL
5
6
  tags: [swc-112, delegatecall, proxy, initializer, upgrade, fund-loss]
6
7
  aliases: [delegatecall, unprotected-upgrade]
@@ -0,0 +1,61 @@
1
+ ---
2
+ id: reentrancy
3
+ title: Reentrancy
4
+ lens: reentrancy
5
+ impact: CRITICAL
6
+ tags: [swc-107, reentrancy, fund-loss]
7
+ aliases: [read-only-reentrancy]
8
+ triggers: [".call{value", ".call(", "transfer(", "send(", "external call", "balances[", "withdraw", "nonReentrant", "safeTransfer", "onERC721Received", "tokensToSend", "tokensReceived", "ERC777", "buyout", "before state", "get_virtual_price", "getReserves", "getRate", "sharePrice", "view returns", "balanceOf(address(this))"]
9
+ ---
10
+
11
+ ## Reentrancy
12
+
13
+ An external call hands control to the callee before the contract finishes updating its
14
+ own state, so the callee can call back in and act on the stale pre-update state. The
15
+ classic form drains a balance by re-entering a withdraw before the balance is zeroed.
16
+ Cross-function reentrancy re-enters a different function that shares the same state, and
17
+ read-only reentrancy reads a view mid-update from another protocol. The read-only form
18
+ needs no state write in the reentered call: a price or share view such as a Curve
19
+ `get_virtual_price`, a Balancer `getRate`, or a `getReserves` or `balanceOf(address(this))`
20
+ spot read returns a stale value while a withdraw or exit has sent value but not yet synced
21
+ its reserves, and a consumer that prices collateral off that view is fooled. Write state
22
+ before the external call, or guard with `nonReentrant`, and remember a guard on the mutating
23
+ function does not stop the cross-contract read-only form, the view must be consistent at the
24
+ moment of the external call.
25
+
26
+ A token transfer is an external call too. An ERC777 token runs a `tokensToSend` hook on
27
+ the sender and a `tokensReceived` hook on the recipient, and other tokens add their own
28
+ transfer callback, so a plain `transfer` or `safeTransfer` of an ERC20 can hand control to
29
+ an attacker exactly like a raw `call`. Treat any token whose address is not a fixed trusted
30
+ constant as possibly hook bearing and check every token-move path for reentrancy, not only
31
+ the `call{value}` paths. Clearing a path because the token is assumed to be a normal ERC20
32
+ with no hook is an assumed off-file control unless the token set is pinned to a known
33
+ allowlist, so keep the finding, see the recall red line.
34
+
35
+ Ordering effects before interactions in the one function under review is not enough on its
36
+ own. A transfer hook can reenter a different function that reads or writes the same position,
37
+ loan, or reserve while this flow is only half finished, the cross-function form, so trace
38
+ where control can go during every transfer and check the whole flow's invariants, not just
39
+ the local ones. In a lending, auction, or buyout flow that pays the current holder before it
40
+ finalizes the position's terms, that payout is the callback window, so audit the order of
41
+ the payout against the state writes.
42
+
43
+ ### Vulnerable
44
+ ```solidity
45
+ function withdraw() external {
46
+ uint256 bal = balances[msg.sender];
47
+ (bool ok, ) = msg.sender.call{value: bal}(""); // call before the state update
48
+ require(ok);
49
+ balances[msg.sender] = 0; // too late, attacker reentered
50
+ }
51
+ ```
52
+
53
+ ### Secure
54
+ ```solidity
55
+ function withdraw() external nonReentrant {
56
+ uint256 bal = balances[msg.sender];
57
+ balances[msg.sender] = 0; // effects before interaction
58
+ (bool ok, ) = msg.sender.call{value: bal}("");
59
+ require(ok);
60
+ }
61
+ ```
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: signature-replay
3
3
  title: Signature Replay and Malleability
4
+ lens: signature-replay
4
5
  impact: HIGH
5
6
  tags: [swc-117, swc-121, signature, replay, eip712, fund-loss]
6
7
  aliases: [replay, replay-attack]
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  id: unchecked-low-level-call
3
3
  title: Unchecked Low-Level Call
4
+ lens: unchecked-low-level-call
4
5
  impact: HIGH
5
6
  tags: [swc-104, low-level-call, return-value, fund-loss]
6
7
  aliases: [unchecked-call, unchecked-return]
7
- triggers: [".call(", ".call{value", ".delegatecall(", ".send(", "transfer(", "bool success", "bool ok", "(bool", "safeTransfer", "returndata"]
8
+ triggers: [".call(", ".call{value", ".delegatecall(", ".send(", "transfer(", "bool success", "bool ok", "(bool", "safeTransfer", "returndata", "returndatacopy", "abi.decode(returndata", "excessivelySafeCall"]
8
9
  ---
9
10
 
10
11
  ## Unchecked Low-Level Call
@@ -17,6 +18,12 @@ raw ERC-20 `transfer` on a token that returns false on failure, or returns nothi
17
18
  be checked or wrapped with SafeERC20. Check every low-level return, or use a wrapper that
18
19
  reverts.
19
20
 
21
+ The mirror risk is trusting the returned data rather than the boolean: a callee can return
22
+ an enormous bytes blob so that copying it, an implicit `returndatacopy` or an
23
+ `abi.decode(returndata, ...)`, burns all forwarded gas and griefs the caller, the return-bomb.
24
+ When the return payload is attacker-influenced, cap the copied size or use an
25
+ `excessivelySafeCall` style helper, and never let a callee's return data dictate gas.
26
+
20
27
  ### Vulnerable
21
28
  ```solidity
22
29
  function payout(address to, uint256 amount) external {
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: unsafe-math
3
3
  title: Unchecked Math and Unsafe Cast
4
+ lens: unsafe-math
4
5
  impact: HIGH
5
6
  tags: [swc-101, overflow, downcast, fund-loss]
6
7
  aliases: [unchecked-math, unsafe-cast, downcast-truncation]
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: weird-erc20
3
3
  title: Weird ERC20 Behavior
4
+ lens: weird-erc20
4
5
  impact: HIGH
5
6
  tags: [fee-on-transfer, rebasing, erc777, accounting, fund-loss]
6
7
  aliases: [fee-on-transfer, deflationary-token, rebasing-token, erc777-hook]
@@ -1,7 +1,41 @@
1
1
  ---
2
- description: Run a codejury whole-protocol smart contract security review, interactively, by fanning out per contract
2
+ description: Run a codejury smart contract security review of a diff or a whole repository
3
3
  ---
4
- Run a codejury whole-protocol smart contract security review of: $ARGUMENTS
4
+ Run a codejury smart contract security review of: $ARGUMENTS
5
+
6
+ First decide which path $ARGUMENTS names, the two are different tools, do not mix them.
7
+
8
+ - DIFF REVIEW when $ARGUMENTS is a unified diff file such as a `.diff` or `.patch`, a single
9
+ file of diff text, or a git range such as `origin/main...HEAD`. This path is fully coded,
10
+ you run one command and relay its report, there is no fan-out and no workspace.
11
+ - REPO REVIEW when $ARGUMENTS is a directory, a whole protocol to audit. This path is the
12
+ fan-out you orchestrate, follow the numbered steps under Repo Review.
13
+
14
+ ## Diff Review
15
+
16
+ Run the coded engine and relay its report. There is nothing for you to judge, the engine
17
+ chunks the diff, runs its passes, filters, and prints the findings.
18
+
19
+ ```
20
+ codejury review diff --file <the diff file> --domain evm
21
+ ```
22
+
23
+ For a git range instead of a file, drop `--file` and pass the range, with `--repo` if the
24
+ repository is not the current directory:
25
+
26
+ ```
27
+ codejury review diff --repo <repo dir> --git-range origin/main...HEAD --domain evm
28
+ ```
29
+
30
+ If `codejury` is not on PATH it is a pip-installed console script, so activate the project
31
+ venv first, for example `. .venv/bin/activate`, or run `python -m codejury`.
32
+
33
+ Relay the report as it prints. A failed, rate-limited, blank, or error-exited run is a
34
+ failed review, not a clean pass, surface the error and never report zero findings from a
35
+ broken run. A non-zero exit means a finding hit the severity gate or the audit degraded, say
36
+ which. Then stop, Diff Review does not use the units, the workspace, or the gate below.
37
+
38
+ ## Repo Review
5
39
 
6
40
  You are the orchestrator, not the reviewer. Recall comes from fanning out: the tool gives
7
41
  you a deterministic unit worklist, you run one focused sub-review per contract in parallel,
@@ -64,6 +64,11 @@ of a named guard:
64
64
  moves more of the holder's balance than was approved.
65
65
  - **Signatures**: is a signed privileged message bound to a nonce, a chainid, and a domain
66
66
  separator, the signer checked nonzero? A signature alone is replayable.
67
+ - **Failure mode**: when a call or a guard fails, does the path fail closed or fail open? A
68
+ low-level `call`, `delegatecall`, or `send` whose returned success bool is not checked lets
69
+ execution continue as if it succeeded, and a `try/catch` whose catch branch proceeds rather
70
+ than reverting leaves the contract in a partly updated state. Confirm a failed external call
71
+ or check reverts the transaction rather than silently continuing.
67
72
  - **Trusted-source**: a value is not safe because a caller you treat as trusted set it, if
68
73
  that caller is an arbitrary external account or contract.
69
74
  - **Seeded invariant**: `inventory/_invariants.md` may name a property the operator asserts
@@ -4,13 +4,13 @@ compile and run it, turning a model claim into a reproduced fact. Behind the cod
4
4
  extra and a Foundry toolchain, availability is lazy-checked so importing the domain never
5
5
  needs forge.
6
6
 
7
- Safety is the hard contract, invariant 4. A PoC runs only against a local fork or a dev
7
+ Safety is the hard contract, invariant 6. A PoC runs only against a local fork or a dev
8
8
  node, never a production system. It never holds a real private key, never broadcasts a
9
9
  transaction, and takes no destructive action without explicit operator approval.
10
10
 
11
11
  The generation and run logic lands once Foundry is validated on a real target. Until then
12
12
  this is the wired seam: it reports availability honestly and fails loud when asked to
13
- verify without the tool, never silently confirming or refuting a finding, invariant 3.
13
+ verify without the tool, never silently confirming or refuting a finding, invariant 4.
14
14
  """
15
15
 
16
16
  from __future__ import annotations