@hoanghainh/komagu 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/LICENSE +202 -0
- package/README.md +1 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/ai-run-stats.json +87 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/auth-model.md +120 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/index.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/injection-001.json +383 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/injection-002.json +205 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/privilege-001.json +838 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/privilege-002.json +124 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-001.json +778 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-002.json +546 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-003.json +661 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-004.json +659 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-005.json +612 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/batches/routes-006.json +106 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/coverage.json +19 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-001.json +516 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-002.json +332 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-003.json +218 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-004.json +561 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-005.json +316 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/bola-routes-006.json +14 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/inj-injection-001.json +782 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/inj-injection-002.json +418 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/priv-privilege-001.json +627 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/priv-privilege-002.json +253 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-1.json +68 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-10.json +68 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-11.json +61 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-12.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-13.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-14.json +66 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-15.json +69 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-16.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-17.json +61 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-18.json +69 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-19.json +64 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-2.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-20.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-21.json +61 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-22.json +50 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-3.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-4.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-5.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-6.json +70 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-7.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-8.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings/verified-9.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/findings.json +4857 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/report.ja.md +2112 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/report.md +2112 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/score.json +82 -0
- package/assess/lsa/examples/eval-run-6/gptoss-a/stack-profile.json +225 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/ai-run-stats.json +91 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/auth-model.md +120 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/index.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/injection-001.json +383 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/injection-002.json +205 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/privilege-001.json +838 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/privilege-002.json +124 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-001.json +778 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-002.json +546 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-003.json +661 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-004.json +659 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-005.json +612 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/batches/routes-006.json +106 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/coverage.json +21 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-001.json +593 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-002.json +367 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-003.json +207 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-004.json +477 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-005.json +341 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/bola-routes-006.json +14 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/inj-injection-001.json +821 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/inj-injection-002.json +410 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/priv-privilege-001.json +649 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/priv-privilege-002.json +222 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-1.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-10.json +67 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-11.json +68 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-12.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-13.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-14.json +64 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-15.json +70 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-16.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-17.json +61 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-18.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-19.json +65 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-2.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-20.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-21.json +62 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-22.json +50 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-3.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-4.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-5.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-6.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-7.json +64 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-8.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings/verified-9.json +60 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/findings.json +4966 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/score.json +87 -0
- package/assess/lsa/examples/eval-run-6/gptoss-b/stack-profile.json +225 -0
- package/assess/lsa/examples/eval-run-6/ground-truth.json +851 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/ai-run-stats.json +96 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/auth-model.md +191 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/index.json +60 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/injection-001.json +383 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/injection-002.json +205 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/privilege-001.json +838 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/privilege-002.json +124 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-001.json +778 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-002.json +546 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-003.json +661 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-004.json +659 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-005.json +612 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/batches/routes-006.json +106 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/coverage.json +31 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-001.json +527 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-002.json +568 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-003.json +467 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-004.json +342 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-005.json +693 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/bola-routes-006.json +16 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/inj-injection-001.json +1123 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/inj-injection-002.json +391 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/priv-privilege-001.json +726 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/priv-privilege-002.json +306 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-1.json +137 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-10.json +106 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-11.json +81 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-12.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-13.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-14.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-15.json +110 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-16.json +116 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-17.json +141 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-18.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-19.json +140 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-2.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-20.json +82 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-21.json +74 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-22.json +99 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-3.json +140 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-4.json +145 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-5.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-6.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-7.json +87 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-8.json +125 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings/verified-9.json +135 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/findings.json +6989 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/memory.log +36 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/score.json +106 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-nothink/stack-profile.json +225 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/auth-model.md +517 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-001.json +5 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-002.json +428 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-003.json +388 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-004.json +463 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-005.json +5 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/bola-routes-006.json +67 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings/inj-injection-001.json +964 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/findings.partial.json +1872 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/memory.log +194 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/score.partial.json +108 -0
- package/assess/lsa/examples/eval-run-6/qwen-16k-partial/stack-profile.json +225 -0
- package/assess/lsa/scripts/detect_stack.py +13 -5
- package/assess/lsa/scripts/find_injection.py +35 -2
- package/assess/lsa/scripts/java_routes.py +323 -0
- package/assess/lsa/scripts/laravel_routes.py +102 -0
- package/assess/lsa/scripts/lsa_common.py +72 -2
- package/assess/lsa/scripts/map_routes.py +40 -161
- package/assess/lsa/scripts/merge_findings.py +2 -0
- package/assess/lsa/tests/EVAL-RESULTS.md +126 -0
- package/assess/lsa/tests/test_eval_run_6.py +104 -0
- package/assess/lsa/tests/test_java_legacy_realistic.py +453 -0
- package/assess/lsa/tests/test_laravel_routes.py +95 -0
- package/contracts/schemas/persona-suite.schema.json +38 -2
- package/examples/member-portal/backend-dotnet/Dockerfile +2 -1
- package/examples/member-portal/backend-spring/Dockerfile +2 -0
- package/examples/member-portal/docker-compose.yml +1 -1
- package/examples/member-portal/frontend/Dockerfile +3 -2
- package/examples/member-portal/frontend/package-lock.json +24 -656
- package/examples/member-portal/frontend/package.json +1 -1
- package/operate/ai/client.py +22 -9
- package/operate/ai/runner.py +24 -4
- package/operate/cli/__init__.py +1 -1
- package/operate/cli/commands/fix.py +4 -0
- package/operate/cli/commands/leak_check.py +75 -0
- package/operate/cli/commands/protect.py +32 -3
- package/operate/cli/commands/scan.py +8 -1
- package/operate/cli/commands/test.py +131 -15
- package/operate/cli/main.py +5 -3
- package/operate/cross_user_tester/persona_matrix.py +118 -25
- package/operate/cross_user_tester/runner.py +190 -7
- package/operate/cross_user_tester/session_config.py +180 -0
- package/operate/cross_user_tester/session_http.py +288 -0
- package/operate/devsecops/leak_check.py +314 -0
- package/operate/remediator/runner.py +18 -1
- package/operate/remediator/strategies.py +2 -2
- package/operate/simulate/scenarios.py +2 -2
- package/operate/tests/mock_legacy_session_app.py +160 -0
- package/operate/tests/test_ai_reasoning_effort.py +65 -0
- package/operate/tests/test_ai_route_inventory.py +53 -0
- package/operate/tests/test_cross_user_session.py +565 -0
- package/operate/tests/test_docker_packaging.py +3 -3
- package/operate/tests/test_headless_ai.py +6 -0
- package/operate/tests/test_leak_check.py +250 -0
- package/operate/tests/test_legacy_session_edge.py +192 -0
- package/operate/tests/test_pilot_safeguards.py +245 -0
- package/operate/tests/test_remediator_java_legacy.py +330 -0
- package/package.json +2 -1
- package/remediate/authz/patcher.py +120 -11
- package/remediate/authz/tests/test_bola_patcher.py +4 -1
- package/remediate/edge/coraza/Caddyfile +1 -1
- package/remediate/edge/coraza/Dockerfile +9 -0
- package/remediate/edge/coraza/rules/coraza-crs-custom.conf +12 -0
- package/remediate/edge/coraza/virtual_patcher.py +24 -0
- package/remediate/edge/docker-compose.yml +7 -2
- package/remediate/edge/kong/kong-legacy-session.yaml +51 -0
- package/remediate/edge/mock-backend/Dockerfile +2 -0
- package/remediate/edge/tests/test_edge_security.py +21 -1
- package/remediate/resilience/terraform/backup_worm.tf +39 -1
- package/remediate/resilience/terraform/main.tf +15 -3
- package/remediate/resilience/terraform/variables.tf +11 -0
- package/remediate/resilience/tests/test_resilience.py +16 -0
|
@@ -379,7 +379,7 @@ class IDCFrontierRansomwareScenario(BaseScenario):
|
|
|
379
379
|
self.target_dir.mkdir(parents=True, exist_ok=True)
|
|
380
380
|
test_file.write_text("Komagu Immutable WORM Backup - Read Only", encoding="utf-8")
|
|
381
381
|
# Mark file read-only (simulating local WORM compliance mode)
|
|
382
|
-
os.chmod(str(test_file),
|
|
382
|
+
os.chmod(str(test_file), 0o400)
|
|
383
383
|
except Exception:
|
|
384
384
|
pass
|
|
385
385
|
|
|
@@ -416,7 +416,7 @@ class IDCFrontierRansomwareScenario(BaseScenario):
|
|
|
416
416
|
|
|
417
417
|
# Clean up
|
|
418
418
|
try:
|
|
419
|
-
os.chmod(str(test_file),
|
|
419
|
+
os.chmod(str(test_file), 0o600)
|
|
420
420
|
test_file.unlink(missing_ok=True)
|
|
421
421
|
except Exception:
|
|
422
422
|
pass
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Small session-based app that behaves like a JSP / Struts application.
|
|
3
|
+
|
|
4
|
+
Used to rehearse ``komagu test`` on legacy behaviour:
|
|
5
|
+
|
|
6
|
+
- login state lives in a ``JSESSIONID`` cookie;
|
|
7
|
+
- writes are ``application/x-www-form-urlencoded`` and need a single-use Struts token;
|
|
8
|
+
- a successful write answers 302 (post/redirect/get);
|
|
9
|
+
- a denied request is a 200 Shift_JIS error page, a 302 to login, or a 404,
|
|
10
|
+
depending on ``server.denial``;
|
|
11
|
+
- ``server.fixed`` is the ownership check being deployed.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import secrets
|
|
17
|
+
import threading
|
|
18
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
19
|
+
from typing import Dict, List, NamedTuple, Optional, Tuple
|
|
20
|
+
from urllib.parse import parse_qs, urlsplit
|
|
21
|
+
|
|
22
|
+
ALICE_COOKIE = "JSESSIONID=sess-alice-0001"
|
|
23
|
+
BOB_COOKIE = "JSESSIONID=sess-bob-0002"
|
|
24
|
+
EXPIRED_COOKIE = "JSESSIONID=sess-expired-0003"
|
|
25
|
+
TOKEN_FIELD = "org.apache.struts.taglib.html.TOKEN"
|
|
26
|
+
DENIED_TEXT = "アクセス権限がありません"
|
|
27
|
+
LOGIN_REDIRECT = "/login.do;jsessionid=sess-rewritten-url-9999"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Seen(NamedTuple):
|
|
31
|
+
method: str
|
|
32
|
+
path: str
|
|
33
|
+
cookie: Optional[str]
|
|
34
|
+
authorization: Optional[str]
|
|
35
|
+
content_type: Optional[str]
|
|
36
|
+
body: str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LegacyHandler(BaseHTTPRequestHandler):
|
|
40
|
+
server: "LegacyServer"
|
|
41
|
+
|
|
42
|
+
def log_message(self, format: str, *args: object) -> None: # noqa: A002
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def _user(self) -> Optional[str]:
|
|
46
|
+
cookie = self.headers.get("Cookie", "")
|
|
47
|
+
pairs = dict(p.strip().split("=", 1) for p in cookie.split(";") if "=" in p)
|
|
48
|
+
return self.server.sessions.get(pairs.get("JSESSIONID", ""))
|
|
49
|
+
|
|
50
|
+
def _send(self, status: int, body: bytes = b"", charset: str = "UTF-8", location: Optional[str] = None) -> None:
|
|
51
|
+
self.send_response(status)
|
|
52
|
+
self.send_header("Content-Type", f"text/html; charset={charset}")
|
|
53
|
+
self.send_header("Content-Length", str(len(body)))
|
|
54
|
+
if location:
|
|
55
|
+
self.send_header("Location", location)
|
|
56
|
+
self.end_headers()
|
|
57
|
+
self.wfile.write(body)
|
|
58
|
+
|
|
59
|
+
def _to_login(self) -> None:
|
|
60
|
+
self._send(302, location=LOGIN_REDIRECT)
|
|
61
|
+
|
|
62
|
+
def _deny(self) -> None:
|
|
63
|
+
if self.server.denial == "login_redirect":
|
|
64
|
+
self._to_login()
|
|
65
|
+
elif self.server.denial == "not_found":
|
|
66
|
+
self._send(404, b"<html>Not Found</html>")
|
|
67
|
+
else:
|
|
68
|
+
page = f"<html><body><p>{DENIED_TEXT}</p></body></html>"
|
|
69
|
+
self._send(200, page.encode("cp932"), charset="Windows-31J")
|
|
70
|
+
|
|
71
|
+
def _may_access(self, user: str, order_id: str) -> bool:
|
|
72
|
+
owner = self.server.orders.get(order_id)
|
|
73
|
+
if owner is None:
|
|
74
|
+
return False
|
|
75
|
+
return owner == user or not self.server.fixed
|
|
76
|
+
|
|
77
|
+
def _record(self, method: str, body: str = "") -> None:
|
|
78
|
+
self.server.seen.append(Seen(
|
|
79
|
+
method, self.path, self.headers.get("Cookie"), self.headers.get("Authorization"),
|
|
80
|
+
self.headers.get("Content-Type"), body,
|
|
81
|
+
))
|
|
82
|
+
|
|
83
|
+
def do_GET(self) -> None: # noqa: N802
|
|
84
|
+
self._record("GET")
|
|
85
|
+
url = urlsplit(self.path)
|
|
86
|
+
query = parse_qs(url.query)
|
|
87
|
+
if url.path == "/login.do":
|
|
88
|
+
self._send(200, b"<html>Please log in</html>")
|
|
89
|
+
return
|
|
90
|
+
user = self._user()
|
|
91
|
+
if user is None:
|
|
92
|
+
self._to_login()
|
|
93
|
+
elif url.path == "/home.do":
|
|
94
|
+
self._send(200, f"<html>Welcome {user}</html>".encode())
|
|
95
|
+
elif url.path == "/order/view.do":
|
|
96
|
+
self._view(user, query.get("id", [""])[0])
|
|
97
|
+
elif url.path == "/order/edit.do":
|
|
98
|
+
self._edit_form(user)
|
|
99
|
+
else:
|
|
100
|
+
self._send(404, b"<html>No such action</html>")
|
|
101
|
+
|
|
102
|
+
def _view(self, user: str, order_id: str) -> None:
|
|
103
|
+
if not self._may_access(user, order_id):
|
|
104
|
+
self._deny()
|
|
105
|
+
return
|
|
106
|
+
self._send(200, f"<html>Order detail {order_id}: secret-note</html>".encode())
|
|
107
|
+
|
|
108
|
+
def _edit_form(self, user: str) -> None:
|
|
109
|
+
if self.server.hide_token:
|
|
110
|
+
self._send(200, b"<html><form></form></html>")
|
|
111
|
+
return
|
|
112
|
+
token = secrets.token_hex(8)
|
|
113
|
+
self.server.tokens[user] = token
|
|
114
|
+
# value before name, as some tag libraries render it.
|
|
115
|
+
page = f'<html><form><input value="{token}" type="hidden" name="{TOKEN_FIELD}"></form></html>'
|
|
116
|
+
self._send(200, page.encode())
|
|
117
|
+
|
|
118
|
+
def do_POST(self) -> None: # noqa: N802
|
|
119
|
+
length = int(self.headers.get("Content-Length") or 0)
|
|
120
|
+
raw = self.rfile.read(length).decode("utf-8", "replace")
|
|
121
|
+
self._record("POST", raw)
|
|
122
|
+
user = self._user()
|
|
123
|
+
if user is None:
|
|
124
|
+
self._to_login()
|
|
125
|
+
return
|
|
126
|
+
if self.headers.get("Content-Type", "").split(";")[0] != "application/x-www-form-urlencoded":
|
|
127
|
+
self._send(415, b"<html>Form post expected</html>")
|
|
128
|
+
return
|
|
129
|
+
form = parse_qs(raw)
|
|
130
|
+
if urlsplit(self.path).path != "/order/update.do":
|
|
131
|
+
self._send(404, b"<html>No such action</html>")
|
|
132
|
+
return
|
|
133
|
+
if form.get(TOKEN_FIELD, [""])[0] != self.server.tokens.pop(user, None):
|
|
134
|
+
self._send(400, b"<html>Invalid token</html>")
|
|
135
|
+
return
|
|
136
|
+
order_id = form.get("id", [""])[0]
|
|
137
|
+
if not self._may_access(user, order_id):
|
|
138
|
+
self._deny()
|
|
139
|
+
return
|
|
140
|
+
self.server.updates.append((user, order_id))
|
|
141
|
+
self._send(302, location="/order/list.do")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class LegacyServer(HTTPServer):
|
|
145
|
+
def __init__(self) -> None:
|
|
146
|
+
super().__init__(("127.0.0.1", 0), LegacyHandler)
|
|
147
|
+
self.fixed = False
|
|
148
|
+
self.denial = "error_page"
|
|
149
|
+
self.hide_token = False
|
|
150
|
+
self.orders: Dict[str, str] = {"100": "alice", "200": "bob"}
|
|
151
|
+
self.sessions: Dict[str, str] = {"sess-alice-0001": "alice", "sess-bob-0002": "bob"}
|
|
152
|
+
self.tokens: Dict[str, str] = {}
|
|
153
|
+
self.updates: List[Tuple[str, str]] = []
|
|
154
|
+
self.seen: List[Seen] = []
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def start_legacy_app() -> Tuple[LegacyServer, str]:
|
|
158
|
+
server = LegacyServer()
|
|
159
|
+
threading.Thread(target=server.serve_forever, daemon=True).start()
|
|
160
|
+
return server, f"http://127.0.0.1:{server.server_port}"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""`--ai-reasoning-effort` reaches each /chat/completions request and ai-run-stats.json (#382)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import shutil
|
|
9
|
+
import tempfile
|
|
10
|
+
import unittest
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from operate.ai.client import LLMClient
|
|
14
|
+
from operate.ai.runner import HeadlessAIRunner
|
|
15
|
+
from operate.cli.commands.scan import register_subcommand
|
|
16
|
+
|
|
17
|
+
MESSAGES = [{"role": "user", "content": "audit"}]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestReasoningEffortPayload(unittest.TestCase):
|
|
21
|
+
def test_not_sent_by_default(self) -> None:
|
|
22
|
+
payload = LLMClient(model="m").build_payload(MESSAGES)
|
|
23
|
+
self.assertEqual(payload, {"model": "m", "messages": MESSAGES, "temperature": 0.1})
|
|
24
|
+
|
|
25
|
+
def test_sent_when_set(self) -> None:
|
|
26
|
+
payload = LLMClient(model="m", reasoning_effort="none").build_payload(MESSAGES, max_tokens=50)
|
|
27
|
+
self.assertEqual(payload["reasoning_effort"], "none")
|
|
28
|
+
self.assertEqual(payload["max_tokens"], 50)
|
|
29
|
+
|
|
30
|
+
def test_unknown_value_is_rejected(self) -> None:
|
|
31
|
+
with self.assertRaises(ValueError):
|
|
32
|
+
LLMClient(reasoning_effort="off")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TestReasoningEffortCli(unittest.TestCase):
|
|
36
|
+
def parse(self, *argv: str) -> argparse.Namespace:
|
|
37
|
+
parser = argparse.ArgumentParser()
|
|
38
|
+
register_subcommand(parser.add_subparsers(dest="command"))
|
|
39
|
+
return parser.parse_args(["scan", ".", *argv])
|
|
40
|
+
|
|
41
|
+
def test_flag_is_optional(self) -> None:
|
|
42
|
+
self.assertIsNone(self.parse().ai_reasoning_effort)
|
|
43
|
+
self.assertEqual(self.parse("--ai-reasoning-effort", "none").ai_reasoning_effort, "none")
|
|
44
|
+
|
|
45
|
+
def test_flag_rejects_unknown_values(self) -> None:
|
|
46
|
+
with self.assertRaises(SystemExit):
|
|
47
|
+
self.parse("--ai-reasoning-effort", "off")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestReasoningEffortStats(unittest.TestCase):
|
|
51
|
+
def setUp(self) -> None:
|
|
52
|
+
self.tmp = Path(tempfile.mkdtemp(prefix="komagu_effort_"))
|
|
53
|
+
|
|
54
|
+
def tearDown(self) -> None:
|
|
55
|
+
shutil.rmtree(self.tmp, ignore_errors=True)
|
|
56
|
+
|
|
57
|
+
def test_value_is_recorded(self) -> None:
|
|
58
|
+
runner = HeadlessAIRunner(self.tmp, self.tmp / "out", LLMClient(model="m", reasoning_effort="low"))
|
|
59
|
+
(self.tmp / "out").mkdir(parents=True, exist_ok=True)
|
|
60
|
+
stats = json.loads(runner.write_run_stats().read_text(encoding="utf-8"))
|
|
61
|
+
self.assertEqual(stats["reasoning_effort"], "low")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
unittest.main()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Route inventory built by the headless runner from Tier 1 route batches (#380)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import shutil
|
|
8
|
+
import tempfile
|
|
9
|
+
import unittest
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from operate.ai.runner import HeadlessAIRunner
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _NoCallClient:
|
|
16
|
+
model = "none"
|
|
17
|
+
endpoint = "test://"
|
|
18
|
+
|
|
19
|
+
def chat(self, messages, temperature=0.1): # noqa: ANN001
|
|
20
|
+
raise AssertionError("route inventory must not call the model")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestRouteInventory(unittest.TestCase):
|
|
24
|
+
def setUp(self) -> None:
|
|
25
|
+
self.tmp = Path(tempfile.mkdtemp(prefix="komagu_inventory_"))
|
|
26
|
+
self.out = self.tmp / "out"
|
|
27
|
+
(self.out / "batches").mkdir(parents=True)
|
|
28
|
+
|
|
29
|
+
def tearDown(self) -> None:
|
|
30
|
+
shutil.rmtree(self.tmp, ignore_errors=True)
|
|
31
|
+
|
|
32
|
+
def inventory(self, candidates: list) -> list:
|
|
33
|
+
(self.out / "batches" / "routes-001.json").write_text(json.dumps({"candidates": candidates}), encoding="utf-8")
|
|
34
|
+
HeadlessAIRunner(self.tmp, self.out, _NoCallClient()).map_routes_inventory()
|
|
35
|
+
return json.loads((self.out / "routes" / "routes-001.json").read_text(encoding="utf-8"))["routes"]
|
|
36
|
+
|
|
37
|
+
def test_route_without_id_params_gets_no_invented_id(self) -> None:
|
|
38
|
+
routes = self.inventory([{"route_id": "R-0001", "method": ["POST"], "path": "/register",
|
|
39
|
+
"handler": {"file": "app/Http/Controllers/AccountController.php", "line": 11},
|
|
40
|
+
"id_params": []}])
|
|
41
|
+
self.assertEqual(routes[0]["id_inputs"], [])
|
|
42
|
+
|
|
43
|
+
def test_detected_id_params_are_kept(self) -> None:
|
|
44
|
+
routes = self.inventory([{"route_id": "R-0001", "method": ["GET"], "path": "/orders/{id}",
|
|
45
|
+
"handler": {"file": "routes/web.php", "line": 3},
|
|
46
|
+
"id_params": [{"name": "id", "source": "path", "is_id_like": True},
|
|
47
|
+
{"name": "orderId", "source": "query"}]}])
|
|
48
|
+
self.assertEqual(routes[0]["id_inputs"], [{"name": "id", "source": "path"},
|
|
49
|
+
{"name": "orderId", "source": "query"}])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
unittest.main()
|