@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,3913 @@
1
+ {
2
+ "generated_by": "lsa merge_findings.py 0.3.1",
3
+ "summary": {
4
+ "total_reported": 89,
5
+ "active": 86,
6
+ "rejected_by_verifier": 3,
7
+ "by_severity": {
8
+ "critical": 48,
9
+ "high": 29,
10
+ "medium": 9,
11
+ "low": 0,
12
+ "info": 0
13
+ },
14
+ "by_category": {
15
+ "bola": 29,
16
+ "sql_injection": 31,
17
+ "nosql_injection": 0,
18
+ "bfla": 12,
19
+ "mass_assignment": 14
20
+ },
21
+ "by_status": {
22
+ "unverified": 0,
23
+ "confirmed": 60,
24
+ "likely": 26,
25
+ "rejected": 3,
26
+ "needs_review": 0
27
+ }
28
+ },
29
+ "findings": [
30
+ {
31
+ "finding_id": "BFLA-R-0003-1",
32
+ "category": "bfla",
33
+ "subtype": "missing_role_check",
34
+ "title": "Member deletion without admin check",
35
+ "route_id": "R-0003",
36
+ "location": {
37
+ "file": "classic-asp/shop/member.asp",
38
+ "start_line": 9
39
+ },
40
+ "evidence": [
41
+ {
42
+ "file": "classic-asp/shop/member.asp",
43
+ "line": 9,
44
+ "why": "privileged action, no role check"
45
+ }
46
+ ],
47
+ "impact": "any member can be deleted by any authenticated user",
48
+ "severity": "critical",
49
+ "confidence": "high",
50
+ "cwe": [
51
+ "CWE-285"
52
+ ],
53
+ "recommendation": "Add an admin role check before executing the delete statement",
54
+ "status": "confirmed",
55
+ "verification": {
56
+ "verdict": "confirmed",
57
+ "reason_code": "traced",
58
+ "checked": [
59
+ "classic-asp/shop/member.asp lines 1-12",
60
+ "classic-asp/shop/inc/auth.asp line 2"
61
+ ],
62
+ "control_found": null,
63
+ "notes": "The page includes an authentication check but no role or privilege check before executing the DELETE statement. Any authenticated user can trigger member deletion.",
64
+ "suggested_subtype": null,
65
+ "suggested_cwe": null
66
+ },
67
+ "fingerprint": "kfp1:83b02bec36973742a22a1a70d067dab98296c064b1b633e015f7e580b3b66d97",
68
+ "baseline_state": "new"
69
+ },
70
+ {
71
+ "finding_id": "BFLA-R-0007-1",
72
+ "category": "bfla",
73
+ "subtype": "missing_role_check",
74
+ "title": "Delete user without admin role check",
75
+ "route_id": "R-0007",
76
+ "location": {
77
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
78
+ "start_line": 12
79
+ },
80
+ "evidence": [
81
+ {
82
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
83
+ "line": 12,
84
+ "why": "privileged action, no role check"
85
+ }
86
+ ],
87
+ "impact": "any authenticated user can delete any user",
88
+ "severity": "critical",
89
+ "confidence": "high",
90
+ "cwe": [
91
+ "CWE-285"
92
+ ],
93
+ "recommendation": "Add [Authorize(Roles = \"Admin\")] to DeleteUser",
94
+ "status": "confirmed",
95
+ "verification": {
96
+ "verdict": "confirmed",
97
+ "reason_code": "traced",
98
+ "checked": [
99
+ "dotnet-legacy/Web/Controllers/AdminController.cs lines 6-12"
100
+ ],
101
+ "control_found": null,
102
+ "notes": "The DeleteUser action is protected only by a class‑level [Authorize] attribute, which allows any authenticated user. No role check or other authorization guard is present in the method body.",
103
+ "suggested_subtype": null,
104
+ "suggested_cwe": null
105
+ },
106
+ "fingerprint": "kfp1:8c677e45a76f6bd7c5516a436e8831955ce3e1b1cf0a6c24402479abce231451",
107
+ "baseline_state": "new"
108
+ },
109
+ {
110
+ "finding_id": "BFLA-R-0009-1",
111
+ "category": "bfla",
112
+ "subtype": "role_from_request",
113
+ "title": "Export users controlled by request param",
114
+ "route_id": "R-0009",
115
+ "location": {
116
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
117
+ "start_line": 32
118
+ },
119
+ "evidence": [
120
+ {
121
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
122
+ "line": 32,
123
+ "why": "privileged action controlled by request param"
124
+ }
125
+ ],
126
+ "impact": "any authenticated user can export all users",
127
+ "severity": "critical",
128
+ "confidence": "high",
129
+ "cwe": [
130
+ "CWE-285"
131
+ ],
132
+ "recommendation": "Enforce admin role check before exporting",
133
+ "status": "confirmed",
134
+ "verification": {
135
+ "verdict": "confirmed",
136
+ "reason_code": "traced",
137
+ "checked": [
138
+ "dotnet-legacy/Web/Controllers/AdminController.cs lines 30-36"
139
+ ],
140
+ "control_found": null,
141
+ "notes": "Export() checks only the query string parameter \"isAdmin\" and returns all users if it equals \"true\". No role or ownership verification is performed.",
142
+ "suggested_subtype": null,
143
+ "suggested_cwe": null
144
+ },
145
+ "fingerprint": "kfp1:4fae28566f869dfb7291cfba8d46526d59c3fb1c6c2978d1f915d1d1da35fa68",
146
+ "baseline_state": "new"
147
+ },
148
+ {
149
+ "finding_id": "BFLA-R-0023-1",
150
+ "category": "bfla",
151
+ "subtype": "missing_role_check",
152
+ "title": "Delete user without admin role check",
153
+ "route_id": "R-0023",
154
+ "location": {
155
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
156
+ "start_line": 13
157
+ },
158
+ "evidence": [
159
+ {
160
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
161
+ "line": 13,
162
+ "why": "privileged action, no role check"
163
+ }
164
+ ],
165
+ "impact": "any authenticated user can delete any user",
166
+ "severity": "critical",
167
+ "confidence": "high",
168
+ "cwe": [
169
+ "CWE-285"
170
+ ],
171
+ "recommendation": "Add @PreAuthorize(\"hasRole('ADMIN')\") to delete method",
172
+ "status": "confirmed",
173
+ "verification": {
174
+ "verdict": "confirmed",
175
+ "reason_code": "traced",
176
+ "checked": [
177
+ "java-legacy/src/com/acme/web/AdminUserController.java lines 13-16"
178
+ ],
179
+ "control_found": null,
180
+ "notes": "The delete method lacks any @PreAuthorize or other security annotation. The class itself has no global security configuration shown in the context.",
181
+ "suggested_subtype": null,
182
+ "suggested_cwe": null
183
+ },
184
+ "fingerprint": "kfp1:c185d47f85be7d9e30916ed85ddc9948179375151aa429c9ba9bdba3df193d42",
185
+ "baseline_state": "new"
186
+ },
187
+ {
188
+ "finding_id": "BFLA-R-0030-1",
189
+ "category": "bfla",
190
+ "subtype": "missing_role_check",
191
+ "title": "Delete user without admin check",
192
+ "route_id": "R-0030",
193
+ "location": {
194
+ "file": "node-express/routes/admin.js",
195
+ "start_line": 8
196
+ },
197
+ "evidence": [
198
+ {
199
+ "file": "node-express/routes/admin.js",
200
+ "line": 8,
201
+ "why": "privileged action, no admin check"
202
+ }
203
+ ],
204
+ "impact": "any authenticated user can delete any user",
205
+ "severity": "critical",
206
+ "confidence": "high",
207
+ "cwe": [
208
+ "CWE-285"
209
+ ],
210
+ "recommendation": "Add requireAdmin middleware before delete route",
211
+ "status": "confirmed",
212
+ "verification": {
213
+ "verdict": "confirmed",
214
+ "reason_code": "traced",
215
+ "checked": [
216
+ "node-express/routes/admin.js lines 8-9"
217
+ ],
218
+ "control_found": null,
219
+ "notes": "The delete route uses only ensureLoggedIn middleware; no requireAdmin or role check is applied before the handler.",
220
+ "suggested_subtype": null,
221
+ "suggested_cwe": null
222
+ },
223
+ "fingerprint": "kfp1:c38121b6ace7111a415f9995085dd8d95b1a63052e9eaa92233dd93446a3c631",
224
+ "baseline_state": "new"
225
+ },
226
+ {
227
+ "finding_id": "BFLA-R-0032-1",
228
+ "category": "bfla",
229
+ "subtype": "role_from_request",
230
+ "title": "Export users controlled by request param",
231
+ "route_id": "R-0032",
232
+ "location": {
233
+ "file": "node-express/routes/admin.js",
234
+ "start_line": 16
235
+ },
236
+ "evidence": [
237
+ {
238
+ "file": "node-express/routes/admin.js",
239
+ "line": 16,
240
+ "why": "privileged action controlled by request param"
241
+ }
242
+ ],
243
+ "impact": "any authenticated user can export all users",
244
+ "severity": "critical",
245
+ "confidence": "high",
246
+ "cwe": [
247
+ "CWE-285"
248
+ ],
249
+ "recommendation": "Enforce admin role check before exporting",
250
+ "status": "confirmed",
251
+ "verification": {
252
+ "verdict": "confirmed",
253
+ "reason_code": "traced",
254
+ "checked": [
255
+ "node-express/routes/admin.js lines 16-18"
256
+ ],
257
+ "control_found": null,
258
+ "notes": "The export route checks only req.query.isAdmin and returns data if it equals \"true\". No role verification is present.",
259
+ "suggested_subtype": null,
260
+ "suggested_cwe": null
261
+ },
262
+ "fingerprint": "kfp1:1ff8888a17724ef410c375511a5f1973e8c6485e141a12d400968de937f78234",
263
+ "baseline_state": "new"
264
+ },
265
+ {
266
+ "finding_id": "BFLA-R-0057-1",
267
+ "category": "bfla",
268
+ "subtype": "missing_role_check",
269
+ "title": "View user details without admin check",
270
+ "route_id": "R-0057",
271
+ "location": {
272
+ "file": "php-plain/admin.php",
273
+ "start_line": 6
274
+ },
275
+ "evidence": [
276
+ {
277
+ "file": "php-plain/admin.php",
278
+ "line": 6,
279
+ "why": "privileged action, no role check"
280
+ }
281
+ ],
282
+ "impact": "any authenticated user can view any user record",
283
+ "severity": "critical",
284
+ "confidence": "high",
285
+ "cwe": [
286
+ "CWE-285"
287
+ ],
288
+ "recommendation": "Add admin role check before rendering user details",
289
+ "status": "confirmed",
290
+ "verification": {
291
+ "verdict": "confirmed",
292
+ "reason_code": "traced",
293
+ "checked": [
294
+ "php-plain/admin.php lines 5-8",
295
+ "php-plain/inc/auth.php lines 1-7"
296
+ ],
297
+ "control_found": null,
298
+ "notes": "The code performs a direct SELECT on users by id with no role or ownership check. The included auth.php only verifies login, not admin status.",
299
+ "suggested_subtype": null,
300
+ "suggested_cwe": null
301
+ },
302
+ "fingerprint": "kfp1:d49176fe451bbe3c61de25550631bf23a5b4b79949df949f18698aa7183f6e10",
303
+ "baseline_state": "new"
304
+ },
305
+ {
306
+ "finding_id": "BFLA-R-0058-1",
307
+ "category": "bfla",
308
+ "subtype": "missing_role_check",
309
+ "title": "Delete user without admin check",
310
+ "route_id": "R-0058",
311
+ "location": {
312
+ "file": "php-plain/admin.php",
313
+ "start_line": 10
314
+ },
315
+ "evidence": [
316
+ {
317
+ "file": "php-plain/admin.php",
318
+ "line": 10,
319
+ "why": "privileged action, no role check"
320
+ }
321
+ ],
322
+ "impact": "any authenticated user can delete any user",
323
+ "severity": "critical",
324
+ "confidence": "high",
325
+ "cwe": [
326
+ "CWE-285"
327
+ ],
328
+ "recommendation": "Add admin role check before delete operation",
329
+ "status": "confirmed",
330
+ "verification": {
331
+ "verdict": "confirmed",
332
+ "reason_code": "traced",
333
+ "checked": [
334
+ "php-plain/admin.php lines 9-12",
335
+ "php-plain/inc/auth.php lines 1-7"
336
+ ],
337
+ "control_found": null,
338
+ "notes": "The DELETE statement is executed without any admin role verification. The auth.php only checks for a logged‑in user.",
339
+ "suggested_subtype": null,
340
+ "suggested_cwe": null
341
+ },
342
+ "fingerprint": "kfp1:781c6244908fea9052bbe7da28e222368d76d6420cac232e23403ef72b9d0f96",
343
+ "baseline_state": "new"
344
+ },
345
+ {
346
+ "finding_id": "BFLA-R-0059-1",
347
+ "category": "bfla",
348
+ "subtype": "missing_role_check",
349
+ "title": "Delete user without admin check",
350
+ "route_id": "R-0059",
351
+ "location": {
352
+ "file": "php-plain/manage_users.php",
353
+ "start_line": 6
354
+ },
355
+ "evidence": [
356
+ {
357
+ "file": "php-plain/manage_users.php",
358
+ "line": 6,
359
+ "why": "privileged action, no role check"
360
+ }
361
+ ],
362
+ "impact": "any authenticated user can delete any user",
363
+ "severity": "critical",
364
+ "confidence": "high",
365
+ "cwe": [
366
+ "CWE-285"
367
+ ],
368
+ "recommendation": "Add admin role check before delete operation",
369
+ "status": "confirmed",
370
+ "verification": {
371
+ "verdict": "confirmed",
372
+ "reason_code": "traced",
373
+ "checked": [
374
+ "php-plain/manage_users.php lines 5-7",
375
+ "php-plain/inc/auth.php lines 1-7"
376
+ ],
377
+ "control_found": null,
378
+ "notes": "The DELETE operation is executed on POST without any admin role verification. The UI link is protected, but the server side lacks a check.",
379
+ "suggested_subtype": null,
380
+ "suggested_cwe": null
381
+ },
382
+ "fingerprint": "kfp1:d2b897f49484b719e30cae11a6f37d362659eab27bb3f41960c94c9ff80a9b18",
383
+ "baseline_state": "new"
384
+ },
385
+ {
386
+ "finding_id": "BFLA-R-0063-1",
387
+ "category": "bfla",
388
+ "subtype": "missing_role_check",
389
+ "title": "User can change own role",
390
+ "route_id": "R-0063",
391
+ "location": {
392
+ "file": "php-plain/profile.php",
393
+ "start_line": 10
394
+ },
395
+ "evidence": [
396
+ {
397
+ "file": "php-plain/profile.php",
398
+ "line": 10,
399
+ "why": "privileged action, no role check"
400
+ }
401
+ ],
402
+ "impact": "any authenticated user can elevate own role",
403
+ "severity": "critical",
404
+ "confidence": "high",
405
+ "cwe": [
406
+ "CWE-285"
407
+ ],
408
+ "recommendation": "Restrict role changes to admin only",
409
+ "status": "confirmed",
410
+ "verification": {
411
+ "verdict": "confirmed",
412
+ "reason_code": "traced",
413
+ "checked": [
414
+ "php-plain/profile.php lines 1-11"
415
+ ],
416
+ "control_found": null,
417
+ "notes": "The code assigns the role directly from $_POST without any role check or authorization guard.",
418
+ "suggested_subtype": null,
419
+ "suggested_cwe": null
420
+ },
421
+ "fingerprint": "kfp1:d0b4cd60af62471f9051858077082a67187dc186ab6e5ce97accd2762fdc3f64",
422
+ "baseline_state": "new"
423
+ },
424
+ {
425
+ "finding_id": "BFLA-R-0066-1",
426
+ "category": "bfla",
427
+ "subtype": "missing_role_check",
428
+ "title": "Delete user without admin check",
429
+ "route_id": "R-0066",
430
+ "location": {
431
+ "file": "php-plain/admin/delete_user.php",
432
+ "start_line": 1
433
+ },
434
+ "evidence": [
435
+ {
436
+ "file": "php-plain/admin/delete_user.php",
437
+ "line": 1,
438
+ "why": "privileged action, no role check"
439
+ }
440
+ ],
441
+ "impact": "any authenticated user can delete any user",
442
+ "severity": "critical",
443
+ "confidence": "high",
444
+ "cwe": [
445
+ "CWE-285"
446
+ ],
447
+ "recommendation": "Add admin role check before delete operation",
448
+ "status": "confirmed",
449
+ "verification": {
450
+ "verdict": "confirmed",
451
+ "reason_code": "traced",
452
+ "checked": [
453
+ "php-plain/admin/delete_user.php lines 1-7",
454
+ "php-plain/inc/auth.php lines 1-7"
455
+ ],
456
+ "control_found": null,
457
+ "notes": "The script casts the id to int but performs the DELETE without an admin check. No other role verification is present.",
458
+ "suggested_subtype": null,
459
+ "suggested_cwe": null
460
+ },
461
+ "fingerprint": "kfp1:ac5acea0a998815d202206c2242aa04d68020c74c2b9bf604fd6e9620c81190d",
462
+ "baseline_state": "new"
463
+ },
464
+ {
465
+ "finding_id": "BFLA-R-0067-1",
466
+ "category": "bfla",
467
+ "subtype": "role_from_request",
468
+ "title": "Promote user based on request param",
469
+ "route_id": "R-0067",
470
+ "location": {
471
+ "file": "php-plain/admin/promote.php",
472
+ "start_line": 5
473
+ },
474
+ "evidence": [
475
+ {
476
+ "file": "php-plain/admin/promote.php",
477
+ "line": 5,
478
+ "why": "privileged action controlled by request param"
479
+ }
480
+ ],
481
+ "impact": "any authenticated user can promote any user to admin",
482
+ "severity": "critical",
483
+ "confidence": "high",
484
+ "cwe": [
485
+ "CWE-285"
486
+ ],
487
+ "recommendation": "Enforce admin role check before promotion",
488
+ "status": "confirmed",
489
+ "verification": {
490
+ "verdict": "confirmed",
491
+ "reason_code": "traced",
492
+ "checked": [
493
+ "php-plain/admin/promote.php lines 1-8",
494
+ "php-plain/inc/auth.php lines 1-7"
495
+ ],
496
+ "control_found": null,
497
+ "notes": "The promotion to admin is controlled solely by the request parameter 'is_admin' with no server‑side role check.",
498
+ "suggested_subtype": null,
499
+ "suggested_cwe": null
500
+ },
501
+ "fingerprint": "kfp1:9cd249bae08772bb387c13a9272d9dd009f65b2ec0297ebe8033e7711d8300dd",
502
+ "baseline_state": "new"
503
+ },
504
+ {
505
+ "finding_id": "INJ-I-0001",
506
+ "category": "sql_injection",
507
+ "subtype": "string_concatenation",
508
+ "title": "SQL Injection in classic-asp/shop/member.asp:6",
509
+ "candidate_id": "I-0001",
510
+ "location": {
511
+ "file": "classic-asp/shop/member.asp",
512
+ "start_line": 6
513
+ },
514
+ "evidence": [
515
+ {
516
+ "file": "classic-asp/shop/member.asp",
517
+ "start_line": 6,
518
+ "code": "sql = \"UPDATE members SET name = '\" & Replace(Request.Form(\"name\"), \"'\", \"''\") & \"'\"",
519
+ "why": "Unsanitized input concatenated into query",
520
+ "line": 6
521
+ },
522
+ {
523
+ "file": "classic-asp/shop/member.asp",
524
+ "start_line": 7,
525
+ "code": "sql = sql & \" WHERE member_id = \" & Request.Form(\"member_id\")",
526
+ "why": "Unsanitized input concatenated into query",
527
+ "line": 7
528
+ }
529
+ ],
530
+ "severity": "critical",
531
+ "confidence": "medium",
532
+ "cwe": [
533
+ "CWE-89"
534
+ ],
535
+ "recommendation": "Use parameterized queries",
536
+ "status": "likely",
537
+ "verification": {
538
+ "verdict": "likely",
539
+ "reason_code": "code_not_visible",
540
+ "checked": [
541
+ "classic-asp/shop/member.asp lines 6-8"
542
+ ],
543
+ "control_found": null,
544
+ "notes": "The query concatenates Request.Form(\"member_id\") directly into the SQL string. No parameter binding, cast, or allow‑list is visible in the snippet.",
545
+ "suggested_subtype": null,
546
+ "suggested_cwe": null
547
+ },
548
+ "fingerprint": "kfp1:52c63ccaf583de3933acccb3eff1f79314c58ac311cdeecdfc26c798403a7c89",
549
+ "baseline_state": "new"
550
+ },
551
+ {
552
+ "finding_id": "INJ-I-0002",
553
+ "category": "sql_injection",
554
+ "subtype": "string_concatenation",
555
+ "title": "SQL Injection in classic-asp/shop/member.asp:7",
556
+ "candidate_id": "I-0002",
557
+ "location": {
558
+ "file": "classic-asp/shop/member.asp",
559
+ "start_line": 7
560
+ },
561
+ "evidence": [
562
+ {
563
+ "file": "classic-asp/shop/member.asp",
564
+ "start_line": 7,
565
+ "code": "sql = sql & \" WHERE member_id = \" & Request.Form(\"member_id\")",
566
+ "why": "Unsanitized input concatenated into query",
567
+ "line": 7
568
+ }
569
+ ],
570
+ "severity": "critical",
571
+ "confidence": "medium",
572
+ "cwe": [
573
+ "CWE-89"
574
+ ],
575
+ "recommendation": "Use parameterized queries",
576
+ "status": "likely",
577
+ "verification": {
578
+ "verdict": "likely",
579
+ "reason_code": "code_not_visible",
580
+ "checked": [
581
+ "classic-asp/shop/member.asp lines 7-8"
582
+ ],
583
+ "control_found": null,
584
+ "notes": "Same as INJ-I-0001: member_id is concatenated unsanitized; no cast or parameter binding is shown.",
585
+ "suggested_subtype": null,
586
+ "suggested_cwe": null
587
+ },
588
+ "fingerprint": "kfp1:a425457aee9a3c9db04caa44ddba7d35ecadd834bf5a0100207b9fce90ea85dd",
589
+ "baseline_state": "new"
590
+ },
591
+ {
592
+ "finding_id": "INJ-I-0003",
593
+ "category": "sql_injection",
594
+ "subtype": "string_concatenation",
595
+ "title": "SQL Injection in classic-asp/shop/member.asp:10",
596
+ "candidate_id": "I-0003",
597
+ "location": {
598
+ "file": "classic-asp/shop/member.asp",
599
+ "start_line": 10
600
+ },
601
+ "evidence": [
602
+ {
603
+ "file": "classic-asp/shop/member.asp",
604
+ "start_line": 10,
605
+ "code": "conn.Execute \"DELETE FROM members WHERE member_id = \" & Request.QueryString(\"member_id\")",
606
+ "why": "Unsanitized input concatenated into query",
607
+ "line": 10
608
+ }
609
+ ],
610
+ "severity": "critical",
611
+ "confidence": "medium",
612
+ "cwe": [
613
+ "CWE-89"
614
+ ],
615
+ "recommendation": "Use parameterized queries",
616
+ "status": "likely",
617
+ "verification": {
618
+ "verdict": "likely",
619
+ "reason_code": "code_not_visible",
620
+ "checked": [
621
+ "classic-asp/shop/member.asp lines 10-11"
622
+ ],
623
+ "control_found": null,
624
+ "notes": "The DELETE statement uses Request.QueryString(\"member_id\") unsanitized. No cast or parameter binding is present.",
625
+ "suggested_subtype": null,
626
+ "suggested_cwe": null
627
+ },
628
+ "fingerprint": "kfp1:f1fb34f9c39374bc339b3dc230f5ac6b00731c69bf6cd1399b983ba2d2833525",
629
+ "baseline_state": "new"
630
+ },
631
+ {
632
+ "finding_id": "INJ-I-0004",
633
+ "category": "sql_injection",
634
+ "subtype": "string_concatenation",
635
+ "title": "SQL Injection in classic-asp/shop/order.asp:6",
636
+ "candidate_id": "I-0004",
637
+ "location": {
638
+ "file": "classic-asp/shop/order.asp",
639
+ "start_line": 6
640
+ },
641
+ "evidence": [
642
+ {
643
+ "file": "classic-asp/shop/order.asp",
644
+ "start_line": 6,
645
+ "code": "Set rs = conn.Execute(\"SELECT * FROM orders WHERE id = \" & id)",
646
+ "why": "Unsanitized input concatenated into query",
647
+ "line": 6
648
+ }
649
+ ],
650
+ "severity": "critical",
651
+ "confidence": "medium",
652
+ "cwe": [
653
+ "CWE-89"
654
+ ],
655
+ "recommendation": "Use parameterized queries",
656
+ "status": "likely",
657
+ "verification": {
658
+ "verdict": "likely",
659
+ "reason_code": "code_not_visible",
660
+ "checked": [
661
+ "classic-asp/shop/order.asp lines 6-7"
662
+ ],
663
+ "control_found": null,
664
+ "notes": "The SELECT query concatenates the id parameter directly. No integer cast or parameter binding is visible for this variable.",
665
+ "suggested_subtype": null,
666
+ "suggested_cwe": null
667
+ },
668
+ "fingerprint": "kfp1:07b1c914202a928e4156e6b6e9e5ab32fe7a75cdceda182d55d68051eeb61ac9",
669
+ "baseline_state": "new"
670
+ },
671
+ {
672
+ "finding_id": "INJ-I-0006",
673
+ "category": "sql_injection",
674
+ "subtype": "string_concatenation",
675
+ "title": "SQL Injection in coldfusion/app/OrderService.cfc:5",
676
+ "candidate_id": "I-0006",
677
+ "location": {
678
+ "file": "coldfusion/app/OrderService.cfc",
679
+ "start_line": 5
680
+ },
681
+ "evidence": [
682
+ {
683
+ "file": "coldfusion/app/OrderService.cfc",
684
+ "start_line": 5,
685
+ "code": "SELECT * FROM orders WHERE id = #arguments.orderId#",
686
+ "why": "Unsanitized input concatenated into query",
687
+ "line": 5
688
+ }
689
+ ],
690
+ "severity": "critical",
691
+ "confidence": "medium",
692
+ "cwe": [
693
+ "CWE-89"
694
+ ],
695
+ "recommendation": "Use parameterized queries",
696
+ "status": "likely",
697
+ "verification": {
698
+ "verdict": "likely",
699
+ "reason_code": "code_not_visible",
700
+ "checked": [
701
+ "Reviewed lines 4–6 of coldfusion/app/OrderService.cfc, which contain the unsanitized query SELECT * FROM orders WHERE id = #arguments.orderId#"
702
+ ],
703
+ "control_found": null,
704
+ "notes": "No integer cast, parameter binding, or ownership check is present in the visible code. The query concatenates user input directly, so the vulnerability remains unless a hidden global filter or configuration is applied, which is not shown.",
705
+ "suggested_subtype": null,
706
+ "suggested_cwe": null
707
+ },
708
+ "fingerprint": "kfp1:b6824160d6d38156b7a16dc1526dda6be874be4401ad843a3c185f94446dcf5e",
709
+ "baseline_state": "new"
710
+ },
711
+ {
712
+ "finding_id": "INJ-I-0007",
713
+ "category": "sql_injection",
714
+ "subtype": "string_concatenation",
715
+ "title": "SQL Injection in coldfusion/app/order.cfm:3",
716
+ "candidate_id": "I-0007",
717
+ "location": {
718
+ "file": "coldfusion/app/order.cfm",
719
+ "start_line": 3
720
+ },
721
+ "evidence": [
722
+ {
723
+ "file": "coldfusion/app/order.cfm",
724
+ "start_line": 3,
725
+ "code": "SELECT * FROM orders WHERE id = #url.id#",
726
+ "why": "Unsanitized input concatenated into query",
727
+ "line": 3
728
+ }
729
+ ],
730
+ "severity": "critical",
731
+ "confidence": "medium",
732
+ "cwe": [
733
+ "CWE-89"
734
+ ],
735
+ "recommendation": "Use parameterized queries",
736
+ "status": "likely",
737
+ "verification": {
738
+ "verdict": "likely",
739
+ "reason_code": "code_not_visible",
740
+ "checked": [
741
+ "Reviewed lines 2–4 of coldfusion/app/order.cfm, which include the unsanitized query SELECT * FROM orders WHERE id = #url.id#"
742
+ ],
743
+ "control_found": null,
744
+ "notes": "The query directly interpolates the URL parameter without casting or parameterization. No ownership or role check is visible in this snippet.",
745
+ "suggested_subtype": null,
746
+ "suggested_cwe": null
747
+ },
748
+ "fingerprint": "kfp1:d2650d07ae7cedfc90bd1abda3c42a63716563d54c45ecde428ae21505214f72",
749
+ "baseline_state": "new"
750
+ },
751
+ {
752
+ "finding_id": "INJ-I-0008",
753
+ "category": "sql_injection",
754
+ "subtype": "string_concatenation",
755
+ "title": "SQL Injection in dotnet-legacy/Web/Controllers/InvoiceController.cs:28",
756
+ "candidate_id": "I-0008",
757
+ "location": {
758
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
759
+ "start_line": 28
760
+ },
761
+ "evidence": [
762
+ {
763
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
764
+ "start_line": 28,
765
+ "code": "var list = db.Database.SqlQuery<Invoice>(\"SELECT * FROM Invoices WHERE Customer = '\" + customer + \"'\").ToList();",
766
+ "why": "unsanitized string concatenation",
767
+ "line": 28
768
+ }
769
+ ],
770
+ "severity": "critical",
771
+ "confidence": "high",
772
+ "cwe": [
773
+ "CWE-89"
774
+ ],
775
+ "recommendation": "Use parameterized queries",
776
+ "status": "confirmed",
777
+ "verification": {
778
+ "verdict": "confirmed",
779
+ "reason_code": "traced",
780
+ "checked": [
781
+ "dotnet-legacy/Web/Controllers/InvoiceController.cs lines 26-28"
782
+ ],
783
+ "control_found": null,
784
+ "notes": "The customer value is concatenated into the SQL string and executed via SqlQuery, with no sanitization or parameterization.",
785
+ "suggested_subtype": null,
786
+ "suggested_cwe": null
787
+ },
788
+ "fingerprint": "kfp1:81106c3dfe7181ee5aad08bbd8e6c94143e8ebb6bf477a18828ca399339d1271",
789
+ "baseline_state": "new"
790
+ },
791
+ {
792
+ "finding_id": "INJ-I-0009",
793
+ "category": "sql_injection",
794
+ "subtype": "string_concatenation",
795
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/EditOrder.aspx.vb:7",
796
+ "candidate_id": "I-0009",
797
+ "location": {
798
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
799
+ "start_line": 7
800
+ },
801
+ "evidence": [
802
+ {
803
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
804
+ "start_line": 7,
805
+ "code": "Dim sql As String = \"UPDATE Orders SET Note = '\" & txtNote.Text & \"'\"",
806
+ "why": "Unsanitized input concatenated into query",
807
+ "line": 7
808
+ },
809
+ {
810
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
811
+ "start_line": 8,
812
+ "code": "sql &= \" WHERE OrderId = \" & hfOrderId.Value",
813
+ "why": "Unsanitized input concatenated into query",
814
+ "line": 8
815
+ }
816
+ ],
817
+ "severity": "critical",
818
+ "confidence": "medium",
819
+ "cwe": [
820
+ "CWE-89"
821
+ ],
822
+ "recommendation": "Use parameterized queries",
823
+ "status": "likely",
824
+ "verification": {
825
+ "verdict": "likely",
826
+ "reason_code": "code_not_visible",
827
+ "checked": [
828
+ "Reviewed lines 6–8 of dotnet-legacy/Web/Orders/EditOrder.aspx.vb, where the SQL string is built by concatenating txtNote.Text and hfOrderId.Value"
829
+ ],
830
+ "control_found": null,
831
+ "notes": "The code concatenates user input into the UPDATE statement without parameterization or ownership validation. No compensating control is visible.",
832
+ "suggested_subtype": null,
833
+ "suggested_cwe": null
834
+ },
835
+ "fingerprint": "kfp1:999e755f32c1edc6b46f7497ebdfd065d8afcc3518cab3211f45fc11a7342afd",
836
+ "baseline_state": "new"
837
+ },
838
+ {
839
+ "finding_id": "INJ-I-0010",
840
+ "category": "sql_injection",
841
+ "subtype": "string_concatenation",
842
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/EditOrder.aspx.vb:8",
843
+ "candidate_id": "I-0010",
844
+ "location": {
845
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
846
+ "start_line": 8
847
+ },
848
+ "evidence": [
849
+ {
850
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
851
+ "start_line": 8,
852
+ "code": "sql &= \" WHERE OrderId = \" & hfOrderId.Value",
853
+ "why": "Unsanitized input concatenated into query",
854
+ "line": 8
855
+ }
856
+ ],
857
+ "severity": "critical",
858
+ "confidence": "medium",
859
+ "cwe": [
860
+ "CWE-89"
861
+ ],
862
+ "recommendation": "Use parameterized queries",
863
+ "status": "likely",
864
+ "verification": {
865
+ "verdict": "likely",
866
+ "reason_code": "code_not_visible",
867
+ "checked": [
868
+ "Reviewed lines 7–8 of dotnet-legacy/Web/Orders/EditOrder.aspx.vb, showing the same unsanitized concatenation of OrderId"
869
+ ],
870
+ "control_found": null,
871
+ "notes": "Same as INJ-I-0009; no parameterization or ownership check is present in the visible code.",
872
+ "suggested_subtype": null,
873
+ "suggested_cwe": null
874
+ },
875
+ "fingerprint": "kfp1:323eb8697c45d7b7466292d80a0763dd2310608e0975d837273bde14e3458f59",
876
+ "baseline_state": "new"
877
+ },
878
+ {
879
+ "finding_id": "INJ-I-0011",
880
+ "category": "sql_injection",
881
+ "subtype": "string_concatenation",
882
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/OrderDetail.aspx.cs:13",
883
+ "candidate_id": "I-0011",
884
+ "location": {
885
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
886
+ "start_line": 13
887
+ },
888
+ "evidence": [
889
+ {
890
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
891
+ "start_line": 13,
892
+ "code": "var cmd = new SqlCommand(\"SELECT * FROM Orders WHERE OrderId = \" + id, conn);",
893
+ "why": "Unsanitized input concatenated into query",
894
+ "line": 13
895
+ },
896
+ {
897
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
898
+ "start_line": 19,
899
+ "code": "string sql2 = string.Format(\"SELECT * FROM Logs WHERE Ref = '{0}'\", Request[\"ref\"]);",
900
+ "why": "Unsanitized input concatenated into query",
901
+ "line": 19
902
+ },
903
+ {
904
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
905
+ "start_line": 20,
906
+ "code": "string sql3 = $\"DELETE FROM Carts WHERE CartId = {Request.Form[\"cart\"]}\";",
907
+ "why": "Unsanitized input concatenated into query",
908
+ "line": 20
909
+ }
910
+ ],
911
+ "severity": "critical",
912
+ "confidence": "medium",
913
+ "cwe": [
914
+ "CWE-89"
915
+ ],
916
+ "recommendation": "Use parameterized queries",
917
+ "status": "likely",
918
+ "verification": {
919
+ "verdict": "likely",
920
+ "reason_code": "code_not_visible",
921
+ "checked": [
922
+ "Reviewed lines 10–22 of dotnet-legacy/Web/Orders/OrderDetail.aspx.cs, which include unsanitized queries at lines 13, 19, and 20, and a parameterized safe query at lines 16–18"
923
+ ],
924
+ "control_found": null,
925
+ "notes": "Although a parameterized query with user ownership is present, the unsanitized queries are still executed and not protected by any ownership check. No compensating control is visible for those queries.",
926
+ "suggested_subtype": null,
927
+ "suggested_cwe": null
928
+ },
929
+ "fingerprint": "kfp1:c9aa426f72d032a9b0729b0905c9c25a6fb8844a92a91c45d72d3e26170bbae5",
930
+ "baseline_state": "new"
931
+ },
932
+ {
933
+ "finding_id": "INJ-I-0012",
934
+ "category": "sql_injection",
935
+ "subtype": "string_concatenation",
936
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/OrderDetail.aspx.cs:19",
937
+ "candidate_id": "I-0012",
938
+ "location": {
939
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
940
+ "start_line": 19
941
+ },
942
+ "evidence": [
943
+ {
944
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
945
+ "start_line": 19,
946
+ "code": "string sql2 = string.Format(\"SELECT * FROM Logs WHERE Ref = '{0}'\", Request[\"ref\"]);",
947
+ "why": "Unsanitized input concatenated into query",
948
+ "line": 19
949
+ },
950
+ {
951
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
952
+ "start_line": 20,
953
+ "code": "string sql3 = $\"DELETE FROM Carts WHERE CartId = {Request.Form[\"cart\"]}\";",
954
+ "why": "Unsanitized input concatenated into query",
955
+ "line": 20
956
+ }
957
+ ],
958
+ "severity": "critical",
959
+ "confidence": "high",
960
+ "cwe": [
961
+ "CWE-89"
962
+ ],
963
+ "recommendation": "Use parameterized queries",
964
+ "status": "confirmed",
965
+ "verification": {
966
+ "verdict": "confirmed",
967
+ "reason_code": "traced",
968
+ "checked": [
969
+ "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs lines 13-22"
970
+ ],
971
+ "control_found": null,
972
+ "notes": "The query built with string concatenation is executed without any cast, allow‑list or parameter binding. No ownership or role check is present in the visible code.",
973
+ "suggested_subtype": null,
974
+ "suggested_cwe": null
975
+ },
976
+ "fingerprint": "kfp1:c0adff113329292a51bb7d2f2aa03dd242c6d767cee060a789ff750f3ab9b946",
977
+ "baseline_state": "new"
978
+ },
979
+ {
980
+ "finding_id": "INJ-I-0013",
981
+ "category": "sql_injection",
982
+ "subtype": "string_concatenation",
983
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/OrderDetail.aspx.cs:20",
984
+ "candidate_id": "I-0013",
985
+ "location": {
986
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
987
+ "start_line": 20
988
+ },
989
+ "evidence": [
990
+ {
991
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
992
+ "start_line": 20,
993
+ "code": "string sql3 = $\"DELETE FROM Carts WHERE CartId = {Request.Form[\"cart\"]}\";",
994
+ "why": "Unsanitized input concatenated into query",
995
+ "line": 20
996
+ }
997
+ ],
998
+ "severity": "critical",
999
+ "confidence": "high",
1000
+ "cwe": [
1001
+ "CWE-89"
1002
+ ],
1003
+ "recommendation": "Use parameterized queries",
1004
+ "status": "confirmed",
1005
+ "verification": {
1006
+ "verdict": "confirmed",
1007
+ "reason_code": "traced",
1008
+ "checked": [
1009
+ "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs lines 13-22"
1010
+ ],
1011
+ "control_found": null,
1012
+ "notes": "Same as INJ-I-0012: the DELETE statement is constructed via string interpolation and executed directly. No compensating control is visible.",
1013
+ "suggested_subtype": null,
1014
+ "suggested_cwe": null
1015
+ },
1016
+ "fingerprint": "kfp1:4dbfab5201fa4fb196296b8ea0e4be14bb1ae1e6d3136309f24af78eb9977441",
1017
+ "baseline_state": "new"
1018
+ },
1019
+ {
1020
+ "finding_id": "INJ-I-0014",
1021
+ "category": "sql_injection",
1022
+ "subtype": "string_concatenation",
1023
+ "title": "SQL Injection in java-legacy/WebContent/order/detail.jsp:5",
1024
+ "candidate_id": "I-0014",
1025
+ "location": {
1026
+ "file": "java-legacy/WebContent/order/detail.jsp",
1027
+ "start_line": 5
1028
+ },
1029
+ "evidence": [
1030
+ {
1031
+ "file": "java-legacy/WebContent/order/detail.jsp",
1032
+ "start_line": 5,
1033
+ "code": "ResultSet rs = c.createStatement().executeQuery(\"SELECT * FROM orders WHERE order_no = '\" + no + \"'\");",
1034
+ "why": "Unsanitized input concatenated into query",
1035
+ "line": 5
1036
+ }
1037
+ ],
1038
+ "severity": "critical",
1039
+ "confidence": "high",
1040
+ "cwe": [
1041
+ "CWE-89"
1042
+ ],
1043
+ "recommendation": "Use parameterized queries",
1044
+ "status": "confirmed",
1045
+ "verification": {
1046
+ "verdict": "confirmed",
1047
+ "reason_code": "traced",
1048
+ "checked": [
1049
+ "java-legacy/WebContent/order/detail.jsp lines 3-5"
1050
+ ],
1051
+ "control_found": null,
1052
+ "notes": "The SQL statement concatenates the request parameter directly. No cast, allow‑list, or parameter binding is present.",
1053
+ "suggested_subtype": null,
1054
+ "suggested_cwe": null
1055
+ },
1056
+ "fingerprint": "kfp1:b8e7acb1ca7119156e96cf2bd42356b53bb6107a0a904b492534714730ac45f8",
1057
+ "baseline_state": "new"
1058
+ },
1059
+ {
1060
+ "finding_id": "INJ-I-0015",
1061
+ "category": "sql_injection",
1062
+ "subtype": "dynamic_identifier",
1063
+ "title": "SQL Injection in java-legacy/src/com/acme/mapper/OrderMapper.xml:9",
1064
+ "candidate_id": "I-0015",
1065
+ "location": {
1066
+ "file": "java-legacy/src/com/acme/mapper/OrderMapper.xml",
1067
+ "start_line": 9
1068
+ },
1069
+ "evidence": [
1070
+ {
1071
+ "file": "java-legacy/src/com/acme/mapper/OrderMapper.xml",
1072
+ "start_line": 9,
1073
+ "code": "ORDER BY ${orderBy}",
1074
+ "why": "raw identifier substitution from caller",
1075
+ "line": 9
1076
+ }
1077
+ ],
1078
+ "severity": "critical",
1079
+ "confidence": "high",
1080
+ "cwe": [
1081
+ "CWE-89"
1082
+ ],
1083
+ "recommendation": "Allow‑list ORDER BY values or use parameter binding",
1084
+ "status": "confirmed",
1085
+ "verification": {
1086
+ "verdict": "confirmed",
1087
+ "reason_code": "traced",
1088
+ "checked": [
1089
+ "java-legacy/src/com/acme/mapper/OrderMapper.xml lines 8-10"
1090
+ ],
1091
+ "control_found": null,
1092
+ "notes": "The ORDER BY clause uses a raw identifier substitution (${orderBy}) without any allow‑list or binding, exposing the query to injection.",
1093
+ "suggested_subtype": null,
1094
+ "suggested_cwe": null
1095
+ },
1096
+ "fingerprint": "kfp1:a0212ab67bd8e95a9fa71520fd15ba101852184637a1a60ec1bb91525308964f",
1097
+ "baseline_state": "new"
1098
+ },
1099
+ {
1100
+ "finding_id": "INJ-I-0016",
1101
+ "category": "sql_injection",
1102
+ "subtype": "string_concatenation",
1103
+ "title": "SQL Injection in java-legacy/src/com/acme/struts/EditUserAction.java:13",
1104
+ "candidate_id": "I-0016",
1105
+ "location": {
1106
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
1107
+ "start_line": 13
1108
+ },
1109
+ "evidence": [
1110
+ {
1111
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
1112
+ "start_line": 13,
1113
+ "code": "PreparedStatement ps = conn.prepareStatement(\"UPDATE users SET email = '\" + email + \"' WHERE id = \" + userId);",
1114
+ "why": "Unsanitized input concatenated into query",
1115
+ "line": 13
1116
+ }
1117
+ ],
1118
+ "severity": "critical",
1119
+ "confidence": "high",
1120
+ "cwe": [
1121
+ "CWE-89"
1122
+ ],
1123
+ "recommendation": "Use parameterized queries",
1124
+ "status": "confirmed",
1125
+ "verification": {
1126
+ "verdict": "confirmed",
1127
+ "reason_code": "traced",
1128
+ "checked": [
1129
+ "java-legacy/src/com/acme/struts/EditUserAction.java lines 10-14"
1130
+ ],
1131
+ "control_found": null,
1132
+ "notes": "The UPDATE statement concatenates user input without parameter binding or validation. No ownership or role check is visible.",
1133
+ "suggested_subtype": null,
1134
+ "suggested_cwe": null
1135
+ },
1136
+ "fingerprint": "kfp1:2976fb21c23c927d1de0f3af7dc883d9b66a1d051ec67bb9ec1b44396f6479fc",
1137
+ "baseline_state": "new"
1138
+ },
1139
+ {
1140
+ "finding_id": "INJ-I-0017",
1141
+ "category": "sql_injection",
1142
+ "subtype": "string_concatenation",
1143
+ "title": "SQL Injection in java-legacy/src/com/acme/web/InvoiceController.java:29",
1144
+ "candidate_id": "I-0017",
1145
+ "location": {
1146
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
1147
+ "start_line": 29
1148
+ },
1149
+ "evidence": [
1150
+ {
1151
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
1152
+ "start_line": 29,
1153
+ "code": "model.addAttribute(\"list\", session.createQuery(\"from Invoice i where i.customer = '\" + customer + \"'\").list());",
1154
+ "why": "Unsanitized input concatenated into query",
1155
+ "line": 29
1156
+ }
1157
+ ],
1158
+ "severity": "critical",
1159
+ "confidence": "high",
1160
+ "cwe": [
1161
+ "CWE-89"
1162
+ ],
1163
+ "recommendation": "Use parameterized queries",
1164
+ "status": "confirmed",
1165
+ "verification": {
1166
+ "verdict": "confirmed",
1167
+ "reason_code": "traced",
1168
+ "checked": [
1169
+ "java-legacy/src/com/acme/web/InvoiceController.java lines 27-30"
1170
+ ],
1171
+ "control_found": null,
1172
+ "notes": "The HQL query concatenates the request parameter directly. No parameter binding, cast, or allow‑list is present.",
1173
+ "suggested_subtype": null,
1174
+ "suggested_cwe": null
1175
+ },
1176
+ "fingerprint": "kfp1:7796f734194bfe71dc7268f597efd6338a4e967a788e0220edb61b8e1c198b16",
1177
+ "baseline_state": "new"
1178
+ },
1179
+ {
1180
+ "finding_id": "INJ-I-0018",
1181
+ "category": "sql_injection",
1182
+ "subtype": "string_concatenation",
1183
+ "title": "SQL Injection in java-legacy/src/com/acme/web/OrderServlet.java:15",
1184
+ "candidate_id": "I-0018",
1185
+ "location": {
1186
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1187
+ "start_line": 15
1188
+ },
1189
+ "evidence": [
1190
+ {
1191
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1192
+ "start_line": 15,
1193
+ "code": "ResultSet rs = stmt.executeQuery(\"SELECT * FROM orders WHERE id = '\" + id + \"'\");",
1194
+ "why": "unsanitized string concatenation",
1195
+ "line": 15
1196
+ }
1197
+ ],
1198
+ "severity": "critical",
1199
+ "confidence": "high",
1200
+ "cwe": [
1201
+ "CWE-89"
1202
+ ],
1203
+ "recommendation": "Use prepared statements",
1204
+ "status": "confirmed",
1205
+ "verification": {
1206
+ "verdict": "confirmed",
1207
+ "reason_code": "traced",
1208
+ "checked": [
1209
+ "java-legacy/src/com/acme/web/OrderServlet.java lines 11-15"
1210
+ ],
1211
+ "control_found": null,
1212
+ "notes": "The query concatenates the raw request parameter \"orderId\" into the SQL string without any cast, binding, or allow‑list. No ownership or role check is present.",
1213
+ "suggested_subtype": null,
1214
+ "suggested_cwe": null
1215
+ },
1216
+ "fingerprint": "kfp1:eed713c040fe4b03f16a649b4da2fa326995fd074ad3ba46bbbb4958486b6b8d",
1217
+ "baseline_state": "new"
1218
+ },
1219
+ {
1220
+ "finding_id": "INJ-I-0019",
1221
+ "category": "sql_injection",
1222
+ "subtype": "string_concatenation",
1223
+ "title": "SQL Injection in java-legacy/src/com/acme/web/OrderServlet.java:17",
1224
+ "candidate_id": "I-0019",
1225
+ "location": {
1226
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1227
+ "start_line": 17
1228
+ },
1229
+ "evidence": [
1230
+ {
1231
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1232
+ "start_line": 17,
1233
+ "code": "sb.append(\" AND order_id = \").append(id);",
1234
+ "why": "unsanitized identifier concatenation",
1235
+ "line": 17
1236
+ },
1237
+ {
1238
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1239
+ "start_line": 18,
1240
+ "code": "ResultSet items = stmt.executeQuery(sb.toString());",
1241
+ "why": "query executed",
1242
+ "line": 18
1243
+ }
1244
+ ],
1245
+ "severity": "critical",
1246
+ "confidence": "high",
1247
+ "cwe": [
1248
+ "CWE-89"
1249
+ ],
1250
+ "recommendation": "Use prepared statements for dynamic values",
1251
+ "status": "confirmed",
1252
+ "verification": {
1253
+ "verdict": "confirmed",
1254
+ "reason_code": "traced",
1255
+ "checked": [
1256
+ "java-legacy/src/com/acme/web/OrderServlet.java lines 16-18"
1257
+ ],
1258
+ "control_found": null,
1259
+ "notes": "The dynamic SQL built with a StringBuilder appends the raw \"id\" parameter directly into the query. No cast, binding, or allow‑list is applied.",
1260
+ "suggested_subtype": null,
1261
+ "suggested_cwe": null
1262
+ },
1263
+ "fingerprint": "kfp1:22951bde3fee457a15c0237a8c8b4af2558c7f1e2ce5d391ee825918057e7480",
1264
+ "baseline_state": "new"
1265
+ },
1266
+ {
1267
+ "finding_id": "INJ-I-0020",
1268
+ "category": "sql_injection",
1269
+ "subtype": "string_concatenation",
1270
+ "title": "SQL Injection in node-express/routes/orders.js:17",
1271
+ "candidate_id": "I-0020",
1272
+ "location": {
1273
+ "file": "node-express/routes/orders.js",
1274
+ "start_line": 17
1275
+ },
1276
+ "evidence": [
1277
+ {
1278
+ "file": "node-express/routes/orders.js",
1279
+ "start_line": 17,
1280
+ "code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
1281
+ "why": "Unsanitized input concatenated into query",
1282
+ "line": 17
1283
+ }
1284
+ ],
1285
+ "severity": "critical",
1286
+ "confidence": "high",
1287
+ "cwe": [
1288
+ "CWE-89"
1289
+ ],
1290
+ "recommendation": "Use parameterized queries",
1291
+ "status": "confirmed",
1292
+ "verification": {
1293
+ "verdict": "confirmed",
1294
+ "reason_code": "traced",
1295
+ "checked": [
1296
+ "node-express/routes/orders.js:17"
1297
+ ],
1298
+ "control_found": null,
1299
+ "notes": "The query concatenates req.query.month directly into the SQL string. The only surrounding check is ensureLoggedIn, which only verifies authentication, not input sanitization or ownership.",
1300
+ "suggested_subtype": null,
1301
+ "suggested_cwe": null
1302
+ },
1303
+ "fingerprint": "kfp1:fc0f31011ca4510d026845f7fb0dcd77812ef24f6ea5329ad24fbc08a15b58ff",
1304
+ "baseline_state": "new"
1305
+ },
1306
+ {
1307
+ "finding_id": "INJ-I-0021",
1308
+ "category": "sql_injection",
1309
+ "subtype": "string_concatenation",
1310
+ "title": "SQL Injection in node-express/routes/orders.js:18",
1311
+ "candidate_id": "I-0021",
1312
+ "location": {
1313
+ "file": "node-express/routes/orders.js",
1314
+ "start_line": 29
1315
+ },
1316
+ "evidence": [
1317
+ {
1318
+ "file": "node-express/routes/orders.js",
1319
+ "start_line": 18,
1320
+ "code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
1321
+ "why": "Unsanitized input concatenated into query",
1322
+ "line": 18
1323
+ }
1324
+ ],
1325
+ "severity": "critical",
1326
+ "confidence": "high",
1327
+ "cwe": [
1328
+ "CWE-89"
1329
+ ],
1330
+ "recommendation": "Use parameterized queries",
1331
+ "status": "confirmed",
1332
+ "verification": {
1333
+ "verdict": "confirmed",
1334
+ "reason_code": "traced",
1335
+ "checked": [
1336
+ "node-express/routes/orders.js:29"
1337
+ ],
1338
+ "control_found": null,
1339
+ "notes": "The shop_id value is interpolated directly into the SQL string with template literals. No cast, allow‑list, or parameter binding is present.",
1340
+ "suggested_subtype": null,
1341
+ "suggested_cwe": null
1342
+ },
1343
+ "fingerprint": "kfp1:5fef20fe2b97718add08c2ce704c057931ba2f5b5f9277493213205e35d8fde8",
1344
+ "baseline_state": "new"
1345
+ },
1346
+ {
1347
+ "finding_id": "INJ-I-0023",
1348
+ "category": "sql_injection",
1349
+ "subtype": "string_concatenation",
1350
+ "title": "SQL Injection in node-express/routes/orders.js:20",
1351
+ "candidate_id": "I-0023",
1352
+ "location": {
1353
+ "file": "node-express/routes/orders.js",
1354
+ "start_line": 25
1355
+ },
1356
+ "evidence": [
1357
+ {
1358
+ "file": "node-express/routes/orders.js",
1359
+ "start_line": 20,
1360
+ "code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
1361
+ "why": "Unsanitized input concatenated into query",
1362
+ "line": 20
1363
+ }
1364
+ ],
1365
+ "severity": "critical",
1366
+ "confidence": "high",
1367
+ "cwe": [
1368
+ "CWE-89"
1369
+ ],
1370
+ "recommendation": "Use parameterized queries",
1371
+ "status": "confirmed",
1372
+ "verification": {
1373
+ "verdict": "confirmed",
1374
+ "reason_code": "traced",
1375
+ "checked": [
1376
+ "node-express/routes/orders.js:25"
1377
+ ],
1378
+ "control_found": null,
1379
+ "notes": "The $where clause concatenates req.query.n into a JavaScript expression. No sanitization or parameter binding is applied.",
1380
+ "suggested_subtype": null,
1381
+ "suggested_cwe": null
1382
+ },
1383
+ "fingerprint": "kfp1:8e5cfd6e3f422eadec3eaa05620719e1aca2c4e0ef3bfd5aa14e641d10f8be5e",
1384
+ "baseline_state": "new"
1385
+ },
1386
+ {
1387
+ "finding_id": "INJ-I-0024",
1388
+ "category": "sql_injection",
1389
+ "subtype": "string_concatenation",
1390
+ "title": "SQL Injection in perl-cgi/cgi-bin/order.cgi:13",
1391
+ "candidate_id": "I-0024",
1392
+ "location": {
1393
+ "file": "perl-cgi/cgi-bin/order.cgi",
1394
+ "start_line": 13
1395
+ },
1396
+ "evidence": [
1397
+ {
1398
+ "file": "perl-cgi/cgi-bin/order.cgi",
1399
+ "start_line": 13,
1400
+ "code": "my $sth = $dbh->prepare(\"SELECT * FROM orders WHERE id = $id\");",
1401
+ "why": "Unsanitized input concatenated into query",
1402
+ "line": 13
1403
+ },
1404
+ {
1405
+ "file": "perl-cgi/cgi-bin/order.cgi",
1406
+ "start_line": 19,
1407
+ "code": "$dbh->do(\"DELETE FROM carts WHERE cart_id = \" . $q->param('cart'))",
1408
+ "why": "Unsanitized input concatenated into query",
1409
+ "line": 19
1410
+ }
1411
+ ],
1412
+ "severity": "critical",
1413
+ "confidence": "high",
1414
+ "cwe": [
1415
+ "CWE-89"
1416
+ ],
1417
+ "recommendation": "Use parameterized queries",
1418
+ "status": "confirmed",
1419
+ "verification": {
1420
+ "verdict": "confirmed",
1421
+ "reason_code": "traced",
1422
+ "checked": [
1423
+ "perl-cgi/cgi-bin/order.cgi:13",
1424
+ "perl-cgi/cgi-bin/order.cgi:19"
1425
+ ],
1426
+ "control_found": null,
1427
+ "notes": "Both SQL statements interpolate user input directly into the query string. No parameter binding or sanitization is used.",
1428
+ "suggested_subtype": null,
1429
+ "suggested_cwe": null
1430
+ },
1431
+ "fingerprint": "kfp1:0594761f603533b7391a9b7a8464e6865b71c1b61b71fdcd10991d861f51b5fb",
1432
+ "baseline_state": "new"
1433
+ },
1434
+ {
1435
+ "finding_id": "INJ-I-0025",
1436
+ "category": "sql_injection",
1437
+ "subtype": "string_concatenation",
1438
+ "title": "SQL Injection in perl-cgi/cgi-bin/order.cgi:19",
1439
+ "candidate_id": "I-0025",
1440
+ "location": {
1441
+ "file": "perl-cgi/cgi-bin/order.cgi",
1442
+ "start_line": 19
1443
+ },
1444
+ "evidence": [
1445
+ {
1446
+ "file": "perl-cgi/cgi-bin/order.cgi",
1447
+ "start_line": 19,
1448
+ "code": "$dbh->do(\"DELETE FROM carts WHERE cart_id = \" . $q->param('cart'))",
1449
+ "why": "Unsanitized input concatenated into query",
1450
+ "line": 19
1451
+ }
1452
+ ],
1453
+ "severity": "critical",
1454
+ "confidence": "high",
1455
+ "cwe": [
1456
+ "CWE-89"
1457
+ ],
1458
+ "recommendation": "Use parameterized queries",
1459
+ "status": "confirmed",
1460
+ "verification": {
1461
+ "verdict": "confirmed",
1462
+ "reason_code": "traced",
1463
+ "checked": [
1464
+ "perl-cgi/cgi-bin/order.cgi:19"
1465
+ ],
1466
+ "control_found": null,
1467
+ "notes": "The DELETE statement concatenates the cart_id directly from user input without any sanitisation, cast, or parameter binding. No ownership or role check is present in the visible code.",
1468
+ "suggested_subtype": null,
1469
+ "suggested_cwe": null
1470
+ },
1471
+ "fingerprint": "kfp1:c7206e5510c18d43418b9ed9a4d20047783ea5263cfdc395a138b735b613debc",
1472
+ "baseline_state": "new"
1473
+ },
1474
+ {
1475
+ "finding_id": "INJ-I-0026",
1476
+ "category": "sql_injection",
1477
+ "subtype": "string_concatenation",
1478
+ "title": "SQL Injection in php-codeigniter/application/controllers/Invoice.php:10",
1479
+ "candidate_id": "I-0026",
1480
+ "location": {
1481
+ "file": "php-codeigniter/application/controllers/Invoice.php",
1482
+ "start_line": 10
1483
+ },
1484
+ "evidence": [
1485
+ {
1486
+ "file": "php-codeigniter/application/controllers/Invoice.php",
1487
+ "start_line": 10,
1488
+ "code": "$q = $this->db->query(\"SELECT * FROM invoices WHERE id = $id\");",
1489
+ "why": "unsanitized string concatenation",
1490
+ "line": 10
1491
+ }
1492
+ ],
1493
+ "severity": "critical",
1494
+ "confidence": "high",
1495
+ "cwe": [
1496
+ "CWE-89"
1497
+ ],
1498
+ "recommendation": "Use parameter binding",
1499
+ "status": "confirmed",
1500
+ "verification": {
1501
+ "verdict": "confirmed",
1502
+ "reason_code": "traced",
1503
+ "checked": [
1504
+ "php-codeigniter/application/controllers/Invoice.php lines 9-11",
1505
+ "php-codeigniter/application/controllers/Invoice.php lines 14-17"
1506
+ ],
1507
+ "control_found": null,
1508
+ "notes": "The `view` method concatenates the `$id` parameter into a raw SQL string. No type cast, binding, or allow‑list is visible. The `mine` method does include a user‑id filter, but the vulnerable `view` method lacks any ownership check.",
1509
+ "suggested_subtype": null,
1510
+ "suggested_cwe": null
1511
+ },
1512
+ "fingerprint": "kfp1:cab7179a42b52f77a862191152d4b0ebc8511c6c77b956503f7c53525c9390b0",
1513
+ "baseline_state": "new"
1514
+ },
1515
+ {
1516
+ "finding_id": "INJ-I-0027",
1517
+ "category": "sql_injection",
1518
+ "subtype": "string_concatenation",
1519
+ "title": "SQL Injection in php-plain/admin.php:12",
1520
+ "candidate_id": "I-0027",
1521
+ "location": {
1522
+ "file": "php-plain/admin.php",
1523
+ "start_line": 8
1524
+ },
1525
+ "evidence": [
1526
+ {
1527
+ "file": "php-plain/admin.php",
1528
+ "start_line": 12,
1529
+ "code": "mysql_query(\"DELETE FROM users WHERE id = $id\");",
1530
+ "why": "Unsanitized input concatenated into query",
1531
+ "line": 12
1532
+ }
1533
+ ],
1534
+ "severity": "critical",
1535
+ "confidence": "high",
1536
+ "cwe": [
1537
+ "CWE-89"
1538
+ ],
1539
+ "recommendation": "Use parameterized queries",
1540
+ "status": "confirmed",
1541
+ "verification": {
1542
+ "verdict": "confirmed",
1543
+ "reason_code": "traced",
1544
+ "checked": [
1545
+ "php-plain/admin.php:12"
1546
+ ],
1547
+ "control_found": null,
1548
+ "notes": "The DELETE query uses $id from $_POST without casting or parameter binding. The included auth.php only performs a login check and does not enforce ownership.",
1549
+ "suggested_subtype": null,
1550
+ "suggested_cwe": null
1551
+ },
1552
+ "fingerprint": "kfp1:014bf1f148a69f5a2348bedec2ac3df44db7c5d193293c50159e266555329bf9",
1553
+ "baseline_state": "new"
1554
+ },
1555
+ {
1556
+ "finding_id": "INJ-I-0028",
1557
+ "category": "sql_injection",
1558
+ "subtype": "string_concatenation",
1559
+ "title": "SQL Injection in php-plain/admin.php:12",
1560
+ "candidate_id": "I-0028",
1561
+ "location": {
1562
+ "file": "php-plain/admin.php",
1563
+ "start_line": 12
1564
+ },
1565
+ "evidence": [
1566
+ {
1567
+ "file": "php-plain/admin.php",
1568
+ "start_line": 12,
1569
+ "code": "mysql_query(\"DELETE FROM users WHERE id = $id\");",
1570
+ "why": "Unsanitized input concatenated into query",
1571
+ "line": 12
1572
+ }
1573
+ ],
1574
+ "severity": "critical",
1575
+ "confidence": "high",
1576
+ "cwe": [
1577
+ "CWE-89"
1578
+ ],
1579
+ "recommendation": "Use parameterized queries",
1580
+ "status": "confirmed",
1581
+ "verification": {
1582
+ "verdict": "confirmed",
1583
+ "reason_code": "traced",
1584
+ "checked": [
1585
+ "php-plain/admin.php:12"
1586
+ ],
1587
+ "control_found": null,
1588
+ "notes": "Same as INJ-I-0027: unsanitised $id used in a DELETE statement with no ownership enforcement.",
1589
+ "suggested_subtype": null,
1590
+ "suggested_cwe": null
1591
+ },
1592
+ "fingerprint": "kfp1:59fca8cd0dedd8f70d286a5d3808292d9eddcf084148a63a448b7043b69c7948",
1593
+ "baseline_state": "new"
1594
+ },
1595
+ {
1596
+ "finding_id": "INJ-I-0031",
1597
+ "category": "sql_injection",
1598
+ "subtype": "string_concatenation",
1599
+ "title": "SQL Injection in php-plain/order_detail.php:6",
1600
+ "candidate_id": "I-0031",
1601
+ "location": {
1602
+ "file": "php-plain/order_detail.php",
1603
+ "start_line": 6
1604
+ },
1605
+ "evidence": [
1606
+ {
1607
+ "file": "php-plain/order_detail.php",
1608
+ "start_line": 5,
1609
+ "code": "$id = $_GET['id'];",
1610
+ "why": "source: GET parameter",
1611
+ "line": 5
1612
+ },
1613
+ {
1614
+ "file": "php-plain/order_detail.php",
1615
+ "start_line": 6,
1616
+ "code": "$res = mysql_query(\"SELECT * FROM orders WHERE id = \" . $id);",
1617
+ "why": "unsanitized input concatenated into query",
1618
+ "line": 6
1619
+ }
1620
+ ],
1621
+ "severity": "critical",
1622
+ "confidence": "high",
1623
+ "cwe": [
1624
+ "CWE-89"
1625
+ ],
1626
+ "recommendation": "Use parameterized queries",
1627
+ "status": "confirmed",
1628
+ "verification": {
1629
+ "verdict": "confirmed",
1630
+ "reason_code": "traced",
1631
+ "checked": [
1632
+ "php-plain/order_detail.php:6"
1633
+ ],
1634
+ "control_found": null,
1635
+ "notes": "The SELECT query concatenates $id from $_GET without casting or parameter binding. No ownership check is present in the visible code.",
1636
+ "suggested_subtype": null,
1637
+ "suggested_cwe": null
1638
+ },
1639
+ "fingerprint": "kfp1:3065614ddeff53f64ce73f2bd0210f5b36a2249f4d2561f69219095ad867ac9f",
1640
+ "baseline_state": "new"
1641
+ },
1642
+ {
1643
+ "finding_id": "INJ-I-0032",
1644
+ "category": "sql_injection",
1645
+ "subtype": "string_concatenation",
1646
+ "title": "SQL Injection in php-plain/search.php:7-11",
1647
+ "candidate_id": "I-0032",
1648
+ "location": {
1649
+ "file": "php-plain/search.php",
1650
+ "start_line": 8
1651
+ },
1652
+ "evidence": [
1653
+ {
1654
+ "file": "php-plain/search.php",
1655
+ "start_line": 5,
1656
+ "code": "$kw = addslashes($_GET['q']);",
1657
+ "why": "source: GET parameter, addslashes not sufficient",
1658
+ "line": 5
1659
+ },
1660
+ {
1661
+ "file": "php-plain/search.php",
1662
+ "start_line": 6,
1663
+ "code": "$sort = $_GET['sort'];",
1664
+ "why": "source: GET parameter, unsanitized",
1665
+ "line": 6
1666
+ },
1667
+ {
1668
+ "file": "php-plain/search.php",
1669
+ "start_line": 7,
1670
+ "code": "$sql = \"SELECT id, name FROM products WHERE 1=1\";",
1671
+ "why": "query base",
1672
+ "line": 7
1673
+ },
1674
+ {
1675
+ "file": "php-plain/search.php",
1676
+ "start_line": 8,
1677
+ "code": "$sql .= \" AND name LIKE '%\" . $kw . \"%'\";",
1678
+ "why": "unsanitized keyword concatenated",
1679
+ "line": 8
1680
+ },
1681
+ {
1682
+ "file": "php-plain/search.php",
1683
+ "start_line": 9,
1684
+ "code": "$sql .= \" ORDER BY \" . $sort;",
1685
+ "why": "unsanitized ORDER BY concatenated",
1686
+ "line": 9
1687
+ },
1688
+ {
1689
+ "file": "php-plain/search.php",
1690
+ "start_line": 11,
1691
+ "code": "$res = mysql_query($sql);",
1692
+ "why": "query executed",
1693
+ "line": 11
1694
+ }
1695
+ ],
1696
+ "severity": "critical",
1697
+ "confidence": "high",
1698
+ "cwe": [
1699
+ "CWE-89"
1700
+ ],
1701
+ "recommendation": "Use parameterized queries and allow‑list ORDER BY values",
1702
+ "status": "confirmed",
1703
+ "verification": {
1704
+ "verdict": "confirmed",
1705
+ "reason_code": "traced",
1706
+ "checked": [
1707
+ "php-plain/search.php:8",
1708
+ "php-plain/search.php:9"
1709
+ ],
1710
+ "control_found": null,
1711
+ "notes": "Both the keyword and ORDER BY clauses are built from user input without sanitisation or an allow‑list. No ownership or role check is visible.",
1712
+ "suggested_subtype": null,
1713
+ "suggested_cwe": null
1714
+ },
1715
+ "fingerprint": "kfp1:7290ec7c22df15c86f213eb1e27320eaf80f4bafc68a9a6813fb0bd32771b6da",
1716
+ "baseline_state": "new"
1717
+ },
1718
+ {
1719
+ "finding_id": "INJ-I-0034",
1720
+ "category": "sql_injection",
1721
+ "subtype": "string_concatenation",
1722
+ "title": "SQL Injection in php-plain/search.php:12-16",
1723
+ "candidate_id": "I-0034",
1724
+ "location": {
1725
+ "file": "php-plain/search.php",
1726
+ "start_line": 14
1727
+ },
1728
+ "evidence": [
1729
+ {
1730
+ "file": "php-plain/search.php",
1731
+ "start_line": 12,
1732
+ "code": "$q = <<<SQL\nSELECT * FROM products\nWHERE category = '{$_GET['cat']}'\nSQL;",
1733
+ "why": "unsanitized GET parameter interpolated into query",
1734
+ "line": 12
1735
+ },
1736
+ {
1737
+ "file": "php-plain/search.php",
1738
+ "start_line": 16,
1739
+ "code": "$res2 = mysql_query($q);",
1740
+ "why": "query executed",
1741
+ "line": 16
1742
+ }
1743
+ ],
1744
+ "severity": "critical",
1745
+ "confidence": "high",
1746
+ "cwe": [
1747
+ "CWE-89"
1748
+ ],
1749
+ "recommendation": "Use parameterized queries",
1750
+ "status": "confirmed",
1751
+ "verification": {
1752
+ "verdict": "confirmed",
1753
+ "reason_code": "traced",
1754
+ "checked": [
1755
+ "php-plain/search.php lines 12-16"
1756
+ ],
1757
+ "control_found": null,
1758
+ "notes": "The query uses an unsanitized GET parameter 'cat' interpolated directly into the SQL string and is executed with mysql_query. No cast, allow‑list, or ownership check is present.",
1759
+ "suggested_subtype": null,
1760
+ "suggested_cwe": null
1761
+ },
1762
+ "fingerprint": "kfp1:59c91493f8e83065ed0bab7d82438a17443ae7ba9bc82c1659053686f63083d7",
1763
+ "baseline_state": "new"
1764
+ },
1765
+ {
1766
+ "finding_id": "INJ-I-0036",
1767
+ "category": "sql_injection",
1768
+ "subtype": "string_interpolation",
1769
+ "title": "SQL Injection in python-flask/app.py:29",
1770
+ "candidate_id": "I-0036",
1771
+ "location": {
1772
+ "file": "python-flask/app.py",
1773
+ "start_line": 29
1774
+ },
1775
+ "evidence": [
1776
+ {
1777
+ "file": "python-flask/app.py",
1778
+ "start_line": 29,
1779
+ "code": "cur.execute(f\"SELECT * FROM products WHERE name LIKE '%{q}%'\")",
1780
+ "why": "unsanitized f-string interpolation",
1781
+ "line": 29
1782
+ }
1783
+ ],
1784
+ "severity": "critical",
1785
+ "confidence": "high",
1786
+ "cwe": [
1787
+ "CWE-89"
1788
+ ],
1789
+ "recommendation": "Use parameterized queries",
1790
+ "status": "confirmed",
1791
+ "verification": {
1792
+ "verdict": "confirmed",
1793
+ "reason_code": "traced",
1794
+ "checked": [
1795
+ "python-flask/app.py lines 27-30"
1796
+ ],
1797
+ "control_found": null,
1798
+ "notes": "The f‑string interpolates the raw query parameter q directly into the SQL statement without any sanitization or parameter binding.",
1799
+ "suggested_subtype": null,
1800
+ "suggested_cwe": null
1801
+ },
1802
+ "fingerprint": "kfp1:52c1a1b2beb4129f6fbbfa5c9bcc2f05de1cb6b4a835dcb6adfa7075c03edf79",
1803
+ "baseline_state": "new"
1804
+ },
1805
+ {
1806
+ "finding_id": "INJ-I-0037",
1807
+ "category": "sql_injection",
1808
+ "subtype": "stored_procedure_dynamic_sql",
1809
+ "title": "SQL Injection in sql-procs/sp_search_orders.sql:5-6",
1810
+ "candidate_id": "I-0037",
1811
+ "location": {
1812
+ "file": "sql-procs/sp_search_orders.sql",
1813
+ "start_line": 5
1814
+ },
1815
+ "evidence": [
1816
+ {
1817
+ "file": "sql-procs/sp_search_orders.sql",
1818
+ "start_line": 5,
1819
+ "code": "SET @sql = 'SELECT * FROM Orders WHERE Customer = ''' + @Customer + ''' ORDER BY ' + @Sort",
1820
+ "why": "dynamic SQL built from caller parameters",
1821
+ "line": 5
1822
+ },
1823
+ {
1824
+ "file": "sql-procs/sp_search_orders.sql",
1825
+ "start_line": 6,
1826
+ "code": "EXEC(@sql)",
1827
+ "why": "query executed",
1828
+ "line": 6
1829
+ }
1830
+ ],
1831
+ "severity": "critical",
1832
+ "confidence": "high",
1833
+ "cwe": [
1834
+ "CWE-89"
1835
+ ],
1836
+ "recommendation": "Avoid dynamic SQL; use parameterized queries or allow‑list ORDER BY values",
1837
+ "status": "confirmed",
1838
+ "verification": {
1839
+ "verdict": "confirmed",
1840
+ "reason_code": "traced",
1841
+ "checked": [
1842
+ "sql-procs/sp_search_orders.sql lines 5-6"
1843
+ ],
1844
+ "control_found": null,
1845
+ "notes": "The stored procedure constructs dynamic SQL by concatenating the `@Customer` and `@Sort` parameters directly into the query string. No allow‑list or parameter binding is present.",
1846
+ "suggested_subtype": null,
1847
+ "suggested_cwe": null
1848
+ },
1849
+ "fingerprint": "kfp1:dda6a5bb147b8aa741739e9da8799b0f942492567758d5d3209c1395a97c8f98",
1850
+ "baseline_state": "new"
1851
+ },
1852
+ {
1853
+ "finding_id": "MASS-M-0003-1",
1854
+ "category": "mass_assignment",
1855
+ "subtype": "privileged_field_writable",
1856
+ "title": "Account update allows role change",
1857
+ "candidate_id": "M-0003",
1858
+ "location": {
1859
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
1860
+ "start_line": 14
1861
+ },
1862
+ "evidence": [
1863
+ {
1864
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
1865
+ "line": 27,
1866
+ "why": "whole object binding, 'role' field writable"
1867
+ }
1868
+ ],
1869
+ "impact": "role",
1870
+ "severity": "critical",
1871
+ "confidence": "high",
1872
+ "cwe": [
1873
+ "CWE-915"
1874
+ ],
1875
+ "recommendation": "Remove 'role' from the binding or enforce role changes only by admins",
1876
+ "status": "confirmed",
1877
+ "verification": {
1878
+ "verdict": "confirmed",
1879
+ "reason_code": "traced",
1880
+ "checked": [
1881
+ "AccountController.java:13-15",
1882
+ "AccountController.java:14"
1883
+ ],
1884
+ "control_found": null,
1885
+ "notes": "The save method binds the entire Account object from the request and persists it without any role or ownership check.",
1886
+ "suggested_subtype": null,
1887
+ "suggested_cwe": null
1888
+ },
1889
+ "fingerprint": "kfp1:b427f067ed1c164d59f88449e3f3d39e10773b28dafaf7db21426e9881df1269",
1890
+ "baseline_state": "new"
1891
+ },
1892
+ {
1893
+ "finding_id": "MASS-M-0004-1",
1894
+ "category": "mass_assignment",
1895
+ "subtype": "privileged_field_writable",
1896
+ "title": "Account update allows role change",
1897
+ "candidate_id": "M-0004",
1898
+ "location": {
1899
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
1900
+ "start_line": 27
1901
+ },
1902
+ "evidence": [
1903
+ {
1904
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
1905
+ "line": 27,
1906
+ "why": "whole object binding, 'role' field writable"
1907
+ }
1908
+ ],
1909
+ "impact": "role",
1910
+ "severity": "critical",
1911
+ "confidence": "high",
1912
+ "cwe": [
1913
+ "CWE-915"
1914
+ ],
1915
+ "recommendation": "Remove 'role' from the binding or enforce role changes only by admins",
1916
+ "status": "confirmed",
1917
+ "verification": {
1918
+ "verdict": "confirmed",
1919
+ "reason_code": "traced",
1920
+ "checked": [
1921
+ "AccountController.java:27-28",
1922
+ "AccountController.java:27"
1923
+ ],
1924
+ "control_found": null,
1925
+ "notes": "The update method accepts a full Account object via @RequestBody and saves it directly, with no role validation.",
1926
+ "suggested_subtype": null,
1927
+ "suggested_cwe": null
1928
+ },
1929
+ "fingerprint": "kfp1:d35e421652a66141ead0efd242f840a67488e56a31666c91fce47bd638bb29f9",
1930
+ "baseline_state": "new"
1931
+ },
1932
+ {
1933
+ "finding_id": "MASS-M-0005-1",
1934
+ "category": "mass_assignment",
1935
+ "subtype": "privileged_field_writable",
1936
+ "title": "BeanUtils.populate allows role change",
1937
+ "candidate_id": "M-0005",
1938
+ "location": {
1939
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
1940
+ "start_line": 12
1941
+ },
1942
+ "evidence": [
1943
+ {
1944
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
1945
+ "line": 12,
1946
+ "why": "whole object binding, 'role' field writable"
1947
+ }
1948
+ ],
1949
+ "impact": "role",
1950
+ "severity": "critical",
1951
+ "confidence": "high",
1952
+ "cwe": [
1953
+ "CWE-915"
1954
+ ],
1955
+ "recommendation": "Remove 'role' from the binding or enforce role changes only by admins",
1956
+ "status": "confirmed",
1957
+ "verification": {
1958
+ "verdict": "confirmed",
1959
+ "reason_code": "traced",
1960
+ "checked": [
1961
+ "ProfileServlet.java:12-13",
1962
+ "ProfileServlet.java:12"
1963
+ ],
1964
+ "control_found": null,
1965
+ "notes": "BeanUtils.populate binds all request parameters to the Account object, including the role field, with no subsequent check.",
1966
+ "suggested_subtype": null,
1967
+ "suggested_cwe": null
1968
+ },
1969
+ "fingerprint": "kfp1:47cc80b68d7dd6dcc0d605fb83d8a929ebd6a5752da9de89a7b50339f698eaea",
1970
+ "baseline_state": "new"
1971
+ },
1972
+ {
1973
+ "finding_id": "MASS-M-0009-1",
1974
+ "category": "mass_assignment",
1975
+ "subtype": "privileged_field_writable",
1976
+ "title": "Team creation allows owner_id to be set via mass assignment",
1977
+ "candidate_id": "M-0009",
1978
+ "location": {
1979
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
1980
+ "start_line": 23
1981
+ },
1982
+ "evidence": [
1983
+ {
1984
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
1985
+ "line": 23,
1986
+ "why": "Team::create($request->all()); binds all request fields including owner_id"
1987
+ },
1988
+ {
1989
+ "file": "php-laravel/app/Models/Team.php",
1990
+ "line": 8,
1991
+ "why": "$fillable = ['name', 'owner_id']; owner_id is privileged field"
1992
+ }
1993
+ ],
1994
+ "impact": "owner_id can be set to arbitrary value, enabling impersonation of other teams.",
1995
+ "severity": "critical",
1996
+ "confidence": "high",
1997
+ "cwe": [
1998
+ "CWE-915"
1999
+ ],
2000
+ "recommendation": "Remove owner_id from $fillable or guard it; enforce ownership server‑side.",
2001
+ "status": "confirmed",
2002
+ "verification": {
2003
+ "verdict": "confirmed",
2004
+ "reason_code": "traced",
2005
+ "checked": [
2006
+ "php-laravel/app/Http/Controllers/AccountController.php line 23",
2007
+ "php-laravel/app/Models/Team.php line 8"
2008
+ ],
2009
+ "control_found": null,
2010
+ "notes": "Team::create($request->all()) allows owner_id to be set via mass assignment; the Team model lists owner_id in $fillable, providing no protection.",
2011
+ "suggested_subtype": null,
2012
+ "suggested_cwe": null
2013
+ },
2014
+ "fingerprint": "kfp1:8b971db7731e918c2e512a87c2f1fa5db1332111bbe36388f36cd8dbc836da8a",
2015
+ "baseline_state": "new"
2016
+ },
2017
+ {
2018
+ "finding_id": "MASS-M-0012-1",
2019
+ "category": "mass_assignment",
2020
+ "subtype": "privileged_field_writable",
2021
+ "title": "User can set role via profile update",
2022
+ "candidate_id": "M-0012",
2023
+ "location": {
2024
+ "file": "php-plain/profile.php",
2025
+ "start_line": 10
2026
+ },
2027
+ "evidence": [
2028
+ {
2029
+ "file": "php-plain/profile.php",
2030
+ "line": 10,
2031
+ "why": "privileged field 'role' writable"
2032
+ }
2033
+ ],
2034
+ "impact": "role",
2035
+ "severity": "critical",
2036
+ "confidence": "high",
2037
+ "cwe": [
2038
+ "CWE-915"
2039
+ ],
2040
+ "recommendation": "Remove 'role' from mass assignment and enforce role changes only by admins",
2041
+ "status": "confirmed",
2042
+ "verification": {
2043
+ "verdict": "confirmed",
2044
+ "reason_code": "traced",
2045
+ "checked": [
2046
+ "php-plain/profile.php line 10"
2047
+ ],
2048
+ "control_found": null,
2049
+ "notes": "The privileged field 'role' is explicitly set from $_POST, allowing an authenticated user to elevate their role.",
2050
+ "suggested_subtype": null,
2051
+ "suggested_cwe": null
2052
+ },
2053
+ "fingerprint": "kfp1:3cf973b9c61ff5e9e4970142d7ba13438311c9725a52aafd7f136bf224549cdd",
2054
+ "baseline_state": "new"
2055
+ },
2056
+ {
2057
+ "finding_id": "BOLA-R-0010",
2058
+ "category": "bola",
2059
+ "subtype": "missing_ownership_check",
2060
+ "title": "BOLA in POST /Customer/Edit: missing owner verification",
2061
+ "route_id": "R-0010",
2062
+ "endpoint": {
2063
+ "method": "POST",
2064
+ "path": "/Customer/Edit"
2065
+ },
2066
+ "location": {
2067
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2068
+ "start_line": 12
2069
+ },
2070
+ "evidence": [
2071
+ {
2072
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2073
+ "start_line": 12,
2074
+ "code": "public ActionResult Edit(Customer customer)",
2075
+ "why": "Accepts a Customer object with an Id from the request and updates it without checking ownership",
2076
+ "line": 12
2077
+ },
2078
+ {
2079
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2080
+ "start_line": 14,
2081
+ "code": "db.Entry(customer).State = System.Data.Entity.EntityState.Modified;\n db.SaveChanges();",
2082
+ "why": "Saves the customer record, allowing any authenticated user to modify any customer",
2083
+ "line": 14
2084
+ }
2085
+ ],
2086
+ "severity": "high",
2087
+ "confidence": "medium",
2088
+ "cwe": [
2089
+ "CWE-284",
2090
+ "CWE-639"
2091
+ ],
2092
+ "recommendation": "Verify that the customer Id belongs to the current user before saving",
2093
+ "status": "likely",
2094
+ "verification": {
2095
+ "verdict": "likely",
2096
+ "reason_code": "code_not_visible",
2097
+ "checked": [
2098
+ "method signature and body lines 11-16 in dotnet-legacy/Web/Controllers/CustomerController.cs"
2099
+ ],
2100
+ "control_found": null,
2101
+ "notes": "The Edit action accepts a Customer object and marks it modified without verifying that the customer belongs to the current user. The only visible guard is the [Authorize] attribute on the controller, which only ensures authentication, not ownership.",
2102
+ "suggested_subtype": null,
2103
+ "suggested_cwe": null
2104
+ },
2105
+ "fingerprint": "kfp1:2ff50be6396a3261e2543aa3b955d99113acdc748a014446cb0b489407bafe90",
2106
+ "baseline_state": "new"
2107
+ },
2108
+ {
2109
+ "finding_id": "BOLA-R-0011",
2110
+ "category": "bola",
2111
+ "subtype": "missing_ownership_check",
2112
+ "title": "BOLA in POST /Customer/EditName: missing owner verification",
2113
+ "route_id": "R-0011",
2114
+ "endpoint": {
2115
+ "method": "POST",
2116
+ "path": "/Customer/EditName"
2117
+ },
2118
+ "location": {
2119
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2120
+ "start_line": 20
2121
+ },
2122
+ "evidence": [
2123
+ {
2124
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2125
+ "start_line": 20,
2126
+ "code": "public ActionResult EditName([Bind(Include = \"Id,Name\")] Customer customer)",
2127
+ "why": "Accepts a Customer object with Id from the request and updates it without ownership check",
2128
+ "line": 20
2129
+ },
2130
+ {
2131
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2132
+ "start_line": 22,
2133
+ "code": "db.SaveChanges();",
2134
+ "why": "Saves the customer record, allowing any authenticated user to modify any customer",
2135
+ "line": 22
2136
+ }
2137
+ ],
2138
+ "severity": "high",
2139
+ "confidence": "medium",
2140
+ "cwe": [
2141
+ "CWE-284",
2142
+ "CWE-639"
2143
+ ],
2144
+ "recommendation": "Ensure the customer Id matches the current user before saving",
2145
+ "status": "likely",
2146
+ "verification": {
2147
+ "verdict": "likely",
2148
+ "reason_code": "code_not_visible",
2149
+ "checked": [
2150
+ "method signature and body lines 20-23 in dotnet-legacy/Web/Controllers/CustomerController.cs"
2151
+ ],
2152
+ "control_found": null,
2153
+ "notes": "EditName accepts a Customer with Id and Name and calls SaveChanges without any ownership validation. No ownership check is present in the snippet.",
2154
+ "suggested_subtype": null,
2155
+ "suggested_cwe": null
2156
+ },
2157
+ "fingerprint": "kfp1:f8f8312969722c53dc4fbfcebddf09bfc6ea44e77f308058c6f5a0a79916c6a1",
2158
+ "baseline_state": "new"
2159
+ },
2160
+ {
2161
+ "finding_id": "BOLA-R-0012",
2162
+ "category": "bola",
2163
+ "subtype": "missing_ownership_check",
2164
+ "title": "BOLA in POST /Customer/Refresh: missing owner verification",
2165
+ "route_id": "R-0012",
2166
+ "endpoint": {
2167
+ "method": "POST",
2168
+ "path": "/Customer/Refresh"
2169
+ },
2170
+ "location": {
2171
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2172
+ "start_line": 27
2173
+ },
2174
+ "evidence": [
2175
+ {
2176
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2177
+ "start_line": 27,
2178
+ "code": "public ActionResult Refresh(int id)",
2179
+ "why": "Accepts a customer Id from the request and loads the record without ownership check",
2180
+ "line": 27
2181
+ },
2182
+ {
2183
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2184
+ "start_line": 29,
2185
+ "code": "var customer = db.Customers.Find(id);\n TryUpdateModel(customer);\n db.SaveChanges();",
2186
+ "why": "Updates and saves the customer record, allowing any authenticated user to modify any customer",
2187
+ "line": 29
2188
+ }
2189
+ ],
2190
+ "severity": "high",
2191
+ "confidence": "medium",
2192
+ "cwe": [
2193
+ "CWE-284",
2194
+ "CWE-639"
2195
+ ],
2196
+ "recommendation": "Validate that the customer Id belongs to the current user before updating",
2197
+ "status": "likely",
2198
+ "verification": {
2199
+ "verdict": "likely",
2200
+ "reason_code": "code_not_visible",
2201
+ "checked": [
2202
+ "method signature and body lines 27-32 in dotnet-legacy/Web/Controllers/CustomerController.cs"
2203
+ ],
2204
+ "control_found": null,
2205
+ "notes": "Refresh loads a Customer by id and updates it via TryUpdateModel without verifying ownership. The snippet shows no ownership guard.",
2206
+ "suggested_subtype": null,
2207
+ "suggested_cwe": null
2208
+ },
2209
+ "fingerprint": "kfp1:642d9e00b1146b80dc8724396082e7bf839dc74fdcc9224c6452557ed7b19ab7",
2210
+ "baseline_state": "new"
2211
+ },
2212
+ {
2213
+ "finding_id": "BOLA-R-0016",
2214
+ "category": "bola",
2215
+ "subtype": "missing_ownership_check",
2216
+ "title": "BOLA in ANY /dotnet-legacy/Web/Handlers/GetFile.ashx: missing owner verification",
2217
+ "route_id": "R-0016",
2218
+ "endpoint": {
2219
+ "method": "ANY",
2220
+ "path": "/dotnet-legacy/Web/Handlers/GetFile.ashx"
2221
+ },
2222
+ "location": {
2223
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2224
+ "start_line": 1
2225
+ },
2226
+ "evidence": [
2227
+ {
2228
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2229
+ "start_line": 8,
2230
+ "code": "string fileId = context.Request.QueryString[\"file_id\"];",
2231
+ "why": "Takes file identifier directly from the request without ownership validation",
2232
+ "line": 8
2233
+ },
2234
+ {
2235
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2236
+ "start_line": 9,
2237
+ "code": "string path = DocStore.PathFor(fileId);",
2238
+ "why": "Resolves file path based solely on the supplied fileId",
2239
+ "line": 9
2240
+ },
2241
+ {
2242
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2243
+ "start_line": 10,
2244
+ "code": "context.Response.TransmitFile(path);",
2245
+ "why": "Serves the file to the requester, enabling download of any file by ID",
2246
+ "line": 10
2247
+ }
2248
+ ],
2249
+ "severity": "high",
2250
+ "confidence": "medium",
2251
+ "cwe": [
2252
+ "CWE-284",
2253
+ "CWE-639"
2254
+ ],
2255
+ "recommendation": "Restrict file access to the current user or enforce ownership checks",
2256
+ "status": "likely",
2257
+ "verification": {
2258
+ "verdict": "likely",
2259
+ "reason_code": "code_not_visible",
2260
+ "checked": [
2261
+ "code lines 8-10 in dotnet-legacy/Web/Handlers/GetFile.ashx"
2262
+ ],
2263
+ "control_found": null,
2264
+ "notes": "GetFile retrieves a file path based solely on the file_id query parameter and transmits it. No ownership or role check is visible in the snippet.",
2265
+ "suggested_subtype": null,
2266
+ "suggested_cwe": null
2267
+ },
2268
+ "fingerprint": "kfp1:3a00a7f2b857b16149c5a6df74d705a9e4bdf0830a11061873af55f4ab95a865",
2269
+ "baseline_state": "new"
2270
+ },
2271
+ {
2272
+ "finding_id": "BOLA-R-0020",
2273
+ "category": "bola",
2274
+ "subtype": "missing_ownership_check",
2275
+ "title": "BOLA in POST /account/save: missing owner verification",
2276
+ "route_id": "R-0020",
2277
+ "endpoint": {
2278
+ "method": "POST",
2279
+ "path": "/account/save"
2280
+ },
2281
+ "location": {
2282
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2283
+ "start_line": 13
2284
+ },
2285
+ "evidence": [
2286
+ {
2287
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2288
+ "start_line": 13,
2289
+ "code": " @RequestMapping(value = \"/save\", method = RequestMethod.POST)\n public String save(@ModelAttribute Account account) {\n accountDao.save(account);\n return \"redirect:/account\";\n }",
2290
+ "why": "The account ID comes from the request body and is persisted without checking that the current session user owns the account.",
2291
+ "line": 13
2292
+ }
2293
+ ],
2294
+ "severity": "high",
2295
+ "confidence": "medium",
2296
+ "cwe": [
2297
+ "CWE-284",
2298
+ "CWE-639"
2299
+ ],
2300
+ "recommendation": "Bind the save operation to the current session user.",
2301
+ "status": "likely",
2302
+ "verification": {
2303
+ "verdict": "likely",
2304
+ "reason_code": "code_not_visible",
2305
+ "checked": [
2306
+ "java-legacy/src/com/acme/web/AccountController.java lines 13-16"
2307
+ ],
2308
+ "control_found": null,
2309
+ "notes": "The method simply persists the Account object received from the request body without any ownership or role verification. No cast or allow‑list is present in the visible code.",
2310
+ "suggested_subtype": null,
2311
+ "suggested_cwe": null
2312
+ },
2313
+ "fingerprint": "kfp1:da6ba81e707bc9691e776f217be38c128a81b5a660f94e3a2778b25f874e1957",
2314
+ "baseline_state": "new"
2315
+ },
2316
+ {
2317
+ "finding_id": "BOLA-R-0021",
2318
+ "category": "bola",
2319
+ "subtype": "missing_ownership_check",
2320
+ "title": "BOLA in POST /account/rename: missing owner verification",
2321
+ "route_id": "R-0021",
2322
+ "endpoint": {
2323
+ "method": "POST",
2324
+ "path": "/account/rename"
2325
+ },
2326
+ "location": {
2327
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2328
+ "start_line": 19
2329
+ },
2330
+ "evidence": [
2331
+ {
2332
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2333
+ "start_line": 19,
2334
+ "code": " @RequestMapping(value = \"/rename\", method = RequestMethod.POST)\n public String rename(@ModelAttribute RenameForm form) {\n accountDao.rename(form.getId(), form.getName());\n return \"redirect:/account\";\n }",
2335
+ "why": "The account ID is taken from the form and the rename is performed without verifying ownership.",
2336
+ "line": 19
2337
+ }
2338
+ ],
2339
+ "severity": "high",
2340
+ "confidence": "medium",
2341
+ "cwe": [
2342
+ "CWE-284",
2343
+ "CWE-639"
2344
+ ],
2345
+ "recommendation": "Verify that the current user owns the account before renaming.",
2346
+ "status": "likely",
2347
+ "verification": {
2348
+ "verdict": "likely",
2349
+ "reason_code": "code_not_visible",
2350
+ "checked": [
2351
+ "java-legacy/src/com/acme/web/AccountController.java lines 19-22"
2352
+ ],
2353
+ "control_found": null,
2354
+ "notes": "The rename operation uses the ID from the form directly and calls the DAO without checking that the current user owns the account. No role guard or ownership check is visible.",
2355
+ "suggested_subtype": null,
2356
+ "suggested_cwe": null
2357
+ },
2358
+ "fingerprint": "kfp1:436b1118118c6a11d9a08401ceb7e845c737fc510a1527be37b102622c97cf52",
2359
+ "baseline_state": "new"
2360
+ },
2361
+ {
2362
+ "finding_id": "BOLA-R-0022",
2363
+ "category": "bola",
2364
+ "subtype": "missing_ownership_check",
2365
+ "title": "BOLA in PUT /account/api: missing owner verification",
2366
+ "route_id": "R-0022",
2367
+ "endpoint": {
2368
+ "method": "PUT",
2369
+ "path": "/account/api"
2370
+ },
2371
+ "location": {
2372
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2373
+ "start_line": 25
2374
+ },
2375
+ "evidence": [
2376
+ {
2377
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2378
+ "start_line": 25,
2379
+ "code": " @RequestMapping(value = \"/api\", method = RequestMethod.PUT)\n @ResponseBody\n public Account update(@RequestBody Account account) {\n return accountDao.save(account);\n }",
2380
+ "why": "The account ID is supplied in the JSON body and is saved without checking that the current session user owns the account.",
2381
+ "line": 25
2382
+ }
2383
+ ],
2384
+ "severity": "high",
2385
+ "confidence": "medium",
2386
+ "cwe": [
2387
+ "CWE-284",
2388
+ "CWE-639"
2389
+ ],
2390
+ "recommendation": "Bind the update to the current session user.",
2391
+ "status": "likely",
2392
+ "verification": {
2393
+ "verdict": "likely",
2394
+ "reason_code": "code_not_visible",
2395
+ "checked": [
2396
+ "java-legacy/src/com/acme/web/AccountController.java lines 25-28"
2397
+ ],
2398
+ "control_found": null,
2399
+ "notes": "The update endpoint accepts an Account object and persists it without verifying the session user. No cast, allow‑list, or ownership check is present in the shown code.",
2400
+ "suggested_subtype": null,
2401
+ "suggested_cwe": null
2402
+ },
2403
+ "fingerprint": "kfp1:da398947b87b88d6858e1e8fbf762cd899ea7d68e9159b815743d29deef2a666",
2404
+ "baseline_state": "new"
2405
+ },
2406
+ {
2407
+ "finding_id": "BOLA-R-0023",
2408
+ "category": "bola",
2409
+ "subtype": "missing_ownership_check",
2410
+ "title": "BOLA in POST /admin/users/{id}/delete: missing ownership and role check",
2411
+ "route_id": "R-0023",
2412
+ "endpoint": {
2413
+ "method": "POST",
2414
+ "path": "/admin/users/{id}/delete"
2415
+ },
2416
+ "location": {
2417
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
2418
+ "start_line": 13
2419
+ },
2420
+ "evidence": [
2421
+ {
2422
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
2423
+ "start_line": 13,
2424
+ "code": " @RequestMapping(value = \"/{id}/delete\", method = RequestMethod.POST)\n public String delete(@PathVariable(\"id\") Long id) {\n userDao.deleteById(id);\n return \"redirect:/admin/users\";\n }",
2425
+ "why": "Any authenticated user can delete any user because the method lacks an admin role check and does not verify ownership.",
2426
+ "line": 13
2427
+ }
2428
+ ],
2429
+ "severity": "high",
2430
+ "confidence": "medium",
2431
+ "cwe": [
2432
+ "CWE-284",
2433
+ "CWE-639"
2434
+ ],
2435
+ "recommendation": "Add an admin role guard and verify ownership if needed.",
2436
+ "status": "likely",
2437
+ "verification": {
2438
+ "verdict": "likely",
2439
+ "reason_code": "code_not_visible",
2440
+ "checked": [
2441
+ "java-legacy/src/com/acme/web/AdminUserController.java lines 13-16"
2442
+ ],
2443
+ "control_found": null,
2444
+ "notes": "The delete method performs a direct DAO call with the supplied ID and lacks any @PreAuthorize or other role guard. No ownership check is visible.",
2445
+ "suggested_subtype": null,
2446
+ "suggested_cwe": null
2447
+ },
2448
+ "fingerprint": "kfp1:a4f576c3ecb678956e186f0ee7498af55e42a7b8632ed464b8eada05c59007b4",
2449
+ "baseline_state": "new"
2450
+ },
2451
+ {
2452
+ "finding_id": "BOLA-R-0025",
2453
+ "category": "bola",
2454
+ "subtype": "missing_ownership_check",
2455
+ "title": "BOLA in GET /invoices/{id}: missing owner verification",
2456
+ "route_id": "R-0025",
2457
+ "endpoint": {
2458
+ "method": "GET",
2459
+ "path": "/invoices/{id}"
2460
+ },
2461
+ "location": {
2462
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
2463
+ "start_line": 15
2464
+ },
2465
+ "evidence": [
2466
+ {
2467
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
2468
+ "start_line": 15,
2469
+ "code": " @RequestMapping(value = \"/{id}\", method = RequestMethod.GET)\n public String show(@PathVariable(\"id\") Long id, Model model) {\n model.addAttribute(\"invoice\", invoiceDao.findById(id));\n return \"invoice/show\";\n }",
2470
+ "why": "The invoice ID is taken from the path and the invoice is returned without checking that the current user owns it.",
2471
+ "line": 15
2472
+ }
2473
+ ],
2474
+ "severity": "high",
2475
+ "confidence": "medium",
2476
+ "cwe": [
2477
+ "CWE-284",
2478
+ "CWE-639"
2479
+ ],
2480
+ "recommendation": "Add an ownership check to the query.",
2481
+ "status": "likely",
2482
+ "verification": {
2483
+ "verdict": "likely",
2484
+ "reason_code": "code_not_visible",
2485
+ "checked": [
2486
+ "java-legacy/src/com/acme/web/InvoiceController.java lines 15-18"
2487
+ ],
2488
+ "control_found": null,
2489
+ "notes": "The show method retrieves an invoice by ID without verifying that the current user owns it. No ownership guard or role check is present in the visible code.",
2490
+ "suggested_subtype": null,
2491
+ "suggested_cwe": null
2492
+ },
2493
+ "fingerprint": "kfp1:4213dd0e852a3473b5f77a0dd8c52fbbb9776802a6d4475b6edad70be330df73",
2494
+ "baseline_state": "new"
2495
+ },
2496
+ {
2497
+ "finding_id": "BOLA-R-0027",
2498
+ "category": "bola",
2499
+ "subtype": "missing_ownership_check",
2500
+ "title": "BOLA in GET /invoices/search: missing owner verification",
2501
+ "route_id": "R-0027",
2502
+ "endpoint": {
2503
+ "method": "GET",
2504
+ "path": "/invoices/search"
2505
+ },
2506
+ "location": {
2507
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
2508
+ "start_line": 27
2509
+ },
2510
+ "evidence": [
2511
+ {
2512
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
2513
+ "start_line": 27,
2514
+ "code": " @RequestMapping(value = \"/search\", method = RequestMethod.GET)\n public String search(@RequestParam(\"customer\") String customer, Model model) {\n model.addAttribute(\"list\", session.createQuery(\"from Invoice i where i.customer = '\" + customer + \"'\").list());\n return \"invoice/list\";\n }",
2515
+ "why": "The query returns all invoices for a customer without scoping to the current user.",
2516
+ "line": 27
2517
+ }
2518
+ ],
2519
+ "severity": "high",
2520
+ "confidence": "medium",
2521
+ "cwe": [
2522
+ "CWE-284",
2523
+ "CWE-639"
2524
+ ],
2525
+ "recommendation": "Scope the search to the current user.",
2526
+ "status": "likely",
2527
+ "verification": {
2528
+ "verdict": "likely",
2529
+ "reason_code": "code_not_visible",
2530
+ "checked": [
2531
+ "java-legacy/src/com/acme/web/InvoiceController.java:27-30"
2532
+ ],
2533
+ "control_found": null,
2534
+ "notes": "No ownership or role check, no cast or allow‑list is visible in the search method. The query is executed directly with the customer parameter.",
2535
+ "suggested_subtype": null,
2536
+ "suggested_cwe": null
2537
+ },
2538
+ "fingerprint": "kfp1:b576c39e6469454d66f748f828a753ff3f9f250c5538a9c88b4a57760334d368",
2539
+ "baseline_state": "new"
2540
+ },
2541
+ {
2542
+ "finding_id": "BOLA-R-0028",
2543
+ "category": "bola",
2544
+ "subtype": "missing_ownership_check",
2545
+ "title": "BOLA in GET /order/view: missing owner verification",
2546
+ "route_id": "R-0028",
2547
+ "endpoint": {
2548
+ "method": "GET",
2549
+ "path": "/order/view"
2550
+ },
2551
+ "location": {
2552
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
2553
+ "start_line": 11
2554
+ },
2555
+ "evidence": [
2556
+ {
2557
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
2558
+ "start_line": 11,
2559
+ "code": " String id = request.getParameter(\"orderId\");\n try {\n Connection conn = DataSourceHolder.get().getConnection();\n Statement stmt = conn.createStatement();\n ResultSet rs = stmt.executeQuery(\"SELECT * FROM orders WHERE id = '\" + id + \"'\");",
2560
+ "why": "The order ID comes from the query string and the order is fetched without checking that the current session user owns it.",
2561
+ "line": 11
2562
+ }
2563
+ ],
2564
+ "severity": "high",
2565
+ "confidence": "medium",
2566
+ "cwe": [
2567
+ "CWE-284",
2568
+ "CWE-639"
2569
+ ],
2570
+ "recommendation": "Add an ownership check to the SQL query.",
2571
+ "status": "likely",
2572
+ "verification": {
2573
+ "verdict": "likely",
2574
+ "reason_code": "code_not_visible",
2575
+ "checked": [
2576
+ "java-legacy/src/com/acme/web/OrderServlet.java:11-15"
2577
+ ],
2578
+ "control_found": null,
2579
+ "notes": "The order ID is concatenated into the SQL string with no ownership verification, cast, or allow‑list. No role check is present.",
2580
+ "suggested_subtype": null,
2581
+ "suggested_cwe": null
2582
+ },
2583
+ "fingerprint": "kfp1:a589b3cdcfea2b9cc2c5190ffe4a0c2989e46fdd4d15b59781c1b74e3c554524",
2584
+ "baseline_state": "new"
2585
+ },
2586
+ {
2587
+ "finding_id": "BOLA-R-0029",
2588
+ "category": "bola",
2589
+ "subtype": "missing_ownership_check",
2590
+ "title": "BOLA in POST /ProfileServlet: missing owner verification",
2591
+ "route_id": "R-0029",
2592
+ "endpoint": {
2593
+ "method": "POST",
2594
+ "path": "/ProfileServlet"
2595
+ },
2596
+ "location": {
2597
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
2598
+ "start_line": 9
2599
+ },
2600
+ "evidence": [
2601
+ {
2602
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
2603
+ "start_line": 12,
2604
+ "code": "BeanUtils.populate(account, request.getParameterMap());",
2605
+ "why": "Populates an Account object with all request parameters, including an Id, without verifying that the Id belongs to the current session user",
2606
+ "line": 12
2607
+ }
2608
+ ],
2609
+ "severity": "high",
2610
+ "confidence": "medium",
2611
+ "cwe": [
2612
+ "CWE-284",
2613
+ "CWE-639"
2614
+ ],
2615
+ "recommendation": "Ensure that the account Id matches the current user before persisting changes",
2616
+ "status": "likely",
2617
+ "verification": {
2618
+ "verdict": "likely",
2619
+ "reason_code": "code_not_visible",
2620
+ "checked": [
2621
+ "java-legacy/src/com/acme/web/ProfileServlet.java lines 8-16"
2622
+ ],
2623
+ "control_found": null,
2624
+ "notes": "The servlet populates an Account object directly from request parameters without any visible ownership check, cast, or allow‑list. No compensating control is present in the shown code.",
2625
+ "suggested_subtype": null,
2626
+ "suggested_cwe": null
2627
+ },
2628
+ "fingerprint": "kfp1:8692bf234ec8d01df90fe0a2a82a5ac8e2d2487dec8fe223ff2020499b4c7844",
2629
+ "baseline_state": "new"
2630
+ },
2631
+ {
2632
+ "finding_id": "BOLA-R-0030",
2633
+ "category": "bola",
2634
+ "subtype": "missing_ownership_check",
2635
+ "title": "BOLA in POST /admin/users/:id/delete: missing ownership and role check",
2636
+ "route_id": "R-0030",
2637
+ "endpoint": {
2638
+ "method": "POST",
2639
+ "path": "/admin/users/:id/delete"
2640
+ },
2641
+ "location": {
2642
+ "file": "node-express/routes/admin.js",
2643
+ "start_line": 8
2644
+ },
2645
+ "evidence": [
2646
+ {
2647
+ "file": "node-express/routes/admin.js",
2648
+ "start_line": 8,
2649
+ "code": "router.post('/admin/users/:id/delete', ensureLoggedIn, function (req, res) {\n User.deleteOne({ _id: req.params.id }, function () { res.sendStatus(204); });\n});",
2650
+ "why": "Any authenticated user can delete any user because the route lacks an admin guard and does not verify ownership.",
2651
+ "line": 8
2652
+ }
2653
+ ],
2654
+ "severity": "high",
2655
+ "confidence": "medium",
2656
+ "cwe": [
2657
+ "CWE-284",
2658
+ "CWE-639"
2659
+ ],
2660
+ "recommendation": "Add an admin guard and verify ownership.",
2661
+ "status": "likely",
2662
+ "verification": {
2663
+ "verdict": "likely",
2664
+ "reason_code": "code_not_visible",
2665
+ "checked": [
2666
+ "node-express/routes/admin.js:8-9"
2667
+ ],
2668
+ "control_found": null,
2669
+ "notes": "The route uses only ensureLoggedIn, which does not enforce an admin role. No ownership or role guard is visible.",
2670
+ "suggested_subtype": null,
2671
+ "suggested_cwe": null
2672
+ },
2673
+ "fingerprint": "kfp1:3dd2e6ecf2f0730978e0c2a05f31e2af3920a107cd2af0b5ea81c9ded7f6a100",
2674
+ "baseline_state": "new"
2675
+ },
2676
+ {
2677
+ "finding_id": "BOLA-R-0033",
2678
+ "category": "bola",
2679
+ "subtype": "missing_ownership_check",
2680
+ "title": "BOLA in GET /orders/:id: missing owner verification",
2681
+ "route_id": "R-0033",
2682
+ "endpoint": {
2683
+ "method": "GET",
2684
+ "path": "/orders/:id"
2685
+ },
2686
+ "location": {
2687
+ "file": "node-express/routes/orders.js",
2688
+ "start_line": 8,
2689
+ "end_line": 9
2690
+ },
2691
+ "evidence": [
2692
+ {
2693
+ "file": "node-express/routes/orders.js",
2694
+ "start_line": 8,
2695
+ "code": "Order.findById(req.params.id, function (err, order) { res.json(order); });",
2696
+ "why": "Order is fetched solely by id from the request; no check that the order belongs to the logged‑in user.",
2697
+ "line": 8
2698
+ }
2699
+ ],
2700
+ "severity": "high",
2701
+ "confidence": "medium",
2702
+ "cwe": [
2703
+ "CWE-284",
2704
+ "CWE-639"
2705
+ ],
2706
+ "recommendation": "Bind the query to the current session user.",
2707
+ "status": "likely",
2708
+ "verification": {
2709
+ "verdict": "likely",
2710
+ "reason_code": "code_not_visible",
2711
+ "checked": [
2712
+ "node-express/routes/orders.js:8-9"
2713
+ ],
2714
+ "control_found": null,
2715
+ "notes": "Order is fetched solely by ID with no check that it belongs to the authenticated user. No role or ownership guard is shown.",
2716
+ "suggested_subtype": null,
2717
+ "suggested_cwe": null
2718
+ },
2719
+ "fingerprint": "kfp1:3efb41972fd6c818041a922563d6a1e65eb9cdcd63b764b52ff4ffce238a6c8d",
2720
+ "baseline_state": "new"
2721
+ },
2722
+ {
2723
+ "finding_id": "BOLA-R-0035",
2724
+ "category": "bola",
2725
+ "subtype": "missing_ownership_check",
2726
+ "title": "BOLA in GET /reports: missing owner verification",
2727
+ "route_id": "R-0035",
2728
+ "endpoint": {
2729
+ "method": "GET",
2730
+ "path": "/reports"
2731
+ },
2732
+ "location": {
2733
+ "file": "node-express/routes/orders.js",
2734
+ "start_line": 16,
2735
+ "end_line": 18
2736
+ },
2737
+ "evidence": [
2738
+ {
2739
+ "file": "node-express/routes/orders.js",
2740
+ "start_line": 16,
2741
+ "code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
2742
+ "why": "The query returns all reports for the requested month without scoping to the logged‑in user.",
2743
+ "line": 16
2744
+ }
2745
+ ],
2746
+ "severity": "high",
2747
+ "confidence": "medium",
2748
+ "cwe": [
2749
+ "CWE-284",
2750
+ "CWE-639"
2751
+ ],
2752
+ "recommendation": "Scope the query to the current user or tenant.",
2753
+ "status": "likely",
2754
+ "verification": {
2755
+ "verdict": "likely",
2756
+ "reason_code": "code_not_visible",
2757
+ "checked": [
2758
+ "node-express/routes/orders.js:16-18"
2759
+ ],
2760
+ "control_found": null,
2761
+ "notes": "The SQL query concatenates the month parameter without scoping to the logged‑in user or tenant. No ownership or role check is visible.",
2762
+ "suggested_subtype": null,
2763
+ "suggested_cwe": null
2764
+ },
2765
+ "fingerprint": "kfp1:9be9be3e74c3014c61d218020cd85c64557f3e1c49d462ce43942dd410bfa0a6",
2766
+ "baseline_state": "new"
2767
+ },
2768
+ {
2769
+ "finding_id": "BOLA-R-0037",
2770
+ "category": "bola",
2771
+ "subtype": "missing_ownership_check",
2772
+ "title": "BOLA in GET /search: missing owner verification",
2773
+ "route_id": "R-0037",
2774
+ "endpoint": {
2775
+ "method": "GET",
2776
+ "path": "/search"
2777
+ },
2778
+ "location": {
2779
+ "file": "node-express/routes/orders.js",
2780
+ "start_line": 24,
2781
+ "end_line": 26
2782
+ },
2783
+ "evidence": [
2784
+ {
2785
+ "file": "node-express/routes/orders.js",
2786
+ "start_line": 24,
2787
+ "code": "db.collection('items').find({ $where: \"this.name == '\" + req.query.n + \"'\" }).toArray(function (e, r) { res.json(r); });",
2788
+ "why": "The query returns all items whose name matches the request parameter; no user or tenant scoping.",
2789
+ "line": 24
2790
+ }
2791
+ ],
2792
+ "severity": "high",
2793
+ "confidence": "high",
2794
+ "cwe": [
2795
+ "CWE-284",
2796
+ "CWE-639"
2797
+ ],
2798
+ "recommendation": "Add a user/tenant filter to the query.",
2799
+ "status": "confirmed",
2800
+ "verification": {
2801
+ "verdict": "confirmed",
2802
+ "reason_code": "traced",
2803
+ "checked": [
2804
+ "node-express/routes/orders.js lines 24-26"
2805
+ ],
2806
+ "control_found": null,
2807
+ "notes": "The query uses the request parameter directly without any owner or tenant filter. No cast or allow‑list is present.",
2808
+ "suggested_subtype": null,
2809
+ "suggested_cwe": null
2810
+ },
2811
+ "fingerprint": "kfp1:89a1069104f2e244e26853b7bc117cd96e8315252b38628037302493f8147b9a",
2812
+ "baseline_state": "new"
2813
+ },
2814
+ {
2815
+ "finding_id": "BOLA-R-0038",
2816
+ "category": "bola",
2817
+ "subtype": "missing_ownership_check",
2818
+ "title": "BOLA in GET /stats: missing owner verification",
2819
+ "route_id": "R-0038",
2820
+ "endpoint": {
2821
+ "method": "GET",
2822
+ "path": "/stats"
2823
+ },
2824
+ "location": {
2825
+ "file": "node-express/routes/orders.js",
2826
+ "start_line": 28,
2827
+ "end_line": 30
2828
+ },
2829
+ "evidence": [
2830
+ {
2831
+ "file": "node-express/routes/orders.js",
2832
+ "start_line": 28,
2833
+ "code": "db.query(`SELECT COUNT(*) AS n FROM orders WHERE shop_id = ${req.query.shop}`, function (e, r) { res.json(r); });",
2834
+ "why": "The query counts orders for any shop_id supplied in the request; no check that the shop belongs to the logged‑in user.",
2835
+ "line": 28
2836
+ }
2837
+ ],
2838
+ "severity": "high",
2839
+ "confidence": "high",
2840
+ "cwe": [
2841
+ "CWE-284",
2842
+ "CWE-639"
2843
+ ],
2844
+ "recommendation": "Scope the query to the user's shop or tenant.",
2845
+ "status": "confirmed",
2846
+ "verification": {
2847
+ "verdict": "confirmed",
2848
+ "reason_code": "traced",
2849
+ "checked": [
2850
+ "node-express/routes/orders.js lines 28-30"
2851
+ ],
2852
+ "control_found": null,
2853
+ "notes": "The SQL statement interpolates the shop_id from the request without verifying that the shop belongs to the logged‑in user.",
2854
+ "suggested_subtype": null,
2855
+ "suggested_cwe": null
2856
+ },
2857
+ "fingerprint": "kfp1:e0d4076276caf1e9557699dcd9494f106ac443c30bfdb55c71fb7aaedda29b14",
2858
+ "baseline_state": "new"
2859
+ },
2860
+ {
2861
+ "finding_id": "BOLA-R-0043",
2862
+ "category": "bola",
2863
+ "subtype": "missing_ownership_check",
2864
+ "title": "BOLA in ANY /perl-cgi/cgi-bin/order.cgi: missing owner verification",
2865
+ "route_id": "R-0043",
2866
+ "endpoint": {
2867
+ "method": "ANY",
2868
+ "path": "/perl-cgi/cgi-bin/order.cgi"
2869
+ },
2870
+ "location": {
2871
+ "file": "perl-cgi/cgi-bin/order.cgi",
2872
+ "start_line": 11,
2873
+ "end_line": 14
2874
+ },
2875
+ "evidence": [
2876
+ {
2877
+ "file": "perl-cgi/cgi-bin/order.cgi",
2878
+ "start_line": 11,
2879
+ "code": "$id = $q->param('id');\nmy $sth = $dbh->prepare(\"SELECT * FROM orders WHERE id = $id\");\n$sth->execute();",
2880
+ "why": "The order is fetched solely by id from the request; no check that the order belongs to the logged‑in user.",
2881
+ "line": 11
2882
+ }
2883
+ ],
2884
+ "severity": "high",
2885
+ "confidence": "high",
2886
+ "cwe": [
2887
+ "CWE-284",
2888
+ "CWE-639"
2889
+ ],
2890
+ "recommendation": "Use a parameterized query that also filters by user_id.",
2891
+ "status": "confirmed",
2892
+ "verification": {
2893
+ "verdict": "confirmed",
2894
+ "reason_code": "traced",
2895
+ "checked": [
2896
+ "perl-cgi/cgi-bin/order.cgi lines 11-14"
2897
+ ],
2898
+ "control_found": null,
2899
+ "notes": "The first query selects an order solely by id from the request; no parameter binding, cast, or ownership check is applied.",
2900
+ "suggested_subtype": null,
2901
+ "suggested_cwe": null
2902
+ },
2903
+ "fingerprint": "kfp1:446abc0a5ca9d538f5d680526af5265194e8ee2f7644e6ca49c64b3b9d7b2b4e",
2904
+ "baseline_state": "new"
2905
+ },
2906
+ {
2907
+ "finding_id": "BOLA-R-0044",
2908
+ "category": "bola",
2909
+ "subtype": "missing_ownership_check",
2910
+ "title": "BOLA in ANY /invoice/view/{id}: missing owner verification",
2911
+ "route_id": "R-0044",
2912
+ "endpoint": {
2913
+ "method": "ANY",
2914
+ "path": "/invoice/view/{id}"
2915
+ },
2916
+ "location": {
2917
+ "file": "php-codeigniter/application/controllers/Invoice.php",
2918
+ "start_line": 9,
2919
+ "end_line": 11
2920
+ },
2921
+ "evidence": [
2922
+ {
2923
+ "file": "php-codeigniter/application/controllers/Invoice.php",
2924
+ "start_line": 9,
2925
+ "code": "$q = $this->db->query(\"SELECT * FROM invoices WHERE id = $id\");\n$this->load->view('invoice', array('inv' => $q->row()));",
2926
+ "why": "The invoice is fetched solely by id from the request; no check that the invoice belongs to the logged‑in user.",
2927
+ "line": 9
2928
+ }
2929
+ ],
2930
+ "severity": "high",
2931
+ "confidence": "high",
2932
+ "cwe": [
2933
+ "CWE-284",
2934
+ "CWE-639"
2935
+ ],
2936
+ "recommendation": "Scope the query to the current user.",
2937
+ "status": "confirmed",
2938
+ "verification": {
2939
+ "verdict": "confirmed",
2940
+ "reason_code": "traced",
2941
+ "checked": [
2942
+ "php-codeigniter/application/controllers/Invoice.php lines 9-11"
2943
+ ],
2944
+ "control_found": null,
2945
+ "notes": "The invoice is fetched by id from the request without any user or tenant filter. No cast or parameter binding is used.",
2946
+ "suggested_subtype": null,
2947
+ "suggested_cwe": null
2948
+ },
2949
+ "fingerprint": "kfp1:266f870d96cd69a0b4644c5c983b5c92d59a3d989f9d2514e1016f6ffd372f3b",
2950
+ "baseline_state": "new"
2951
+ },
2952
+ {
2953
+ "finding_id": "BOLA-R-0046",
2954
+ "category": "bola",
2955
+ "subtype": "missing_ownership_check",
2956
+ "title": "BOLA in ANY /invoice/update: missing owner verification",
2957
+ "route_id": "R-0046",
2958
+ "endpoint": {
2959
+ "method": "ANY",
2960
+ "path": "/invoice/update"
2961
+ },
2962
+ "location": {
2963
+ "file": "php-codeigniter/application/controllers/Invoice.php",
2964
+ "start_line": 20,
2965
+ "end_line": 24
2966
+ },
2967
+ "evidence": [
2968
+ {
2969
+ "file": "php-codeigniter/application/controllers/Invoice.php",
2970
+ "start_line": 20,
2971
+ "code": "$id = $this->input->post('invoice_id');\n$note = $this->input->post('note');\n$this->db->query(\"UPDATE invoices SET note = ? WHERE id = ?\", array($note, $id));",
2972
+ "why": "The update is performed solely by id from the request; no check that the invoice belongs to the logged‑in user.",
2973
+ "line": 20
2974
+ }
2975
+ ],
2976
+ "severity": "high",
2977
+ "confidence": "high",
2978
+ "cwe": [
2979
+ "CWE-284",
2980
+ "CWE-639"
2981
+ ],
2982
+ "recommendation": "Add a user_id filter to the UPDATE statement.",
2983
+ "status": "confirmed",
2984
+ "verification": {
2985
+ "verdict": "confirmed",
2986
+ "reason_code": "traced",
2987
+ "checked": [
2988
+ "php-codeigniter/application/controllers/Invoice.php lines 20-24"
2989
+ ],
2990
+ "control_found": null,
2991
+ "notes": "The UPDATE statement uses the id from the request without verifying ownership or applying a cast.",
2992
+ "suggested_subtype": null,
2993
+ "suggested_cwe": null
2994
+ },
2995
+ "fingerprint": "kfp1:f8000507518dfd07801d08ee0115647eaf2efdfdcfcd467cab0be7ea43514273",
2996
+ "baseline_state": "new"
2997
+ },
2998
+ {
2999
+ "finding_id": "BOLA-R-0057-1",
3000
+ "category": "bola",
3001
+ "subtype": "missing_ownership_check",
3002
+ "title": "BOLA in GET /php-plain/admin.php?action=view: missing owner verification",
3003
+ "route_id": "R-0057",
3004
+ "endpoint": {
3005
+ "method": "GET",
3006
+ "path": "/php-plain/admin.php?action=view"
3007
+ },
3008
+ "location": {
3009
+ "file": "php-plain/admin.php",
3010
+ "start_line": 6
3011
+ },
3012
+ "evidence": [
3013
+ {
3014
+ "file": "php-plain/admin.php",
3015
+ "start_line": 6,
3016
+ "code": "$uid = (int)$_GET['uid'];\n$r = mysql_query(\"SELECT * FROM users WHERE id = \" . $uid);",
3017
+ "why": "The user ID is taken directly from the request and used in a query without binding to the current session user.",
3018
+ "line": 6
3019
+ }
3020
+ ],
3021
+ "severity": "high",
3022
+ "confidence": "high",
3023
+ "cwe": [
3024
+ "CWE-284",
3025
+ "CWE-639"
3026
+ ],
3027
+ "recommendation": "Bind the query to the current session user or enforce an admin check.",
3028
+ "status": "confirmed",
3029
+ "verification": {
3030
+ "verdict": "confirmed",
3031
+ "reason_code": "traced",
3032
+ "checked": [
3033
+ "php-plain/admin.php lines 6-8"
3034
+ ],
3035
+ "control_found": null,
3036
+ "notes": "The user ID is cast to int but no ownership or admin check is performed before the SELECT query.",
3037
+ "suggested_subtype": null,
3038
+ "suggested_cwe": null
3039
+ },
3040
+ "fingerprint": "kfp1:3691f0046489f4fb4004e0df5818c6f613138b47d5675c7dc926255b0a186096",
3041
+ "baseline_state": "new"
3042
+ },
3043
+ {
3044
+ "finding_id": "BOLA-R-0058-1",
3045
+ "category": "bola",
3046
+ "subtype": "missing_ownership_check",
3047
+ "title": "BOLA in GET /php-plain/admin.php?action=view: missing owner verification",
3048
+ "route_id": "R-0058",
3049
+ "endpoint": {
3050
+ "method": "GET",
3051
+ "path": "/php-plain/admin.php?action=view"
3052
+ },
3053
+ "location": {
3054
+ "file": "php-plain/admin.php",
3055
+ "start_line": 10
3056
+ },
3057
+ "evidence": [
3058
+ {
3059
+ "file": "php-plain/admin.php",
3060
+ "start_line": 6,
3061
+ "code": "$uid = (int)$_GET['uid'];\n$r = mysql_query(\"SELECT * FROM users WHERE id = \" . $uid);",
3062
+ "why": "The user ID is taken directly from the request and used in a query without binding to the current session user.",
3063
+ "line": 6
3064
+ }
3065
+ ],
3066
+ "severity": "high",
3067
+ "confidence": "high",
3068
+ "cwe": [
3069
+ "CWE-284",
3070
+ "CWE-639"
3071
+ ],
3072
+ "recommendation": "Bind the query to the current session user or enforce an admin check.",
3073
+ "status": "confirmed",
3074
+ "verification": {
3075
+ "verdict": "confirmed",
3076
+ "reason_code": "traced",
3077
+ "checked": [
3078
+ "php-plain/admin.php lines 6-8"
3079
+ ],
3080
+ "control_found": null,
3081
+ "notes": "Same as BOLA-R-0057-1; no ownership or admin check before the SELECT.",
3082
+ "suggested_subtype": null,
3083
+ "suggested_cwe": null
3084
+ },
3085
+ "fingerprint": "kfp1:74c7b93955266d9a404d143b14ad26eb2283862744eade1080c184055620e657",
3086
+ "baseline_state": "new"
3087
+ },
3088
+ {
3089
+ "finding_id": "BOLA-R-0059-1",
3090
+ "category": "bola",
3091
+ "subtype": "missing_ownership_check",
3092
+ "title": "BOLA in POST /php-plain/manage_users.php: missing owner verification",
3093
+ "route_id": "R-0059",
3094
+ "endpoint": {
3095
+ "method": "POST",
3096
+ "path": "/php-plain/manage_users.php"
3097
+ },
3098
+ "location": {
3099
+ "file": "php-plain/manage_users.php",
3100
+ "start_line": 5
3101
+ },
3102
+ "evidence": [
3103
+ {
3104
+ "file": "php-plain/manage_users.php",
3105
+ "start_line": 5,
3106
+ "code": "if ($_SERVER['REQUEST_METHOD'] === 'POST') {\n $stmt = $db->prepare(\"DELETE FROM users WHERE id = ?\");\n $stmt->execute(array((int)$_POST['id']));\n}",
3107
+ "why": "The user ID is taken from the request and used to delete a user without verifying that the requester is an admin.",
3108
+ "line": 5
3109
+ }
3110
+ ],
3111
+ "severity": "high",
3112
+ "confidence": "high",
3113
+ "cwe": [
3114
+ "CWE-284",
3115
+ "CWE-639"
3116
+ ],
3117
+ "recommendation": "Require admin privileges before allowing user deletion.",
3118
+ "status": "confirmed",
3119
+ "verification": {
3120
+ "verdict": "confirmed",
3121
+ "reason_code": "traced",
3122
+ "checked": [
3123
+ "php-plain/manage_users.php lines 5-7",
3124
+ "php-plain/inc/auth.php lines 1-7"
3125
+ ],
3126
+ "control_found": null,
3127
+ "notes": "The delete statement uses a POST parameter for the user id with an integer cast, but there is no admin or ownership verification.",
3128
+ "suggested_subtype": null,
3129
+ "suggested_cwe": null
3130
+ },
3131
+ "fingerprint": "kfp1:08e7e40b1898b061e12415cfde4fc28a2ba729418d4c548f7e22b68a4056e0cc",
3132
+ "baseline_state": "new"
3133
+ },
3134
+ {
3135
+ "finding_id": "BOLA-R-0061-1",
3136
+ "category": "bola",
3137
+ "subtype": "missing_ownership_check",
3138
+ "title": "BOLA in GET /php-plain/order_detail.php: missing owner verification",
3139
+ "route_id": "R-0061",
3140
+ "endpoint": {
3141
+ "method": "GET",
3142
+ "path": "/php-plain/order_detail.php"
3143
+ },
3144
+ "location": {
3145
+ "file": "php-plain/order_detail.php",
3146
+ "start_line": 5
3147
+ },
3148
+ "evidence": [
3149
+ {
3150
+ "file": "php-plain/order_detail.php",
3151
+ "start_line": 5,
3152
+ "code": "$id = $_GET['id'];\n$res = mysql_query(\"SELECT * FROM orders WHERE id = \" . $id);",
3153
+ "why": "The order ID is taken from the request and used to fetch an order without ensuring it belongs to the current user.",
3154
+ "line": 5
3155
+ }
3156
+ ],
3157
+ "severity": "high",
3158
+ "confidence": "high",
3159
+ "cwe": [
3160
+ "CWE-284",
3161
+ "CWE-639"
3162
+ ],
3163
+ "recommendation": "Bind the query to the current session user or enforce ownership check.",
3164
+ "status": "confirmed",
3165
+ "verification": {
3166
+ "verdict": "confirmed",
3167
+ "reason_code": "traced",
3168
+ "checked": [
3169
+ "php-plain/order_detail.php lines 5-6",
3170
+ "php-plain/inc/auth.php lines 1-7"
3171
+ ],
3172
+ "control_found": null,
3173
+ "notes": "The order id is taken directly from $_GET and concatenated into the query; no ownership check or cast is present.",
3174
+ "suggested_subtype": null,
3175
+ "suggested_cwe": null
3176
+ },
3177
+ "fingerprint": "kfp1:140efc93b08c193dd167db59f6a495c4cda56c967489cf32705bb8e227ed82e4",
3178
+ "baseline_state": "new"
3179
+ },
3180
+ {
3181
+ "finding_id": "BOLA-R-0066-1",
3182
+ "category": "bola",
3183
+ "subtype": "missing_ownership_check",
3184
+ "title": "BOLA in POST /php-plain/admin/delete_user.php: missing owner verification",
3185
+ "route_id": "R-0066",
3186
+ "endpoint": {
3187
+ "method": "POST",
3188
+ "path": "/php-plain/admin/delete_user.php"
3189
+ },
3190
+ "location": {
3191
+ "file": "php-plain/admin/delete_user.php",
3192
+ "start_line": 5
3193
+ },
3194
+ "evidence": [
3195
+ {
3196
+ "file": "php-plain/admin/delete_user.php",
3197
+ "start_line": 5,
3198
+ "code": "$id = (int)$_POST['id'];\n$stmt = $db->prepare(\"DELETE FROM users WHERE id = ?\");\n$stmt->execute(array($id));",
3199
+ "why": "The user ID is taken directly from the request and used in a delete query without verifying ownership or admin status.",
3200
+ "line": 5
3201
+ }
3202
+ ],
3203
+ "severity": "high",
3204
+ "confidence": "high",
3205
+ "cwe": [
3206
+ "CWE-284",
3207
+ "CWE-639"
3208
+ ],
3209
+ "recommendation": "Require admin privileges or bind the delete to the current session user.",
3210
+ "status": "confirmed",
3211
+ "verification": {
3212
+ "verdict": "confirmed",
3213
+ "reason_code": "traced",
3214
+ "checked": [
3215
+ "php-plain/admin/delete_user.php lines 5-7"
3216
+ ],
3217
+ "control_found": null,
3218
+ "notes": "The ID is cast to int and used in a prepared DELETE statement, but no ownership or admin check is performed.",
3219
+ "suggested_subtype": null,
3220
+ "suggested_cwe": null
3221
+ },
3222
+ "fingerprint": "kfp1:0da2f858297a3d4ebd54b94ece77760ab41c73280cf69a320b15b12ae8d88b7f",
3223
+ "baseline_state": "new"
3224
+ },
3225
+ {
3226
+ "finding_id": "BOLA-R-0067-1",
3227
+ "category": "bola",
3228
+ "subtype": "missing_ownership_check",
3229
+ "title": "BOLA in POST /php-plain/admin/promote.php: missing owner verification",
3230
+ "route_id": "R-0067",
3231
+ "endpoint": {
3232
+ "method": "POST",
3233
+ "path": "/php-plain/admin/promote.php"
3234
+ },
3235
+ "location": {
3236
+ "file": "php-plain/admin/promote.php",
3237
+ "start_line": 5
3238
+ },
3239
+ "evidence": [
3240
+ {
3241
+ "file": "php-plain/admin/promote.php",
3242
+ "start_line": 5,
3243
+ "code": "if ($_POST['is_admin'] == '1') {\n $stmt = $db->prepare(\"UPDATE users SET role = 'admin' WHERE id = ?\");\n $stmt->execute(array((int)$_POST['uid']));\n}",
3244
+ "why": "The target user ID is taken from the request and used to change role without verifying that the requester is an admin.",
3245
+ "line": 5
3246
+ }
3247
+ ],
3248
+ "severity": "high",
3249
+ "confidence": "high",
3250
+ "cwe": [
3251
+ "CWE-284",
3252
+ "CWE-639"
3253
+ ],
3254
+ "recommendation": "Require admin privileges before allowing role changes.",
3255
+ "status": "confirmed",
3256
+ "verification": {
3257
+ "verdict": "confirmed",
3258
+ "reason_code": "traced",
3259
+ "checked": [
3260
+ "php-plain/admin/promote.php lines 5-7",
3261
+ "php-plain/inc/auth.php lines 1-7"
3262
+ ],
3263
+ "control_found": null,
3264
+ "notes": "The code updates a user’s role based solely on a POST parameter. The only visible control is an integer cast on the user id; no role or ownership check is present.",
3265
+ "suggested_subtype": null,
3266
+ "suggested_cwe": null
3267
+ },
3268
+ "fingerprint": "kfp1:c015ee6be1af117790c78fc9a97374926e8a34a36361142150be94c318ff9dd8",
3269
+ "baseline_state": "new"
3270
+ },
3271
+ {
3272
+ "finding_id": "BOLA-R-0069-1",
3273
+ "category": "bola",
3274
+ "subtype": "missing_ownership_check",
3275
+ "title": "BOLA in GET /orders/<int:order_id>: missing owner verification",
3276
+ "route_id": "R-0069",
3277
+ "endpoint": {
3278
+ "method": "GET",
3279
+ "path": "/orders/<int:order_id>"
3280
+ },
3281
+ "location": {
3282
+ "file": "python-flask/app.py",
3283
+ "start_line": 12
3284
+ },
3285
+ "evidence": [
3286
+ {
3287
+ "file": "python-flask/app.py",
3288
+ "start_line": 12,
3289
+ "code": "cur.execute(\"SELECT * FROM orders WHERE id = %s\" % order_id)",
3290
+ "why": "The order ID is taken from the request and used to fetch an order without ensuring it belongs to the current user.",
3291
+ "line": 12
3292
+ }
3293
+ ],
3294
+ "severity": "high",
3295
+ "confidence": "high",
3296
+ "cwe": [
3297
+ "CWE-284",
3298
+ "CWE-639"
3299
+ ],
3300
+ "recommendation": "Add a user_id filter or enforce ownership check.",
3301
+ "status": "confirmed",
3302
+ "verification": {
3303
+ "verdict": "confirmed",
3304
+ "reason_code": "traced",
3305
+ "checked": [
3306
+ "python-flask/app.py lines 8-13",
3307
+ "python-flask/app.py lines 16-21"
3308
+ ],
3309
+ "control_found": null,
3310
+ "notes": "The route is protected by @login_required, but the query uses string interpolation with the order_id and does not filter by current_user.id.",
3311
+ "suggested_subtype": null,
3312
+ "suggested_cwe": null
3313
+ },
3314
+ "fingerprint": "kfp1:8cba56db899ed30b4e1dab440f085921515c324f8d9fff2d15ee38eb60afa641",
3315
+ "baseline_state": "new"
3316
+ },
3317
+ {
3318
+ "finding_id": "BOLA-R-0073",
3319
+ "category": "bola",
3320
+ "subtype": "missing_ownership_check",
3321
+ "title": "BOLA in POST /users/<int:uid>/name: missing owner verification",
3322
+ "route_id": "R-0073",
3323
+ "endpoint": {
3324
+ "method": "POST",
3325
+ "path": "/users/<int:uid>/name"
3326
+ },
3327
+ "location": {
3328
+ "file": "python-flask/profile.py",
3329
+ "start_line": 16
3330
+ },
3331
+ "evidence": [
3332
+ {
3333
+ "file": "python-flask/profile.py",
3334
+ "start_line": 18,
3335
+ "code": "user = User.query.get(uid)",
3336
+ "why": "Loads user by ID supplied in the URL without checking that the ID belongs to the current session user",
3337
+ "line": 18
3338
+ },
3339
+ {
3340
+ "file": "python-flask/profile.py",
3341
+ "start_line": 19,
3342
+ "code": "user.name = request.json[\"name\"]",
3343
+ "why": "Updates the name of the loaded user, allowing an attacker to change any user's name",
3344
+ "line": 19
3345
+ }
3346
+ ],
3347
+ "severity": "high",
3348
+ "confidence": "high",
3349
+ "cwe": [
3350
+ "CWE-284",
3351
+ "CWE-639"
3352
+ ],
3353
+ "recommendation": "Bind the update to the current session user",
3354
+ "status": "confirmed",
3355
+ "verification": {
3356
+ "verdict": "confirmed",
3357
+ "reason_code": "traced",
3358
+ "checked": [
3359
+ "python-flask/profile.py lines 16-20"
3360
+ ],
3361
+ "control_found": null,
3362
+ "notes": "The endpoint accepts a user id from the URL, loads that user, and updates the name without any authentication or ownership check.",
3363
+ "suggested_subtype": null,
3364
+ "suggested_cwe": null
3365
+ },
3366
+ "fingerprint": "kfp1:bfc179df51db588076982a9b160cccb6aa5f7c21b296231a88eb6cbdfe2c3842",
3367
+ "baseline_state": "new"
3368
+ },
3369
+ {
3370
+ "finding_id": "BOLA-R-0074",
3371
+ "category": "bola",
3372
+ "subtype": "missing_ownership_check",
3373
+ "title": "BOLA in PATCH /users/<int:uid>: missing owner verification",
3374
+ "route_id": "R-0074",
3375
+ "endpoint": {
3376
+ "method": "PATCH",
3377
+ "path": "/users/<int:uid>"
3378
+ },
3379
+ "location": {
3380
+ "file": "python-flask/profile.py",
3381
+ "start_line": 24
3382
+ },
3383
+ "evidence": [
3384
+ {
3385
+ "file": "python-flask/profile.py",
3386
+ "start_line": 26,
3387
+ "code": "user = User.query.get(uid)",
3388
+ "why": "Loads user by ID supplied in the URL without ownership check",
3389
+ "line": 26
3390
+ },
3391
+ {
3392
+ "file": "python-flask/profile.py",
3393
+ "start_line": 27,
3394
+ "code": "for key, value in request.json.items():\n setattr(user, key, value)",
3395
+ "why": "Allows arbitrary field updates on any user record",
3396
+ "line": 27
3397
+ }
3398
+ ],
3399
+ "severity": "high",
3400
+ "confidence": "medium",
3401
+ "cwe": [
3402
+ "CWE-284",
3403
+ "CWE-639"
3404
+ ],
3405
+ "recommendation": "Enforce that uid matches the current session user",
3406
+ "status": "likely",
3407
+ "verification": {
3408
+ "verdict": "likely",
3409
+ "reason_code": "code_not_visible",
3410
+ "checked": [
3411
+ "route definition and body lines 24-30 in python-flask/profile.py"
3412
+ ],
3413
+ "control_found": null,
3414
+ "notes": "The PATCH handler loads a User by the supplied uid and updates all fields without any ownership check. No role or ownership guard is visible in the provided snippet, and no global filter or middleware is shown.",
3415
+ "suggested_subtype": null,
3416
+ "suggested_cwe": null
3417
+ },
3418
+ "fingerprint": "kfp1:53120a82329f249cb5d921adbb2d6965297c52b37440e5f47d8a2525bbe46fde",
3419
+ "baseline_state": "new"
3420
+ },
3421
+ {
3422
+ "finding_id": "MASS-M-0001-1",
3423
+ "category": "mass_assignment",
3424
+ "title": "Entity binding with unknown model definition",
3425
+ "candidate_id": "M-0001",
3426
+ "location": {
3427
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
3428
+ "start_line": 12
3429
+ },
3430
+ "evidence": [
3431
+ {
3432
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
3433
+ "line": 12,
3434
+ "why": "Location taken from the Tier 1 inventory"
3435
+ }
3436
+ ],
3437
+ "impact": "unknown – potential privileged fields may be set",
3438
+ "severity": "medium",
3439
+ "confidence": "high",
3440
+ "cwe": [
3441
+ "CWE-915"
3442
+ ],
3443
+ "recommendation": "Inspect the Customer model and restrict writable fields",
3444
+ "status": "confirmed",
3445
+ "subtype": "whole_object_binding",
3446
+ "verification": {
3447
+ "verdict": "confirmed",
3448
+ "reason_code": "traced",
3449
+ "checked": [
3450
+ "dotnet-legacy/Web/Controllers/CustomerController.cs lines 6-15"
3451
+ ],
3452
+ "control_found": null,
3453
+ "notes": "The Edit action accepts a Customer entity directly and marks it Modified without any [Bind] attribute or allow‑list, enabling mass assignment of all properties.",
3454
+ "suggested_subtype": null,
3455
+ "suggested_cwe": null
3456
+ },
3457
+ "fingerprint": "kfp1:b8ff1220f2bfc2aad02ddc82e76e7b05a8ef97fbed13f9330351f5b0b53212bf",
3458
+ "baseline_state": "new"
3459
+ },
3460
+ {
3461
+ "finding_id": "MASS-M-0002-1",
3462
+ "category": "mass_assignment",
3463
+ "title": "Entity binding with unknown model definition",
3464
+ "candidate_id": "M-0002",
3465
+ "location": {
3466
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
3467
+ "start_line": 30
3468
+ },
3469
+ "evidence": [
3470
+ {
3471
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
3472
+ "line": 30,
3473
+ "why": "Location taken from the Tier 1 inventory"
3474
+ }
3475
+ ],
3476
+ "impact": "unknown – potential privileged fields may be set",
3477
+ "severity": "medium",
3478
+ "confidence": "high",
3479
+ "cwe": [
3480
+ "CWE-915"
3481
+ ],
3482
+ "recommendation": "Inspect the Customer model and restrict writable fields",
3483
+ "status": "confirmed",
3484
+ "subtype": "whole_object_binding",
3485
+ "verification": {
3486
+ "verdict": "confirmed",
3487
+ "reason_code": "traced",
3488
+ "checked": [
3489
+ "dotnet-legacy/Web/Controllers/CustomerController.cs lines 27-31"
3490
+ ],
3491
+ "control_found": null,
3492
+ "notes": "TryUpdateModel updates the entity with all request parameters without an allow‑list, allowing privileged fields to be set.",
3493
+ "suggested_subtype": null,
3494
+ "suggested_cwe": null
3495
+ },
3496
+ "fingerprint": "kfp1:7a2510eb85faa00a922436a5eb10a27d438515480f11ac0784f88c9415ded4c0",
3497
+ "baseline_state": "new"
3498
+ },
3499
+ {
3500
+ "finding_id": "MASS-M-0006-1",
3501
+ "category": "mass_assignment",
3502
+ "subtype": "whole_object_binding",
3503
+ "title": "Profile update binds entire request body to User model",
3504
+ "candidate_id": "M-0006",
3505
+ "location": {
3506
+ "file": "node-express/routes/profile.js",
3507
+ "start_line": 8
3508
+ },
3509
+ "evidence": [
3510
+ {
3511
+ "file": "node-express/routes/profile.js",
3512
+ "line": 8,
3513
+ "why": "User.findByIdAndUpdate(req.user._id, req.body, ...) binds entire request body to update"
3514
+ },
3515
+ {
3516
+ "file": "node-express/routes/profile.js",
3517
+ "line": 16,
3518
+ "why": "new User(req.body); user.save(); binds entire request body to new User"
3519
+ }
3520
+ ],
3521
+ "impact": "unknown; potential mass assignment of privileged fields",
3522
+ "severity": "medium",
3523
+ "confidence": "high",
3524
+ "cwe": [
3525
+ "CWE-915"
3526
+ ],
3527
+ "recommendation": "Limit fields to allowed set; use explicit field assignment or whitelist.",
3528
+ "status": "confirmed",
3529
+ "verification": {
3530
+ "verdict": "confirmed",
3531
+ "reason_code": "traced",
3532
+ "checked": [
3533
+ "profile.js:8-9",
3534
+ "profile.js:8"
3535
+ ],
3536
+ "control_found": null,
3537
+ "notes": "User.findByIdAndUpdate uses the entire request body as the update payload, exposing all fields to the client.",
3538
+ "suggested_subtype": null,
3539
+ "suggested_cwe": null
3540
+ },
3541
+ "fingerprint": "kfp1:ec78e14c30f0e52cc4cdf220e7fb71d0f8c9347306faf2453ce6d54de02aeb7a",
3542
+ "baseline_state": "new"
3543
+ },
3544
+ {
3545
+ "finding_id": "MASS-M-0007-1",
3546
+ "category": "mass_assignment",
3547
+ "subtype": "whole_object_binding",
3548
+ "title": "Profile update binds entire request body to User model",
3549
+ "candidate_id": "M-0007",
3550
+ "location": {
3551
+ "file": "node-express/routes/profile.js",
3552
+ "start_line": 16
3553
+ },
3554
+ "evidence": [
3555
+ {
3556
+ "file": "node-express/routes/profile.js",
3557
+ "line": 8,
3558
+ "why": "User.findByIdAndUpdate(req.user._id, req.body, ...) binds entire request body to update"
3559
+ },
3560
+ {
3561
+ "file": "node-express/routes/profile.js",
3562
+ "line": 16,
3563
+ "why": "new User(req.body); user.save(); binds entire request body to new User"
3564
+ }
3565
+ ],
3566
+ "impact": "unknown; potential mass assignment of privileged fields",
3567
+ "severity": "medium",
3568
+ "confidence": "high",
3569
+ "cwe": [
3570
+ "CWE-915"
3571
+ ],
3572
+ "recommendation": "Limit fields to allowed set; use explicit field assignment or whitelist.",
3573
+ "status": "confirmed",
3574
+ "verification": {
3575
+ "verdict": "confirmed",
3576
+ "reason_code": "traced",
3577
+ "checked": [
3578
+ "profile.js:16-17",
3579
+ "profile.js:16"
3580
+ ],
3581
+ "control_found": null,
3582
+ "notes": "Creating a new User with req.body allows all submitted fields, including privileged ones, to be persisted.",
3583
+ "suggested_subtype": null,
3584
+ "suggested_cwe": null
3585
+ },
3586
+ "fingerprint": "kfp1:ff246809367a9a11f3cf5de56d796bac3a7d168644e45ba113f83bbe26fcd608",
3587
+ "baseline_state": "new"
3588
+ },
3589
+ {
3590
+ "finding_id": "MASS-M-0008-1",
3591
+ "category": "mass_assignment",
3592
+ "subtype": "whole_object_binding",
3593
+ "title": "User registration uses mass assignment without field restrictions",
3594
+ "candidate_id": "M-0008",
3595
+ "location": {
3596
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
3597
+ "start_line": 13
3598
+ },
3599
+ "evidence": [
3600
+ {
3601
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
3602
+ "line": 13,
3603
+ "why": "User::create($request->all()); binds all request fields to User model"
3604
+ }
3605
+ ],
3606
+ "impact": "unknown; potential mass assignment of privileged fields",
3607
+ "severity": "medium",
3608
+ "confidence": "medium",
3609
+ "cwe": [
3610
+ "CWE-915"
3611
+ ],
3612
+ "recommendation": "Define $fillable or $guarded to restrict mass assignable attributes.",
3613
+ "status": "likely",
3614
+ "verification": {
3615
+ "verdict": "likely",
3616
+ "reason_code": "code_not_visible",
3617
+ "checked": [
3618
+ "php-laravel/app/Http/Controllers/AccountController.php line 13"
3619
+ ],
3620
+ "control_found": null,
3621
+ "notes": "The controller performs a mass assignment via User::create($request->all()), but the User model’s $fillable or $guarded definition is not shown, so a compensating control cannot be confirmed.",
3622
+ "suggested_subtype": null,
3623
+ "suggested_cwe": null
3624
+ },
3625
+ "fingerprint": "kfp1:0d993d49cea372dd80c171b78e6f7ec5a9ee98469cbb325ec6fa20f80f2824d3",
3626
+ "baseline_state": "new"
3627
+ },
3628
+ {
3629
+ "finding_id": "MASS-M-0010-1",
3630
+ "category": "mass_assignment",
3631
+ "subtype": "whole_object_binding",
3632
+ "title": "Team creation uses mass assignment without model definition",
3633
+ "candidate_id": "M-0010",
3634
+ "location": {
3635
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
3636
+ "start_line": 36
3637
+ },
3638
+ "evidence": [
3639
+ {
3640
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
3641
+ "line": 23,
3642
+ "why": "Team::create($request->all()); binds all request fields"
3643
+ }
3644
+ ],
3645
+ "impact": "unknown; potential mass assignment of privileged fields",
3646
+ "severity": "medium",
3647
+ "confidence": "high",
3648
+ "cwe": [
3649
+ "CWE-915"
3650
+ ],
3651
+ "recommendation": "Review Team model to ensure privileged fields are not mass assignable.",
3652
+ "status": "confirmed",
3653
+ "verification": {
3654
+ "verdict": "confirmed",
3655
+ "reason_code": "traced",
3656
+ "checked": [
3657
+ "php-laravel/app/Http/Controllers/AccountController.php line 23",
3658
+ "php-laravel/app/Models/Team.php line 8"
3659
+ ],
3660
+ "control_found": null,
3661
+ "notes": "Same as MASS-M-0009-1: the Team model’s $fillable includes owner_id, so mass assignment is possible.",
3662
+ "suggested_subtype": null,
3663
+ "suggested_cwe": null
3664
+ },
3665
+ "fingerprint": "kfp1:244d21feb05b766e446de3435905992e0d39b20a5436a9de0683c6a008b0159a",
3666
+ "baseline_state": "new"
3667
+ },
3668
+ {
3669
+ "finding_id": "MASS-M-0011-1",
3670
+ "category": "mass_assignment",
3671
+ "title": "Mass assignment with unknown model fields",
3672
+ "candidate_id": "M-0011",
3673
+ "location": {
3674
+ "file": "php-plain/profile.php",
3675
+ "start_line": 6
3676
+ },
3677
+ "evidence": [
3678
+ {
3679
+ "file": "php-plain/profile.php",
3680
+ "line": 6,
3681
+ "why": "whole object binding, model fields unknown"
3682
+ }
3683
+ ],
3684
+ "impact": "unknown – potential privileged fields may be set",
3685
+ "severity": "medium",
3686
+ "confidence": "high",
3687
+ "cwe": [
3688
+ "CWE-915"
3689
+ ],
3690
+ "recommendation": "Review the User model definition and restrict writable fields",
3691
+ "status": "confirmed",
3692
+ "subtype": "whole_object_binding",
3693
+ "verification": {
3694
+ "verdict": "confirmed",
3695
+ "reason_code": "traced",
3696
+ "checked": [
3697
+ "php-plain/profile.php lines 5-8"
3698
+ ],
3699
+ "control_found": null,
3700
+ "notes": "A foreach loop iterates over all POST fields and assigns them to the user object, enabling mass assignment of unknown fields.",
3701
+ "suggested_subtype": null,
3702
+ "suggested_cwe": null
3703
+ },
3704
+ "fingerprint": "kfp1:28f9b676a4c7dfe2ce08e9296670fec111158546a25875135cf76aee2fd4b27d",
3705
+ "baseline_state": "new"
3706
+ },
3707
+ {
3708
+ "finding_id": "MASS-M-0014-1",
3709
+ "category": "mass_assignment",
3710
+ "subtype": "whole_object_binding",
3711
+ "title": "User creation binds request JSON to User model",
3712
+ "candidate_id": "M-0014",
3713
+ "location": {
3714
+ "file": "python-flask/profile.py",
3715
+ "start_line": 10
3716
+ },
3717
+ "evidence": [
3718
+ {
3719
+ "file": "python-flask/app.py",
3720
+ "line": 10,
3721
+ "why": "user = User(**request.json); binds all request fields to User model"
3722
+ }
3723
+ ],
3724
+ "impact": "unknown; potential mass assignment of privileged fields",
3725
+ "severity": "medium",
3726
+ "confidence": "high",
3727
+ "cwe": [
3728
+ "CWE-915"
3729
+ ],
3730
+ "recommendation": "Use explicit field assignment or DTO to restrict fields.",
3731
+ "status": "confirmed",
3732
+ "verification": {
3733
+ "verdict": "confirmed",
3734
+ "reason_code": "traced",
3735
+ "checked": [
3736
+ "python-flask/app.py line 10: user = User(**request.json)"
3737
+ ],
3738
+ "control_found": null,
3739
+ "notes": "The code directly binds all request JSON fields to the User model without any whitelist or explicit field assignment, exposing a classic mass‑assignment vulnerability.",
3740
+ "suggested_subtype": null,
3741
+ "suggested_cwe": null
3742
+ },
3743
+ "fingerprint": "kfp1:51138561ba9122be66c6d5528102fe010b0ace15250bc672817339e3750f8de1",
3744
+ "baseline_state": "new"
3745
+ },
3746
+ {
3747
+ "finding_id": "MASS-M-0015-1",
3748
+ "category": "mass_assignment",
3749
+ "subtype": "whole_object_binding",
3750
+ "title": "PATCH user endpoint assigns arbitrary fields from request",
3751
+ "candidate_id": "M-0015",
3752
+ "location": {
3753
+ "file": "python-flask/profile.py",
3754
+ "start_line": 27
3755
+ },
3756
+ "evidence": [
3757
+ {
3758
+ "file": "python-flask/app.py",
3759
+ "line": 24,
3760
+ "why": "for key, value in request.json.items(): setattr(user, key, value); assigns all fields"
3761
+ }
3762
+ ],
3763
+ "impact": "unknown; potential mass assignment of privileged fields",
3764
+ "severity": "medium",
3765
+ "confidence": "high",
3766
+ "cwe": [
3767
+ "CWE-915"
3768
+ ],
3769
+ "recommendation": "Whitelist allowed fields; avoid setattr on arbitrary keys.",
3770
+ "status": "confirmed",
3771
+ "verification": {
3772
+ "verdict": "confirmed",
3773
+ "reason_code": "traced",
3774
+ "checked": [
3775
+ "python-flask/profile.py lines 24-30",
3776
+ "python-flask/app.py lines 18-21"
3777
+ ],
3778
+ "control_found": null,
3779
+ "notes": "The PATCH endpoint iterates over request.json and sets attributes via setattr, allowing arbitrary fields to be updated without a whitelist.",
3780
+ "suggested_subtype": null,
3781
+ "suggested_cwe": null
3782
+ },
3783
+ "fingerprint": "kfp1:c5462b8a2767bef52d1825278c78faa6d04a3dcaeaba8db7445a2210c5deac7c",
3784
+ "baseline_state": "new"
3785
+ },
3786
+ {
3787
+ "finding_id": "INJ-I-0022",
3788
+ "category": "sql_injection",
3789
+ "subtype": "string_concatenation",
3790
+ "title": "SQL Injection in node-express/routes/orders.js:19",
3791
+ "candidate_id": "I-0022",
3792
+ "location": {
3793
+ "file": "node-express/routes/orders.js",
3794
+ "start_line": 21
3795
+ },
3796
+ "evidence": [
3797
+ {
3798
+ "file": "node-express/routes/orders.js",
3799
+ "start_line": 19,
3800
+ "code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
3801
+ "why": "Unsanitized input concatenated into query",
3802
+ "line": 19
3803
+ }
3804
+ ],
3805
+ "severity": "critical",
3806
+ "confidence": "high",
3807
+ "cwe": [
3808
+ "CWE-89"
3809
+ ],
3810
+ "recommendation": "Use parameterized queries",
3811
+ "status": "rejected",
3812
+ "verification": {
3813
+ "verdict": "rejected",
3814
+ "reason_code": "evidence_mismatch",
3815
+ "checked": [
3816
+ "node-express/routes/orders.js:21"
3817
+ ],
3818
+ "control_found": null,
3819
+ "notes": "The evidence claims string concatenation into a query, but the code uses MongoDB's findOne with req.body. No SQL query is constructed here, so the evidence does not match the code.",
3820
+ "suggested_subtype": null,
3821
+ "suggested_cwe": null
3822
+ },
3823
+ "fingerprint": "kfp1:ce1d2e4f45d43976acdef65e9ac39896b64d4b16b8d2d07e7403e9408e5daba4"
3824
+ },
3825
+ {
3826
+ "finding_id": "INJ-I-0035",
3827
+ "category": "sql_injection",
3828
+ "subtype": "string_interpolation",
3829
+ "title": "SQL Injection in python-flask/app.py:12",
3830
+ "candidate_id": "I-0035",
3831
+ "location": {
3832
+ "file": "python-flask/app.py",
3833
+ "start_line": 12
3834
+ },
3835
+ "evidence": [
3836
+ {
3837
+ "file": "python-flask/app.py",
3838
+ "start_line": 12,
3839
+ "code": "cur.execute(\"SELECT * FROM orders WHERE id = %s\" % order_id)",
3840
+ "why": "unsanitized string formatting",
3841
+ "line": 12
3842
+ }
3843
+ ],
3844
+ "severity": "critical",
3845
+ "confidence": "high",
3846
+ "cwe": [
3847
+ "CWE-89"
3848
+ ],
3849
+ "recommendation": "Use parameterized queries",
3850
+ "status": "rejected",
3851
+ "verification": {
3852
+ "verdict": "rejected",
3853
+ "reason_code": "control_found",
3854
+ "checked": [
3855
+ "python-flask/app.py lines 8-12"
3856
+ ],
3857
+ "control_found": {
3858
+ "file": "python-flask/app.py",
3859
+ "line": 8,
3860
+ "code": "@app.route('/orders/<int:order_id>')"
3861
+ },
3862
+ "notes": "The route enforces that order_id is an integer, so the string formatting with %s cannot inject SQL. The query is safe.",
3863
+ "suggested_subtype": null,
3864
+ "suggested_cwe": null
3865
+ },
3866
+ "fingerprint": "kfp1:083783e9bf3df62b46b67e3b90cc7c5639ab0d659cbe13583bc1255416cf0182"
3867
+ },
3868
+ {
3869
+ "finding_id": "MASS-M-0013-1",
3870
+ "category": "mass_assignment",
3871
+ "subtype": "whole_object_binding",
3872
+ "title": "Manual extraction of POST data may allow mass assignment",
3873
+ "candidate_id": "M-0013",
3874
+ "location": {
3875
+ "file": "php-plain/register.php",
3876
+ "start_line": 4
3877
+ },
3878
+ "evidence": [
3879
+ {
3880
+ "file": "php-plain/inc/db.php",
3881
+ "line": 4,
3882
+ "why": "extract($_POST); pulls all POST variables into local scope"
3883
+ },
3884
+ {
3885
+ "file": "php-plain/inc/db.php",
3886
+ "line": 5,
3887
+ "why": "$stmt->execute(array($email, $name)); only uses email and name, but extract may expose other variables"
3888
+ }
3889
+ ],
3890
+ "impact": "unknown; potential mass assignment of privileged fields if not validated",
3891
+ "severity": "medium",
3892
+ "confidence": "high",
3893
+ "cwe": [
3894
+ "CWE-915"
3895
+ ],
3896
+ "recommendation": "Avoid extract($_POST); use explicit variable extraction and validation.",
3897
+ "status": "rejected",
3898
+ "verification": {
3899
+ "verdict": "rejected",
3900
+ "reason_code": "evidence_mismatch",
3901
+ "checked": [
3902
+ "php-plain/register.php lines 4-6",
3903
+ "php-plain/inc/db.php lines 1-4"
3904
+ ],
3905
+ "control_found": null,
3906
+ "notes": "extract($_POST) is used, but only $email and $name are bound to the INSERT statement; no mass assignment of privileged fields occurs.",
3907
+ "suggested_subtype": null,
3908
+ "suggested_cwe": null
3909
+ },
3910
+ "fingerprint": "kfp1:8083b1070ea8434ace18c5599bb58a5b7793bb16ca3ff34eafa6112c524e7926"
3911
+ }
3912
+ ]
3913
+ }