codejury 0.14.6__tar.gz → 0.15.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.14.6 → codejury-0.15.0}/PKG-INFO +45 -14
- codejury-0.15.0/README.md +104 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/cli.py +1 -1
- codejury-0.14.6/codejury/data/agent/full-review.md → codejury-0.15.0/codejury/data/agent/repo-review.md +13 -13
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/SKILL.md +1 -1
- {codejury-0.14.6 → codejury-0.15.0}/codejury/repo/model.py +14 -6
- {codejury-0.14.6 → codejury-0.15.0}/codejury/repo/scaffold.py +8 -7
- {codejury-0.14.6 → codejury-0.15.0}/codejury/resources.py +1 -1
- {codejury-0.14.6 → codejury-0.15.0}/codejury.egg-info/PKG-INFO +45 -14
- {codejury-0.14.6 → codejury-0.15.0}/codejury.egg-info/SOURCES.txt +1 -1
- {codejury-0.14.6 → codejury-0.15.0}/pyproject.toml +1 -1
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_repo_model.py +12 -6
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_repo_scaffold.py +4 -4
- codejury-0.14.6/README.md +0 -73
- {codejury-0.14.6 → codejury-0.15.0}/LICENSE +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/__init__.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/agent/security-review-memory.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/entrypoints.yaml +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/business-logic.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/code-injection.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/command-injection.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/cross-site-request-forgery.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/cross-site-scripting.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/hardcoded-secrets.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/http-response-splitting.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/improper-authentication.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/information-exposure.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/insecure-cryptography.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/insecure-deserialization.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/insecure-direct-object-reference.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/insecure-transport.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/jwt-validation.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/mass-assignment.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/missing-authorization.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/open-redirect.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/path-traversal.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/race-condition.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/replay-attack.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/server-side-request-forgery.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/server-side-template-injection.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/session-fixation.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/sql-injection.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/data/rules/xml-external-entity.md +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/__init__.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/debate.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/debate_prompts.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/engine.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/findings_filter.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/prompts.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/rules.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/diff/runner.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/domain/__init__.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/domain/finding.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/json_parse.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/__init__.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/anthropic.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/base.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/factory.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/litellm.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/mock.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/openai.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/openai_format.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/providers/retry.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/repo/__init__.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury/report.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury.egg-info/dependency_links.txt +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury.egg-info/entry_points.txt +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury.egg-info/requires.txt +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/codejury.egg-info/top_level.txt +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/setup.cfg +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_anthropic_provider.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_cli_audit.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_diff_debate.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_diff_engine.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_json_parse.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_litellm_provider.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_openai_format.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_openai_provider.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_report.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_retry_provider.py +0 -0
- {codejury-0.14.6 → codejury-0.15.0}/tests/test_rules.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codejury
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: AI code security review: an adversarial diff-audit engine and an agent-driven whole-repo review methodology, with security knowledge as rich rules
|
|
5
5
|
Author: AISecLabs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -34,13 +34,14 @@ Dynamic: license-file
|
|
|
34
34
|
AI code security review, in two paths matched to their nature:
|
|
35
35
|
|
|
36
36
|
- **Diff review** (coded): audit a pull request's diff for newly introduced,
|
|
37
|
-
exploitable risks. A single balanced LLM call, or an adversarial
|
|
38
|
-
Finder/Challenger/Judge pass
|
|
37
|
+
exploitable risks. A single balanced LLM call (the default), or an adversarial
|
|
38
|
+
Finder/Challenger/Judge pass that trades roughly 3x the cost for extra recall
|
|
39
|
+
on subtle, cross-cutting flaws.
|
|
39
40
|
- **Whole-repo review** (agent-driven): a methodology an interactive agent
|
|
40
|
-
(Claude Code, Codex) runs to
|
|
41
|
-
verify issues with a real PoC, and iterate over rounds with
|
|
42
|
-
memory. Too large for a single LLM call, so codejury ships the
|
|
43
|
-
scaffolds the workspace rather than running a pipeline.
|
|
41
|
+
(Claude Code, Codex) runs to map a codebase's attack surface, trace inputs to
|
|
42
|
+
sinks across files, verify issues with a real PoC, and iterate over rounds with
|
|
43
|
+
a persistent memory. Too large for a single LLM call, so codejury ships the
|
|
44
|
+
methodology and scaffolds the workspace rather than running a pipeline.
|
|
44
45
|
|
|
45
46
|
Security knowledge lives in **rich rules** (`codejury/data/rules/*.md`, with
|
|
46
47
|
per-language vulnerable/secure examples), injected into the audit prompt, not
|
|
@@ -65,7 +66,7 @@ codejury review diff --repo /path/to/app --git-range origin/main...HEAD
|
|
|
65
66
|
# from stdin
|
|
66
67
|
git diff HEAD~1 | codejury review diff
|
|
67
68
|
|
|
68
|
-
# adversarial mode: Finder + Challenger + Judge (
|
|
69
|
+
# adversarial mode: Finder + Challenger + Judge (extra recall on subtle flaws, ~3x cost)
|
|
69
70
|
codejury review diff --diff-file changes.diff --mode adversarial
|
|
70
71
|
|
|
71
72
|
# CI gate + SARIF
|
|
@@ -77,18 +78,48 @@ Configure a backend with `--provider`/`--model`/`--api-key`/`--api-base` or the
|
|
|
77
78
|
`codejury review diff --dry-run` exercises the engine with a mock provider and
|
|
78
79
|
no key (it uses a built-in demo diff when you do not pass one).
|
|
79
80
|
|
|
81
|
+
### Choosing a model and mode
|
|
82
|
+
|
|
83
|
+
Detection quality is dominated by the model first, then the mode. On real-diff
|
|
84
|
+
probes:
|
|
85
|
+
|
|
86
|
+
- A strong model (Claude Sonnet tier) in **standard** mode caught every planted
|
|
87
|
+
vulnerability with near-zero false positives. A weaker model raised false
|
|
88
|
+
positives in both modes, so the model is the lever that matters most.
|
|
89
|
+
- **Adversarial** mode did not lower false positives over standard on those
|
|
90
|
+
probes and costs ~3x. Reach for it for extra recall on subtle, cross-file
|
|
91
|
+
logic, not as a false-positive reducer.
|
|
92
|
+
|
|
93
|
+
Default to **standard mode with a strong model** (set it with `--model` or
|
|
94
|
+
`CODEJURY_MODEL`). False positives are held down by the do-not-report list and
|
|
95
|
+
the post-filter, not by the mode.
|
|
96
|
+
|
|
97
|
+
### Use in CI (GitHub Actions)
|
|
98
|
+
|
|
99
|
+
Audit every pull request and surface findings in the code scanning tab. Copy
|
|
100
|
+
[`examples/codejury-pr-review.yml`](examples/codejury-pr-review.yml) into
|
|
101
|
+
`.github/workflows/`, add a `CODEJURY_API_KEY` repo secret, and it will:
|
|
102
|
+
|
|
103
|
+
1. diff the PR against its base (`--git-range origin/<base>...HEAD`),
|
|
104
|
+
2. write SARIF and upload it with `github/codeql-action/upload-sarif`,
|
|
105
|
+
3. fail the check on a HIGH or CRITICAL finding (`--fail-on high`).
|
|
106
|
+
|
|
107
|
+
The job makes one model call per PR (standard mode); the SARIF is uploaded even
|
|
108
|
+
when the gate fails, so findings always show up on the PR.
|
|
109
|
+
|
|
80
110
|
## Whole-repo review
|
|
81
111
|
|
|
82
112
|
```bash
|
|
83
113
|
codejury review repo /path/to/your/repo
|
|
84
114
|
```
|
|
85
115
|
|
|
86
|
-
This scaffolds a review workspace (`
|
|
87
|
-
`security-review-memory.md`), seeds the
|
|
88
|
-
and prints the methodology. Run it with an interactive
|
|
89
|
-
methodology and the rules,
|
|
90
|
-
high-confidence issues with a PoC, and
|
|
91
|
-
positives along the way. Nothing runs
|
|
116
|
+
This scaffolds a review workspace (`entrypoints/`, `issues/`, `analysis/`, and a
|
|
117
|
+
`security-review-memory.md`), seeds the entrypoint inventory from a
|
|
118
|
+
deterministic scan, and prints the methodology. Run it with an interactive
|
|
119
|
+
agent: it reads the methodology and the rules, maps the attack surface, traces
|
|
120
|
+
inputs to sinks across files, records high-confidence issues with a PoC, and
|
|
121
|
+
asks you to confirm credentials or false positives along the way. Nothing runs
|
|
122
|
+
against production.
|
|
92
123
|
|
|
93
124
|
## Findings
|
|
94
125
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# codejury
|
|
2
|
+
|
|
3
|
+
AI code security review, in two paths matched to their nature:
|
|
4
|
+
|
|
5
|
+
- **Diff review** (coded): audit a pull request's diff for newly introduced,
|
|
6
|
+
exploitable risks. A single balanced LLM call (the default), or an adversarial
|
|
7
|
+
Finder/Challenger/Judge pass that trades roughly 3x the cost for extra recall
|
|
8
|
+
on subtle, cross-cutting flaws.
|
|
9
|
+
- **Whole-repo review** (agent-driven): a methodology an interactive agent
|
|
10
|
+
(Claude Code, Codex) runs to map a codebase's attack surface, trace inputs to
|
|
11
|
+
sinks across files, verify issues with a real PoC, and iterate over rounds with
|
|
12
|
+
a persistent memory. Too large for a single LLM call, so codejury ships the
|
|
13
|
+
methodology and scaffolds the workspace rather than running a pipeline.
|
|
14
|
+
|
|
15
|
+
Security knowledge lives in **rich rules** (`codejury/data/rules/*.md`, with
|
|
16
|
+
per-language vulnerable/secure examples), injected into the audit prompt, not
|
|
17
|
+
buried in code.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install codejury # core
|
|
23
|
+
pip install "codejury[anthropic]" # or [openai] / [litellm] for a backend
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Diff review
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# audit a diff file
|
|
30
|
+
codejury review diff --diff-file changes.diff
|
|
31
|
+
|
|
32
|
+
# audit a git range in a repo
|
|
33
|
+
codejury review diff --repo /path/to/app --git-range origin/main...HEAD
|
|
34
|
+
|
|
35
|
+
# from stdin
|
|
36
|
+
git diff HEAD~1 | codejury review diff
|
|
37
|
+
|
|
38
|
+
# adversarial mode: Finder + Challenger + Judge (extra recall on subtle flaws, ~3x cost)
|
|
39
|
+
codejury review diff --diff-file changes.diff --mode adversarial
|
|
40
|
+
|
|
41
|
+
# CI gate + SARIF
|
|
42
|
+
codejury review diff --diff-file changes.diff --format sarif --fail-on high
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Configure a backend with `--provider`/`--model`/`--api-key`/`--api-base` or the
|
|
46
|
+
`CODEJURY_API_KEY` / `CODEJURY_MODEL` / `CODEJURY_API_BASE` environment variables.
|
|
47
|
+
`codejury review diff --dry-run` exercises the engine with a mock provider and
|
|
48
|
+
no key (it uses a built-in demo diff when you do not pass one).
|
|
49
|
+
|
|
50
|
+
### Choosing a model and mode
|
|
51
|
+
|
|
52
|
+
Detection quality is dominated by the model first, then the mode. On real-diff
|
|
53
|
+
probes:
|
|
54
|
+
|
|
55
|
+
- A strong model (Claude Sonnet tier) in **standard** mode caught every planted
|
|
56
|
+
vulnerability with near-zero false positives. A weaker model raised false
|
|
57
|
+
positives in both modes, so the model is the lever that matters most.
|
|
58
|
+
- **Adversarial** mode did not lower false positives over standard on those
|
|
59
|
+
probes and costs ~3x. Reach for it for extra recall on subtle, cross-file
|
|
60
|
+
logic, not as a false-positive reducer.
|
|
61
|
+
|
|
62
|
+
Default to **standard mode with a strong model** (set it with `--model` or
|
|
63
|
+
`CODEJURY_MODEL`). False positives are held down by the do-not-report list and
|
|
64
|
+
the post-filter, not by the mode.
|
|
65
|
+
|
|
66
|
+
### Use in CI (GitHub Actions)
|
|
67
|
+
|
|
68
|
+
Audit every pull request and surface findings in the code scanning tab. Copy
|
|
69
|
+
[`examples/codejury-pr-review.yml`](examples/codejury-pr-review.yml) into
|
|
70
|
+
`.github/workflows/`, add a `CODEJURY_API_KEY` repo secret, and it will:
|
|
71
|
+
|
|
72
|
+
1. diff the PR against its base (`--git-range origin/<base>...HEAD`),
|
|
73
|
+
2. write SARIF and upload it with `github/codeql-action/upload-sarif`,
|
|
74
|
+
3. fail the check on a HIGH or CRITICAL finding (`--fail-on high`).
|
|
75
|
+
|
|
76
|
+
The job makes one model call per PR (standard mode); the SARIF is uploaded even
|
|
77
|
+
when the gate fails, so findings always show up on the PR.
|
|
78
|
+
|
|
79
|
+
## Whole-repo review
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
codejury review repo /path/to/your/repo
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This scaffolds a review workspace (`entrypoints/`, `issues/`, `analysis/`, and a
|
|
86
|
+
`security-review-memory.md`), seeds the entrypoint inventory from a
|
|
87
|
+
deterministic scan, and prints the methodology. Run it with an interactive
|
|
88
|
+
agent: it reads the methodology and the rules, maps the attack surface, traces
|
|
89
|
+
inputs to sinks across files, records high-confidence issues with a PoC, and
|
|
90
|
+
asks you to confirm credentials or false positives along the way. Nothing runs
|
|
91
|
+
against production.
|
|
92
|
+
|
|
93
|
+
## Findings
|
|
94
|
+
|
|
95
|
+
Each finding carries a file and line, a severity and category, a concrete
|
|
96
|
+
exploit scenario, a recommendation, and a confidence. A false-positive filter
|
|
97
|
+
drops test/mock-path and low-confidence noise; the model is also told not to
|
|
98
|
+
report dependency CVEs, style notes, speculation, or config-leak-only risks.
|
|
99
|
+
|
|
100
|
+
## Extending
|
|
101
|
+
|
|
102
|
+
Add a vulnerability class by dropping a new `codejury/data/rules/<class>.md` with
|
|
103
|
+
the standard frontmatter (title, impact, tags, triggers) and vulnerable/secure
|
|
104
|
+
examples. It is data; no code change needed.
|
|
@@ -132,7 +132,7 @@ def _dispatch(args, parser) -> int:
|
|
|
132
132
|
if args.command == "review" and scope == "repo":
|
|
133
133
|
res = scaffold(args.directory, args.workspace)
|
|
134
134
|
print(f"Workspace: {res.workspace}", file=sys.stderr)
|
|
135
|
-
print(f"Seeded {res.entrypoints} entrypoints into {res.workspace}/
|
|
135
|
+
print(f"Seeded {res.entrypoints} entrypoints into {res.workspace}/entrypoints/_entrypoints.md", file=sys.stderr)
|
|
136
136
|
print(f"Memory: {res.memory_path}", file=sys.stderr)
|
|
137
137
|
print("\nRun this review with an interactive agent (Claude Code / Codex) using the methodology below.\n")
|
|
138
138
|
print(res.methodology)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Repo Security Review — Agent Methodology
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Code, Codex, etc.), not a one-shot LLM call. It maps the
|
|
5
|
-
inputs to sinks across files, verifies issues with a real
|
|
6
|
-
iterates over multiple rounds with a persistent memory. One round is
|
|
7
|
-
30 minutes; run as many rounds as needed.
|
|
3
|
+
The `review repo` path: a whole-repository security audit, run by an interactive
|
|
4
|
+
coding agent (Claude Code, Codex, etc.), not a one-shot LLM call. It maps the
|
|
5
|
+
attack surface, traces inputs to sinks across files, verifies issues with a real
|
|
6
|
+
PoC, and iterates over multiple rounds with a persistent memory. One round is
|
|
7
|
+
roughly 30 minutes; run as many rounds as needed.
|
|
8
8
|
|
|
9
9
|
Target repository: the directory you were given.
|
|
10
|
-
Workspace: `<workspace>/<project>/` (created for you), holding `
|
|
10
|
+
Workspace: `<workspace>/<project>/` (created for you), holding `entrypoints/`,
|
|
11
11
|
`issues/`, `analysis/`, and `security-review-memory.md`.
|
|
12
12
|
|
|
13
13
|
---
|
|
@@ -18,9 +18,9 @@ Workspace: `<workspace>/<project>/` (created for you), holding `api/`,
|
|
|
18
18
|
- skip every pattern under "Confirmed false positives";
|
|
19
19
|
- do not re-report anything under "Fixed";
|
|
20
20
|
- weight the files under "High-risk areas" more heavily.
|
|
21
|
-
2. Read `
|
|
22
|
-
*starting* map of the attack surface. It lists HTTP routes and CLI
|
|
23
|
-
only,
|
|
21
|
+
2. Read `entrypoints/_entrypoints.md` (seeded for you from a deterministic AST
|
|
22
|
+
scan) as a *starting* map of the attack surface. It lists HTTP routes and CLI
|
|
23
|
+
commands only, a subset, not the whole surface (see "Map the attack surface").
|
|
24
24
|
3. Read the relevant rule files under the shipped `rules/` for the target's stack
|
|
25
25
|
(sql-injection, idor, ssrf, authentication-jwt, insecure-deserialization, ...).
|
|
26
26
|
|
|
@@ -28,7 +28,7 @@ Workspace: `<workspace>/<project>/` (created for you), holding `api/`,
|
|
|
28
28
|
|
|
29
29
|
The seeded inventory lists HTTP routes and CLI commands only. Before analysing,
|
|
30
30
|
complete the surface: untrusted input enters at more than HTTP. Enumerate every
|
|
31
|
-
source the attacker can influence and add it to `
|
|
31
|
+
source the attacker can influence and add it to `entrypoints/`:
|
|
32
32
|
|
|
33
33
|
- HTTP routes, GraphQL resolvers, gRPC / RPC handlers, WebSocket handlers;
|
|
34
34
|
- CLI commands, scheduled jobs / cron, queue and topic consumers, webhooks and
|
|
@@ -40,8 +40,8 @@ source the attacker can influence and add it to `api/`:
|
|
|
40
40
|
inter-service calls.
|
|
41
41
|
|
|
42
42
|
`pickle.loads(cookie)` and `yaml.load(upload)` are entrypoints just as much as a
|
|
43
|
-
route is. Record the inventory in `
|
|
44
|
-
method + review status ✅/⚠️/❌).
|
|
43
|
+
route is. Record the inventory in `entrypoints/` (one file per module: source +
|
|
44
|
+
auth method + review status ✅/⚠️/❌).
|
|
45
45
|
|
|
46
46
|
## Analyse each source
|
|
47
47
|
|
|
@@ -8,7 +8,7 @@ description: "Application security rules for reviewing code for exploitable vuln
|
|
|
8
8
|
Application-security rules, one file per weakness class under `rules/`, named by
|
|
9
9
|
the specific weakness (CWE-style). Each rule states impact, the markers to hunt
|
|
10
10
|
(`triggers`), and vulnerable-vs-secure examples. The diff-audit engine injects the
|
|
11
|
-
rules relevant to a change into the prompt; the
|
|
11
|
+
rules relevant to a change into the prompt; the repo-review agent reads them for
|
|
12
12
|
the target's stack. A finding's `category` is one of these ids.
|
|
13
13
|
|
|
14
14
|
## Rules by OWASP category
|
|
@@ -245,10 +245,18 @@ def _method(decorator_name: str, call: ast.Call | None, rule: str) -> str:
|
|
|
245
245
|
|
|
246
246
|
def _view_name(call: ast.Call) -> str:
|
|
247
247
|
# Django path("route", view): the view is the second positional argument
|
|
248
|
-
if len(call.args)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
248
|
+
if len(call.args) < 2:
|
|
249
|
+
return ""
|
|
250
|
+
view = call.args[1]
|
|
251
|
+
if isinstance(view, ast.Name):
|
|
252
|
+
return view.id
|
|
253
|
+
if isinstance(view, ast.Attribute):
|
|
254
|
+
return view.attr
|
|
255
|
+
# class-based view: SomeView.as_view() / views.SomeView.as_view() -> class name
|
|
256
|
+
if isinstance(view, ast.Call) and isinstance(view.func, ast.Attribute) and view.func.attr == "as_view":
|
|
257
|
+
target = view.func.value
|
|
258
|
+
if isinstance(target, ast.Name):
|
|
259
|
+
return target.id
|
|
260
|
+
if isinstance(target, ast.Attribute):
|
|
261
|
+
return target.attr
|
|
254
262
|
return ""
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
The `review repo` path. Whole-repo review is too large for a single LLM call, so
|
|
4
4
|
codejury does not run it as a pipeline. Instead it scaffolds a workspace for an
|
|
5
5
|
interactive agent (Claude Code, Codex) and hands over the methodology: it creates
|
|
6
|
-
the
|
|
7
|
-
|
|
8
|
-
text to print.
|
|
6
|
+
the entrypoints/issues/analysis directories, copies the review-memory template,
|
|
7
|
+
seeds the entrypoint inventory from a deterministic RepoModel scan, and returns
|
|
8
|
+
the methodology text to print.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
@@ -16,7 +16,7 @@ from pathlib import Path
|
|
|
16
16
|
from codejury.repo.model import build_repo_model_from_dir
|
|
17
17
|
from codejury.resources import AGENT_DIR
|
|
18
18
|
|
|
19
|
-
_METHODOLOGY = AGENT_DIR / "
|
|
19
|
+
_METHODOLOGY = AGENT_DIR / "repo-review.md"
|
|
20
20
|
_MEMORY_TEMPLATE = AGENT_DIR / "security-review-memory.md"
|
|
21
21
|
|
|
22
22
|
|
|
@@ -33,7 +33,8 @@ class ScaffoldResult:
|
|
|
33
33
|
def _entrypoints_md(model) -> str:
|
|
34
34
|
http = [e for e in model.entrypoints if e.kind == "http"]
|
|
35
35
|
cli = [e for e in model.entrypoints if e.kind == "cli"]
|
|
36
|
-
lines = ["#
|
|
36
|
+
lines = ["# Entrypoints (seeded from a deterministic scan)", "",
|
|
37
|
+
"HTTP routes and CLI commands only; add non-HTTP sources here too.", "",
|
|
37
38
|
"Status legend: ❌ not reviewed · ⚠️ to deepen · ✅ reviewed", ""]
|
|
38
39
|
if http:
|
|
39
40
|
lines += ["## HTTP routes", ""]
|
|
@@ -53,7 +54,7 @@ def scaffold(target: str | Path, workspace: str | Path) -> ScaffoldResult:
|
|
|
53
54
|
project = target.name
|
|
54
55
|
ws = Path(workspace) / project
|
|
55
56
|
created: list[str] = []
|
|
56
|
-
for sub in ("
|
|
57
|
+
for sub in ("entrypoints", "issues", "analysis"):
|
|
57
58
|
d = ws / sub
|
|
58
59
|
if not d.exists():
|
|
59
60
|
d.mkdir(parents=True, exist_ok=True)
|
|
@@ -66,7 +67,7 @@ def scaffold(target: str | Path, workspace: str | Path) -> ScaffoldResult:
|
|
|
66
67
|
created.append(str(memory_path))
|
|
67
68
|
|
|
68
69
|
model = build_repo_model_from_dir(target)
|
|
69
|
-
(ws / "
|
|
70
|
+
(ws / "entrypoints" / "_entrypoints.md").write_text(_entrypoints_md(model), encoding="utf-8")
|
|
70
71
|
|
|
71
72
|
return ScaffoldResult(
|
|
72
73
|
project=project,
|
|
@@ -9,5 +9,5 @@ from pathlib import Path
|
|
|
9
9
|
_DATA = Path(__file__).resolve().parent / "data"
|
|
10
10
|
|
|
11
11
|
RULES_DIR = _DATA / "rules" # security rules injected into the audit prompt
|
|
12
|
-
AGENT_DIR = _DATA / "agent" #
|
|
12
|
+
AGENT_DIR = _DATA / "agent" # repo-review methodology and memory template
|
|
13
13
|
ENTRYPOINTS_FILE = _DATA / "entrypoints.yaml" # framework signatures for RepoModel
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codejury
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: AI code security review: an adversarial diff-audit engine and an agent-driven whole-repo review methodology, with security knowledge as rich rules
|
|
5
5
|
Author: AISecLabs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -34,13 +34,14 @@ Dynamic: license-file
|
|
|
34
34
|
AI code security review, in two paths matched to their nature:
|
|
35
35
|
|
|
36
36
|
- **Diff review** (coded): audit a pull request's diff for newly introduced,
|
|
37
|
-
exploitable risks. A single balanced LLM call, or an adversarial
|
|
38
|
-
Finder/Challenger/Judge pass
|
|
37
|
+
exploitable risks. A single balanced LLM call (the default), or an adversarial
|
|
38
|
+
Finder/Challenger/Judge pass that trades roughly 3x the cost for extra recall
|
|
39
|
+
on subtle, cross-cutting flaws.
|
|
39
40
|
- **Whole-repo review** (agent-driven): a methodology an interactive agent
|
|
40
|
-
(Claude Code, Codex) runs to
|
|
41
|
-
verify issues with a real PoC, and iterate over rounds with
|
|
42
|
-
memory. Too large for a single LLM call, so codejury ships the
|
|
43
|
-
scaffolds the workspace rather than running a pipeline.
|
|
41
|
+
(Claude Code, Codex) runs to map a codebase's attack surface, trace inputs to
|
|
42
|
+
sinks across files, verify issues with a real PoC, and iterate over rounds with
|
|
43
|
+
a persistent memory. Too large for a single LLM call, so codejury ships the
|
|
44
|
+
methodology and scaffolds the workspace rather than running a pipeline.
|
|
44
45
|
|
|
45
46
|
Security knowledge lives in **rich rules** (`codejury/data/rules/*.md`, with
|
|
46
47
|
per-language vulnerable/secure examples), injected into the audit prompt, not
|
|
@@ -65,7 +66,7 @@ codejury review diff --repo /path/to/app --git-range origin/main...HEAD
|
|
|
65
66
|
# from stdin
|
|
66
67
|
git diff HEAD~1 | codejury review diff
|
|
67
68
|
|
|
68
|
-
# adversarial mode: Finder + Challenger + Judge (
|
|
69
|
+
# adversarial mode: Finder + Challenger + Judge (extra recall on subtle flaws, ~3x cost)
|
|
69
70
|
codejury review diff --diff-file changes.diff --mode adversarial
|
|
70
71
|
|
|
71
72
|
# CI gate + SARIF
|
|
@@ -77,18 +78,48 @@ Configure a backend with `--provider`/`--model`/`--api-key`/`--api-base` or the
|
|
|
77
78
|
`codejury review diff --dry-run` exercises the engine with a mock provider and
|
|
78
79
|
no key (it uses a built-in demo diff when you do not pass one).
|
|
79
80
|
|
|
81
|
+
### Choosing a model and mode
|
|
82
|
+
|
|
83
|
+
Detection quality is dominated by the model first, then the mode. On real-diff
|
|
84
|
+
probes:
|
|
85
|
+
|
|
86
|
+
- A strong model (Claude Sonnet tier) in **standard** mode caught every planted
|
|
87
|
+
vulnerability with near-zero false positives. A weaker model raised false
|
|
88
|
+
positives in both modes, so the model is the lever that matters most.
|
|
89
|
+
- **Adversarial** mode did not lower false positives over standard on those
|
|
90
|
+
probes and costs ~3x. Reach for it for extra recall on subtle, cross-file
|
|
91
|
+
logic, not as a false-positive reducer.
|
|
92
|
+
|
|
93
|
+
Default to **standard mode with a strong model** (set it with `--model` or
|
|
94
|
+
`CODEJURY_MODEL`). False positives are held down by the do-not-report list and
|
|
95
|
+
the post-filter, not by the mode.
|
|
96
|
+
|
|
97
|
+
### Use in CI (GitHub Actions)
|
|
98
|
+
|
|
99
|
+
Audit every pull request and surface findings in the code scanning tab. Copy
|
|
100
|
+
[`examples/codejury-pr-review.yml`](examples/codejury-pr-review.yml) into
|
|
101
|
+
`.github/workflows/`, add a `CODEJURY_API_KEY` repo secret, and it will:
|
|
102
|
+
|
|
103
|
+
1. diff the PR against its base (`--git-range origin/<base>...HEAD`),
|
|
104
|
+
2. write SARIF and upload it with `github/codeql-action/upload-sarif`,
|
|
105
|
+
3. fail the check on a HIGH or CRITICAL finding (`--fail-on high`).
|
|
106
|
+
|
|
107
|
+
The job makes one model call per PR (standard mode); the SARIF is uploaded even
|
|
108
|
+
when the gate fails, so findings always show up on the PR.
|
|
109
|
+
|
|
80
110
|
## Whole-repo review
|
|
81
111
|
|
|
82
112
|
```bash
|
|
83
113
|
codejury review repo /path/to/your/repo
|
|
84
114
|
```
|
|
85
115
|
|
|
86
|
-
This scaffolds a review workspace (`
|
|
87
|
-
`security-review-memory.md`), seeds the
|
|
88
|
-
and prints the methodology. Run it with an interactive
|
|
89
|
-
methodology and the rules,
|
|
90
|
-
high-confidence issues with a PoC, and
|
|
91
|
-
positives along the way. Nothing runs
|
|
116
|
+
This scaffolds a review workspace (`entrypoints/`, `issues/`, `analysis/`, and a
|
|
117
|
+
`security-review-memory.md`), seeds the entrypoint inventory from a
|
|
118
|
+
deterministic scan, and prints the methodology. Run it with an interactive
|
|
119
|
+
agent: it reads the methodology and the rules, maps the attack surface, traces
|
|
120
|
+
inputs to sinks across files, records high-confidence issues with a PoC, and
|
|
121
|
+
asks you to confirm credentials or false positives along the way. Nothing runs
|
|
122
|
+
against production.
|
|
92
123
|
|
|
93
124
|
## Findings
|
|
94
125
|
|
|
@@ -13,7 +13,7 @@ codejury.egg-info/entry_points.txt
|
|
|
13
13
|
codejury.egg-info/requires.txt
|
|
14
14
|
codejury.egg-info/top_level.txt
|
|
15
15
|
codejury/data/entrypoints.yaml
|
|
16
|
-
codejury/data/agent/
|
|
16
|
+
codejury/data/agent/repo-review.md
|
|
17
17
|
codejury/data/agent/security-review-memory.md
|
|
18
18
|
codejury/data/rules/SKILL.md
|
|
19
19
|
codejury/data/rules/business-logic.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codejury"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.15.0"
|
|
4
4
|
description = "AI code security review: an adversarial diff-audit engine and an agent-driven whole-repo review methodology, with security knowledge as rich rules"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -59,7 +59,8 @@ urlpatterns = [
|
|
|
59
59
|
]
|
|
60
60
|
'''
|
|
61
61
|
|
|
62
|
-
# include()
|
|
62
|
+
# include() to a module not in the tree resolves to no file: dropped.
|
|
63
|
+
# class-based views resolve to the view class name.
|
|
63
64
|
DJANGO_NOISE = '''
|
|
64
65
|
from django.urls import path, include
|
|
65
66
|
from .views import DashboardView
|
|
@@ -69,6 +70,7 @@ urlpatterns = [
|
|
|
69
70
|
path("accounts/", include("allauth.urls")),
|
|
70
71
|
path("profile/", views.profile),
|
|
71
72
|
path("dash/", DashboardView.as_view()),
|
|
73
|
+
path("admin-panel/", views.AdminView.as_view()),
|
|
72
74
|
]
|
|
73
75
|
'''
|
|
74
76
|
|
|
@@ -163,12 +165,16 @@ def test_django_include_mounts_subroutes_under_prefix():
|
|
|
163
165
|
assert all(e.route != "users" for e in model.entrypoints)
|
|
164
166
|
|
|
165
167
|
|
|
166
|
-
def
|
|
167
|
-
# include()
|
|
168
|
-
#
|
|
168
|
+
def test_django_unresolvable_include_dropped_and_cbv_resolved():
|
|
169
|
+
# include() whose module is not in the tree resolves to no file and is dropped;
|
|
170
|
+
# class-based views resolve to the view class name (both Name and views.Attr forms)
|
|
169
171
|
model = _model({"urls.py": DJANGO_NOISE})
|
|
170
|
-
assert _routes(model) == {
|
|
171
|
-
|
|
172
|
+
assert _routes(model) == {
|
|
173
|
+
("http", "django", "profile", "profile/", ""),
|
|
174
|
+
("http", "django", "DashboardView", "dash/", ""),
|
|
175
|
+
("http", "django", "AdminView", "admin-panel/", ""),
|
|
176
|
+
}
|
|
177
|
+
assert all(e.function for e in model.entrypoints) # no empty `- -` noise entries
|
|
172
178
|
|
|
173
179
|
|
|
174
180
|
def test_plain_module_has_no_entrypoints():
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""RW-4: the
|
|
1
|
+
"""RW-4: the repo-review scaffold sets up the agent workspace (entrypoints/issues/
|
|
2
2
|
analysis + memory + seeded entrypoints) and returns the methodology. It does not
|
|
3
3
|
run an LLM pipeline."""
|
|
4
4
|
|
|
@@ -32,7 +32,7 @@ def test_scaffold_creates_workspace(tmp_path):
|
|
|
32
32
|
|
|
33
33
|
assert res.project == "myservice"
|
|
34
34
|
assert res.workspace == ws_root / "myservice"
|
|
35
|
-
for sub in ("
|
|
35
|
+
for sub in ("entrypoints", "issues", "analysis"):
|
|
36
36
|
assert (res.workspace / sub).is_dir()
|
|
37
37
|
assert res.memory_path.is_file()
|
|
38
38
|
assert "Security Review Memory" in res.memory_path.read_text()
|
|
@@ -42,7 +42,7 @@ def test_scaffold_creates_workspace(tmp_path):
|
|
|
42
42
|
def test_scaffold_seeds_entrypoints_from_repomodel(tmp_path):
|
|
43
43
|
res = scaffold(_target(tmp_path), tmp_path / "work")
|
|
44
44
|
assert res.entrypoints == 2
|
|
45
|
-
seeded = (res.workspace / "
|
|
45
|
+
seeded = (res.workspace / "entrypoints" / "_entrypoints.md").read_text()
|
|
46
46
|
assert "/users" in seeded and "/admin/users/<uid>" in seeded
|
|
47
47
|
assert "list_users" in seeded and "❌" in seeded
|
|
48
48
|
|
|
@@ -71,4 +71,4 @@ def test_no_python_entrypoints_still_scaffolds(tmp_path):
|
|
|
71
71
|
(d / "notes.txt").write_text("hi")
|
|
72
72
|
res = scaffold(d, tmp_path / "work")
|
|
73
73
|
assert res.entrypoints == 0
|
|
74
|
-
assert "enumerate them manually" in (res.workspace / "
|
|
74
|
+
assert "enumerate them manually" in (res.workspace / "entrypoints" / "_entrypoints.md").read_text()
|
codejury-0.14.6/README.md
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# codejury
|
|
2
|
-
|
|
3
|
-
AI code security review, in two paths matched to their nature:
|
|
4
|
-
|
|
5
|
-
- **Diff review** (coded): audit a pull request's diff for newly introduced,
|
|
6
|
-
exploitable risks. A single balanced LLM call, or an adversarial
|
|
7
|
-
Finder/Challenger/Judge pass for higher coverage and fewer false positives.
|
|
8
|
-
- **Whole-repo review** (agent-driven): a methodology an interactive agent
|
|
9
|
-
(Claude Code, Codex) runs to traverse a codebase from its API entrypoints,
|
|
10
|
-
verify issues with a real PoC, and iterate over rounds with a persistent
|
|
11
|
-
memory. Too large for a single LLM call, so codejury ships the methodology and
|
|
12
|
-
scaffolds the workspace rather than running a pipeline.
|
|
13
|
-
|
|
14
|
-
Security knowledge lives in **rich rules** (`codejury/data/rules/*.md`, with
|
|
15
|
-
per-language vulnerable/secure examples), injected into the audit prompt, not
|
|
16
|
-
buried in code.
|
|
17
|
-
|
|
18
|
-
## Install
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
pip install codejury # core
|
|
22
|
-
pip install "codejury[anthropic]" # or [openai] / [litellm] for a backend
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Diff review
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
# audit a diff file
|
|
29
|
-
codejury review diff --diff-file changes.diff
|
|
30
|
-
|
|
31
|
-
# audit a git range in a repo
|
|
32
|
-
codejury review diff --repo /path/to/app --git-range origin/main...HEAD
|
|
33
|
-
|
|
34
|
-
# from stdin
|
|
35
|
-
git diff HEAD~1 | codejury review diff
|
|
36
|
-
|
|
37
|
-
# adversarial mode: Finder + Challenger + Judge (higher coverage, lower FP, ~3x cost)
|
|
38
|
-
codejury review diff --diff-file changes.diff --mode adversarial
|
|
39
|
-
|
|
40
|
-
# CI gate + SARIF
|
|
41
|
-
codejury review diff --diff-file changes.diff --format sarif --fail-on high
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Configure a backend with `--provider`/`--model`/`--api-key`/`--api-base` or the
|
|
45
|
-
`CODEJURY_API_KEY` / `CODEJURY_MODEL` / `CODEJURY_API_BASE` environment variables.
|
|
46
|
-
`codejury review diff --dry-run` exercises the engine with a mock provider and
|
|
47
|
-
no key (it uses a built-in demo diff when you do not pass one).
|
|
48
|
-
|
|
49
|
-
## Whole-repo review
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
codejury review repo /path/to/your/repo
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
This scaffolds a review workspace (`api/`, `issues/`, `analysis/`, and a
|
|
56
|
-
`security-review-memory.md`), seeds the API inventory from a deterministic scan,
|
|
57
|
-
and prints the methodology. Run it with an interactive agent: it reads the
|
|
58
|
-
methodology and the rules, traverses the code from its API entrypoints, records
|
|
59
|
-
high-confidence issues with a PoC, and asks you to confirm credentials or false
|
|
60
|
-
positives along the way. Nothing runs against production.
|
|
61
|
-
|
|
62
|
-
## Findings
|
|
63
|
-
|
|
64
|
-
Each finding carries a file and line, a severity and category, a concrete
|
|
65
|
-
exploit scenario, a recommendation, and a confidence. A false-positive filter
|
|
66
|
-
drops test/mock-path and low-confidence noise; the model is also told not to
|
|
67
|
-
report dependency CVEs, style notes, speculation, or config-leak-only risks.
|
|
68
|
-
|
|
69
|
-
## Extending
|
|
70
|
-
|
|
71
|
-
Add a vulnerability class by dropping a new `codejury/data/rules/<class>.md` with
|
|
72
|
-
the standard frontmatter (title, impact, tags, triggers) and vulnerable/secure
|
|
73
|
-
examples. It is data; no code change needed.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|