codejury 0.18.0__tar.gz → 0.19.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.
Files changed (99) hide show
  1. {codejury-0.18.0 → codejury-0.19.0}/PKG-INFO +1 -1
  2. codejury-0.19.0/codejury/data/detection.yaml +17 -0
  3. codejury-0.19.0/codejury/data/frameworks/go/echo.md +31 -0
  4. codejury-0.19.0/codejury/data/frameworks/go/gin.md +34 -0
  5. codejury-0.19.0/codejury/data/frameworks/javascript/express.md +36 -0
  6. codejury-0.19.0/codejury/data/frameworks/javascript/nestjs.md +36 -0
  7. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/frameworks/python/django.md +1 -0
  8. codejury-0.19.0/codejury/data/frameworks/python/fastapi.md +37 -0
  9. codejury-0.19.0/codejury/data/frameworks/python/flask.md +37 -0
  10. codejury-0.19.0/codejury/data/languages/go.md +35 -0
  11. codejury-0.19.0/codejury/data/languages/javascript.md +34 -0
  12. codejury-0.19.0/codejury/data/languages/typescript.md +28 -0
  13. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/methodologies/repo-review.md +77 -18
  14. codejury-0.19.0/codejury/detection.py +56 -0
  15. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/engine.py +1 -1
  16. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/findings_filter.py +2 -21
  17. {codejury-0.18.0 → codejury-0.19.0}/codejury/guides.py +27 -12
  18. {codejury-0.18.0 → codejury-0.19.0}/codejury/repo/model.py +23 -26
  19. {codejury-0.18.0 → codejury-0.19.0}/codejury/repo/scaffold.py +73 -24
  20. {codejury-0.18.0 → codejury-0.19.0}/codejury/resources.py +1 -0
  21. {codejury-0.18.0 → codejury-0.19.0}/codejury.egg-info/PKG-INFO +1 -1
  22. {codejury-0.18.0 → codejury-0.19.0}/codejury.egg-info/SOURCES.txt +12 -0
  23. {codejury-0.18.0 → codejury-0.19.0}/pyproject.toml +1 -1
  24. codejury-0.19.0/tests/test_detection.py +35 -0
  25. {codejury-0.18.0 → codejury-0.19.0}/tests/test_diff_engine.py +1 -1
  26. {codejury-0.18.0 → codejury-0.19.0}/tests/test_guides.py +10 -4
  27. {codejury-0.18.0 → codejury-0.19.0}/tests/test_repo_scaffold.py +44 -7
  28. {codejury-0.18.0 → codejury-0.19.0}/LICENSE +0 -0
  29. {codejury-0.18.0 → codejury-0.19.0}/README.md +0 -0
  30. {codejury-0.18.0 → codejury-0.19.0}/codejury/__init__.py +0 -0
  31. {codejury-0.18.0 → codejury-0.19.0}/codejury/cli.py +0 -0
  32. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/frameworks/python/celery.md +0 -0
  33. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/languages/python.md +0 -0
  34. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/methodologies/security-review-memory.md +0 -0
  35. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/protocols/oauth.md +0 -0
  36. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/SKILL.md +0 -0
  37. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/business-logic.md +0 -0
  38. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/code-injection.md +0 -0
  39. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/command-injection.md +0 -0
  40. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/cross-site-request-forgery.md +0 -0
  41. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/cross-site-scripting.md +0 -0
  42. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/hardcoded-secrets.md +0 -0
  43. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/http-response-splitting.md +0 -0
  44. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/improper-authentication.md +0 -0
  45. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/information-exposure.md +0 -0
  46. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/insecure-cryptography.md +0 -0
  47. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/insecure-deserialization.md +0 -0
  48. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/insecure-direct-object-reference.md +0 -0
  49. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/insecure-transport.md +0 -0
  50. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/jwt-validation.md +0 -0
  51. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/mass-assignment.md +0 -0
  52. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/missing-authorization.md +0 -0
  53. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/open-redirect.md +0 -0
  54. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/path-traversal.md +0 -0
  55. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/race-condition.md +0 -0
  56. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/replay-attack.md +0 -0
  57. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/server-side-request-forgery.md +0 -0
  58. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/server-side-template-injection.md +0 -0
  59. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/session-fixation.md +0 -0
  60. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/sql-injection.md +0 -0
  61. {codejury-0.18.0 → codejury-0.19.0}/codejury/data/vulnerabilities/xml-external-entity.md +0 -0
  62. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/__init__.py +0 -0
  63. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/debate.py +0 -0
  64. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/debate_prompts.py +0 -0
  65. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/prompts.py +0 -0
  66. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/runner.py +0 -0
  67. {codejury-0.18.0 → codejury-0.19.0}/codejury/diff/vulnerabilities.py +0 -0
  68. {codejury-0.18.0 → codejury-0.19.0}/codejury/domain/__init__.py +0 -0
  69. {codejury-0.18.0 → codejury-0.19.0}/codejury/domain/finding.py +0 -0
  70. {codejury-0.18.0 → codejury-0.19.0}/codejury/json_parse.py +0 -0
  71. {codejury-0.18.0 → codejury-0.19.0}/codejury/mddoc.py +0 -0
  72. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/__init__.py +0 -0
  73. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/anthropic.py +0 -0
  74. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/base.py +0 -0
  75. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/factory.py +0 -0
  76. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/litellm.py +0 -0
  77. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/mock.py +0 -0
  78. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/openai.py +0 -0
  79. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/openai_format.py +0 -0
  80. {codejury-0.18.0 → codejury-0.19.0}/codejury/providers/retry.py +0 -0
  81. {codejury-0.18.0 → codejury-0.19.0}/codejury/repo/__init__.py +0 -0
  82. {codejury-0.18.0 → codejury-0.19.0}/codejury/report.py +0 -0
  83. {codejury-0.18.0 → codejury-0.19.0}/codejury.egg-info/dependency_links.txt +0 -0
  84. {codejury-0.18.0 → codejury-0.19.0}/codejury.egg-info/entry_points.txt +0 -0
  85. {codejury-0.18.0 → codejury-0.19.0}/codejury.egg-info/requires.txt +0 -0
  86. {codejury-0.18.0 → codejury-0.19.0}/codejury.egg-info/top_level.txt +0 -0
  87. {codejury-0.18.0 → codejury-0.19.0}/setup.cfg +0 -0
  88. {codejury-0.18.0 → codejury-0.19.0}/tests/test_anthropic_provider.py +0 -0
  89. {codejury-0.18.0 → codejury-0.19.0}/tests/test_cli_audit.py +0 -0
  90. {codejury-0.18.0 → codejury-0.19.0}/tests/test_diff_debate.py +0 -0
  91. {codejury-0.18.0 → codejury-0.19.0}/tests/test_json_parse.py +0 -0
  92. {codejury-0.18.0 → codejury-0.19.0}/tests/test_litellm_provider.py +0 -0
  93. {codejury-0.18.0 → codejury-0.19.0}/tests/test_mddoc.py +0 -0
  94. {codejury-0.18.0 → codejury-0.19.0}/tests/test_openai_format.py +0 -0
  95. {codejury-0.18.0 → codejury-0.19.0}/tests/test_openai_provider.py +0 -0
  96. {codejury-0.18.0 → codejury-0.19.0}/tests/test_repo_model.py +0 -0
  97. {codejury-0.18.0 → codejury-0.19.0}/tests/test_report.py +0 -0
  98. {codejury-0.18.0 → codejury-0.19.0}/tests/test_retry_provider.py +0 -0
  99. {codejury-0.18.0 → codejury-0.19.0}/tests/test_vulnerabilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codejury
3
- Version: 0.18.0
3
+ Version: 0.19.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
@@ -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"]
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.
@@ -9,6 +9,7 @@ detect:
9
9
  imports: ["from django", "import django"]
10
10
  entrypoint_files: ["*urls.py", "*views.py", "*viewsets.py", "*/views/*.py", "*serializers.py", "*api.py", "*consumers.py"]
11
11
  entrypoint_markers: ["APIView", "ViewSet", "@api_view", "@action", "router.register", "path(", "re_path(", "as_view("]
12
+ logic_layers: ["*/controllers/*.py", "*controllers.py", "*/managers/*.py", "*managers.py", "*/dao/*.py", "*dao.py", "*/services/*.py", "*services.py", "*/models/*.py", "*models.py"]
12
13
  ---
13
14
  # Django Review Notes
14
15
 
@@ -0,0 +1,37 @@
1
+ ---
2
+ id: fastapi
3
+ title: FastAPI
4
+ kind: framework
5
+ language: python
6
+ detect:
7
+ manifest: ["fastapi"]
8
+ imports: ["from fastapi", "import fastapi"]
9
+ entrypoint_files: ["*main.py", "*/routers/*.py", "*/api/*.py", "*api.py", "*routes.py", "*/endpoints/*.py"]
10
+ entrypoint_markers: ["FastAPI(", "APIRouter(", "@app.get", "@app.post", "@router.get", "@router.post", "Depends("]
11
+ logic_layers: ["*/services/*.py", "*services.py", "*/models/*.py", "*models.py", "*/repositories/*.py", "*/crud/*.py", "*/dao/*.py"]
12
+ ---
13
+ # FastAPI Review Notes
14
+
15
+ ## Entrypoints
16
+ - Path operations decorated with `@app.get` / `@app.post` or `@router.*` on an
17
+ `APIRouter`. Inputs arrive as path and query parameters, and as a request body
18
+ validated by a Pydantic model.
19
+ - A Pydantic model bounds the body's shape, but an over-wide model still binds
20
+ privileged fields, the mass-assignment shape.
21
+
22
+ ## Authorization / IDOR
23
+ - Auth and access control run through `Depends`, for example a dependency that
24
+ resolves the current user or checks a scope. Note an endpoint that omits the
25
+ dependency its siblings use, or a dependency that authenticates but does not
26
+ authorize the specific resource.
27
+ - IDOR: an endpoint that loads a record by an id parameter with no owner or tenant
28
+ check.
29
+
30
+ ## Common Sinks / Gotchas
31
+ - SQL: a raw query or an ORM `text()` built from a parameter.
32
+ - SSRF: `httpx` or `requests` to a URL from input, common in webhook and fetch
33
+ endpoints.
34
+ - Path: `FileResponse` on a path from input.
35
+ - `CORSMiddleware` with `allow_origins=["*"]` together with credentials, and an
36
+ OAuth2 bearer dependency that decodes a token without verifying signature,
37
+ audience, and expiry.
@@ -0,0 +1,37 @@
1
+ ---
2
+ id: flask
3
+ title: Flask
4
+ kind: framework
5
+ language: python
6
+ detect:
7
+ manifest: ["flask"]
8
+ imports: ["from flask", "import flask"]
9
+ entrypoint_files: ["*app.py", "*views.py", "*routes.py", "*/views/*.py", "*/blueprints/*.py", "*api.py"]
10
+ entrypoint_markers: ["@app.route", ".route(", "Blueprint(", "add_url_rule(", "MethodView", "@app.before_request"]
11
+ logic_layers: ["*/services/*.py", "*services.py", "*/models/*.py", "*models.py", "*/repositories/*.py", "*/dao/*.py"]
12
+ ---
13
+ # Flask Review Notes
14
+
15
+ ## Entrypoints
16
+ - Routes are functions decorated with `@app.route` or `@bp.route`, or registered
17
+ with `add_url_rule`. Blueprints mount a group under a URL prefix. Class views
18
+ subclass `MethodView`.
19
+ - Read input from `request.args`, `request.form`, `request.values`,
20
+ `request.json`, `request.files`, `request.headers`, and `request.cookies`, all
21
+ attacker-controlled.
22
+
23
+ ## Authorization / IDOR
24
+ - Auth is enforced by a `@login_required` style decorator, a `before_request`
25
+ hook, or an explicit check in the view. Note where it is and where a route
26
+ lacks it.
27
+ - IDOR: a model fetched by an id from the request with no owner or tenant scope,
28
+ then returned.
29
+
30
+ ## Common Sinks / Gotchas
31
+ - SSTI: `render_template_string` on input, or `Markup` and `|safe` on unescaped
32
+ input.
33
+ - SQL: raw `cursor.execute` or an ORM `text()` built from input.
34
+ - Path: `send_file` or `send_from_directory` with a path from input, the traversal
35
+ sink.
36
+ - A hardcoded `SECRET_KEY`, `debug=True` in production, and an open redirect via
37
+ `redirect(request.args[...])`.
@@ -0,0 +1,35 @@
1
+ ---
2
+ id: go
3
+ title: Go
4
+ kind: language
5
+ detect:
6
+ files: ["*.go", "go.mod"]
7
+ entrypoint_files: ["*main.go", "*/handlers/*.go", "*/handler/*.go", "*/api/*.go", "*/routes/*.go"]
8
+ entrypoint_markers: ["http.HandleFunc", "http.ListenAndServe", "ServeMux", "http.Handler", "func(w http.ResponseWriter"]
9
+ logic_layers: ["*/service/*.go", "*/services/*.go", "*/usecase/*.go", "*/repository/*.go", "*/repo/*.go", "*/store/*.go", "*/dao/*.go", "*/model/*.go", "*/models/*.go"]
10
+ ---
11
+ # Go Review Notes
12
+
13
+ Where untrusted input enters beyond web routes, which the framework guides cover.
14
+ The standard `net/http` server is itself an entrypoint: a handler that takes an
15
+ `http.ResponseWriter` and an `*http.Request`, registered with `http.HandleFunc`
16
+ or a `ServeMux`. Read the request through `r.URL.Query`, `r.FormValue`, `r.PathValue`,
17
+ `r.Header`, and the decoded body, all attacker-controlled.
18
+
19
+ ## Common Sinks
20
+ - SQL: a query built with `fmt.Sprintf` or string concatenation passed to
21
+ `db.Query` or `db.Exec`. Use placeholders, never build SQL from input.
22
+ - Command: `exec.Command` with a shell or with arguments built from input,
23
+ `os/exec` reaching `sh -c`.
24
+ - Path: `filepath.Join` or `os.Open` on a path from input with no `filepath.Clean`
25
+ and containment check, the traversal sink.
26
+ - SSRF: `http.Get`, `http.NewRequest`, or a client `Do` on a URL from input.
27
+ - Deserialization and templates: `encoding/gob`, `text/template` rendering input,
28
+ and `html/template` used with the wrong escaping context.
29
+
30
+ ## Gotchas
31
+ - Errors ignored with `_` can skip a security check whose failure is never seen.
32
+ - A type assertion or `interface{}` body decoded with `json.Unmarshal` into a
33
+ wide struct is mass assignment if privileged fields are bound.
34
+ - Goroutines and shared state without a lock are a race, relevant to one-time
35
+ tokens and balances.
@@ -0,0 +1,34 @@
1
+ ---
2
+ id: javascript
3
+ title: JavaScript
4
+ kind: language
5
+ detect:
6
+ files: ["*.js", "*.mjs", "*.cjs", "*.jsx"]
7
+ entrypoint_files: ["*server.js", "*app.js", "*index.js", "*/routes/*.js", "*/handlers/*.js", "*/api/*.js"]
8
+ entrypoint_markers: ["http.createServer", "createServer(", "require('http')", "addEventListener('fetch'", "exports.handler"]
9
+ logic_layers: ["*/services/*.js", "*/service/*.js", "*/models/*.js", "*/repositories/*.js", "*/dao/*.js", "*service*.js", "*model*.js"]
10
+ ---
11
+ # JavaScript Review Notes
12
+
13
+ Node is the usual runtime. Where untrusted input enters beyond web routes, which
14
+ the framework guides cover. A plain `http.createServer` callback, a serverless
15
+ `exports.handler`, and a `fetch` event listener are entrypoints too. Read the
16
+ request body, query, params, headers, and cookies as attacker-controlled.
17
+
18
+ ## Common Sinks
19
+ - Command: `child_process.exec`, `execSync`, or `spawn` with a shell, built from
20
+ input.
21
+ - Code: `eval`, `new Function`, `vm.runInContext` on input.
22
+ - SQL and NoSQL: a query built by string concatenation or template literal, and a
23
+ Mongo query that takes a raw object from the body, the operator-injection sink.
24
+ - Path: `fs.readFile`, `path.join`, or `res.sendFile` on a path from input with no
25
+ containment check.
26
+ - SSRF: `fetch`, `axios`, or `http.request` on a URL from input.
27
+ - Prototype pollution: a recursive merge or `lodash.merge` of a request body into
28
+ an object, reaching `__proto__`.
29
+
30
+ ## Gotchas
31
+ - A missing `await` on an async auth check lets the handler proceed before it
32
+ resolves.
33
+ - A regex from input, or a catastrophic backtracking regex on input, is a ReDoS.
34
+ - `JSON.parse` of input into a wide model and assigning it whole is mass assignment.
@@ -0,0 +1,28 @@
1
+ ---
2
+ id: typescript
3
+ title: TypeScript
4
+ kind: language
5
+ detect:
6
+ files: ["*.ts", "*.tsx"]
7
+ entrypoint_files: ["*server.ts", "*app.ts", "*index.ts", "*/routes/*.ts", "*/handlers/*.ts", "*/api/*.ts"]
8
+ entrypoint_markers: ["http.createServer", "createServer(", "addEventListener('fetch'", "export const handler", "export async function handler"]
9
+ logic_layers: ["*/services/*.ts", "*/service/*.ts", "*/models/*.ts", "*/repositories/*.ts", "*/dao/*.ts", "*service*.ts", "*model*.ts"]
10
+ ---
11
+ # TypeScript Review Notes
12
+
13
+ TypeScript runs as JavaScript on Node, so the JavaScript sinks and gotchas all
14
+ apply, read the JavaScript guide. The Node frameworks are shared, so an Express
15
+ or Nest service in TypeScript uses the same framework guides under
16
+ `frameworks/javascript`.
17
+
18
+ ## What Types Do Not Protect
19
+ - Types are erased at runtime. A value typed as `string` is still attacker
20
+ input, so type annotations do not sanitize a query, a path, or a command.
21
+ - A DTO typed in the code does not constrain the request body unless a runtime
22
+ validator such as class-validator or zod actually enforces it, so an
23
+ unvalidated body is still mass assignment.
24
+ - An `as` cast or `any` hides an untrusted value behind a safe-looking type.
25
+ - `JSON.parse` returns `any`, so a parsed body carries no real guarantees.
26
+
27
+ Beyond that, hunt the same sinks as JavaScript: command, code, SQL and NoSQL,
28
+ path traversal, SSRF, and prototype pollution.
@@ -29,6 +29,9 @@ Workspace: `<workspace>/<project>/`, created for you, holding `entrypoints/`,
29
29
  4. Read the relevant vulnerability files under the shipped `vulnerabilities/` for
30
30
  the target's stack such as sql-injection, idor, ssrf, jwt-validation, or
31
31
  insecure-deserialization.
32
+ 5. Read `analysis/_trace_targets.md`, the seeded downstream logic-layer files such
33
+ as managers and dao to trace into, and `analysis/_rounds.md`, the round ledger
34
+ you must keep. See "Trace Attack Paths" and "Completeness Gate".
32
35
 
33
36
  ## Map the Attack Surface
34
37
 
@@ -94,8 +97,11 @@ Then hunt three shapes:
94
97
  A whole-repo review earns its keep by reasoning *across files*: a flaw is usually
95
98
  a source in one file reaching a dangerous sink in another, past a control defined
96
99
  in a third, for example a route that trusts a helper which skips signature checks, or an id that
97
- reaches a query with no ownership check. For each promising source, trace the
98
- path and record it in `analysis/`:
100
+ reaches a query with no ownership check. The flaw usually lives below the
101
+ entrypoint, in a manager, a controller, a dao, or a service, not in the view. The
102
+ seeded `analysis/_trace_targets.md` lists those downstream logic-layer files. For
103
+ each promising source, follow the calls out of the entrypoint into those layers
104
+ to the real sink, and record the path in `analysis/`:
99
105
 
100
106
  - **Source**: the entrypoint and the attacker-controlled value.
101
107
  - **Sink**: the dangerous operation it reaches such as a query, shell, file path, fetch,
@@ -106,19 +112,45 @@ path and record it in `analysis/`:
106
112
 
107
113
  The vulnerability is a path with a reachable sink and no adequate control. Record
108
114
  the system's trust boundaries and auth/authz model in `analysis/` once, so every
109
- trace can refer to it instead of restating it.
115
+ trace can refer to it instead of restating it. An entrypoint is not done until its
116
+ path is traced through the downstream layers to a sink or cleared, since stopping
117
+ at the view is what hides the deep flaw, for example the missing lock in a dao or
118
+ the skipped expiry check in a manager.
119
+
120
+ ## Controls That Live in a Library
121
+
122
+ An entrypoint's security control is often not in the first-party code at all: the
123
+ authentication, the signature or replay check, the permission test is implemented
124
+ in a library the endpoint calls. You cannot judge whether the endpoint is
125
+ exploitable from the first-party code alone, because the control that would stop
126
+ the attack lives in the library.
127
+
128
+ So when a traced path relies on a control a library provides, follow into that
129
+ library's relevant function and verify it actually enforces the control, for
130
+ example that a signature check also binds a nonce or a timestamp window, or that
131
+ an auth helper truly validates the caller. Read the specific function the path
132
+ depends on, not the whole library, and read it where it is installed or vendored.
133
+
134
+ This is about this app's exposure, so it applies to any library, internal or
135
+ third-party. It is not auditing the library for its own bugs, which belongs to
136
+ that library's own review. It is confirming that the control your endpoint relies
137
+ on holds here.
110
138
 
111
139
  ## Scope
112
140
 
113
141
  Report only HIGH / CRITICAL, exploitable, high-confidence issues. **Do not report**
114
142
  regardless of severity, dependency CVEs, style or best-practice notes,
115
143
  speculative issues you cannot tie to a concrete exploit, and risks that only
116
- matter if production config is leaked.
144
+ matter if production config is leaked. A control that a library fails to enforce
145
+ for a reachable first-party entrypoint is not a dependency CVE, it is this app's
146
+ exploitable exposure, so it is in scope. See "Controls That Live in a Library".
117
147
 
118
148
  ## Recording an Issue
119
149
 
120
- Write one `issues/<name>.md` per confirmed issue. Do not write an issue you cannot
121
- confirm with high confidence. Each must have:
150
+ Write one `issues/<name>.md` per issue, and save its PoC as a real runnable file
151
+ `issues/poc_<name>.<ext>`, a script or an `.http` file, not a sketch in prose. If
152
+ you cannot write a concrete runnable PoC, the finding is most likely a false
153
+ positive, so do not report it. Each issue file must have:
122
154
 
123
155
  ```markdown
124
156
  # <title>
@@ -126,6 +158,7 @@ confirm with high confidence. Each must have:
126
158
  - Risk: HIGH | CRITICAL
127
159
  - Type: IDOR | auth bypass | signature flaw | business logic | ...
128
160
  - Source: `<METHOD> <path>` or the non-HTTP entrypoint (queue, deserializer, ...)
161
+ - Verification: reproduced | blocked, needs <what> | not run
129
162
 
130
163
  ## Analysis
131
164
  (cite exact file paths and line numbers)
@@ -134,22 +167,29 @@ confirm with high confidence. Each must have:
134
167
  (end-to-end, actionable steps)
135
168
 
136
169
  ## PoC
137
- (a curl command or script that actually triggers it)
170
+ (the path to `issues/poc_<name>.<ext>` and how to run it)
138
171
 
139
172
  ## Verification
140
- (result of actually running the PoC in a sandbox / dev environment)
173
+ (the actual output of running the PoC, or the exact blocker)
141
174
 
142
175
  ## Fix
143
176
  ```
144
177
 
145
- ## PoC Verification, Human in the Loop
178
+ ## PoC Verification, the False-Positive Gate
146
179
 
147
- Confirm each issue by running the PoC against a sandbox / dev environment. When
148
- you need something only the operator has, stop and ask:
180
+ A finding is a hypothesis until a PoC proves it. The tool, and you, can be
181
+ confident and still wrong, so the PoC is what separates a real vulnerability from
182
+ a plausible misread. Confirm each issue by running its PoC against a sandbox or
183
+ dev environment, and gate reporting on the result:
149
184
 
150
- - an auth cookie or token,
151
- - an MFA step,
152
- - specific test data or an account.
185
+ - **Reproduced**: the PoC ran and triggered the issue. Only these are reported as
186
+ confirmed HIGH / CRITICAL.
187
+ - **Blocked**: the PoC is written and correct but you need something only the
188
+ operator has, so stop and ask, for an auth cookie or token, an MFA step, or
189
+ specific test data or an account. Report these separately as suspected and
190
+ needing verification, with the exact blocker, never mixed into the confirmed
191
+ set.
192
+ - **Not run with no concrete PoC**: do not report. It is a guess.
153
193
 
154
194
  Never run a PoC against production, and never use real credentials or perform a
155
195
  destructive action without the operator's explicit go-ahead.
@@ -158,13 +198,32 @@ destructive action without the operator's explicit go-ahead.
158
198
 
159
199
  Each round, read the workspace history first and do not repeat finished work.
160
200
  Process leftover TODOs, otherwise pick an unreviewed ❌ or to-deepen ⚠️ source
161
- from the inventory. One round rarely finds the deep cross-file and stateful bugs,
162
- so keep going until two consecutive rounds surface nothing new, then report the
163
- review complete. The hard classes such as authorization, replay, and broken
164
- business state usually appear only after several rounds.
201
+ from the inventory, trace it through the downstream layers, and log the round in
202
+ `analysis/_rounds.md`. One round rarely finds the deep cross-file and stateful
203
+ bugs. The hard classes such as authorization, replay, and broken business state
204
+ usually appear only after several rounds, so keep going.
205
+
206
+ ## Completeness Gate
207
+
208
+ Do not report the review complete until all of the following hold. A short run
209
+ with most entrypoints still ❌ is an incomplete review, not a clean one, and
210
+ reporting it as clean is the failure this gate exists to prevent.
211
+
212
+ - Every entrypoint in the inventory is resolved to ✅, none left ❌.
213
+ - Each entrypoint's path is traced through the downstream layers in
214
+ `analysis/_trace_targets.md` to a real sink or explicitly cleared, not stopped
215
+ at the view.
216
+ - The Authorization Model pass ran: the access gate is mapped, sibling endpoints
217
+ compared, and IDOR and unauthenticated privileged paths checked.
218
+ - `analysis/_rounds.md` shows two consecutive rounds that added no new source, no
219
+ new traced path, and no new issue.
220
+
221
+ If any item fails, run another round. State which items pass when you report.
165
222
 
166
223
  ## On Finish
167
224
 
225
+ Report the confirmed findings, the ones with a reproduced PoC, separately from
226
+ the suspected ones still blocked on verification, so the two are never conflated.
168
227
  Append a row to the audit history in `security-review-memory.md`, and ask the
169
228
  operator which findings were false positives. Record those under "Confirmed false
170
229
  positives" so future rounds skip them.
@@ -0,0 +1,56 @@
1
+ """File and path classification config, loaded from `data/detection.yaml`.
2
+
3
+ What the engine treats as a source file, a dependency manifest, a noise
4
+ directory, or test code, across ecosystems. Kept in data so the implementation
5
+ enumerates no language itself: adding a language is a data edit, not a code
6
+ change. This is distinct from a guide's stack detection in `guides.py`, which
7
+ decides which language, framework, or protocol applies.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import fnmatch
13
+ from dataclasses import dataclass
14
+ from functools import lru_cache
15
+
16
+ import yaml
17
+
18
+ from codejury.resources import DETECTION_FILE
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class Detection:
23
+ skip_dirs: frozenset[str] # directory names that are noise, never walked
24
+ source_extensions: frozenset[str] # extensions whose content is scanned for markers
25
+ config_extensions: frozenset[str] # config extensions also sampled when detecting the stack
26
+ manifests: tuple[str, ...] # dependency-manifest filenames read to detect the stack
27
+ test_dirs: frozenset[str] # directory segments that mark test code
28
+ test_name_patterns: tuple[str, ...] # filename globs that mark a test file
29
+
30
+ @property
31
+ def detection_extensions(self) -> frozenset[str]:
32
+ """Source plus config, the files sampled when detecting the stack."""
33
+ return self.source_extensions | self.config_extensions
34
+
35
+ def is_test_path(self, path: str) -> bool:
36
+ """True when a path is test code, by a test directory segment or a
37
+ test-file naming convention. Conservative, so a production file is not
38
+ suppressed."""
39
+ parts = path.replace("\\", "/").split("/")
40
+ if any(p in self.test_dirs for p in parts[:-1]):
41
+ return True
42
+ name = parts[-1].lower()
43
+ return any(fnmatch.fnmatch(name, pat) for pat in self.test_name_patterns)
44
+
45
+
46
+ @lru_cache(maxsize=1)
47
+ def load_detection() -> Detection:
48
+ data = yaml.safe_load(DETECTION_FILE.read_text(encoding="utf-8")) or {}
49
+ return Detection(
50
+ skip_dirs=frozenset(data.get("skip_dirs", [])),
51
+ source_extensions=frozenset(data.get("source_extensions", [])),
52
+ config_extensions=frozenset(data.get("config_extensions", [])),
53
+ manifests=tuple(data.get("manifests", [])),
54
+ test_dirs=frozenset(data.get("test_dirs", [])),
55
+ test_name_patterns=tuple(data.get("test_name_patterns", [])),
56
+ )
@@ -25,7 +25,7 @@ def guides_for_diff(diff: str) -> str:
25
25
  Lives here, not in the shared guides module, because parsing a diff is a
26
26
  diff-path concern."""
27
27
  paths = _DIFF_PATH.findall(diff)
28
- return "\n\n---\n\n".join(g.body for g in select_guides(paths, text=diff))
28
+ return "\n\n---\n\n".join(g.body for g in select_guides(paths, source_text=diff))
29
29
 
30
30
 
31
31
  class AuditError(RuntimeError):
@@ -13,26 +13,7 @@ from __future__ import annotations
13
13
 
14
14
  from dataclasses import dataclass, field
15
15
 
16
- # directory segments that mark test code
17
- _TEST_DIRS = frozenset({
18
- "test", "tests", "__tests__", "__mocks__", "mocks", "fixtures", "testdata", "e2e", "spec", "specs",
19
- })
20
-
21
-
22
- def _is_test_path(path: str) -> bool:
23
- parts = path.replace("\\", "/").split("/")
24
- if any(p in _TEST_DIRS for p in parts[:-1]): # a directory segment, not the filename
25
- return True
26
- name = parts[-1].lower()
27
- if name == "conftest.py":
28
- return True
29
- stem = name.rsplit(".", 1)[0]
30
- return (
31
- stem.startswith("test_")
32
- or stem.endswith("_test")
33
- or stem.endswith(".test")
34
- or stem.endswith(".spec")
35
- )
16
+ from codejury.detection import load_detection
36
17
 
37
18
 
38
19
  @dataclass(frozen=True, kw_only=True)
@@ -53,7 +34,7 @@ class FindingsFilter:
53
34
  if f.confidence < self.min_confidence:
54
35
  return f"confidence {f.confidence:.2f} below floor {self.min_confidence:.2f}"
55
36
  path = f.file or ""
56
- if self.drop_test_paths and _is_test_path(path):
37
+ if self.drop_test_paths and load_detection().is_test_path(path):
57
38
  return "test path (test/mock/fixture directory or test-file naming)"
58
39
  match = next((e for e in self.exclude_paths if e and e in path), None)
59
40
  if match: