@hoanghainh/komagu 2.0.0 → 2.1.0

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 (143) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +1 -1
  3. package/assess/lsa/examples/eval-run-4/ground-truth.json +823 -0
  4. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/ai-run-stats.json +58 -0
  5. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/coverage.json +85 -0
  6. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/findings/priv-privilege-001.json +822 -0
  7. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/findings/priv-privilege-002.json +393 -0
  8. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/findings.json +4477 -0
  9. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/mass-assignment.candidates.json +232 -0
  10. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-001.json +425 -0
  11. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-002.json +333 -0
  12. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-003.json +400 -0
  13. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-004.json +403 -0
  14. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-005.json +398 -0
  15. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-006.json +63 -0
  16. package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/score.json +120 -0
  17. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/ai-run-stats.json +55 -0
  18. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/coverage.json +50 -0
  19. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/findings/priv-privilege-001.json +613 -0
  20. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/findings/priv-privilege-002.json +287 -0
  21. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/findings.json +4870 -0
  22. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/mass-assignment.candidates.json +232 -0
  23. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-001.json +425 -0
  24. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-002.json +333 -0
  25. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-003.json +400 -0
  26. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-004.json +403 -0
  27. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-005.json +398 -0
  28. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-006.json +63 -0
  29. package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/score.json +107 -0
  30. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/ai-run-stats.json +58 -0
  31. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/coverage.json +92 -0
  32. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/findings/priv-privilege-001.json +645 -0
  33. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/findings/priv-privilege-002.json +222 -0
  34. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/findings.json +4859 -0
  35. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/mass-assignment.candidates.json +232 -0
  36. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-001.json +425 -0
  37. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-002.json +333 -0
  38. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-003.json +400 -0
  39. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-004.json +403 -0
  40. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-005.json +398 -0
  41. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-006.json +63 -0
  42. package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/score.json +121 -0
  43. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/coverage.json +111 -0
  44. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/findings/priv-privilege-001.json +6 -0
  45. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/findings/priv-privilege-002.json +344 -0
  46. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/findings.json +3544 -0
  47. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/mass-assignment.candidates.json +232 -0
  48. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-001.json +425 -0
  49. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-002.json +333 -0
  50. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-003.json +400 -0
  51. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-004.json +403 -0
  52. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-005.json +398 -0
  53. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-006.json +63 -0
  54. package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/score.json +123 -0
  55. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/ai-run-stats.json +50 -0
  56. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/coverage.json +42 -0
  57. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings/priv-privilege-001.json +540 -0
  58. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings/priv-privilege-002.json +238 -0
  59. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings.json +4383 -0
  60. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings.sarif +8729 -0
  61. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/mass-assignment.candidates.json +232 -0
  62. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/report.ja.md +1911 -0
  63. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/report.md +1911 -0
  64. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-001.json +425 -0
  65. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-002.json +333 -0
  66. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-003.json +400 -0
  67. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-004.json +403 -0
  68. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-005.json +398 -0
  69. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-006.json +63 -0
  70. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/score.json +101 -0
  71. package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/stack-profile.json +226 -0
  72. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/ai-run-stats.json +54 -0
  73. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/coverage.json +66 -0
  74. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/findings/priv-privilege-001.json +440 -0
  75. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/findings/priv-privilege-002.json +229 -0
  76. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/findings.json +3424 -0
  77. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/mass-assignment.candidates.json +232 -0
  78. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-001.json +425 -0
  79. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-002.json +333 -0
  80. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-003.json +400 -0
  81. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-004.json +403 -0
  82. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-005.json +398 -0
  83. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-006.json +63 -0
  84. package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/score.json +120 -0
  85. package/assess/lsa/examples/eval-run-5/gptoss-a/ai-run-stats.json +88 -0
  86. package/assess/lsa/examples/eval-run-5/gptoss-a/coverage.json +36 -0
  87. package/assess/lsa/examples/eval-run-5/gptoss-a/findings/priv-privilege-001.json +530 -0
  88. package/assess/lsa/examples/eval-run-5/gptoss-a/findings/priv-privilege-002.json +248 -0
  89. package/assess/lsa/examples/eval-run-5/gptoss-a/findings.json +4766 -0
  90. package/assess/lsa/examples/eval-run-5/gptoss-a/findings.sarif +9326 -0
  91. package/assess/lsa/examples/eval-run-5/gptoss-a/mass-assignment.candidates.json +232 -0
  92. package/assess/lsa/examples/eval-run-5/gptoss-a/report.ja.md +2011 -0
  93. package/assess/lsa/examples/eval-run-5/gptoss-a/report.md +2011 -0
  94. package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-001.json +425 -0
  95. package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-002.json +333 -0
  96. package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-003.json +400 -0
  97. package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-004.json +403 -0
  98. package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-005.json +398 -0
  99. package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-006.json +63 -0
  100. package/assess/lsa/examples/eval-run-5/gptoss-a/score.json +80 -0
  101. package/assess/lsa/examples/eval-run-5/gptoss-a/stack-profile.json +226 -0
  102. package/assess/lsa/examples/eval-run-5/gptoss-b/ai-run-stats.json +83 -0
  103. package/assess/lsa/examples/eval-run-5/gptoss-b/coverage.json +44 -0
  104. package/assess/lsa/examples/eval-run-5/gptoss-b/findings/priv-privilege-001.json +579 -0
  105. package/assess/lsa/examples/eval-run-5/gptoss-b/findings/priv-privilege-002.json +268 -0
  106. package/assess/lsa/examples/eval-run-5/gptoss-b/findings.json +3913 -0
  107. package/assess/lsa/examples/eval-run-5/gptoss-b/mass-assignment.candidates.json +232 -0
  108. package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-001.json +425 -0
  109. package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-002.json +333 -0
  110. package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-003.json +400 -0
  111. package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-004.json +403 -0
  112. package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-005.json +398 -0
  113. package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-006.json +63 -0
  114. package/assess/lsa/examples/eval-run-5/gptoss-b/score.json +94 -0
  115. package/assess/lsa/examples/eval-run-5/ground-truth.json +823 -0
  116. package/assess/lsa/knowledge/injection-patterns.md +8 -1
  117. package/assess/lsa/knowledge/stacks/nextjs.md +62 -0
  118. package/assess/lsa/prompts/35-privilege-auditor.md +3 -1
  119. package/assess/lsa/prompts/_shared-rules.md +1 -0
  120. package/assess/lsa/scripts/detect_stack.py +5 -1
  121. package/assess/lsa/scripts/find_injection.py +34 -5
  122. package/assess/lsa/scripts/lsa_common.py +9 -2
  123. package/assess/lsa/scripts/make_batches.py +2 -1
  124. package/assess/lsa/scripts/map_routes.py +10 -2
  125. package/assess/lsa/scripts/nextjs_routes.py +236 -0
  126. package/assess/lsa/templates/audit-config.json +1 -1
  127. package/assess/lsa/tests/EVAL-RESULTS.md +156 -0
  128. package/assess/lsa/tests/score_findings.py +58 -0
  129. package/assess/lsa/tests/test_default_scope.py +86 -0
  130. package/assess/lsa/tests/test_eval_run_4.py +73 -0
  131. package/assess/lsa/tests/test_eval_run_5.py +73 -0
  132. package/assess/lsa/tests/test_js_tagged_sql.py +112 -0
  133. package/assess/lsa/tests/test_nextjs.py +198 -0
  134. package/assess/lsa/tests/test_privilege_tier2.py +30 -0
  135. package/operate/ai/anchoring.py +130 -0
  136. package/operate/ai/client.py +13 -0
  137. package/operate/ai/context.py +105 -0
  138. package/operate/ai/runner.py +197 -69
  139. package/operate/cli/__init__.py +1 -1
  140. package/operate/cli/commands/scan.py +1 -1
  141. package/operate/tests/test_ai_anchoring.py +184 -0
  142. package/operate/tests/test_headless_ai.py +148 -1
  143. package/package.json +1 -1
@@ -0,0 +1,393 @@
1
+ {
2
+ "batch_id": "privilege-002",
3
+ "findings": [
4
+ {
5
+ "finding_id": "MASS-M-0006-1",
6
+ "category": "mass_assignment",
7
+ "subtype": "whole_object_binding",
8
+ "title": "Profile update binds the entire request body to the User model",
9
+ "candidate_id": "M-0006",
10
+ "endpoint": {
11
+ "method": "PUT",
12
+ "path": "/profile"
13
+ },
14
+ "attack_vector": {
15
+ "method": "PUT",
16
+ "path": "/profile",
17
+ "fields": {
18
+ "role": "admin"
19
+ },
20
+ "readback": "GET /profile"
21
+ },
22
+ "location": {
23
+ "file": "routes/user.js",
24
+ "start_line": 7,
25
+ "end_line": 8
26
+ },
27
+ "evidence": [
28
+ {
29
+ "file": "routes/user.js",
30
+ "line": 8,
31
+ "code": "User.findByIdAndUpdate(req.user._id, req.body, function (e, u) { res.json(u); });",
32
+ "why": "Whole request body is bound to the User model without an allowlist."
33
+ }
34
+ ],
35
+ "attack_scenario": "A normal authenticated user can set the role field to \"admin\" and become an administrator.",
36
+ "impact": [
37
+ "role"
38
+ ],
39
+ "severity": "critical",
40
+ "confidence": "high",
41
+ "cwe": [
42
+ "CWE-915"
43
+ ],
44
+ "owasp": [
45
+ "API3:2023"
46
+ ],
47
+ "recommendation": "Only copy allowed fields from the request, e.g. { name: req.body.name, email: req.body.email }.",
48
+ "fix_example": "User.findByIdAndUpdate(req.user._id, { name: req.body.name, email: req.body.email }, ...);",
49
+ "status": "unverified"
50
+ },
51
+ {
52
+ "finding_id": "MASS-M-0007-1",
53
+ "category": "mass_assignment",
54
+ "subtype": "whole_object_binding",
55
+ "title": "Profile update binds the entire request body to the User model (duplicate route)",
56
+ "candidate_id": "M-0007",
57
+ "endpoint": {
58
+ "method": "PUT",
59
+ "path": "/profile"
60
+ },
61
+ "attack_vector": {
62
+ "method": "PUT",
63
+ "path": "/profile",
64
+ "fields": {
65
+ "role": "admin"
66
+ },
67
+ "readback": "GET /profile"
68
+ },
69
+ "location": {
70
+ "file": "routes/user.js",
71
+ "start_line": 7,
72
+ "end_line": 8
73
+ },
74
+ "evidence": [
75
+ {
76
+ "file": "routes/user.js",
77
+ "line": 8,
78
+ "code": "User.findByIdAndUpdate(req.user._id, req.body, function (e, u) { res.json(u); });",
79
+ "why": "Whole request body is bound to the User model without an allowlist."
80
+ }
81
+ ],
82
+ "attack_scenario": "A normal authenticated user can set the role field to \"admin\" and become an administrator.",
83
+ "impact": [
84
+ "role"
85
+ ],
86
+ "severity": "critical",
87
+ "confidence": "high",
88
+ "cwe": [
89
+ "CWE-915"
90
+ ],
91
+ "owasp": [
92
+ "API3:2023"
93
+ ],
94
+ "recommendation": "Only copy allowed fields from the request, e.g. { name: req.body.name, email: req.body.email }.",
95
+ "fix_example": "User.findByIdAndUpdate(req.user._id, { name: req.body.name, email: req.body.email }, ...);",
96
+ "status": "unverified"
97
+ },
98
+ {
99
+ "finding_id": "MASS-M-0008-1",
100
+ "category": "mass_assignment",
101
+ "subtype": "whole_object_binding",
102
+ "title": "User registration binds the entire request body to the User model",
103
+ "candidate_id": "M-0008",
104
+ "endpoint": {
105
+ "method": "POST",
106
+ "path": "/register"
107
+ },
108
+ "attack_vector": {
109
+ "method": "POST",
110
+ "path": "/register",
111
+ "fields": {
112
+ "role": "admin"
113
+ },
114
+ "readback": "GET /users/<id>"
115
+ },
116
+ "location": {
117
+ "file": "app/Http/Controllers/AccountController.php",
118
+ "start_line": 11,
119
+ "end_line": 13
120
+ },
121
+ "evidence": [
122
+ {
123
+ "file": "app/Http/Controllers/AccountController.php",
124
+ "line": 13,
125
+ "code": "return User::create($request->all());",
126
+ "why": "Whole request body is bound to the User model without an allowlist."
127
+ }
128
+ ],
129
+ "attack_scenario": "A user can set the role field to \"admin\" during registration and gain administrative privileges.",
130
+ "impact": [
131
+ "role"
132
+ ],
133
+ "severity": "critical",
134
+ "confidence": "high",
135
+ "cwe": [
136
+ "CWE-915"
137
+ ],
138
+ "owasp": [
139
+ "API3:2023"
140
+ ],
141
+ "recommendation": "Use a fillable/guarded list or explicitly copy allowed fields.",
142
+ "fix_example": "return User::create($request->only(['name', 'email', 'password']));",
143
+ "status": "unverified"
144
+ },
145
+ {
146
+ "finding_id": "MASS-M-0009-1",
147
+ "category": "mass_assignment",
148
+ "subtype": "privileged_field_writable",
149
+ "title": "Team creation allows arbitrary owner_id assignment",
150
+ "candidate_id": "M-0009",
151
+ "endpoint": {
152
+ "method": "POST",
153
+ "path": "/team"
154
+ },
155
+ "attack_vector": {
156
+ "method": "POST",
157
+ "path": "/team",
158
+ "fields": {
159
+ "owner_id": 999
160
+ },
161
+ "readback": "GET /teams/<id>"
162
+ },
163
+ "location": {
164
+ "file": "app/Http/Controllers/AccountController.php",
165
+ "start_line": 21,
166
+ "end_line": 23
167
+ },
168
+ "evidence": [
169
+ {
170
+ "file": "app/Http/Controllers/AccountController.php",
171
+ "line": 23,
172
+ "code": "return Team::create($request->all());",
173
+ "why": "Whole request body is bound to the Team model; owner_id is a privileged field."
174
+ }
175
+ ],
176
+ "attack_scenario": "A user can create a team and set the owner_id to any user, effectively transferring ownership.",
177
+ "impact": [
178
+ "owner_id"
179
+ ],
180
+ "severity": "critical",
181
+ "confidence": "high",
182
+ "cwe": [
183
+ "CWE-915",
184
+ "CWE-269"
185
+ ],
186
+ "owasp": [
187
+ "API3:2023"
188
+ ],
189
+ "recommendation": "Exclude owner_id from the bind or set it server\u2011side to the current user.",
190
+ "fix_example": "return Team::create($request->only(['name', 'description']));",
191
+ "status": "unverified"
192
+ },
193
+ {
194
+ "finding_id": "MASS-M-0010-1",
195
+ "category": "mass_assignment",
196
+ "subtype": "privileged_field_writable",
197
+ "title": "Team creation allows arbitrary owner_id assignment (duplicate route)",
198
+ "candidate_id": "M-0010",
199
+ "endpoint": {
200
+ "method": "POST",
201
+ "path": "/team"
202
+ },
203
+ "attack_vector": {
204
+ "method": "POST",
205
+ "path": "/team",
206
+ "fields": {
207
+ "owner_id": 999
208
+ },
209
+ "readback": "GET /teams/<id>"
210
+ },
211
+ "location": {
212
+ "file": "app/Http/Controllers/AccountController.php",
213
+ "start_line": 21,
214
+ "end_line": 23
215
+ },
216
+ "evidence": [
217
+ {
218
+ "file": "app/Http/Controllers/AccountController.php",
219
+ "line": 23,
220
+ "code": "return Team::create($request->all());",
221
+ "why": "Whole request body is bound to the Team model; owner_id is a privileged field."
222
+ }
223
+ ],
224
+ "attack_scenario": "A user can create a team and set the owner_id to any user, effectively transferring ownership.",
225
+ "impact": [
226
+ "owner_id"
227
+ ],
228
+ "severity": "critical",
229
+ "confidence": "high",
230
+ "cwe": [
231
+ "CWE-915",
232
+ "CWE-269"
233
+ ],
234
+ "owasp": [
235
+ "API3:2023"
236
+ ],
237
+ "recommendation": "Exclude owner_id from the bind or set it server\u2011side to the current user.",
238
+ "fix_example": "return Team::create($request->only(['name', 'description']));",
239
+ "status": "unverified"
240
+ },
241
+ {
242
+ "finding_id": "MASS-M-0015-1",
243
+ "category": "mass_assignment",
244
+ "subtype": "whole_object_binding",
245
+ "title": "User patch route binds arbitrary fields to the User model",
246
+ "candidate_id": "M-0015",
247
+ "endpoint": {
248
+ "method": "PATCH",
249
+ "path": "/users/<int:uid>"
250
+ },
251
+ "attack_vector": {
252
+ "method": "PATCH",
253
+ "path": "/users/42",
254
+ "fields": {
255
+ "role": "admin"
256
+ },
257
+ "readback": "GET /users/42"
258
+ },
259
+ "location": {
260
+ "file": "app.py",
261
+ "start_line": 24,
262
+ "end_line": 30
263
+ },
264
+ "evidence": [
265
+ {
266
+ "file": "app.py",
267
+ "line": 27,
268
+ "code": "for key, value in request.json.items():\n setattr(user, key, value)",
269
+ "why": "Whole request body is bound to the User model without an allowlist."
270
+ }
271
+ ],
272
+ "attack_scenario": "A user can set the role field to \"admin\" and gain administrative privileges.",
273
+ "impact": [
274
+ "role"
275
+ ],
276
+ "severity": "critical",
277
+ "confidence": "high",
278
+ "cwe": [
279
+ "CWE-915"
280
+ ],
281
+ "owasp": [
282
+ "API3:2023"
283
+ ],
284
+ "recommendation": "Only copy allowed fields from the request, e.g. { name: request.json.get('name') }.",
285
+ "fix_example": "if 'role' in request.json: del request.json['role']\nuser.update(request.json)",
286
+ "status": "unverified"
287
+ },
288
+ {
289
+ "finding_id": "MASS-M-0014-1",
290
+ "category": "mass_assignment",
291
+ "subtype": "whole_object_binding",
292
+ "title": "User creation binds arbitrary fields to the User model",
293
+ "candidate_id": "M-0014",
294
+ "endpoint": {
295
+ "method": "POST",
296
+ "path": "/users"
297
+ },
298
+ "attack_vector": {
299
+ "method": "POST",
300
+ "path": "/users",
301
+ "fields": {
302
+ "role": "admin"
303
+ },
304
+ "readback": "GET /users/<id>"
305
+ },
306
+ "location": {
307
+ "file": "app.py",
308
+ "start_line": 8,
309
+ "end_line": 12
310
+ },
311
+ "evidence": [
312
+ {
313
+ "file": "app.py",
314
+ "line": 10,
315
+ "code": "user = User(**request.json)",
316
+ "why": "Whole request body is bound to the User model without an allowlist."
317
+ }
318
+ ],
319
+ "attack_scenario": "A user can set the role field to \"admin\" during account creation and gain administrative privileges.",
320
+ "impact": [
321
+ "role"
322
+ ],
323
+ "severity": "critical",
324
+ "confidence": "high",
325
+ "cwe": [
326
+ "CWE-915"
327
+ ],
328
+ "owasp": [
329
+ "API3:2023"
330
+ ],
331
+ "recommendation": "Only copy allowed fields from the request, e.g. { name: request.json.get('name'), email: request.json.get('email') }.",
332
+ "fix_example": "user = User(name=request.json.get('name'), email=request.json.get('email'))",
333
+ "status": "unverified"
334
+ }
335
+ ],
336
+ "privilege_route_verdicts": [],
337
+ "candidate_verdicts": [
338
+ {
339
+ "candidate_id": "M-0006",
340
+ "verdict": "vulnerable",
341
+ "finding_ids": [
342
+ "MASS-M-0006-1"
343
+ ]
344
+ },
345
+ {
346
+ "candidate_id": "M-0007",
347
+ "verdict": "vulnerable",
348
+ "finding_ids": [
349
+ "MASS-M-0007-1"
350
+ ]
351
+ },
352
+ {
353
+ "candidate_id": "M-0008",
354
+ "verdict": "vulnerable",
355
+ "finding_ids": [
356
+ "MASS-M-0008-1"
357
+ ]
358
+ },
359
+ {
360
+ "candidate_id": "M-0009",
361
+ "verdict": "vulnerable",
362
+ "finding_ids": [
363
+ "MASS-M-0009-1"
364
+ ]
365
+ },
366
+ {
367
+ "candidate_id": "M-0010",
368
+ "verdict": "vulnerable",
369
+ "finding_ids": [
370
+ "MASS-M-0010-1"
371
+ ]
372
+ },
373
+ {
374
+ "candidate_id": "M-0013",
375
+ "verdict": "safe",
376
+ "reason": "No privileged fields in the users table; only email and name are inserted."
377
+ },
378
+ {
379
+ "candidate_id": "M-0015",
380
+ "verdict": "vulnerable",
381
+ "finding_ids": [
382
+ "MASS-M-0015-1"
383
+ ]
384
+ },
385
+ {
386
+ "candidate_id": "M-0014",
387
+ "verdict": "vulnerable",
388
+ "finding_ids": [
389
+ "MASS-M-0014-1"
390
+ ]
391
+ }
392
+ ]
393
+ }