codejury 0.18.0__tar.gz → 0.20.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.18.0 → codejury-0.20.0}/PKG-INFO +83 -33
- codejury-0.20.0/README.md +166 -0
- codejury-0.20.0/codejury/__main__.py +10 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/cli.py +43 -23
- codejury-0.20.0/codejury/data/commands/codejury-review.md +29 -0
- codejury-0.20.0/codejury/data/detection.yaml +17 -0
- codejury-0.20.0/codejury/data/frameworks/go/echo.md +31 -0
- codejury-0.20.0/codejury/data/frameworks/go/gin.md +34 -0
- codejury-0.20.0/codejury/data/frameworks/javascript/express.md +36 -0
- codejury-0.20.0/codejury/data/frameworks/javascript/nestjs.md +36 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/frameworks/python/django.md +1 -0
- codejury-0.20.0/codejury/data/frameworks/python/fastapi.md +37 -0
- codejury-0.20.0/codejury/data/frameworks/python/flask.md +37 -0
- codejury-0.20.0/codejury/data/languages/go.md +35 -0
- codejury-0.20.0/codejury/data/languages/javascript.md +34 -0
- codejury-0.20.0/codejury/data/languages/typescript.md +28 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/methodologies/repo-review.md +79 -18
- codejury-0.20.0/codejury/detection.py +56 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/engine.py +1 -1
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/findings_filter.py +2 -21
- {codejury-0.18.0 → codejury-0.20.0}/codejury/guides.py +27 -12
- {codejury-0.18.0 → codejury-0.20.0}/codejury/repo/model.py +23 -26
- {codejury-0.18.0 → codejury-0.20.0}/codejury/repo/scaffold.py +74 -25
- {codejury-0.18.0 → codejury-0.20.0}/codejury/resources.py +2 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury.egg-info/PKG-INFO +83 -33
- {codejury-0.18.0 → codejury-0.20.0}/codejury.egg-info/SOURCES.txt +14 -0
- {codejury-0.18.0 → codejury-0.20.0}/pyproject.toml +2 -2
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_cli_audit.py +26 -2
- codejury-0.20.0/tests/test_detection.py +35 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_diff_engine.py +1 -1
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_guides.py +10 -4
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_repo_model.py +3 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_repo_scaffold.py +45 -8
- codejury-0.18.0/README.md +0 -116
- {codejury-0.18.0 → codejury-0.20.0}/LICENSE +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/__init__.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/frameworks/python/celery.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/languages/python.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/methodologies/security-review-memory.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/protocols/oauth.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/SKILL.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/business-logic.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/code-injection.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/command-injection.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/cross-site-request-forgery.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/cross-site-scripting.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/hardcoded-secrets.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/http-response-splitting.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/improper-authentication.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/information-exposure.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/insecure-cryptography.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/insecure-deserialization.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/insecure-direct-object-reference.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/insecure-transport.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/jwt-validation.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/mass-assignment.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/missing-authorization.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/open-redirect.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/path-traversal.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/race-condition.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/replay-attack.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/server-side-request-forgery.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/server-side-template-injection.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/session-fixation.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/sql-injection.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/data/vulnerabilities/xml-external-entity.md +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/__init__.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/debate.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/debate_prompts.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/prompts.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/runner.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/diff/vulnerabilities.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/domain/__init__.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/domain/finding.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/json_parse.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/mddoc.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/__init__.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/anthropic.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/base.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/factory.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/litellm.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/mock.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/openai.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/openai_format.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/providers/retry.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/repo/__init__.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury/report.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury.egg-info/dependency_links.txt +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury.egg-info/entry_points.txt +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury.egg-info/requires.txt +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/codejury.egg-info/top_level.txt +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/setup.cfg +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_anthropic_provider.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_diff_debate.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_json_parse.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_litellm_provider.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_mddoc.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_openai_format.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_openai_provider.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_report.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_retry_provider.py +0 -0
- {codejury-0.18.0 → codejury-0.20.0}/tests/test_vulnerabilities.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.20.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,52 +40,61 @@ 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` 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` 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
|
|
80
|
+
codejury diff --diff-file changes.diff
|
|
72
81
|
|
|
73
82
|
# audit a git range in a repo
|
|
74
|
-
codejury
|
|
83
|
+
codejury diff --repo /path/to/app --git-range origin/main...HEAD
|
|
75
84
|
|
|
76
85
|
# from stdin
|
|
77
|
-
git diff HEAD~1 | codejury
|
|
86
|
+
git diff HEAD~1 | codejury diff
|
|
78
87
|
|
|
79
88
|
# adversarial mode, more recall on subtle flaws, about 3x the cost
|
|
80
|
-
codejury
|
|
89
|
+
codejury diff --diff-file changes.diff --mode adversarial
|
|
81
90
|
|
|
82
91
|
# CI gate and SARIF
|
|
83
|
-
codejury
|
|
92
|
+
codejury diff --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
|
|
87
96
|
the `CODEJURY_API_KEY`, `CODEJURY_MODEL`, and `CODEJURY_API_BASE` environment
|
|
88
|
-
variables. `codejury
|
|
97
|
+
variables. `codejury diff --dry-run` exercises the engine with a mock
|
|
89
98
|
provider and no key, and falls back to a built in demo diff when you pass none.
|
|
90
99
|
|
|
91
100
|
### Choosing a Model and Mode
|
|
@@ -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
|
-
codejury review
|
|
137
|
+
codejury review /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 /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,15 @@ 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.
|
|
197
|
+
```
|
|
@@ -0,0 +1,166 @@
|
|
|
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` 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` 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 diff --diff-file changes.diff
|
|
50
|
+
|
|
51
|
+
# audit a git range in a repo
|
|
52
|
+
codejury diff --repo /path/to/app --git-range origin/main...HEAD
|
|
53
|
+
|
|
54
|
+
# from stdin
|
|
55
|
+
git diff HEAD~1 | codejury diff
|
|
56
|
+
|
|
57
|
+
# adversarial mode, more recall on subtle flaws, about 3x the cost
|
|
58
|
+
codejury diff --diff-file changes.diff --mode adversarial
|
|
59
|
+
|
|
60
|
+
# CI gate and SARIF
|
|
61
|
+
codejury diff --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 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 /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 /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.
|
|
166
|
+
```
|
|
@@ -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())
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Two paths matched to their nature:
|
|
4
4
|
|
|
5
|
-
- ``
|
|
6
|
-
|
|
7
|
-
- ``review
|
|
5
|
+
- ``diff`` runs the coded diff engine over a unified diff: a single balanced call
|
|
6
|
+
(standard) or the adversarial Finder/Challenger/Judge pass.
|
|
7
|
+
- ``review <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
|
+
``diff --dry-run`` exercises the engine with a mock provider and no key.
|
|
12
12
|
The audit orchestration itself lives in ``codejury.diff.runner``.
|
|
13
13
|
"""
|
|
14
14
|
|
|
@@ -17,6 +17,7 @@ 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
|
|
@@ -92,12 +93,17 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
92
93
|
parser.add_argument("--version", action="version", version=f"codejury {__version__}")
|
|
93
94
|
sub = parser.add_subparsers(dest="command")
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
_add_audit_args(sub.add_parser("diff", help="audit a unified diff (the coded engine)"))
|
|
97
|
+
|
|
98
|
+
review = sub.add_parser("review", help="scaffold a whole-repo review for an interactive agent")
|
|
99
|
+
review.add_argument("directory", help="target repository to review")
|
|
100
|
+
review.add_argument("--workspace", default="codejury-review", help="where to create the review workspace")
|
|
101
|
+
|
|
102
|
+
inst = sub.add_parser("install-slash-command",
|
|
103
|
+
help="install the /codejury-review slash command for an agent")
|
|
104
|
+
inst.add_argument("--agent", choices=("claude", "codex"), default="claude",
|
|
105
|
+
help="which agent's command directory to install into")
|
|
106
|
+
inst.add_argument("--dir", default=None, help="explicit target directory, overrides --agent")
|
|
101
107
|
|
|
102
108
|
args = parser.parse_args(argv)
|
|
103
109
|
try:
|
|
@@ -109,8 +115,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
109
115
|
|
|
110
116
|
|
|
111
117
|
def _dispatch(args, parser) -> int:
|
|
112
|
-
|
|
113
|
-
if args.command == "review" and scope == "diff":
|
|
118
|
+
if args.command == "diff":
|
|
114
119
|
if args.dry_run:
|
|
115
120
|
provider = MockProvider(default=_MOCK_REPLY)
|
|
116
121
|
model = "mock"
|
|
@@ -129,23 +134,38 @@ def _dispatch(args, parser) -> int:
|
|
|
129
134
|
print(render(args.fmt, kept))
|
|
130
135
|
return 1 if gate(kept, args.fail_on) else 0
|
|
131
136
|
|
|
132
|
-
if args.command == "review"
|
|
137
|
+
if args.command == "review":
|
|
133
138
|
res = scaffold(args.directory, args.workspace)
|
|
134
|
-
|
|
139
|
+
(Path(res.workspace) / "METHODOLOGY.md").write_text(res.methodology, encoding="utf-8")
|
|
140
|
+
print(f"Workspace ready: {res.workspace}", file=sys.stderr)
|
|
135
141
|
if res.guides:
|
|
136
|
-
print(f"Detected stack: {', '.join(res.guides)}
|
|
142
|
+
print(f"Detected stack: {', '.join(res.guides)}, notes in {res.workspace}/_stack.md", file=sys.stderr)
|
|
137
143
|
print(f"Flagged {len(res.candidate_files)} candidate entrypoint files into "
|
|
138
144
|
f"{res.workspace}/entrypoints/_entrypoints.md", file=sys.stderr)
|
|
139
|
-
print(f"
|
|
140
|
-
print(
|
|
141
|
-
|
|
145
|
+
print(f"Methodology: {res.workspace}/METHODOLOGY.md", file=sys.stderr)
|
|
146
|
+
print(
|
|
147
|
+
"This command sets up the review, it does not find the issues itself. Next, have an "
|
|
148
|
+
f"interactive agent follow {res.workspace}/METHODOLOGY.md to run the review, or use the "
|
|
149
|
+
"/codejury-review command in Claude Code or Codex. Findings are written to "
|
|
150
|
+
f"{res.workspace}/issues/."
|
|
151
|
+
)
|
|
142
152
|
return 0
|
|
143
153
|
|
|
144
|
-
if args.command == "
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
154
|
+
if args.command == "install-slash-command":
|
|
155
|
+
from codejury.resources import COMMANDS_DIR
|
|
156
|
+
# the command body is portable, only the directory differs per agent
|
|
157
|
+
agent_dirs = {
|
|
158
|
+
"claude": Path.home() / ".claude" / "commands",
|
|
159
|
+
"codex": Path.home() / ".codex" / "prompts",
|
|
160
|
+
}
|
|
161
|
+
target_dir = Path(args.dir) if args.dir else agent_dirs[args.agent]
|
|
162
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
163
|
+
name = "codejury-review.md"
|
|
164
|
+
dst = target_dir / name
|
|
165
|
+
dst.write_text((COMMANDS_DIR / name).read_text(encoding="utf-8"), encoding="utf-8")
|
|
166
|
+
print(f"Installed slash command to {dst}")
|
|
167
|
+
print("Run it in the agent with: /codejury-review <repository>")
|
|
168
|
+
return 0
|
|
149
169
|
|
|
150
170
|
parser.print_help()
|
|
151
171
|
return 1
|
|
@@ -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 $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/`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# File and path classification, across ecosystems: what the engine treats as a
|
|
2
|
+
# source file, a dependency manifest, a noise directory, or test code. Kept here
|
|
3
|
+
# so the implementation enumerates no language itself, adding a language is a
|
|
4
|
+
# data edit. This is distinct from a guide's stack detection, which decides which
|
|
5
|
+
# language, framework, or protocol applies.
|
|
6
|
+
|
|
7
|
+
skip_dirs: [".git", ".venv", "venv", "node_modules", "__pycache__", ".mypy_cache", ".pytest_cache", "build", "dist", "target", "out", ".tox", ".eggs", ".next", ".nuxt"]
|
|
8
|
+
|
|
9
|
+
source_extensions: [".py", ".js", ".ts", ".tsx", ".jsx", ".mjs", ".go", ".rb", ".java", ".kt", ".php", ".cs", ".scala", ".rs"]
|
|
10
|
+
|
|
11
|
+
config_extensions: [".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf", ".env"]
|
|
12
|
+
|
|
13
|
+
manifests: ["requirements.txt", "requirements-dev.txt", "pyproject.toml", "setup.py", "Pipfile", "package.json", "go.mod", "Gemfile", "pom.xml", "build.gradle", "Cargo.toml", "composer.json"]
|
|
14
|
+
|
|
15
|
+
test_dirs: ["test", "tests", "__tests__", "__mocks__", "mocks", "fixtures", "testdata", "e2e", "spec", "specs"]
|
|
16
|
+
|
|
17
|
+
test_name_patterns: ["test_*", "*_test.*", "*.test.*", "*.spec.*"]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: echo
|
|
3
|
+
title: Echo
|
|
4
|
+
kind: framework
|
|
5
|
+
language: go
|
|
6
|
+
detect:
|
|
7
|
+
manifest: ["labstack/echo"]
|
|
8
|
+
imports: ["github.com/labstack/echo"]
|
|
9
|
+
entrypoint_files: ["*main.go", "*/handlers/*.go", "*/handler/*.go", "*/api/*.go", "*/routes/*.go", "*/controllers/*.go"]
|
|
10
|
+
entrypoint_markers: ["echo.New(", "echo.Context", "e.GET", "e.POST", ".GET(", ".POST(", ".Group(", "c.Param", "c.QueryParam", "c.Bind"]
|
|
11
|
+
logic_layers: ["*/service/*.go", "*/services/*.go", "*/usecase/*.go", "*/repository/*.go", "*/repo/*.go", "*/store/*.go", "*/dao/*.go", "*/model/*.go"]
|
|
12
|
+
---
|
|
13
|
+
# Echo Review Notes
|
|
14
|
+
|
|
15
|
+
## Entrypoints
|
|
16
|
+
- Handlers have the signature `func(c echo.Context) error`, registered with
|
|
17
|
+
`e.GET`, `.POST`, and grouped under `e.Group`. Input comes from `c.Param`,
|
|
18
|
+
`c.QueryParam`, `c.FormValue`, `c.Request().Header`, and `c.Bind` into a struct.
|
|
19
|
+
|
|
20
|
+
## Authorization / IDOR
|
|
21
|
+
- Auth is middleware, applied globally, on a `Group`, or per route. The flaw to
|
|
22
|
+
hunt is a route registered outside the authenticated group, inheriting no
|
|
23
|
+
check. Compare grouped routes against routes on the bare instance.
|
|
24
|
+
- IDOR: a record loaded by `c.Param("id")` with no owner or tenant scope.
|
|
25
|
+
|
|
26
|
+
## Common Sinks / Gotchas
|
|
27
|
+
- SQL: `fmt.Sprintf` into `db.Query` or `db.Exec`, instead of placeholders.
|
|
28
|
+
- Command: `exec.Command` built from input.
|
|
29
|
+
- Path: `c.File` or `c.Attachment` and `filepath.Join` on a path from input.
|
|
30
|
+
- `c.Bind` into a struct with privileged fields is mass assignment.
|
|
31
|
+
- A returned `error` that the caller drops can hide a failed auth or validation.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: gin
|
|
3
|
+
title: Gin
|
|
4
|
+
kind: framework
|
|
5
|
+
language: go
|
|
6
|
+
detect:
|
|
7
|
+
manifest: ["gin-gonic/gin"]
|
|
8
|
+
imports: ["github.com/gin-gonic/gin"]
|
|
9
|
+
entrypoint_files: ["*main.go", "*/handlers/*.go", "*/handler/*.go", "*/api/*.go", "*/routes/*.go", "*/controllers/*.go"]
|
|
10
|
+
entrypoint_markers: ["gin.Default(", "gin.New(", "*gin.Context", "router.GET", "router.POST", ".GET(", ".POST(", ".Group(", "c.Param", "c.Query", "c.ShouldBind"]
|
|
11
|
+
logic_layers: ["*/service/*.go", "*/services/*.go", "*/usecase/*.go", "*/repository/*.go", "*/repo/*.go", "*/store/*.go", "*/dao/*.go", "*/model/*.go"]
|
|
12
|
+
---
|
|
13
|
+
# Gin Review Notes
|
|
14
|
+
|
|
15
|
+
## Entrypoints
|
|
16
|
+
- Handlers have the signature `func(c *gin.Context)`, registered with
|
|
17
|
+
`router.GET`, `.POST`, and grouped under `router.Group`. Input comes from
|
|
18
|
+
`c.Param`, `c.Query`, `c.PostForm`, `c.GetHeader`, and `c.ShouldBindJSON` or
|
|
19
|
+
`c.Bind` into a struct.
|
|
20
|
+
|
|
21
|
+
## Authorization / IDOR
|
|
22
|
+
- Auth is middleware, applied globally, on a `Group`, or per route. The classic
|
|
23
|
+
flaw is a route registered outside the authenticated group, so it inherits no
|
|
24
|
+
check. Compare a group's routes against the routes registered on the bare
|
|
25
|
+
engine.
|
|
26
|
+
- IDOR: a record loaded by `c.Param("id")` with no owner or tenant scope.
|
|
27
|
+
|
|
28
|
+
## Common Sinks / Gotchas
|
|
29
|
+
- SQL: `fmt.Sprintf` into `db.Query` or `db.Exec`, instead of placeholders.
|
|
30
|
+
- Command: `exec.Command` built from input.
|
|
31
|
+
- Path: `c.File` or `filepath.Join` on a path from input, the traversal sink.
|
|
32
|
+
- `c.ShouldBindJSON` into a struct with privileged fields is mass assignment.
|
|
33
|
+
- A handler that ignores the error from a bind or an auth call proceeds as if it
|
|
34
|
+
passed.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: express
|
|
3
|
+
title: Express
|
|
4
|
+
kind: framework
|
|
5
|
+
language: javascript
|
|
6
|
+
detect:
|
|
7
|
+
manifest: ["express"]
|
|
8
|
+
imports: ["require('express')", "require(\"express\")", "from 'express'"]
|
|
9
|
+
entrypoint_files: ["*app.js", "*server.js", "*app.ts", "*server.ts", "*/routes/*.js", "*/routes/*.ts", "*/controllers/*.js", "*/controllers/*.ts", "*router*.js", "*router*.ts"]
|
|
10
|
+
entrypoint_markers: ["express()", "app.get(", "app.post(", "app.use(", "router.get(", "router.post(", ".get(", ".post(", "req.params", "req.query", "req.body"]
|
|
11
|
+
logic_layers: ["*/services/*.js", "*/services/*.ts", "*/models/*.js", "*/models/*.ts", "*/repositories/*.js", "*/repositories/*.ts", "*/dao/*.js", "*/dao/*.ts"]
|
|
12
|
+
---
|
|
13
|
+
# Express Review Notes
|
|
14
|
+
|
|
15
|
+
Works the same in JavaScript and TypeScript. See the JavaScript guide for the
|
|
16
|
+
runtime sinks.
|
|
17
|
+
|
|
18
|
+
## Entrypoints
|
|
19
|
+
- Routes are `app.get` / `app.post` / `router.*`, and a `Router` mounted with
|
|
20
|
+
`app.use("/prefix", router)`. The handler is `(req, res, next)`. Input is
|
|
21
|
+
`req.params`, `req.query`, `req.body`, `req.headers`, and `req.cookies`.
|
|
22
|
+
|
|
23
|
+
## Authorization / IDOR
|
|
24
|
+
- Auth is middleware, passed to `app.use` or per route. The flaw to hunt is a
|
|
25
|
+
route mounted before the auth middleware, or one that omits the middleware its
|
|
26
|
+
siblings have, so order and placement matter.
|
|
27
|
+
- IDOR: a record loaded by `req.params.id` with no owner or tenant scope.
|
|
28
|
+
|
|
29
|
+
## Common Sinks / Gotchas
|
|
30
|
+
- SQL and NoSQL: a query built by string concatenation, or a Mongo filter built
|
|
31
|
+
straight from `req.body`, the operator-injection sink.
|
|
32
|
+
- Command: `child_process.exec` from input. Code: `eval` from input.
|
|
33
|
+
- Path: `res.sendFile` or `path.join` on a path from input, the traversal sink.
|
|
34
|
+
- Open redirect via `res.redirect(req.query...)`, and prototype pollution from a
|
|
35
|
+
body merge.
|
|
36
|
+
- A body spread whole into a model or an ORM create is mass assignment.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: nestjs
|
|
3
|
+
title: NestJS
|
|
4
|
+
kind: framework
|
|
5
|
+
language: javascript
|
|
6
|
+
detect:
|
|
7
|
+
manifest: ["@nestjs/core", "@nestjs/common"]
|
|
8
|
+
imports: ["@nestjs/common", "@nestjs/core"]
|
|
9
|
+
entrypoint_files: ["*.controller.ts", "*.controller.js", "*/controllers/*.ts", "*.resolver.ts", "*.gateway.ts"]
|
|
10
|
+
entrypoint_markers: ["@Controller(", "@Get(", "@Post(", "@Put(", "@Delete(", "@Body(", "@Param(", "@Query(", "@UseGuards("]
|
|
11
|
+
logic_layers: ["*.service.ts", "*.service.js", "*/services/*.ts", "*.repository.ts", "*/repositories/*.ts", "*.entity.ts", "*/dao/*.ts"]
|
|
12
|
+
---
|
|
13
|
+
# NestJS Review Notes
|
|
14
|
+
|
|
15
|
+
Usually TypeScript on Node. See the JavaScript and TypeScript guides for the
|
|
16
|
+
runtime sinks and for why types do not sanitize input.
|
|
17
|
+
|
|
18
|
+
## Entrypoints
|
|
19
|
+
- A `@Controller` class with `@Get` / `@Post` methods. Input binds through
|
|
20
|
+
`@Param`, `@Query`, `@Body`, and `@Headers`. GraphQL resolvers and WebSocket
|
|
21
|
+
gateways are entrypoints too.
|
|
22
|
+
|
|
23
|
+
## Authorization / IDOR
|
|
24
|
+
- Access control is a guard applied with `@UseGuards`, at the controller or the
|
|
25
|
+
method, plus role decorators. The flaw to hunt is a route or controller missing
|
|
26
|
+
the guard its siblings declare, and a guard that authenticates but does not
|
|
27
|
+
authorize the specific resource.
|
|
28
|
+
- IDOR: a handler that loads by `@Param("id")` with no owner or tenant check.
|
|
29
|
+
|
|
30
|
+
## Common Sinks / Gotchas
|
|
31
|
+
- Mass assignment: a `@Body` DTO with no `ValidationPipe` and `whitelist: true`
|
|
32
|
+
binds any field the client sends. Confirm the pipe is global or applied.
|
|
33
|
+
- SQL: a TypeORM or Prisma raw query built from input.
|
|
34
|
+
- SSRF: a server-side `fetch` or `axios` to a URL from input.
|
|
35
|
+
- A custom guard that returns true on an unhandled path, or reads the user from a
|
|
36
|
+
header the client controls.
|