@hoanghainh/komagu 2.5.0 → 2.6.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 (225) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/assess/lsa/examples/eval-run-6/gptoss-a/ai-run-stats.json +87 -0
  3. package/assess/lsa/examples/eval-run-6/gptoss-a/auth-model.md +120 -0
  4. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/index.json +60 -0
  5. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/injection-001.json +383 -0
  6. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/injection-002.json +205 -0
  7. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/privilege-001.json +838 -0
  8. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/privilege-002.json +124 -0
  9. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-001.json +778 -0
  10. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-002.json +546 -0
  11. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-003.json +661 -0
  12. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-004.json +659 -0
  13. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-005.json +612 -0
  14. package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-006.json +106 -0
  15. package/assess/lsa/examples/eval-run-6/gptoss-a/coverage.json +19 -0
  16. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-001.json +516 -0
  17. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-002.json +332 -0
  18. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-003.json +218 -0
  19. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-004.json +561 -0
  20. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-005.json +316 -0
  21. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-006.json +14 -0
  22. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/inj-injection-001.json +782 -0
  23. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/inj-injection-002.json +418 -0
  24. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/priv-privilege-001.json +627 -0
  25. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/priv-privilege-002.json +253 -0
  26. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-1.json +68 -0
  27. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-10.json +68 -0
  28. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-11.json +61 -0
  29. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-12.json +60 -0
  30. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-13.json +60 -0
  31. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-14.json +66 -0
  32. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-15.json +69 -0
  33. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-16.json +60 -0
  34. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-17.json +61 -0
  35. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-18.json +69 -0
  36. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-19.json +64 -0
  37. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-2.json +60 -0
  38. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-20.json +60 -0
  39. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-21.json +61 -0
  40. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-22.json +50 -0
  41. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-3.json +60 -0
  42. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-4.json +60 -0
  43. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-5.json +60 -0
  44. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-6.json +70 -0
  45. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-7.json +60 -0
  46. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-8.json +60 -0
  47. package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-9.json +60 -0
  48. package/assess/lsa/examples/eval-run-6/gptoss-a/findings.json +4857 -0
  49. package/assess/lsa/examples/eval-run-6/gptoss-a/mass-assignment.candidates.json +232 -0
  50. package/assess/lsa/examples/eval-run-6/gptoss-a/report.ja.md +2112 -0
  51. package/assess/lsa/examples/eval-run-6/gptoss-a/report.md +2112 -0
  52. package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-001.json +425 -0
  53. package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-002.json +333 -0
  54. package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-003.json +400 -0
  55. package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-004.json +403 -0
  56. package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-005.json +398 -0
  57. package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-006.json +63 -0
  58. package/assess/lsa/examples/eval-run-6/gptoss-a/score.json +82 -0
  59. package/assess/lsa/examples/eval-run-6/gptoss-a/stack-profile.json +225 -0
  60. package/assess/lsa/examples/eval-run-6/gptoss-b/ai-run-stats.json +91 -0
  61. package/assess/lsa/examples/eval-run-6/gptoss-b/auth-model.md +120 -0
  62. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/index.json +60 -0
  63. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/injection-001.json +383 -0
  64. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/injection-002.json +205 -0
  65. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/privilege-001.json +838 -0
  66. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/privilege-002.json +124 -0
  67. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-001.json +778 -0
  68. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-002.json +546 -0
  69. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-003.json +661 -0
  70. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-004.json +659 -0
  71. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-005.json +612 -0
  72. package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-006.json +106 -0
  73. package/assess/lsa/examples/eval-run-6/gptoss-b/coverage.json +21 -0
  74. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-001.json +593 -0
  75. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-002.json +367 -0
  76. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-003.json +207 -0
  77. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-004.json +477 -0
  78. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-005.json +341 -0
  79. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-006.json +14 -0
  80. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/inj-injection-001.json +821 -0
  81. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/inj-injection-002.json +410 -0
  82. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/priv-privilege-001.json +649 -0
  83. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/priv-privilege-002.json +222 -0
  84. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-1.json +60 -0
  85. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-10.json +67 -0
  86. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-11.json +68 -0
  87. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-12.json +60 -0
  88. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-13.json +60 -0
  89. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-14.json +64 -0
  90. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-15.json +70 -0
  91. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-16.json +60 -0
  92. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-17.json +61 -0
  93. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-18.json +60 -0
  94. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-19.json +65 -0
  95. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-2.json +60 -0
  96. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-20.json +60 -0
  97. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-21.json +62 -0
  98. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-22.json +50 -0
  99. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-3.json +60 -0
  100. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-4.json +60 -0
  101. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-5.json +60 -0
  102. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-6.json +60 -0
  103. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-7.json +64 -0
  104. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-8.json +60 -0
  105. package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-9.json +60 -0
  106. package/assess/lsa/examples/eval-run-6/gptoss-b/findings.json +4966 -0
  107. package/assess/lsa/examples/eval-run-6/gptoss-b/mass-assignment.candidates.json +232 -0
  108. package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-001.json +425 -0
  109. package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-002.json +333 -0
  110. package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-003.json +400 -0
  111. package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-004.json +403 -0
  112. package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-005.json +398 -0
  113. package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-006.json +63 -0
  114. package/assess/lsa/examples/eval-run-6/gptoss-b/score.json +87 -0
  115. package/assess/lsa/examples/eval-run-6/gptoss-b/stack-profile.json +225 -0
  116. package/assess/lsa/examples/eval-run-6/ground-truth.json +851 -0
  117. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/ai-run-stats.json +96 -0
  118. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/auth-model.md +191 -0
  119. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/index.json +60 -0
  120. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/injection-001.json +383 -0
  121. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/injection-002.json +205 -0
  122. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/privilege-001.json +838 -0
  123. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/privilege-002.json +124 -0
  124. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-001.json +778 -0
  125. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-002.json +546 -0
  126. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-003.json +661 -0
  127. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-004.json +659 -0
  128. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-005.json +612 -0
  129. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-006.json +106 -0
  130. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/coverage.json +31 -0
  131. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-001.json +527 -0
  132. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-002.json +568 -0
  133. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-003.json +467 -0
  134. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-004.json +342 -0
  135. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-005.json +693 -0
  136. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-006.json +16 -0
  137. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/inj-injection-001.json +1123 -0
  138. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/inj-injection-002.json +391 -0
  139. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/priv-privilege-001.json +726 -0
  140. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/priv-privilege-002.json +306 -0
  141. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-1.json +137 -0
  142. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-10.json +106 -0
  143. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-11.json +81 -0
  144. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-12.json +135 -0
  145. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-13.json +135 -0
  146. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-14.json +135 -0
  147. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-15.json +110 -0
  148. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-16.json +116 -0
  149. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-17.json +141 -0
  150. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-18.json +135 -0
  151. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-19.json +140 -0
  152. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-2.json +135 -0
  153. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-20.json +82 -0
  154. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-21.json +74 -0
  155. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-22.json +99 -0
  156. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-3.json +140 -0
  157. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-4.json +145 -0
  158. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-5.json +135 -0
  159. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-6.json +135 -0
  160. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-7.json +87 -0
  161. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-8.json +125 -0
  162. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-9.json +135 -0
  163. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings.json +6989 -0
  164. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/mass-assignment.candidates.json +232 -0
  165. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/memory.log +36 -0
  166. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-001.json +425 -0
  167. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-002.json +333 -0
  168. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-003.json +400 -0
  169. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-004.json +403 -0
  170. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-005.json +398 -0
  171. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-006.json +63 -0
  172. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/score.json +106 -0
  173. package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/stack-profile.json +225 -0
  174. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/auth-model.md +517 -0
  175. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-001.json +5 -0
  176. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-002.json +428 -0
  177. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-003.json +388 -0
  178. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-004.json +463 -0
  179. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-005.json +5 -0
  180. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-006.json +67 -0
  181. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/inj-injection-001.json +964 -0
  182. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings.partial.json +1872 -0
  183. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/memory.log +194 -0
  184. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/score.partial.json +108 -0
  185. package/assess/lsa/examples/eval-run-6/qwen-16k-partial/stack-profile.json +225 -0
  186. package/assess/lsa/scripts/laravel_routes.py +102 -0
  187. package/assess/lsa/scripts/map_routes.py +4 -1
  188. package/assess/lsa/tests/EVAL-RESULTS.md +126 -0
  189. package/assess/lsa/tests/test_eval_run_6.py +104 -0
  190. package/assess/lsa/tests/test_laravel_routes.py +95 -0
  191. package/examples/member-portal/backend-dotnet/Auth/DemoAuthenticationHandler.cs +75 -0
  192. package/examples/member-portal/backend-dotnet/Program.cs +1 -72
  193. package/examples/member-portal/backend-spring/src/main/kotlin/io/komagu/portal/Application.kt +4 -0
  194. package/examples/member-portal/backend-spring/src/main/kotlin/io/komagu/portal/controller/Controllers.kt +3 -0
  195. package/examples/member-portal/backend-spring/src/main/kotlin/io/komagu/portal/model/Entities.kt +4 -0
  196. package/examples/member-portal/tests/test_d6_demo_app.py +4 -4
  197. package/examples/member-portal/tests/test_d6_remediation_verification.py +6 -6
  198. package/operate/ai/client.py +27 -10
  199. package/operate/ai/runner.py +4 -2
  200. package/operate/cli/__init__.py +1 -1
  201. package/operate/cli/commands/scan.py +8 -1
  202. package/operate/cross_user_tester/runner.py +4 -1
  203. package/operate/devsecops/sbom.py +12 -4
  204. package/operate/simulate/scenarios.py +11 -2
  205. package/operate/tests/test_ai_reasoning_effort.py +65 -0
  206. package/operate/tests/test_ai_route_inventory.py +53 -0
  207. package/operate/tests/test_audit_minimum_versions.py +89 -0
  208. package/operate/tests/test_cli.py +1 -1
  209. package/operate/tests/test_scheme_and_xml_guards.py +67 -0
  210. package/package.json +1 -1
  211. package/remediate/authz/dotnet/src/Komagu.Security.Authorization/LoggingSecurityAuditSink.cs +19 -3
  212. package/remediate/authz/golang/authz_test.go +5 -2
  213. package/remediate/authz/spring/src/main/kotlin/io/komagu/security/authorization/DefaultKomaguSecurityManager.kt +74 -78
  214. package/remediate/authz/spring/src/main/kotlin/io/komagu/security/spring/OwnershipAspect.kt +129 -158
  215. package/remediate/deps/patcher.py +2 -1
  216. package/remediate/edge/mock-backend/app.py +2 -1
  217. package/remediate/edge/tests/test_edge_security.py +5 -5
  218. package/remediate/ekyc/requirements.txt +1 -1
  219. package/remediate/identity/dotnet/src/Komagu.Security.Identity/OidcServiceCollectionExtensions.cs +19 -6
  220. package/remediate/identity/python/requirements.txt +2 -2
  221. package/remediate/identity/spring/src/main/kotlin/io/komagu/security/identity/KomaguIdentityAutoConfiguration.kt +31 -38
  222. package/remediate/legacy_shims/tests/test_legacy_shims.py +8 -8
  223. package/remediate/query/dotnet/src/Komagu.Security.Query/SafeSortAllowList.cs +5 -2
  224. package/remediate/query/spring/src/main/kotlin/io/komagu/security/query/SafeSortAllowList.kt +10 -11
  225. package/remediate/query/spring/src/main/kotlin/io/komagu/security/query/detekt/StringBuiltSqlQueryRule.kt +19 -24
@@ -47,46 +47,12 @@ class KomaguIdentityAutoConfiguration {
47
47
  else -> ""
48
48
  }
49
49
 
50
- if (properties.audience.isBlank()) {
51
- throw IllegalStateException("komagu.security.oidc.audience must not be blank")
50
+ check(properties.audience.isNotBlank()) { "komagu.security.oidc.audience must not be blank" }
51
+ check(properties.clockSkewSeconds in 0L..120L) {
52
+ "komagu.security.oidc.clock-skew-seconds must be between 0 and 120 (got ${properties.clockSkewSeconds})"
52
53
  }
53
54
 
54
- if (properties.clockSkewSeconds < 0L || properties.clockSkewSeconds > 120L) {
55
- throw IllegalStateException(
56
- "komagu.security.oidc.clock-skew-seconds must be between 0 and 120 (got ${properties.clockSkewSeconds})"
57
- )
58
- }
59
-
60
- val jwkUri = properties.jwkSetUri?.trim()
61
- val decoder: NimbusJwtDecoder = when {
62
- !jwkUri.isNullOrBlank() -> {
63
- if (effectiveIssuer.isBlank()) {
64
- throw IllegalStateException("komagu.security.oidc.issuer must not be blank")
65
- }
66
- if (!properties.allowInsecureHttpJwks && !jwkUri.lowercase().startsWith("https://")) {
67
- throw IllegalStateException(
68
- "komagu.security.oidc.jwk-set-uri must use HTTPS ($jwkUri). " +
69
- "Set komagu.security.oidc.allow-insecure-http-jwks=true only for testing."
70
- )
71
- }
72
- NimbusJwtDecoder.withJwkSetUri(jwkUri).build()
73
- }
74
- effectiveIssuer.isNotBlank() -> {
75
- if (!properties.allowInsecureHttpJwks && !effectiveIssuer.lowercase().startsWith("https://")) {
76
- throw IllegalStateException(
77
- "OIDC issuer URI must use HTTPS ($effectiveIssuer). " +
78
- "Set komagu.security.oidc.allow-insecure-http-jwks=true only for testing."
79
- )
80
- }
81
- NimbusJwtDecoder.withIssuerLocation(effectiveIssuer).build()
82
- }
83
- else -> {
84
- throw IllegalStateException(
85
- "Either komagu.security.oidc.jwk-set-uri or komagu.security.oidc.issuer " +
86
- "(or spring.security.oauth2.resourceserver.jwt.issuer-uri) must be configured"
87
- )
88
- }
89
- }
55
+ val decoder = buildDecoder(properties, effectiveIssuer)
90
56
 
91
57
  val expirationValidator = KomaguExpirationValidator()
92
58
  val timestampValidator = JwtTimestampValidator(Duration.ofSeconds(properties.clockSkewSeconds))
@@ -106,6 +72,33 @@ class KomaguIdentityAutoConfiguration {
106
72
  return decoder
107
73
  }
108
74
 
75
+ private fun buildDecoder(properties: KomaguOidcProperties, effectiveIssuer: String): NimbusJwtDecoder {
76
+ val jwkUri = properties.jwkSetUri?.trim()
77
+ return when {
78
+ !jwkUri.isNullOrBlank() -> {
79
+ check(effectiveIssuer.isNotBlank()) { "komagu.security.oidc.issuer must not be blank" }
80
+ check(properties.allowInsecureHttpJwks || isHttps(jwkUri)) {
81
+ "komagu.security.oidc.jwk-set-uri must use HTTPS ($jwkUri). " +
82
+ "Set komagu.security.oidc.allow-insecure-http-jwks=true only for testing."
83
+ }
84
+ NimbusJwtDecoder.withJwkSetUri(jwkUri).build()
85
+ }
86
+ effectiveIssuer.isNotBlank() -> {
87
+ check(properties.allowInsecureHttpJwks || isHttps(effectiveIssuer)) {
88
+ "OIDC issuer URI must use HTTPS ($effectiveIssuer). " +
89
+ "Set komagu.security.oidc.allow-insecure-http-jwks=true only for testing."
90
+ }
91
+ NimbusJwtDecoder.withIssuerLocation(effectiveIssuer).build()
92
+ }
93
+ else -> error(
94
+ "Either komagu.security.oidc.jwk-set-uri or komagu.security.oidc.issuer " +
95
+ "(or spring.security.oauth2.resourceserver.jwt.issuer-uri) must be configured"
96
+ )
97
+ }
98
+ }
99
+
100
+ private fun isHttps(uri: String): Boolean = uri.lowercase().startsWith("https://")
101
+
109
102
  @Bean
110
103
  @ConditionalOnMissingBean(SecurityFilterChain::class)
111
104
  fun komaguSecurityFilterChain(
@@ -222,23 +222,23 @@ public class OwnershipEnforcerBehaviouralTest {
222
222
  def test_php_shim_behavioural_execution(self) -> None:
223
223
  """Verify PHP shim behavioral normalization and denial with SQLite in-memory PDO."""
224
224
  php_file = SHIMS_ROOT / "php" / "komagu_authz_shim.php"
225
- php_test_script = f"""
226
- require_once '{php_file}';
225
+ php_test_script = """
226
+ require_once '__PHP_FILE__';
227
227
 
228
228
  // 1. Test komagu_normalize_id
229
229
  assert(komagu_normalize_id(1) === 1);
230
230
  assert(komagu_normalize_id('42') === 42);
231
231
 
232
232
  $invalidIds = [null, '', false, true, 0, '0', -1, '-5', '007', '1e2', '0x5', '1.5'];
233
- foreach ($invalidIds as $inv) {{
234
- try {{
233
+ foreach ($invalidIds as $inv) {
234
+ try {
235
235
  komagu_normalize_id($inv);
236
236
  echo "FAIL: Expected exception for " . var_export($inv, true) . "\\n";
237
237
  exit(1);
238
- }} catch (InvalidArgumentException $e) {{
238
+ } catch (InvalidArgumentException $e) {
239
239
  // Expected
240
- }}
241
- }}
240
+ }
241
+ }
242
242
 
243
243
  // 2. Test komagu_check_ownership with SQLite in-memory PDO
244
244
  $pdo = new PDO('sqlite::memory:');
@@ -258,7 +258,7 @@ public class OwnershipEnforcerBehaviouralTest {
258
258
  assert(komagu_check_ownership($pdo, 'orders', 'id', '', 'user_id', 100, $allowed) === false);
259
259
 
260
260
  echo "ALL_PHP_BEHAVIOURAL_TESTS_PASSED\\n";
261
- """
261
+ """.replace("__PHP_FILE__", str(php_file)) # nosec B608 # PHP test script with a fixed repo path, not a query
262
262
  proc = subprocess.run(
263
263
  ["php", "-r", php_test_script],
264
264
  capture_output=True,
@@ -16,9 +16,9 @@ public enum SortDirection
16
16
  /// Strict allow-list validator for dynamic column names and sort clauses.
17
17
  /// Prevents SQL injection via dynamic ORDER BY clauses.
18
18
  /// </summary>
19
- public sealed class SafeSortAllowList
19
+ public sealed partial class SafeSortAllowList
20
20
  {
21
- private static readonly Regex SafeIdentifierRegex = new("^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled);
21
+ private static readonly Regex SafeIdentifierRegex = SafeIdentifier();
22
22
  private readonly HashSet<string> _allowedColumns;
23
23
 
24
24
  public SafeSortAllowList(IEnumerable<string> allowedColumns)
@@ -73,4 +73,7 @@ public sealed class SafeSortAllowList
73
73
  var dirString = direction == SortDirection.Descending ? "DESC" : "ASC";
74
74
  return $"{validColumn} {dirString}";
75
75
  }
76
+
77
+ [GeneratedRegex("^[a-zA-Z_][a-zA-Z0-9_]*$")]
78
+ private static partial Regex SafeIdentifier();
76
79
  }
@@ -25,18 +25,17 @@ class SafeSortAllowList(allowedColumns: Collection<String>) {
25
25
  * Throws SecurityException on validation failure.
26
26
  */
27
27
  fun validateColumnName(columnName: String?): String {
28
- if (columnName.isNullOrBlank()) {
29
- throw SecurityException("Sort column name cannot be null or blank")
30
- }
31
-
32
- val trimmed = columnName.trim()
33
-
34
- if (!safeIdentifierRegex.matches(trimmed)) {
35
- throw SecurityException("Potential SQL injection detected in sort column: '$columnName'. Illegal characters detected.")
36
- }
28
+ val trimmed = columnName?.trim().orEmpty()
29
+ val isIdentifier = trimmed.isNotEmpty() && safeIdentifierRegex.matches(trimmed)
30
+ val canonical = if (isIdentifier) allowedMap[trimmed.lowercase()] else null
31
+ return canonical ?: throw SecurityException(rejectionReason(columnName, trimmed))
32
+ }
37
33
 
38
- return allowedMap[trimmed.lowercase()]
39
- ?: throw SecurityException("Unauthorized sort column requested: '$columnName'. Column is not in allowed list.")
34
+ private fun rejectionReason(columnName: String?, trimmed: String): String = when {
35
+ trimmed.isEmpty() -> "Sort column name cannot be null or blank"
36
+ !safeIdentifierRegex.matches(trimmed) ->
37
+ "Potential SQL injection detected in sort column: '$columnName'. Illegal characters detected."
38
+ else -> "Unauthorized sort column requested: '$columnName'. Column is not in allowed list."
40
39
  }
41
40
 
42
41
  /**
@@ -1,6 +1,12 @@
1
1
  package io.komagu.security.query.detekt
2
2
 
3
- import io.gitlab.arturbosch.detekt.api.*
3
+ import io.gitlab.arturbosch.detekt.api.CodeSmell
4
+ import io.gitlab.arturbosch.detekt.api.Config
5
+ import io.gitlab.arturbosch.detekt.api.Debt
6
+ import io.gitlab.arturbosch.detekt.api.Entity
7
+ import io.gitlab.arturbosch.detekt.api.Issue
8
+ import io.gitlab.arturbosch.detekt.api.Rule
9
+ import io.gitlab.arturbosch.detekt.api.Severity
4
10
  import org.jetbrains.kotlin.lexer.KtTokens
5
11
  import org.jetbrains.kotlin.psi.KtBinaryExpression
6
12
  import org.jetbrains.kotlin.psi.KtCallExpression
@@ -33,37 +39,26 @@ class StringBuiltSqlQueryRule(config: Config = Config.empty) : Rule(config) {
33
39
  override fun visitCallExpression(expression: KtCallExpression) {
34
40
  super.visitCallExpression(expression)
35
41
 
36
- val callee = expression.calleeExpression?.text ?: return
37
- if (!targetSqlMethods.contains(callee)) {
42
+ val callee = expression.calleeExpression?.text
43
+ val firstArg = expression.valueArguments.firstOrNull()?.getArgumentExpression()
44
+ if (callee == null || callee !in targetSqlMethods || firstArg == null) {
38
45
  return
39
46
  }
40
47
 
41
- val arguments = expression.valueArguments
42
- if (arguments.isEmpty()) {
43
- return
44
- }
45
-
46
- val firstArg = arguments[0].getArgumentExpression() ?: return
47
-
48
- // 1. String template with variable interpolation: "SELECT * FROM orders WHERE id = $orderId"
49
- if (firstArg is KtStringTemplateExpression && firstArg.hasInterpolation()) {
50
- report(
51
- CodeSmell(
52
- issue = issue,
53
- entity = Entity.from(firstArg),
54
- message = "SQL/JPQL query in '$callee' uses string template interpolation. All parameters must be passed as parameterized bindings."
55
- )
56
- )
57
- return
48
+ val construction = when {
49
+ // 1. String template with variable interpolation: "SELECT * FROM orders WHERE id = $orderId"
50
+ firstArg is KtStringTemplateExpression && firstArg.hasInterpolation() -> "uses string template interpolation"
51
+ // 2. Binary string concatenation: "SELECT * FROM orders WHERE id = " + orderId
52
+ firstArg is KtBinaryExpression && firstArg.operationToken == KtTokens.PLUS ->
53
+ "is built using binary string concatenation (+)"
54
+ else -> null
58
55
  }
59
-
60
- // 2. Binary string concatenation: "SELECT * FROM orders WHERE id = " + orderId
61
- if (firstArg is KtBinaryExpression && firstArg.operationToken == KtTokens.PLUS) {
56
+ if (construction != null) {
62
57
  report(
63
58
  CodeSmell(
64
59
  issue = issue,
65
60
  entity = Entity.from(firstArg),
66
- message = "SQL/JPQL query in '$callee' is built using binary string concatenation (+). All parameters must be passed as parameterized bindings."
61
+ message = "SQL/JPQL query in '$callee' $construction. All parameters must be passed as parameterized bindings."
67
62
  )
68
63
  )
69
64
  }