codeintely-cli 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,167 @@
1
+ rules:
2
+ # --- JS/TS backend authz gap closure ---------------------------------------
3
+ #
4
+ # Every JS/TS rule shipped before this file was React-*frontend*-only
5
+ # (owasp_generic.yaml: dangerouslySetInnerHTML, JWT-in-localStorage) —
6
+ # zero Express/NestJS backend tenant-isolation/authz coverage existed.
7
+ # These retarget the same IDOR/privilege-escalation/RBAC categories
8
+ # already proven reliable for Django/FastAPI (identity_security.yaml,
9
+ # tenant_isolation.yaml) at the two most common modern Node backend
10
+ # stacks: Express+Prisma/Mongoose, and NestJS's decorator-based routing.
11
+ #
12
+ # Real Semgrep behavior difference from Python, found by testing rather
13
+ # than assumed: a JS/TS object-literal pattern like `{ id: $ID }` (no
14
+ # ellipsis) matches ANY object containing at least an `id` key,
15
+ # regardless of what else is in it — unlike Python's dict patterns,
16
+ # which need an explicit `...` to mean "and possibly more" and default
17
+ # to an exact match without one. This meant the first version of the
18
+ # Prisma rules below, using a `pattern-not` with an explicit `...` to
19
+ # try to exclude "has more than just id," instead matched NOTHING at
20
+ # all — `...` in a JS object pattern also matches the zero-additional-
21
+ # keys case, so the pattern-not cancelled out the intended vulnerable
22
+ # match too. Fixed with a `pattern-not-regex` naming the common
23
+ # tenant/ownership field names directly (organizationId/orgId/
24
+ # tenantId/userId/ownerId/accountId) instead of trying to express "no
25
+ # additional keys of any kind" structurally.
26
+
27
+ - id: express-prisma-find-unique-missing-tenant-filter
28
+ languages: [js, ts]
29
+ severity: ERROR
30
+ message: >-
31
+ This Prisma `findUnique` looks up a row by `id` alone, with no
32
+ tenant/org/user field in the same `where` clause. In a multi-tenant
33
+ app this is a direct IDOR: any authenticated caller can fetch another
34
+ tenant's row by guessing/incrementing the id. Add the caller's
35
+ tenant/owner as an extra field in the same `where` object, e.g.
36
+ `where: { id, organizationId: req.user.organizationId }`. (Heuristic
37
+ rule based on common field names — expect some false positives on
38
+ genuinely single-tenant/global models.)
39
+ metadata:
40
+ owasp_category: "A01:2021 - Broken Access Control"
41
+ cwe: "CWE-639"
42
+ internal_severity: HIGH
43
+ patterns:
44
+ - pattern: '$PRISMA.$MODEL.findUnique({ where: { id: $ID } })'
45
+ - pattern-not-regex: '(?i)(organizationId|orgId|tenantId|userId|ownerId|accountId)'
46
+
47
+ - id: express-prisma-find-first-missing-tenant-filter
48
+ languages: [js, ts]
49
+ severity: ERROR
50
+ message: >-
51
+ This Prisma `findFirst` filters by `id` alone, with no tenant/org/
52
+ user field in the same `where` clause — the same IDOR shape as
53
+ `findUnique` with only an id filter. Add the caller's tenant/owner as
54
+ an extra field in the same `where` object. (Heuristic rule based on
55
+ common field names — expect some false positives on genuinely
56
+ single-tenant/global models.)
57
+ metadata:
58
+ owasp_category: "A01:2021 - Broken Access Control"
59
+ cwe: "CWE-639"
60
+ internal_severity: HIGH
61
+ patterns:
62
+ - pattern: '$PRISMA.$MODEL.findFirst({ where: { id: $ID } })'
63
+ - pattern-not-regex: '(?i)(organizationId|orgId|tenantId|userId|ownerId|accountId)'
64
+
65
+ - id: express-mongoose-findbyid-no-tenant-scoping
66
+ languages: [js, ts]
67
+ severity: WARNING
68
+ message: >-
69
+ `Model.findById(id)` looks up a row by id alone with no way to add a
70
+ tenant/owner filter in the same call (unlike `findOne`/`find`, which
71
+ take a full query object) — in a multi-tenant app this is a direct
72
+ IDOR unless ownership is verified separately after the fetch. Prefer
73
+ `Model.findOne({ _id: id, organizationId: req.user.organizationId })`
74
+ instead, or explicitly verify the fetched document's owner/tenant
75
+ field before returning it. (Heuristic rule — flags every bare
76
+ `findById` call as worth a second look, since this shape has no
77
+ inline way to show a tenant check even when one does happen
78
+ elsewhere; expect some false positives where ownership is verified
79
+ in a separate line.)
80
+ metadata:
81
+ owasp_category: "A01:2021 - Broken Access Control"
82
+ cwe: "CWE-639"
83
+ internal_severity: MEDIUM
84
+ pattern: $MODEL.findById($ID)
85
+
86
+ - id: express-mass-assignment-from-body
87
+ languages: [js, ts]
88
+ severity: ERROR
89
+ message: >-
90
+ This ORM write passes the raw request body straight through with no
91
+ field allowlist. Any field the client includes in the body — a
92
+ privilege/tenancy field like `role`/`isAdmin`/`organizationId`
93
+ included — gets written as-is. Explicitly pick only the fields this
94
+ endpoint should accept (destructure the specific fields, or use your
95
+ ORM's field-allowlist option) instead of passing the whole body
96
+ object through.
97
+ metadata:
98
+ owasp_category: "A01:2021 - Broken Access Control"
99
+ cwe: "CWE-915"
100
+ internal_severity: CRITICAL
101
+ patterns:
102
+ - pattern-either:
103
+ - pattern: $MODEL.create(req.body)
104
+ - pattern: '$MODEL.create({ data: req.body })'
105
+ - pattern: $MODEL.update(req.body)
106
+ - pattern: $MODEL.updateOne(req.body)
107
+ - pattern: Object.assign($OBJ, req.body)
108
+
109
+ - id: express-privilege-field-from-body
110
+ languages: [js, ts]
111
+ severity: ERROR
112
+ message: >-
113
+ A privilege/tenancy attribute is being set directly from
114
+ `req.body`. If this code path is reachable by a normal
115
+ (non-admin) authenticated user, they can grant themselves elevated
116
+ privileges or move themselves into another tenant by sending that
117
+ field in the request body. Set this field only from trusted
118
+ server-side logic, gated behind an explicit admin/role check.
119
+ metadata:
120
+ owasp_category: "A01:2021 - Broken Access Control"
121
+ cwe: "CWE-269"
122
+ internal_severity: CRITICAL
123
+ patterns:
124
+ - pattern-either:
125
+ - pattern: $OBJ.role = req.body.role
126
+ - pattern: $OBJ.isAdmin = req.body.isAdmin
127
+ - pattern: $OBJ.is_admin = req.body.is_admin
128
+ - pattern: $OBJ.isStaff = req.body.isStaff
129
+ - pattern: $OBJ.organizationId = req.body.organizationId
130
+ - pattern: $OBJ.tenantId = req.body.tenantId
131
+
132
+ - id: nestjs-controller-missing-guard
133
+ languages: [ts]
134
+ severity: WARNING
135
+ message: >-
136
+ This `@Controller` class has no `@UseGuards(...)` anywhere on the
137
+ class or its route handlers. Being routable isn't the same as being
138
+ authorized — every route on this controller is reachable by anyone
139
+ who can reach the app at all, with no authentication/authorization
140
+ check. Add `@UseGuards(AuthGuard(...))` (or your app's equivalent) at
141
+ the class level, or per-method if only some routes need it.
142
+ metadata:
143
+ owasp_category: "A01:2021 - Broken Access Control"
144
+ cwe: "CWE-862"
145
+ internal_severity: HIGH
146
+ patterns:
147
+ - pattern: |
148
+ @Controller(...)
149
+ class $CTRL {
150
+ ...
151
+ }
152
+ - pattern-not: |
153
+ @Controller(...)
154
+ @UseGuards(...)
155
+ class $CTRL {
156
+ ...
157
+ }
158
+ - pattern-not: |
159
+ @Controller(...)
160
+ class $CTRL {
161
+ ...
162
+ @UseGuards(...)
163
+ $METHOD(...) {
164
+ ...
165
+ }
166
+ ...
167
+ }
@@ -0,0 +1,37 @@
1
+ rules:
2
+ - id: drf-serializer-fields-all
3
+ languages: [python]
4
+ severity: WARNING
5
+ message: >-
6
+ This DRF ModelSerializer uses `fields = "__all__"`. Any field added to the
7
+ model later (including sensitive ones like `is_admin`, `tenant_id`, `role`)
8
+ becomes writable through this serializer without a deliberate decision.
9
+ Prefer an explicit `fields` list or `read_only_fields` for anything the
10
+ client shouldn't set.
11
+ metadata:
12
+ owasp_category: "A01:2021 - Broken Access Control"
13
+ cwe: "CWE-915"
14
+ internal_severity: MEDIUM
15
+ patterns:
16
+ - pattern: |
17
+ class $META:
18
+ model = $MODEL
19
+ fields = "__all__"
20
+
21
+ - id: django-mass-assignment-from-request-data
22
+ languages: [python]
23
+ severity: ERROR
24
+ message: >-
25
+ This creates/updates a model directly from `request.data`/`request.POST`
26
+ without going through a serializer's field allowlist. A client can set any
27
+ model field this way — including ones they shouldn't control, like
28
+ `is_admin`, `tenant_id`, or `role`.
29
+ metadata:
30
+ owasp_category: "A01:2021 - Broken Access Control"
31
+ cwe: "CWE-915"
32
+ internal_severity: HIGH
33
+ patterns:
34
+ - pattern-either:
35
+ - pattern: $MODEL.objects.create(**request.data)
36
+ - pattern: $MODEL.objects.create(**request.POST)
37
+ - pattern: $MODEL.objects.create(**request.POST.dict())
@@ -0,0 +1,94 @@
1
+ rules:
2
+ - id: django-raw-sql-string-built
3
+ languages: [python]
4
+ severity: WARNING
5
+ message: >-
6
+ This raw SQL call looks string-built (f-string/`.format()`/`%`) rather than
7
+ using parameterized placeholders. If any part of the query comes from user
8
+ input, this is SQL injection. Use parameterized placeholders (`%s` with a
9
+ params list/tuple) instead of formatting user input into the query string.
10
+ metadata:
11
+ owasp_category: "A03:2021 - Injection"
12
+ cwe: "CWE-89"
13
+ internal_severity: HIGH
14
+ patterns:
15
+ - pattern-either:
16
+ - pattern: $MODEL.objects.raw($QUERY)
17
+ - pattern: $CURSOR.execute($QUERY)
18
+ - metavariable-regex:
19
+ metavariable: $QUERY
20
+ regex: '.*(f"|f''|\.format\(|%\s*\().*'
21
+
22
+ - id: django-debug-true
23
+ languages: [python]
24
+ severity: ERROR
25
+ message: >-
26
+ DEBUG = True must never ship to production — it leaks stack traces, settings
27
+ values, and source snippets on error pages to anyone who can trigger a 500.
28
+ metadata:
29
+ owasp_category: "A05:2021 - Security Misconfiguration"
30
+ cwe: "CWE-215"
31
+ internal_severity: CRITICAL
32
+ pattern: DEBUG = True
33
+
34
+ - id: django-cors-allow-all-origins
35
+ languages: [python]
36
+ severity: ERROR
37
+ message: >-
38
+ CORS_ALLOW_ALL_ORIGINS = True lets any website make cross-origin requests to
39
+ this API, including ones carrying a logged-in user's credentials if
40
+ CORS_ALLOW_CREDENTIALS is also on. Set an explicit CORS_ALLOWED_ORIGINS
41
+ allowlist instead.
42
+ metadata:
43
+ owasp_category: "A05:2021 - Security Misconfiguration"
44
+ cwe: "CWE-942"
45
+ internal_severity: HIGH
46
+ pattern: CORS_ALLOW_ALL_ORIGINS = True
47
+
48
+ - id: django-fileupload-original-filename
49
+ languages: [python]
50
+ severity: WARNING
51
+ message: >-
52
+ This saves an uploaded file using its original filename with no visible
53
+ extension/content-type validation nearby. A crafted filename (path
54
+ traversal, double extension, disguised executable) could be written as-is.
55
+ Validate extension/content-type and generate a safe filename before saving.
56
+ metadata:
57
+ owasp_category: "A04:2021 - Insecure Design"
58
+ cwe: "CWE-434"
59
+ internal_severity: MEDIUM
60
+ patterns:
61
+ - pattern-either:
62
+ - pattern: default_storage.save($FILE.name, $FILE)
63
+ - pattern: open($FILE.name, ...)
64
+
65
+ - id: react-dangerously-set-inner-html
66
+ languages: [js, ts]
67
+ severity: WARNING
68
+ message: >-
69
+ `dangerouslySetInnerHTML` renders raw HTML without React's normal escaping.
70
+ If any part of this value comes from user input or an API response, this is
71
+ a stored/reflected XSS vector. Sanitize with a library like DOMPurify before
72
+ rendering, or avoid raw HTML entirely.
73
+ metadata:
74
+ owasp_category: "A03:2021 - Injection"
75
+ cwe: "CWE-79"
76
+ internal_severity: HIGH
77
+ pattern: <$EL dangerouslySetInnerHTML={...} />
78
+
79
+ - id: jwt-stored-in-localstorage
80
+ languages: [js, ts]
81
+ severity: WARNING
82
+ message: >-
83
+ Storing a JWT/access token in localStorage exposes it to any XSS on the
84
+ page — localStorage is readable by any script running on the origin.
85
+ Prefer an httpOnly, Secure, SameSite cookie for session/auth tokens.
86
+ metadata:
87
+ owasp_category: "A02:2021 - Cryptographic Failures"
88
+ cwe: "CWE-522"
89
+ internal_severity: MEDIUM
90
+ patterns:
91
+ - pattern: localStorage.setItem($KEY, $TOKEN)
92
+ - metavariable-regex:
93
+ metavariable: $KEY
94
+ regex: '(?i).*(token|jwt|auth).*'
@@ -0,0 +1,100 @@
1
+ rules:
2
+ - id: django-viewset-missing-permission-classes
3
+ languages: [python]
4
+ severity: ERROR
5
+ message: >-
6
+ This DRF ViewSet/APIView does not declare `permission_classes`. Without it, DRF
7
+ falls back to the global DEFAULT_PERMISSION_CLASSES (or AllowAny if that's unset),
8
+ which can expose this endpoint to unauthenticated or unauthorized users.
9
+ metadata:
10
+ owasp_category: "A01:2021 - Broken Access Control"
11
+ cwe: "CWE-862"
12
+ internal_severity: HIGH
13
+ patterns:
14
+ - pattern: |
15
+ class $VIEW(...):
16
+ ...
17
+ - pattern-not: |
18
+ class $VIEW(...):
19
+ ...
20
+ permission_classes = ...
21
+ ...
22
+ - metavariable-regex:
23
+ metavariable: $VIEW
24
+ regex: ".*(ViewSet|APIView)$"
25
+
26
+ - id: django-orm-query-missing-tenant-filter
27
+ languages: [python]
28
+ severity: WARNING
29
+ message: >-
30
+ This ORM lookup filters only by id/pk with no tenant/org scoping visible in the
31
+ same call. In a multi-tenant app, a user could read another tenant's row by
32
+ guessing/incrementing the id. Scope the query with the caller's tenant, e.g.
33
+ `.filter(id=pk, tenant=request.user.tenant)`. (Heuristic rule — expect some
34
+ false positives on genuinely single-tenant models; a strong candidate for
35
+ suppression-pattern learning once that exists.)
36
+ metadata:
37
+ owasp_category: "A01:2021 - Broken Access Control"
38
+ cwe: "CWE-639"
39
+ internal_severity: HIGH
40
+ patterns:
41
+ - pattern-either:
42
+ - pattern: $MODEL.objects.get(id=$ID)
43
+ - pattern: $MODEL.objects.get(pk=$PK)
44
+ - pattern: $MODEL.objects.filter(id=$ID)
45
+ - pattern: $MODEL.objects.filter(pk=$PK)
46
+
47
+ - id: fastapi-route-missing-auth-dependency
48
+ languages: [python]
49
+ severity: ERROR
50
+ message: >-
51
+ This FastAPI route has no `Depends(...)` in its decorator/signature. If this
52
+ endpoint should require authentication, add a dependency such as
53
+ `Depends(get_current_user)`.
54
+ metadata:
55
+ owasp_category: "A01:2021 - Broken Access Control"
56
+ cwe: "CWE-306"
57
+ internal_severity: HIGH
58
+ patterns:
59
+ - pattern-either:
60
+ - pattern: |
61
+ @$APP.get($PATH, ...)
62
+ def $FUNC(...):
63
+ ...
64
+ - pattern: |
65
+ @$APP.post($PATH, ...)
66
+ def $FUNC(...):
67
+ ...
68
+ - pattern: |
69
+ @$APP.put($PATH, ...)
70
+ def $FUNC(...):
71
+ ...
72
+ - pattern: |
73
+ @$APP.delete($PATH, ...)
74
+ def $FUNC(...):
75
+ ...
76
+ - pattern-not-regex: "Depends\\("
77
+
78
+ - id: fastapi-admin-route-missing-role-check
79
+ languages: [python]
80
+ severity: WARNING
81
+ message: >-
82
+ This route's path looks admin/internal-only but its body has no visible
83
+ role/permission check (e.g. `require_role(...)`, `.is_admin`,
84
+ `check_permission(...)`). Being authenticated isn't the same as being
85
+ authorized — verify the caller's role explicitly for privileged actions.
86
+ (Heuristic rule based on path naming; adjust the regex to your project's
87
+ conventions.)
88
+ metadata:
89
+ owasp_category: "A01:2021 - Broken Access Control"
90
+ cwe: "CWE-862"
91
+ internal_severity: MEDIUM
92
+ patterns:
93
+ - pattern: |
94
+ @$APP.$METHOD($PATH, ...)
95
+ def $FUNC(...):
96
+ ...
97
+ - metavariable-regex:
98
+ metavariable: $PATH
99
+ regex: '.*(admin|internal).*'
100
+ - pattern-not-regex: "(require_role|check_permission|is_admin|is_staff|is_superuser)"