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