codejury 0.19.0__tar.gz → 0.21.0__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.
- {codejury-0.19.0 → codejury-0.21.0}/PKG-INFO +78 -29
- codejury-0.21.0/README.md +165 -0
- codejury-0.21.0/codejury/__main__.py +10 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/cli.py +41 -13
- codejury-0.21.0/codejury/data/commands/codejury-review-repo.md +29 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/detection.yaml +1 -1
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/methodologies/repo-review.md +7 -5
- {codejury-0.19.0 → codejury-0.21.0}/codejury/resources.py +1 -0
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/diff/debate.py +2 -2
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/diff/debate_prompts.py +1 -1
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/diff/engine.py +2 -2
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/diff/prompts.py +1 -1
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/diff/runner.py +4 -4
- codejury-0.21.0/codejury/review/repo/__init__.py +0 -0
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/repo/scaffold.py +2 -2
- {codejury-0.19.0 → codejury-0.21.0}/codejury.egg-info/PKG-INFO +78 -29
- {codejury-0.19.0 → codejury-0.21.0}/codejury.egg-info/SOURCES.txt +14 -11
- {codejury-0.19.0 → codejury-0.21.0}/pyproject.toml +2 -2
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_cli_audit.py +27 -3
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_diff_debate.py +2 -2
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_diff_engine.py +6 -6
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_repo_model.py +4 -1
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_repo_scaffold.py +2 -2
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_vulnerabilities.py +1 -1
- codejury-0.19.0/README.md +0 -116
- {codejury-0.19.0 → codejury-0.21.0}/LICENSE +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/__init__.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/go/echo.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/go/gin.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/javascript/express.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/javascript/nestjs.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/python/celery.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/python/django.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/python/fastapi.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/frameworks/python/flask.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/languages/go.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/languages/javascript.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/languages/python.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/languages/typescript.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/methodologies/security-review-memory.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/protocols/oauth.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/SKILL.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/business-logic.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/code-injection.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/command-injection.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/cross-site-request-forgery.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/cross-site-scripting.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/hardcoded-secrets.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/http-response-splitting.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/improper-authentication.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/information-exposure.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/insecure-cryptography.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/insecure-deserialization.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/insecure-direct-object-reference.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/insecure-transport.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/jwt-validation.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/mass-assignment.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/missing-authorization.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/open-redirect.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/path-traversal.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/race-condition.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/replay-attack.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/server-side-request-forgery.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/server-side-template-injection.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/session-fixation.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/sql-injection.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/data/vulnerabilities/xml-external-entity.md +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/detection.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/domain/__init__.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/domain/finding.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/guides.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/json_parse.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/mddoc.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/__init__.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/anthropic.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/base.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/factory.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/litellm.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/mock.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/openai.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/openai_format.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/providers/retry.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury/report.py +0 -0
- {codejury-0.19.0/codejury/diff → codejury-0.21.0/codejury/review}/__init__.py +0 -0
- {codejury-0.19.0/codejury/repo → codejury-0.21.0/codejury/review/diff}/__init__.py +0 -0
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/diff/findings_filter.py +0 -0
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/diff/vulnerabilities.py +0 -0
- {codejury-0.19.0/codejury → codejury-0.21.0/codejury/review}/repo/model.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury.egg-info/dependency_links.txt +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury.egg-info/entry_points.txt +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury.egg-info/requires.txt +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/codejury.egg-info/top_level.txt +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/setup.cfg +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_anthropic_provider.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_detection.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_guides.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_json_parse.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_litellm_provider.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_mddoc.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_openai_format.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_openai_provider.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_report.py +0 -0
- {codejury-0.19.0 → codejury-0.21.0}/tests/test_retry_provider.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codejury
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: AI code security review
|
|
3
|
+
Version: 0.21.0
|
|
4
|
+
Summary: AI code security review for diffs and whole repositories.
|
|
5
5
|
Author: AISecLabs
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/aiseclabs/codejury
|
|
@@ -40,35 +40,44 @@ Dynamic: license-file
|
|
|
40
40
|
|
|
41
41
|
> AI code security review for diffs and whole repositories.
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
codejury runs two paths matched to their nature.
|
|
44
44
|
|
|
45
45
|
- **Diff Review** is coded. It audits a pull request diff for newly introduced
|
|
46
46
|
exploitable risk, as a single balanced LLM call or an adversarial Finder,
|
|
47
47
|
Challenger, and Judge pass that trades roughly 3x the cost for extra recall on
|
|
48
|
-
subtle flaws that span files.
|
|
49
|
-
- **Repo Review** is agent driven.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
subtle flaws that span files. One command in, findings out.
|
|
49
|
+
- **Repo Review** is agent driven. A whole repository is too large for one LLM
|
|
50
|
+
call, so codejury scaffolds a workspace and hands an interactive agent such as
|
|
51
|
+
Claude Code or Codex a methodology to run. The agent maps the attack surface,
|
|
52
|
+
traces inputs to sinks across files, verifies issues with a real PoC, and
|
|
53
|
+
iterates over rounds with a persistent memory.
|
|
54
|
+
|
|
55
|
+
Security knowledge is data, not code. Vulnerability classes live in
|
|
56
|
+
`codejury/data/vulnerabilities/*.md` with a vulnerable and a secure example per
|
|
57
|
+
language, and the stack guides live under `data/languages`, `data/frameworks`,
|
|
58
|
+
and `data/protocols`. The engine stays language neutral and names no framework,
|
|
59
|
+
so adding a stack is a drop-in markdown file.
|
|
59
60
|
|
|
60
61
|
## Install
|
|
61
62
|
|
|
62
63
|
```bash
|
|
63
|
-
pip install codejury
|
|
64
|
-
pip install "codejury[anthropic]"
|
|
64
|
+
pip install codejury # core
|
|
65
|
+
pip install "codejury[anthropic]" # add a backend, also openai or litellm
|
|
66
|
+
codejury install-slash-command # Claude Code, ~/.claude/commands/
|
|
67
|
+
codejury install-slash-command --agent codex # Codex, ~/.codex/prompts/
|
|
65
68
|
```
|
|
66
69
|
|
|
70
|
+
`install-slash-command` copies the `/codejury-review-repo` command into the agent's
|
|
71
|
+
command directory. The command body is the same for every agent, only the
|
|
72
|
+
directory differs, so pass `--dir` for any other agent. The repo review itself is
|
|
73
|
+
agent neutral, so even without the command you can run `codejury review repo` and
|
|
74
|
+
tell any agent to follow the methodology it writes.
|
|
75
|
+
|
|
67
76
|
## Diff Review
|
|
68
77
|
|
|
69
78
|
```bash
|
|
70
79
|
# audit a diff file
|
|
71
|
-
codejury review diff --
|
|
80
|
+
codejury review diff --file changes.diff
|
|
72
81
|
|
|
73
82
|
# audit a git range in a repo
|
|
74
83
|
codejury review diff --repo /path/to/app --git-range origin/main...HEAD
|
|
@@ -77,10 +86,10 @@ codejury review diff --repo /path/to/app --git-range origin/main...HEAD
|
|
|
77
86
|
git diff HEAD~1 | codejury review diff
|
|
78
87
|
|
|
79
88
|
# adversarial mode, more recall on subtle flaws, about 3x the cost
|
|
80
|
-
codejury review diff --
|
|
89
|
+
codejury review diff --file changes.diff --mode adversarial
|
|
81
90
|
|
|
82
91
|
# CI gate and SARIF
|
|
83
|
-
codejury review diff --
|
|
92
|
+
codejury review diff --file changes.diff --format sarif --fail-on high
|
|
84
93
|
```
|
|
85
94
|
|
|
86
95
|
Configure a backend with `--provider`, `--model`, `--api-key`, `--api-base`, or
|
|
@@ -120,17 +129,49 @@ request.
|
|
|
120
129
|
|
|
121
130
|
## Repo Review
|
|
122
131
|
|
|
132
|
+
Repo Review does not scan and print findings. It sets up a review for an agent to
|
|
133
|
+
run, because a whole repository needs many rounds of reading, cross-file tracing,
|
|
134
|
+
and PoC work that an agent does, not a single call.
|
|
135
|
+
|
|
123
136
|
```bash
|
|
124
137
|
codejury review repo /path/to/your/repo
|
|
125
138
|
```
|
|
126
139
|
|
|
127
|
-
This
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
140
|
+
This detects the stack, seeds the entrypoint inventory and the downstream trace
|
|
141
|
+
targets from a deterministic scan, writes the methodology to
|
|
142
|
+
`<workspace>/METHODOLOGY.md`, and prints a short pointer. It creates the
|
|
143
|
+
workspace:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
entrypoints/ the candidate entrypoint files to start from
|
|
147
|
+
issues/ one write-up per confirmed or suspected issue
|
|
148
|
+
pocs/ a runnable PoC per issue, same name as the issue
|
|
149
|
+
analysis/ _trace_targets.md, the round ledger, and trace notes
|
|
150
|
+
security-review-memory.md
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Then run it with an interactive agent. In Claude Code or Codex:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
/codejury-review-repo /path/to/your/repo
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Any agent works, the slash command is just a shortcut. Without it, tell the agent
|
|
160
|
+
to follow the `METHODOLOGY.md` the scaffold wrote.
|
|
161
|
+
|
|
162
|
+
The agent follows `METHODOLOGY.md`: it maps the attack surface including non HTTP
|
|
163
|
+
sources, traces each input to its sink through the downstream layers, runs an
|
|
164
|
+
Authorization Model pass for missing-auth and IDOR, and follows a control into a
|
|
165
|
+
library when an entrypoint delegates it. It keeps going until a Completeness Gate
|
|
166
|
+
passes, two consecutive rounds that add nothing new. It confirms each issue with
|
|
167
|
+
a real PoC against a sandbox or dev environment and asks you for any credential or
|
|
168
|
+
test data it needs. Only a reproduced PoC is a confirmed finding, and nothing
|
|
169
|
+
runs against production.
|
|
170
|
+
|
|
171
|
+
The supported stacks today are Python with Django, Celery, Flask, and FastAPI,
|
|
172
|
+
Go with Gin and Echo, JavaScript and TypeScript with Express and NestJS, and the
|
|
173
|
+
OAuth and OIDC protocol. The methodology still works on an unguided stack, it just
|
|
174
|
+
leans more on the agent's own knowledge.
|
|
134
175
|
|
|
135
176
|
## Findings
|
|
136
177
|
|
|
@@ -142,6 +183,14 @@ production config leaks.
|
|
|
142
183
|
|
|
143
184
|
## Extending
|
|
144
185
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
186
|
+
Knowledge is data, so extending codejury is a drop-in markdown file with no code
|
|
187
|
+
change.
|
|
188
|
+
|
|
189
|
+
- A vulnerability class: `codejury/data/vulnerabilities/<class>.md` with
|
|
190
|
+
frontmatter of title, impact, tags, and triggers, plus a vulnerable and a
|
|
191
|
+
secure example.
|
|
192
|
+
- A language or framework: `codejury/data/languages/<lang>.md` or
|
|
193
|
+
`codejury/data/frameworks/<lang>/<framework>.md`, declaring its detect signals,
|
|
194
|
+
entrypoint markers, and downstream logic layers.
|
|
195
|
+
- A protocol such as OAuth: `codejury/data/protocols/<name>.md`, detected by
|
|
196
|
+
language neutral content tokens.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
```
|
|
2
|
+
██████╗ ██████╗ ██████╗ ███████╗ ██╗██╗ ██╗██████╗ ██╗ ██╗
|
|
3
|
+
██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██║██║ ██║██╔══██╗╚██╗ ██╔╝
|
|
4
|
+
██║ ██║ ██║██║ ██║█████╗ ██║██║ ██║██████╔╝ ╚████╔╝
|
|
5
|
+
██║ ██║ ██║██║ ██║██╔══╝ ██ ██║██║ ██║██╔══██╗ ╚██╔╝
|
|
6
|
+
╚██████╗╚██████╔╝██████╔╝███████╗╚█████╔╝╚██████╔╝██║ ██║ ██║
|
|
7
|
+
╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
> AI code security review for diffs and whole repositories.
|
|
11
|
+
|
|
12
|
+
codejury runs two paths matched to their nature.
|
|
13
|
+
|
|
14
|
+
- **Diff Review** is coded. It audits a pull request diff for newly introduced
|
|
15
|
+
exploitable risk, as a single balanced LLM call or an adversarial Finder,
|
|
16
|
+
Challenger, and Judge pass that trades roughly 3x the cost for extra recall on
|
|
17
|
+
subtle flaws that span files. One command in, findings out.
|
|
18
|
+
- **Repo Review** is agent driven. A whole repository is too large for one LLM
|
|
19
|
+
call, so codejury scaffolds a workspace and hands an interactive agent such as
|
|
20
|
+
Claude Code or Codex a methodology to run. The agent maps the attack surface,
|
|
21
|
+
traces inputs to sinks across files, verifies issues with a real PoC, and
|
|
22
|
+
iterates over rounds with a persistent memory.
|
|
23
|
+
|
|
24
|
+
Security knowledge is data, not code. Vulnerability classes live in
|
|
25
|
+
`codejury/data/vulnerabilities/*.md` with a vulnerable and a secure example per
|
|
26
|
+
language, and the stack guides live under `data/languages`, `data/frameworks`,
|
|
27
|
+
and `data/protocols`. The engine stays language neutral and names no framework,
|
|
28
|
+
so adding a stack is a drop-in markdown file.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install codejury # core
|
|
34
|
+
pip install "codejury[anthropic]" # add a backend, also openai or litellm
|
|
35
|
+
codejury install-slash-command # Claude Code, ~/.claude/commands/
|
|
36
|
+
codejury install-slash-command --agent codex # Codex, ~/.codex/prompts/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`install-slash-command` copies the `/codejury-review-repo` command into the agent's
|
|
40
|
+
command directory. The command body is the same for every agent, only the
|
|
41
|
+
directory differs, so pass `--dir` for any other agent. The repo review itself is
|
|
42
|
+
agent neutral, so even without the command you can run `codejury review repo` and
|
|
43
|
+
tell any agent to follow the methodology it writes.
|
|
44
|
+
|
|
45
|
+
## Diff Review
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# audit a diff file
|
|
49
|
+
codejury review diff --file changes.diff
|
|
50
|
+
|
|
51
|
+
# audit a git range in a repo
|
|
52
|
+
codejury review diff --repo /path/to/app --git-range origin/main...HEAD
|
|
53
|
+
|
|
54
|
+
# from stdin
|
|
55
|
+
git diff HEAD~1 | codejury review diff
|
|
56
|
+
|
|
57
|
+
# adversarial mode, more recall on subtle flaws, about 3x the cost
|
|
58
|
+
codejury review diff --file changes.diff --mode adversarial
|
|
59
|
+
|
|
60
|
+
# CI gate and SARIF
|
|
61
|
+
codejury review diff --file changes.diff --format sarif --fail-on high
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Configure a backend with `--provider`, `--model`, `--api-key`, `--api-base`, or
|
|
65
|
+
the `CODEJURY_API_KEY`, `CODEJURY_MODEL`, and `CODEJURY_API_BASE` environment
|
|
66
|
+
variables. `codejury review diff --dry-run` exercises the engine with a mock
|
|
67
|
+
provider and no key, and falls back to a built in demo diff when you pass none.
|
|
68
|
+
|
|
69
|
+
### Choosing a Model and Mode
|
|
70
|
+
|
|
71
|
+
Detection quality is dominated by the model first, then the mode. On real diff
|
|
72
|
+
probes:
|
|
73
|
+
|
|
74
|
+
- A strong model at the Claude Sonnet tier in standard mode caught every planted
|
|
75
|
+
vulnerability with almost no false positives. A weaker model raised false
|
|
76
|
+
positives in both modes, so the model is the lever that matters most.
|
|
77
|
+
- Adversarial mode did not lower false positives over standard on those probes
|
|
78
|
+
and costs about 3x. Reach for it to gain recall on subtle logic that spans
|
|
79
|
+
files, not as a way to cut false positives.
|
|
80
|
+
|
|
81
|
+
Default to standard mode with a strong model, set with `--model` or
|
|
82
|
+
`CODEJURY_MODEL`. False positives are held down by the do not report list and the
|
|
83
|
+
post filter, not by the mode.
|
|
84
|
+
|
|
85
|
+
### Use in CI with GitHub Actions
|
|
86
|
+
|
|
87
|
+
Audit every pull request and surface findings in the code scanning tab. Copy
|
|
88
|
+
`examples/codejury-pr-review.yml` into `.github/workflows/`, add a
|
|
89
|
+
`CODEJURY_API_KEY` repository secret, and it will
|
|
90
|
+
|
|
91
|
+
1. diff the pull request against its base with `--git-range origin/<base>...HEAD`,
|
|
92
|
+
2. write SARIF and upload it with `github/codeql-action/upload-sarif`,
|
|
93
|
+
3. fail the check on a HIGH or CRITICAL finding with `--fail-on high`.
|
|
94
|
+
|
|
95
|
+
The job makes one model call per pull request in standard mode. The SARIF is
|
|
96
|
+
uploaded even when the gate fails, so findings always show up on the pull
|
|
97
|
+
request.
|
|
98
|
+
|
|
99
|
+
## Repo Review
|
|
100
|
+
|
|
101
|
+
Repo Review does not scan and print findings. It sets up a review for an agent to
|
|
102
|
+
run, because a whole repository needs many rounds of reading, cross-file tracing,
|
|
103
|
+
and PoC work that an agent does, not a single call.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
codejury review repo /path/to/your/repo
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This detects the stack, seeds the entrypoint inventory and the downstream trace
|
|
110
|
+
targets from a deterministic scan, writes the methodology to
|
|
111
|
+
`<workspace>/METHODOLOGY.md`, and prints a short pointer. It creates the
|
|
112
|
+
workspace:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
entrypoints/ the candidate entrypoint files to start from
|
|
116
|
+
issues/ one write-up per confirmed or suspected issue
|
|
117
|
+
pocs/ a runnable PoC per issue, same name as the issue
|
|
118
|
+
analysis/ _trace_targets.md, the round ledger, and trace notes
|
|
119
|
+
security-review-memory.md
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Then run it with an interactive agent. In Claude Code or Codex:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
/codejury-review-repo /path/to/your/repo
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Any agent works, the slash command is just a shortcut. Without it, tell the agent
|
|
129
|
+
to follow the `METHODOLOGY.md` the scaffold wrote.
|
|
130
|
+
|
|
131
|
+
The agent follows `METHODOLOGY.md`: it maps the attack surface including non HTTP
|
|
132
|
+
sources, traces each input to its sink through the downstream layers, runs an
|
|
133
|
+
Authorization Model pass for missing-auth and IDOR, and follows a control into a
|
|
134
|
+
library when an entrypoint delegates it. It keeps going until a Completeness Gate
|
|
135
|
+
passes, two consecutive rounds that add nothing new. It confirms each issue with
|
|
136
|
+
a real PoC against a sandbox or dev environment and asks you for any credential or
|
|
137
|
+
test data it needs. Only a reproduced PoC is a confirmed finding, and nothing
|
|
138
|
+
runs against production.
|
|
139
|
+
|
|
140
|
+
The supported stacks today are Python with Django, Celery, Flask, and FastAPI,
|
|
141
|
+
Go with Gin and Echo, JavaScript and TypeScript with Express and NestJS, and the
|
|
142
|
+
OAuth and OIDC protocol. The methodology still works on an unguided stack, it just
|
|
143
|
+
leans more on the agent's own knowledge.
|
|
144
|
+
|
|
145
|
+
## Findings
|
|
146
|
+
|
|
147
|
+
Each finding carries a file and line, a severity and category, a concrete exploit
|
|
148
|
+
scenario, a recommendation, and a confidence. A false positive filter drops test
|
|
149
|
+
paths, mock paths, and low confidence noise. The model is also told not to report
|
|
150
|
+
dependency CVEs, style notes, speculation, or risks that only matter when
|
|
151
|
+
production config leaks.
|
|
152
|
+
|
|
153
|
+
## Extending
|
|
154
|
+
|
|
155
|
+
Knowledge is data, so extending codejury is a drop-in markdown file with no code
|
|
156
|
+
change.
|
|
157
|
+
|
|
158
|
+
- A vulnerability class: `codejury/data/vulnerabilities/<class>.md` with
|
|
159
|
+
frontmatter of title, impact, tags, and triggers, plus a vulnerable and a
|
|
160
|
+
secure example.
|
|
161
|
+
- A language or framework: `codejury/data/languages/<lang>.md` or
|
|
162
|
+
`codejury/data/frameworks/<lang>/<framework>.md`, declaring its detect signals,
|
|
163
|
+
entrypoint markers, and downstream logic layers.
|
|
164
|
+
- A protocol such as OAuth: `codejury/data/protocols/<name>.md`, detected by
|
|
165
|
+
language neutral content tokens.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Entry point for `python -m codejury`, mirroring the `codejury` console script.
|
|
2
|
+
|
|
3
|
+
Lets the tool run without the console script on PATH, for example from a fresh
|
|
4
|
+
shell that has not activated the project venv: `python -m codejury ...`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from codejury.cli import main
|
|
8
|
+
|
|
9
|
+
if __name__ == "__main__":
|
|
10
|
+
raise SystemExit(main())
|
|
@@ -4,12 +4,12 @@ Two paths matched to their nature:
|
|
|
4
4
|
|
|
5
5
|
- ``review diff`` runs the coded diff engine over a unified diff: a single
|
|
6
6
|
balanced call (standard) or the adversarial Finder/Challenger/Judge pass.
|
|
7
|
-
- ``review repo
|
|
7
|
+
- ``review repo <dir>`` scaffolds a workspace and prints the methodology for an
|
|
8
8
|
interactive agent to run a whole-repo review (it does not run an LLM pipeline,
|
|
9
9
|
which a single call cannot do for a whole codebase).
|
|
10
10
|
|
|
11
11
|
``review diff --dry-run`` exercises the engine with a mock provider and no key.
|
|
12
|
-
The audit orchestration itself lives in ``codejury.diff.runner``.
|
|
12
|
+
The audit orchestration itself lives in ``codejury.review.diff.runner``.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
@@ -17,10 +17,11 @@ from __future__ import annotations
|
|
|
17
17
|
import argparse
|
|
18
18
|
import subprocess
|
|
19
19
|
import sys
|
|
20
|
+
from pathlib import Path
|
|
20
21
|
|
|
21
22
|
from codejury import __version__
|
|
22
23
|
from codejury.report import gate, render
|
|
23
|
-
from codejury.diff.runner import audit_diff
|
|
24
|
+
from codejury.review.diff.runner import audit_diff
|
|
24
25
|
from codejury.providers.factory import (
|
|
25
26
|
DEFAULT_API_BASE,
|
|
26
27
|
DEFAULT_API_KEY,
|
|
@@ -32,15 +33,15 @@ from codejury.providers.factory import (
|
|
|
32
33
|
make_provider,
|
|
33
34
|
)
|
|
34
35
|
from codejury.providers.mock import MockProvider
|
|
35
|
-
from codejury.repo.scaffold import scaffold
|
|
36
|
+
from codejury.review.repo.scaffold import scaffold
|
|
36
37
|
|
|
37
38
|
_FORMATS = ("text", "markdown", "json", "sarif")
|
|
38
39
|
_FAIL_ON = ("critical", "high", "medium", "low")
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
def _read_diff(args) -> str:
|
|
42
|
-
if args.
|
|
43
|
-
with open(args.
|
|
43
|
+
if args.file:
|
|
44
|
+
with open(args.file, encoding="utf-8") as f:
|
|
44
45
|
return f.read()
|
|
45
46
|
if args.git_range:
|
|
46
47
|
return subprocess.run(
|
|
@@ -65,7 +66,7 @@ _MOCK_REPLY = (
|
|
|
65
66
|
|
|
66
67
|
def _add_audit_args(p) -> None:
|
|
67
68
|
"""The diff-audit flags for `review diff`."""
|
|
68
|
-
p.add_argument("--
|
|
69
|
+
p.add_argument("--file", default=None, help="unified diff file (default: read stdin)")
|
|
69
70
|
p.add_argument("--repo", default=None, help="repo path for --git-range")
|
|
70
71
|
p.add_argument("--git-range", default=None, help="git range to diff, e.g. origin/main...HEAD")
|
|
71
72
|
p.add_argument("--dry-run", action="store_true",
|
|
@@ -99,6 +100,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
99
100
|
repo.add_argument("directory", help="target repository to review")
|
|
100
101
|
repo.add_argument("--workspace", default="codejury-review", help="where to create the review workspace")
|
|
101
102
|
|
|
103
|
+
inst = sub.add_parser("install-slash-command",
|
|
104
|
+
help="install the /codejury-review-repo slash command for an agent")
|
|
105
|
+
inst.add_argument("--agent", choices=("claude", "codex"), default="claude",
|
|
106
|
+
help="which agent's command directory to install into")
|
|
107
|
+
inst.add_argument("--dir", default=None, help="explicit target directory, overrides --agent")
|
|
108
|
+
|
|
102
109
|
args = parser.parse_args(argv)
|
|
103
110
|
try:
|
|
104
111
|
return _dispatch(args, parser)
|
|
@@ -115,7 +122,7 @@ def _dispatch(args, parser) -> int:
|
|
|
115
122
|
provider = MockProvider(default=_MOCK_REPLY)
|
|
116
123
|
model = "mock"
|
|
117
124
|
# zero-config smoke test: fall back to a built-in demo diff when none is supplied
|
|
118
|
-
diff = _read_diff(args) if (args.
|
|
125
|
+
diff = _read_diff(args) if (args.file or args.git_range) else _dry_run_diff()
|
|
119
126
|
else:
|
|
120
127
|
provider = make_provider(args.provider, api_key=args.api_key, api_base=args.api_base, retries=args.retries)
|
|
121
128
|
model = args.model
|
|
@@ -131,14 +138,35 @@ def _dispatch(args, parser) -> int:
|
|
|
131
138
|
|
|
132
139
|
if args.command == "review" and scope == "repo":
|
|
133
140
|
res = scaffold(args.directory, args.workspace)
|
|
134
|
-
|
|
141
|
+
(Path(res.workspace) / "METHODOLOGY.md").write_text(res.methodology, encoding="utf-8")
|
|
142
|
+
print(f"Workspace ready: {res.workspace}", file=sys.stderr)
|
|
135
143
|
if res.guides:
|
|
136
|
-
print(f"Detected stack: {', '.join(res.guides)}
|
|
144
|
+
print(f"Detected stack: {', '.join(res.guides)}, notes in {res.workspace}/_stack.md", file=sys.stderr)
|
|
137
145
|
print(f"Flagged {len(res.candidate_files)} candidate entrypoint files into "
|
|
138
146
|
f"{res.workspace}/entrypoints/_entrypoints.md", file=sys.stderr)
|
|
139
|
-
print(f"
|
|
140
|
-
print(
|
|
141
|
-
|
|
147
|
+
print(f"Methodology: {res.workspace}/METHODOLOGY.md", file=sys.stderr)
|
|
148
|
+
print(
|
|
149
|
+
"This command sets up the review, it does not find the issues itself. Next, have an "
|
|
150
|
+
f"interactive agent follow {res.workspace}/METHODOLOGY.md to run the review, or use the "
|
|
151
|
+
"/codejury-review-repo command in Claude Code or Codex. Findings are written to "
|
|
152
|
+
f"{res.workspace}/issues/."
|
|
153
|
+
)
|
|
154
|
+
return 0
|
|
155
|
+
|
|
156
|
+
if args.command == "install-slash-command":
|
|
157
|
+
from codejury.resources import COMMANDS_DIR
|
|
158
|
+
# the command body is portable, only the directory differs per agent
|
|
159
|
+
agent_dirs = {
|
|
160
|
+
"claude": Path.home() / ".claude" / "commands",
|
|
161
|
+
"codex": Path.home() / ".codex" / "prompts",
|
|
162
|
+
}
|
|
163
|
+
target_dir = Path(args.dir) if args.dir else agent_dirs[args.agent]
|
|
164
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
name = "codejury-review-repo.md"
|
|
166
|
+
dst = target_dir / name
|
|
167
|
+
dst.write_text((COMMANDS_DIR / name).read_text(encoding="utf-8"), encoding="utf-8")
|
|
168
|
+
print(f"Installed slash command to {dst}")
|
|
169
|
+
print("Run it in the agent with: /codejury-review-repo <repository>")
|
|
142
170
|
return 0
|
|
143
171
|
|
|
144
172
|
if args.command == "review": # no scope given
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run a codejury whole-repo security review on a repository, interactively
|
|
3
|
+
---
|
|
4
|
+
Run a codejury whole-repository security review of: $ARGUMENTS
|
|
5
|
+
|
|
6
|
+
1. Scaffold the workspace:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
codejury review repo $ARGUMENTS --workspace /tmp/codejury-review
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
If `codejury` is not on PATH it is a pip-installed console script, so activate
|
|
13
|
+
the project venv first, for example `. .venv/bin/activate`, or run it through
|
|
14
|
+
that venv's Python, for example `python -m codejury`.
|
|
15
|
+
|
|
16
|
+
2. Read `<workspace>/METHODOLOGY.md` and follow it to completion. It is the single
|
|
17
|
+
source of truth for how to run the review, the entrypoint map, the trace
|
|
18
|
+
targets, the Authorization Model pass, the dependency-control checks, the round
|
|
19
|
+
ledger, and the Completeness Gate. Do not improvise a different process.
|
|
20
|
+
|
|
21
|
+
3. Verify each issue with a real PoC, human in the loop. Ask me for any
|
|
22
|
+
credential, test account, or MFA step you need. Never touch production, never
|
|
23
|
+
use real credentials, and never run a destructive action without my go-ahead.
|
|
24
|
+
Only a reproduced PoC is a confirmed finding.
|
|
25
|
+
|
|
26
|
+
4. Report confirmed findings, the ones with a reproduced PoC, separately from
|
|
27
|
+
suspected ones still blocked on verification, as a table: title, class,
|
|
28
|
+
`file:line`, exploit, verification status. The issue files live in the
|
|
29
|
+
workspace `issues/`.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# data edit. This is distinct from a guide's stack detection, which decides which
|
|
5
5
|
# language, framework, or protocol applies.
|
|
6
6
|
|
|
7
|
-
skip_dirs: [".git", ".venv", "venv", "node_modules", "__pycache__", ".mypy_cache", ".pytest_cache"]
|
|
7
|
+
skip_dirs: [".git", ".venv", "venv", "node_modules", "__pycache__", ".mypy_cache", ".pytest_cache", "build", "dist", "target", "out", ".tox", ".eggs", ".next", ".nuxt"]
|
|
8
8
|
|
|
9
9
|
source_extensions: [".py", ".js", ".ts", ".tsx", ".jsx", ".mjs", ".go", ".rb", ".java", ".kt", ".php", ".cs", ".scala", ".rs"]
|
|
10
10
|
|
|
@@ -147,10 +147,12 @@ exploitable exposure, so it is in scope. See "Controls That Live in a Library".
|
|
|
147
147
|
|
|
148
148
|
## Recording an Issue
|
|
149
149
|
|
|
150
|
-
Write one `issues/<name>.md` per issue, and save its PoC as a
|
|
151
|
-
`
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
Write one `issues/<name>.md` per issue, the write-up only, and save its PoC as a
|
|
151
|
+
real runnable file `pocs/<name>.<ext>` with the **same `<name>`** so the two pair
|
|
152
|
+
one to one, a script or an `.http` file, not a sketch in prose and not a `.md`.
|
|
153
|
+
Keep `issues/` write-ups and `pocs/` scripts in their own directories, do not mix
|
|
154
|
+
them. If you cannot write a concrete runnable PoC, the finding is most likely a
|
|
155
|
+
false positive, so do not report it. Each issue file must have:
|
|
154
156
|
|
|
155
157
|
```markdown
|
|
156
158
|
# <title>
|
|
@@ -167,7 +169,7 @@ positive, so do not report it. Each issue file must have:
|
|
|
167
169
|
(end-to-end, actionable steps)
|
|
168
170
|
|
|
169
171
|
## PoC
|
|
170
|
-
(the path to `
|
|
172
|
+
(the path to `pocs/<name>.<ext>` and how to run it)
|
|
171
173
|
|
|
172
174
|
## Verification
|
|
173
175
|
(the actual output of running the PoC, or the exact blocker)
|
|
@@ -14,3 +14,4 @@ FRAMEWORKS_DIR = _DATA / "frameworks" # per-framework review guides (how the ta
|
|
|
14
14
|
PROTOCOLS_DIR = _DATA / "protocols" # protocol guides such as oauth (what to check)
|
|
15
15
|
METHODOLOGIES_DIR = _DATA / "methodologies" # repo-review methodology and memory template
|
|
16
16
|
DETECTION_FILE = _DATA / "detection.yaml" # file and path classification across ecosystems
|
|
17
|
+
COMMANDS_DIR = _DATA / "commands" # Claude Code slash commands shipped for install
|
|
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
from dataclasses import dataclass, field
|
|
13
13
|
|
|
14
|
-
from codejury.diff.debate_prompts import (
|
|
14
|
+
from codejury.review.diff.debate_prompts import (
|
|
15
15
|
CHALLENGER_SYSTEM,
|
|
16
16
|
FINDER_SYSTEM,
|
|
17
17
|
JUDGE_SYSTEM,
|
|
@@ -19,7 +19,7 @@ from codejury.diff.debate_prompts import (
|
|
|
19
19
|
finder_prompt,
|
|
20
20
|
judge_prompt,
|
|
21
21
|
)
|
|
22
|
-
from codejury.diff.vulnerabilities import vulnerabilities_for_diff
|
|
22
|
+
from codejury.review.diff.vulnerabilities import vulnerabilities_for_diff
|
|
23
23
|
from codejury.domain.finding import Finding, findings_from_list
|
|
24
24
|
from codejury.json_parse import extract_json_object
|
|
25
25
|
from codejury.providers.base import Message, Provider
|
|
@@ -16,7 +16,7 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
18
|
|
|
19
|
-
from codejury.diff.prompts import DO_NOT_REPORT, FOCUS, category_block
|
|
19
|
+
from codejury.review.diff.prompts import DO_NOT_REPORT, FOCUS, category_block
|
|
20
20
|
|
|
21
21
|
_FINDING_FIELDS = (
|
|
22
22
|
'{"file": "path", "line": 0, "severity": "CRITICAL|HIGH|MEDIUM|LOW", '
|
|
@@ -10,8 +10,8 @@ from __future__ import annotations
|
|
|
10
10
|
import re
|
|
11
11
|
|
|
12
12
|
from codejury.domain.finding import Finding, findings_from_list
|
|
13
|
-
from codejury.diff.prompts import SYSTEM, standard_audit_prompt
|
|
14
|
-
from codejury.diff.vulnerabilities import vulnerabilities_for_diff
|
|
13
|
+
from codejury.review.diff.prompts import SYSTEM, standard_audit_prompt
|
|
14
|
+
from codejury.review.diff.vulnerabilities import vulnerabilities_for_diff
|
|
15
15
|
from codejury.guides import select_guides
|
|
16
16
|
from codejury.json_parse import extract_json_object
|
|
17
17
|
from codejury.providers.base import Message, Provider
|
|
@@ -46,7 +46,7 @@ _JSON_SHAPE = (
|
|
|
46
46
|
|
|
47
47
|
def category_block() -> str:
|
|
48
48
|
"""The closed category set the model must choose from, the vulnerability ids."""
|
|
49
|
-
from codejury.diff.vulnerabilities import allowed_categories
|
|
49
|
+
from codejury.review.diff.vulnerabilities import allowed_categories
|
|
50
50
|
|
|
51
51
|
cats = allowed_categories()
|
|
52
52
|
return (
|
|
@@ -10,10 +10,10 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import dataclasses
|
|
12
12
|
|
|
13
|
-
from codejury.diff.debate import AdversarialAuditRunner
|
|
14
|
-
from codejury.diff.engine import AuditRunner
|
|
15
|
-
from codejury.diff.findings_filter import FindingsFilter
|
|
16
|
-
from codejury.diff.vulnerabilities import allowed_categories, normalize_category
|
|
13
|
+
from codejury.review.diff.debate import AdversarialAuditRunner
|
|
14
|
+
from codejury.review.diff.engine import AuditRunner
|
|
15
|
+
from codejury.review.diff.findings_filter import FindingsFilter
|
|
16
|
+
from codejury.review.diff.vulnerabilities import allowed_categories, normalize_category
|
|
17
17
|
from codejury.domain.finding import Finding
|
|
18
18
|
|
|
19
19
|
# A diff larger than this is audited file-by-file so a big PR does not overflow
|
|
File without changes
|
|
@@ -21,7 +21,7 @@ from codejury.guides import (
|
|
|
21
21
|
logic_layer_globs,
|
|
22
22
|
select_guides,
|
|
23
23
|
)
|
|
24
|
-
from codejury.repo.model import build_repo_model_from_dir, candidate_entrypoint_files, logic_layer_files
|
|
24
|
+
from codejury.review.repo.model import build_repo_model_from_dir, candidate_entrypoint_files, logic_layer_files
|
|
25
25
|
from codejury.resources import METHODOLOGIES_DIR
|
|
26
26
|
|
|
27
27
|
_METHODOLOGY = METHODOLOGIES_DIR / "repo-review.md"
|
|
@@ -151,7 +151,7 @@ def scaffold(target: str | Path, workspace: str | Path) -> ScaffoldResult:
|
|
|
151
151
|
project = target.name
|
|
152
152
|
ws = Path(workspace) / project
|
|
153
153
|
created: list[str] = []
|
|
154
|
-
for sub in ("entrypoints", "issues", "analysis"):
|
|
154
|
+
for sub in ("entrypoints", "issues", "pocs", "analysis"):
|
|
155
155
|
d = ws / sub
|
|
156
156
|
if not d.exists():
|
|
157
157
|
d.mkdir(parents=True, exist_ok=True)
|