@trac3r/oh-my-god 2.2.11

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 (638) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/INSTALL-VERIFICATION-INDEX.md +51 -0
  3. package/LICENSE +21 -0
  4. package/OMG-setup.sh +2549 -0
  5. package/QUICK-REFERENCE.md +58 -0
  6. package/README.md +207 -0
  7. package/agents/__init__.py +1 -0
  8. package/agents/__pycache__/model_roles.cpython-313.pyc +0 -0
  9. package/agents/_model_roles.yaml +26 -0
  10. package/agents/designer.md +67 -0
  11. package/agents/explore.md +60 -0
  12. package/agents/model_roles.py +196 -0
  13. package/agents/omg-api-builder.md +23 -0
  14. package/agents/omg-architect-mode.md +41 -0
  15. package/agents/omg-architect.md +13 -0
  16. package/agents/omg-backend-engineer.md +41 -0
  17. package/agents/omg-critic.md +16 -0
  18. package/agents/omg-database-engineer.md +41 -0
  19. package/agents/omg-escalation-router.md +17 -0
  20. package/agents/omg-executor.md +12 -0
  21. package/agents/omg-frontend-designer.md +41 -0
  22. package/agents/omg-implement-mode.md +49 -0
  23. package/agents/omg-infra-engineer.md +41 -0
  24. package/agents/omg-qa-tester.md +16 -0
  25. package/agents/omg-research-mode.md +41 -0
  26. package/agents/omg-security-auditor.md +41 -0
  27. package/agents/omg-testing-engineer.md +41 -0
  28. package/agents/plan.md +80 -0
  29. package/agents/quick_task.md +64 -0
  30. package/agents/reviewer.md +83 -0
  31. package/agents/task.md +71 -0
  32. package/bin/omg +41 -0
  33. package/commands/OMG:ai-commit.md +113 -0
  34. package/commands/OMG:api-twin.md +22 -0
  35. package/commands/OMG:arch.md +313 -0
  36. package/commands/OMG:browser.md +29 -0
  37. package/commands/OMG:ccg.md +22 -0
  38. package/commands/OMG:compat.md +57 -0
  39. package/commands/OMG:cost.md +181 -0
  40. package/commands/OMG:crazy.md +125 -0
  41. package/commands/OMG:create-agent.md +183 -0
  42. package/commands/OMG:deep-plan.md +18 -0
  43. package/commands/OMG:deps.md +248 -0
  44. package/commands/OMG:diagnose-plugins.md +33 -0
  45. package/commands/OMG:doctor.md +37 -0
  46. package/commands/OMG:domain-init.md +11 -0
  47. package/commands/OMG:escalate.md +52 -0
  48. package/commands/OMG:forge.md +103 -0
  49. package/commands/OMG:health-check.md +48 -0
  50. package/commands/OMG:init.md +134 -0
  51. package/commands/OMG:issue.md +56 -0
  52. package/commands/OMG:mode.md +44 -0
  53. package/commands/OMG:playwright.md +17 -0
  54. package/commands/OMG:preflight.md +26 -0
  55. package/commands/OMG:preset.md +49 -0
  56. package/commands/OMG:profile-review.md +58 -0
  57. package/commands/OMG:project-init.md +11 -0
  58. package/commands/OMG:ralph-start.md +43 -0
  59. package/commands/OMG:ralph-stop.md +23 -0
  60. package/commands/OMG:security-check.md +28 -0
  61. package/commands/OMG:session-branch.md +101 -0
  62. package/commands/OMG:session-fork.md +57 -0
  63. package/commands/OMG:session-merge.md +138 -0
  64. package/commands/OMG:setup.md +82 -0
  65. package/commands/OMG:ship.md +18 -0
  66. package/commands/OMG:stats.md +225 -0
  67. package/commands/OMG:teams.md +54 -0
  68. package/commands/OMG:theme.md +44 -0
  69. package/commands/OMG:validate.md +59 -0
  70. package/commands/__init__.py +1 -0
  71. package/docs/command-surface.md +55 -0
  72. package/docs/install/claude-code.md +53 -0
  73. package/docs/install/codex.md +45 -0
  74. package/docs/install/gemini.md +43 -0
  75. package/docs/install/github-action.md +81 -0
  76. package/docs/install/github-app-required-checks.md +107 -0
  77. package/docs/install/github-app.md +161 -0
  78. package/docs/install/kimi.md +43 -0
  79. package/docs/install/opencode.md +38 -0
  80. package/docs/proof.md +182 -0
  81. package/hooks/__init__.py +0 -0
  82. package/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  83. package/hooks/__pycache__/_agent_registry.cpython-313.pyc +0 -0
  84. package/hooks/__pycache__/_analytics.cpython-313.pyc +0 -0
  85. package/hooks/__pycache__/_budget.cpython-313.pyc +0 -0
  86. package/hooks/__pycache__/_common.cpython-313.pyc +0 -0
  87. package/hooks/__pycache__/_compression_optimizer.cpython-313.pyc +0 -0
  88. package/hooks/__pycache__/_cost_ledger.cpython-313.pyc +0 -0
  89. package/hooks/__pycache__/_learnings.cpython-313.pyc +0 -0
  90. package/hooks/__pycache__/_memory.cpython-313.pyc +0 -0
  91. package/hooks/__pycache__/_post_write.cpython-313.pyc +0 -0
  92. package/hooks/__pycache__/_protected_context.cpython-313.pyc +0 -0
  93. package/hooks/__pycache__/_token_counter.cpython-313.pyc +0 -0
  94. package/hooks/__pycache__/branch_manager.cpython-313.pyc +0 -0
  95. package/hooks/__pycache__/budget_governor.cpython-313.pyc +0 -0
  96. package/hooks/__pycache__/circuit-breaker.cpython-313.pyc +0 -0
  97. package/hooks/__pycache__/compression_feedback.cpython-313.pyc +0 -0
  98. package/hooks/__pycache__/config-guard.cpython-313.pyc +0 -0
  99. package/hooks/__pycache__/context_pressure.cpython-313.pyc +0 -0
  100. package/hooks/__pycache__/credential_store.cpython-313.pyc +0 -0
  101. package/hooks/__pycache__/fetch-rate-limits.cpython-313.pyc +0 -0
  102. package/hooks/__pycache__/firewall.cpython-313.pyc +0 -0
  103. package/hooks/__pycache__/hashline-formatter-bridge.cpython-313.pyc +0 -0
  104. package/hooks/__pycache__/hashline-injector.cpython-313.pyc +0 -0
  105. package/hooks/__pycache__/hashline-validator.cpython-313.pyc +0 -0
  106. package/hooks/__pycache__/idle-detector.cpython-313.pyc +0 -0
  107. package/hooks/__pycache__/instructions-loaded.cpython-313.pyc +0 -0
  108. package/hooks/__pycache__/intentgate-keyword-detector.cpython-313.pyc +0 -0
  109. package/hooks/__pycache__/magic-keyword-router.cpython-313.pyc +0 -0
  110. package/hooks/__pycache__/policy_engine.cpython-313.pyc +0 -0
  111. package/hooks/__pycache__/post-tool-failure.cpython-313.pyc +0 -0
  112. package/hooks/__pycache__/post-write.cpython-313.pyc +0 -0
  113. package/hooks/__pycache__/post_write.cpython-313.pyc +0 -0
  114. package/hooks/__pycache__/pre-compact.cpython-313.pyc +0 -0
  115. package/hooks/__pycache__/pre-tool-inject.cpython-313.pyc +0 -0
  116. package/hooks/__pycache__/prompt-enhancer.cpython-313.pyc +0 -0
  117. package/hooks/__pycache__/quality-runner.cpython-313.pyc +0 -0
  118. package/hooks/__pycache__/query.cpython-313.pyc +0 -0
  119. package/hooks/__pycache__/secret-guard.cpython-313.pyc +0 -0
  120. package/hooks/__pycache__/secret_audit.cpython-313.pyc +0 -0
  121. package/hooks/__pycache__/security_validators.cpython-313.pyc +0 -0
  122. package/hooks/__pycache__/session-end-capture.cpython-313.pyc +0 -0
  123. package/hooks/__pycache__/session-start.cpython-313.pyc +0 -0
  124. package/hooks/__pycache__/setup_wizard.cpython-313.pyc +0 -0
  125. package/hooks/__pycache__/shadow_manager.cpython-313.pyc +0 -0
  126. package/hooks/__pycache__/state_migration.cpython-313.pyc +0 -0
  127. package/hooks/__pycache__/stop-gate.cpython-313.pyc +0 -0
  128. package/hooks/__pycache__/stop_dispatcher.cpython-313.pyc +0 -0
  129. package/hooks/__pycache__/tdd-gate.cpython-313.pyc +0 -0
  130. package/hooks/__pycache__/terms-guard.cpython-313.pyc +0 -0
  131. package/hooks/__pycache__/test-validator.cpython-313.pyc +0 -0
  132. package/hooks/__pycache__/test_generator_hook.cpython-313.pyc +0 -0
  133. package/hooks/__pycache__/todo-state-tracker.cpython-313.pyc +0 -0
  134. package/hooks/__pycache__/tool-ledger.cpython-313.pyc +0 -0
  135. package/hooks/__pycache__/trust_review.cpython-313.pyc +0 -0
  136. package/hooks/__pycache__/user-prompt-submit.cpython-313.pyc +0 -0
  137. package/hooks/_agent_registry.py +481 -0
  138. package/hooks/_analytics.py +291 -0
  139. package/hooks/_budget.py +31 -0
  140. package/hooks/_common.py +761 -0
  141. package/hooks/_compression_optimizer.py +119 -0
  142. package/hooks/_cost_ledger.py +176 -0
  143. package/hooks/_learnings.py +126 -0
  144. package/hooks/_memory.py +103 -0
  145. package/hooks/_post_write.py +46 -0
  146. package/hooks/_protected_context.py +150 -0
  147. package/hooks/_token_counter.py +221 -0
  148. package/hooks/branch_manager.py +255 -0
  149. package/hooks/budget_governor.py +326 -0
  150. package/hooks/circuit-breaker.py +270 -0
  151. package/hooks/compression_feedback.py +254 -0
  152. package/hooks/config-guard.py +193 -0
  153. package/hooks/context_pressure.py +119 -0
  154. package/hooks/credential_store.py +970 -0
  155. package/hooks/fetch-rate-limits.py +212 -0
  156. package/hooks/firewall.py +323 -0
  157. package/hooks/hashline-formatter-bridge.py +224 -0
  158. package/hooks/hashline-injector.py +273 -0
  159. package/hooks/hashline-validator.py +216 -0
  160. package/hooks/idle-detector.py +97 -0
  161. package/hooks/instructions-loaded.py +26 -0
  162. package/hooks/intentgate-keyword-detector.py +200 -0
  163. package/hooks/magic-keyword-router.py +195 -0
  164. package/hooks/policy_engine.py +767 -0
  165. package/hooks/post-tool-failure.py +19 -0
  166. package/hooks/post-write.py +233 -0
  167. package/hooks/pre-compact.py +470 -0
  168. package/hooks/pre-tool-inject.py +98 -0
  169. package/hooks/prompt-enhancer.py +879 -0
  170. package/hooks/quality-runner.py +191 -0
  171. package/hooks/query.py +512 -0
  172. package/hooks/secret-guard.py +120 -0
  173. package/hooks/secret_audit.py +144 -0
  174. package/hooks/security_validators.py +93 -0
  175. package/hooks/session-end-capture.py +505 -0
  176. package/hooks/session-start.py +261 -0
  177. package/hooks/setup_wizard.py +1101 -0
  178. package/hooks/shadow_manager.py +476 -0
  179. package/hooks/state_migration.py +228 -0
  180. package/hooks/stop-gate.py +7 -0
  181. package/hooks/stop_dispatcher.py +1259 -0
  182. package/hooks/tdd-gate.py +10 -0
  183. package/hooks/terms-guard.py +98 -0
  184. package/hooks/test-validator.py +462 -0
  185. package/hooks/test_generator_hook.py +123 -0
  186. package/hooks/todo-state-tracker.py +114 -0
  187. package/hooks/tool-ledger.py +165 -0
  188. package/hooks/trust_review.py +662 -0
  189. package/hooks/user-prompt-submit.py +12 -0
  190. package/hud/omg-hud.mjs +1571 -0
  191. package/lab/__init__.py +1 -0
  192. package/lab/__pycache__/__init__.cpython-313.pyc +0 -0
  193. package/lab/__pycache__/axolotl_adapter.cpython-313.pyc +0 -0
  194. package/lab/__pycache__/forge_runner.cpython-313.pyc +0 -0
  195. package/lab/__pycache__/gazebo_adapter.cpython-313.pyc +0 -0
  196. package/lab/__pycache__/isaac_gym_adapter.cpython-313.pyc +0 -0
  197. package/lab/__pycache__/mock_isaac_env.cpython-313.pyc +0 -0
  198. package/lab/__pycache__/pipeline.cpython-313.pyc +0 -0
  199. package/lab/__pycache__/policies.cpython-313.pyc +0 -0
  200. package/lab/__pycache__/pybullet_adapter.cpython-313.pyc +0 -0
  201. package/lab/axolotl_adapter.py +531 -0
  202. package/lab/forge_runner.py +103 -0
  203. package/lab/gazebo_adapter.py +168 -0
  204. package/lab/isaac_gym_adapter.py +190 -0
  205. package/lab/mock_isaac_env.py +47 -0
  206. package/lab/pipeline.py +712 -0
  207. package/lab/policies.py +52 -0
  208. package/lab/pybullet_adapter.py +192 -0
  209. package/package.json +61 -0
  210. package/plugins/README.md +78 -0
  211. package/plugins/__init__.py +1 -0
  212. package/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
  213. package/plugins/advanced/commands/OMG-code-review.md +114 -0
  214. package/plugins/advanced/commands/OMG-deep-plan.md +266 -0
  215. package/plugins/advanced/commands/OMG-handoff.md +115 -0
  216. package/plugins/advanced/commands/OMG-learn.md +110 -0
  217. package/plugins/advanced/commands/OMG-maintainer.md +31 -0
  218. package/plugins/advanced/commands/OMG-ralph-start.md +43 -0
  219. package/plugins/advanced/commands/OMG-ralph-stop.md +23 -0
  220. package/plugins/advanced/commands/OMG-security-review.md +16 -0
  221. package/plugins/advanced/commands/OMG-sequential-thinking.md +20 -0
  222. package/plugins/advanced/commands/OMG-ship.md +46 -0
  223. package/plugins/advanced/commands/OMG:code-review.md +114 -0
  224. package/plugins/advanced/commands/OMG:deep-plan.md +266 -0
  225. package/plugins/advanced/commands/OMG:handoff.md +115 -0
  226. package/plugins/advanced/commands/OMG:learn.md +110 -0
  227. package/plugins/advanced/commands/OMG:maintainer.md +31 -0
  228. package/plugins/advanced/commands/OMG:ralph-start.md +43 -0
  229. package/plugins/advanced/commands/OMG:ralph-stop.md +23 -0
  230. package/plugins/advanced/commands/OMG:security-review.md +16 -0
  231. package/plugins/advanced/commands/OMG:sequential-thinking.md +20 -0
  232. package/plugins/advanced/commands/OMG:ship.md +46 -0
  233. package/plugins/advanced/plugin.json +104 -0
  234. package/plugins/core/plugin.json +204 -0
  235. package/plugins/dephealth/__init__.py +0 -0
  236. package/plugins/dephealth/__pycache__/__init__.cpython-313.pyc +0 -0
  237. package/plugins/dephealth/__pycache__/cve_scanner.cpython-313.pyc +0 -0
  238. package/plugins/dephealth/__pycache__/license_checker.cpython-313.pyc +0 -0
  239. package/plugins/dephealth/__pycache__/manifest_detector.cpython-313.pyc +0 -0
  240. package/plugins/dephealth/__pycache__/vuln_analyzer.cpython-313.pyc +0 -0
  241. package/plugins/dephealth/cve_scanner.py +279 -0
  242. package/plugins/dephealth/license_checker.py +135 -0
  243. package/plugins/dephealth/manifest_detector.py +423 -0
  244. package/plugins/dephealth/vuln_analyzer.py +176 -0
  245. package/plugins/testgen/__init__.py +0 -0
  246. package/plugins/testgen/__pycache__/__init__.cpython-313.pyc +0 -0
  247. package/plugins/testgen/__pycache__/codamosa_engine.cpython-313.pyc +0 -0
  248. package/plugins/testgen/__pycache__/edge_case_synthesizer.cpython-313.pyc +0 -0
  249. package/plugins/testgen/__pycache__/framework_detector.cpython-313.pyc +0 -0
  250. package/plugins/testgen/__pycache__/skeleton_generator.cpython-313.pyc +0 -0
  251. package/plugins/testgen/codamosa_engine.py +402 -0
  252. package/plugins/testgen/edge_case_synthesizer.py +184 -0
  253. package/plugins/testgen/framework_detector.py +271 -0
  254. package/plugins/testgen/skeleton_generator.py +219 -0
  255. package/plugins/viz/__init__.py +0 -0
  256. package/plugins/viz/__pycache__/__init__.cpython-313.pyc +0 -0
  257. package/plugins/viz/__pycache__/ast_parser.cpython-313.pyc +0 -0
  258. package/plugins/viz/__pycache__/diagram_generator.cpython-313.pyc +0 -0
  259. package/plugins/viz/__pycache__/graph_builder.cpython-313.pyc +0 -0
  260. package/plugins/viz/__pycache__/native_parsers.cpython-313.pyc +0 -0
  261. package/plugins/viz/__pycache__/regex_parser.cpython-313.pyc +0 -0
  262. package/plugins/viz/ast_parser.py +139 -0
  263. package/plugins/viz/diagram_generator.py +192 -0
  264. package/plugins/viz/graph_builder.py +444 -0
  265. package/plugins/viz/native_parsers.py +259 -0
  266. package/plugins/viz/regex_parser.py +112 -0
  267. package/pyproject.toml +143 -0
  268. package/registry/__init__.py +1 -0
  269. package/registry/__pycache__/__init__.cpython-313.pyc +0 -0
  270. package/registry/__pycache__/approval_artifact.cpython-313.pyc +0 -0
  271. package/registry/__pycache__/verify_artifact.cpython-313.pyc +0 -0
  272. package/registry/approval_artifact.py +236 -0
  273. package/registry/bundles/algorithms.yaml +45 -0
  274. package/registry/bundles/api-twin.yaml +48 -0
  275. package/registry/bundles/ast-pack.yaml +80 -0
  276. package/registry/bundles/claim-judge.yaml +49 -0
  277. package/registry/bundles/control-plane.yaml +192 -0
  278. package/registry/bundles/data-lineage.yaml +47 -0
  279. package/registry/bundles/delta-classifier.yaml +47 -0
  280. package/registry/bundles/eval-gate.yaml +47 -0
  281. package/registry/bundles/hash-edit.yaml +73 -0
  282. package/registry/bundles/health.yaml +45 -0
  283. package/registry/bundles/hook-governor.yaml +101 -0
  284. package/registry/bundles/incident-replay.yaml +47 -0
  285. package/registry/bundles/lsp-pack.yaml +80 -0
  286. package/registry/bundles/mcp-fabric.yaml +53 -0
  287. package/registry/bundles/plan-council.yaml +56 -0
  288. package/registry/bundles/preflight.yaml +48 -0
  289. package/registry/bundles/proof-gate.yaml +49 -0
  290. package/registry/bundles/remote-supervisor.yaml +49 -0
  291. package/registry/bundles/robotics.yaml +45 -0
  292. package/registry/bundles/secure-worktree-pipeline.yaml +69 -0
  293. package/registry/bundles/security-check.yaml +50 -0
  294. package/registry/bundles/terminal-lane.yaml +61 -0
  295. package/registry/bundles/test-intent-lock.yaml +49 -0
  296. package/registry/bundles/tracebank.yaml +47 -0
  297. package/registry/bundles/vision.yaml +45 -0
  298. package/registry/omg-capability.schema.json +378 -0
  299. package/registry/policy-packs/airgapped.lock.json +11 -0
  300. package/registry/policy-packs/airgapped.signature.json +10 -0
  301. package/registry/policy-packs/airgapped.yaml +16 -0
  302. package/registry/policy-packs/fintech.lock.json +11 -0
  303. package/registry/policy-packs/fintech.signature.json +10 -0
  304. package/registry/policy-packs/fintech.yaml +15 -0
  305. package/registry/policy-packs/locked-prod.lock.json +11 -0
  306. package/registry/policy-packs/locked-prod.signature.json +10 -0
  307. package/registry/policy-packs/locked-prod.yaml +18 -0
  308. package/registry/trusted_signers.json +44 -0
  309. package/registry/verify_artifact.py +493 -0
  310. package/runtime/__init__.py +36 -0
  311. package/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
  312. package/runtime/__pycache__/adoption.cpython-313.pyc +0 -0
  313. package/runtime/__pycache__/agent_selector.cpython-313.pyc +0 -0
  314. package/runtime/__pycache__/api_twin.cpython-313.pyc +0 -0
  315. package/runtime/__pycache__/architecture_signal.cpython-313.pyc +0 -0
  316. package/runtime/__pycache__/artifact_parsers.cpython-313.pyc +0 -0
  317. package/runtime/__pycache__/asset_loader.cpython-313.pyc +0 -0
  318. package/runtime/__pycache__/background_verification.cpython-313.pyc +0 -0
  319. package/runtime/__pycache__/budget_envelopes.cpython-313.pyc +0 -0
  320. package/runtime/__pycache__/business_workflow.cpython-313.pyc +0 -0
  321. package/runtime/__pycache__/canonical_surface.cpython-313.pyc +0 -0
  322. package/runtime/__pycache__/canonical_taxonomy.cpython-313.pyc +0 -0
  323. package/runtime/__pycache__/claim_judge.cpython-313.pyc +0 -0
  324. package/runtime/__pycache__/cli_provider.cpython-313.pyc +0 -0
  325. package/runtime/__pycache__/compat.cpython-313.pyc +0 -0
  326. package/runtime/__pycache__/complexity_scorer.cpython-313.pyc +0 -0
  327. package/runtime/__pycache__/compliance_governor.cpython-313.pyc +0 -0
  328. package/runtime/__pycache__/config_transaction.cpython-313.pyc +0 -0
  329. package/runtime/__pycache__/context_compiler.cpython-313.pyc +0 -0
  330. package/runtime/__pycache__/context_engine.cpython-313.pyc +0 -0
  331. package/runtime/__pycache__/context_limits.cpython-313.pyc +0 -0
  332. package/runtime/__pycache__/contract_compiler.cpython-313.pyc +0 -0
  333. package/runtime/__pycache__/custom_agent_loader.cpython-313.pyc +0 -0
  334. package/runtime/__pycache__/data_lineage.cpython-313.pyc +0 -0
  335. package/runtime/__pycache__/defense_state.cpython-313.pyc +0 -0
  336. package/runtime/__pycache__/delta_classifier.cpython-313.pyc +0 -0
  337. package/runtime/__pycache__/dispatcher.cpython-313.pyc +0 -0
  338. package/runtime/__pycache__/doc_generator.cpython-313.pyc +0 -0
  339. package/runtime/__pycache__/domain_packs.cpython-313.pyc +0 -0
  340. package/runtime/__pycache__/ecosystem.cpython-313.pyc +0 -0
  341. package/runtime/__pycache__/equalizer.cpython-313.pyc +0 -0
  342. package/runtime/__pycache__/eval_gate.cpython-313.pyc +0 -0
  343. package/runtime/__pycache__/evidence_narrator.cpython-313.pyc +0 -0
  344. package/runtime/__pycache__/evidence_query.cpython-313.pyc +0 -0
  345. package/runtime/__pycache__/evidence_registry.cpython-313.pyc +0 -0
  346. package/runtime/__pycache__/evidence_requirements.cpython-313.pyc +0 -0
  347. package/runtime/__pycache__/exec_kernel.cpython-313.pyc +0 -0
  348. package/runtime/__pycache__/explainer_formatter.cpython-313.pyc +0 -0
  349. package/runtime/__pycache__/feature_registry.cpython-313.pyc +0 -0
  350. package/runtime/__pycache__/forge_agents.cpython-313.pyc +0 -0
  351. package/runtime/__pycache__/forge_contracts.cpython-313.pyc +0 -0
  352. package/runtime/__pycache__/forge_domains.cpython-313.pyc +0 -0
  353. package/runtime/__pycache__/forge_run_id.cpython-313.pyc +0 -0
  354. package/runtime/__pycache__/github_integration.cpython-313.pyc +0 -0
  355. package/runtime/__pycache__/github_review_bot.cpython-313.pyc +0 -0
  356. package/runtime/__pycache__/github_review_contract.cpython-313.pyc +0 -0
  357. package/runtime/__pycache__/github_review_formatter.cpython-313.pyc +0 -0
  358. package/runtime/__pycache__/guide_assert.cpython-313.pyc +0 -0
  359. package/runtime/__pycache__/hook_governor.cpython-313.pyc +0 -0
  360. package/runtime/__pycache__/host_parity.cpython-313.pyc +0 -0
  361. package/runtime/__pycache__/incident_replay.cpython-313.pyc +0 -0
  362. package/runtime/__pycache__/install_planner.cpython-313.pyc +0 -0
  363. package/runtime/__pycache__/interaction_journal.cpython-313.pyc +0 -0
  364. package/runtime/__pycache__/issue_surface.cpython-313.pyc +0 -0
  365. package/runtime/__pycache__/legacy_compat.cpython-313.pyc +0 -0
  366. package/runtime/__pycache__/mcp_config_writers.cpython-313.pyc +0 -0
  367. package/runtime/__pycache__/mcp_lifecycle.cpython-313.pyc +0 -0
  368. package/runtime/__pycache__/mcp_memory_server.cpython-313.pyc +0 -0
  369. package/runtime/__pycache__/memory_store.cpython-313.pyc +0 -0
  370. package/runtime/__pycache__/merge_writer.cpython-313.pyc +0 -0
  371. package/runtime/__pycache__/music_omr_testbed.cpython-313.pyc +0 -0
  372. package/runtime/__pycache__/mutation_gate.cpython-313.pyc +0 -0
  373. package/runtime/__pycache__/omc_compat.cpython-313.pyc +0 -0
  374. package/runtime/__pycache__/omg_browser_cli.cpython-313.pyc +0 -0
  375. package/runtime/__pycache__/omg_mcp_server.cpython-313.pyc +0 -0
  376. package/runtime/__pycache__/opus_plan.cpython-313.pyc +0 -0
  377. package/runtime/__pycache__/playwright_adapter.cpython-313.pyc +0 -0
  378. package/runtime/__pycache__/playwright_pack.cpython-313.pyc +0 -0
  379. package/runtime/__pycache__/plugin_diagnostics.cpython-313.pyc +0 -0
  380. package/runtime/__pycache__/plugin_interop.cpython-313.pyc +0 -0
  381. package/runtime/__pycache__/policy_pack_loader.cpython-313.pyc +0 -0
  382. package/runtime/__pycache__/preflight.cpython-313.pyc +0 -0
  383. package/runtime/__pycache__/profile_io.cpython-313.pyc +0 -0
  384. package/runtime/__pycache__/prompt_compiler.cpython-313.pyc +0 -0
  385. package/runtime/__pycache__/proof_chain.cpython-313.pyc +0 -0
  386. package/runtime/__pycache__/proof_gate.cpython-313.pyc +0 -0
  387. package/runtime/__pycache__/provider_parity_eval.cpython-313.pyc +0 -0
  388. package/runtime/__pycache__/release_artifact_audit.cpython-313.pyc +0 -0
  389. package/runtime/__pycache__/release_run_coordinator.cpython-313.pyc +0 -0
  390. package/runtime/__pycache__/release_surface_compiler.cpython-313.pyc +0 -0
  391. package/runtime/__pycache__/release_surface_registry.cpython-313.pyc +0 -0
  392. package/runtime/__pycache__/release_surfaces.cpython-313.pyc +0 -0
  393. package/runtime/__pycache__/remote_supervisor.cpython-313.pyc +0 -0
  394. package/runtime/__pycache__/repro_pack.cpython-313.pyc +0 -0
  395. package/runtime/__pycache__/rollback_manifest.cpython-313.pyc +0 -0
  396. package/runtime/__pycache__/router_critics.cpython-313.pyc +0 -0
  397. package/runtime/__pycache__/router_executor.cpython-313.pyc +0 -0
  398. package/runtime/__pycache__/router_selector.cpython-313.pyc +0 -0
  399. package/runtime/__pycache__/runtime_contracts.cpython-313.pyc +0 -0
  400. package/runtime/__pycache__/runtime_profile.cpython-313.pyc +0 -0
  401. package/runtime/__pycache__/security_check.cpython-313.pyc +0 -0
  402. package/runtime/__pycache__/session_health.cpython-313.pyc +0 -0
  403. package/runtime/__pycache__/skill_evolution.cpython-313.pyc +0 -0
  404. package/runtime/__pycache__/skill_registry.cpython-313.pyc +0 -0
  405. package/runtime/__pycache__/subagent_dispatcher.cpython-313.pyc +0 -0
  406. package/runtime/__pycache__/subscription_tiers.cpython-313.pyc +0 -0
  407. package/runtime/__pycache__/team_router.cpython-313.pyc +0 -0
  408. package/runtime/__pycache__/test_intent_lock.cpython-313-pytest-9.0.2.pyc +0 -0
  409. package/runtime/__pycache__/test_intent_lock.cpython-313.pyc +0 -0
  410. package/runtime/__pycache__/tmux_session_manager.cpython-313.pyc +0 -0
  411. package/runtime/__pycache__/tool_fabric.cpython-313.pyc +0 -0
  412. package/runtime/__pycache__/tool_plan_gate.cpython-313.pyc +0 -0
  413. package/runtime/__pycache__/tool_relevance.cpython-313.pyc +0 -0
  414. package/runtime/__pycache__/tracebank.cpython-313.pyc +0 -0
  415. package/runtime/__pycache__/untrusted_content.cpython-313.pyc +0 -0
  416. package/runtime/__pycache__/validate.cpython-313.pyc +0 -0
  417. package/runtime/__pycache__/verdict_schema.cpython-313.pyc +0 -0
  418. package/runtime/__pycache__/verification_controller.cpython-313.pyc +0 -0
  419. package/runtime/__pycache__/verification_loop.cpython-313.pyc +0 -0
  420. package/runtime/__pycache__/vision_artifacts.cpython-313.pyc +0 -0
  421. package/runtime/__pycache__/vision_cache.cpython-313.pyc +0 -0
  422. package/runtime/__pycache__/vision_jobs.cpython-313.pyc +0 -0
  423. package/runtime/__pycache__/worker_watchdog.cpython-313.pyc +0 -0
  424. package/runtime/adapters/__init__.py +13 -0
  425. package/runtime/adapters/__pycache__/__init__.cpython-313.pyc +0 -0
  426. package/runtime/adapters/__pycache__/claude.cpython-313.pyc +0 -0
  427. package/runtime/adapters/__pycache__/gpt.cpython-313.pyc +0 -0
  428. package/runtime/adapters/__pycache__/local.cpython-313.pyc +0 -0
  429. package/runtime/adapters/claude.py +63 -0
  430. package/runtime/adapters/gpt.py +56 -0
  431. package/runtime/adapters/local.py +56 -0
  432. package/runtime/adoption.py +280 -0
  433. package/runtime/api_twin.py +450 -0
  434. package/runtime/architecture_signal.py +226 -0
  435. package/runtime/artifact_parsers.py +161 -0
  436. package/runtime/asset_loader.py +62 -0
  437. package/runtime/background_verification.py +178 -0
  438. package/runtime/budget_envelopes.py +398 -0
  439. package/runtime/business_workflow.py +234 -0
  440. package/runtime/canonical_surface.py +53 -0
  441. package/runtime/canonical_taxonomy.py +27 -0
  442. package/runtime/claim_judge.py +648 -0
  443. package/runtime/cli_provider.py +105 -0
  444. package/runtime/compat.py +2222 -0
  445. package/runtime/complexity_scorer.py +148 -0
  446. package/runtime/compliance_governor.py +505 -0
  447. package/runtime/config_transaction.py +304 -0
  448. package/runtime/context_compiler.py +131 -0
  449. package/runtime/context_engine.py +708 -0
  450. package/runtime/context_limits.py +363 -0
  451. package/runtime/contract_compiler.py +3664 -0
  452. package/runtime/custom_agent_loader.py +366 -0
  453. package/runtime/data_lineage.py +244 -0
  454. package/runtime/defense_state.py +261 -0
  455. package/runtime/delta_classifier.py +231 -0
  456. package/runtime/dispatcher.py +47 -0
  457. package/runtime/doc_generator.py +319 -0
  458. package/runtime/domain_packs.py +75 -0
  459. package/runtime/ecosystem.py +371 -0
  460. package/runtime/equalizer.py +268 -0
  461. package/runtime/eval_gate.py +96 -0
  462. package/runtime/evidence_narrator.py +147 -0
  463. package/runtime/evidence_query.py +303 -0
  464. package/runtime/evidence_registry.py +16 -0
  465. package/runtime/evidence_requirements.py +157 -0
  466. package/runtime/exec_kernel.py +267 -0
  467. package/runtime/explainer_formatter.py +82 -0
  468. package/runtime/feature_registry.py +109 -0
  469. package/runtime/forge_agents.py +915 -0
  470. package/runtime/forge_contracts.py +519 -0
  471. package/runtime/forge_domains.py +68 -0
  472. package/runtime/forge_run_id.py +86 -0
  473. package/runtime/guide_assert.py +135 -0
  474. package/runtime/hook_governor.py +156 -0
  475. package/runtime/host_parity.py +373 -0
  476. package/runtime/incident_replay.py +310 -0
  477. package/runtime/install_planner.py +617 -0
  478. package/runtime/interaction_journal.py +566 -0
  479. package/runtime/issue_surface.py +472 -0
  480. package/runtime/legacy_compat.py +7 -0
  481. package/runtime/mcp_config_writers.py +360 -0
  482. package/runtime/mcp_lifecycle.py +175 -0
  483. package/runtime/mcp_memory_server.py +220 -0
  484. package/runtime/memory_parsers/__init__.py +0 -0
  485. package/runtime/memory_parsers/__pycache__/__init__.cpython-313.pyc +0 -0
  486. package/runtime/memory_parsers/__pycache__/chatgpt_parser.cpython-313.pyc +0 -0
  487. package/runtime/memory_parsers/__pycache__/claude_import.cpython-313.pyc +0 -0
  488. package/runtime/memory_parsers/__pycache__/export.cpython-313.pyc +0 -0
  489. package/runtime/memory_parsers/__pycache__/gemini_import.cpython-313.pyc +0 -0
  490. package/runtime/memory_parsers/__pycache__/kimi_import.cpython-313.pyc +0 -0
  491. package/runtime/memory_parsers/chatgpt_parser.py +257 -0
  492. package/runtime/memory_parsers/claude_import.py +107 -0
  493. package/runtime/memory_parsers/export.py +97 -0
  494. package/runtime/memory_parsers/gemini_import.py +91 -0
  495. package/runtime/memory_parsers/kimi_import.py +91 -0
  496. package/runtime/memory_store.py +1182 -0
  497. package/runtime/merge_writer.py +445 -0
  498. package/runtime/music_omr_testbed.py +336 -0
  499. package/runtime/mutation_gate.py +320 -0
  500. package/runtime/omc_compat.py +7 -0
  501. package/runtime/omg_browser_cli.py +95 -0
  502. package/runtime/omg_compat_contract_snapshot.json +936 -0
  503. package/runtime/omg_contract_snapshot.json +936 -0
  504. package/runtime/omg_mcp_server.py +306 -0
  505. package/runtime/playwright_adapter.py +39 -0
  506. package/runtime/playwright_pack.py +253 -0
  507. package/runtime/plugin_diagnostics.py +308 -0
  508. package/runtime/plugin_interop.py +1060 -0
  509. package/runtime/policy_pack_loader.py +147 -0
  510. package/runtime/preflight.py +135 -0
  511. package/runtime/profile_io.py +328 -0
  512. package/runtime/proof_chain.py +472 -0
  513. package/runtime/proof_gate.py +442 -0
  514. package/runtime/provider_parity_eval.py +109 -0
  515. package/runtime/providers/__init__.py +0 -0
  516. package/runtime/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  517. package/runtime/providers/__pycache__/codex_provider.cpython-313.pyc +0 -0
  518. package/runtime/providers/__pycache__/gemini_provider.cpython-313.pyc +0 -0
  519. package/runtime/providers/__pycache__/kimi_provider.cpython-313.pyc +0 -0
  520. package/runtime/providers/__pycache__/opencode_provider.cpython-313.pyc +0 -0
  521. package/runtime/providers/codex_provider.py +129 -0
  522. package/runtime/providers/gemini_provider.py +143 -0
  523. package/runtime/providers/kimi_provider.py +167 -0
  524. package/runtime/providers/opencode_provider.py +99 -0
  525. package/runtime/release_artifact_audit.py +556 -0
  526. package/runtime/release_run_coordinator.py +574 -0
  527. package/runtime/release_surface_compiler.py +643 -0
  528. package/runtime/release_surface_registry.py +283 -0
  529. package/runtime/release_surfaces.py +320 -0
  530. package/runtime/remote_supervisor.py +79 -0
  531. package/runtime/repro_pack.py +398 -0
  532. package/runtime/rollback_manifest.py +143 -0
  533. package/runtime/router_critics.py +229 -0
  534. package/runtime/router_executor.py +142 -0
  535. package/runtime/router_selector.py +99 -0
  536. package/runtime/runtime_contracts.py +292 -0
  537. package/runtime/runtime_profile.py +133 -0
  538. package/runtime/security_check.py +1094 -0
  539. package/runtime/session_health.py +546 -0
  540. package/runtime/skill_evolution.py +221 -0
  541. package/runtime/skill_registry.py +53 -0
  542. package/runtime/subagent_dispatcher.py +604 -0
  543. package/runtime/subscription_tiers.py +258 -0
  544. package/runtime/team_router.py +1399 -0
  545. package/runtime/test_intent_lock.py +543 -0
  546. package/runtime/tmux_session_manager.py +172 -0
  547. package/runtime/tool_fabric.py +570 -0
  548. package/runtime/tool_plan_gate.py +460 -0
  549. package/runtime/tracebank.py +125 -0
  550. package/runtime/untrusted_content.py +360 -0
  551. package/runtime/validate.py +293 -0
  552. package/runtime/verdict_schema.py +198 -0
  553. package/runtime/verification_controller.py +235 -0
  554. package/runtime/verification_loop.py +73 -0
  555. package/runtime/vision_artifacts.py +31 -0
  556. package/runtime/vision_cache.py +38 -0
  557. package/runtime/vision_jobs.py +92 -0
  558. package/runtime/worker_watchdog.py +526 -0
  559. package/scripts/__pycache__/audit-published-artifact.cpython-313.pyc +0 -0
  560. package/scripts/__pycache__/check-doc-parity.cpython-313.pyc +0 -0
  561. package/scripts/__pycache__/check-omg-standalone-clean.cpython-313.pyc +0 -0
  562. package/scripts/__pycache__/github_review_helpers.cpython-313.pyc +0 -0
  563. package/scripts/__pycache__/omg.cpython-313.pyc +0 -0
  564. package/scripts/__pycache__/prepare-release-proof-fixtures.cpython-313.pyc +0 -0
  565. package/scripts/__pycache__/sync-release-identity.cpython-313.pyc +0 -0
  566. package/scripts/__pycache__/validate-release-identity.cpython-313.pyc +0 -0
  567. package/scripts/audit-published-artifact.py +59 -0
  568. package/scripts/check-omg-compat-contract-snapshot.py +137 -0
  569. package/scripts/check-omg-contract-snapshot.py +12 -0
  570. package/scripts/check-omg-public-ready.py +273 -0
  571. package/scripts/check-omg-standalone-clean.py +133 -0
  572. package/scripts/emit_host_parity.py +72 -0
  573. package/scripts/legacy_to_omg_migrate.py +29 -0
  574. package/scripts/migrate-legacy.py +464 -0
  575. package/scripts/omc_to_omg_migrate.py +12 -0
  576. package/scripts/omg.py +2962 -0
  577. package/scripts/pre-release-check.sh +38 -0
  578. package/scripts/prepare-release-proof-fixtures.py +602 -0
  579. package/scripts/print-canonical-version.py +80 -0
  580. package/scripts/settings-merge.py +289 -0
  581. package/scripts/sync-release-identity.py +481 -0
  582. package/scripts/validate-release-identity.py +632 -0
  583. package/scripts/verify-no-omc.sh +5 -0
  584. package/scripts/verify-standalone.sh +35 -0
  585. package/settings.json +751 -0
  586. package/tools/__init__.py +2 -0
  587. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  588. package/tools/__pycache__/browser_consent.cpython-313.pyc +0 -0
  589. package/tools/__pycache__/browser_stealth.cpython-313.pyc +0 -0
  590. package/tools/__pycache__/browser_tool.cpython-313.pyc +0 -0
  591. package/tools/__pycache__/changelog_generator.cpython-313.pyc +0 -0
  592. package/tools/__pycache__/commit_splitter.cpython-313.pyc +0 -0
  593. package/tools/__pycache__/config_discovery.cpython-313.pyc +0 -0
  594. package/tools/__pycache__/config_merger.cpython-313.pyc +0 -0
  595. package/tools/__pycache__/dashboard_generator.cpython-313.pyc +0 -0
  596. package/tools/__pycache__/git_inspector.cpython-313.pyc +0 -0
  597. package/tools/__pycache__/lsp_client.cpython-313.pyc +0 -0
  598. package/tools/__pycache__/lsp_operations.cpython-313.pyc +0 -0
  599. package/tools/__pycache__/pr_generator.cpython-313.pyc +0 -0
  600. package/tools/__pycache__/python_repl.cpython-313.pyc +0 -0
  601. package/tools/__pycache__/python_sandbox.cpython-313.pyc +0 -0
  602. package/tools/__pycache__/session_snapshot.cpython-313.pyc +0 -0
  603. package/tools/__pycache__/ssh_manager.cpython-313.pyc +0 -0
  604. package/tools/__pycache__/theme_engine.cpython-313.pyc +0 -0
  605. package/tools/__pycache__/theme_selector.cpython-313.pyc +0 -0
  606. package/tools/__pycache__/web_search.cpython-313.pyc +0 -0
  607. package/tools/browser_consent.py +289 -0
  608. package/tools/browser_stealth.py +481 -0
  609. package/tools/browser_tool.py +448 -0
  610. package/tools/changelog_generator.py +347 -0
  611. package/tools/commit_splitter.py +749 -0
  612. package/tools/config_discovery.py +151 -0
  613. package/tools/config_merger.py +449 -0
  614. package/tools/dashboard_generator.py +300 -0
  615. package/tools/git_inspector.py +298 -0
  616. package/tools/lsp_client.py +275 -0
  617. package/tools/lsp_discovery.py +231 -0
  618. package/tools/lsp_operations.py +392 -0
  619. package/tools/pr_generator.py +404 -0
  620. package/tools/python_repl.py +712 -0
  621. package/tools/python_sandbox.py +768 -0
  622. package/tools/search_providers/__init__.py +77 -0
  623. package/tools/search_providers/__pycache__/__init__.cpython-313.pyc +0 -0
  624. package/tools/search_providers/__pycache__/brave.cpython-313.pyc +0 -0
  625. package/tools/search_providers/__pycache__/exa.cpython-313.pyc +0 -0
  626. package/tools/search_providers/__pycache__/jina.cpython-313.pyc +0 -0
  627. package/tools/search_providers/__pycache__/perplexity.cpython-313.pyc +0 -0
  628. package/tools/search_providers/__pycache__/synthetic.cpython-313.pyc +0 -0
  629. package/tools/search_providers/brave.py +115 -0
  630. package/tools/search_providers/exa.py +116 -0
  631. package/tools/search_providers/jina.py +104 -0
  632. package/tools/search_providers/perplexity.py +139 -0
  633. package/tools/search_providers/synthetic.py +74 -0
  634. package/tools/session_snapshot.py +851 -0
  635. package/tools/ssh_manager.py +912 -0
  636. package/tools/theme_engine.py +296 -0
  637. package/tools/theme_selector.py +137 -0
  638. package/tools/web_search.py +675 -0
@@ -0,0 +1,767 @@
1
+ #!/usr/bin/env python3
2
+ """OMG v1 Policy Engine
3
+
4
+ Centralized policy decision layer for tool access, file access, and supply-chain
5
+ artifact verification.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, asdict
10
+ from fnmatch import fnmatch
11
+ import importlib
12
+ import os
13
+ import re
14
+ from typing import Any
15
+
16
+
17
+ Action = str
18
+ RiskLevel = str
19
+
20
+
21
+ @dataclass
22
+ class PolicyDecision:
23
+ action: Action # allow | ask | deny
24
+ risk_level: RiskLevel # low | med | high | critical
25
+ reason: str = ""
26
+ controls: list[str] | None = None
27
+
28
+ def to_dict(self) -> dict[str, Any]:
29
+ data = asdict(self)
30
+ if data.get("controls") is None:
31
+ data["controls"] = []
32
+ return data
33
+
34
+
35
+ def allow(reason: str = "", controls: list[str] | None = None) -> PolicyDecision:
36
+ return PolicyDecision("allow", "low", reason, controls or [])
37
+
38
+
39
+ def ask(reason: str, risk_level: RiskLevel = "med", controls: list[str] | None = None) -> PolicyDecision:
40
+ return PolicyDecision("ask", risk_level, reason, controls or [])
41
+
42
+
43
+ def deny(reason: str, risk_level: RiskLevel = "high", controls: list[str] | None = None) -> PolicyDecision:
44
+ return PolicyDecision("deny", risk_level, reason, controls or [])
45
+
46
+
47
+ # === BASH POLICY ============================================================
48
+
49
+ DESTRUCT_PATTERNS = [
50
+ (r"rm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+/(\s|$|\*)", "rm -rf /"),
51
+ (r"rm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+~/?(\s|$|\*)", "rm -rf ~"),
52
+ (r"rm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\$HOME", "rm -rf $HOME"),
53
+ (r"rm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\$\{?HOME\}?", "rm -rf ${HOME}"),
54
+ (r"rm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\.\.\s", "rm -rf .."),
55
+ (r":\(\)\s*\{\s*:\|:&\s*\}\s*;:", "fork bomb"),
56
+ (r"function\s+\w+\(\)\s*\{\s*\w+\s*\|\s*\w+\s*&", "potential fork bomb"),
57
+ (r">\s*/dev/sd[a-z]", "overwrite disk"),
58
+ (r"dd\s+.*of=/dev/sd[a-z]", "dd to disk device"),
59
+ (r"sudo\s+(dd|mkfs|fdisk|parted|wipefs)\b", "destructive disk op"),
60
+ (r"sudo\s+rm\b", "sudo rm"),
61
+ (r"echo\s+.*>\s*/proc/", "write to /proc"),
62
+ (r"echo\s+.*>\s*/sys/", "write to /sys"),
63
+ ]
64
+
65
+ PIPE_SHELL_PATTERNS = [
66
+ r"(curl|wget)\s+.*\|\s*(sudo\s+)?(ba)?sh",
67
+ r"(curl|wget)\s+.*\|\s*python[23]?",
68
+ r"(curl|wget)\s+.*\|\s*perl",
69
+ r"(curl|wget)\s+.*\|\s*ruby",
70
+ r"base64\s+.*\|\s*(ba)?sh",
71
+ r"echo\s+.*\|\s*base64\s+-d\s*\|\s*(ba)?sh",
72
+ ]
73
+
74
+ EVAL_PATTERNS = [
75
+ r"\beval\s+\"\$",
76
+ r"\beval\s+\$\(",
77
+ r"\beval\s+`",
78
+ ]
79
+
80
+ SAFE_ENV_REFERENCE = re.compile(r"\.env\.(example|sample|template)\b", re.IGNORECASE)
81
+
82
+ SECRET_FILE_PATTERNS = [
83
+ r"\.(env|pem|key|p12|pfx|jks|keystore|netrc|npmrc|pypirc)\b",
84
+ r"/\.aws/(credentials|config)\b",
85
+ r"/\.kube/config\b",
86
+ r"/id_(rsa|ed25519|ecdsa)\b",
87
+ r"/\.ssh/",
88
+ r"\bsecrets?/",
89
+ r"\bcredentials?\.",
90
+ r"\bpasswords?\.",
91
+ r"\btokens?\.",
92
+ ]
93
+
94
+ READ_COMMANDS = [
95
+ "cat", "less", "more", "head", "tail", "strings", "xxd", "od",
96
+ "hexdump", "base64", "vim", "vi", "nano", "emacs", "view",
97
+ "bat", "pygmentize", "highlight", "source", "\\.",
98
+ "awk", "gawk", "mawk", "perl", "ruby", "python", "python3", "node",
99
+ ]
100
+ READ_PATTERN = r"(?:^|\s|;|&&|\|\|)(?:" + "|".join(re.escape(c) for c in READ_COMMANDS) + r")\s+"
101
+
102
+ EXFIL_COMMANDS = [
103
+ r"\b(cp|mv|ln\s+-s)\s+",
104
+ r"\btar\s+.*-?c",
105
+ r"\bzip\s+",
106
+ ]
107
+
108
+ ASK_PATTERNS = [
109
+ (r"(^|\s)(curl|wget)(\s|$)", "Network egress"),
110
+ (r"(^|\s)(ssh|scp|rsync)(\s|$)", "Remote connection"),
111
+ (r"git\s+push\s+.*(-f|--force)", "Force push"),
112
+ (r"git\s+push\s+.*(main|master|production|release)", "Push to protected branch"),
113
+ (r"chmod\s+(777|666|a\+[rwx])", "Overly permissive chmod"),
114
+ (r"docker\s+run\s+.*--privileged", "Privileged container"),
115
+ (r"python[23]?\s+-c\s+", "Inline Python execution"),
116
+ (r"node\s+-e\s+", "Inline Node execution"),
117
+ ]
118
+
119
+ UNTRUSTED_MUTATION_PATTERNS = [
120
+ r"\bgit\s+(commit|push|tag)\b",
121
+ r"\bnpm\s+(install|publish)\b",
122
+ r"\bpython[23]?\s+.*\b(setup\.py|manage\.py)\b",
123
+ r"\b(mv|cp|tee|sed\s+-i|touch|mkdir)\b",
124
+ ]
125
+
126
+ TRUSTED_CONTENT_TIERS = frozenset({"local", "balanced"})
127
+ UNTRUSTED_EXTERNAL_TIERS = frozenset({"research", "browser"})
128
+
129
+ INJECTION_MARKER_PATTERNS: tuple[tuple[re.Pattern[str], float, str], ...] = (
130
+ (re.compile(r"\bIGNORE\s+(?:ALL\s+)?PREVIOUS(?:\s+INSTRUCTIONS?)?\b", re.IGNORECASE), 0.03, "ignore-previous-instructions"),
131
+ (re.compile(r"<\|im_start\|>", re.IGNORECASE), 0.03, "im-start-token"),
132
+ (re.compile(r"<\|im_end\|>", re.IGNORECASE), 0.03, "im-end-token"),
133
+ (re.compile(r"\[INST\]", re.IGNORECASE), 0.03, "inst-token"),
134
+ (re.compile(r"\[/INST\]", re.IGNORECASE), 0.03, "inst-close-token"),
135
+ )
136
+
137
+ HIDDEN_INSTRUCTION_PATTERNS: tuple[tuple[re.Pattern[str], float, str], ...] = (
138
+ (re.compile(r"(?:^|\s)SYSTEM\s*:", re.IGNORECASE), 0.02, "system-role-token"),
139
+ (re.compile(r"(?:^|\s)ASSISTANT\s*:", re.IGNORECASE), 0.02, "assistant-role-token"),
140
+ (re.compile(r"(?:(?:#|//|/\*|<!--).{0,80})\b(?:ignore|override|jailbreak|bypass)\b", re.IGNORECASE), 0.01, "comment-hidden-instruction"),
141
+ (re.compile(r"\bbase64\s+(?:-d|--decode)\b", re.IGNORECASE), 0.01, "base64-decoder-token"),
142
+ (re.compile(r"\b[A-Za-z0-9+/]{48,}={0,2}\b"), 0.01, "opaque-base64-payload"),
143
+ )
144
+
145
+ CACHE_POISONING_PATTERNS: tuple[tuple[re.Pattern[str], float, str], ...] = (
146
+ (re.compile(r"(?:>|>>|tee\b|cp\b|mv\b|rm\b|sed\s+-i\b).{0,120}(?:/)?\.omg/state/", re.IGNORECASE), 0.04, "state-path-overwrite-attempt"),
147
+ (re.compile(r"(?:>|>>|tee\b|cp\b|mv\b|rm\b|sed\s+-i\b).{0,120}(?:/)?\.omg/shadow/active-run", re.IGNORECASE), 0.04, "active-run-overwrite-attempt"),
148
+ (re.compile(r"\b(?:cache|state)\s*(?:poison|override|overwrite|tamper)\b", re.IGNORECASE), 0.02, "cache-poisoning-language"),
149
+ )
150
+
151
+ CLARIFICATION_AMBIGUITY_PATTERNS: tuple[tuple[re.Pattern[str], float, str], ...] = (
152
+ (re.compile(r"\b(?:without\s+asking|no\s+questions\s+asked|skip\s+clarif(?:y|ication))\b", re.IGNORECASE), 0.08, "clarification-bypass-language"),
153
+ (re.compile(r"\b(?:just\s+fix\s+it|fix\s+everything|do\s+whatever\s+it\s+takes)\b", re.IGNORECASE), 0.08, "ambiguous-mutation-intent"),
154
+ )
155
+
156
+
157
+ def _project_dir() -> str:
158
+ return os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
159
+
160
+
161
+ def _load_untrusted_provenance_entries() -> list[dict[str, Any]]:
162
+ try:
163
+ from runtime.untrusted_content import get_untrusted_content_state
164
+
165
+ state = get_untrusted_content_state(_project_dir())
166
+ provenance = state.get("provenance", [])
167
+ if isinstance(provenance, list):
168
+ return [entry for entry in provenance if isinstance(entry, dict)]
169
+ except Exception:
170
+ return []
171
+ return []
172
+
173
+
174
+ def _is_state_changing_action(action: str) -> bool:
175
+ normalized = str(action).strip().lower()
176
+ return normalized in {
177
+ "state_change",
178
+ "state-changing",
179
+ "bash_mutation",
180
+ "file_mutation",
181
+ "write",
182
+ "edit",
183
+ "delete",
184
+ }
185
+
186
+
187
+ def evaluate_action_justification(
188
+ *,
189
+ action: str,
190
+ evidence: list[dict[str, Any]],
191
+ require_explicit_approval: bool = True,
192
+ ) -> PolicyDecision:
193
+ if not _is_state_changing_action(action):
194
+ return allow("non-mutating action")
195
+ if not evidence:
196
+ return ask(
197
+ "State-changing action lacks trust-scored evidence.",
198
+ "high",
199
+ ["manual-approval", "trusted-evidence-required"],
200
+ )
201
+
202
+ tiers = {
203
+ str(item.get("_trust_tier") or item.get("trust_tier") or "").strip().lower()
204
+ for item in evidence
205
+ if isinstance(item, dict)
206
+ }
207
+ tiers.discard("")
208
+ has_trusted = bool(tiers & TRUSTED_CONTENT_TIERS)
209
+ has_external_only = bool(tiers) and tiers.issubset(UNTRUSTED_EXTERNAL_TIERS)
210
+
211
+ if has_trusted:
212
+ return allow("trusted local evidence present")
213
+
214
+ if has_external_only:
215
+ reason = (
216
+ "State-changing action is justified only by UNTRUSTED_EXTERNAL_CONTENT "
217
+ "(research/browser tier)."
218
+ )
219
+ controls = ["manual-approval", "trusted-corroboration", "review-provenance"]
220
+ if require_explicit_approval:
221
+ return ask(reason, "high", controls)
222
+ return deny(reason, "high", controls)
223
+
224
+ return ask(
225
+ "State-changing action has unknown trust provenance.",
226
+ "high",
227
+ ["manual-approval", "review-provenance"],
228
+ )
229
+
230
+
231
+ def _is_untrusted_content_mode_active() -> bool:
232
+ try:
233
+ from runtime.untrusted_content import is_untrusted_content_mode_active
234
+
235
+ project_dir = _project_dir()
236
+ return is_untrusted_content_mode_active(project_dir)
237
+ except Exception:
238
+ return False
239
+
240
+
241
+ def scan_mutation_command(cmd: str) -> dict[str, Any]:
242
+ text = str(cmd or "")
243
+ if not text.strip():
244
+ return {
245
+ "injection_hits": 0,
246
+ "contamination_score": 0.0,
247
+ "overthinking_score": 0.0,
248
+ "premature_fixer_score": 0.0,
249
+ "signals": [],
250
+ }
251
+
252
+ contamination_score = 0.0
253
+ overthinking_score = 0.0
254
+ premature_fixer_score = 0.0
255
+ injection_hits = 0
256
+ signals: list[str] = []
257
+
258
+ for pattern, weight, label in INJECTION_MARKER_PATTERNS:
259
+ if pattern.search(text):
260
+ injection_hits += 1
261
+ contamination_score += weight
262
+ signals.append(label)
263
+
264
+ for pattern, weight, label in HIDDEN_INSTRUCTION_PATTERNS:
265
+ if pattern.search(text):
266
+ injection_hits += 1
267
+ contamination_score += weight
268
+ signals.append(label)
269
+
270
+ for pattern, weight, label in CACHE_POISONING_PATTERNS:
271
+ if pattern.search(text):
272
+ injection_hits += 1
273
+ contamination_score += weight
274
+ signals.append(label)
275
+
276
+ for pattern, weight, label in CLARIFICATION_AMBIGUITY_PATTERNS:
277
+ if pattern.search(text):
278
+ overthinking_score += weight
279
+ premature_fixer_score += weight
280
+ signals.append(label)
281
+
282
+ return {
283
+ "injection_hits": max(0, injection_hits),
284
+ "contamination_score": round(max(0.0, min(1.0, contamination_score)), 4),
285
+ "overthinking_score": round(max(0.0, min(1.0, overthinking_score)), 4),
286
+ "premature_fixer_score": round(max(0.0, min(1.0, premature_fixer_score)), 4),
287
+ "signals": signals,
288
+ }
289
+
290
+
291
+ def evaluate_bash_command(cmd: str) -> PolicyDecision:
292
+ if not cmd:
293
+ return allow("empty command")
294
+
295
+ for pat, label in DESTRUCT_PATTERNS:
296
+ if re.search(pat, cmd):
297
+ return deny(f"Blocked: {label}", "critical", ["destructive-op"])
298
+
299
+ for pat in PIPE_SHELL_PATTERNS:
300
+ if re.search(pat, cmd):
301
+ return deny("Blocked: pipe-to-shell", "critical", ["remote-code-exec"])
302
+
303
+ for pat in EVAL_PATTERNS:
304
+ if re.search(pat, cmd):
305
+ return deny("Blocked: dynamic eval", "high", ["dynamic-eval"])
306
+
307
+ for secret_pat in SECRET_FILE_PATTERNS:
308
+ if not re.search(secret_pat, cmd, re.IGNORECASE):
309
+ continue
310
+
311
+ if SAFE_ENV_REFERENCE.search(cmd):
312
+ cleaned = SAFE_ENV_REFERENCE.sub("__SAFE_REF__", cmd)
313
+ if not re.search(secret_pat, cleaned, re.IGNORECASE):
314
+ continue
315
+
316
+ if re.search(READ_PATTERN, cmd, re.IGNORECASE):
317
+ return deny("Blocked: reading secret file", "critical", ["secret-access"])
318
+
319
+ if re.search(r"<\s*\S*(" + secret_pat + r")", cmd, re.IGNORECASE):
320
+ return deny("Blocked: reading secret file via redirect", "critical", ["secret-access"])
321
+
322
+ for exfil in EXFIL_COMMANDS:
323
+ if re.search(exfil, cmd):
324
+ return deny("Blocked: copying secret file", "critical", ["secret-exfiltration"])
325
+
326
+ if re.search(r"\bgrep\b", cmd):
327
+ return ask("Searching inside potential secret file — confirm this is safe", "high", ["secret-search"])
328
+
329
+ for pat, label in ASK_PATTERNS:
330
+ if re.search(pat, cmd):
331
+ return ask(f"{label}: {cmd[:120]}", "med", ["human-approval"])
332
+
333
+ for pat in UNTRUSTED_MUTATION_PATTERNS:
334
+ if not re.search(pat, cmd):
335
+ continue
336
+ provenance_entries = _load_untrusted_provenance_entries()
337
+ if provenance_entries:
338
+ decision = evaluate_action_justification(
339
+ action="state_change",
340
+ evidence=provenance_entries,
341
+ require_explicit_approval=True,
342
+ )
343
+ if decision.action != "allow":
344
+ return decision
345
+ if _is_untrusted_content_mode_active():
346
+ return ask(
347
+ "Untrusted external content mode is active. Review before running state-changing commands.",
348
+ "high",
349
+ ["manual-approval", "review-provenance"],
350
+ )
351
+ break
352
+
353
+ return allow("command allowed")
354
+
355
+
356
+ # === FILE POLICY ============================================================
357
+
358
+ BLOCKED_FILES = {
359
+ ".env", ".env.local", ".env.development", ".env.production",
360
+ ".env.staging", ".env.test", ".npmrc", ".pypirc", ".netrc",
361
+ "id_rsa", "id_ed25519", "id_ecdsa", "id_rsa.pub", "id_ed25519.pub", "id_ecdsa.pub",
362
+ }
363
+
364
+ EXAMPLE_FILES = {".env.example", ".env.sample", ".env.template"}
365
+
366
+ _SECRET_VALUE_RE = re.compile(
367
+ r"^(\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*\s*=\s*)(.+)$"
368
+ )
369
+ _SAFE_VALUES = {
370
+ "true", "false", "0", "1", "yes", "no",
371
+ "development", "production", "staging", "test", "localhost",
372
+ }
373
+ _MASKED_UNPARSEABLE_ENV_LINE = "[masked unparseable line]"
374
+
375
+ BLOCKED_PATH_PATTERNS = [
376
+ r"/\.aws/(credentials|config)$",
377
+ r"/\.kube/config$",
378
+ r"/\.ssh/",
379
+ r"/\.gnupg/",
380
+ r"/secrets?/",
381
+ r"\.(pem|key|p12|pfx|jks|keystore)$",
382
+ r"(^|/)secret[s]?\.",
383
+ r"(^|/)credential[s]?\.",
384
+ r"(^|/)password[s]?\.",
385
+ r"(^|/)token[s]?\.",
386
+ r"(^|/)\.docker/config\.json$",
387
+ r"(^|/)\.git-credentials$",
388
+ ]
389
+
390
+
391
+ # OMG internal credential store paths (exempted from secret-file blocking)
392
+ # Only these exact filenames inside .omg/state/ are allowed.
393
+ _OMG_CREDENTIAL_STORE_ALLOWLIST = frozenset({
394
+ "credentials.enc",
395
+ "credentials.meta",
396
+ })
397
+
398
+
399
+ def _is_omg_credential_path(normalized_path: str) -> bool:
400
+ """Return True if the path is an OMG credential store file.
401
+
402
+ Only exempts files that are:
403
+ 1. Inside the current project's .omg/state/ directory
404
+ 2. Named exactly 'credentials.enc' or 'credentials.meta'
405
+ 3. Feature flag MULTI_CREDENTIAL is enabled
406
+
407
+ This is deliberately narrow to prevent path traversal attacks.
408
+ """
409
+ # Import here to avoid circular dependency at module level
410
+ try:
411
+ get_feature_flag = getattr(importlib.import_module("hooks._common"), "get_feature_flag")
412
+ except Exception:
413
+ get_feature_flag = getattr(importlib.import_module("_common"), "get_feature_flag")
414
+
415
+ if not get_feature_flag("MULTI_CREDENTIAL", default=False):
416
+ return False
417
+
418
+ try:
419
+ real_path = os.path.realpath(normalized_path)
420
+ except (OSError, ValueError):
421
+ real_path = normalized_path
422
+
423
+ basename = os.path.basename(real_path).lower()
424
+ if basename not in _OMG_CREDENTIAL_STORE_ALLOWLIST:
425
+ return False
426
+
427
+ project_state_dir = os.path.realpath(os.path.join(_project_dir(), ".omg", "state"))
428
+ return os.path.dirname(real_path) == project_state_dir
429
+
430
+
431
+ def mask_env_content(file_path: str) -> str:
432
+ """Return a masked preview of env-file contents."""
433
+ try:
434
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
435
+ lines = f.readlines()
436
+ except OSError:
437
+ return "[Could not read file]"
438
+
439
+ masked: list[str] = []
440
+ for line in lines:
441
+ stripped = line.rstrip("\n")
442
+ if stripped.startswith("#") or not stripped.strip():
443
+ masked.append(stripped)
444
+ continue
445
+ match = _SECRET_VALUE_RE.match(stripped)
446
+ if not match:
447
+ masked.append(_MASKED_UNPARSEABLE_ENV_LINE)
448
+ continue
449
+ key_part, value_part = match.group(1), match.group(2).strip().strip("\"'")
450
+ if not value_part or value_part.lower() in _SAFE_VALUES:
451
+ masked.append(stripped)
452
+ else:
453
+ masked.append(f"{key_part}****")
454
+ return "\n".join(masked)
455
+
456
+
457
+ # === ALLOWLIST SUPPORT =======================================================
458
+
459
+ # Globs that are too broad to be safe — reject these in allowlist entries.
460
+ OVERLY_BROAD_GLOBS = frozenset({
461
+ "*", "**", "**/*", "**/**", "*/*", "*/**",
462
+ })
463
+
464
+
465
+ def validate_allowlist_entry(entry: dict[str, Any]) -> None:
466
+ """Validate a single allowlist entry.
467
+
468
+ Schema: {"path": "glob", "tools": ["Read", "Write"], "reason": "text"}
469
+
470
+ Raises ValueError if the entry is invalid.
471
+ """
472
+ if not isinstance(entry, dict):
473
+ raise ValueError("Allowlist entry must be a dict")
474
+
475
+ for field in ("path", "tools", "reason"):
476
+ if field not in entry:
477
+ raise ValueError(f"Missing required field: {field}")
478
+
479
+ path = entry["path"]
480
+ if path in OVERLY_BROAD_GLOBS:
481
+ raise ValueError(f"Overly broad glob rejected: {path}")
482
+
483
+ tools = entry["tools"]
484
+ if not isinstance(tools, list) or not tools:
485
+ raise ValueError("tools must be a non-empty list")
486
+
487
+
488
+ def is_allowlisted(file_path: str, tool: str, allowlist: list[dict[str, Any]]) -> bool:
489
+ """Check if a file_path + tool combination is allowlisted.
490
+
491
+ Matches the file's basename and normalized path against allowlist globs.
492
+ Invalid entries are silently skipped.
493
+
494
+ Returns True if the path+tool matches any valid allowlist entry.
495
+ """
496
+ if not allowlist:
497
+ return False
498
+
499
+ normalized = os.path.normpath(file_path)
500
+ basename = os.path.basename(normalized)
501
+
502
+ for entry in allowlist:
503
+ try:
504
+ validate_allowlist_entry(entry)
505
+ except (ValueError, TypeError):
506
+ continue
507
+
508
+ pattern = entry["path"]
509
+ entry_tools = entry["tools"]
510
+
511
+ # Match against basename or full normalized path
512
+ if fnmatch(basename, pattern) or fnmatch(normalized, pattern):
513
+ if tool in entry_tools:
514
+ _log_allowlist_bypass(
515
+ file_path, tool, entry.get("reason", "")
516
+ )
517
+ return True
518
+
519
+ return False
520
+
521
+
522
+ def load_allowlist(project_dir: str = ".") -> list[dict[str, Any]]:
523
+ """Load allowlist entries from .omg/policy.yaml.
524
+
525
+ Returns a list of valid allowlist entries. Invalid entries (overly broad
526
+ globs, missing fields) are filtered out silently.
527
+
528
+ Returns empty list if file doesn't exist or has no allowlist section.
529
+ """
530
+ policy_path = os.path.join(project_dir, ".omg", "policy.yaml")
531
+ if not os.path.isfile(policy_path):
532
+ return []
533
+
534
+ try:
535
+ import yaml
536
+ with open(policy_path, "r") as f:
537
+ data = yaml.safe_load(f)
538
+ except ImportError:
539
+ # Fallback: no yaml module — try simple line-by-line parse
540
+ data = _parse_policy_yaml_fallback(policy_path)
541
+ except Exception:
542
+ return []
543
+
544
+ if not isinstance(data, dict):
545
+ return []
546
+
547
+ raw_allowlist = data.get("allowlist")
548
+ if not isinstance(raw_allowlist, list):
549
+ return []
550
+
551
+ # Filter out invalid entries
552
+ valid = []
553
+ for entry in raw_allowlist:
554
+ try:
555
+ validate_allowlist_entry(entry)
556
+ valid.append(entry)
557
+ except (ValueError, TypeError):
558
+ continue
559
+
560
+ return valid
561
+
562
+
563
+ def _parse_policy_yaml_fallback(path: str) -> dict[str, Any]:
564
+ """Minimal YAML-like parser for allowlist section only.
565
+
566
+ Used when PyYAML is not available. Handles simple allowlist entries.
567
+ """
568
+ try:
569
+ with open(path, "r") as f:
570
+ lines = f.readlines()
571
+ except Exception:
572
+ return {}
573
+
574
+ result: dict[str, Any] = {}
575
+ in_allowlist = False
576
+ allowlist: list[dict[str, Any]] = []
577
+ current_entry: dict[str, Any] | None = None
578
+
579
+ for line in lines:
580
+ stripped = line.rstrip()
581
+
582
+ if stripped == "allowlist:":
583
+ in_allowlist = True
584
+ continue
585
+
586
+ if in_allowlist:
587
+ # Detect end of allowlist section (new top-level key)
588
+ if stripped and not stripped.startswith(" ") and not stripped.startswith("\t"):
589
+ in_allowlist = False
590
+ continue
591
+
592
+ # New list entry
593
+ if stripped.lstrip().startswith("- path:"):
594
+ if current_entry is not None:
595
+ allowlist.append(current_entry)
596
+ val = stripped.split(":", 1)[1].strip().strip("'\"")
597
+ current_entry = {"path": val, "tools": [], "reason": ""}
598
+ elif current_entry is not None:
599
+ clean = stripped.strip()
600
+ if clean.startswith("reason:"):
601
+ current_entry["reason"] = clean.split(":", 1)[1].strip().strip("'\"")
602
+ elif clean.startswith("- ") and "tools" not in clean:
603
+ current_entry["tools"].append(clean[2:].strip().strip("'\""))
604
+
605
+ if current_entry is not None:
606
+ allowlist.append(current_entry)
607
+
608
+ if allowlist:
609
+ result["allowlist"] = allowlist
610
+
611
+ return result
612
+
613
+
614
+ def _log_allowlist_bypass(path: str, tool: str, reason: str) -> None:
615
+ """Record that an allowlist entry overrode a deny decision.
616
+
617
+ Writes an audit trail entry to .omg/state/ledger/secret-access.jsonl
618
+ with allowlisted=True. Uses CLAUDE_PROJECT_DIR or cwd as project root.
619
+ Silently fails — never raises exceptions (crash isolation invariant).
620
+ """
621
+ try:
622
+ try:
623
+ log_secret_access = getattr(importlib.import_module("hooks.secret_audit"), "log_secret_access")
624
+ except Exception:
625
+ log_secret_access = getattr(importlib.import_module("secret_audit"), "log_secret_access")
626
+
627
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
628
+ log_secret_access(
629
+ project_dir=project_dir,
630
+ tool=tool,
631
+ file_path=path,
632
+ decision="allow",
633
+ reason=f"allowlist bypass: {reason}",
634
+ allowlisted=True,
635
+ )
636
+ except Exception:
637
+ pass # Crash isolation: audit logging must never break policy evaluation
638
+
639
+
640
+ def evaluate_file_access(
641
+ tool: str,
642
+ file_path: str,
643
+ allowlist: list[dict[str, Any]] | None = None,
644
+ ) -> PolicyDecision:
645
+ """Evaluate file access policy.
646
+
647
+ If an allowlist is provided, matching entries may override non-secret-file
648
+ deny decisions for the given path and tool combination.
649
+ """
650
+ if not file_path:
651
+ return allow("no file")
652
+
653
+ normalized = os.path.normpath(file_path)
654
+ # Resolve symlinks to prevent bypass via symlink to secret file
655
+ try:
656
+ normalized = os.path.realpath(normalized)
657
+ except (OSError, ValueError):
658
+ pass
659
+ basename = os.path.basename(normalized).lower()
660
+ lowpath = normalized.lower()
661
+
662
+ if basename in EXAMPLE_FILES and tool in ("Write", "Edit", "MultiEdit"):
663
+ return deny(
664
+ f"Modifying example env file blocked (Read is allowed): {file_path}",
665
+ "high",
666
+ ["immutable-env-template"],
667
+ )
668
+
669
+ if re.match(r"^\.env(\..+)?$", basename) and basename not in EXAMPLE_FILES:
670
+ if tool in ("Write", "Edit", "MultiEdit"):
671
+ return deny(
672
+ f"Secret file write blocked: {file_path}",
673
+ "critical",
674
+ ["secret-access"],
675
+ )
676
+ masked = mask_env_content(normalized)
677
+ return deny(
678
+ f"Direct .env read blocked. Masked preview:\n\n{masked}\n\n"
679
+ "Keys/tokens/passwords masked with ****. Ask the user for specific values if needed.",
680
+ "high",
681
+ ["secret-access-masked"],
682
+ )
683
+
684
+ if basename in BLOCKED_FILES:
685
+ return deny(f"Secret file blocked: {file_path}", "critical", ["secret-access"])
686
+
687
+ # EXEMPTION: OMG credential store files within .omg/state/
688
+ # These are managed by hooks/credential_store.py and must be accessible
689
+ if _is_omg_credential_path(normalized):
690
+ return allow("OMG credential store (managed path)")
691
+
692
+ for pat in BLOCKED_PATH_PATTERNS:
693
+ if re.search(pat, lowpath):
694
+ return deny(f"Sensitive path blocked: {file_path}", "critical", ["secret-access"])
695
+
696
+ if tool in {"Write", "Edit", "MultiEdit"}:
697
+ provenance_entries = _load_untrusted_provenance_entries()
698
+ if provenance_entries:
699
+ decision = evaluate_action_justification(
700
+ action="file_mutation",
701
+ evidence=provenance_entries,
702
+ require_explicit_approval=True,
703
+ )
704
+ if decision.action != "allow":
705
+ return decision
706
+ if _is_untrusted_content_mode_active():
707
+ return ask(
708
+ "Untrusted external content mode is active. Review before mutating files.",
709
+ "high",
710
+ ["manual-approval", "review-provenance"],
711
+ )
712
+
713
+ if allowlist and is_allowlisted(file_path, tool, allowlist):
714
+ return allow(f"Allowlisted: {file_path}")
715
+
716
+ return allow("file allowed")
717
+
718
+
719
+ # === SUPPLY CHAIN POLICY ====================================================
720
+
721
+
722
+ def evaluate_supply_artifact(artifact: dict[str, Any], mode: str = "warn_and_run") -> PolicyDecision:
723
+ """Verify artifact trust with Warn-And-Run semantics.
724
+
725
+ mode=warn_and_run: missing trust metadata returns ASK
726
+ critical findings always DENY
727
+ """
728
+ findings = artifact.get("static_scan") or []
729
+ permissions = artifact.get("permissions") or []
730
+ signer = artifact.get("signer")
731
+ checksum = artifact.get("checksum")
732
+
733
+ for finding in findings:
734
+ sev = str((finding or {}).get("severity", "")).lower()
735
+ if sev == "critical":
736
+ return deny("Critical static-scan finding detected", "critical", ["supply-critical-block"])
737
+
738
+ joined_perms = " ".join(str(p) for p in permissions)
739
+ if any(token in joined_perms for token in ["sudo", "rm -rf", "--privileged", "curl |", "wget |"]):
740
+ return deny("Critical permission profile detected in artifact", "critical", ["dangerous-permissions"])
741
+
742
+ if not signer or not checksum:
743
+ if mode == "warn_and_run":
744
+ return ask(
745
+ "Artifact missing signer/checksum metadata (untrusted). Continue with isolation.",
746
+ "high",
747
+ ["isolate-network", "read-only-fs", "manual-approval"],
748
+ )
749
+ return deny("Artifact missing signer/checksum metadata", "high", ["unsigned-artifact"])
750
+
751
+ has_high = any(str((finding or {}).get("severity", "")).lower() == "high" for finding in findings)
752
+ if has_high:
753
+ return ask("High-risk findings present. Explicit approval required.", "high", ["manual-approval"])
754
+
755
+ return allow("artifact trusted")
756
+
757
+
758
+ def to_pretool_hook_output(decision: PolicyDecision) -> dict[str, Any] | None:
759
+ if decision.action == "allow":
760
+ return None
761
+ return {
762
+ "hookSpecificOutput": {
763
+ "hookEventName": "PreToolUse",
764
+ "permissionDecision": decision.action,
765
+ "permissionDecisionReason": decision.reason,
766
+ }
767
+ }