@hoanghainh/komagu 2.0.0 → 2.0.1

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 (130) hide show
  1. package/CHANGELOG.md +7 -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/prompts/35-privilege-auditor.md +3 -1
  117. package/assess/lsa/tests/EVAL-RESULTS.md +156 -0
  118. package/assess/lsa/tests/score_findings.py +58 -0
  119. package/assess/lsa/tests/test_eval_run_4.py +73 -0
  120. package/assess/lsa/tests/test_eval_run_5.py +73 -0
  121. package/assess/lsa/tests/test_privilege_tier2.py +30 -0
  122. package/operate/ai/anchoring.py +130 -0
  123. package/operate/ai/client.py +13 -0
  124. package/operate/ai/context.py +105 -0
  125. package/operate/ai/runner.py +197 -69
  126. package/operate/cli/__init__.py +1 -1
  127. package/operate/cli/commands/scan.py +1 -1
  128. package/operate/tests/test_ai_anchoring.py +184 -0
  129. package/operate/tests/test_headless_ai.py +148 -1
  130. package/package.json +1 -1
@@ -0,0 +1,4766 @@
1
+ {
2
+ "generated_by": "lsa merge_findings.py 0.3.1",
3
+ "summary": {
4
+ "total_reported": 101,
5
+ "active": 99,
6
+ "rejected_by_verifier": 2,
7
+ "by_severity": {
8
+ "critical": 50,
9
+ "high": 43,
10
+ "medium": 6,
11
+ "low": 0,
12
+ "info": 0
13
+ },
14
+ "by_category": {
15
+ "bola": 42,
16
+ "sql_injection": 30,
17
+ "nosql_injection": 5,
18
+ "bfla": 12,
19
+ "mass_assignment": 10
20
+ },
21
+ "by_status": {
22
+ "unverified": 0,
23
+ "confirmed": 57,
24
+ "likely": 42,
25
+ "rejected": 2,
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": "Unrestricted member deletion in classic‑ASP",
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": 10,
44
+ "why": "DELETE statement executed without admin check"
45
+ },
46
+ {
47
+ "file": "classic-asp/shop/member.asp",
48
+ "line": 1,
49
+ "why": "only session login check, no role verification"
50
+ }
51
+ ],
52
+ "impact": "Any authenticated user can delete any member record",
53
+ "severity": "critical",
54
+ "confidence": "high",
55
+ "cwe": [
56
+ "CWE-285"
57
+ ],
58
+ "recommendation": "Add an admin role check before executing the DELETE",
59
+ "status": "confirmed",
60
+ "verification": {
61
+ "verdict": "confirmed",
62
+ "reason_code": "traced",
63
+ "checked": [
64
+ "classic-asp/shop/member.asp:1-10",
65
+ "classic-asp/shop/inc/auth.asp:1-3"
66
+ ],
67
+ "control_found": null,
68
+ "notes": "The included auth.asp only verifies a logged‑in session; it does not enforce an admin role. The delete case uses the member_id from the query string without any role check.",
69
+ "suggested_subtype": null,
70
+ "suggested_cwe": null
71
+ },
72
+ "fingerprint": "kfp1:83b02bec36973742a22a1a70d067dab98296c064b1b633e015f7e580b3b66d97",
73
+ "baseline_state": "new"
74
+ },
75
+ {
76
+ "finding_id": "BFLA-R-0007-1",
77
+ "category": "bfla",
78
+ "subtype": "missing_role_check",
79
+ "title": "Unrestricted user deletion in ASP.NET MVC",
80
+ "route_id": "R-0007",
81
+ "location": {
82
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
83
+ "start_line": 12
84
+ },
85
+ "evidence": [
86
+ {
87
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
88
+ "line": 12,
89
+ "why": "DeleteUser action lacks [Authorize(Roles=\"Admin\")]"
90
+ }
91
+ ],
92
+ "impact": "Any authenticated user can delete any user account",
93
+ "severity": "critical",
94
+ "confidence": "high",
95
+ "cwe": [
96
+ "CWE-285"
97
+ ],
98
+ "recommendation": "Add [Authorize(Roles=\"Admin\")] to DeleteUser action",
99
+ "status": "confirmed",
100
+ "verification": {
101
+ "verdict": "confirmed",
102
+ "reason_code": "traced",
103
+ "checked": [
104
+ "dotnet-legacy/Web/Controllers/AdminController.cs:6-12"
105
+ ],
106
+ "control_found": null,
107
+ "notes": "The controller is marked with [Authorize], allowing any authenticated user. The DeleteUser action lacks a role restriction, so any authenticated user can delete any account.",
108
+ "suggested_subtype": null,
109
+ "suggested_cwe": null
110
+ },
111
+ "fingerprint": "kfp1:8c677e45a76f6bd7c5516a436e8831955ce3e1b1cf0a6c24402479abce231451",
112
+ "baseline_state": "new"
113
+ },
114
+ {
115
+ "finding_id": "BFLA-R-0009-1",
116
+ "category": "bfla",
117
+ "subtype": "role_from_request",
118
+ "title": "Export endpoint controlled by request parameter",
119
+ "route_id": "R-0009",
120
+ "location": {
121
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
122
+ "start_line": 30
123
+ },
124
+ "evidence": [
125
+ {
126
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
127
+ "line": 32,
128
+ "why": "Request.QueryString[\"isAdmin\"] == \"true\" allows export"
129
+ },
130
+ {
131
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
132
+ "line": 30,
133
+ "why": "no server‑side role check"
134
+ }
135
+ ],
136
+ "impact": "Any authenticated user can export all user data",
137
+ "severity": "critical",
138
+ "confidence": "high",
139
+ "cwe": [
140
+ "CWE-285"
141
+ ],
142
+ "recommendation": "Validate admin role from session before allowing export",
143
+ "status": "confirmed",
144
+ "verification": {
145
+ "verdict": "confirmed",
146
+ "reason_code": "traced",
147
+ "checked": [
148
+ "dotnet-legacy/Web/Controllers/AdminController.cs:30-34"
149
+ ],
150
+ "control_found": null,
151
+ "notes": "Export logic relies solely on a request query string parameter. No server‑side role validation is performed, allowing any authenticated user to trigger the export.",
152
+ "suggested_subtype": null,
153
+ "suggested_cwe": null
154
+ },
155
+ "fingerprint": "kfp1:0345fc5c650ab741a5683992a0ffaaa8536be025f0e4576566feb91f4f055940",
156
+ "baseline_state": "new"
157
+ },
158
+ {
159
+ "finding_id": "BFLA-R-0023-1",
160
+ "category": "bfla",
161
+ "subtype": "missing_role_check",
162
+ "title": "Unrestricted user deletion in Spring MVC",
163
+ "route_id": "R-0023",
164
+ "location": {
165
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
166
+ "start_line": 13
167
+ },
168
+ "evidence": [
169
+ {
170
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
171
+ "line": 13,
172
+ "why": "delete method lacks @PreAuthorize"
173
+ },
174
+ {
175
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
176
+ "line": 19,
177
+ "why": "other actions have @PreAuthorize, but not this one"
178
+ }
179
+ ],
180
+ "impact": "Any authenticated user can delete any user",
181
+ "severity": "critical",
182
+ "confidence": "medium",
183
+ "cwe": [
184
+ "CWE-285"
185
+ ],
186
+ "recommendation": "Add @PreAuthorize(\"hasRole('ADMIN')\") to delete method",
187
+ "status": "likely",
188
+ "verification": {
189
+ "verdict": "likely",
190
+ "reason_code": "code_not_visible",
191
+ "checked": [
192
+ "java-legacy/src/com/acme/web/AdminUserController.java lines 13-15",
193
+ "java-legacy/src/com/acme/web/AdminUserController.java lines 19-21"
194
+ ],
195
+ "control_found": null,
196
+ "notes": "The delete method lacks any @PreAuthorize or other role guard in the visible code. No global filter or security configuration is shown in the provided context, so a compensating control cannot be confirmed.",
197
+ "suggested_subtype": null,
198
+ "suggested_cwe": null
199
+ },
200
+ "fingerprint": "kfp1:c185d47f85be7d9e30916ed85ddc9948179375151aa429c9ba9bdba3df193d42",
201
+ "baseline_state": "new"
202
+ },
203
+ {
204
+ "finding_id": "BFLA-R-0030-1",
205
+ "category": "bfla",
206
+ "subtype": "missing_role_check",
207
+ "title": "Unrestricted user deletion in Express.js",
208
+ "route_id": "R-0030",
209
+ "location": {
210
+ "file": "node-express/routes/admin.js",
211
+ "start_line": 8
212
+ },
213
+ "evidence": [
214
+ {
215
+ "file": "node-express/routes/admin.js",
216
+ "line": 8,
217
+ "why": "no requireAdmin middleware applied"
218
+ }
219
+ ],
220
+ "impact": "Any authenticated user can delete any user",
221
+ "severity": "critical",
222
+ "confidence": "medium",
223
+ "cwe": [
224
+ "CWE-285"
225
+ ],
226
+ "recommendation": "Add requireAdmin middleware to the delete route",
227
+ "status": "likely",
228
+ "verification": {
229
+ "verdict": "likely",
230
+ "reason_code": "code_not_visible",
231
+ "checked": [
232
+ "node-express/routes/admin.js lines 8-9"
233
+ ],
234
+ "control_found": null,
235
+ "notes": "The delete route uses only ensureLoggedIn; the requireAdmin middleware is not applied in the visible snippet, and no other role check is present.",
236
+ "suggested_subtype": null,
237
+ "suggested_cwe": null
238
+ },
239
+ "fingerprint": "kfp1:c38121b6ace7111a415f9995085dd8d95b1a63052e9eaa92233dd93446a3c631",
240
+ "baseline_state": "new"
241
+ },
242
+ {
243
+ "finding_id": "BFLA-R-0032-1",
244
+ "category": "bfla",
245
+ "subtype": "role_from_request",
246
+ "title": "Export endpoint controlled by query parameter in Express.js",
247
+ "route_id": "R-0032",
248
+ "location": {
249
+ "file": "node-express/routes/admin.js",
250
+ "start_line": 16
251
+ },
252
+ "evidence": [
253
+ {
254
+ "file": "node-express/routes/admin.js",
255
+ "line": 17,
256
+ "why": "req.query.isAdmin === 'true' allows export"
257
+ },
258
+ {
259
+ "file": "node-express/routes/admin.js",
260
+ "line": 16,
261
+ "why": "no role check before export"
262
+ }
263
+ ],
264
+ "impact": "Any authenticated user can export all user data",
265
+ "severity": "critical",
266
+ "confidence": "medium",
267
+ "cwe": [
268
+ "CWE-285"
269
+ ],
270
+ "recommendation": "Validate admin role from session before allowing export",
271
+ "status": "likely",
272
+ "verification": {
273
+ "verdict": "likely",
274
+ "reason_code": "code_not_visible",
275
+ "checked": [
276
+ "node-express/routes/admin.js lines 16-18"
277
+ ],
278
+ "control_found": null,
279
+ "notes": "The export endpoint checks only a query parameter for admin status; no session‑based role verification is shown in the snippet.",
280
+ "suggested_subtype": null,
281
+ "suggested_cwe": null
282
+ },
283
+ "fingerprint": "kfp1:1ff8888a17724ef410c375511a5f1973e8c6485e141a12d400968de937f78234",
284
+ "baseline_state": "new"
285
+ },
286
+ {
287
+ "finding_id": "BFLA-R-0057-1",
288
+ "category": "bfla",
289
+ "subtype": "missing_role_check",
290
+ "title": "Unrestricted user view in PHP",
291
+ "route_id": "R-0057",
292
+ "location": {
293
+ "file": "php-plain/admin.php",
294
+ "start_line": 6
295
+ },
296
+ "evidence": [
297
+ {
298
+ "file": "php-plain/admin.php",
299
+ "line": 6,
300
+ "why": "SELECT * FROM users WHERE id = ... executed without admin check"
301
+ },
302
+ {
303
+ "file": "php-plain/admin.php",
304
+ "line": 2,
305
+ "why": "only session login check, no role verification"
306
+ }
307
+ ],
308
+ "impact": "Any authenticated user can view any user record",
309
+ "severity": "critical",
310
+ "confidence": "medium",
311
+ "cwe": [
312
+ "CWE-285"
313
+ ],
314
+ "recommendation": "Add admin role check before executing the SELECT",
315
+ "status": "likely",
316
+ "verification": {
317
+ "verdict": "likely",
318
+ "reason_code": "code_not_visible",
319
+ "checked": [
320
+ "php-plain/admin.php lines 2-8",
321
+ "php-plain/inc/auth.php lines 1-7"
322
+ ],
323
+ "control_found": null,
324
+ "notes": "The included auth file performs only a login check; there is no admin role guard before the SELECT statement in the visible code.",
325
+ "suggested_subtype": null,
326
+ "suggested_cwe": null
327
+ },
328
+ "fingerprint": "kfp1:d49176fe451bbe3c61de25550631bf23a5b4b79949df949f18698aa7183f6e10",
329
+ "baseline_state": "new"
330
+ },
331
+ {
332
+ "finding_id": "BFLA-R-0058-1",
333
+ "category": "bfla",
334
+ "subtype": "missing_role_check",
335
+ "title": "Unrestricted user deletion in PHP",
336
+ "route_id": "R-0058",
337
+ "location": {
338
+ "file": "php-plain/admin.php",
339
+ "start_line": 10
340
+ },
341
+ "evidence": [
342
+ {
343
+ "file": "php-plain/admin.php",
344
+ "line": 10,
345
+ "why": "DELETE executed without admin check"
346
+ },
347
+ {
348
+ "file": "php-plain/admin.php",
349
+ "line": 2,
350
+ "why": "only session login check, no role verification"
351
+ }
352
+ ],
353
+ "impact": "Any authenticated user can delete any user record",
354
+ "severity": "critical",
355
+ "confidence": "medium",
356
+ "cwe": [
357
+ "CWE-285"
358
+ ],
359
+ "recommendation": "Add admin role check before executing the DELETE",
360
+ "status": "likely",
361
+ "verification": {
362
+ "verdict": "likely",
363
+ "reason_code": "code_not_visible",
364
+ "checked": [
365
+ "php-plain/admin.php lines 10-12",
366
+ "php-plain/inc/auth.php lines 1-7"
367
+ ],
368
+ "control_found": null,
369
+ "notes": "The DELETE statement is executed without any admin role check in the visible code, and the included auth file does not provide such a guard.",
370
+ "suggested_subtype": null,
371
+ "suggested_cwe": null
372
+ },
373
+ "fingerprint": "kfp1:781c6244908fea9052bbe7da28e222368d76d6420cac232e23403ef72b9d0f96",
374
+ "baseline_state": "new"
375
+ },
376
+ {
377
+ "finding_id": "BFLA-R-0059-1",
378
+ "category": "bfla",
379
+ "subtype": "client_side_role_check",
380
+ "title": "Client‑side admin check allows user deletion in PHP",
381
+ "route_id": "R-0059",
382
+ "location": {
383
+ "file": "php-plain/manage_users.php",
384
+ "start_line": 1
385
+ },
386
+ "evidence": [
387
+ {
388
+ "file": "php-plain/manage_users.php",
389
+ "line": 5,
390
+ "why": "DELETE executed without server‑side admin check"
391
+ },
392
+ {
393
+ "file": "php-plain/manage_users.php",
394
+ "line": 11,
395
+ "why": "role check only in HTML, not in handler"
396
+ }
397
+ ],
398
+ "impact": "Any authenticated user can delete any user record",
399
+ "severity": "critical",
400
+ "confidence": "medium",
401
+ "cwe": [
402
+ "CWE-285"
403
+ ],
404
+ "recommendation": "Add server‑side admin check before executing the DELETE",
405
+ "status": "likely",
406
+ "verification": {
407
+ "verdict": "likely",
408
+ "reason_code": "code_not_visible",
409
+ "checked": [
410
+ "php-plain/manage_users.php",
411
+ "php-plain/inc/auth.php",
412
+ "php-plain/inc/db.php"
413
+ ],
414
+ "control_found": null,
415
+ "notes": "Deletion is performed on POST without a server‑side role check; the role check appears only in the HTML output.",
416
+ "suggested_subtype": null,
417
+ "suggested_cwe": null
418
+ },
419
+ "fingerprint": "kfp1:46a95c25c9fdd0024572d331fd6b49713b18fd6e351df3afa986ca37372da405",
420
+ "baseline_state": "new"
421
+ },
422
+ {
423
+ "finding_id": "BFLA-R-0063-1",
424
+ "category": "bfla",
425
+ "subtype": "role_from_request",
426
+ "title": "Privilege escalation via role field in profile update",
427
+ "route_id": "R-0063",
428
+ "location": {
429
+ "file": "php-plain/profile.php",
430
+ "start_line": 1
431
+ },
432
+ "evidence": [
433
+ {
434
+ "file": "php-plain/profile.php",
435
+ "line": 10,
436
+ "why": "role set from $_POST['role'] without admin check"
437
+ },
438
+ {
439
+ "file": "php-plain/profile.php",
440
+ "line": 2,
441
+ "why": "only session login check, no role verification"
442
+ }
443
+ ],
444
+ "impact": "Any authenticated user can change their own role",
445
+ "severity": "critical",
446
+ "confidence": "medium",
447
+ "cwe": [
448
+ "CWE-285"
449
+ ],
450
+ "recommendation": "Validate role changes against allowed values and enforce admin checks",
451
+ "status": "likely",
452
+ "verification": {
453
+ "verdict": "likely",
454
+ "reason_code": "code_not_visible",
455
+ "checked": [
456
+ "php-plain/profile.php",
457
+ "php-plain/inc/auth.php",
458
+ "php-plain/inc/db.php"
459
+ ],
460
+ "control_found": null,
461
+ "notes": "The role field is updated directly from POST data with no admin verification in the visible code.",
462
+ "suggested_subtype": null,
463
+ "suggested_cwe": null
464
+ },
465
+ "fingerprint": "kfp1:5d358b0d7773db98c6fc18bec77889109e89b127d7146e7bc9ca7406ae6ff5fc",
466
+ "baseline_state": "new"
467
+ },
468
+ {
469
+ "finding_id": "BFLA-R-0066-1",
470
+ "category": "bfla",
471
+ "subtype": "missing_role_check",
472
+ "title": "Unrestricted user deletion in PHP",
473
+ "route_id": "R-0066",
474
+ "location": {
475
+ "file": "php-plain/admin/delete_user.php",
476
+ "start_line": 1
477
+ },
478
+ "evidence": [
479
+ {
480
+ "file": "php-plain/admin/delete_user.php",
481
+ "line": 6,
482
+ "why": "DELETE executed without admin check"
483
+ },
484
+ {
485
+ "file": "php-plain/admin/delete_user.php",
486
+ "line": 2,
487
+ "why": "only session login check, no role verification"
488
+ }
489
+ ],
490
+ "impact": "Any authenticated user can delete any user record",
491
+ "severity": "critical",
492
+ "confidence": "medium",
493
+ "cwe": [
494
+ "CWE-285"
495
+ ],
496
+ "recommendation": "Add admin role check before executing the DELETE",
497
+ "status": "likely",
498
+ "verification": {
499
+ "verdict": "likely",
500
+ "reason_code": "code_not_visible",
501
+ "checked": [
502
+ "php-plain/admin/delete_user.php",
503
+ "php-plain/inc/auth.php",
504
+ "php-plain/inc/db.php"
505
+ ],
506
+ "control_found": null,
507
+ "notes": "No role or ownership check is present in the visible code. The included auth file only verifies session existence.",
508
+ "suggested_subtype": null,
509
+ "suggested_cwe": null
510
+ },
511
+ "fingerprint": "kfp1:ac5acea0a998815d202206c2242aa04d68020c74c2b9bf604fd6e9620c81190d",
512
+ "baseline_state": "new"
513
+ },
514
+ {
515
+ "finding_id": "BFLA-R-0067-1",
516
+ "category": "bfla",
517
+ "subtype": "role_from_request",
518
+ "title": "Privilege escalation via role promotion in PHP",
519
+ "route_id": "R-0067",
520
+ "location": {
521
+ "file": "php-plain/admin/promote.php",
522
+ "start_line": 1
523
+ },
524
+ "evidence": [
525
+ {
526
+ "file": "php-plain/admin/promote.php",
527
+ "line": 5,
528
+ "why": "role set from $_POST['is_admin'] without admin check"
529
+ },
530
+ {
531
+ "file": "php-plain/admin/promote.php",
532
+ "line": 2,
533
+ "why": "only session login check, no role verification"
534
+ }
535
+ ],
536
+ "impact": "Any authenticated user can promote themselves to admin",
537
+ "severity": "critical",
538
+ "confidence": "medium",
539
+ "cwe": [
540
+ "CWE-285"
541
+ ],
542
+ "recommendation": "Validate admin role from session before allowing role changes",
543
+ "status": "likely",
544
+ "verification": {
545
+ "verdict": "likely",
546
+ "reason_code": "code_not_visible",
547
+ "checked": [
548
+ "php-plain/admin/promote.php",
549
+ "php-plain/inc/auth.php",
550
+ "php-plain/inc/db.php"
551
+ ],
552
+ "control_found": null,
553
+ "notes": "The code sets the role directly from POST data without any server‑side admin check. No compensating control is visible.",
554
+ "suggested_subtype": null,
555
+ "suggested_cwe": null
556
+ },
557
+ "fingerprint": "kfp1:d174ebc0365672a7b111b699d6c623186ff22f9478565d4ad9e479d844293faf",
558
+ "baseline_state": "new"
559
+ },
560
+ {
561
+ "finding_id": "INJ-I-0001",
562
+ "category": "sql_injection",
563
+ "subtype": "string_concatenation",
564
+ "title": "SQL Injection in classic-asp/shop/member.asp:6",
565
+ "candidate_id": "I-0001",
566
+ "location": {
567
+ "file": "classic-asp/shop/member.asp",
568
+ "start_line": 6
569
+ },
570
+ "evidence": [
571
+ {
572
+ "file": "classic-asp/shop/member.asp",
573
+ "start_line": 6,
574
+ "code": "sql = \"UPDATE members SET name = '\" & Replace(Request.Form(\"name\"), \"'\", \"''\") & \"'\"",
575
+ "why": "Unsanitized input concatenated into query (member_id later)",
576
+ "line": 6
577
+ },
578
+ {
579
+ "file": "classic-asp/shop/member.asp",
580
+ "start_line": 7,
581
+ "code": "sql = sql & \" WHERE member_id = \" & Request.Form(\"member_id\")",
582
+ "why": "Unsanitized input concatenated into query",
583
+ "line": 7
584
+ },
585
+ {
586
+ "file": "classic-asp/shop/member.asp",
587
+ "start_line": 8,
588
+ "code": "conn.Execute sql",
589
+ "why": "Query executed without parameter binding",
590
+ "line": 8
591
+ }
592
+ ],
593
+ "severity": "critical",
594
+ "confidence": "high",
595
+ "cwe": [
596
+ "CWE-89"
597
+ ],
598
+ "recommendation": "Use parameterized queries",
599
+ "status": "confirmed",
600
+ "verification": {
601
+ "verdict": "confirmed",
602
+ "reason_code": "traced",
603
+ "checked": [
604
+ "classic-asp/shop/member.asp:6-8",
605
+ "classic-asp/shop/inc/auth.asp:1-3"
606
+ ],
607
+ "control_found": null,
608
+ "notes": "The SQL UPDATE statement is built via string concatenation with unsanitized form values. The included auth.asp only verifies that a user is logged in; it does not provide parameter binding or sanitization.",
609
+ "suggested_subtype": null,
610
+ "suggested_cwe": null
611
+ },
612
+ "fingerprint": "kfp1:52c63ccaf583de3933acccb3eff1f79314c58ac311cdeecdfc26c798403a7c89",
613
+ "baseline_state": "new"
614
+ },
615
+ {
616
+ "finding_id": "INJ-I-0002",
617
+ "category": "sql_injection",
618
+ "subtype": "string_concatenation",
619
+ "title": "SQL Injection in classic-asp/shop/member.asp:7",
620
+ "candidate_id": "I-0002",
621
+ "location": {
622
+ "file": "classic-asp/shop/member.asp",
623
+ "start_line": 7
624
+ },
625
+ "evidence": [
626
+ {
627
+ "file": "classic-asp/shop/member.asp",
628
+ "start_line": 7,
629
+ "code": "sql = sql & \" WHERE member_id = \" & Request.Form(\"member_id\")",
630
+ "why": "Unsanitized input concatenated into query",
631
+ "line": 7
632
+ },
633
+ {
634
+ "file": "classic-asp/shop/member.asp",
635
+ "start_line": 8,
636
+ "code": "conn.Execute sql",
637
+ "why": "Query executed without parameter binding",
638
+ "line": 8
639
+ }
640
+ ],
641
+ "severity": "critical",
642
+ "confidence": "high",
643
+ "cwe": [
644
+ "CWE-89"
645
+ ],
646
+ "recommendation": "Use parameterized queries",
647
+ "status": "confirmed",
648
+ "verification": {
649
+ "verdict": "confirmed",
650
+ "reason_code": "traced",
651
+ "checked": [
652
+ "classic-asp/shop/member.asp:7-8",
653
+ "classic-asp/shop/inc/auth.asp:1-3"
654
+ ],
655
+ "control_found": null,
656
+ "notes": "The WHERE clause is concatenated with an unsanitized form value. No parameter binding or integer cast is present.",
657
+ "suggested_subtype": null,
658
+ "suggested_cwe": null
659
+ },
660
+ "fingerprint": "kfp1:a425457aee9a3c9db04caa44ddba7d35ecadd834bf5a0100207b9fce90ea85dd",
661
+ "baseline_state": "new"
662
+ },
663
+ {
664
+ "finding_id": "INJ-I-0003",
665
+ "category": "sql_injection",
666
+ "subtype": "string_concatenation",
667
+ "title": "SQL Injection in classic-asp/shop/member.asp:10",
668
+ "candidate_id": "I-0003",
669
+ "location": {
670
+ "file": "classic-asp/shop/member.asp",
671
+ "start_line": 10
672
+ },
673
+ "evidence": [
674
+ {
675
+ "file": "classic-asp/shop/member.asp",
676
+ "start_line": 10,
677
+ "code": "conn.Execute \"DELETE FROM members WHERE member_id = \" & Request.QueryString(\"member_id\")",
678
+ "why": "Unsanitized input concatenated into query",
679
+ "line": 10
680
+ }
681
+ ],
682
+ "severity": "critical",
683
+ "confidence": "high",
684
+ "cwe": [
685
+ "CWE-89"
686
+ ],
687
+ "recommendation": "Use parameterized queries",
688
+ "status": "confirmed",
689
+ "verification": {
690
+ "verdict": "confirmed",
691
+ "reason_code": "traced",
692
+ "checked": [
693
+ "classic-asp/shop/member.asp:10",
694
+ "classic-asp/shop/inc/auth.asp:1-3"
695
+ ],
696
+ "control_found": null,
697
+ "notes": "The DELETE statement concatenates an unsanitized query string value. No sanitization or parameter binding is visible.",
698
+ "suggested_subtype": null,
699
+ "suggested_cwe": null
700
+ },
701
+ "fingerprint": "kfp1:f1fb34f9c39374bc339b3dc230f5ac6b00731c69bf6cd1399b983ba2d2833525",
702
+ "baseline_state": "new"
703
+ },
704
+ {
705
+ "finding_id": "INJ-I-0004",
706
+ "category": "sql_injection",
707
+ "subtype": "string_concatenation",
708
+ "title": "SQL Injection in classic-asp/shop/order.asp:6",
709
+ "candidate_id": "I-0004",
710
+ "location": {
711
+ "file": "classic-asp/shop/order.asp",
712
+ "start_line": 6
713
+ },
714
+ "evidence": [
715
+ {
716
+ "file": "classic-asp/shop/order.asp",
717
+ "start_line": 6,
718
+ "code": "Set rs = conn.Execute(\"SELECT * FROM orders WHERE id = \" & id)",
719
+ "why": "Unsanitized input concatenated into query",
720
+ "line": 6
721
+ }
722
+ ],
723
+ "severity": "critical",
724
+ "confidence": "high",
725
+ "cwe": [
726
+ "CWE-89"
727
+ ],
728
+ "recommendation": "Use parameterized queries",
729
+ "status": "confirmed",
730
+ "verification": {
731
+ "verdict": "confirmed",
732
+ "reason_code": "traced",
733
+ "checked": [
734
+ "classic-asp/shop/order.asp lines 5-6",
735
+ "classic-asp/shop/order.asp line 12"
736
+ ],
737
+ "control_found": null,
738
+ "notes": "The query at line 6 concatenates the unsanitized request parameter directly into the SQL statement. No ownership check or parameter binding is performed before execution. The subsequent query at line 12 includes a user_id filter, but it does not mitigate the vulnerability in the first query.",
739
+ "suggested_subtype": null,
740
+ "suggested_cwe": null
741
+ },
742
+ "fingerprint": "kfp1:07b1c914202a928e4156e6b6e9e5ab32fe7a75cdceda182d55d68051eeb61ac9",
743
+ "baseline_state": "new"
744
+ },
745
+ {
746
+ "finding_id": "INJ-I-0006",
747
+ "category": "sql_injection",
748
+ "subtype": "string_concatenation",
749
+ "title": "SQL Injection in coldfusion/app/OrderService.cfc:5",
750
+ "candidate_id": "I-0006",
751
+ "location": {
752
+ "file": "coldfusion/app/OrderService.cfc",
753
+ "start_line": 5
754
+ },
755
+ "evidence": [
756
+ {
757
+ "file": "coldfusion/app/OrderService.cfc",
758
+ "start_line": 5,
759
+ "code": "SELECT * FROM orders WHERE id = #arguments.orderId#",
760
+ "why": "Unsanitized input concatenated into query",
761
+ "line": 5
762
+ }
763
+ ],
764
+ "severity": "critical",
765
+ "confidence": "high",
766
+ "cwe": [
767
+ "CWE-89"
768
+ ],
769
+ "recommendation": "Use cfqueryparam to bind parameters",
770
+ "status": "confirmed",
771
+ "verification": {
772
+ "verdict": "confirmed",
773
+ "reason_code": "traced",
774
+ "checked": [
775
+ "coldfusion/app/OrderService.cfc lines 3-6"
776
+ ],
777
+ "control_found": null,
778
+ "notes": "The query at line 5 directly interpolates the unvalidated argument `orderId` into the SQL string. No parameter binding, type casting, or ownership check is present.",
779
+ "suggested_subtype": null,
780
+ "suggested_cwe": null
781
+ },
782
+ "fingerprint": "kfp1:b6824160d6d38156b7a16dc1526dda6be874be4401ad843a3c185f94446dcf5e",
783
+ "baseline_state": "new"
784
+ },
785
+ {
786
+ "finding_id": "INJ-I-0007",
787
+ "category": "sql_injection",
788
+ "subtype": "string_concatenation",
789
+ "title": "SQL Injection in coldfusion/app/order.cfm:3",
790
+ "candidate_id": "I-0007",
791
+ "location": {
792
+ "file": "coldfusion/app/order.cfm",
793
+ "start_line": 3
794
+ },
795
+ "evidence": [
796
+ {
797
+ "file": "coldfusion/app/order.cfm",
798
+ "start_line": 3,
799
+ "code": "SELECT * FROM orders WHERE id = #url.id#",
800
+ "why": "Unsanitized input concatenated into query",
801
+ "line": 3
802
+ }
803
+ ],
804
+ "severity": "critical",
805
+ "confidence": "high",
806
+ "cwe": [
807
+ "CWE-89"
808
+ ],
809
+ "recommendation": "Use cfqueryparam to bind parameters",
810
+ "status": "confirmed",
811
+ "verification": {
812
+ "verdict": "confirmed",
813
+ "reason_code": "traced",
814
+ "checked": [
815
+ "coldfusion/app/order.cfm lines 2-4"
816
+ ],
817
+ "control_found": null,
818
+ "notes": "The query at line 3 concatenates the unsanitized `url.id` into the SQL statement. Although a subsequent query uses `cfqueryparam` and a user_id filter, the first query remains vulnerable.",
819
+ "suggested_subtype": null,
820
+ "suggested_cwe": null
821
+ },
822
+ "fingerprint": "kfp1:d2650d07ae7cedfc90bd1abda3c42a63716563d54c45ecde428ae21505214f72",
823
+ "baseline_state": "new"
824
+ },
825
+ {
826
+ "finding_id": "INJ-I-0008-1",
827
+ "category": "sql_injection",
828
+ "subtype": "string_concatenation",
829
+ "title": "SQL Injection in dotnet-legacy/Web/Controllers/InvoiceController.cs:28",
830
+ "candidate_id": "I-0008",
831
+ "location": {
832
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
833
+ "start_line": 28
834
+ },
835
+ "evidence": [
836
+ {
837
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
838
+ "start_line": 27,
839
+ "code": "var customer = Request.Form[\"customer\"];",
840
+ "why": "source: POST body",
841
+ "line": 27
842
+ },
843
+ {
844
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
845
+ "start_line": 28,
846
+ "code": "var list = db.Database.SqlQuery<Invoice>(\"SELECT * FROM Invoices WHERE Customer = '\" + customer + \"'\").ToList();",
847
+ "why": "unsanitized concatenation",
848
+ "line": 28
849
+ }
850
+ ],
851
+ "severity": "critical",
852
+ "confidence": "medium",
853
+ "cwe": [
854
+ "CWE-89"
855
+ ],
856
+ "recommendation": "Use parameterized queries",
857
+ "status": "likely",
858
+ "verification": {
859
+ "verdict": "likely",
860
+ "reason_code": "code_not_visible",
861
+ "checked": [
862
+ "dotnet-legacy/Web/Controllers/InvoiceController.cs lines 25-29 (Search action)"
863
+ ],
864
+ "control_found": null,
865
+ "notes": "No parameter binding, casting, or allow‑list is visible in the provided code context. The raw concatenation of `customer` into the SQL string remains a potential injection vector.",
866
+ "suggested_subtype": null,
867
+ "suggested_cwe": null
868
+ },
869
+ "fingerprint": "kfp1:81106c3dfe7181ee5aad08bbd8e6c94143e8ebb6bf477a18828ca399339d1271",
870
+ "baseline_state": "new"
871
+ },
872
+ {
873
+ "finding_id": "INJ-I-0009",
874
+ "category": "sql_injection",
875
+ "subtype": "string_concatenation",
876
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/EditOrder.aspx.vb:7",
877
+ "candidate_id": "I-0009",
878
+ "location": {
879
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
880
+ "start_line": 7
881
+ },
882
+ "evidence": [
883
+ {
884
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
885
+ "start_line": 7,
886
+ "code": "Dim sql As String = \"UPDATE Orders SET Note = '\" & txtNote.Text & \"'\"",
887
+ "why": "Unsanitized input concatenated into query",
888
+ "line": 7
889
+ },
890
+ {
891
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
892
+ "start_line": 8,
893
+ "code": "sql &= \" WHERE OrderId = \" & hfOrderId.Value",
894
+ "why": "Unsanitized input concatenated into query",
895
+ "line": 8
896
+ },
897
+ {
898
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
899
+ "start_line": 10,
900
+ "code": "cmd.ExecuteNonQuery()",
901
+ "why": "Query executed without parameter binding",
902
+ "line": 10
903
+ }
904
+ ],
905
+ "severity": "critical",
906
+ "confidence": "high",
907
+ "cwe": [
908
+ "CWE-89"
909
+ ],
910
+ "recommendation": "Use parameterized queries",
911
+ "status": "confirmed",
912
+ "verification": {
913
+ "verdict": "confirmed",
914
+ "reason_code": "traced",
915
+ "checked": [
916
+ "dotnet-legacy/Web/Orders/EditOrder.aspx.vb lines 6-8"
917
+ ],
918
+ "control_found": null,
919
+ "notes": "The SQL string is built by concatenating `txtNote.Text` and `hfOrderId.Value` without any parameterization or type casting. No ownership check is performed before execution.",
920
+ "suggested_subtype": null,
921
+ "suggested_cwe": null
922
+ },
923
+ "fingerprint": "kfp1:999e755f32c1edc6b46f7497ebdfd065d8afcc3518cab3211f45fc11a7342afd",
924
+ "baseline_state": "new"
925
+ },
926
+ {
927
+ "finding_id": "INJ-I-0010",
928
+ "category": "sql_injection",
929
+ "subtype": "string_concatenation",
930
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/EditOrder.aspx.vb:8",
931
+ "candidate_id": "I-0010",
932
+ "location": {
933
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
934
+ "start_line": 8
935
+ },
936
+ "evidence": [
937
+ {
938
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
939
+ "start_line": 8,
940
+ "code": "sql &= \" WHERE OrderId = \" & hfOrderId.Value",
941
+ "why": "Unsanitized input concatenated into query",
942
+ "line": 8
943
+ },
944
+ {
945
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
946
+ "start_line": 10,
947
+ "code": "cmd.ExecuteNonQuery()",
948
+ "why": "Query executed without parameter binding",
949
+ "line": 10
950
+ }
951
+ ],
952
+ "severity": "critical",
953
+ "confidence": "high",
954
+ "cwe": [
955
+ "CWE-89"
956
+ ],
957
+ "recommendation": "Use parameterized queries",
958
+ "status": "confirmed",
959
+ "verification": {
960
+ "verdict": "confirmed",
961
+ "reason_code": "traced",
962
+ "checked": [
963
+ "dotnet-legacy/Web/Orders/EditOrder.aspx.vb lines 7-8"
964
+ ],
965
+ "control_found": null,
966
+ "notes": "Same as INJ-I-0009: the query is constructed via string concatenation with user-supplied values and executed without parameter binding or ownership validation.",
967
+ "suggested_subtype": null,
968
+ "suggested_cwe": null
969
+ },
970
+ "fingerprint": "kfp1:323eb8697c45d7b7466292d80a0763dd2310608e0975d837273bde14e3458f59",
971
+ "baseline_state": "new"
972
+ },
973
+ {
974
+ "finding_id": "INJ-I-0011-1",
975
+ "category": "sql_injection",
976
+ "subtype": "string_concatenation",
977
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/OrderDetail.aspx.cs:13",
978
+ "candidate_id": "I-0011",
979
+ "location": {
980
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
981
+ "start_line": 13
982
+ },
983
+ "evidence": [
984
+ {
985
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
986
+ "start_line": 13,
987
+ "code": "var cmd = new SqlCommand(\"SELECT * FROM Orders WHERE OrderId = \" + id, conn);",
988
+ "why": "Unsanitized input concatenated into query",
989
+ "line": 13
990
+ },
991
+ {
992
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
993
+ "start_line": 14,
994
+ "code": "conn.Open();",
995
+ "why": "Query executed without parameter binding",
996
+ "line": 14
997
+ }
998
+ ],
999
+ "severity": "critical",
1000
+ "confidence": "high",
1001
+ "cwe": [
1002
+ "CWE-89"
1003
+ ],
1004
+ "recommendation": "Use parameterized queries",
1005
+ "status": "confirmed",
1006
+ "verification": {
1007
+ "verdict": "confirmed",
1008
+ "reason_code": "traced",
1009
+ "checked": [
1010
+ "line 13"
1011
+ ],
1012
+ "control_found": null,
1013
+ "notes": "The query at line 13 concatenates the unsanitized request parameter \"id\" directly into the SQL string and is executed immediately. No ownership, role check, integer cast, or allow‑list is present in the visible code.",
1014
+ "suggested_subtype": null,
1015
+ "suggested_cwe": null
1016
+ },
1017
+ "fingerprint": "kfp1:c9aa426f72d032a9b0729b0905c9c25a6fb8844a92a91c45d72d3e26170bbae5",
1018
+ "baseline_state": "new"
1019
+ },
1020
+ {
1021
+ "finding_id": "INJ-I-0011-3",
1022
+ "category": "sql_injection",
1023
+ "subtype": "string_interpolation",
1024
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/OrderDetail.aspx.cs:20",
1025
+ "candidate_id": "I-0011",
1026
+ "location": {
1027
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1028
+ "start_line": 13
1029
+ },
1030
+ "evidence": [
1031
+ {
1032
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1033
+ "start_line": 20,
1034
+ "code": "string sql3 = $\"DELETE FROM Carts WHERE CartId = {Request.Form[\"cart\"]}\";",
1035
+ "why": "Unsanitized input concatenated into query",
1036
+ "line": 20
1037
+ },
1038
+ {
1039
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1040
+ "start_line": 22,
1041
+ "code": "new SqlCommand(sql3, conn).ExecuteNonQuery();",
1042
+ "why": "Query executed without parameter binding",
1043
+ "line": 22
1044
+ }
1045
+ ],
1046
+ "severity": "critical",
1047
+ "confidence": "high",
1048
+ "cwe": [
1049
+ "CWE-89"
1050
+ ],
1051
+ "recommendation": "Use parameterized queries",
1052
+ "status": "confirmed",
1053
+ "verification": {
1054
+ "verdict": "confirmed",
1055
+ "reason_code": "traced",
1056
+ "checked": [
1057
+ "line 20"
1058
+ ],
1059
+ "control_found": null,
1060
+ "notes": "The interpolated string at line 20 directly injects the unsanitized form parameter \"cart\" into a DELETE statement, executed at line 22. No protection is evident.",
1061
+ "suggested_subtype": null,
1062
+ "suggested_cwe": null
1063
+ },
1064
+ "fingerprint": "kfp1:ffb5c060494d8556500d82003827d34b69c9197e90809dd8cffb8bc91642f981",
1065
+ "baseline_state": "new"
1066
+ },
1067
+ {
1068
+ "finding_id": "INJ-I-0012",
1069
+ "category": "sql_injection",
1070
+ "subtype": "string_concatenation",
1071
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/OrderDetail.aspx.cs:19",
1072
+ "candidate_id": "I-0012",
1073
+ "location": {
1074
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1075
+ "start_line": 19
1076
+ },
1077
+ "evidence": [
1078
+ {
1079
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1080
+ "start_line": 19,
1081
+ "code": "string sql2 = string.Format(\"SELECT * FROM Logs WHERE Ref = '{0}'\", Request[\"ref\"]);",
1082
+ "why": "Unsanitized input concatenated into query",
1083
+ "line": 19
1084
+ },
1085
+ {
1086
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1087
+ "start_line": 21,
1088
+ "code": "new SqlCommand(sql2, conn).ExecuteReader();",
1089
+ "why": "Query executed without parameter binding",
1090
+ "line": 21
1091
+ }
1092
+ ],
1093
+ "severity": "critical",
1094
+ "confidence": "high",
1095
+ "cwe": [
1096
+ "CWE-89"
1097
+ ],
1098
+ "recommendation": "Use parameterized queries",
1099
+ "status": "confirmed",
1100
+ "verification": {
1101
+ "verdict": "confirmed",
1102
+ "reason_code": "traced",
1103
+ "checked": [
1104
+ "line 19"
1105
+ ],
1106
+ "control_found": null,
1107
+ "notes": "Same as INJ-I-0011-2: unsanitized \"ref\" is concatenated into SQL and executed without parameter binding.",
1108
+ "suggested_subtype": null,
1109
+ "suggested_cwe": null
1110
+ },
1111
+ "fingerprint": "kfp1:c0adff113329292a51bb7d2f2aa03dd242c6d767cee060a789ff750f3ab9b946",
1112
+ "baseline_state": "new"
1113
+ },
1114
+ {
1115
+ "finding_id": "INJ-I-0013",
1116
+ "category": "sql_injection",
1117
+ "subtype": "string_interpolation",
1118
+ "title": "SQL Injection in dotnet-legacy/Web/Orders/OrderDetail.aspx.cs:20",
1119
+ "candidate_id": "I-0013",
1120
+ "location": {
1121
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1122
+ "start_line": 20
1123
+ },
1124
+ "evidence": [
1125
+ {
1126
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1127
+ "start_line": 20,
1128
+ "code": "string sql3 = $\"DELETE FROM Carts WHERE CartId = {Request.Form[\"cart\"]}\";",
1129
+ "why": "Unsanitized input concatenated into query",
1130
+ "line": 20
1131
+ },
1132
+ {
1133
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
1134
+ "start_line": 22,
1135
+ "code": "new SqlCommand(sql3, conn).ExecuteNonQuery();",
1136
+ "why": "Query executed without parameter binding",
1137
+ "line": 22
1138
+ }
1139
+ ],
1140
+ "severity": "critical",
1141
+ "confidence": "high",
1142
+ "cwe": [
1143
+ "CWE-89"
1144
+ ],
1145
+ "recommendation": "Use parameterized queries",
1146
+ "status": "confirmed",
1147
+ "verification": {
1148
+ "verdict": "confirmed",
1149
+ "reason_code": "traced",
1150
+ "checked": [
1151
+ "line 20"
1152
+ ],
1153
+ "control_found": null,
1154
+ "notes": "Duplicate finding ID; same issue as INJ-I-0011-3.",
1155
+ "suggested_subtype": null,
1156
+ "suggested_cwe": null
1157
+ },
1158
+ "fingerprint": "kfp1:85f16690de7a315c7ef9dc35f534e32d9f39803093f42a6ec4200391523c52c2",
1159
+ "baseline_state": "new"
1160
+ },
1161
+ {
1162
+ "finding_id": "INJ-I-0014",
1163
+ "category": "sql_injection",
1164
+ "subtype": "string_concatenation",
1165
+ "title": "SQL Injection in java-legacy/WebContent/order/detail.jsp:5",
1166
+ "candidate_id": "I-0014",
1167
+ "location": {
1168
+ "file": "java-legacy/WebContent/order/detail.jsp",
1169
+ "start_line": 5
1170
+ },
1171
+ "evidence": [
1172
+ {
1173
+ "file": "java-legacy/WebContent/order/detail.jsp",
1174
+ "start_line": 5,
1175
+ "code": "ResultSet rs = c.createStatement().executeQuery(\"SELECT * FROM orders WHERE order_no = '\" + no + \"'\");",
1176
+ "why": "Unsanitized input concatenated into query",
1177
+ "line": 5
1178
+ }
1179
+ ],
1180
+ "severity": "critical",
1181
+ "confidence": "medium",
1182
+ "cwe": [
1183
+ "CWE-89"
1184
+ ],
1185
+ "recommendation": "Use PreparedStatement with parameters",
1186
+ "status": "likely",
1187
+ "verification": {
1188
+ "verdict": "likely",
1189
+ "reason_code": "code_not_visible",
1190
+ "checked": [
1191
+ "java-legacy/WebContent/order/detail.jsp lines 1-7"
1192
+ ],
1193
+ "control_found": null,
1194
+ "notes": "The JSP page concatenates the request parameter directly into a SQL query without any parameter binding, casting, or ownership check visible in the provided code context.",
1195
+ "suggested_subtype": null,
1196
+ "suggested_cwe": null
1197
+ },
1198
+ "fingerprint": "kfp1:b8e7acb1ca7119156e96cf2bd42356b53bb6107a0a904b492534714730ac45f8",
1199
+ "baseline_state": "new"
1200
+ },
1201
+ {
1202
+ "finding_id": "INJ-I-0015-1",
1203
+ "category": "sql_injection",
1204
+ "subtype": "template_placeholder",
1205
+ "title": "SQL Injection in java-legacy/src/com/acme/mapper/OrderMapper.xml:9",
1206
+ "candidate_id": "I-0015",
1207
+ "location": {
1208
+ "file": "java-legacy/src/com/acme/mapper/OrderMapper.xml",
1209
+ "start_line": 9
1210
+ },
1211
+ "evidence": [
1212
+ {
1213
+ "file": "java-legacy/src/com/acme/mapper/OrderMapper.xml",
1214
+ "start_line": 9,
1215
+ "code": "ORDER BY ${orderBy}",
1216
+ "why": "raw string substitution unsanitized",
1217
+ "line": 9
1218
+ }
1219
+ ],
1220
+ "severity": "critical",
1221
+ "confidence": "medium",
1222
+ "cwe": [
1223
+ "CWE-89"
1224
+ ],
1225
+ "recommendation": "Use parameterized queries or allow-list ORDER BY columns",
1226
+ "status": "likely",
1227
+ "verification": {
1228
+ "verdict": "likely",
1229
+ "reason_code": "code_not_visible",
1230
+ "checked": [
1231
+ "java-legacy/src/com/acme/mapper/OrderMapper.xml lines 8-10 (ORDER BY clause)"
1232
+ ],
1233
+ "control_found": null,
1234
+ "notes": "The `${orderBy}` placeholder is substituted directly into the SQL string with no visible allow‑list or type enforcement in the provided snippet.",
1235
+ "suggested_subtype": null,
1236
+ "suggested_cwe": null
1237
+ },
1238
+ "fingerprint": "kfp1:8df8ce68ec0891d8df90282a79909ca8020e544c090dec299a62140d23fde444",
1239
+ "baseline_state": "new"
1240
+ },
1241
+ {
1242
+ "finding_id": "INJ-I-0016",
1243
+ "category": "sql_injection",
1244
+ "subtype": "string_concatenation",
1245
+ "title": "SQL Injection in java-legacy/src/com/acme/struts/EditUserAction.java:13",
1246
+ "candidate_id": "I-0016",
1247
+ "location": {
1248
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
1249
+ "start_line": 13
1250
+ },
1251
+ "evidence": [
1252
+ {
1253
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
1254
+ "start_line": 13,
1255
+ "code": "PreparedStatement ps = conn.prepareStatement(\"UPDATE users SET email = '\" + email + \"' WHERE id = \" + userId);",
1256
+ "why": "Unsanitized input concatenated into query",
1257
+ "line": 13
1258
+ },
1259
+ {
1260
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
1261
+ "start_line": 14,
1262
+ "code": "ps.executeUpdate();",
1263
+ "why": "Query executed without parameter binding",
1264
+ "line": 14
1265
+ }
1266
+ ],
1267
+ "severity": "critical",
1268
+ "confidence": "medium",
1269
+ "cwe": [
1270
+ "CWE-89"
1271
+ ],
1272
+ "recommendation": "Use PreparedStatement with parameters",
1273
+ "status": "likely",
1274
+ "verification": {
1275
+ "verdict": "likely",
1276
+ "reason_code": "code_not_visible",
1277
+ "checked": [
1278
+ "java-legacy/src/com/acme/struts/EditUserAction.java lines 1-17"
1279
+ ],
1280
+ "control_found": null,
1281
+ "notes": "The action builds an UPDATE statement by concatenating user-supplied values without using prepared statement parameters or any ownership verification in the visible code.",
1282
+ "suggested_subtype": null,
1283
+ "suggested_cwe": null
1284
+ },
1285
+ "fingerprint": "kfp1:2976fb21c23c927d1de0f3af7dc883d9b66a1d051ec67bb9ec1b44396f6479fc",
1286
+ "baseline_state": "new"
1287
+ },
1288
+ {
1289
+ "finding_id": "INJ-I-0017",
1290
+ "category": "sql_injection",
1291
+ "subtype": "string_concatenation",
1292
+ "title": "SQL Injection in java-legacy/src/com/acme/web/InvoiceController.java:29",
1293
+ "candidate_id": "I-0017",
1294
+ "location": {
1295
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
1296
+ "start_line": 29
1297
+ },
1298
+ "evidence": [
1299
+ {
1300
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
1301
+ "start_line": 29,
1302
+ "code": "model.addAttribute(\"list\", session.createQuery(\"from Invoice i where i.customer = '\" + customer + \"'\").list());",
1303
+ "why": "Unsanitized input concatenated into query",
1304
+ "line": 29
1305
+ }
1306
+ ],
1307
+ "severity": "critical",
1308
+ "confidence": "medium",
1309
+ "cwe": [
1310
+ "CWE-89"
1311
+ ],
1312
+ "recommendation": "Use parameterized HQL/JPQL",
1313
+ "status": "likely",
1314
+ "verification": {
1315
+ "verdict": "likely",
1316
+ "reason_code": "code_not_visible",
1317
+ "checked": [
1318
+ "java-legacy/src/com/acme/web/InvoiceController.java lines 9-31"
1319
+ ],
1320
+ "control_found": null,
1321
+ "notes": "The controller concatenates the 'customer' request parameter into an HQL query without parameter binding or ownership checks in the shown snippet.",
1322
+ "suggested_subtype": null,
1323
+ "suggested_cwe": null
1324
+ },
1325
+ "fingerprint": "kfp1:7796f734194bfe71dc7268f597efd6338a4e967a788e0220edb61b8e1c198b16",
1326
+ "baseline_state": "new"
1327
+ },
1328
+ {
1329
+ "finding_id": "INJ-I-0018-1",
1330
+ "category": "sql_injection",
1331
+ "subtype": "string_concatenation",
1332
+ "title": "SQL Injection in java-legacy/src/com/acme/web/OrderServlet.java:15",
1333
+ "candidate_id": "I-0018",
1334
+ "location": {
1335
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1336
+ "start_line": 15
1337
+ },
1338
+ "evidence": [
1339
+ {
1340
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1341
+ "start_line": 11,
1342
+ "code": "String id = request.getParameter(\"orderId\");",
1343
+ "why": "source: request parameter",
1344
+ "line": 11
1345
+ },
1346
+ {
1347
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
1348
+ "start_line": 15,
1349
+ "code": "ResultSet rs = stmt.executeQuery(\"SELECT * FROM orders WHERE id = '\" + id + \"'\");",
1350
+ "why": "unsanitized concatenation",
1351
+ "line": 15
1352
+ }
1353
+ ],
1354
+ "severity": "critical",
1355
+ "confidence": "medium",
1356
+ "cwe": [
1357
+ "CWE-89"
1358
+ ],
1359
+ "recommendation": "Use prepared statements",
1360
+ "status": "likely",
1361
+ "verification": {
1362
+ "verdict": "likely",
1363
+ "reason_code": "code_not_visible",
1364
+ "checked": [
1365
+ "java-legacy/src/com/acme/web/OrderServlet.java lines 13-15 (first query)"
1366
+ ],
1367
+ "control_found": null,
1368
+ "notes": "The `id` parameter is concatenated into the SQL string without any casting or validation in the shown code.",
1369
+ "suggested_subtype": null,
1370
+ "suggested_cwe": null
1371
+ },
1372
+ "fingerprint": "kfp1:eed713c040fe4b03f16a649b4da2fa326995fd074ad3ba46bbbb4958486b6b8d",
1373
+ "baseline_state": "new"
1374
+ },
1375
+ {
1376
+ "finding_id": "INJ-I-0020-1",
1377
+ "category": "sql_injection",
1378
+ "subtype": "string_concatenation",
1379
+ "title": "SQL Injection in node-express/routes/orders.js:17",
1380
+ "candidate_id": "I-0020",
1381
+ "location": {
1382
+ "file": "node-express/routes/orders.js",
1383
+ "start_line": 17
1384
+ },
1385
+ "evidence": [
1386
+ {
1387
+ "file": "node-express/routes/orders.js",
1388
+ "start_line": 17,
1389
+ "code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
1390
+ "why": "Unsanitized input concatenated into query",
1391
+ "line": 17
1392
+ }
1393
+ ],
1394
+ "severity": "critical",
1395
+ "confidence": "medium",
1396
+ "cwe": [
1397
+ "CWE-89"
1398
+ ],
1399
+ "recommendation": "Use parameterized queries",
1400
+ "status": "likely",
1401
+ "verification": {
1402
+ "verdict": "likely",
1403
+ "reason_code": "code_not_visible",
1404
+ "checked": [
1405
+ "node-express/routes/orders.js lines 1-32"
1406
+ ],
1407
+ "control_found": null,
1408
+ "notes": "The route constructs a raw SQL query by concatenating req.query.month without any type casting, parameter binding, or ownership verification in the visible code.",
1409
+ "suggested_subtype": null,
1410
+ "suggested_cwe": null
1411
+ },
1412
+ "fingerprint": "kfp1:fc0f31011ca4510d026845f7fb0dcd77812ef24f6ea5329ad24fbc08a15b58ff",
1413
+ "baseline_state": "new"
1414
+ },
1415
+ {
1416
+ "finding_id": "INJ-I-0020-3",
1417
+ "category": "nosql_injection",
1418
+ "subtype": "operator_injection",
1419
+ "title": "NoSQL Injection in node-express/routes/orders.js:25",
1420
+ "candidate_id": "I-0020",
1421
+ "location": {
1422
+ "file": "node-express/routes/orders.js",
1423
+ "start_line": 17
1424
+ },
1425
+ "evidence": [
1426
+ {
1427
+ "file": "node-express/routes/orders.js",
1428
+ "start_line": 25,
1429
+ "code": "db.collection('items').find({ $where: \"this.name == '\" + req.query.n + \"'\" }).toArray(function (e, r) { res.json(r); });",
1430
+ "why": "Unescaped string used in $where expression",
1431
+ "line": 25
1432
+ }
1433
+ ],
1434
+ "severity": "critical",
1435
+ "confidence": "high",
1436
+ "cwe": [
1437
+ "CWE-89"
1438
+ ],
1439
+ "recommendation": "Avoid $where; use parameterized queries or safe operators",
1440
+ "status": "confirmed",
1441
+ "verification": {
1442
+ "verdict": "confirmed",
1443
+ "reason_code": "traced",
1444
+ "checked": [
1445
+ "lines 24-26 of node-express/routes/orders.js"
1446
+ ],
1447
+ "control_found": null,
1448
+ "notes": "The query concatenates user input into a $where expression with no sanitization or allow‑list. The only guard is ensureLoggedIn, which only verifies authentication, not ownership or input validation.",
1449
+ "suggested_subtype": null,
1450
+ "suggested_cwe": null
1451
+ },
1452
+ "fingerprint": "kfp1:47ce27f1a9f336e20d5e241fd635274073032ac2f65abcfe9d60fb3a2b8fca3a",
1453
+ "baseline_state": "new"
1454
+ },
1455
+ {
1456
+ "finding_id": "INJ-I-0020-4",
1457
+ "category": "sql_injection",
1458
+ "subtype": "string_interpolation",
1459
+ "title": "SQL Injection in node-express/routes/orders.js:29",
1460
+ "candidate_id": "I-0020",
1461
+ "location": {
1462
+ "file": "node-express/routes/orders.js",
1463
+ "start_line": 17
1464
+ },
1465
+ "evidence": [
1466
+ {
1467
+ "file": "node-express/routes/orders.js",
1468
+ "start_line": 29,
1469
+ "code": "db.query(`SELECT COUNT(*) AS n FROM orders WHERE shop_id = ${req.query.shop}`, function (e, r) { res.json(r); });",
1470
+ "why": "Unsanitized input interpolated into query",
1471
+ "line": 29
1472
+ }
1473
+ ],
1474
+ "severity": "critical",
1475
+ "confidence": "high",
1476
+ "cwe": [
1477
+ "CWE-89"
1478
+ ],
1479
+ "recommendation": "Use parameterized queries",
1480
+ "status": "confirmed",
1481
+ "verification": {
1482
+ "verdict": "confirmed",
1483
+ "reason_code": "traced",
1484
+ "checked": [
1485
+ "lines 28-30 of node-express/routes/orders.js"
1486
+ ],
1487
+ "control_found": null,
1488
+ "notes": "The SQL statement interpolates req.query.shop directly into the query string. No cast, parameter binding, or ownership check is present.",
1489
+ "suggested_subtype": null,
1490
+ "suggested_cwe": null
1491
+ },
1492
+ "fingerprint": "kfp1:87003bc21a5066f127e17d017411e5ede375de322eabe11a402d6ee498598376",
1493
+ "baseline_state": "new"
1494
+ },
1495
+ {
1496
+ "finding_id": "INJ-I-0021",
1497
+ "category": "sql_injection",
1498
+ "subtype": "string_interpolation",
1499
+ "title": "SQL Injection in node-express/routes/orders.js:29",
1500
+ "candidate_id": "I-0021",
1501
+ "location": {
1502
+ "file": "node-express/routes/orders.js",
1503
+ "start_line": 29
1504
+ },
1505
+ "evidence": [
1506
+ {
1507
+ "file": "node-express/routes/orders.js",
1508
+ "start_line": 29,
1509
+ "code": "db.query(`SELECT COUNT(*) AS n FROM orders WHERE shop_id = ${req.query.shop}`, function (e, r) { res.json(r); });",
1510
+ "why": "Unsanitized input interpolated into query",
1511
+ "line": 29
1512
+ }
1513
+ ],
1514
+ "severity": "critical",
1515
+ "confidence": "high",
1516
+ "cwe": [
1517
+ "CWE-89"
1518
+ ],
1519
+ "recommendation": "Use parameterized queries",
1520
+ "status": "confirmed",
1521
+ "verification": {
1522
+ "verdict": "confirmed",
1523
+ "reason_code": "traced",
1524
+ "checked": [
1525
+ "lines 28-30 of node-express/routes/orders.js"
1526
+ ],
1527
+ "control_found": null,
1528
+ "notes": "Same as INJ-I-0020-4; the route is identical and lacks any compensating control.",
1529
+ "suggested_subtype": null,
1530
+ "suggested_cwe": null
1531
+ },
1532
+ "fingerprint": "kfp1:2abbfba5afbab6fe47146424b128ea7bf5496a9ec3ae59589a94d2eccd4c3d66",
1533
+ "baseline_state": "new"
1534
+ },
1535
+ {
1536
+ "finding_id": "INJ-I-0022",
1537
+ "category": "nosql_injection",
1538
+ "subtype": "operator_injection",
1539
+ "title": "NoSQL Injection in node-express/routes/orders.js:21",
1540
+ "candidate_id": "I-0022",
1541
+ "location": {
1542
+ "file": "node-express/routes/orders.js",
1543
+ "start_line": 21
1544
+ },
1545
+ "evidence": [
1546
+ {
1547
+ "file": "node-express/routes/orders.js",
1548
+ "start_line": 21,
1549
+ "code": "db.collection('users').findOne(req.body, function (e, u) { res.json(!!u); });",
1550
+ "why": "Unvalidated request body used directly as query filter",
1551
+ "line": 21
1552
+ }
1553
+ ],
1554
+ "severity": "critical",
1555
+ "confidence": "high",
1556
+ "cwe": [
1557
+ "CWE-89"
1558
+ ],
1559
+ "recommendation": "Validate and whitelist query parameters; avoid passing raw request body to findOne",
1560
+ "status": "confirmed",
1561
+ "verification": {
1562
+ "verdict": "confirmed",
1563
+ "reason_code": "traced",
1564
+ "checked": [
1565
+ "lines 20-22 of node-express/routes/orders.js"
1566
+ ],
1567
+ "control_found": null,
1568
+ "notes": "The request body is passed directly to findOne without validation or sanitization. No authentication guard is applied to this POST /login route.",
1569
+ "suggested_subtype": null,
1570
+ "suggested_cwe": null
1571
+ },
1572
+ "fingerprint": "kfp1:b11f292e89c1d1ed0de413d0c30a47a3f3e034679b695b5f5b7c5167f5f1e929",
1573
+ "baseline_state": "new"
1574
+ },
1575
+ {
1576
+ "finding_id": "INJ-I-0023",
1577
+ "category": "nosql_injection",
1578
+ "subtype": "operator_injection",
1579
+ "title": "NoSQL Injection in node-express/routes/orders.js:25",
1580
+ "candidate_id": "I-0023",
1581
+ "location": {
1582
+ "file": "node-express/routes/orders.js",
1583
+ "start_line": 25
1584
+ },
1585
+ "evidence": [
1586
+ {
1587
+ "file": "node-express/routes/orders.js",
1588
+ "start_line": 25,
1589
+ "code": "db.collection('items').find({ $where: \"this.name == '\" + req.query.n + \"'\" }).toArray(function (e, r) { res.json(r); });",
1590
+ "why": "Unescaped string used in $where expression",
1591
+ "line": 25
1592
+ }
1593
+ ],
1594
+ "severity": "critical",
1595
+ "confidence": "high",
1596
+ "cwe": [
1597
+ "CWE-89"
1598
+ ],
1599
+ "recommendation": "Avoid $where; use parameterized queries or safe operators",
1600
+ "status": "confirmed",
1601
+ "verification": {
1602
+ "verdict": "confirmed",
1603
+ "reason_code": "traced",
1604
+ "checked": [
1605
+ "lines 24-26 of node-express/routes/orders.js"
1606
+ ],
1607
+ "control_found": null,
1608
+ "notes": "Identical to INJ-I-0020-3; no sanitization or allow‑list is present.",
1609
+ "suggested_subtype": null,
1610
+ "suggested_cwe": null
1611
+ },
1612
+ "fingerprint": "kfp1:3c8358a689974ac6ce80d5d8933718bcf7ce7c7afafe2196d8fcfb9abcde049b",
1613
+ "baseline_state": "new"
1614
+ },
1615
+ {
1616
+ "finding_id": "INJ-I-0024-1",
1617
+ "category": "sql_injection",
1618
+ "subtype": "string_concatenation",
1619
+ "title": "SQL Injection in perl-cgi/cgi-bin/order.cgi:13",
1620
+ "candidate_id": "I-0024",
1621
+ "location": {
1622
+ "file": "perl-cgi/cgi-bin/order.cgi",
1623
+ "start_line": 13
1624
+ },
1625
+ "evidence": [
1626
+ {
1627
+ "file": "perl-cgi/cgi-bin/order.cgi",
1628
+ "start_line": 13,
1629
+ "code": "my $sth = $dbh->prepare(\"SELECT * FROM orders WHERE id = $id\");",
1630
+ "why": "Unsanitized input concatenated into query",
1631
+ "line": 13
1632
+ }
1633
+ ],
1634
+ "severity": "critical",
1635
+ "confidence": "high",
1636
+ "cwe": [
1637
+ "CWE-89"
1638
+ ],
1639
+ "recommendation": "Use parameterized queries",
1640
+ "status": "confirmed",
1641
+ "verification": {
1642
+ "verdict": "confirmed",
1643
+ "reason_code": "traced",
1644
+ "checked": [
1645
+ "perl-cgi/cgi-bin/order.cgi line 13",
1646
+ "perl-cgi/cgi-bin/order.cgi line 11-14"
1647
+ ],
1648
+ "control_found": null,
1649
+ "notes": "The query concatenates the unsanitized $id directly into the SQL string. No integer cast, parameter binding, or ownership check is present in the visible code.",
1650
+ "suggested_subtype": null,
1651
+ "suggested_cwe": null
1652
+ },
1653
+ "fingerprint": "kfp1:0594761f603533b7391a9b7a8464e6865b71c1b61b71fdcd10991d861f51b5fb",
1654
+ "baseline_state": "new"
1655
+ },
1656
+ {
1657
+ "finding_id": "INJ-I-0025",
1658
+ "category": "sql_injection",
1659
+ "subtype": "string_concatenation",
1660
+ "title": "SQL Injection in perl-cgi/cgi-bin/order.cgi:19",
1661
+ "candidate_id": "I-0025",
1662
+ "location": {
1663
+ "file": "perl-cgi/cgi-bin/order.cgi",
1664
+ "start_line": 19
1665
+ },
1666
+ "evidence": [
1667
+ {
1668
+ "file": "perl-cgi/cgi-bin/order.cgi",
1669
+ "start_line": 19,
1670
+ "code": "$dbh->do(\"DELETE FROM carts WHERE cart_id = \" . $q->param('cart'));",
1671
+ "why": "Unsanitized input concatenated into query",
1672
+ "line": 19
1673
+ }
1674
+ ],
1675
+ "severity": "critical",
1676
+ "confidence": "high",
1677
+ "cwe": [
1678
+ "CWE-89"
1679
+ ],
1680
+ "recommendation": "Use parameterized queries",
1681
+ "status": "confirmed",
1682
+ "verification": {
1683
+ "verdict": "confirmed",
1684
+ "reason_code": "traced",
1685
+ "checked": [
1686
+ "perl-cgi/cgi-bin/order.cgi line 19",
1687
+ "perl-cgi/cgi-bin/order.cgi line 18-20"
1688
+ ],
1689
+ "control_found": null,
1690
+ "notes": "Same as INJ-I-0024-2; unsanitized input used in a DELETE statement with no protection.",
1691
+ "suggested_subtype": null,
1692
+ "suggested_cwe": null
1693
+ },
1694
+ "fingerprint": "kfp1:c7206e5510c18d43418b9ed9a4d20047783ea5263cfdc395a138b735b613debc",
1695
+ "baseline_state": "new"
1696
+ },
1697
+ {
1698
+ "finding_id": "INJ-I-0026-1",
1699
+ "category": "sql_injection",
1700
+ "subtype": "string_concatenation",
1701
+ "title": "SQL Injection in php-codeigniter/application/controllers/Invoice.php:10",
1702
+ "candidate_id": "I-0026",
1703
+ "location": {
1704
+ "file": "php-codeigniter/application/controllers/Invoice.php",
1705
+ "start_line": 10
1706
+ },
1707
+ "evidence": [
1708
+ {
1709
+ "file": "php-codeigniter/application/controllers/Invoice.php",
1710
+ "start_line": 9,
1711
+ "code": "$id = $this->uri->segment(3);",
1712
+ "why": "source: URL segment",
1713
+ "line": 9
1714
+ },
1715
+ {
1716
+ "file": "php-codeigniter/application/controllers/Invoice.php",
1717
+ "start_line": 10,
1718
+ "code": "$q = $this->db->query(\"SELECT * FROM invoices WHERE id = $id\");",
1719
+ "why": "unsanitized concatenation",
1720
+ "line": 10
1721
+ }
1722
+ ],
1723
+ "severity": "critical",
1724
+ "confidence": "high",
1725
+ "cwe": [
1726
+ "CWE-89"
1727
+ ],
1728
+ "recommendation": "Use parameterized queries",
1729
+ "status": "confirmed",
1730
+ "verification": {
1731
+ "verdict": "confirmed",
1732
+ "reason_code": "traced",
1733
+ "checked": [
1734
+ "php-codeigniter/application/controllers/Invoice.php:9-10"
1735
+ ],
1736
+ "control_found": null,
1737
+ "notes": "The query concatenates the raw $id parameter without any sanitization, casting, or parameter binding. No ownership or role check is present in the visible code.",
1738
+ "suggested_subtype": null,
1739
+ "suggested_cwe": null
1740
+ },
1741
+ "fingerprint": "kfp1:cab7179a42b52f77a862191152d4b0ebc8511c6c77b956503f7c53525c9390b0",
1742
+ "baseline_state": "new"
1743
+ },
1744
+ {
1745
+ "finding_id": "INJ-I-0028",
1746
+ "category": "sql_injection",
1747
+ "subtype": "string_concatenation",
1748
+ "title": "SQL Injection in php-plain/admin.php:12",
1749
+ "candidate_id": "I-0028",
1750
+ "location": {
1751
+ "file": "php-plain/admin.php",
1752
+ "start_line": 12
1753
+ },
1754
+ "evidence": [
1755
+ {
1756
+ "file": "php-plain/admin.php",
1757
+ "start_line": 12,
1758
+ "code": "mysql_query(\"DELETE FROM users WHERE id = $id\");",
1759
+ "why": "Unsanitized input concatenated into query",
1760
+ "line": 12
1761
+ }
1762
+ ],
1763
+ "severity": "critical",
1764
+ "confidence": "high",
1765
+ "cwe": [
1766
+ "CWE-89"
1767
+ ],
1768
+ "recommendation": "Use parameterized queries",
1769
+ "status": "confirmed",
1770
+ "verification": {
1771
+ "verdict": "confirmed",
1772
+ "reason_code": "traced",
1773
+ "checked": [
1774
+ "php-plain/admin.php line 12",
1775
+ "php-plain/admin.php lines 5-13"
1776
+ ],
1777
+ "control_found": null,
1778
+ "notes": "The DELETE query uses the raw $_POST['id'] value. No cast, parameter binding, or ownership check is present. The included auth.php only verifies login, not admin role.",
1779
+ "suggested_subtype": null,
1780
+ "suggested_cwe": null
1781
+ },
1782
+ "fingerprint": "kfp1:59fca8cd0dedd8f70d286a5d3808292d9eddcf084148a63a448b7043b69c7948",
1783
+ "baseline_state": "new"
1784
+ },
1785
+ {
1786
+ "finding_id": "INJ-I-0029-2",
1787
+ "category": "nosql_injection",
1788
+ "subtype": "operator_injection",
1789
+ "title": "NoSQL Injection in php-plain/mongo_find.php:6",
1790
+ "candidate_id": "I-0029",
1791
+ "location": {
1792
+ "file": "php-plain/mongo_find.php",
1793
+ "start_line": 5
1794
+ },
1795
+ "evidence": [
1796
+ {
1797
+ "file": "php-plain/mongo_find.php",
1798
+ "start_line": 6,
1799
+ "code": "$docs = $coll->find(array('$where' => \"this.name == '\" . $_GET['n'] . \"'\"));",
1800
+ "why": "Unescaped string used in $where expression",
1801
+ "line": 6
1802
+ }
1803
+ ],
1804
+ "severity": "critical",
1805
+ "confidence": "high",
1806
+ "cwe": [
1807
+ "CWE-89"
1808
+ ],
1809
+ "recommendation": "Avoid $where; use parameterized queries or safe operators",
1810
+ "status": "confirmed",
1811
+ "verification": {
1812
+ "verdict": "confirmed",
1813
+ "reason_code": "traced",
1814
+ "checked": [
1815
+ "php-plain/mongo_find.php line 6",
1816
+ "php-plain/mongo_find.php lines 1-6"
1817
+ ],
1818
+ "control_found": null,
1819
+ "notes": "The $where expression concatenates the unsanitized $_GET['n'] value. No sanitization or ownership check is visible.",
1820
+ "suggested_subtype": null,
1821
+ "suggested_cwe": null
1822
+ },
1823
+ "fingerprint": "kfp1:0bebccae3a46f1c54cc666792689e68bb695435e0130148b1e2f6fc35e872ed4",
1824
+ "baseline_state": "new"
1825
+ },
1826
+ {
1827
+ "finding_id": "INJ-I-0030",
1828
+ "category": "nosql_injection",
1829
+ "subtype": "operator_injection",
1830
+ "title": "NoSQL Injection in php-plain/mongo_find.php:6",
1831
+ "candidate_id": "I-0030",
1832
+ "location": {
1833
+ "file": "php-plain/mongo_find.php",
1834
+ "start_line": 6
1835
+ },
1836
+ "evidence": [
1837
+ {
1838
+ "file": "php-plain/mongo_find.php",
1839
+ "start_line": 6,
1840
+ "code": "$docs = $coll->find(array('$where' => \"this.name == '\" . $_GET['n'] . \"'\"));",
1841
+ "why": "Unescaped string used in $where expression",
1842
+ "line": 6
1843
+ }
1844
+ ],
1845
+ "severity": "critical",
1846
+ "confidence": "high",
1847
+ "cwe": [
1848
+ "CWE-89"
1849
+ ],
1850
+ "recommendation": "Avoid $where; use parameterized queries or safe operators",
1851
+ "status": "confirmed",
1852
+ "verification": {
1853
+ "verdict": "confirmed",
1854
+ "reason_code": "traced",
1855
+ "checked": [
1856
+ "php-plain/mongo_find.php",
1857
+ "php-plain/inc/auth.php"
1858
+ ],
1859
+ "control_found": null,
1860
+ "notes": "The query concatenates user input into a $where expression with no sanitisation or ownership check. The included auth file only verifies login, not object ownership.",
1861
+ "suggested_subtype": null,
1862
+ "suggested_cwe": null
1863
+ },
1864
+ "fingerprint": "kfp1:bf0197ac678eaca25ff151d805751c71b878e909d2e7180bacf39dd373b53b09",
1865
+ "baseline_state": "new"
1866
+ },
1867
+ {
1868
+ "finding_id": "INJ-I-0031-1",
1869
+ "category": "sql_injection",
1870
+ "subtype": "string_concatenation",
1871
+ "title": "SQL Injection in php-plain/order_detail.php:6",
1872
+ "candidate_id": "I-0031",
1873
+ "location": {
1874
+ "file": "php-plain/order_detail.php",
1875
+ "start_line": 6
1876
+ },
1877
+ "evidence": [
1878
+ {
1879
+ "file": "php-plain/order_detail.php",
1880
+ "start_line": 5,
1881
+ "code": "$id = $_GET['id'];",
1882
+ "why": "source: GET parameter",
1883
+ "line": 5
1884
+ },
1885
+ {
1886
+ "file": "php-plain/order_detail.php",
1887
+ "start_line": 6,
1888
+ "code": "$res = mysql_query(\"SELECT * FROM orders WHERE id = \" . $id);",
1889
+ "why": "unsanitized input concatenated into query",
1890
+ "line": 6
1891
+ }
1892
+ ],
1893
+ "severity": "critical",
1894
+ "confidence": "high",
1895
+ "cwe": [
1896
+ "CWE-89"
1897
+ ],
1898
+ "recommendation": "Use parameterized queries",
1899
+ "status": "confirmed",
1900
+ "verification": {
1901
+ "verdict": "confirmed",
1902
+ "reason_code": "traced",
1903
+ "checked": [
1904
+ "php-plain/order_detail.php",
1905
+ "php-plain/inc/auth.php"
1906
+ ],
1907
+ "control_found": null,
1908
+ "notes": "The SQL statement concatenates the GET id directly. No ownership or role guard is present in the visible code.",
1909
+ "suggested_subtype": null,
1910
+ "suggested_cwe": null
1911
+ },
1912
+ "fingerprint": "kfp1:3065614ddeff53f64ce73f2bd0210f5b36a2249f4d2561f69219095ad867ac9f",
1913
+ "baseline_state": "new"
1914
+ },
1915
+ {
1916
+ "finding_id": "INJ-I-0032-1",
1917
+ "category": "sql_injection",
1918
+ "subtype": "string_concatenation",
1919
+ "title": "SQL Injection in php-plain/search.php:7",
1920
+ "candidate_id": "I-0032",
1921
+ "location": {
1922
+ "file": "php-plain/search.php",
1923
+ "start_line": 8
1924
+ },
1925
+ "evidence": [
1926
+ {
1927
+ "file": "php-plain/search.php",
1928
+ "start_line": 5,
1929
+ "code": "$kw = addslashes($_GET['q']);",
1930
+ "why": "source: GET parameter",
1931
+ "line": 5
1932
+ },
1933
+ {
1934
+ "file": "php-plain/search.php",
1935
+ "start_line": 6,
1936
+ "code": "$sort = $_GET['sort'];",
1937
+ "why": "source: GET parameter",
1938
+ "line": 6
1939
+ },
1940
+ {
1941
+ "file": "php-plain/search.php",
1942
+ "start_line": 7,
1943
+ "code": "$sql = \"SELECT id, name FROM products WHERE 1=1\";",
1944
+ "why": "query start",
1945
+ "line": 7
1946
+ },
1947
+ {
1948
+ "file": "php-plain/search.php",
1949
+ "start_line": 8,
1950
+ "code": "$sql .= \" AND name LIKE '%\" . $kw . \"%'\";",
1951
+ "why": "unsanitized input concatenated",
1952
+ "line": 8
1953
+ },
1954
+ {
1955
+ "file": "php-plain/search.php",
1956
+ "start_line": 9,
1957
+ "code": "$sql .= \" ORDER BY \" . $sort;",
1958
+ "why": "unsanitized ORDER BY",
1959
+ "line": 9
1960
+ },
1961
+ {
1962
+ "file": "php-plain/search.php",
1963
+ "start_line": 11,
1964
+ "code": "$res = mysql_query($sql);",
1965
+ "why": "sink",
1966
+ "line": 11
1967
+ }
1968
+ ],
1969
+ "severity": "critical",
1970
+ "confidence": "high",
1971
+ "cwe": [
1972
+ "CWE-89"
1973
+ ],
1974
+ "recommendation": "Use parameterized queries and allow-list ORDER BY",
1975
+ "status": "confirmed",
1976
+ "verification": {
1977
+ "verdict": "confirmed",
1978
+ "reason_code": "traced",
1979
+ "checked": [
1980
+ "php-plain/search.php",
1981
+ "php-plain/inc/auth.php",
1982
+ "php-plain/inc/db.php"
1983
+ ],
1984
+ "control_found": null,
1985
+ "notes": "Both the keyword and ORDER BY values are unsanitised and no allow‑list or ownership check is visible.",
1986
+ "suggested_subtype": null,
1987
+ "suggested_cwe": null
1988
+ },
1989
+ "fingerprint": "kfp1:7290ec7c22df15c86f213eb1e27320eaf80f4bafc68a9a6813fb0bd32771b6da",
1990
+ "baseline_state": "new"
1991
+ },
1992
+ {
1993
+ "finding_id": "INJ-I-0032-2",
1994
+ "category": "sql_injection",
1995
+ "subtype": "string_interpolation",
1996
+ "title": "SQL Injection in php-plain/search.php:12",
1997
+ "candidate_id": "I-0032",
1998
+ "location": {
1999
+ "file": "php-plain/search.php",
2000
+ "start_line": 8
2001
+ },
2002
+ "evidence": [
2003
+ {
2004
+ "file": "php-plain/search.php",
2005
+ "start_line": 14,
2006
+ "code": "WHERE category = '{$_GET['cat']}'",
2007
+ "why": "unsanitized interpolation",
2008
+ "line": 14
2009
+ },
2010
+ {
2011
+ "file": "php-plain/search.php",
2012
+ "start_line": 16,
2013
+ "code": "$res2 = mysql_query($q);",
2014
+ "why": "sink",
2015
+ "line": 16
2016
+ }
2017
+ ],
2018
+ "severity": "critical",
2019
+ "confidence": "high",
2020
+ "cwe": [
2021
+ "CWE-89"
2022
+ ],
2023
+ "recommendation": "Use parameterized queries",
2024
+ "status": "confirmed",
2025
+ "verification": {
2026
+ "verdict": "confirmed",
2027
+ "reason_code": "traced",
2028
+ "checked": [
2029
+ "php-plain/search.php",
2030
+ "php-plain/inc/auth.php",
2031
+ "php-plain/inc/db.php"
2032
+ ],
2033
+ "control_found": null,
2034
+ "notes": "The interpolated category value is used directly in the query with no sanitisation or ownership guard.",
2035
+ "suggested_subtype": null,
2036
+ "suggested_cwe": null
2037
+ },
2038
+ "fingerprint": "kfp1:272fb833676c5fa2882f4c605fd61f324c91b78b15356dbb8dd10ae9bbf2934d",
2039
+ "baseline_state": "new"
2040
+ },
2041
+ {
2042
+ "finding_id": "INJ-I-0036-1",
2043
+ "category": "sql_injection",
2044
+ "subtype": "string_interpolation",
2045
+ "title": "SQL Injection in python-flask/app.py:29",
2046
+ "candidate_id": "I-0036",
2047
+ "location": {
2048
+ "file": "python-flask/app.py",
2049
+ "start_line": 29
2050
+ },
2051
+ "evidence": [
2052
+ {
2053
+ "file": "python-flask/app.py",
2054
+ "start_line": 27,
2055
+ "code": "q = request.args.get('q', '')",
2056
+ "why": "source: query parameter",
2057
+ "line": 27
2058
+ },
2059
+ {
2060
+ "file": "python-flask/app.py",
2061
+ "start_line": 29,
2062
+ "code": "cur.execute(f\"SELECT * FROM products WHERE name LIKE '%{q}%'\")",
2063
+ "why": "unsanitized interpolation",
2064
+ "line": 29
2065
+ }
2066
+ ],
2067
+ "severity": "critical",
2068
+ "confidence": "high",
2069
+ "cwe": [
2070
+ "CWE-89"
2071
+ ],
2072
+ "recommendation": "Use parameterized queries",
2073
+ "status": "confirmed",
2074
+ "verification": {
2075
+ "verdict": "confirmed",
2076
+ "reason_code": "traced",
2077
+ "checked": [
2078
+ "python-flask/app.py"
2079
+ ],
2080
+ "control_found": null,
2081
+ "notes": "The f‑string interpolates the query parameter directly into the SQL. The @login_required decorator only ensures authentication, not ownership or parameter validation.",
2082
+ "suggested_subtype": null,
2083
+ "suggested_cwe": null
2084
+ },
2085
+ "fingerprint": "kfp1:52c1a1b2beb4129f6fbbfa5c9bcc2f05de1cb6b4a835dcb6adfa7075c03edf79",
2086
+ "baseline_state": "new"
2087
+ },
2088
+ {
2089
+ "finding_id": "INJ-I-0037-1",
2090
+ "category": "sql_injection",
2091
+ "subtype": "stored_procedure_dynamic_sql",
2092
+ "title": "SQL Injection in sql-procs/sp_search_orders.sql:5",
2093
+ "candidate_id": "I-0037",
2094
+ "location": {
2095
+ "file": "sql-procs/sp_search_orders.sql",
2096
+ "start_line": 5
2097
+ },
2098
+ "evidence": [
2099
+ {
2100
+ "file": "sql-procs/sp_search_orders.sql",
2101
+ "start_line": 5,
2102
+ "code": "SET @sql = 'SELECT * FROM Orders WHERE Customer = ''' + @Customer + ''' ORDER BY ' + @Sort",
2103
+ "why": "unsanitized dynamic SQL",
2104
+ "line": 5
2105
+ },
2106
+ {
2107
+ "file": "sql-procs/sp_search_orders.sql",
2108
+ "start_line": 6,
2109
+ "code": "EXEC(@sql)",
2110
+ "why": "sink",
2111
+ "line": 6
2112
+ }
2113
+ ],
2114
+ "severity": "critical",
2115
+ "confidence": "high",
2116
+ "cwe": [
2117
+ "CWE-89"
2118
+ ],
2119
+ "recommendation": "Use sp_executesql with parameters",
2120
+ "status": "confirmed",
2121
+ "verification": {
2122
+ "verdict": "confirmed",
2123
+ "reason_code": "traced",
2124
+ "checked": [
2125
+ "sql-procs/sp_search_orders.sql:5-6"
2126
+ ],
2127
+ "control_found": null,
2128
+ "notes": "Dynamic SQL is built by concatenating @Customer and @Sort directly into the statement. No allow‑list, integer cast, or parameterized execution is visible.",
2129
+ "suggested_subtype": null,
2130
+ "suggested_cwe": null
2131
+ },
2132
+ "fingerprint": "kfp1:dda6a5bb147b8aa741739e9da8799b0f942492567758d5d3209c1395a97c8f98",
2133
+ "baseline_state": "new"
2134
+ },
2135
+ {
2136
+ "finding_id": "MASS-M-0003-1",
2137
+ "category": "mass_assignment",
2138
+ "subtype": "privileged_field_writable",
2139
+ "title": "Unrestricted role binding in Spring MVC",
2140
+ "candidate_id": "M-0003",
2141
+ "location": {
2142
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2143
+ "start_line": 14
2144
+ },
2145
+ "evidence": [
2146
+ {
2147
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2148
+ "line": 14,
2149
+ "why": "Location taken from the Tier 1 inventory"
2150
+ }
2151
+ ],
2152
+ "impact": "Any authenticated user can set the role field of an Account",
2153
+ "severity": "critical",
2154
+ "confidence": "medium",
2155
+ "cwe": [
2156
+ "CWE-915"
2157
+ ],
2158
+ "recommendation": "Bind only allowed fields or set role server‑side",
2159
+ "status": "likely",
2160
+ "verification": {
2161
+ "verdict": "likely",
2162
+ "reason_code": "code_not_visible",
2163
+ "checked": [
2164
+ "java-legacy/src/com/acme/web/AccountController.java"
2165
+ ],
2166
+ "control_found": null,
2167
+ "notes": "The controller accepts a full Account model via @ModelAttribute and persists it without any field‑level binding or role restrictions shown.",
2168
+ "suggested_subtype": null,
2169
+ "suggested_cwe": null
2170
+ },
2171
+ "fingerprint": "kfp1:b427f067ed1c164d59f88449e3f3d39e10773b28dafaf7db21426e9881df1269",
2172
+ "baseline_state": "new"
2173
+ },
2174
+ {
2175
+ "finding_id": "MASS-M-0004-1",
2176
+ "category": "mass_assignment",
2177
+ "subtype": "privileged_field_writable",
2178
+ "title": "Unrestricted role binding in Spring MVC (duplicate)",
2179
+ "candidate_id": "M-0004",
2180
+ "location": {
2181
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2182
+ "start_line": 27
2183
+ },
2184
+ "evidence": [
2185
+ {
2186
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
2187
+ "line": 27,
2188
+ "why": "Location taken from the Tier 1 inventory"
2189
+ }
2190
+ ],
2191
+ "impact": "Any authenticated user can set the role field of an Account",
2192
+ "severity": "critical",
2193
+ "confidence": "medium",
2194
+ "cwe": [
2195
+ "CWE-915"
2196
+ ],
2197
+ "recommendation": "Bind only allowed fields or set role server‑side",
2198
+ "status": "likely",
2199
+ "verification": {
2200
+ "verdict": "likely",
2201
+ "reason_code": "code_not_visible",
2202
+ "checked": [
2203
+ "AccountController.update method (lines 13-28)",
2204
+ "AccountDao.save invocation"
2205
+ ],
2206
+ "control_found": null,
2207
+ "notes": "No ownership or role check, no allow‑list, and no type cast is visible in the provided code context.",
2208
+ "suggested_subtype": null,
2209
+ "suggested_cwe": null
2210
+ },
2211
+ "fingerprint": "kfp1:d35e421652a66141ead0efd242f840a67488e56a31666c91fce47bd638bb29f9",
2212
+ "baseline_state": "new"
2213
+ },
2214
+ {
2215
+ "finding_id": "MASS-M-0005-1",
2216
+ "category": "mass_assignment",
2217
+ "subtype": "privileged_field_writable",
2218
+ "title": "Unrestricted role binding via BeanUtils in Java Servlet",
2219
+ "candidate_id": "M-0005",
2220
+ "location": {
2221
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
2222
+ "start_line": 12
2223
+ },
2224
+ "evidence": [
2225
+ {
2226
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
2227
+ "line": 12,
2228
+ "why": "BeanUtils.populate binds all request parameters including role"
2229
+ },
2230
+ {
2231
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
2232
+ "line": 10,
2233
+ "why": "no field filtering or role check"
2234
+ }
2235
+ ],
2236
+ "impact": "Any authenticated user can set the role field of an Account",
2237
+ "severity": "critical",
2238
+ "confidence": "medium",
2239
+ "cwe": [
2240
+ "CWE-915"
2241
+ ],
2242
+ "recommendation": "Bind only allowed fields or set role server‑side",
2243
+ "status": "likely",
2244
+ "verification": {
2245
+ "verdict": "likely",
2246
+ "reason_code": "code_not_visible",
2247
+ "checked": [
2248
+ "ProfileServlet.doPost method (lines 9-16)",
2249
+ "BeanUtils.populate usage"
2250
+ ],
2251
+ "control_found": null,
2252
+ "notes": "The code shows direct population of all request parameters into the Account object with no filtering or role enforcement.",
2253
+ "suggested_subtype": null,
2254
+ "suggested_cwe": null
2255
+ },
2256
+ "fingerprint": "kfp1:47cc80b68d7dd6dcc0d605fb83d8a929ebd6a5752da9de89a7b50339f698eaea",
2257
+ "baseline_state": "new"
2258
+ },
2259
+ {
2260
+ "finding_id": "BOLA-R-0001",
2261
+ "category": "bola",
2262
+ "subtype": "missing_ownership_check",
2263
+ "title": "BOLA in ANY /user/edit.do: missing owner verification",
2264
+ "route_id": "R-0001",
2265
+ "endpoint": {
2266
+ "method": "ANY",
2267
+ "path": "/user/edit.do"
2268
+ },
2269
+ "location": {
2270
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
2271
+ "start_line": 10
2272
+ },
2273
+ "evidence": [
2274
+ {
2275
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
2276
+ "start_line": 10,
2277
+ "code": "String userId = request.getParameter(\"user_id\");",
2278
+ "why": "ID from request",
2279
+ "line": 10
2280
+ },
2281
+ {
2282
+ "file": "java-legacy/src/com/acme/struts/EditUserAction.java",
2283
+ "start_line": 13,
2284
+ "code": "PreparedStatement ps = conn.prepareStatement(\"UPDATE users SET email = '\" + email + \"' WHERE id = \" + userId);",
2285
+ "why": "Query does not bind to user_id",
2286
+ "line": 13
2287
+ }
2288
+ ],
2289
+ "severity": "high",
2290
+ "confidence": "high",
2291
+ "cwe": [
2292
+ "CWE-284",
2293
+ "CWE-639"
2294
+ ],
2295
+ "recommendation": "Verify user ownership before update",
2296
+ "status": "confirmed",
2297
+ "verification": {
2298
+ "verdict": "confirmed",
2299
+ "reason_code": "traced",
2300
+ "checked": [
2301
+ "java-legacy/src/com/acme/struts/EditUserAction.java"
2302
+ ],
2303
+ "control_found": null,
2304
+ "notes": "The UPDATE statement uses the user_id from the request without any ownership verification or parameter binding.",
2305
+ "suggested_subtype": null,
2306
+ "suggested_cwe": null
2307
+ },
2308
+ "fingerprint": "kfp1:fd2aa0a34a547de1965d6735e927f0eb2dac3c019cdf1e1d14bdfa9666ae1581",
2309
+ "baseline_state": "new"
2310
+ },
2311
+ {
2312
+ "finding_id": "BOLA-R-0002",
2313
+ "category": "bola",
2314
+ "subtype": "missing_ownership_check",
2315
+ "title": "BOLA in ANY /classic-asp/shop/member.asp?mode=edit: missing owner verification",
2316
+ "route_id": "R-0002",
2317
+ "endpoint": {
2318
+ "method": "ANY",
2319
+ "path": "/classic-asp/shop/member.asp?mode=edit"
2320
+ },
2321
+ "location": {
2322
+ "file": "classic-asp/shop/member.asp",
2323
+ "start_line": 6
2324
+ },
2325
+ "evidence": [
2326
+ {
2327
+ "file": "classic-asp/shop/member.asp",
2328
+ "start_line": 6,
2329
+ "code": "sql = \"UPDATE members SET name = '\" & Replace(Request.Form(\"name\"), \"'\", \"''\") & \"'\"",
2330
+ "why": "ID from request, no ownership check",
2331
+ "line": 6
2332
+ },
2333
+ {
2334
+ "file": "classic-asp/shop/member.asp",
2335
+ "start_line": 7,
2336
+ "code": "sql = sql & \" WHERE member_id = \" & Request.Form(\"member_id\")",
2337
+ "why": "ID from request, no ownership check",
2338
+ "line": 7
2339
+ },
2340
+ {
2341
+ "file": "classic-asp/shop/member.asp",
2342
+ "start_line": 8,
2343
+ "code": "conn.Execute sql",
2344
+ "why": "Executes query without owner check",
2345
+ "line": 8
2346
+ }
2347
+ ],
2348
+ "severity": "high",
2349
+ "confidence": "high",
2350
+ "cwe": [
2351
+ "CWE-284",
2352
+ "CWE-639"
2353
+ ],
2354
+ "recommendation": "Bind query to current session user",
2355
+ "status": "confirmed",
2356
+ "verification": {
2357
+ "verdict": "confirmed",
2358
+ "reason_code": "traced",
2359
+ "checked": [
2360
+ "classic-asp/shop/member.asp lines 6-8",
2361
+ "classic-asp/shop/inc/auth.asp line 2"
2362
+ ],
2363
+ "control_found": null,
2364
+ "notes": "The code updates a member record using the member_id from the request without any ownership verification. The only visible check is authentication in inc/auth.asp, which does not enforce ownership.",
2365
+ "suggested_subtype": null,
2366
+ "suggested_cwe": null
2367
+ },
2368
+ "fingerprint": "kfp1:99af1eeee9810e226e1e9087abed7ad8883a9cb6e96fcb50fdcd281bc652e4bd",
2369
+ "baseline_state": "new"
2370
+ },
2371
+ {
2372
+ "finding_id": "BOLA-R-0003",
2373
+ "category": "bola",
2374
+ "subtype": "missing_ownership_check",
2375
+ "title": "BOLA in ANY /classic-asp/shop/member.asp?mode=delete: missing owner verification",
2376
+ "route_id": "R-0003",
2377
+ "endpoint": {
2378
+ "method": "ANY",
2379
+ "path": "/classic-asp/shop/member.asp?mode=delete"
2380
+ },
2381
+ "location": {
2382
+ "file": "classic-asp/shop/member.asp",
2383
+ "start_line": 9
2384
+ },
2385
+ "evidence": [
2386
+ {
2387
+ "file": "classic-asp/shop/member.asp",
2388
+ "start_line": 9,
2389
+ "code": "conn.Execute \"DELETE FROM members WHERE member_id = \" & Request.QueryString(\"member_id\")",
2390
+ "why": "ID from request, no ownership check",
2391
+ "line": 9
2392
+ }
2393
+ ],
2394
+ "severity": "high",
2395
+ "confidence": "high",
2396
+ "cwe": [
2397
+ "CWE-284",
2398
+ "CWE-639"
2399
+ ],
2400
+ "recommendation": "Bind delete to current session user",
2401
+ "status": "confirmed",
2402
+ "verification": {
2403
+ "verdict": "confirmed",
2404
+ "reason_code": "traced",
2405
+ "checked": [
2406
+ "classic-asp/shop/member.asp line 9",
2407
+ "classic-asp/shop/inc/auth.asp line 2"
2408
+ ],
2409
+ "control_found": null,
2410
+ "notes": "The delete statement uses the member_id from the query string directly. No ownership check is present in the visible code.",
2411
+ "suggested_subtype": null,
2412
+ "suggested_cwe": null
2413
+ },
2414
+ "fingerprint": "kfp1:29fb5a10552560ba47d892b16a1ad7a135ce2f282623ae6cc75f5074f34e83cf",
2415
+ "baseline_state": "new"
2416
+ },
2417
+ {
2418
+ "finding_id": "BOLA-R-0004",
2419
+ "category": "bola",
2420
+ "subtype": "missing_ownership_check",
2421
+ "title": "BOLA in ANY /classic-asp/shop/order.asp: missing owner verification on first query",
2422
+ "route_id": "R-0004",
2423
+ "endpoint": {
2424
+ "method": "ANY",
2425
+ "path": "/classic-asp/shop/order.asp"
2426
+ },
2427
+ "location": {
2428
+ "file": "classic-asp/shop/order.asp",
2429
+ "start_line": 5
2430
+ },
2431
+ "evidence": [
2432
+ {
2433
+ "file": "classic-asp/shop/order.asp",
2434
+ "start_line": 5,
2435
+ "code": "id = Request(\"id\")",
2436
+ "why": "ID from request, no ownership check",
2437
+ "line": 5
2438
+ },
2439
+ {
2440
+ "file": "classic-asp/shop/order.asp",
2441
+ "start_line": 6,
2442
+ "code": "Set rs = conn.Execute(\"SELECT * FROM orders WHERE id = \" & id)",
2443
+ "why": "Query does not bind to user_id",
2444
+ "line": 6
2445
+ }
2446
+ ],
2447
+ "severity": "high",
2448
+ "confidence": "high",
2449
+ "cwe": [
2450
+ "CWE-284",
2451
+ "CWE-639"
2452
+ ],
2453
+ "recommendation": "Bind order query to current session user",
2454
+ "status": "confirmed",
2455
+ "verification": {
2456
+ "verdict": "confirmed",
2457
+ "reason_code": "traced",
2458
+ "checked": [
2459
+ "classic-asp/shop/order.asp lines 5-6",
2460
+ "classic-asp/shop/inc/auth.asp line 2"
2461
+ ],
2462
+ "control_found": null,
2463
+ "notes": "The first query selects an order by id from the request without binding to the current user. The later query includes a user_id check, but the initial query remains vulnerable.",
2464
+ "suggested_subtype": null,
2465
+ "suggested_cwe": null
2466
+ },
2467
+ "fingerprint": "kfp1:e93fc6e82ca183dc55be6fef39c3146001c4dbab631322ba960d00930e254b5d",
2468
+ "baseline_state": "new"
2469
+ },
2470
+ {
2471
+ "finding_id": "BOLA-R-0005",
2472
+ "category": "bola",
2473
+ "subtype": "missing_ownership_check",
2474
+ "title": "BOLA in ANY /coldfusion/app/OrderService.cfc?method=getOrder: missing owner verification",
2475
+ "route_id": "R-0005",
2476
+ "endpoint": {
2477
+ "method": "ANY",
2478
+ "path": "/coldfusion/app/OrderService.cfc?method=getOrder"
2479
+ },
2480
+ "location": {
2481
+ "file": "coldfusion/app/OrderService.cfc",
2482
+ "start_line": 4
2483
+ },
2484
+ "evidence": [
2485
+ {
2486
+ "file": "coldfusion/app/OrderService.cfc",
2487
+ "start_line": 4,
2488
+ "code": "<cfquery name=\"q\" datasource=\"shop\">",
2489
+ "why": "Query starts here",
2490
+ "line": 4
2491
+ },
2492
+ {
2493
+ "file": "coldfusion/app/OrderService.cfc",
2494
+ "start_line": 5,
2495
+ "code": "SELECT * FROM orders WHERE id = #arguments.orderId#",
2496
+ "why": "ID from request, no ownership check",
2497
+ "line": 5
2498
+ }
2499
+ ],
2500
+ "severity": "high",
2501
+ "confidence": "high",
2502
+ "cwe": [
2503
+ "CWE-284",
2504
+ "CWE-639"
2505
+ ],
2506
+ "recommendation": "Bind order query to current session user",
2507
+ "status": "confirmed",
2508
+ "verification": {
2509
+ "verdict": "confirmed",
2510
+ "reason_code": "traced",
2511
+ "checked": [
2512
+ "coldfusion/app/OrderService.cfc lines 4-5"
2513
+ ],
2514
+ "control_found": null,
2515
+ "notes": "The cfquery directly interpolates the orderId argument into the SQL without any ownership verification or parameter binding.",
2516
+ "suggested_subtype": null,
2517
+ "suggested_cwe": null
2518
+ },
2519
+ "fingerprint": "kfp1:c856936fd2eec990ab6cbc31fe4731bcb2077a14890d911f4e4369445335a26d",
2520
+ "baseline_state": "new"
2521
+ },
2522
+ {
2523
+ "finding_id": "BOLA-R-0006",
2524
+ "category": "bola",
2525
+ "subtype": "missing_ownership_check",
2526
+ "title": "BOLA in ANY /coldfusion/app/order.cfm: missing owner verification on first query",
2527
+ "route_id": "R-0006",
2528
+ "endpoint": {
2529
+ "method": "ANY",
2530
+ "path": "/coldfusion/app/order.cfm"
2531
+ },
2532
+ "location": {
2533
+ "file": "coldfusion/app/order.cfm",
2534
+ "start_line": 2
2535
+ },
2536
+ "evidence": [
2537
+ {
2538
+ "file": "coldfusion/app/order.cfm",
2539
+ "start_line": 2,
2540
+ "code": "<cfquery name=\"qOrder\" datasource=\"shop\">",
2541
+ "why": "Query starts here",
2542
+ "line": 2
2543
+ },
2544
+ {
2545
+ "file": "coldfusion/app/order.cfm",
2546
+ "start_line": 3,
2547
+ "code": "SELECT * FROM orders WHERE id = #url.id#",
2548
+ "why": "ID from request, no ownership check",
2549
+ "line": 3
2550
+ }
2551
+ ],
2552
+ "severity": "high",
2553
+ "confidence": "high",
2554
+ "cwe": [
2555
+ "CWE-284",
2556
+ "CWE-639"
2557
+ ],
2558
+ "recommendation": "Bind order query to current session user",
2559
+ "status": "confirmed",
2560
+ "verification": {
2561
+ "verdict": "confirmed",
2562
+ "reason_code": "traced",
2563
+ "checked": [
2564
+ "coldfusion/app/order.cfm lines 2-3",
2565
+ "coldfusion/app/order.cfm lines 5-8"
2566
+ ],
2567
+ "control_found": null,
2568
+ "notes": "The first query qOrder selects an order by id from the URL without checking the current user. The subsequent qMine query includes a user_id check, but the initial query is still exposed.",
2569
+ "suggested_subtype": null,
2570
+ "suggested_cwe": null
2571
+ },
2572
+ "fingerprint": "kfp1:eb3e24aeaf1e9815041914c036da60526aa633ef0d70a5b2fcebfdd4a8b2309b",
2573
+ "baseline_state": "new"
2574
+ },
2575
+ {
2576
+ "finding_id": "BOLA-R-0007",
2577
+ "category": "bola",
2578
+ "subtype": "missing_ownership_check",
2579
+ "title": "BOLA in POST /Admin/DeleteUser: missing owner verification",
2580
+ "route_id": "R-0007",
2581
+ "endpoint": {
2582
+ "method": "POST",
2583
+ "path": "/Admin/DeleteUser"
2584
+ },
2585
+ "location": {
2586
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2587
+ "start_line": 12
2588
+ },
2589
+ "evidence": [
2590
+ {
2591
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2592
+ "start_line": 11,
2593
+ "code": "public ActionResult DeleteUser(int id)",
2594
+ "why": "Method signature",
2595
+ "line": 11
2596
+ },
2597
+ {
2598
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2599
+ "start_line": 12,
2600
+ "code": "var u = db.Users.Find(id);",
2601
+ "why": "Finds user by id from request",
2602
+ "line": 12
2603
+ },
2604
+ {
2605
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2606
+ "start_line": 13,
2607
+ "code": "db.Users.Remove(u);",
2608
+ "why": "Deletes user without ownership check",
2609
+ "line": 13
2610
+ }
2611
+ ],
2612
+ "severity": "high",
2613
+ "confidence": "high",
2614
+ "cwe": [
2615
+ "CWE-284",
2616
+ "CWE-639"
2617
+ ],
2618
+ "recommendation": "Restrict to admin role and verify ownership",
2619
+ "status": "confirmed",
2620
+ "verification": {
2621
+ "verdict": "confirmed",
2622
+ "reason_code": "traced",
2623
+ "checked": [
2624
+ "dotnet-legacy/Web/Controllers/AdminController.cs lines 6-13"
2625
+ ],
2626
+ "control_found": null,
2627
+ "notes": "The DeleteUser action lacks any ownership or role check; the controller is only marked with [Authorize], allowing any authenticated user to delete any user.",
2628
+ "suggested_subtype": null,
2629
+ "suggested_cwe": null
2630
+ },
2631
+ "fingerprint": "kfp1:ff7ce7baa2540985e6cf2198bc15a646ece40603a915b3ccbe7e0fb05ccca5d7",
2632
+ "baseline_state": "new"
2633
+ },
2634
+ {
2635
+ "finding_id": "BOLA-R-0009",
2636
+ "category": "bola",
2637
+ "subtype": "missing_role_check",
2638
+ "title": "BOLA in POST /Admin/Export: missing role check allows data exposure",
2639
+ "route_id": "R-0009",
2640
+ "endpoint": {
2641
+ "method": "POST",
2642
+ "path": "/Admin/Export"
2643
+ },
2644
+ "location": {
2645
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2646
+ "start_line": 30
2647
+ },
2648
+ "evidence": [
2649
+ {
2650
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2651
+ "start_line": 30,
2652
+ "code": "public ActionResult Export()",
2653
+ "why": "Method signature",
2654
+ "line": 30
2655
+ },
2656
+ {
2657
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2658
+ "start_line": 32,
2659
+ "code": "if (Request.QueryString[\"isAdmin\"] == \"true\")",
2660
+ "why": "Checks query string, not role",
2661
+ "line": 32
2662
+ },
2663
+ {
2664
+ "file": "dotnet-legacy/Web/Controllers/AdminController.cs",
2665
+ "start_line": 33,
2666
+ "code": "return Json(db.Users.ToList(), JsonRequestBehavior.AllowGet);",
2667
+ "why": "Returns all users",
2668
+ "line": 33
2669
+ }
2670
+ ],
2671
+ "severity": "high",
2672
+ "confidence": "high",
2673
+ "cwe": [
2674
+ "CWE-284",
2675
+ "CWE-285"
2676
+ ],
2677
+ "recommendation": "Add role-based authorization",
2678
+ "status": "confirmed",
2679
+ "verification": {
2680
+ "verdict": "confirmed",
2681
+ "reason_code": "traced",
2682
+ "checked": [
2683
+ "dotnet-legacy/Web/Controllers/AdminController.cs lines 30-35"
2684
+ ],
2685
+ "control_found": null,
2686
+ "notes": "Export is protected only by class-level [Authorize] and a query‑string flag; no role or ownership verification is performed.",
2687
+ "suggested_subtype": null,
2688
+ "suggested_cwe": null
2689
+ },
2690
+ "fingerprint": "kfp1:c99eea2bd0e04671e882ae366561fdcb05e616e63bc0be8447284701cb748766",
2691
+ "baseline_state": "new"
2692
+ },
2693
+ {
2694
+ "finding_id": "BOLA-R-0010",
2695
+ "category": "bola",
2696
+ "subtype": "missing_ownership_check",
2697
+ "title": "BOLA in POST /Customer/Edit: missing owner verification",
2698
+ "route_id": "R-0010",
2699
+ "endpoint": {
2700
+ "method": "POST",
2701
+ "path": "/Customer/Edit"
2702
+ },
2703
+ "location": {
2704
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2705
+ "start_line": 12
2706
+ },
2707
+ "evidence": [
2708
+ {
2709
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2710
+ "start_line": 14,
2711
+ "code": "db.Entry(customer).State = System.Data.Entity.EntityState.Modified;",
2712
+ "why": "Marks the supplied customer entity as modified without checking that the current user owns it",
2713
+ "line": 14
2714
+ },
2715
+ {
2716
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2717
+ "start_line": 15,
2718
+ "code": "db.SaveChanges();",
2719
+ "why": "Persists the changes, allowing any authenticated user to modify any customer record",
2720
+ "line": 15
2721
+ }
2722
+ ],
2723
+ "severity": "high",
2724
+ "confidence": "medium",
2725
+ "cwe": [
2726
+ "CWE-284",
2727
+ "CWE-639"
2728
+ ],
2729
+ "recommendation": "Load the customer by ID and verify ownership before applying changes",
2730
+ "status": "likely",
2731
+ "verification": {
2732
+ "verdict": "likely",
2733
+ "reason_code": "code_not_visible",
2734
+ "checked": [
2735
+ "dotnet-legacy/Web/Controllers/CustomerController.cs lines 11-15"
2736
+ ],
2737
+ "control_found": null,
2738
+ "notes": "The Edit action marks the supplied Customer entity as Modified and saves it without checking ownership. The controller is decorated with [Authorize], which only ensures authentication, not ownership.",
2739
+ "suggested_subtype": null,
2740
+ "suggested_cwe": null
2741
+ },
2742
+ "fingerprint": "kfp1:2ff50be6396a3261e2543aa3b955d99113acdc748a014446cb0b489407bafe90",
2743
+ "baseline_state": "new"
2744
+ },
2745
+ {
2746
+ "finding_id": "BOLA-R-0011",
2747
+ "category": "bola",
2748
+ "subtype": "missing_ownership_check",
2749
+ "title": "BOLA in POST /Customer/EditName: missing owner verification",
2750
+ "route_id": "R-0011",
2751
+ "endpoint": {
2752
+ "method": "POST",
2753
+ "path": "/Customer/EditName"
2754
+ },
2755
+ "location": {
2756
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2757
+ "start_line": 20
2758
+ },
2759
+ "evidence": [
2760
+ {
2761
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2762
+ "start_line": 22,
2763
+ "code": "db.SaveChanges();",
2764
+ "why": "Saves changes to the customer entity without verifying that the current user owns it",
2765
+ "line": 22
2766
+ }
2767
+ ],
2768
+ "severity": "high",
2769
+ "confidence": "medium",
2770
+ "cwe": [
2771
+ "CWE-284",
2772
+ "CWE-639"
2773
+ ],
2774
+ "recommendation": "Verify ownership before saving changes",
2775
+ "status": "likely",
2776
+ "verification": {
2777
+ "verdict": "likely",
2778
+ "reason_code": "code_not_visible",
2779
+ "checked": [
2780
+ "dotnet-legacy/Web/Controllers/CustomerController.cs lines 20-23"
2781
+ ],
2782
+ "control_found": null,
2783
+ "notes": "EditName receives a Customer with only Id and Name bound, then calls SaveChanges without verifying that the current user owns the customer.",
2784
+ "suggested_subtype": null,
2785
+ "suggested_cwe": null
2786
+ },
2787
+ "fingerprint": "kfp1:f8f8312969722c53dc4fbfcebddf09bfc6ea44e77f308058c6f5a0a79916c6a1",
2788
+ "baseline_state": "new"
2789
+ },
2790
+ {
2791
+ "finding_id": "BOLA-R-0012",
2792
+ "category": "bola",
2793
+ "subtype": "missing_ownership_check",
2794
+ "title": "BOLA in POST /Customer/Refresh: missing owner verification",
2795
+ "route_id": "R-0012",
2796
+ "endpoint": {
2797
+ "method": "POST",
2798
+ "path": "/Customer/Refresh"
2799
+ },
2800
+ "location": {
2801
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2802
+ "start_line": 27
2803
+ },
2804
+ "evidence": [
2805
+ {
2806
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2807
+ "start_line": 29,
2808
+ "code": "var customer = db.Customers.Find(id);",
2809
+ "why": "Loads customer by ID supplied in the request without checking ownership",
2810
+ "line": 29
2811
+ },
2812
+ {
2813
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2814
+ "start_line": 30,
2815
+ "code": "TryUpdateModel(customer);",
2816
+ "why": "Updates the customer entity without verifying that the current user owns it",
2817
+ "line": 30
2818
+ },
2819
+ {
2820
+ "file": "dotnet-legacy/Web/Controllers/CustomerController.cs",
2821
+ "start_line": 31,
2822
+ "code": "db.SaveChanges();",
2823
+ "why": "Persists the changes, allowing any authenticated user to modify any customer record",
2824
+ "line": 31
2825
+ }
2826
+ ],
2827
+ "severity": "high",
2828
+ "confidence": "medium",
2829
+ "cwe": [
2830
+ "CWE-284",
2831
+ "CWE-639"
2832
+ ],
2833
+ "recommendation": "Load the customer and verify ownership before updating",
2834
+ "status": "likely",
2835
+ "verification": {
2836
+ "verdict": "likely",
2837
+ "reason_code": "code_not_visible",
2838
+ "checked": [
2839
+ "dotnet-legacy/Web/Controllers/CustomerController.cs lines 27-32"
2840
+ ],
2841
+ "control_found": null,
2842
+ "notes": "Refresh loads a Customer by id and applies TryUpdateModel before saving, with no ownership check. The [Authorize] attribute only guarantees authentication.",
2843
+ "suggested_subtype": null,
2844
+ "suggested_cwe": null
2845
+ },
2846
+ "fingerprint": "kfp1:642d9e00b1146b80dc8724396082e7bf839dc74fdcc9224c6452557ed7b19ab7",
2847
+ "baseline_state": "new"
2848
+ },
2849
+ {
2850
+ "finding_id": "BOLA-R-0013",
2851
+ "category": "bola",
2852
+ "subtype": "missing_ownership_check",
2853
+ "title": "BOLA in GET /Invoice/Details: missing owner verification",
2854
+ "route_id": "R-0013",
2855
+ "endpoint": {
2856
+ "method": "ANY",
2857
+ "path": "/Invoice/Details"
2858
+ },
2859
+ "location": {
2860
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
2861
+ "start_line": 11
2862
+ },
2863
+ "evidence": [
2864
+ {
2865
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
2866
+ "start_line": 11,
2867
+ "code": "public ActionResult Details(int id)",
2868
+ "why": "Method signature",
2869
+ "line": 11
2870
+ },
2871
+ {
2872
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
2873
+ "start_line": 13,
2874
+ "code": "var inv = db.Invoices.Find(id);",
2875
+ "why": "Finds invoice by id from request",
2876
+ "line": 13
2877
+ },
2878
+ {
2879
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
2880
+ "start_line": 14,
2881
+ "code": "return View(inv);",
2882
+ "why": "Returns invoice without ownership check",
2883
+ "line": 14
2884
+ }
2885
+ ],
2886
+ "severity": "high",
2887
+ "confidence": "high",
2888
+ "cwe": [
2889
+ "CWE-284",
2890
+ "CWE-639"
2891
+ ],
2892
+ "recommendation": "Verify invoice ownership",
2893
+ "status": "confirmed",
2894
+ "verification": {
2895
+ "verdict": "confirmed",
2896
+ "reason_code": "traced",
2897
+ "checked": [
2898
+ "dotnet-legacy/Web/Controllers/InvoiceController.cs lines 6-14"
2899
+ ],
2900
+ "control_found": null,
2901
+ "notes": "Details retrieves an invoice by id without checking that the invoice belongs to the current user.",
2902
+ "suggested_subtype": null,
2903
+ "suggested_cwe": null
2904
+ },
2905
+ "fingerprint": "kfp1:a63c895e83988c707151981826dda723122bb41598834dcfa82591301c26b773",
2906
+ "baseline_state": "new"
2907
+ },
2908
+ {
2909
+ "finding_id": "BOLA-R-0015",
2910
+ "category": "bola",
2911
+ "subtype": "missing_ownership_check",
2912
+ "title": "BOLA in POST /Invoice/Search: missing owner verification",
2913
+ "route_id": "R-0015",
2914
+ "endpoint": {
2915
+ "method": "POST",
2916
+ "path": "/Invoice/Search"
2917
+ },
2918
+ "location": {
2919
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
2920
+ "start_line": 26
2921
+ },
2922
+ "evidence": [
2923
+ {
2924
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
2925
+ "start_line": 26,
2926
+ "code": "public ActionResult Search(string customer)",
2927
+ "why": "Method signature",
2928
+ "line": 26
2929
+ },
2930
+ {
2931
+ "file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
2932
+ "start_line": 28,
2933
+ "code": "var list = db.Database.SqlQuery<Invoice>(\"SELECT * FROM Invoices WHERE Customer = '\" + customer + \"'\").ToList();",
2934
+ "why": "Raw query uses customer from request, no ownership check",
2935
+ "line": 28
2936
+ }
2937
+ ],
2938
+ "severity": "high",
2939
+ "confidence": "high",
2940
+ "cwe": [
2941
+ "CWE-284",
2942
+ "CWE-639"
2943
+ ],
2944
+ "recommendation": "Add ownership filter to query",
2945
+ "status": "confirmed",
2946
+ "verification": {
2947
+ "verdict": "confirmed",
2948
+ "reason_code": "traced",
2949
+ "checked": [
2950
+ "dotnet-legacy/Web/Controllers/InvoiceController.cs lines 25-29"
2951
+ ],
2952
+ "control_found": null,
2953
+ "notes": "Search builds a raw SQL query from user input and returns all matching invoices, with no ownership filter.",
2954
+ "suggested_subtype": null,
2955
+ "suggested_cwe": null
2956
+ },
2957
+ "fingerprint": "kfp1:58c2c056d7e71edc7c9962f37053cd74e5daff6f95dd7fc443c4dd44338ad450",
2958
+ "baseline_state": "new"
2959
+ },
2960
+ {
2961
+ "finding_id": "BOLA-R-0016",
2962
+ "category": "bola",
2963
+ "subtype": "missing_ownership_check",
2964
+ "title": "BOLA in ANY /dotnet-legacy/Web/Handlers/GetFile.ashx: missing owner verification",
2965
+ "route_id": "R-0016",
2966
+ "endpoint": {
2967
+ "method": "ANY",
2968
+ "path": "/dotnet-legacy/Web/Handlers/GetFile.ashx"
2969
+ },
2970
+ "location": {
2971
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2972
+ "start_line": 8
2973
+ },
2974
+ "evidence": [
2975
+ {
2976
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2977
+ "start_line": 8,
2978
+ "code": "string fileId = context.Request.QueryString[\"file_id\"];",
2979
+ "why": "Takes file identifier directly from the request",
2980
+ "line": 8
2981
+ },
2982
+ {
2983
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2984
+ "start_line": 9,
2985
+ "code": "string path = DocStore.PathFor(fileId);",
2986
+ "why": "Builds file path without checking that the current user owns the file",
2987
+ "line": 9
2988
+ },
2989
+ {
2990
+ "file": "dotnet-legacy/Web/Handlers/GetFile.ashx",
2991
+ "start_line": 10,
2992
+ "code": "context.Response.TransmitFile(path);",
2993
+ "why": "Serves the file to the requester, enabling download of any file",
2994
+ "line": 10
2995
+ }
2996
+ ],
2997
+ "severity": "high",
2998
+ "confidence": "high",
2999
+ "cwe": [
3000
+ "CWE-284",
3001
+ "CWE-639"
3002
+ ],
3003
+ "recommendation": "Verify that the current user owns the requested file before transmitting",
3004
+ "status": "confirmed",
3005
+ "verification": {
3006
+ "verdict": "confirmed",
3007
+ "reason_code": "traced",
3008
+ "checked": [
3009
+ "dotnet-legacy/Web/Handlers/GetFile.ashx:8-10"
3010
+ ],
3011
+ "control_found": null,
3012
+ "notes": "The handler reads the file identifier directly from the query string, builds a path without any ownership verification, and transmits the file. No ownership or tenant check is present in the visible code.",
3013
+ "suggested_subtype": null,
3014
+ "suggested_cwe": null
3015
+ },
3016
+ "fingerprint": "kfp1:d642a15232edeffab2a15511431577f3f9ce893382573e1c45e203eec7bb0cad",
3017
+ "baseline_state": "new"
3018
+ },
3019
+ {
3020
+ "finding_id": "BOLA-R-0017",
3021
+ "category": "bola",
3022
+ "subtype": "missing_ownership_check",
3023
+ "title": "BOLA in ANY /dotnet-legacy/Web/Orders/EditOrder.aspx.vb: missing owner verification",
3024
+ "route_id": "R-0017",
3025
+ "endpoint": {
3026
+ "method": "ANY",
3027
+ "path": "/dotnet-legacy/Web/Orders/EditOrder.aspx"
3028
+ },
3029
+ "location": {
3030
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
3031
+ "start_line": 7
3032
+ },
3033
+ "evidence": [
3034
+ {
3035
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
3036
+ "start_line": 7,
3037
+ "code": "Dim sql As String = \"UPDATE Orders SET Note = '\" & txtNote.Text & \"'\"",
3038
+ "why": "SQL built from request data",
3039
+ "line": 7
3040
+ },
3041
+ {
3042
+ "file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
3043
+ "start_line": 8,
3044
+ "code": "sql &= \" WHERE OrderId = \" & hfOrderId.Value",
3045
+ "why": "OrderId from hidden field, no ownership check",
3046
+ "line": 8
3047
+ }
3048
+ ],
3049
+ "severity": "high",
3050
+ "confidence": "high",
3051
+ "cwe": [
3052
+ "CWE-284",
3053
+ "CWE-639"
3054
+ ],
3055
+ "recommendation": "Verify order ownership before update",
3056
+ "status": "confirmed",
3057
+ "verification": {
3058
+ "verdict": "confirmed",
3059
+ "reason_code": "traced",
3060
+ "checked": [
3061
+ "dotnet-legacy/Web/Orders/EditOrder.aspx.vb lines 6-13"
3062
+ ],
3063
+ "control_found": null,
3064
+ "notes": "The update statement uses the OrderId from a hidden field without verifying that the current user owns the order.",
3065
+ "suggested_subtype": null,
3066
+ "suggested_cwe": null
3067
+ },
3068
+ "fingerprint": "kfp1:2ea5e8435292a4d79e53292c88e5a3a1b4a20a146f32ecd3b312dc2abc60a5f2",
3069
+ "baseline_state": "new"
3070
+ },
3071
+ {
3072
+ "finding_id": "BOLA-R-0018",
3073
+ "category": "bola",
3074
+ "subtype": "missing_ownership_check",
3075
+ "title": "BOLA in ANY /dotnet-legacy/Web/Orders/OrderDetail.aspx.cs: missing owner verification",
3076
+ "route_id": "R-0018",
3077
+ "endpoint": {
3078
+ "method": "ANY",
3079
+ "path": "/dotnet-legacy/Web/Orders/OrderDetail.aspx"
3080
+ },
3081
+ "location": {
3082
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
3083
+ "start_line": 10
3084
+ },
3085
+ "evidence": [
3086
+ {
3087
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
3088
+ "start_line": 10,
3089
+ "code": "string id = Request.QueryString[\"id\"];",
3090
+ "why": "ID from request",
3091
+ "line": 10
3092
+ },
3093
+ {
3094
+ "file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
3095
+ "start_line": 13,
3096
+ "code": "var cmd = new SqlCommand(\"SELECT * FROM Orders WHERE OrderId = \" + id, conn);",
3097
+ "why": "Query does not bind to user_id",
3098
+ "line": 13
3099
+ }
3100
+ ],
3101
+ "severity": "high",
3102
+ "confidence": "high",
3103
+ "cwe": [
3104
+ "CWE-284",
3105
+ "CWE-639"
3106
+ ],
3107
+ "recommendation": "Bind order query to current session user",
3108
+ "status": "confirmed",
3109
+ "verification": {
3110
+ "verdict": "confirmed",
3111
+ "reason_code": "traced",
3112
+ "checked": [
3113
+ "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs"
3114
+ ],
3115
+ "control_found": null,
3116
+ "notes": "The unsafe query at line 13 is executed before any ownership check or parameter binding. No role or ownership guard is visible in the provided code.",
3117
+ "suggested_subtype": null,
3118
+ "suggested_cwe": null
3119
+ },
3120
+ "fingerprint": "kfp1:03c16bbd7e443e60e4ed1827e0adb8534c92aaa782468773c94fb64c74f21119",
3121
+ "baseline_state": "new"
3122
+ },
3123
+ {
3124
+ "finding_id": "BOLA-R-0019",
3125
+ "category": "bola",
3126
+ "subtype": "missing_ownership_check",
3127
+ "title": "BOLA in ANY /java-legacy/WebContent/order/detail.jsp: missing owner verification",
3128
+ "route_id": "R-0019",
3129
+ "endpoint": {
3130
+ "method": "ANY",
3131
+ "path": "/java-legacy/WebContent/order/detail.jsp"
3132
+ },
3133
+ "location": {
3134
+ "file": "java-legacy/WebContent/order/detail.jsp",
3135
+ "start_line": 3
3136
+ },
3137
+ "evidence": [
3138
+ {
3139
+ "file": "java-legacy/WebContent/order/detail.jsp",
3140
+ "start_line": 3,
3141
+ "code": "String no = request.getParameter(\"no\");",
3142
+ "why": "ID from request",
3143
+ "line": 3
3144
+ },
3145
+ {
3146
+ "file": "java-legacy/WebContent/order/detail.jsp",
3147
+ "start_line": 5,
3148
+ "code": "ResultSet rs = c.createStatement().executeQuery(\"SELECT * FROM orders WHERE order_no = '\" + no + \"'\");",
3149
+ "why": "Query does not bind to user_id",
3150
+ "line": 5
3151
+ }
3152
+ ],
3153
+ "severity": "high",
3154
+ "confidence": "high",
3155
+ "cwe": [
3156
+ "CWE-284",
3157
+ "CWE-639"
3158
+ ],
3159
+ "recommendation": "Bind order query to current session user",
3160
+ "status": "confirmed",
3161
+ "verification": {
3162
+ "verdict": "confirmed",
3163
+ "reason_code": "traced",
3164
+ "checked": [
3165
+ "java-legacy/WebContent/order/detail.jsp"
3166
+ ],
3167
+ "control_found": null,
3168
+ "notes": "The query concatenates the request parameter directly into the SQL string with no ownership check or parameter binding.",
3169
+ "suggested_subtype": null,
3170
+ "suggested_cwe": null
3171
+ },
3172
+ "fingerprint": "kfp1:ec652d51f2910465a7f501f44711a7f6e698bf5baa030a30a23b1461eab30b63",
3173
+ "baseline_state": "new"
3174
+ },
3175
+ {
3176
+ "finding_id": "BOLA-R-0020",
3177
+ "category": "bola",
3178
+ "subtype": "missing_ownership_check",
3179
+ "title": "BOLA in POST /account/save: missing owner verification",
3180
+ "route_id": "R-0020",
3181
+ "endpoint": {
3182
+ "method": "POST",
3183
+ "path": "/account/save"
3184
+ },
3185
+ "location": {
3186
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3187
+ "start_line": 14
3188
+ },
3189
+ "evidence": [
3190
+ {
3191
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3192
+ "start_line": 14,
3193
+ "code": "public String save(@ModelAttribute Account account) {",
3194
+ "why": "Account object is bound directly from the request; attacker can set any id",
3195
+ "line": 14
3196
+ },
3197
+ {
3198
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3199
+ "start_line": 15,
3200
+ "code": " accountDao.save(account);",
3201
+ "why": "No check that the account belongs to the current user",
3202
+ "line": 15
3203
+ }
3204
+ ],
3205
+ "severity": "high",
3206
+ "confidence": "high",
3207
+ "cwe": [
3208
+ "CWE-284",
3209
+ "CWE-639"
3210
+ ],
3211
+ "recommendation": "Bind the save operation to the authenticated user",
3212
+ "status": "confirmed",
3213
+ "verification": {
3214
+ "verdict": "confirmed",
3215
+ "reason_code": "traced",
3216
+ "checked": [
3217
+ "java-legacy/src/com/acme/web/AccountController.java"
3218
+ ],
3219
+ "control_found": null,
3220
+ "notes": "The Account object is bound directly from the request and passed to the DAO without any ownership check.",
3221
+ "suggested_subtype": null,
3222
+ "suggested_cwe": null
3223
+ },
3224
+ "fingerprint": "kfp1:9000c4fa2f6055d14cf75d1662d403303b29e4f9180300dab9b97c58273fd6ea",
3225
+ "baseline_state": "new"
3226
+ },
3227
+ {
3228
+ "finding_id": "BOLA-R-0021",
3229
+ "category": "bola",
3230
+ "subtype": "missing_ownership_check",
3231
+ "title": "BOLA in POST /account/rename: missing owner verification",
3232
+ "route_id": "R-0021",
3233
+ "endpoint": {
3234
+ "method": "POST",
3235
+ "path": "/account/rename"
3236
+ },
3237
+ "location": {
3238
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3239
+ "start_line": 20
3240
+ },
3241
+ "evidence": [
3242
+ {
3243
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3244
+ "start_line": 20,
3245
+ "code": "public String rename(@ModelAttribute RenameForm form) {",
3246
+ "why": "Form contains an id that can be set by the attacker",
3247
+ "line": 20
3248
+ },
3249
+ {
3250
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3251
+ "start_line": 21,
3252
+ "code": " accountDao.rename(form.getId(), form.getName());",
3253
+ "why": "No check that the id belongs to the current user",
3254
+ "line": 21
3255
+ }
3256
+ ],
3257
+ "severity": "high",
3258
+ "confidence": "high",
3259
+ "cwe": [
3260
+ "CWE-284",
3261
+ "CWE-639"
3262
+ ],
3263
+ "recommendation": "Verify ownership before renaming",
3264
+ "status": "confirmed",
3265
+ "verification": {
3266
+ "verdict": "confirmed",
3267
+ "reason_code": "traced",
3268
+ "checked": [
3269
+ "java-legacy/src/com/acme/web/AccountController.java"
3270
+ ],
3271
+ "control_found": null,
3272
+ "notes": "The RenameForm contains an id that is used directly in the DAO call with no ownership verification.",
3273
+ "suggested_subtype": null,
3274
+ "suggested_cwe": null
3275
+ },
3276
+ "fingerprint": "kfp1:146872d643de658dcd96d7bbf5fe56300d60ee7171d9d0dd7e00305b65d20277",
3277
+ "baseline_state": "new"
3278
+ },
3279
+ {
3280
+ "finding_id": "BOLA-R-0022",
3281
+ "category": "bola",
3282
+ "subtype": "missing_ownership_check",
3283
+ "title": "BOLA in PUT /account/api: missing owner verification",
3284
+ "route_id": "R-0022",
3285
+ "endpoint": {
3286
+ "method": "PUT",
3287
+ "path": "/account/api"
3288
+ },
3289
+ "location": {
3290
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3291
+ "start_line": 27
3292
+ },
3293
+ "evidence": [
3294
+ {
3295
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3296
+ "start_line": 27,
3297
+ "code": "public Account update(@RequestBody Account account) {",
3298
+ "why": "Account object is deserialized from the request; attacker can set any id",
3299
+ "line": 27
3300
+ },
3301
+ {
3302
+ "file": "java-legacy/src/com/acme/web/AccountController.java",
3303
+ "start_line": 28,
3304
+ "code": " return accountDao.save(account);",
3305
+ "why": "No ownership check before saving",
3306
+ "line": 28
3307
+ }
3308
+ ],
3309
+ "severity": "high",
3310
+ "confidence": "medium",
3311
+ "cwe": [
3312
+ "CWE-284",
3313
+ "CWE-639"
3314
+ ],
3315
+ "recommendation": "Bind update to the authenticated user",
3316
+ "status": "likely",
3317
+ "verification": {
3318
+ "verdict": "likely",
3319
+ "reason_code": "code_not_visible",
3320
+ "checked": [
3321
+ "java-legacy/src/com/acme/web/AccountController.java:25-28"
3322
+ ],
3323
+ "control_found": null,
3324
+ "notes": "The update method deserializes an Account object and directly passes it to accountDao.save without any ownership or role check visible in the provided code. No global filter or cast is shown.",
3325
+ "suggested_subtype": null,
3326
+ "suggested_cwe": null
3327
+ },
3328
+ "fingerprint": "kfp1:667b1fe358bedebcb8ba083eadd120bf13739387ae2c54e773f8b5bb8c0943ad",
3329
+ "baseline_state": "new"
3330
+ },
3331
+ {
3332
+ "finding_id": "BOLA-R-0023",
3333
+ "category": "bola",
3334
+ "subtype": "missing_ownership_check",
3335
+ "title": "BOLA in POST /admin/users/{id}/delete: missing owner verification",
3336
+ "route_id": "R-0023",
3337
+ "endpoint": {
3338
+ "method": "POST",
3339
+ "path": "/admin/users/{id}/delete"
3340
+ },
3341
+ "location": {
3342
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
3343
+ "start_line": 13
3344
+ },
3345
+ "evidence": [
3346
+ {
3347
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
3348
+ "start_line": 13,
3349
+ "code": "@RequestMapping(value = \"/{id}/delete\", method = RequestMethod.POST)",
3350
+ "why": "Endpoint accepts any user id",
3351
+ "line": 13
3352
+ },
3353
+ {
3354
+ "file": "java-legacy/src/com/acme/web/AdminUserController.java",
3355
+ "start_line": 15,
3356
+ "code": " userDao.deleteById(id);",
3357
+ "why": "No authentication or role check; any logged‑in user can delete any account",
3358
+ "line": 15
3359
+ }
3360
+ ],
3361
+ "severity": "high",
3362
+ "confidence": "medium",
3363
+ "cwe": [
3364
+ "CWE-284",
3365
+ "CWE-639"
3366
+ ],
3367
+ "recommendation": "Require admin role before deleting a user",
3368
+ "status": "likely",
3369
+ "verification": {
3370
+ "verdict": "likely",
3371
+ "reason_code": "code_not_visible",
3372
+ "checked": [
3373
+ "java-legacy/src/com/acme/web/AdminUserController.java:13-15"
3374
+ ],
3375
+ "control_found": null,
3376
+ "notes": "The delete method accepts any user id and calls userDao.deleteById without a role or ownership guard. The @PreAuthorize annotation is present only on a different method.",
3377
+ "suggested_subtype": null,
3378
+ "suggested_cwe": null
3379
+ },
3380
+ "fingerprint": "kfp1:a4f576c3ecb678956e186f0ee7498af55e42a7b8632ed464b8eada05c59007b4",
3381
+ "baseline_state": "new"
3382
+ },
3383
+ {
3384
+ "finding_id": "BOLA-R-0025",
3385
+ "category": "bola",
3386
+ "subtype": "missing_ownership_check",
3387
+ "title": "BOLA in GET /invoices/{id}: missing owner verification",
3388
+ "route_id": "R-0025",
3389
+ "endpoint": {
3390
+ "method": "GET",
3391
+ "path": "/invoices/{id}"
3392
+ },
3393
+ "location": {
3394
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
3395
+ "start_line": 15
3396
+ },
3397
+ "evidence": [
3398
+ {
3399
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
3400
+ "start_line": 15,
3401
+ "code": "@RequestMapping(value = \"/{id}\", method = RequestMethod.GET)",
3402
+ "why": "Endpoint accepts any invoice id",
3403
+ "line": 15
3404
+ },
3405
+ {
3406
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
3407
+ "start_line": 17,
3408
+ "code": " model.addAttribute(\"invoice\", invoiceDao.findById(id));",
3409
+ "why": "No check that the invoice belongs to the current user",
3410
+ "line": 17
3411
+ }
3412
+ ],
3413
+ "severity": "high",
3414
+ "confidence": "medium",
3415
+ "cwe": [
3416
+ "CWE-284",
3417
+ "CWE-639"
3418
+ ],
3419
+ "recommendation": "Bind invoice lookup to the authenticated user",
3420
+ "status": "likely",
3421
+ "verification": {
3422
+ "verdict": "likely",
3423
+ "reason_code": "code_not_visible",
3424
+ "checked": [
3425
+ "java-legacy/src/com/acme/web/InvoiceController.java:15-17"
3426
+ ],
3427
+ "control_found": null,
3428
+ "notes": "The show method retrieves an invoice by id without verifying that the invoice belongs to the current user. No ownership check or role guard is visible.",
3429
+ "suggested_subtype": null,
3430
+ "suggested_cwe": null
3431
+ },
3432
+ "fingerprint": "kfp1:4213dd0e852a3473b5f77a0dd8c52fbbb9776802a6d4475b6edad70be330df73",
3433
+ "baseline_state": "new"
3434
+ },
3435
+ {
3436
+ "finding_id": "BOLA-R-0027",
3437
+ "category": "bola",
3438
+ "subtype": "missing_ownership_check",
3439
+ "title": "BOLA in GET /invoices/search: missing owner verification",
3440
+ "route_id": "R-0027",
3441
+ "endpoint": {
3442
+ "method": "GET",
3443
+ "path": "/invoices/search"
3444
+ },
3445
+ "location": {
3446
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
3447
+ "start_line": 27
3448
+ },
3449
+ "evidence": [
3450
+ {
3451
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
3452
+ "start_line": 27,
3453
+ "code": "@RequestMapping(value = \"/search\", method = RequestMethod.GET)",
3454
+ "why": "Endpoint accepts any customer string",
3455
+ "line": 27
3456
+ },
3457
+ {
3458
+ "file": "java-legacy/src/com/acme/web/InvoiceController.java",
3459
+ "start_line": 29,
3460
+ "code": " model.addAttribute(\"list\", session.createQuery(\"from Invoice i where i.customer = '\" + customer + \"'\").list());",
3461
+ "why": "Query is built from user input without binding to current user",
3462
+ "line": 29
3463
+ }
3464
+ ],
3465
+ "severity": "high",
3466
+ "confidence": "medium",
3467
+ "cwe": [
3468
+ "CWE-284",
3469
+ "CWE-639"
3470
+ ],
3471
+ "recommendation": "Restrict search to invoices owned by the current user",
3472
+ "status": "likely",
3473
+ "verification": {
3474
+ "verdict": "likely",
3475
+ "reason_code": "code_not_visible",
3476
+ "checked": [
3477
+ "java-legacy/src/com/acme/web/InvoiceController.java:27-29"
3478
+ ],
3479
+ "control_found": null,
3480
+ "notes": "The search method builds a query from a customer string without binding to the current user or applying an allow‑list. No ownership or role check is shown.",
3481
+ "suggested_subtype": null,
3482
+ "suggested_cwe": null
3483
+ },
3484
+ "fingerprint": "kfp1:b576c39e6469454d66f748f828a753ff3f9f250c5538a9c88b4a57760334d368",
3485
+ "baseline_state": "new"
3486
+ },
3487
+ {
3488
+ "finding_id": "BOLA-R-0028",
3489
+ "category": "bola",
3490
+ "subtype": "missing_ownership_check",
3491
+ "title": "BOLA in GET /order/view: missing owner verification",
3492
+ "route_id": "R-0028",
3493
+ "endpoint": {
3494
+ "method": "GET",
3495
+ "path": "/order/view"
3496
+ },
3497
+ "location": {
3498
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
3499
+ "start_line": 11
3500
+ },
3501
+ "evidence": [
3502
+ {
3503
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
3504
+ "start_line": 11,
3505
+ "code": "String id = request.getParameter(\"orderId\");",
3506
+ "why": "Order id comes directly from the request",
3507
+ "line": 11
3508
+ },
3509
+ {
3510
+ "file": "java-legacy/src/com/acme/web/OrderServlet.java",
3511
+ "start_line": 15,
3512
+ "code": "ResultSet rs = stmt.executeQuery(\"SELECT * FROM orders WHERE id = '\" + id + \"'\");",
3513
+ "why": "No check that the order belongs to the current user",
3514
+ "line": 15
3515
+ }
3516
+ ],
3517
+ "severity": "high",
3518
+ "confidence": "medium",
3519
+ "cwe": [
3520
+ "CWE-284",
3521
+ "CWE-639"
3522
+ ],
3523
+ "recommendation": "Bind order lookup to the authenticated user",
3524
+ "status": "likely",
3525
+ "verification": {
3526
+ "verdict": "likely",
3527
+ "reason_code": "code_not_visible",
3528
+ "checked": [
3529
+ "java-legacy/src/com/acme/web/OrderServlet.java:11-15"
3530
+ ],
3531
+ "control_found": null,
3532
+ "notes": "The servlet retrieves an order by id directly from the request and queries the database without verifying ownership. No role or ownership guard is visible in the provided snippet.",
3533
+ "suggested_subtype": null,
3534
+ "suggested_cwe": null
3535
+ },
3536
+ "fingerprint": "kfp1:a589b3cdcfea2b9cc2c5190ffe4a0c2989e46fdd4d15b59781c1b74e3c554524",
3537
+ "baseline_state": "new"
3538
+ },
3539
+ {
3540
+ "finding_id": "BOLA-R-0029",
3541
+ "category": "bola",
3542
+ "subtype": "missing_ownership_check",
3543
+ "title": "BOLA in POST /ProfileServlet: missing owner verification",
3544
+ "route_id": "R-0029",
3545
+ "endpoint": {
3546
+ "method": "POST",
3547
+ "path": "<see web.xml servlet-mapping>"
3548
+ },
3549
+ "location": {
3550
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
3551
+ "start_line": 9
3552
+ },
3553
+ "evidence": [
3554
+ {
3555
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
3556
+ "start_line": 12,
3557
+ "code": "BeanUtils.populate(account, request.getParameterMap());",
3558
+ "why": "Populates an Account object with all request parameters without checking ownership",
3559
+ "line": 12
3560
+ },
3561
+ {
3562
+ "file": "java-legacy/src/com/acme/web/ProfileServlet.java",
3563
+ "start_line": 13,
3564
+ "code": "/* no further processing shown */",
3565
+ "why": "No authentication or ownership check is performed before persisting the account",
3566
+ "line": 13
3567
+ }
3568
+ ],
3569
+ "severity": "high",
3570
+ "confidence": "high",
3571
+ "cwe": [
3572
+ "CWE-284",
3573
+ "CWE-639"
3574
+ ],
3575
+ "recommendation": "Load the account by ID and verify that the current user owns it before updating",
3576
+ "status": "confirmed",
3577
+ "verification": {
3578
+ "verdict": "confirmed",
3579
+ "reason_code": "traced",
3580
+ "checked": [
3581
+ "java-legacy/src/com/acme/web/ProfileServlet.java:12-13"
3582
+ ],
3583
+ "control_found": null,
3584
+ "notes": "The servlet populates an Account object with all request parameters and does not perform any ownership check before persisting. No compensating control is visible.",
3585
+ "suggested_subtype": null,
3586
+ "suggested_cwe": null
3587
+ },
3588
+ "fingerprint": "kfp1:f8c53a1c11f4e356cd58bf22809c720589dfa46c38243f129f05d96341d8a333",
3589
+ "baseline_state": "new"
3590
+ },
3591
+ {
3592
+ "finding_id": "BOLA-R-0030",
3593
+ "category": "bola",
3594
+ "subtype": "missing_ownership_check",
3595
+ "title": "BOLA in POST /admin/users/:id/delete: missing owner verification",
3596
+ "route_id": "R-0030",
3597
+ "endpoint": {
3598
+ "method": "POST",
3599
+ "path": "/admin/users/:id/delete"
3600
+ },
3601
+ "location": {
3602
+ "file": "node-express/routes/admin.js",
3603
+ "start_line": 8
3604
+ },
3605
+ "evidence": [
3606
+ {
3607
+ "file": "node-express/routes/admin.js",
3608
+ "start_line": 8,
3609
+ "code": "router.post('/admin/users/:id/delete', ensureLoggedIn, function (req, res) {",
3610
+ "why": "Endpoint accepts any user id",
3611
+ "line": 8
3612
+ },
3613
+ {
3614
+ "file": "node-express/routes/admin.js",
3615
+ "start_line": 9,
3616
+ "code": " User.deleteOne({ _id: req.params.id }, function () { res.sendStatus(204); });",
3617
+ "why": "No role check; any logged‑in user can delete any account",
3618
+ "line": 9
3619
+ }
3620
+ ],
3621
+ "severity": "high",
3622
+ "confidence": "high",
3623
+ "cwe": [
3624
+ "CWE-284",
3625
+ "CWE-639"
3626
+ ],
3627
+ "recommendation": "Require admin role before deleting a user",
3628
+ "status": "confirmed",
3629
+ "verification": {
3630
+ "verdict": "confirmed",
3631
+ "reason_code": "traced",
3632
+ "checked": [
3633
+ "router.post('/admin/users/:id/delete', ensureLoggedIn, function (req, res) {",
3634
+ " User.deleteOne({ _id: req.params.id }, function () { res.sendStatus(204); });"
3635
+ ],
3636
+ "control_found": null,
3637
+ "notes": "No admin role or ownership check is performed before deleting a user.",
3638
+ "suggested_subtype": null,
3639
+ "suggested_cwe": null
3640
+ },
3641
+ "fingerprint": "kfp1:3dd2e6ecf2f0730978e0c2a05f31e2af3920a107cd2af0b5ea81c9ded7f6a100",
3642
+ "baseline_state": "new"
3643
+ },
3644
+ {
3645
+ "finding_id": "BOLA-R-0033",
3646
+ "category": "bola",
3647
+ "subtype": "missing_ownership_check",
3648
+ "title": "BOLA in GET /orders/:id: missing owner verification",
3649
+ "route_id": "R-0033",
3650
+ "endpoint": {
3651
+ "method": "GET",
3652
+ "path": "/orders/:id"
3653
+ },
3654
+ "location": {
3655
+ "file": "node-express/routes/orders.js",
3656
+ "start_line": 8
3657
+ },
3658
+ "evidence": [
3659
+ {
3660
+ "file": "node-express/routes/orders.js",
3661
+ "start_line": 8,
3662
+ "code": "router.get('/orders/:id', ensureLoggedIn, function (req, res) { Order.findById(req.params.id, function (err, order) { res.json(order); }); });",
3663
+ "why": "The order ID comes from the request and the query does not filter by the authenticated user.",
3664
+ "line": 8
3665
+ }
3666
+ ],
3667
+ "severity": "high",
3668
+ "confidence": "high",
3669
+ "cwe": [
3670
+ "CWE-284",
3671
+ "CWE-639"
3672
+ ],
3673
+ "recommendation": "Bind the query to the current session user.",
3674
+ "status": "confirmed",
3675
+ "verification": {
3676
+ "verdict": "confirmed",
3677
+ "reason_code": "traced",
3678
+ "checked": [
3679
+ "router.get('/orders/:id', ensureLoggedIn, function (req, res) {",
3680
+ " Order.findById(req.params.id, function (err, order) { res.json(order); });"
3681
+ ],
3682
+ "control_found": null,
3683
+ "notes": "The order is retrieved by ID without filtering by the authenticated user.",
3684
+ "suggested_subtype": null,
3685
+ "suggested_cwe": null
3686
+ },
3687
+ "fingerprint": "kfp1:3efb41972fd6c818041a922563d6a1e65eb9cdcd63b764b52ff4ffce238a6c8d",
3688
+ "baseline_state": "new"
3689
+ },
3690
+ {
3691
+ "finding_id": "BOLA-R-0038",
3692
+ "category": "bola",
3693
+ "subtype": "missing_ownership_check",
3694
+ "title": "BOLA in GET /stats: missing owner verification",
3695
+ "route_id": "R-0038",
3696
+ "endpoint": {
3697
+ "method": "GET",
3698
+ "path": "/stats"
3699
+ },
3700
+ "location": {
3701
+ "file": "node-express/routes/orders.js",
3702
+ "start_line": 28
3703
+ },
3704
+ "evidence": [
3705
+ {
3706
+ "file": "node-express/routes/orders.js",
3707
+ "start_line": 28,
3708
+ "code": "router.get('/stats', function (req, res) { db.query(`SELECT COUNT(*) AS n FROM orders WHERE shop_id = ${req.query.shop}`, function (e, r) { res.json(r); }); });",
3709
+ "why": "The shop_id is taken from the request and the query is not filtered by the authenticated user.",
3710
+ "line": 28
3711
+ }
3712
+ ],
3713
+ "severity": "high",
3714
+ "confidence": "high",
3715
+ "cwe": [
3716
+ "CWE-284",
3717
+ "CWE-639"
3718
+ ],
3719
+ "recommendation": "Add authentication guard and filter by the current user’s shop.",
3720
+ "status": "confirmed",
3721
+ "verification": {
3722
+ "verdict": "confirmed",
3723
+ "reason_code": "traced",
3724
+ "checked": [
3725
+ "router.get('/stats', function (req, res) {",
3726
+ " db.query(`SELECT COUNT(*) AS n FROM orders WHERE shop_id = ${req.query.shop}`, function (e, r) { res.json(r); });"
3727
+ ],
3728
+ "control_found": null,
3729
+ "notes": "The shop_id is taken directly from the request and no authentication guard is present.",
3730
+ "suggested_subtype": null,
3731
+ "suggested_cwe": null
3732
+ },
3733
+ "fingerprint": "kfp1:e0d4076276caf1e9557699dcd9494f106ac443c30bfdb55c71fb7aaedda29b14",
3734
+ "baseline_state": "new"
3735
+ },
3736
+ {
3737
+ "finding_id": "BOLA-R-0043",
3738
+ "category": "bola",
3739
+ "subtype": "missing_ownership_check",
3740
+ "title": "BOLA in ANY /perl-cgi/cgi-bin/order.cgi: missing owner verification",
3741
+ "route_id": "R-0043",
3742
+ "endpoint": {
3743
+ "method": "ANY",
3744
+ "path": "/perl-cgi/cgi-bin/order.cgi"
3745
+ },
3746
+ "location": {
3747
+ "file": "perl-cgi/cgi-bin/order.cgi",
3748
+ "start_line": 13
3749
+ },
3750
+ "evidence": [
3751
+ {
3752
+ "file": "perl-cgi/cgi-bin/order.cgi",
3753
+ "start_line": 13,
3754
+ "code": "my $sth = $dbh->prepare(\"SELECT * FROM orders WHERE id = $id\");",
3755
+ "why": "The order ID comes from the request and the query does not filter by the authenticated user.",
3756
+ "line": 13
3757
+ }
3758
+ ],
3759
+ "severity": "high",
3760
+ "confidence": "high",
3761
+ "cwe": [
3762
+ "CWE-284",
3763
+ "CWE-639"
3764
+ ],
3765
+ "recommendation": "Use a parameterized query that includes the user_id.",
3766
+ "status": "confirmed",
3767
+ "verification": {
3768
+ "verdict": "confirmed",
3769
+ "reason_code": "traced",
3770
+ "checked": [
3771
+ "my $sth = $dbh->prepare(\"SELECT * FROM orders WHERE id = $id\");",
3772
+ "$sth->execute();"
3773
+ ],
3774
+ "control_found": null,
3775
+ "notes": "An insecure query is executed before the safe query that includes user_id.",
3776
+ "suggested_subtype": null,
3777
+ "suggested_cwe": null
3778
+ },
3779
+ "fingerprint": "kfp1:0823c809d4892eb324f1b12ff01c568fed5e7b10e716e2a0064b9ed53c6f0380",
3780
+ "baseline_state": "new"
3781
+ },
3782
+ {
3783
+ "finding_id": "BOLA-R-0044",
3784
+ "category": "bola",
3785
+ "subtype": "missing_ownership_check",
3786
+ "title": "BOLA in ANY /invoice/view/{id}: missing owner verification",
3787
+ "route_id": "R-0044",
3788
+ "endpoint": {
3789
+ "method": "ANY",
3790
+ "path": "/invoice/view/{id}"
3791
+ },
3792
+ "location": {
3793
+ "file": "php-codeigniter/application/controllers/Invoice.php",
3794
+ "start_line": 10
3795
+ },
3796
+ "evidence": [
3797
+ {
3798
+ "file": "php-codeigniter/application/controllers/Invoice.php",
3799
+ "start_line": 10,
3800
+ "code": "$q = $this->db->query(\"SELECT * FROM invoices WHERE id = $id\");",
3801
+ "why": "The invoice ID comes from the request and the query does not filter by the authenticated user.",
3802
+ "line": 10
3803
+ }
3804
+ ],
3805
+ "severity": "high",
3806
+ "confidence": "high",
3807
+ "cwe": [
3808
+ "CWE-284",
3809
+ "CWE-639"
3810
+ ],
3811
+ "recommendation": "Add an owner check to the query.",
3812
+ "status": "confirmed",
3813
+ "verification": {
3814
+ "verdict": "confirmed",
3815
+ "reason_code": "traced",
3816
+ "checked": [
3817
+ "$q = $this->db->query(\"SELECT * FROM invoices WHERE id = $id\");",
3818
+ "$this->load->view('invoice', array('inv' => $q->row()));"
3819
+ ],
3820
+ "control_found": null,
3821
+ "notes": "The invoice is fetched by ID without filtering by the authenticated user.",
3822
+ "suggested_subtype": null,
3823
+ "suggested_cwe": null
3824
+ },
3825
+ "fingerprint": "kfp1:9e7c8370d77a2f7c2d568a97ad128c427ef21f3b9775cfaa95eb72b2c5da6115",
3826
+ "baseline_state": "new"
3827
+ },
3828
+ {
3829
+ "finding_id": "BOLA-R-0046",
3830
+ "category": "bola",
3831
+ "subtype": "missing_ownership_check",
3832
+ "title": "BOLA in ANY /invoice/update: missing owner verification",
3833
+ "route_id": "R-0046",
3834
+ "endpoint": {
3835
+ "method": "ANY",
3836
+ "path": "/invoice/update"
3837
+ },
3838
+ "location": {
3839
+ "file": "php-codeigniter/application/controllers/Invoice.php",
3840
+ "start_line": 23
3841
+ },
3842
+ "evidence": [
3843
+ {
3844
+ "file": "php-codeigniter/application/controllers/Invoice.php",
3845
+ "start_line": 23,
3846
+ "code": "$this->db->query(\"UPDATE invoices SET note = ? WHERE id = ?\", array($note, $id));",
3847
+ "why": "The invoice ID comes from the request and the update does not filter by the authenticated user.",
3848
+ "line": 23
3849
+ }
3850
+ ],
3851
+ "severity": "high",
3852
+ "confidence": "medium",
3853
+ "cwe": [
3854
+ "CWE-284",
3855
+ "CWE-639"
3856
+ ],
3857
+ "recommendation": "Add an owner check to the update query.",
3858
+ "status": "likely",
3859
+ "verification": {
3860
+ "verdict": "likely",
3861
+ "reason_code": "code_not_visible",
3862
+ "checked": [
3863
+ "php-codeigniter/application/controllers/Invoice.php lines 20-24"
3864
+ ],
3865
+ "control_found": null,
3866
+ "notes": "No ownership or role check, integer cast, or allow‑list is visible in the update method. The controller does not include any auth guard in the provided context.",
3867
+ "suggested_subtype": null,
3868
+ "suggested_cwe": null
3869
+ },
3870
+ "fingerprint": "kfp1:4f200be5155b2854ef32764906134098549058ea95b61c478a18aa59988ef8cb",
3871
+ "baseline_state": "new"
3872
+ },
3873
+ {
3874
+ "finding_id": "BOLA-R-0057-1",
3875
+ "category": "bola",
3876
+ "subtype": "missing_role_check",
3877
+ "title": "BOLA in GET /php-plain/admin.php?action=view: missing admin authorization",
3878
+ "route_id": "R-0057",
3879
+ "endpoint": {
3880
+ "method": "GET",
3881
+ "path": "/php-plain/admin.php?action=view"
3882
+ },
3883
+ "location": {
3884
+ "file": "php-plain/admin.php",
3885
+ "start_line": 8
3886
+ },
3887
+ "evidence": [
3888
+ {
3889
+ "file": "php-plain/admin.php",
3890
+ "start_line": 8,
3891
+ "code": "$r = mysql_query(\"SELECT * FROM users WHERE id = \" . $uid);",
3892
+ "why": "No admin check; any logged-in user can view any user record by supplying uid",
3893
+ "line": 8
3894
+ }
3895
+ ],
3896
+ "severity": "high",
3897
+ "confidence": "medium",
3898
+ "cwe": [
3899
+ "CWE-285"
3900
+ ],
3901
+ "recommendation": "Restrict admin actions to users with admin role",
3902
+ "status": "likely",
3903
+ "verification": {
3904
+ "verdict": "likely",
3905
+ "reason_code": "code_not_visible",
3906
+ "checked": [
3907
+ "php-plain/admin.php lines 5-9",
3908
+ "php-plain/inc/auth.php lines 1-7"
3909
+ ],
3910
+ "control_found": null,
3911
+ "notes": "The included auth file only verifies a logged‑in session; there is no admin role check before the SELECT query.",
3912
+ "suggested_subtype": null,
3913
+ "suggested_cwe": null
3914
+ },
3915
+ "fingerprint": "kfp1:cc867961fc9e03b8d4c0fa8a48dd37b02dcf819859c0a79e37915d2b20e5bb78",
3916
+ "baseline_state": "new"
3917
+ },
3918
+ {
3919
+ "finding_id": "BOLA-R-0057-2",
3920
+ "category": "bola",
3921
+ "subtype": "missing_role_check",
3922
+ "title": "BOLA in POST /php-plain/admin.php?action=delete: missing admin authorization",
3923
+ "route_id": "R-0057",
3924
+ "endpoint": {
3925
+ "method": "POST",
3926
+ "path": "/php-plain/admin.php?action=delete"
3927
+ },
3928
+ "location": {
3929
+ "file": "php-plain/admin.php",
3930
+ "start_line": 6
3931
+ },
3932
+ "evidence": [
3933
+ {
3934
+ "file": "php-plain/admin.php",
3935
+ "start_line": 12,
3936
+ "code": "mysql_query(\"DELETE FROM users WHERE id = $id\");",
3937
+ "why": "No admin check; any logged-in user can delete any user record by supplying id",
3938
+ "line": 12
3939
+ }
3940
+ ],
3941
+ "severity": "high",
3942
+ "confidence": "medium",
3943
+ "cwe": [
3944
+ "CWE-285"
3945
+ ],
3946
+ "recommendation": "Restrict admin actions to users with admin role",
3947
+ "status": "likely",
3948
+ "verification": {
3949
+ "verdict": "likely",
3950
+ "reason_code": "code_not_visible",
3951
+ "checked": [
3952
+ "php-plain/admin.php lines 10-13",
3953
+ "php-plain/inc/auth.php lines 1-7"
3954
+ ],
3955
+ "control_found": null,
3956
+ "notes": "No admin role check is present before the DELETE statement; the auth file does not enforce role.",
3957
+ "suggested_subtype": null,
3958
+ "suggested_cwe": null
3959
+ },
3960
+ "fingerprint": "kfp1:69e73d203136e6bb864223ff8a016aba9833243225e09244054bc123d36ab648",
3961
+ "baseline_state": "new"
3962
+ },
3963
+ {
3964
+ "finding_id": "BOLA-R-0058-1",
3965
+ "category": "bola",
3966
+ "subtype": "missing_role_check",
3967
+ "title": "BOLA in GET /php-plain/admin.php?action=view: missing admin authorization",
3968
+ "route_id": "R-0058",
3969
+ "endpoint": {
3970
+ "method": "GET",
3971
+ "path": "/php-plain/admin.php?action=view"
3972
+ },
3973
+ "location": {
3974
+ "file": "php-plain/admin.php",
3975
+ "start_line": 10
3976
+ },
3977
+ "evidence": [
3978
+ {
3979
+ "file": "php-plain/admin.php",
3980
+ "start_line": 8,
3981
+ "code": "$r = mysql_query(\"SELECT * FROM users WHERE id = \" . $uid);",
3982
+ "why": "No admin check; any logged-in user can view any user record by supplying uid",
3983
+ "line": 8
3984
+ }
3985
+ ],
3986
+ "severity": "high",
3987
+ "confidence": "medium",
3988
+ "cwe": [
3989
+ "CWE-285"
3990
+ ],
3991
+ "recommendation": "Restrict admin actions to users with admin role",
3992
+ "status": "likely",
3993
+ "verification": {
3994
+ "verdict": "likely",
3995
+ "reason_code": "code_not_visible",
3996
+ "checked": [
3997
+ "php-plain/admin.php lines 5-9",
3998
+ "php-plain/inc/auth.php lines 1-7"
3999
+ ],
4000
+ "control_found": null,
4001
+ "notes": "Same as BOLA-R-0057-1: only a login check is performed, no admin guard before the SELECT.",
4002
+ "suggested_subtype": null,
4003
+ "suggested_cwe": null
4004
+ },
4005
+ "fingerprint": "kfp1:4a9c6a0b64c50807e31b9d3dfcfc80fc6b039ab7e96d393502573010849e1856",
4006
+ "baseline_state": "new"
4007
+ },
4008
+ {
4009
+ "finding_id": "BOLA-R-0058-2",
4010
+ "category": "bola",
4011
+ "subtype": "missing_role_check",
4012
+ "title": "BOLA in POST /php-plain/admin.php?action=delete: missing admin authorization",
4013
+ "route_id": "R-0058",
4014
+ "endpoint": {
4015
+ "method": "POST",
4016
+ "path": "/php-plain/admin.php?action=delete"
4017
+ },
4018
+ "location": {
4019
+ "file": "php-plain/admin.php",
4020
+ "start_line": 12
4021
+ },
4022
+ "evidence": [
4023
+ {
4024
+ "file": "php-plain/admin.php",
4025
+ "start_line": 12,
4026
+ "code": "mysql_query(\"DELETE FROM users WHERE id = $id\");",
4027
+ "why": "No admin check; any logged-in user can delete any user record by supplying id",
4028
+ "line": 12
4029
+ }
4030
+ ],
4031
+ "severity": "high",
4032
+ "confidence": "medium",
4033
+ "cwe": [
4034
+ "CWE-285"
4035
+ ],
4036
+ "recommendation": "Restrict admin actions to users with admin role",
4037
+ "status": "likely",
4038
+ "verification": {
4039
+ "verdict": "likely",
4040
+ "reason_code": "code_not_visible",
4041
+ "checked": [
4042
+ "php-plain/admin.php lines 10-13",
4043
+ "php-plain/inc/auth.php lines 1-7"
4044
+ ],
4045
+ "control_found": null,
4046
+ "notes": "Same as BOLA-R-0057-2: no admin role check before the DELETE.",
4047
+ "suggested_subtype": null,
4048
+ "suggested_cwe": null
4049
+ },
4050
+ "fingerprint": "kfp1:5f4a304f0d15de265879c259fb3e7dce88c9be1061597334f91c47b1b6dd6271",
4051
+ "baseline_state": "new"
4052
+ },
4053
+ {
4054
+ "finding_id": "BOLA-R-0059-1",
4055
+ "category": "bola",
4056
+ "subtype": "missing_role_check",
4057
+ "title": "BOLA in POST /php-plain/manage_users.php: missing admin authorization",
4058
+ "route_id": "R-0059",
4059
+ "endpoint": {
4060
+ "method": "POST",
4061
+ "path": "/php-plain/manage_users.php"
4062
+ },
4063
+ "location": {
4064
+ "file": "php-plain/manage_users.php",
4065
+ "start_line": 6
4066
+ },
4067
+ "evidence": [
4068
+ {
4069
+ "file": "php-plain/manage_users.php",
4070
+ "start_line": 6,
4071
+ "code": "$stmt = $db->prepare(\"DELETE FROM users WHERE id = ?\");",
4072
+ "why": "No admin check; any logged-in user can delete any user record by supplying id",
4073
+ "line": 6
4074
+ },
4075
+ {
4076
+ "file": "php-plain/manage_users.php",
4077
+ "start_line": 7,
4078
+ "code": "$stmt->execute(array((int)$_POST['id']));",
4079
+ "why": "No admin check; any logged-in user can delete any user record by supplying id",
4080
+ "line": 7
4081
+ }
4082
+ ],
4083
+ "severity": "high",
4084
+ "confidence": "high",
4085
+ "cwe": [
4086
+ "CWE-285"
4087
+ ],
4088
+ "recommendation": "Restrict admin actions to users with admin role",
4089
+ "status": "confirmed",
4090
+ "verification": {
4091
+ "verdict": "confirmed",
4092
+ "reason_code": "traced",
4093
+ "checked": [
4094
+ "Checked lines 1-12 of php-plain/manage_users.php"
4095
+ ],
4096
+ "control_found": null,
4097
+ "notes": "The script performs a delete operation after a POST request but contains only a login check. No admin role check is present; the integer cast on $_POST['id'] is insufficient.",
4098
+ "suggested_subtype": null,
4099
+ "suggested_cwe": null
4100
+ },
4101
+ "fingerprint": "kfp1:008ddeaa85f4816b40232882477c347c1374d0e34eb390d7ba54103922d1da59",
4102
+ "baseline_state": "new"
4103
+ },
4104
+ {
4105
+ "finding_id": "BOLA-R-0061-1",
4106
+ "category": "bola",
4107
+ "subtype": "missing_ownership_check",
4108
+ "title": "BOLA in GET /php-plain/order_detail.php: missing ownership verification",
4109
+ "route_id": "R-0061",
4110
+ "endpoint": {
4111
+ "method": "GET",
4112
+ "path": "/php-plain/order_detail.php"
4113
+ },
4114
+ "location": {
4115
+ "file": "php-plain/order_detail.php",
4116
+ "start_line": 6
4117
+ },
4118
+ "evidence": [
4119
+ {
4120
+ "file": "php-plain/order_detail.php",
4121
+ "start_line": 6,
4122
+ "code": "$res = mysql_query(\"SELECT * FROM orders WHERE id = \" . $id);",
4123
+ "why": "No ownership check; any logged-in user can view any order by supplying id",
4124
+ "line": 6
4125
+ }
4126
+ ],
4127
+ "severity": "high",
4128
+ "confidence": "medium",
4129
+ "cwe": [
4130
+ "CWE-284",
4131
+ "CWE-639"
4132
+ ],
4133
+ "recommendation": "Bind query to current session user",
4134
+ "status": "likely",
4135
+ "verification": {
4136
+ "verdict": "likely",
4137
+ "reason_code": "code_not_visible",
4138
+ "checked": [
4139
+ "Checked lines 1-8 of php-plain/order_detail.php"
4140
+ ],
4141
+ "control_found": null,
4142
+ "notes": "The visible code performs a direct query on the order id without any ownership verification. The auth model indicates a check exists elsewhere, but it is not shown in the provided context.",
4143
+ "suggested_subtype": null,
4144
+ "suggested_cwe": null
4145
+ },
4146
+ "fingerprint": "kfp1:835c7046e8f9b4f1efe4a97ba040bb7f831da06ad0f6572e9360c6d7558a8506",
4147
+ "baseline_state": "new"
4148
+ },
4149
+ {
4150
+ "finding_id": "BOLA-R-0066-1",
4151
+ "category": "bola",
4152
+ "subtype": "missing_role_check",
4153
+ "title": "BOLA in POST /php-plain/admin/delete_user.php: missing admin authorization",
4154
+ "route_id": "R-0066",
4155
+ "endpoint": {
4156
+ "method": "POST",
4157
+ "path": "/php-plain/admin/delete_user.php"
4158
+ },
4159
+ "location": {
4160
+ "file": "php-plain/admin/delete_user.php",
4161
+ "start_line": 6
4162
+ },
4163
+ "evidence": [
4164
+ {
4165
+ "file": "php-plain/admin/delete_user.php",
4166
+ "start_line": 6,
4167
+ "code": "$stmt = $db->prepare(\"DELETE FROM users WHERE id = ?\");",
4168
+ "why": "No admin check; any logged-in user can delete any user record by supplying id",
4169
+ "line": 6
4170
+ },
4171
+ {
4172
+ "file": "php-plain/admin/delete_user.php",
4173
+ "start_line": 7,
4174
+ "code": "$stmt->execute(array($id));",
4175
+ "why": "No admin check; any logged-in user can delete any user record by supplying id",
4176
+ "line": 7
4177
+ }
4178
+ ],
4179
+ "severity": "high",
4180
+ "confidence": "high",
4181
+ "cwe": [
4182
+ "CWE-285"
4183
+ ],
4184
+ "recommendation": "Restrict admin actions to users with admin role",
4185
+ "status": "confirmed",
4186
+ "verification": {
4187
+ "verdict": "confirmed",
4188
+ "reason_code": "traced",
4189
+ "checked": [
4190
+ "Checked lines 1-8 of php-plain/admin/delete_user.php"
4191
+ ],
4192
+ "control_found": null,
4193
+ "notes": "The file includes only a login check via inc/auth.php; no admin role check is present. The integer cast on $_POST['id'] does not provide authorization.",
4194
+ "suggested_subtype": null,
4195
+ "suggested_cwe": null
4196
+ },
4197
+ "fingerprint": "kfp1:129cd6c2fbef0081813a0bc03a9d112ed8972207ef449be38fd4cc03bef853a1",
4198
+ "baseline_state": "new"
4199
+ },
4200
+ {
4201
+ "finding_id": "BOLA-R-0067-1",
4202
+ "category": "bola",
4203
+ "subtype": "missing_role_check",
4204
+ "title": "BOLA in POST /php-plain/admin/promote.php: missing admin authorization",
4205
+ "route_id": "R-0067",
4206
+ "endpoint": {
4207
+ "method": "POST",
4208
+ "path": "/php-plain/admin/promote.php"
4209
+ },
4210
+ "location": {
4211
+ "file": "php-plain/admin/promote.php",
4212
+ "start_line": 6
4213
+ },
4214
+ "evidence": [
4215
+ {
4216
+ "file": "php-plain/admin/promote.php",
4217
+ "start_line": 6,
4218
+ "code": "$stmt = $db->prepare(\"UPDATE users SET role = 'admin' WHERE id = ?\");",
4219
+ "why": "No admin check; any logged-in user can promote any user to admin",
4220
+ "line": 6
4221
+ },
4222
+ {
4223
+ "file": "php-plain/admin/promote.php",
4224
+ "start_line": 7,
4225
+ "code": "$stmt->execute(array((int)$_POST['uid']));",
4226
+ "why": "No admin check; any logged-in user can promote any user to admin",
4227
+ "line": 7
4228
+ }
4229
+ ],
4230
+ "severity": "high",
4231
+ "confidence": "high",
4232
+ "cwe": [
4233
+ "CWE-285"
4234
+ ],
4235
+ "recommendation": "Restrict admin actions to users with admin role",
4236
+ "status": "confirmed",
4237
+ "verification": {
4238
+ "verdict": "confirmed",
4239
+ "reason_code": "traced",
4240
+ "checked": [
4241
+ "Checked lines 1-8 of php-plain/admin/promote.php"
4242
+ ],
4243
+ "control_found": null,
4244
+ "notes": "Only a login check is performed; there is no admin role verification. The cast to int on $_POST['uid'] does not mitigate the missing authorization.",
4245
+ "suggested_subtype": null,
4246
+ "suggested_cwe": null
4247
+ },
4248
+ "fingerprint": "kfp1:6dc0277da98df231d8fc02ee1493c920b33ee571a51150fae72b902ead20c3ab",
4249
+ "baseline_state": "new"
4250
+ },
4251
+ {
4252
+ "finding_id": "BOLA-R-0069-1",
4253
+ "category": "bola",
4254
+ "subtype": "missing_ownership_check",
4255
+ "title": "BOLA in GET /orders/<int:order_id>: missing ownership verification",
4256
+ "route_id": "R-0069",
4257
+ "endpoint": {
4258
+ "method": "GET",
4259
+ "path": "/orders/<int:order_id>"
4260
+ },
4261
+ "location": {
4262
+ "file": "python-flask/app.py",
4263
+ "start_line": 12
4264
+ },
4265
+ "evidence": [
4266
+ {
4267
+ "file": "python-flask/app.py",
4268
+ "start_line": 12,
4269
+ "code": "cur.execute(\"SELECT * FROM orders WHERE id = %s\" % order_id);",
4270
+ "why": "No ownership check; any logged-in user can view any order by supplying order_id",
4271
+ "line": 12
4272
+ }
4273
+ ],
4274
+ "severity": "high",
4275
+ "confidence": "high",
4276
+ "cwe": [
4277
+ "CWE-284",
4278
+ "CWE-639"
4279
+ ],
4280
+ "recommendation": "Bind query to current user",
4281
+ "status": "confirmed",
4282
+ "verification": {
4283
+ "verdict": "confirmed",
4284
+ "reason_code": "traced",
4285
+ "checked": [
4286
+ "Checked lines 8-13 of python-flask/app.py"
4287
+ ],
4288
+ "control_found": null,
4289
+ "notes": "The /orders/<int:order_id> route executes a query based solely on the order id with no user‑ownership filter. Only the login_required decorator is present.",
4290
+ "suggested_subtype": null,
4291
+ "suggested_cwe": null
4292
+ },
4293
+ "fingerprint": "kfp1:8cba56db899ed30b4e1dab440f085921515c324f8d9fff2d15ee38eb60afa641",
4294
+ "baseline_state": "new"
4295
+ },
4296
+ {
4297
+ "finding_id": "BOLA-R-0073",
4298
+ "category": "bola",
4299
+ "subtype": "missing_ownership_check",
4300
+ "title": "BOLA in POST /users/<uid>/name: missing owner verification",
4301
+ "route_id": "R-0073",
4302
+ "endpoint": {
4303
+ "method": "POST",
4304
+ "path": "/users/<int:uid>/name"
4305
+ },
4306
+ "location": {
4307
+ "file": "python-flask/profile.py",
4308
+ "start_line": 16
4309
+ },
4310
+ "evidence": [
4311
+ {
4312
+ "file": "python-flask/profile.py",
4313
+ "start_line": 18,
4314
+ "code": "user = User.query.get(uid)",
4315
+ "why": "Loads user by ID supplied in the URL without checking ownership",
4316
+ "line": 18
4317
+ },
4318
+ {
4319
+ "file": "python-flask/profile.py",
4320
+ "start_line": 19,
4321
+ "code": "user.name = request.json[\"name\"]",
4322
+ "why": "Updates the user record without verifying that the current session user owns it",
4323
+ "line": 19
4324
+ }
4325
+ ],
4326
+ "severity": "high",
4327
+ "confidence": "medium",
4328
+ "cwe": [
4329
+ "CWE-284",
4330
+ "CWE-639"
4331
+ ],
4332
+ "recommendation": "Bind the update to the current session user",
4333
+ "status": "likely",
4334
+ "verification": {
4335
+ "verdict": "likely",
4336
+ "reason_code": "code_not_visible",
4337
+ "checked": [
4338
+ "python-flask/profile.py lines 16-20"
4339
+ ],
4340
+ "control_found": null,
4341
+ "notes": "The rename endpoint loads a User by the supplied uid and updates the name without any ownership or role check. No cast or allow‑list is present in the visible code.",
4342
+ "suggested_subtype": null,
4343
+ "suggested_cwe": null
4344
+ },
4345
+ "fingerprint": "kfp1:bfc179df51db588076982a9b160cccb6aa5f7c21b296231a88eb6cbdfe2c3842",
4346
+ "baseline_state": "new"
4347
+ },
4348
+ {
4349
+ "finding_id": "BOLA-R-0074",
4350
+ "category": "bola",
4351
+ "subtype": "missing_ownership_check",
4352
+ "title": "BOLA in PATCH /users/<uid>: missing owner verification",
4353
+ "route_id": "R-0074",
4354
+ "endpoint": {
4355
+ "method": "PATCH",
4356
+ "path": "/users/<int:uid>"
4357
+ },
4358
+ "location": {
4359
+ "file": "python-flask/profile.py",
4360
+ "start_line": 24
4361
+ },
4362
+ "evidence": [
4363
+ {
4364
+ "file": "python-flask/profile.py",
4365
+ "start_line": 26,
4366
+ "code": "user = User.query.get(uid)",
4367
+ "why": "Loads user by ID supplied in the URL without ownership check",
4368
+ "line": 26
4369
+ },
4370
+ {
4371
+ "file": "python-flask/profile.py",
4372
+ "start_line": 27,
4373
+ "code": "for key, value in request.json.items():\n setattr(user, key, value)",
4374
+ "why": "Updates arbitrary fields of the user record without verifying ownership",
4375
+ "line": 27
4376
+ }
4377
+ ],
4378
+ "severity": "high",
4379
+ "confidence": "medium",
4380
+ "cwe": [
4381
+ "CWE-284",
4382
+ "CWE-639"
4383
+ ],
4384
+ "recommendation": "Verify that the current session user owns the target user before updating",
4385
+ "status": "likely",
4386
+ "verification": {
4387
+ "verdict": "likely",
4388
+ "reason_code": "code_not_visible",
4389
+ "checked": [
4390
+ "python-flask/profile.py lines 24-29"
4391
+ ],
4392
+ "control_found": null,
4393
+ "notes": "The patch endpoint retrieves a User by uid and applies arbitrary updates without verifying that the current session user owns the record. No ownership guard is visible.",
4394
+ "suggested_subtype": null,
4395
+ "suggested_cwe": null
4396
+ },
4397
+ "fingerprint": "kfp1:53120a82329f249cb5d921adbb2d6965297c52b37440e5f47d8a2525bbe46fde",
4398
+ "baseline_state": "new"
4399
+ },
4400
+ {
4401
+ "finding_id": "MASS-M-0009-1",
4402
+ "category": "mass_assignment",
4403
+ "subtype": "privileged_field_writable",
4404
+ "title": "Team creation allows owner_id to be set by any user",
4405
+ "candidate_id": "M-0009",
4406
+ "location": {
4407
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
4408
+ "start_line": 23
4409
+ },
4410
+ "evidence": [
4411
+ {
4412
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
4413
+ "line": 21,
4414
+ "why": "Team::create($request->all()) binds all request fields, including owner_id which is writable"
4415
+ }
4416
+ ],
4417
+ "impact": "owner_id field writable by any authenticated user",
4418
+ "severity": "high",
4419
+ "confidence": "medium",
4420
+ "cwe": [
4421
+ "CWE-915"
4422
+ ],
4423
+ "recommendation": "Restrict mass assignment to non-privileged fields or set guarded/fillable to exclude owner_id",
4424
+ "status": "likely",
4425
+ "verification": {
4426
+ "verdict": "likely",
4427
+ "reason_code": "code_not_visible",
4428
+ "checked": [
4429
+ "php-laravel/app/Http/Controllers/AccountController.php lines 21-23"
4430
+ ],
4431
+ "control_found": null,
4432
+ "notes": "The controller calls Team::create($request->all()) with no visible mass‑assignment guard. The Team model definition (fillable/guarded) is not shown, so a compensating control cannot be confirmed.",
4433
+ "suggested_subtype": null,
4434
+ "suggested_cwe": null
4435
+ },
4436
+ "fingerprint": "kfp1:8b971db7731e918c2e512a87c2f1fa5db1332111bbe36388f36cd8dbc836da8a",
4437
+ "baseline_state": "new"
4438
+ },
4439
+ {
4440
+ "finding_id": "MASS-M-0006-1",
4441
+ "category": "mass_assignment",
4442
+ "subtype": "whole_object_binding",
4443
+ "title": "Profile update binds entire request body to User model",
4444
+ "candidate_id": "M-0006",
4445
+ "location": {
4446
+ "file": "node-express/routes/profile.js",
4447
+ "start_line": 8
4448
+ },
4449
+ "evidence": [
4450
+ {
4451
+ "file": "node-express/routes/profile.js",
4452
+ "line": 8,
4453
+ "why": "Location taken from the Tier 1 inventory"
4454
+ }
4455
+ ],
4456
+ "impact": "Unknown privileged fields may be writable",
4457
+ "severity": "medium",
4458
+ "confidence": "medium",
4459
+ "cwe": [
4460
+ "CWE-915"
4461
+ ],
4462
+ "recommendation": "Review model definition to ensure no privileged fields are mass assignable",
4463
+ "status": "likely",
4464
+ "verification": {
4465
+ "verdict": "likely",
4466
+ "reason_code": "code_not_visible",
4467
+ "checked": [
4468
+ "profile.js router.put('/profile') handler (lines 7-9)",
4469
+ "User.findByIdAndUpdate with req.body"
4470
+ ],
4471
+ "control_found": null,
4472
+ "notes": "The handler updates the user document with the entire request body without any field filtering or privilege checks.",
4473
+ "suggested_subtype": null,
4474
+ "suggested_cwe": null
4475
+ },
4476
+ "fingerprint": "kfp1:ec78e14c30f0e52cc4cdf220e7fb71d0f8c9347306faf2453ce6d54de02aeb7a",
4477
+ "baseline_state": "new"
4478
+ },
4479
+ {
4480
+ "finding_id": "MASS-M-0007-1",
4481
+ "category": "mass_assignment",
4482
+ "subtype": "whole_object_binding",
4483
+ "title": "Profile update binds entire request body to User model",
4484
+ "candidate_id": "M-0007",
4485
+ "location": {
4486
+ "file": "node-express/routes/profile.js",
4487
+ "start_line": 16
4488
+ },
4489
+ "evidence": [
4490
+ {
4491
+ "file": "node-express/routes/profile.js",
4492
+ "line": 16,
4493
+ "why": "Location taken from the Tier 1 inventory"
4494
+ }
4495
+ ],
4496
+ "impact": "Unknown privileged fields may be writable",
4497
+ "severity": "medium",
4498
+ "confidence": "medium",
4499
+ "cwe": [
4500
+ "CWE-915"
4501
+ ],
4502
+ "recommendation": "Review model definition to ensure no privileged fields are mass assignable",
4503
+ "status": "likely",
4504
+ "verification": {
4505
+ "verdict": "likely",
4506
+ "reason_code": "code_not_visible",
4507
+ "checked": [
4508
+ "profile.js router.post('/signup') handler (lines 15-17)",
4509
+ "new User(req.body) and user.save()"
4510
+ ],
4511
+ "control_found": null,
4512
+ "notes": "The signup route creates a new user from the full request body, exposing all fields to the client.",
4513
+ "suggested_subtype": null,
4514
+ "suggested_cwe": null
4515
+ },
4516
+ "fingerprint": "kfp1:ff246809367a9a11f3cf5de56d796bac3a7d168644e45ba113f83bbe26fcd608",
4517
+ "baseline_state": "new"
4518
+ },
4519
+ {
4520
+ "finding_id": "MASS-M-0008-1",
4521
+ "category": "mass_assignment",
4522
+ "subtype": "whole_object_binding",
4523
+ "title": "User registration binds entire request body to User model",
4524
+ "candidate_id": "M-0008",
4525
+ "location": {
4526
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
4527
+ "start_line": 13
4528
+ },
4529
+ "evidence": [
4530
+ {
4531
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
4532
+ "line": 13,
4533
+ "why": "Location taken from the Tier 1 inventory"
4534
+ }
4535
+ ],
4536
+ "impact": "Unknown privileged fields may be writable",
4537
+ "severity": "medium",
4538
+ "confidence": "medium",
4539
+ "cwe": [
4540
+ "CWE-915"
4541
+ ],
4542
+ "recommendation": "Review model definition to ensure no privileged fields are mass assignable",
4543
+ "status": "likely",
4544
+ "verification": {
4545
+ "verdict": "likely",
4546
+ "reason_code": "code_not_visible",
4547
+ "checked": [
4548
+ "AccountController.register method (lines 11-14)",
4549
+ "User::create($request->all())"
4550
+ ],
4551
+ "control_found": null,
4552
+ "notes": "The register action mass‑assigns all request fields to the User model without any explicit field filtering.",
4553
+ "suggested_subtype": null,
4554
+ "suggested_cwe": null
4555
+ },
4556
+ "fingerprint": "kfp1:0d993d49cea372dd80c171b78e6f7ec5a9ee98469cbb325ec6fa20f80f2824d3",
4557
+ "baseline_state": "new"
4558
+ },
4559
+ {
4560
+ "finding_id": "MASS-M-0010-1",
4561
+ "category": "mass_assignment",
4562
+ "subtype": "whole_object_binding",
4563
+ "title": "Team creation binds entire request body to Team model",
4564
+ "candidate_id": "M-0010",
4565
+ "location": {
4566
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
4567
+ "start_line": 36
4568
+ },
4569
+ "evidence": [
4570
+ {
4571
+ "file": "php-laravel/app/Http/Controllers/AccountController.php",
4572
+ "line": 36,
4573
+ "why": "Location taken from the Tier 1 inventory"
4574
+ }
4575
+ ],
4576
+ "impact": "Unknown privileged fields may be writable",
4577
+ "severity": "medium",
4578
+ "confidence": "medium",
4579
+ "cwe": [
4580
+ "CWE-915"
4581
+ ],
4582
+ "recommendation": "Review model definition to ensure no privileged fields are mass assignable",
4583
+ "status": "likely",
4584
+ "verification": {
4585
+ "verdict": "likely",
4586
+ "reason_code": "code_not_visible",
4587
+ "checked": [
4588
+ "php-laravel/app/Http/Controllers/AccountController.php lines 21-23"
4589
+ ],
4590
+ "control_found": null,
4591
+ "notes": "Same as finding 1: the entire request body is bound to the Team model with no visible guard. The model’s mass‑assignment configuration is not visible.",
4592
+ "suggested_subtype": null,
4593
+ "suggested_cwe": null
4594
+ },
4595
+ "fingerprint": "kfp1:244d21feb05b766e446de3435905992e0d39b20a5436a9de0683c6a008b0159a",
4596
+ "baseline_state": "new"
4597
+ },
4598
+ {
4599
+ "finding_id": "MASS-M-0014-1",
4600
+ "category": "mass_assignment",
4601
+ "subtype": "whole_object_binding",
4602
+ "title": "User creation binds entire request JSON to User model",
4603
+ "candidate_id": "M-0014",
4604
+ "location": {
4605
+ "file": "python-flask/profile.py",
4606
+ "start_line": 10
4607
+ },
4608
+ "evidence": [
4609
+ {
4610
+ "file": "python-flask/profile.py",
4611
+ "line": 10,
4612
+ "why": "Location taken from the Tier 1 inventory"
4613
+ }
4614
+ ],
4615
+ "impact": "Unknown privileged fields may be writable",
4616
+ "severity": "medium",
4617
+ "confidence": "medium",
4618
+ "cwe": [
4619
+ "CWE-915"
4620
+ ],
4621
+ "recommendation": "Review model definition to ensure no privileged fields are mass assignable",
4622
+ "status": "likely",
4623
+ "verification": {
4624
+ "verdict": "likely",
4625
+ "reason_code": "code_not_visible",
4626
+ "checked": [
4627
+ "python-flask/profile.py lines 8-12"
4628
+ ],
4629
+ "control_found": null,
4630
+ "notes": "User creation uses User(**request.json) to bind all fields. No visible guard is present in the shown code, and the model’s mass‑assignment configuration is not visible.",
4631
+ "suggested_subtype": null,
4632
+ "suggested_cwe": null
4633
+ },
4634
+ "fingerprint": "kfp1:51138561ba9122be66c6d5528102fe010b0ace15250bc672817339e3750f8de1",
4635
+ "baseline_state": "new"
4636
+ },
4637
+ {
4638
+ "finding_id": "MASS-M-0015-1",
4639
+ "category": "mass_assignment",
4640
+ "subtype": "whole_object_binding",
4641
+ "title": "PATCH endpoint sets arbitrary attributes from request JSON",
4642
+ "candidate_id": "M-0015",
4643
+ "location": {
4644
+ "file": "python-flask/profile.py",
4645
+ "start_line": 27
4646
+ },
4647
+ "evidence": [
4648
+ {
4649
+ "file": "python-flask/profile.py",
4650
+ "line": 27,
4651
+ "why": "Location taken from the Tier 1 inventory"
4652
+ }
4653
+ ],
4654
+ "impact": "Unknown privileged fields may be writable",
4655
+ "severity": "medium",
4656
+ "confidence": "medium",
4657
+ "cwe": [
4658
+ "CWE-915"
4659
+ ],
4660
+ "recommendation": "Review model definition to ensure no privileged fields are mass assignable",
4661
+ "status": "likely",
4662
+ "verification": {
4663
+ "verdict": "likely",
4664
+ "reason_code": "code_not_visible",
4665
+ "checked": [
4666
+ "python-flask/profile.py lines 24-29"
4667
+ ],
4668
+ "control_found": null,
4669
+ "notes": "The PATCH endpoint iterates over request.json and sets attributes on the User object without any visible guard. The User model’s mass‑assignment restrictions are not shown.",
4670
+ "suggested_subtype": null,
4671
+ "suggested_cwe": null
4672
+ },
4673
+ "fingerprint": "kfp1:c5462b8a2767bef52d1825278c78faa6d04a3dcaeaba8db7445a2210c5deac7c",
4674
+ "baseline_state": "new"
4675
+ },
4676
+ {
4677
+ "finding_id": "INJ-I-0035-1",
4678
+ "category": "sql_injection",
4679
+ "subtype": "string_concatenation",
4680
+ "title": "SQL Injection in python-flask/app.py:12",
4681
+ "candidate_id": "I-0035",
4682
+ "location": {
4683
+ "file": "python-flask/app.py",
4684
+ "start_line": 12
4685
+ },
4686
+ "evidence": [
4687
+ {
4688
+ "file": "python-flask/app.py",
4689
+ "start_line": 10,
4690
+ "code": "order_id = request.view_args['order_id']",
4691
+ "why": "source: URL path parameter",
4692
+ "line": 10
4693
+ },
4694
+ {
4695
+ "file": "python-flask/app.py",
4696
+ "start_line": 12,
4697
+ "code": "cur.execute(\"SELECT * FROM orders WHERE id = %s\" % order_id)",
4698
+ "why": "unsanitized string formatting",
4699
+ "line": 12
4700
+ }
4701
+ ],
4702
+ "severity": "critical",
4703
+ "confidence": "high",
4704
+ "cwe": [
4705
+ "CWE-89"
4706
+ ],
4707
+ "recommendation": "Use parameterized queries",
4708
+ "status": "rejected",
4709
+ "verification": {
4710
+ "verdict": "rejected",
4711
+ "reason_code": "control_found",
4712
+ "checked": [
4713
+ "python-flask/app.py line 8 (route decorator)",
4714
+ "python-flask/app.py line 12 (query execution)"
4715
+ ],
4716
+ "control_found": {
4717
+ "file": "python-flask/app.py",
4718
+ "line": 8,
4719
+ "code": "@app.route('/orders/<int:order_id>')"
4720
+ },
4721
+ "notes": "The Flask route converter `<int:order_id>` guarantees that `order_id` is an integer before it reaches the handler, preventing SQL injection via the string formatting used in the query.",
4722
+ "suggested_subtype": null,
4723
+ "suggested_cwe": null
4724
+ },
4725
+ "fingerprint": "kfp1:380cba3dbd163699e9ff5dd53924bb40b0532d1acc79dd1496f72558597fd643"
4726
+ },
4727
+ {
4728
+ "finding_id": "MASS-M-0013-1",
4729
+ "category": "mass_assignment",
4730
+ "subtype": "whole_object_binding",
4731
+ "title": "User insertion uses extract($_POST) to bind all fields",
4732
+ "candidate_id": "M-0013",
4733
+ "location": {
4734
+ "file": "php-plain/register.php",
4735
+ "start_line": 4
4736
+ },
4737
+ "evidence": [
4738
+ {
4739
+ "file": "php-plain/register.php",
4740
+ "line": 4,
4741
+ "why": "Location taken from the Tier 1 inventory"
4742
+ }
4743
+ ],
4744
+ "impact": "Unknown privileged fields may be writable",
4745
+ "severity": "medium",
4746
+ "confidence": "high",
4747
+ "cwe": [
4748
+ "CWE-915"
4749
+ ],
4750
+ "recommendation": "Review model definition to ensure no privileged fields are mass assignable",
4751
+ "status": "rejected",
4752
+ "verification": {
4753
+ "verdict": "rejected",
4754
+ "reason_code": "evidence_mismatch",
4755
+ "checked": [
4756
+ "php-plain/register.php lines 4-6"
4757
+ ],
4758
+ "control_found": null,
4759
+ "notes": "The code extracts $_POST but only inserts the email and name columns into the users table. No mass assignment of privileged fields occurs, so the evidence does not support the finding.",
4760
+ "suggested_subtype": null,
4761
+ "suggested_cwe": null
4762
+ },
4763
+ "fingerprint": "kfp1:8083b1070ea8434ace18c5599bb58a5b7793bb16ca3ff34eafa6112c524e7926"
4764
+ }
4765
+ ]
4766
+ }