@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,156 @@
1
+ #!/usr/bin/env python3
2
+ """SARIF input for BFLA and mass assignment in `komagu test --from-scan` and `--export-poc` (#289)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import copy
7
+ import importlib.util
8
+ import io
9
+ import json
10
+ import os
11
+ import sys
12
+ import tempfile
13
+ import unittest
14
+ from contextlib import redirect_stdout
15
+ from pathlib import Path
16
+ from unittest import mock
17
+
18
+ PROJECT_ROOT = Path(__file__).resolve().parents[2]
19
+ if str(PROJECT_ROOT) not in sys.path:
20
+ sys.path.insert(0, str(PROJECT_ROOT))
21
+
22
+ from operate.cli.main import main as cli_main # noqa: E402
23
+ from operate.cross_user_tester.mock_test_server import start_mock_server # noqa: E402
24
+ from operate.cross_user_tester.persona_matrix import generate_matrix_cases_from_findings # noqa: E402
25
+ from operate.exploit.generator import ExploitGenerator # noqa: E402
26
+ from operate.lsa_sarif.lsa_to_sarif import convert_findings_to_sarif # noqa: E402
27
+ from operate.lsa_sarif.sarif_findings import findings_from_sarif # noqa: E402
28
+
29
+ _spec = importlib.util.spec_from_file_location(
30
+ "lsa_findings_to_sarif", PROJECT_ROOT / "assess" / "lsa" / "scripts" / "findings_to_sarif.py"
31
+ )
32
+ assert _spec and _spec.loader
33
+ lsa_findings_to_sarif = importlib.util.module_from_spec(_spec)
34
+ _spec.loader.exec_module(lsa_findings_to_sarif)
35
+
36
+ ENV = {"T_USER": "user-secret-token"}
37
+
38
+ FINDINGS = {
39
+ "findings": [
40
+ {"finding_id": "BFLA-R-0001-1", "category": "bfla", "title": "Any user deletes accounts",
41
+ "location": {"file": "app.py", "line": 10},
42
+ "endpoint": {"method": "DELETE", "path": "/api/admin/users/{id}"}, "status": "confirmed",
43
+ "consensus": {"score": 0.9}},
44
+ {"finding_id": "MASS-M-0001-1", "category": "mass_assignment", "title": "Profile binds body",
45
+ "location": {"file": "app.py", "line": 20},
46
+ "endpoint": {"method": "PUT", "path": "/api/profile"},
47
+ "attack_vector": {"fields": {"role": "admin"}, "readback": "GET /api/profile",
48
+ "suggested_payload": "{\"role\": \"admin\"}"},
49
+ "status": "unverified", "consensus": {"score": 0.6}},
50
+ {"finding_id": "BFLA-R-0002-1", "category": "bfla", "title": "Rejected one",
51
+ "location": {"file": "app.py", "line": 30},
52
+ "endpoint": {"method": "DELETE", "path": "/api/x"}, "status": "rejected"},
53
+ {"finding_id": "BOLA-R-0003-1", "category": "bola", "title": "Not ours",
54
+ "location": {"file": "app.py", "line": 40},
55
+ "endpoint": {"method": "GET", "path": "/api/orders/{id}"}},
56
+ ]
57
+ }
58
+
59
+
60
+ def case_key(c):
61
+ return (c.kind, c.finding_id, c.method, c.path, c.probe_fields, c.readback)
62
+
63
+
64
+ def sarif_from_assess(doc):
65
+ return lsa_findings_to_sarif.to_sarif(copy.deepcopy(doc), include_rejected=True)
66
+
67
+
68
+ def sarif_from_operate(doc):
69
+ return convert_findings_to_sarif(copy.deepcopy(doc["findings"]))
70
+
71
+
72
+ class TestSarifMatrixCases(unittest.TestCase):
73
+ def test_both_exporters_give_the_same_cases_as_findings_json(self):
74
+ expected = [case_key(c) for c in generate_matrix_cases_from_findings(FINDINGS, "T_USER", default_id="7")]
75
+ self.assertEqual([k[1] for k in expected], ["BFLA-R-0001-1", "MASS-M-0001-1"])
76
+ for export in (sarif_from_assess, sarif_from_operate):
77
+ with self.subTest(exporter=export.__name__):
78
+ cases = generate_matrix_cases_from_findings(export(FINDINGS), "T_USER", default_id="7")
79
+ self.assertEqual([case_key(c) for c in cases], expected)
80
+
81
+ def test_min_consensus_applies_to_sarif(self):
82
+ cases = generate_matrix_cases_from_findings(sarif_from_assess(FINDINGS), "T_USER", min_consensus=0.8)
83
+ self.assertEqual([c.finding_id for c in cases], ["BFLA-R-0001-1"])
84
+
85
+ def test_suggested_payload_is_not_written_to_sarif(self):
86
+ text = json.dumps(sarif_from_assess(FINDINGS)) + json.dumps(sarif_from_operate(FINDINGS))
87
+ self.assertNotIn("suggested_payload", text)
88
+
89
+ def test_sarif_without_replay_properties_falls_back_to_rule_and_message(self):
90
+ sarif = sarif_from_assess(FINDINGS)
91
+ for result in sarif["runs"][0]["results"]:
92
+ for key in ("finding_id", "category", "endpoint", "attack_vector", "consensus_score"):
93
+ result["properties"].pop(key, None)
94
+ findings = {f["finding_id"]: f for f in findings_from_sarif(sarif)}
95
+ self.assertEqual(findings["BFLA-R-0001-1"]["category"], "bfla")
96
+ self.assertEqual(findings["BFLA-R-0001-1"]["endpoint"], {"method": "DELETE", "path": "/api/admin/users/{id}"})
97
+ cases = generate_matrix_cases_from_findings(sarif, "T_USER", default_id="7")
98
+ mass = next(c for c in cases if c.kind == "mass_assignment")
99
+ self.assertIn("role", mass.probe_fields) # default probe fields without attack_vector
100
+ self.assertEqual(mass.readback, ("GET", "/api/profile"))
101
+
102
+ def test_owasp_tag_identifies_category_when_rule_id_does_not(self):
103
+ sarif = sarif_from_assess(FINDINGS)
104
+ run = sarif["runs"][0]
105
+ renamed = {}
106
+ for i, rule in enumerate(run["tool"]["driver"]["rules"]):
107
+ renamed[rule["id"]] = f"custom-{i}"
108
+ rule["id"] = f"custom-{i}"
109
+ for result in run["results"]:
110
+ result["ruleId"] = renamed[result["ruleId"]]
111
+ result["properties"].pop("category", None)
112
+ kinds = {f["finding_id"]: f["category"] for f in findings_from_sarif(sarif)}
113
+ self.assertEqual(kinds["BFLA-R-0001-1"], "bfla")
114
+ self.assertEqual(kinds["MASS-M-0001-1"], "mass_assignment")
115
+
116
+
117
+ class TestSarifPocExport(unittest.TestCase):
118
+ def test_pocs_from_sarif_match_findings_json(self):
119
+ gen = ExploitGenerator(base_url="http://127.0.0.1:1", default_resource_id="7")
120
+ expected = {a.finding_id: (a.category, a.method, a.target_path) for a in gen.generate_from_findings_data(FINDINGS)}
121
+ for export in (sarif_from_assess, sarif_from_operate):
122
+ with self.subTest(exporter=export.__name__):
123
+ arts = {a.finding_id: a for a in gen.generate_from_findings_data(export(FINDINGS))}
124
+ self.assertEqual({k: (a.category, a.method, a.target_path) for k, a in arts.items()}, expected)
125
+ self.assertIn('"role": "admin"', arts["MASS-M-0001-1"].python_script)
126
+ self.assertNotIn("BFLA-R-0002-1", arts)
127
+
128
+
129
+ class TestCliFromScanSarif(unittest.TestCase):
130
+ def setUp(self):
131
+ self.server, _, self.base_url = start_mock_server(port=0)
132
+ self.tmp = tempfile.TemporaryDirectory()
133
+ self.dir = Path(self.tmp.name)
134
+
135
+ def tearDown(self):
136
+ self.server.shutdown()
137
+ self.server.server_close()
138
+ self.tmp.cleanup()
139
+
140
+ def test_from_scan_sarif_runs_bfla_and_mass_cases(self):
141
+ scan = self.dir / "findings.sarif"
142
+ scan.write_text(json.dumps(sarif_from_assess(FINDINGS)), encoding="utf-8")
143
+ out = self.dir / "res.json"
144
+ buf = io.StringIO()
145
+ with mock.patch.dict(os.environ, ENV), redirect_stdout(buf):
146
+ code = cli_main(["test", "--url", self.base_url, "--from-scan", str(scan),
147
+ "--token-env", "T_USER", "--resource-id", "7", "--output", str(out)])
148
+ self.assertEqual(code, 1)
149
+ report = json.loads(out.read_text(encoding="utf-8"))
150
+ ids = {r["finding_id"] for r in report["matrix_results"] if r["is_breach"]}
151
+ self.assertEqual(ids, {"BFLA-R-0001-1", "MASS-M-0001-1"})
152
+ self.assertNotIn(ENV["T_USER"], out.read_text(encoding="utf-8") + buf.getvalue())
153
+
154
+
155
+ if __name__ == "__main__":
156
+ unittest.main()
package/package.json CHANGED
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "name": "@hoanghainh/komagu",
3
- "version": "1.8.0",
3
+ "version": "2.0.1",
4
4
  "description": "Security-First Legacy Modernization Platform CLI (LSA Kit, IAM Perimeter, Cross-User Testing)",
5
5
  "main": "remediate/authz/nodejs/src/index.js",
6
6
  "types": "remediate/authz/nodejs/src/index.d.ts",
7
7
  "bin": {
8
- "komagu": "bin/komagu.js",
9
- "komainu": "bin/komainu-legacy.js",
10
- "komainu-guard": "bin/komainu-legacy.js"
8
+ "komagu": "bin/komagu.js"
11
9
  },
12
10
  "files": [
13
11
  "bin/",
@@ -16,7 +16,7 @@ All implementations strictly enforce these architectural guarantees:
16
16
  - **Imperative (Programmatic):** Explicit service calls (`AuthorizeAsync` / `requireOwnership`) for complex business workflows and multi-condition rules.
17
17
  - **Declarative (Metadata/Attribute/Annotation):** Endpoint filter (`[RequireOwnership]`) or AOP Aspect (`@RequireOwnership`) for clean, declarative controller boundaries.
18
18
  4. **Scoped Data-Access Helpers:** Query pipeline extensions (EF Core `WithUserScope`, Spring Data `ScopedRepository`) automatically appending tenant and owner constraints to SQL queries before execution.
19
- 5. **Structured Security Audit Logging:** Every denial emits a structured event (`Komainu.Authorization.Denied`) with `actorUserId`, `actorTenantId`, `resourceType`, `resourceId`, and failure rationale for SIEM and security monitoring.
19
+ 5. **Structured Security Audit Logging:** Every denial emits a structured event (`Komagu.Authorization.Denied`) with `actorUserId`, `actorTenantId`, `resourceType`, `resourceId`, and failure rationale for SIEM and security monitoring.
20
20
 
21
21
  ---
22
22
 
@@ -220,7 +220,7 @@ public sealed class DefaultKomaguAuthorizationService : IKomaguAuthorizationServ
220
220
  var auditEvent = new SecurityAuditEvent
221
221
  {
222
222
  Timestamp = DateTimeOffset.UtcNow,
223
- EventName = "Komainu.Authorization.Denied",
223
+ EventName = "Komagu.Authorization.Denied",
224
224
  ActorUserId = actorUserId,
225
225
  ActorTenantId = actorTenantId,
226
226
  ResourceType = typeof(T).Name,
@@ -11,9 +11,9 @@ public record SecurityAuditEvent
11
11
  public DateTimeOffset Timestamp { get; init; } = DateTimeOffset.UtcNow;
12
12
 
13
13
  /// <summary>
14
- /// Event name or category. Kept as "Komainu.*" after the rename so existing SIEM rules still match (#290).
14
+ /// Event name or category.
15
15
  /// </summary>
16
- public string EventName { get; init; } = "Komainu.Authorization.Denied";
16
+ public string EventName { get; init; } = "Komagu.Authorization.Denied";
17
17
 
18
18
  /// <summary>
19
19
  /// Distributed tracing identifier (W3C traceparent or correlation ID).
@@ -210,7 +210,7 @@ func (a *Authorizer) emitDenial(userId, tenantId string, resource IOwnedResource
210
210
 
211
211
  a.Sink.Emit(SecurityAuditEvent{
212
212
  Timestamp: time.Now().UTC(),
213
- EventName: "Komainu.Authorization.Denied",
213
+ EventName: "Komagu.Authorization.Denied",
214
214
  ActorUserID: userId,
215
215
  ActorTenantID: tenantId,
216
216
  ResourceType: resType,
@@ -140,7 +140,7 @@ class KomaguSecurityService {
140
140
 
141
141
  const event = {
142
142
  timestamp: new Date().toISOString(),
143
- eventName: 'Komainu.Authorization.Denied',
143
+ eventName: 'Komagu.Authorization.Denied',
144
144
  actorUserId: actorUserId || undefined,
145
145
  actorTenantId: actorTenantId || undefined,
146
146
  resourceType: resource && resource.constructor ? resource.constructor.name : 'Object',
@@ -59,7 +59,7 @@ test('KomaguSecurityService: denies attacker with 404 and emits audit', async ()
59
59
  // Verify audit event
60
60
  assert.strictEqual(auditSink.events.length, 2); // 1 from authorize + 1 from requireOwnership
61
61
  const event = auditSink.events[0];
62
- assert.strictEqual(event.eventName, 'Komainu.Authorization.Denied');
62
+ assert.strictEqual(event.eventName, 'Komagu.Authorization.Denied');
63
63
  assert.strictEqual(event.actorUserId, 'user-b');
64
64
  assert.strictEqual(event.actorTenantId, 'tenant-komagu');
65
65
  assert.strictEqual(event.resourceId, 'ord-01');
@@ -85,10 +85,8 @@ class BOLAOwnershipPatcher:
85
85
  def ensure_imports(self, content: str, lang: str) -> Tuple[str, bool]:
86
86
  """Ensure necessary Komagu authorization import is present at the top of the file."""
87
87
  if lang in ("typescript", "javascript"):
88
- # komainu-guard: package name before the rename (#290), already-patched files keep it
89
- for pkg in ("@hoanghainh/komagu", "komainu-guard"):
90
- if f"from '{pkg}'" in content or f'from "{pkg}"' in content:
91
- return content, False
88
+ if "from '@hoanghainh/komagu'" in content or 'from "@hoanghainh/komagu"' in content:
89
+ return content, False
92
90
  import_stmt = "import { RequireOwnership, OwnershipGuard, ResourceOperation } from '@hoanghainh/komagu';\n"
93
91
  return import_stmt + content, True
94
92
 
@@ -143,6 +141,7 @@ class BOLAOwnershipPatcher:
143
141
 
144
142
  # Check if guard is already present on or adjacent to this line
145
143
  surrounding = "".join(lines[max(0, idx - 3):min(len(lines), idx + 3)])
144
+ # [KOMAINU-FIX] is only read: nothing writes it since #216, but files patched before that still carry it (#312)
146
145
  if "RequireOwnership" in surrounding or "OwnershipGuard" in surrounding or "[KOMAINU-FIX]" in surrounding:
147
146
  return content, BOLARewriteResult(
148
147
  original_line=target_line,
@@ -48,7 +48,7 @@ class AuthorizationResult:
48
48
  class SecurityAuditEvent:
49
49
  """Structured security audit event for authorization failures."""
50
50
  timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
51
- event_name: str = "Komainu.Authorization.Denied"
51
+ event_name: str = "Komagu.Authorization.Denied"
52
52
  actor_user_id: Optional[str] = None
53
53
  actor_tenant_id: Optional[str] = None
54
54
  resource_type: str = "Unknown"
@@ -81,7 +81,7 @@ class ISecurityAuditSink(ABC):
81
81
  class LoggingSecurityAuditSink(ISecurityAuditSink):
82
82
  """Default logger-based audit sink emitting JSON to standard logging."""
83
83
 
84
- def __init__(self, logger_name: str = "komainu.security.audit") -> None:
84
+ def __init__(self, logger_name: str = "komagu.security.audit") -> None:
85
85
  import logging
86
86
  self.logger = logging.getLogger(logger_name)
87
87
 
@@ -67,7 +67,7 @@ class TestFastApiAdapter(unittest.TestCase):
67
67
  self.authz_service = KomaguAuthorizationService(audit_sink=self.audit_sink)
68
68
  self.validator = OidcTokenValidator(
69
69
  issuer="https://auth.komagu.test",
70
- audience="komainu-api",
70
+ audience="komagu-api",
71
71
  allow_dev_tokens=True,
72
72
  )
73
73
 
@@ -42,7 +42,7 @@ class MockFastApiServer(http.server.BaseHTTPRequestHandler):
42
42
  authz_service = KomaguAuthorizationService(audit_sink=InMemorySecurityAuditSink())
43
43
  validator = OidcTokenValidator(
44
44
  issuer="https://auth.komagu.test",
45
- audience="komainu-api",
45
+ audience="komagu-api",
46
46
  allow_dev_tokens=True,
47
47
  )
48
48
 
@@ -38,7 +38,7 @@ class TestPythonAuthz(unittest.TestCase):
38
38
  self.authz_service = KomaguAuthorizationService(audit_sink=self.audit_sink)
39
39
  self.validator = OidcTokenValidator(
40
40
  issuer="https://auth.komagu.test",
41
- audience="komainu-api",
41
+ audience="komagu-api",
42
42
  allow_dev_tokens=True,
43
43
  )
44
44
 
@@ -77,7 +77,7 @@ class TestPythonAuthz(unittest.TestCase):
77
77
  self.assertEqual(len(self.audit_sink.events), 0)
78
78
 
79
79
  def test_02_attacker_same_tenant_denied_with_audit(self) -> None:
80
- """Attacker Bob (same tenant) is denied access; emits Komainu.Authorization.Denied event."""
80
+ """Attacker Bob (same tenant) is denied access; emits Komagu.Authorization.Denied event."""
81
81
  result = self.authz_service.authorize(
82
82
  user=self.bob_attacker,
83
83
  resource=self.alice_order,
@@ -89,7 +89,7 @@ class TestPythonAuthz(unittest.TestCase):
89
89
  # Verify audit emission
90
90
  self.assertEqual(len(self.audit_sink.events), 1)
91
91
  event = self.audit_sink.events[0]
92
- self.assertEqual(event.event_name, "Komainu.Authorization.Denied")
92
+ self.assertEqual(event.event_name, "Komagu.Authorization.Denied")
93
93
  self.assertEqual(event.actor_user_id, "user-b")
94
94
  self.assertEqual(event.actor_tenant_id, "tenant-komagu")
95
95
  self.assertEqual(event.resource_id, "ord-001")
@@ -182,7 +182,7 @@ class DefaultKomaguSecurityManager(
182
182
 
183
183
  val auditEvent = SecurityAuditEvent(
184
184
  timestamp = Instant.now(),
185
- eventName = "Komainu.Authorization.Denied",
185
+ eventName = "Komagu.Authorization.Denied",
186
186
  actorUserId = actorUserId,
187
187
  actorTenantId = actorTenantId,
188
188
  resourceType = resource?.let { it::class.java.simpleName } ?: "Unknown",
@@ -7,8 +7,7 @@ import java.time.Instant
7
7
  */
8
8
  data class SecurityAuditEvent(
9
9
  val timestamp: Instant = Instant.now(),
10
- // Kept as "Komainu.*" after the rename so existing SIEM rules still match (#290).
11
- val eventName: String = "Komainu.Authorization.Denied",
10
+ val eventName: String = "Komagu.Authorization.Denied",
12
11
  val traceId: String? = null,
13
12
  val actorUserId: String? = null,
14
13
  val actorTenantId: String? = null,
@@ -157,7 +157,7 @@ class TestBOLAOwnershipPatcher(unittest.TestCase):
157
157
  """If file already contains ownership guards, patcher must skip to prevent duplicates."""
158
158
  controller_file = self.test_dir / "protected_order.ts"
159
159
  controller_file.write_text(
160
- "import { RequireOwnership } from 'komainu-guard';\n"
160
+ "import { RequireOwnership } from '@hoanghainh/komagu';\n"
161
161
  "export class OrderController {\n"
162
162
  " @UseGuards(OwnershipGuard)\n"
163
163
  " @RequireOwnership(ResourceOperation.READ)\n"
@@ -1,6 +1,5 @@
1
1
  # ==============================================================================
2
2
  # Komagu Custom OWASP CRS v4 Tuning & Rule Exclusions
3
- # Rule tags stay 'komainu-*' after the rename so existing WAF log alerts still match (#290).
4
3
  # ==============================================================================
5
4
 
6
5
  # Enable JSON request body inspection for application/json Content-Type
@@ -33,7 +32,7 @@ SecRule ARGS|REQUEST_URI|REQUEST_HEADERS "@rx (?i)(?:(?:\bunion\b[\s\S]*?\bselec
33
32
  msg:'Komagu WAF: SQL Injection Attack Detected',\
34
33
  logdata:'Matched Data: %{TX.0}',\
35
34
  severity:'CRITICAL',\
36
- tag:'komainu-sqli',\
35
+ tag:'komagu-sqli',\
37
36
  setvar:'tx.sql_injection_score=+5',\
38
37
  setvar:'tx.inbound_anomaly_score_exceeded=1'"
39
38
 
@@ -48,7 +47,7 @@ SecRule ARGS|REQUEST_URI "@rx (?i)(?:<script[^>]*>|javascript:[^\"']*|onerror\s*
48
47
  msg:'Komagu WAF: Cross-Site Scripting (XSS) Attack Detected',\
49
48
  logdata:'Matched Data: %{TX.0}',\
50
49
  severity:'CRITICAL',\
51
- tag:'komainu-xss',\
50
+ tag:'komagu-xss',\
52
51
  setvar:'tx.xss_score=+5',\
53
52
  setvar:'tx.inbound_anomaly_score_exceeded=1'"
54
53
 
@@ -62,4 +61,4 @@ SecRule REQUEST_URI|ARGS "@rx (?i)(?:\.\./|\.\.\\)" \
62
61
  msg:'Komagu WAF: Path Traversal Attack Detected',\
63
62
  logdata:'Matched Data: %{TX.0}',\
64
63
  severity:'CRITICAL',\
65
- tag:'komainu-path-traversal'"
64
+ tag:'komagu-path-traversal'"
@@ -3,8 +3,6 @@
3
3
 
4
4
  Translates confirmed LSA findings (BOLA & SQLi) into deterministic,
5
5
  production-ready SecRule configurations for instant Day-2 edge protection.
6
-
7
- Rule tags stay 'komainu-*' after the rename so existing WAF log alerts still match (#290).
8
6
  """
9
7
 
10
8
  from __future__ import annotations
@@ -74,8 +72,8 @@ SecRule REQUEST_URI "@rx {path_regex}" \\
74
72
  msg:'Komagu Virtual Patch: SQL Injection blocked on {path}',\\
75
73
  logdata:'Matched Data: %{{TX.0}}',\\
76
74
  severity:'CRITICAL',\\
77
- tag:'komainu-virtual-patch',\\
78
- tag:'komainu-sqli',\\
75
+ tag:'komagu-virtual-patch',\\
76
+ tag:'komagu-sqli',\\
79
77
  tag:'{finding_id}',\\
80
78
  setvar:'tx.sql_injection_score=+5',\\
81
79
  setvar:'tx.inbound_anomaly_score_exceeded=1',\\
@@ -115,8 +113,8 @@ SecRule REQUEST_URI "@rx {path_regex}" \\
115
113
  t:urlDecodeUni,\\
116
114
  msg:'Komagu Virtual Patch: Missing Authorization Token on BOLA Endpoint {path}',\\
117
115
  severity:'CRITICAL',\\
118
- tag:'komainu-virtual-patch',\\
119
- tag:'komainu-bola',\\
116
+ tag:'komagu-virtual-patch',\\
117
+ tag:'komagu-bola',\\
120
118
  tag:'{finding_id}',\\
121
119
  chain"
122
120
  SecRule &REQUEST_HEADERS:Authorization "@eq 0"
@@ -11,7 +11,7 @@ services:
11
11
  KONG_PROXY_ERROR_LOG: /dev/stderr
12
12
  KONG_ADMIN_ERROR_LOG: /dev/stderr
13
13
  KONG_ADMIN_LISTEN: 0.0.0.0:8001
14
- KOMAGU_JWT_SECRET: ${KOMAGU_JWT_SECRET:-${KOMAINU_JWT_SECRET:-komagu-default-test-jwt-secret-key-32chars}}
14
+ KOMAGU_JWT_SECRET: ${KOMAGU_JWT_SECRET:-komagu-default-test-jwt-secret-key-32chars}
15
15
  volumes:
16
16
  - ./kong/kong.yaml:/kong/declarative/kong.yaml:ro
17
17
  ports:
@@ -16,18 +16,12 @@ import os
16
16
  import secrets
17
17
  import threading
18
18
  import time
19
- import warnings
20
19
  from typing import Any, Callable, Dict, List, Optional, Tuple
21
20
 
22
21
 
23
22
  def _env(name: str, default: Optional[str] = None) -> Optional[str]:
24
- """Read KOMAGU_<name>, falling back to the deprecated KOMAINU_<name> (#290)."""
25
- value = os.environ.get("KOMAGU_" + name)
26
- if value is None:
27
- value = os.environ.get("KOMAINU_" + name)
28
- if value is not None:
29
- warnings.warn(f"KOMAINU_{name} is deprecated, set KOMAGU_{name} instead", DeprecationWarning, stacklevel=2)
30
- return default if value is None else value
23
+ """Read KOMAGU_<name>."""
24
+ return os.environ.get("KOMAGU_" + name, default)
31
25
 
32
26
 
33
27
  try:
@@ -22,18 +22,12 @@ import re
22
22
  import secrets
23
23
  import sys
24
24
  import time
25
- import warnings
26
25
  from typing import Any, Dict, List, Optional, Set, Tuple
27
26
 
28
27
 
29
28
  def _env(name: str, default: Optional[str] = None) -> Optional[str]:
30
- """Read KOMAGU_<name>, falling back to the deprecated KOMAINU_<name> (#290)."""
31
- value = os.environ.get("KOMAGU_" + name)
32
- if value is None:
33
- value = os.environ.get("KOMAINU_" + name)
34
- if value is not None:
35
- warnings.warn(f"KOMAINU_{name} is deprecated, set KOMAGU_{name} instead", DeprecationWarning, stacklevel=2)
36
- return default if value is None else value
29
+ """Read KOMAGU_<name>."""
30
+ return os.environ.get("KOMAGU_" + name, default)
37
31
 
38
32
 
39
33
  # Allowed fields in API requests under Zero-PII contract
@@ -15,7 +15,7 @@ Every incoming request bearing an access token is validated against 5 mandatory
15
15
  | **Missing Token** | No `Authorization: Bearer <token>` header supplied. | **HTTP 401 Unauthorized** |
16
16
  | **Token Expiry** | Current timestamp > `exp` claim (`ClockSkew` enforced to 0s or strict delta). | **HTTP 401 Unauthorized** |
17
17
  | **Issuer Validation** | Token `iss` claim does not match configured trusted IdP authority. | **HTTP 401 Unauthorized** |
18
- | **Audience Validation** | Token `aud` claim does not include the target resource server ID (`komainu-api`). | **HTTP 401 Unauthorized** |
18
+ | **Audience Validation** | Token `aud` claim does not include the target resource server ID (`komagu-api`). | **HTTP 401 Unauthorized** |
19
19
  | **Cryptographic Signature** | Token signed by untrusted key or payload tampered with. | **HTTP 401 Unauthorized** |
20
20
 
21
21
  ---
@@ -36,7 +36,7 @@ Keycloak starts at `http://localhost:8080` with the `komagu` realm pre-loaded:
36
36
  - **Realm Discovery URL:** `http://localhost:8080/realms/komagu/.well-known/openid-configuration`
37
37
  - **Clients:**
38
38
  - `komagu-portal`: Public SPA client (Standard Flow + PKCE enforced)
39
- - `komainu-api`: Resource server bearer-only client (`aud`: `komainu-api`)
39
+ - `komagu-api`: Resource server bearer-only client (`aud`: `komagu-api`)
40
40
  - **Test Personas:**
41
41
  - `alice` (Password: `password123`, Tenant: `tenant-alpha`, Role: `Member`)
42
42
  - `bob` (Password: `password123`, Tenant: `tenant-alpha`, Role: `Member`)
@@ -58,7 +58,7 @@ var builder = WebApplication.CreateBuilder(args);
58
58
  builder.Services.AddKomaguJwtAuthentication(options =>
59
59
  {
60
60
  options.Issuer = builder.Configuration["Authentication:Issuer"] ?? "https://auth.komagu.test/realms/komagu";
61
- options.Audience = "komainu-api";
61
+ options.Audience = "komagu-api";
62
62
  options.RequireHttpsMetadata = builder.Environment.IsProduction();
63
63
  options.ClockSkew = TimeSpan.Zero;
64
64
  });
@@ -82,13 +82,13 @@ komagu:
82
82
  security:
83
83
  oidc:
84
84
  issuer: "https://auth.komagu.test/realms/komagu"
85
- audience: "komainu-api"
85
+ audience: "komagu-api"
86
86
  jwk-set-uri: "https://auth.komagu.test/realms/komagu/protocol/openid-connect/certs"
87
87
  clock-skew-seconds: 0
88
88
  ```
89
89
 
90
90
  The auto-configuration registers:
91
- 1. `KomaguAudienceValidator`: Enforces `aud == komainu-api`.
91
+ 1. `KomaguAudienceValidator`: Enforces `aud == komagu-api`.
92
92
  2. `KomaguJwtAuthenticationConverter`: Maps `sub` to principal and `tenant_id` claim to authentication details.
93
93
  3. Stateless `SecurityFilterChain` permitting public endpoints and enforcing authentication on all other APIs.
94
94
 
@@ -13,10 +13,10 @@ public sealed class KomaguOidcOptions
13
13
  public string Issuer { get; set; } = string.Empty;
14
14
 
15
15
  /// <summary>
16
- /// Expected resource server audience identifier (e.g. "komainu-api").
16
+ /// Expected resource server audience identifier (e.g. "komagu-api").
17
17
  /// The pre-rename default is kept so issued tokens stay valid (#290).
18
18
  /// </summary>
19
- public string Audience { get; set; } = "komainu-api";
19
+ public string Audience { get; set; } = "komagu-api";
20
20
 
21
21
  /// <summary>
22
22
  /// OIDC well-known configuration endpoint URI.
@@ -18,7 +18,7 @@ public sealed class TestTokenGenerator : IDisposable
18
18
 
19
19
  public string GenerateToken(
20
20
  string issuer = "https://auth.komagu.test/realms/komagu",
21
- string audience = "komainu-api",
21
+ string audience = "komagu-api",
22
22
  string userId = "user-alice",
23
23
  string tenantId = "tenant-alpha",
24
24
  DateTime? expires = null,
@@ -14,7 +14,7 @@ using Xunit;
14
14
  public class TokenValidationTests : IDisposable
15
15
  {
16
16
  private const string ExpectedIssuer = "https://auth.komagu.test/realms/komagu";
17
- private const string ExpectedAudience = "komainu-api";
17
+ private const string ExpectedAudience = "komagu-api";
18
18
 
19
19
  private readonly TestTokenGenerator _tokenGenerator;
20
20
  private readonly TestServer _server;
@@ -11,7 +11,6 @@ from __future__ import annotations
11
11
  import math
12
12
  import os
13
13
  import sys
14
- import warnings
15
14
  from typing import Any, Dict, List, Optional, Union
16
15
 
17
16
  import jwt
@@ -33,13 +32,8 @@ from .models import UserPrincipal
33
32
 
34
33
 
35
34
  def _env(name: str, default: Optional[str] = None) -> Optional[str]:
36
- """Read KOMAGU_<name>, falling back to the deprecated KOMAINU_<name> (#290)."""
37
- value = os.environ.get("KOMAGU_" + name)
38
- if value is None:
39
- value = os.environ.get("KOMAINU_" + name)
40
- if value is not None:
41
- warnings.warn(f"KOMAINU_{name} is deprecated, set KOMAGU_{name} instead", DeprecationWarning, stacklevel=2)
42
- return default if value is None else value
35
+ """Read KOMAGU_<name>."""
36
+ return os.environ.get("KOMAGU_" + name, default)
43
37
 
44
38
 
45
39
  # Standard test/dev personas matching Komagu cross-user test fixtures
@@ -44,7 +44,7 @@ class TestOidcTokenValidator(unittest.TestCase):
44
44
  cls.attacker_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
45
45
 
46
46
  cls.issuer = "https://identity.komagu.test"
47
- cls.audience = "komainu-api"
47
+ cls.audience = "komagu-api"
48
48
 
49
49
  def _create_token(
50
50
  self,
@@ -13,10 +13,10 @@ data class KomaguOidcProperties(
13
13
  var issuer: String = "",
14
14
 
15
15
  /**
16
- * Expected resource server audience (default: "komainu-api"). Must not be blank.
16
+ * Expected resource server audience (default: "komagu-api"). Must not be blank.
17
17
  * The pre-rename default is kept so issued tokens stay valid (#290).
18
18
  */
19
- var audience: String = "komainu-api",
19
+ var audience: String = "komagu-api",
20
20
 
21
21
  /**
22
22
  * JSON Web Key Set (JWKS) endpoint URI. Must use HTTPS unless allowInsecureHttpJwks is true.
@@ -101,7 +101,7 @@ class KomaguIdentityAutoConfigurationTest {
101
101
 
102
102
  val port = server.address.port
103
103
  val issuer = "https://auth.komagu.test/realms/komagu"
104
- val audience = "komainu-api"
104
+ val audience = "komagu-api"
105
105
 
106
106
  try {
107
107
  contextRunner
@@ -255,7 +255,7 @@ class KomaguIdentityAutoConfigurationTest {
255
255
  }
256
256
  server.start()
257
257
 
258
- val audience = "komainu-api"
258
+ val audience = "komagu-api"
259
259
 
260
260
  try {
261
261
  // Configure ONLY spring.security.oauth2.resourceserver.jwt.issuer-uri
@@ -26,7 +26,7 @@ class TestJwtFactory {
26
26
 
27
27
  fun createToken(
28
28
  issuer: String = "https://auth.komagu.test/realms/komagu",
29
- audience: String = "komainu-api",
29
+ audience: String = "komagu-api",
30
30
  subject: String? = "alice-100",
31
31
  tenantId: String = "tenant-alpha",
32
32
  issueTime: Instant = Instant.now(),