@jaguilar87/gaia-ops 4.4.0 → 4.7.2
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +12 -3
- package/ARCHITECTURE.md +9 -8
- package/CHANGELOG.md +34 -0
- package/README.md +43 -11
- package/agents/terraform-architect.md +1 -1
- package/bin/README.md +2 -2
- package/bin/gaia-doctor.js +18 -5
- package/bin/gaia-history.js +0 -1
- package/bin/gaia-metrics.js +2 -2
- package/bin/gaia-scan.py +23 -1
- package/bin/gaia-update.js +346 -54
- package/bin/pre-publish-validate.js +33 -10
- package/commands/gaia.md +37 -0
- package/config/README.md +3 -9
- package/config/context-contracts.json +47 -15
- package/config/surface-routing.json +9 -1
- package/dist/gaia-ops/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-ops/agents/cloud-troubleshooter.md +73 -0
- package/dist/gaia-ops/agents/devops-developer.md +57 -0
- package/dist/gaia-ops/agents/gaia-system.md +58 -0
- package/dist/gaia-ops/agents/gitops-operator.md +60 -0
- package/dist/gaia-ops/agents/speckit-planner.md +71 -0
- package/dist/gaia-ops/agents/terraform-architect.md +60 -0
- package/dist/gaia-ops/commands/gaia.md +37 -0
- package/dist/gaia-ops/config/README.md +58 -0
- package/dist/gaia-ops/config/cloud/aws.json +140 -0
- package/dist/gaia-ops/config/cloud/gcp.json +145 -0
- package/dist/gaia-ops/config/context-contracts.json +131 -0
- package/dist/gaia-ops/config/git_standards.json +72 -0
- package/dist/gaia-ops/config/surface-routing.json +197 -0
- package/dist/gaia-ops/config/universal-rules.json +10 -0
- package/dist/gaia-ops/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-ops/hooks/adapters/base.py +219 -0
- package/dist/gaia-ops/hooks/adapters/channel.py +17 -0
- package/dist/gaia-ops/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-ops/hooks/adapters/types.py +194 -0
- package/dist/gaia-ops/hooks/adapters/utils.py +25 -0
- package/dist/gaia-ops/hooks/hooks.json +126 -0
- package/dist/gaia-ops/hooks/modules/__init__.py +15 -0
- package/dist/gaia-ops/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-ops/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-ops/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-ops/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-ops/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-ops/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-ops/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-ops/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-ops/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-ops/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-ops/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-ops/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-ops/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-ops/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-ops/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-ops/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-ops/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-ops/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-ops/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-ops/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-ops/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-ops/hooks/modules/core/state.py +179 -0
- package/dist/gaia-ops/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-ops/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-ops/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-ops/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-ops/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-ops/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-ops/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-ops/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-ops/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-ops/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-ops/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-ops/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-ops/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-ops/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-ops/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-ops/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-ops/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-ops/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-ops/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-ops/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-ops/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-ops/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-ops/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-ops/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-ops/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-ops/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-ops/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-ops/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-ops/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-ops/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-ops/hooks/post_compact.py +43 -0
- package/dist/gaia-ops/hooks/post_tool_use.py +54 -0
- package/dist/gaia-ops/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-ops/hooks/session_start.py +69 -0
- package/dist/gaia-ops/hooks/stop_hook.py +69 -0
- package/dist/gaia-ops/hooks/subagent_start.py +71 -0
- package/dist/gaia-ops/hooks/subagent_stop.py +288 -0
- package/dist/gaia-ops/hooks/task_completed.py +70 -0
- package/dist/gaia-ops/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-ops/settings.json +72 -0
- package/dist/gaia-ops/skills/README.md +109 -0
- package/dist/gaia-ops/skills/agent-protocol/SKILL.md +105 -0
- package/dist/gaia-ops/skills/agent-protocol/examples.md +170 -0
- package/dist/gaia-ops/skills/agent-response/SKILL.md +53 -0
- package/dist/gaia-ops/skills/approval/SKILL.md +85 -0
- package/dist/gaia-ops/skills/approval/examples.md +140 -0
- package/dist/gaia-ops/skills/approval/reference.md +57 -0
- package/dist/gaia-ops/skills/command-execution/SKILL.md +64 -0
- package/dist/gaia-ops/skills/command-execution/reference.md +83 -0
- package/dist/gaia-ops/skills/context-updater/SKILL.md +76 -0
- package/dist/gaia-ops/skills/context-updater/examples.md +71 -0
- package/dist/gaia-ops/skills/developer-patterns/SKILL.md +93 -0
- package/dist/gaia-ops/skills/developer-patterns/reference.md +112 -0
- package/dist/gaia-ops/skills/execution/SKILL.md +66 -0
- package/dist/gaia-ops/skills/fast-queries/SKILL.md +47 -0
- package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +92 -0
- package/dist/gaia-ops/skills/gaia-patterns/reference.md +22 -0
- package/dist/gaia-ops/skills/git-conventions/SKILL.md +48 -0
- package/dist/gaia-ops/skills/gitops-patterns/SKILL.md +73 -0
- package/dist/gaia-ops/skills/gitops-patterns/reference.md +183 -0
- package/dist/gaia-ops/skills/investigation/SKILL.md +77 -0
- package/dist/gaia-ops/skills/orchestrator-approval/SKILL.md +64 -0
- package/dist/gaia-ops/skills/reference.md +134 -0
- package/dist/gaia-ops/skills/security-tiers/SKILL.md +61 -0
- package/dist/gaia-ops/skills/security-tiers/destructive-commands-reference.md +623 -0
- package/dist/gaia-ops/skills/security-tiers/reference.md +39 -0
- package/dist/gaia-ops/skills/skill-creation/SKILL.md +119 -0
- package/dist/gaia-ops/skills/specification/SKILL.md +186 -0
- package/dist/gaia-ops/skills/speckit-workflow/SKILL.md +165 -0
- package/dist/gaia-ops/skills/speckit-workflow/reference.md +117 -0
- package/dist/gaia-ops/skills/terraform-patterns/SKILL.md +63 -0
- package/dist/gaia-ops/skills/terraform-patterns/reference.md +93 -0
- package/dist/gaia-ops/speckit/README.md +516 -0
- package/dist/gaia-ops/speckit/scripts/.gitkeep +0 -0
- package/dist/gaia-ops/speckit/templates/adr-template.md +118 -0
- package/dist/gaia-ops/speckit/templates/agent-file-template.md +23 -0
- package/dist/gaia-ops/speckit/templates/plan-template.md +227 -0
- package/dist/gaia-ops/speckit/templates/spec-template.md +140 -0
- package/dist/gaia-ops/speckit/templates/tasks-template.md +257 -0
- package/dist/gaia-ops/tools/context/README.md +132 -0
- package/dist/gaia-ops/tools/context/__init__.py +42 -0
- package/dist/gaia-ops/tools/context/_paths.py +20 -0
- package/dist/gaia-ops/tools/context/context_provider.py +476 -0
- package/dist/gaia-ops/tools/context/context_section_reader.py +330 -0
- package/dist/gaia-ops/tools/context/deep_merge.py +159 -0
- package/dist/gaia-ops/tools/context/pending_updates.py +760 -0
- package/dist/gaia-ops/tools/context/surface_router.py +278 -0
- package/dist/gaia-ops/tools/fast-queries/README.md +65 -0
- package/dist/gaia-ops/tools/fast-queries/__init__.py +30 -0
- package/dist/gaia-ops/tools/fast-queries/appservices/quicktriage_devops_developer.sh +75 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/aws/quicktriage_aws_troubleshooter.sh +32 -0
- package/dist/gaia-ops/tools/fast-queries/cloud/gcp/quicktriage_gcp_troubleshooter.sh +88 -0
- package/dist/gaia-ops/tools/fast-queries/gitops/quicktriage_gitops_operator.sh +48 -0
- package/dist/gaia-ops/tools/fast-queries/run_triage.sh +59 -0
- package/dist/gaia-ops/tools/fast-queries/terraform/quicktriage_terraform_architect.sh +80 -0
- package/dist/gaia-ops/tools/gaia_simulator/__init__.py +33 -0
- package/dist/gaia-ops/tools/gaia_simulator/cli.py +354 -0
- package/dist/gaia-ops/tools/gaia_simulator/extractor.py +457 -0
- package/dist/gaia-ops/tools/gaia_simulator/reporter.py +258 -0
- package/dist/gaia-ops/tools/gaia_simulator/routing_simulator.py +334 -0
- package/dist/gaia-ops/tools/gaia_simulator/runner.py +539 -0
- package/dist/gaia-ops/tools/gaia_simulator/skills_mapper.py +262 -0
- package/dist/gaia-ops/tools/memory/README.md +0 -0
- package/dist/gaia-ops/tools/memory/__init__.py +20 -0
- package/dist/gaia-ops/tools/memory/episodic.py +1196 -0
- package/dist/gaia-ops/tools/persist_transcript_analysis.py +85 -0
- package/dist/gaia-ops/tools/review/__init__.py +1 -0
- package/dist/gaia-ops/tools/review/review_engine.py +157 -0
- package/dist/gaia-ops/tools/scan/__init__.py +35 -0
- package/dist/gaia-ops/tools/scan/config.py +247 -0
- package/dist/gaia-ops/tools/scan/merge.py +212 -0
- package/dist/gaia-ops/tools/scan/orchestrator.py +549 -0
- package/dist/gaia-ops/tools/scan/registry.py +127 -0
- package/dist/gaia-ops/tools/scan/scanners/__init__.py +18 -0
- package/dist/gaia-ops/tools/scan/scanners/base.py +137 -0
- package/dist/gaia-ops/tools/scan/scanners/environment.py +324 -0
- package/dist/gaia-ops/tools/scan/scanners/git.py +570 -0
- package/dist/gaia-ops/tools/scan/scanners/infrastructure.py +875 -0
- package/dist/gaia-ops/tools/scan/scanners/orchestration.py +600 -0
- package/dist/gaia-ops/tools/scan/scanners/stack.py +1085 -0
- package/dist/gaia-ops/tools/scan/scanners/tools.py +260 -0
- package/dist/gaia-ops/tools/scan/setup.py +753 -0
- package/dist/gaia-ops/tools/scan/tests/__init__.py +1 -0
- package/dist/gaia-ops/tools/scan/tests/conftest.py +796 -0
- package/dist/gaia-ops/tools/scan/tests/test_environment.py +323 -0
- package/dist/gaia-ops/tools/scan/tests/test_git.py +419 -0
- package/dist/gaia-ops/tools/scan/tests/test_infrastructure.py +382 -0
- package/dist/gaia-ops/tools/scan/tests/test_integration.py +920 -0
- package/dist/gaia-ops/tools/scan/tests/test_merge.py +269 -0
- package/dist/gaia-ops/tools/scan/tests/test_orchestration.py +304 -0
- package/dist/gaia-ops/tools/scan/tests/test_stack.py +604 -0
- package/dist/gaia-ops/tools/scan/tests/test_tools.py +349 -0
- package/dist/gaia-ops/tools/scan/ui.py +624 -0
- package/dist/gaia-ops/tools/scan/verify.py +266 -0
- package/dist/gaia-ops/tools/scan/walk.py +118 -0
- package/dist/gaia-ops/tools/scan/workspace.py +85 -0
- package/dist/gaia-ops/tools/validation/README.md +244 -0
- package/dist/gaia-ops/tools/validation/__init__.py +17 -0
- package/dist/gaia-ops/tools/validation/approval_gate.py +321 -0
- package/dist/gaia-ops/tools/validation/validate_skills.py +189 -0
- package/dist/gaia-security/.claude-plugin/plugin.json +22 -0
- package/dist/gaia-security/config/universal-rules.json +10 -0
- package/dist/gaia-security/hooks/adapters/__init__.py +52 -0
- package/dist/gaia-security/hooks/adapters/base.py +219 -0
- package/dist/gaia-security/hooks/adapters/channel.py +17 -0
- package/dist/gaia-security/hooks/adapters/claude_code.py +1477 -0
- package/dist/gaia-security/hooks/adapters/types.py +194 -0
- package/dist/gaia-security/hooks/adapters/utils.py +25 -0
- package/dist/gaia-security/hooks/hooks.json +57 -0
- package/dist/gaia-security/hooks/modules/__init__.py +15 -0
- package/dist/gaia-security/hooks/modules/agents/__init__.py +29 -0
- package/dist/gaia-security/hooks/modules/agents/contract_validator.py +647 -0
- package/dist/gaia-security/hooks/modules/agents/response_contract.py +496 -0
- package/dist/gaia-security/hooks/modules/agents/skill_injection_verifier.py +124 -0
- package/dist/gaia-security/hooks/modules/agents/task_info_builder.py +74 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_analyzer.py +458 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +152 -0
- package/dist/gaia-security/hooks/modules/audit/__init__.py +28 -0
- package/dist/gaia-security/hooks/modules/audit/event_detector.py +168 -0
- package/dist/gaia-security/hooks/modules/audit/logger.py +131 -0
- package/dist/gaia-security/hooks/modules/audit/metrics.py +134 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_auditor.py +576 -0
- package/dist/gaia-security/hooks/modules/audit/workflow_recorder.py +296 -0
- package/dist/gaia-security/hooks/modules/context/__init__.py +11 -0
- package/dist/gaia-security/hooks/modules/context/anchor_tracker.py +317 -0
- package/dist/gaia-security/hooks/modules/context/compact_context_builder.py +215 -0
- package/dist/gaia-security/hooks/modules/context/context_cache.py +129 -0
- package/dist/gaia-security/hooks/modules/context/context_freshness.py +145 -0
- package/dist/gaia-security/hooks/modules/context/context_injector.py +427 -0
- package/dist/gaia-security/hooks/modules/context/context_writer.py +518 -0
- package/dist/gaia-security/hooks/modules/context/contracts_loader.py +161 -0
- package/dist/gaia-security/hooks/modules/core/__init__.py +40 -0
- package/dist/gaia-security/hooks/modules/core/hook_entry.py +78 -0
- package/dist/gaia-security/hooks/modules/core/paths.py +160 -0
- package/dist/gaia-security/hooks/modules/core/plugin_mode.py +149 -0
- package/dist/gaia-security/hooks/modules/core/plugin_setup.py +558 -0
- package/dist/gaia-security/hooks/modules/core/state.py +179 -0
- package/dist/gaia-security/hooks/modules/core/stdin.py +24 -0
- package/dist/gaia-security/hooks/modules/events/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/events/event_writer.py +210 -0
- package/dist/gaia-security/hooks/modules/identity/__init__.py +0 -0
- package/dist/gaia-security/hooks/modules/identity/identity_provider.py +21 -0
- package/dist/gaia-security/hooks/modules/identity/ops_identity.py +34 -0
- package/dist/gaia-security/hooks/modules/identity/security_identity.py +10 -0
- package/dist/gaia-security/hooks/modules/memory/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/memory/episode_writer.py +227 -0
- package/dist/gaia-security/hooks/modules/orchestrator/__init__.py +1 -0
- package/dist/gaia-security/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/dist/gaia-security/hooks/modules/scanning/__init__.py +8 -0
- package/dist/gaia-security/hooks/modules/scanning/scan_trigger.py +84 -0
- package/dist/gaia-security/hooks/modules/security/__init__.py +89 -0
- package/dist/gaia-security/hooks/modules/security/approval_cleanup.py +87 -0
- package/dist/gaia-security/hooks/modules/security/approval_constants.py +23 -0
- package/dist/gaia-security/hooks/modules/security/approval_grants.py +912 -0
- package/dist/gaia-security/hooks/modules/security/approval_messages.py +71 -0
- package/dist/gaia-security/hooks/modules/security/approval_scopes.py +153 -0
- package/dist/gaia-security/hooks/modules/security/blocked_commands.py +584 -0
- package/dist/gaia-security/hooks/modules/security/blocked_message_formatter.py +86 -0
- package/dist/gaia-security/hooks/modules/security/command_semantics.py +130 -0
- package/dist/gaia-security/hooks/modules/security/gitops_validator.py +179 -0
- package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +850 -0
- package/dist/gaia-security/hooks/modules/security/prompt_validator.py +40 -0
- package/dist/gaia-security/hooks/modules/security/tiers.py +196 -0
- package/dist/gaia-security/hooks/modules/session/__init__.py +10 -0
- package/dist/gaia-security/hooks/modules/session/session_context_writer.py +100 -0
- package/dist/gaia-security/hooks/modules/session/session_event_injector.py +158 -0
- package/dist/gaia-security/hooks/modules/session/session_manager.py +31 -0
- package/dist/gaia-security/hooks/modules/tools/__init__.py +25 -0
- package/dist/gaia-security/hooks/modules/tools/bash_validator.py +708 -0
- package/dist/gaia-security/hooks/modules/tools/cloud_pipe_validator.py +181 -0
- package/dist/gaia-security/hooks/modules/tools/hook_response.py +55 -0
- package/dist/gaia-security/hooks/modules/tools/shell_parser.py +227 -0
- package/dist/gaia-security/hooks/modules/tools/task_validator.py +283 -0
- package/dist/gaia-security/hooks/modules/validation/__init__.py +23 -0
- package/dist/gaia-security/hooks/modules/validation/commit_validator.py +380 -0
- package/dist/gaia-security/hooks/post_tool_use.py +54 -0
- package/dist/gaia-security/hooks/pre_tool_use.py +383 -0
- package/dist/gaia-security/hooks/session_start.py +69 -0
- package/dist/gaia-security/hooks/stop_hook.py +69 -0
- package/dist/gaia-security/hooks/user_prompt_submit.py +177 -0
- package/dist/gaia-security/settings.json +58 -0
- package/git-hooks/commit-msg +41 -0
- package/hooks/README.md +8 -6
- package/hooks/adapters/channel.py +0 -25
- package/hooks/adapters/claude_code.py +364 -125
- package/hooks/elicitation_result.py +132 -0
- package/hooks/hooks.json +10 -1
- package/hooks/modules/README.md +3 -2
- package/hooks/modules/agents/contract_validator.py +3 -51
- package/hooks/modules/agents/response_contract.py +4 -8
- package/hooks/modules/agents/transcript_reader.py +4 -5
- package/hooks/modules/audit/__init__.py +4 -6
- package/hooks/modules/audit/event_detector.py +0 -2
- package/hooks/modules/audit/metrics.py +108 -187
- package/hooks/modules/audit/workflow_auditor.py +0 -4
- package/hooks/modules/audit/workflow_recorder.py +0 -5
- package/hooks/modules/context/compact_context_builder.py +1 -0
- package/hooks/modules/context/context_cache.py +129 -0
- package/hooks/modules/context/context_injector.py +18 -40
- package/hooks/modules/context/context_writer.py +1 -25
- package/hooks/modules/context/contracts_loader.py +7 -10
- package/hooks/modules/core/hook_entry.py +1 -0
- package/hooks/modules/core/paths.py +12 -13
- package/hooks/modules/core/plugin_mode.py +74 -4
- package/hooks/modules/core/plugin_setup.py +395 -23
- package/hooks/modules/events/__init__.py +1 -0
- package/hooks/modules/events/event_writer.py +210 -0
- package/hooks/modules/identity/ops_identity.py +18 -27
- package/hooks/modules/memory/episode_writer.py +1 -6
- package/hooks/modules/orchestrator/__init__.py +1 -0
- package/hooks/modules/orchestrator/delegate_mode.py +128 -0
- package/hooks/modules/security/__init__.py +2 -4
- package/hooks/modules/security/approval_constants.py +5 -1
- package/hooks/modules/security/approval_grants.py +189 -6
- package/hooks/modules/security/approval_messages.py +9 -21
- package/hooks/modules/security/blocked_commands.py +98 -34
- package/hooks/modules/security/command_semantics.py +0 -4
- package/hooks/modules/security/gitops_validator.py +1 -11
- package/hooks/modules/security/mutative_verbs.py +179 -38
- package/hooks/modules/security/tiers.py +1 -19
- package/hooks/modules/session/session_event_injector.py +1 -25
- package/hooks/modules/tools/bash_validator.py +310 -94
- package/hooks/modules/tools/shell_parser.py +0 -1
- package/hooks/modules/tools/task_validator.py +9 -29
- package/hooks/post_tool_use.py +0 -72
- package/hooks/pre_tool_use.py +42 -102
- package/hooks/session_start.py +4 -2
- package/hooks/subagent_start.py +6 -2
- package/hooks/subagent_stop.py +1 -13
- package/hooks/user_prompt_submit.py +119 -37
- package/index.js +1 -1
- package/package.json +5 -3
- package/skills/README.md +3 -5
- package/skills/agent-protocol/SKILL.md +17 -16
- package/skills/agent-protocol/examples.md +6 -6
- package/skills/agent-response/SKILL.md +11 -14
- package/skills/approval/SKILL.md +28 -13
- package/skills/approval/reference.md +2 -2
- package/skills/execution/SKILL.md +1 -1
- package/skills/gaia-patterns/SKILL.md +2 -3
- package/skills/orchestrator-approval/SKILL.md +22 -50
- package/skills/security-tiers/SKILL.md +1 -1
- package/templates/README.md +9 -9
- package/templates/managed-settings.template.json +43 -0
- package/tools/gaia_simulator/runner.py +34 -1
- package/tools/scan/orchestrator.py +13 -0
- package/tools/scan/scanners/base.py +8 -0
- package/tools/scan/scanners/git.py +78 -0
- package/tools/scan/scanners/infrastructure.py +65 -0
- package/tools/scan/scanners/stack.py +110 -0
- package/tools/scan/setup.py +120 -13
- package/tools/scan/workspace.py +85 -0
- package/config/context-contracts.aws.json +0 -42
- package/config/context-contracts.gcp.json +0 -39
- package/skills/project-dispatch/SKILL.md +0 -34
- package/templates/settings.template.json +0 -226
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Blocked command patterns - PERMANENTLY BLOCKED operations (exit 2, never approvable).
|
|
3
|
+
|
|
4
|
+
This is the single source of truth for DESTRUCTIVE commands. Commands matched here
|
|
5
|
+
are blocked with exit 2 and cannot be approved.
|
|
6
|
+
|
|
7
|
+
All other state-modifying commands are detected by the universal verb detector
|
|
8
|
+
(mutative_verbs.py) as MUTATIVE and routed through the user approval workflow.
|
|
9
|
+
|
|
10
|
+
Categories:
|
|
11
|
+
- AWS networking/data infrastructure delete operations
|
|
12
|
+
- AWS KMS/Route53/Organizations operations
|
|
13
|
+
- Azure resource group/networking/data/AKS/Key Vault delete operations
|
|
14
|
+
- GCP project/cluster/database delete operations
|
|
15
|
+
- Kubernetes critical delete operations (cluster, namespace, pv, node, CRD, webhooks)
|
|
16
|
+
- Kubernetes bulk delete operations (--all flag)
|
|
17
|
+
- Terraform/Terragrunt destroy (without -target)
|
|
18
|
+
- Docker bulk prune operations
|
|
19
|
+
- Flux uninstall (removes all Flux components)
|
|
20
|
+
- Git force push operations (not --force-with-lease)
|
|
21
|
+
- Git reset --hard
|
|
22
|
+
- GitHub/GitLab repo delete
|
|
23
|
+
- npm unpublish (entire package)
|
|
24
|
+
- SQL destructive operations (drop database, drop table)
|
|
25
|
+
- Disk/filesystem destruction operations (dd, fdisk, mkfs)
|
|
26
|
+
- rm -rf / and rm -rf ~
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import re
|
|
30
|
+
import logging
|
|
31
|
+
from typing import Dict, List, Optional, Tuple
|
|
32
|
+
from dataclasses import dataclass
|
|
33
|
+
|
|
34
|
+
from .command_semantics import CommandSemantics, analyze_command, _contains_ordered_sequence
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class BlockedCommandResult:
|
|
41
|
+
"""Result of blocked command check."""
|
|
42
|
+
is_blocked: bool
|
|
43
|
+
pattern_matched: Optional[str] = None
|
|
44
|
+
category: Optional[str] = None
|
|
45
|
+
suggestion: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class SemanticBlockedRule:
|
|
50
|
+
"""Ordered-token signature for deny-listed commands."""
|
|
51
|
+
|
|
52
|
+
category: str
|
|
53
|
+
sequence: Tuple[str, ...]
|
|
54
|
+
suggestion_key: str
|
|
55
|
+
required_flags: Tuple[str, ...] = ()
|
|
56
|
+
forbidden_flags: Tuple[str, ...] = ()
|
|
57
|
+
head_only: bool = True
|
|
58
|
+
|
|
59
|
+
def matches(self, semantics: CommandSemantics) -> bool:
|
|
60
|
+
tokens = semantics.semantic_head_tokens if self.head_only else semantics.semantic_tokens
|
|
61
|
+
if not _contains_ordered_sequence(tokens, self.sequence):
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
flags = set(semantics.flag_tokens)
|
|
65
|
+
if any(flag not in flags for flag in self.required_flags):
|
|
66
|
+
return False
|
|
67
|
+
# forbidden_flags uses prefix matching to handle -flag=value forms
|
|
68
|
+
# (e.g., "-target=aws_instance.web" should match forbidden flag "-target")
|
|
69
|
+
if self.forbidden_flags:
|
|
70
|
+
for flag_token in semantics.flag_tokens:
|
|
71
|
+
for forbidden in self.forbidden_flags:
|
|
72
|
+
if flag_token == forbidden or flag_token.startswith(forbidden + "="):
|
|
73
|
+
return False
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ============================================================================
|
|
78
|
+
# BLOCKED PATTERNS - PERMANENTLY BLOCKED (exit 2, no nonce, never approvable)
|
|
79
|
+
# ============================================================================
|
|
80
|
+
# These commands are PERMANENTLY BLOCKED and cannot be executed even with approval.
|
|
81
|
+
# They represent irreversible, catastrophic operations at scale.
|
|
82
|
+
#
|
|
83
|
+
# The following are MUTATIVE (approvable via nonce, handled by mutative_verbs.py):
|
|
84
|
+
# - aws iam delete-*, detach-*, remove-user-from-group
|
|
85
|
+
# - aws ec2 delete-key-pair, delete-snapshot, delete-volume,
|
|
86
|
+
# delete-security-group, delete-network-interface
|
|
87
|
+
# - aws lambda delete-function
|
|
88
|
+
# - aws rds delete-db-parameter-group, delete-db-cluster-parameter-group
|
|
89
|
+
# - aws cloudformation delete-stack
|
|
90
|
+
# - aws s3api delete-objects
|
|
91
|
+
# - aws sns delete-topic, aws sqs delete-queue
|
|
92
|
+
# - aws dynamodb delete-item
|
|
93
|
+
# - aws backup delete
|
|
94
|
+
# - aws eks delete-nodegroup, delete-addon
|
|
95
|
+
# - gcloud compute firewall-rules/instances/networks/disks/images/snapshots delete
|
|
96
|
+
# - gcloud container node-pools delete
|
|
97
|
+
# - gcloud iam roles delete
|
|
98
|
+
# - gcloud storage rm
|
|
99
|
+
# - kubectl delete pod/deployment/service/configmap/secret/clusterrole/clusterrolebinding
|
|
100
|
+
# - flux delete (single resource)
|
|
101
|
+
# - terraform destroy -target=<resource> (targeted)
|
|
102
|
+
# - helm uninstall (any release)
|
|
103
|
+
# - docker rm, docker rmi (individual containers/images)
|
|
104
|
+
# ============================================================================
|
|
105
|
+
|
|
106
|
+
BLOCKED_PATTERNS: Dict[str, List[re.Pattern]] = {
|
|
107
|
+
# AWS - Networking and data infrastructure (irreversible)
|
|
108
|
+
# Patterns use (?!-) negative lookahead to prevent prefix matching
|
|
109
|
+
# (e.g., delete-db-cluster must not match delete-db-cluster-parameter-group).
|
|
110
|
+
# \b alone is insufficient because hyphens are non-word characters.
|
|
111
|
+
"aws_critical": [
|
|
112
|
+
re.compile(r"aws\s+ec2\s+delete-vpc(?!-)\b", re.IGNORECASE),
|
|
113
|
+
re.compile(r"aws\s+ec2\s+delete-subnet(?!-)\b", re.IGNORECASE),
|
|
114
|
+
re.compile(r"aws\s+ec2\s+delete-internet-gateway(?!-)\b", re.IGNORECASE),
|
|
115
|
+
re.compile(r"aws\s+ec2\s+delete-route-table(?!-)\b", re.IGNORECASE),
|
|
116
|
+
re.compile(r"aws\s+ec2\s+delete-route(?!-)\b", re.IGNORECASE),
|
|
117
|
+
re.compile(r"aws\s+ec2\s+terminate-instances\b", re.IGNORECASE),
|
|
118
|
+
re.compile(r"aws\s+rds\s+delete-db-instance(?!-)\b", re.IGNORECASE),
|
|
119
|
+
re.compile(r"aws\s+rds\s+delete-db-cluster(?!-)\b", re.IGNORECASE),
|
|
120
|
+
re.compile(r"aws\s+dynamodb\s+delete-table(?!-)\b", re.IGNORECASE),
|
|
121
|
+
re.compile(r"aws\s+s3\s+rb(?!-)\b", re.IGNORECASE),
|
|
122
|
+
re.compile(r"aws\s+s3api\s+delete-bucket(?!-)\b", re.IGNORECASE),
|
|
123
|
+
re.compile(r"aws\s+elasticache\s+delete-cache-cluster(?!-)\b", re.IGNORECASE),
|
|
124
|
+
re.compile(r"aws\s+elasticache\s+delete-replication-group(?!-)\b", re.IGNORECASE),
|
|
125
|
+
re.compile(r"aws\s+eks\s+delete-cluster(?!-)\b", re.IGNORECASE),
|
|
126
|
+
re.compile(r"aws\s+kms\s+schedule-key-deletion\b", re.IGNORECASE),
|
|
127
|
+
re.compile(r"aws\s+organizations\s+delete-organization\b", re.IGNORECASE),
|
|
128
|
+
re.compile(r"aws\s+route53\s+delete-hosted-zone\b", re.IGNORECASE),
|
|
129
|
+
],
|
|
130
|
+
|
|
131
|
+
# Azure - Resource group, networking, data infrastructure (irreversible)
|
|
132
|
+
"azure_critical": [
|
|
133
|
+
re.compile(r"az\s+group\s+delete\b", re.IGNORECASE),
|
|
134
|
+
re.compile(r"az\s+network\s+vnet\s+delete\b", re.IGNORECASE),
|
|
135
|
+
re.compile(r"az\s+network\s+vnet\s+subnet\s+delete\b", re.IGNORECASE),
|
|
136
|
+
re.compile(r"az\s+network\s+nsg\s+delete\b", re.IGNORECASE),
|
|
137
|
+
re.compile(r"az\s+network\s+public-ip\s+delete\b", re.IGNORECASE),
|
|
138
|
+
re.compile(r"az\s+network\s+application-gateway\s+delete\b", re.IGNORECASE),
|
|
139
|
+
re.compile(r"az\s+network\s+lb\s+delete\b", re.IGNORECASE),
|
|
140
|
+
re.compile(r"az\s+network\s+dns\s+zone\s+delete\b", re.IGNORECASE),
|
|
141
|
+
re.compile(r"az\s+network\s+private-dns\s+zone\s+delete\b", re.IGNORECASE),
|
|
142
|
+
re.compile(r"az\s+vm\s+delete\b", re.IGNORECASE),
|
|
143
|
+
re.compile(r"az\s+vmss\s+delete\b", re.IGNORECASE),
|
|
144
|
+
re.compile(r"az\s+disk\s+delete\b", re.IGNORECASE),
|
|
145
|
+
re.compile(r"az\s+snapshot\s+delete\b", re.IGNORECASE),
|
|
146
|
+
re.compile(r"az\s+image\s+delete\b", re.IGNORECASE),
|
|
147
|
+
re.compile(r"az\s+sql\s+server\s+delete\b", re.IGNORECASE),
|
|
148
|
+
re.compile(r"az\s+sql\s+db\s+delete\b", re.IGNORECASE),
|
|
149
|
+
re.compile(r"az\s+cosmosdb\s+delete\b", re.IGNORECASE),
|
|
150
|
+
re.compile(r"az\s+redis\s+delete\b", re.IGNORECASE),
|
|
151
|
+
re.compile(r"az\s+storage\s+account\s+delete\b", re.IGNORECASE),
|
|
152
|
+
re.compile(r"az\s+storage\s+container\s+delete\b", re.IGNORECASE),
|
|
153
|
+
re.compile(r"az\s+storage\s+blob\s+delete-batch\b", re.IGNORECASE),
|
|
154
|
+
re.compile(r"az\s+aks\s+delete\b", re.IGNORECASE),
|
|
155
|
+
re.compile(r"az\s+aks\s+nodepool\s+delete\b", re.IGNORECASE),
|
|
156
|
+
re.compile(r"az\s+acr\s+delete\b", re.IGNORECASE),
|
|
157
|
+
re.compile(r"az\s+keyvault\s+delete\b", re.IGNORECASE),
|
|
158
|
+
re.compile(r"az\s+keyvault\s+key\s+delete\b", re.IGNORECASE),
|
|
159
|
+
re.compile(r"az\s+keyvault\s+secret\s+delete\b", re.IGNORECASE),
|
|
160
|
+
re.compile(r"az\s+functionapp\s+delete\b", re.IGNORECASE),
|
|
161
|
+
re.compile(r"az\s+webapp\s+delete\b", re.IGNORECASE),
|
|
162
|
+
re.compile(r"az\s+ad\s+app\s+delete\b", re.IGNORECASE),
|
|
163
|
+
re.compile(r"az\s+ad\s+sp\s+delete\b", re.IGNORECASE),
|
|
164
|
+
re.compile(r"az\s+servicebus\s+namespace\s+delete\b", re.IGNORECASE),
|
|
165
|
+
re.compile(r"az\s+eventhubs\s+namespace\s+delete\b", re.IGNORECASE),
|
|
166
|
+
],
|
|
167
|
+
|
|
168
|
+
# GCP - Project, cluster, and database operations (irreversible)
|
|
169
|
+
"gcp_critical": [
|
|
170
|
+
re.compile(r"gcloud\s+projects\s+delete\b", re.IGNORECASE),
|
|
171
|
+
re.compile(r"gcloud\s+container\s+clusters\s+delete\b", re.IGNORECASE),
|
|
172
|
+
re.compile(r"gcloud\s+sql\s+instances\s+delete\b", re.IGNORECASE),
|
|
173
|
+
re.compile(r"gcloud\s+sql\s+databases\s+delete\b", re.IGNORECASE),
|
|
174
|
+
re.compile(r"gcloud\s+services\s+disable\b", re.IGNORECASE),
|
|
175
|
+
re.compile(r"gsutil\s+rb\b", re.IGNORECASE),
|
|
176
|
+
re.compile(r"gsutil\s+rm\s+-r\b", re.IGNORECASE),
|
|
177
|
+
],
|
|
178
|
+
|
|
179
|
+
# Kubernetes - Critical cluster operations (irreversible)
|
|
180
|
+
# Word boundaries prevent "cluster" from matching "clusterrole" etc.
|
|
181
|
+
"kubernetes_critical": [
|
|
182
|
+
re.compile(r"kubectl\s+delete\s+namespace\b", re.IGNORECASE),
|
|
183
|
+
re.compile(r"kubectl\s+delete\s+ns\b", re.IGNORECASE),
|
|
184
|
+
re.compile(r"kubectl\s+delete\s+node\b", re.IGNORECASE),
|
|
185
|
+
re.compile(r"kubectl\s+delete\s+cluster\b", re.IGNORECASE),
|
|
186
|
+
re.compile(r"kubectl\s+delete\s+(pv|persistentvolume)\b", re.IGNORECASE),
|
|
187
|
+
re.compile(r"kubectl\s+delete\s+(pvc|persistentvolumeclaim)\b", re.IGNORECASE),
|
|
188
|
+
re.compile(r"kubectl\s+delete\s+(crd|customresourcedefinition)\b", re.IGNORECASE),
|
|
189
|
+
re.compile(r"kubectl\s+delete\s+mutatingwebhookconfiguration\b", re.IGNORECASE),
|
|
190
|
+
re.compile(r"kubectl\s+delete\s+validatingwebhookconfiguration\b", re.IGNORECASE),
|
|
191
|
+
re.compile(r"kubectl\s+drain\b", re.IGNORECASE),
|
|
192
|
+
# Bulk delete with --all flag on any resource type
|
|
193
|
+
re.compile(r"kubectl\s+delete\s+\w+\s+.*--all\b", re.IGNORECASE),
|
|
194
|
+
],
|
|
195
|
+
|
|
196
|
+
# Terraform / Terragrunt - Whole-state destruction
|
|
197
|
+
"terraform_destroy": [
|
|
198
|
+
# terraform destroy WITHOUT -target (bare destroy is destructive)
|
|
199
|
+
# Uses negative lookahead to allow "terraform destroy -target=X" through
|
|
200
|
+
re.compile(r"terraform\s+destroy\b(?!.*-target)", re.IGNORECASE),
|
|
201
|
+
re.compile(r"terragrunt\s+destroy\b(?!.*-target)", re.IGNORECASE),
|
|
202
|
+
re.compile(r"terragrunt\s+run-all\s+destroy\b", re.IGNORECASE),
|
|
203
|
+
re.compile(r"terragrunt\s+destroy-all\b", re.IGNORECASE),
|
|
204
|
+
],
|
|
205
|
+
|
|
206
|
+
# Docker - Bulk prune operations
|
|
207
|
+
"docker_critical": [
|
|
208
|
+
re.compile(r"docker\s+system\s+prune\s+.*(-a|--all)\b", re.IGNORECASE),
|
|
209
|
+
re.compile(r"docker\s+system\s+prune\s+.*--volumes\b", re.IGNORECASE),
|
|
210
|
+
re.compile(r"docker\s+volume\s+prune\b", re.IGNORECASE),
|
|
211
|
+
],
|
|
212
|
+
|
|
213
|
+
# Flux - Uninstall removes ALL Flux components from cluster
|
|
214
|
+
"flux_critical": [
|
|
215
|
+
re.compile(r"flux\s+uninstall\b", re.IGNORECASE),
|
|
216
|
+
],
|
|
217
|
+
|
|
218
|
+
# Git - Force push (history rewrite, not --force-with-lease) and reset --hard
|
|
219
|
+
"git_destructive": [
|
|
220
|
+
re.compile(r"git\s+push\s+.*--force(?!-with-lease)", re.IGNORECASE),
|
|
221
|
+
re.compile(r"git\s+push\s+.*(?<!\w)-f\b", re.IGNORECASE),
|
|
222
|
+
re.compile(r"git\s+reset\s+.*--hard\b", re.IGNORECASE),
|
|
223
|
+
],
|
|
224
|
+
|
|
225
|
+
# GitHub/GitLab - Repo deletion
|
|
226
|
+
"repo_delete": [
|
|
227
|
+
re.compile(r"gh\s+repo\s+delete\b", re.IGNORECASE),
|
|
228
|
+
re.compile(r"glab\s+project\s+delete\b", re.IGNORECASE),
|
|
229
|
+
],
|
|
230
|
+
|
|
231
|
+
# npm - Unpublish entire package (without @version)
|
|
232
|
+
"npm_critical": [
|
|
233
|
+
# Matches "npm unpublish packagename" but NOT "npm unpublish package@1.0.0"
|
|
234
|
+
re.compile(r"npm\s+unpublish\s+(?!.*@)\S+", re.IGNORECASE),
|
|
235
|
+
],
|
|
236
|
+
|
|
237
|
+
# SQL - Drop database/table
|
|
238
|
+
"sql_critical": [
|
|
239
|
+
re.compile(r"drop\s+database\b", re.IGNORECASE),
|
|
240
|
+
re.compile(r"drop\s+table\b", re.IGNORECASE),
|
|
241
|
+
],
|
|
242
|
+
|
|
243
|
+
# Disk operations - Irreversible data destruction
|
|
244
|
+
"disk_operations": [
|
|
245
|
+
re.compile(r"^dd\s+", re.IGNORECASE),
|
|
246
|
+
re.compile(r"^fdisk\s+", re.IGNORECASE),
|
|
247
|
+
re.compile(r"^mkfs(\.(ext[34]|fat|ntfs))?\s+", re.IGNORECASE),
|
|
248
|
+
],
|
|
249
|
+
|
|
250
|
+
# rm -rf / and rm -rf ~ (catastrophic filesystem destruction)
|
|
251
|
+
"rm_critical": [
|
|
252
|
+
re.compile(r"rm\s+.*-[a-z]*r[a-z]*f[a-z]*\s+/\s*$", re.IGNORECASE),
|
|
253
|
+
re.compile(r"rm\s+.*-[a-z]*f[a-z]*r[a-z]*\s+/\s*$", re.IGNORECASE),
|
|
254
|
+
re.compile(r"rm\s+.*-[a-z]*r[a-z]*f[a-z]*\s+/\*", re.IGNORECASE),
|
|
255
|
+
re.compile(r"rm\s+.*-[a-z]*f[a-z]*r[a-z]*\s+/\*", re.IGNORECASE),
|
|
256
|
+
re.compile(r"rm\s+.*-[a-z]*r[a-z]*f[a-z]*\s+~/?", re.IGNORECASE),
|
|
257
|
+
re.compile(r"rm\s+.*-[a-z]*f[a-z]*r[a-z]*\s+~/?", re.IGNORECASE),
|
|
258
|
+
],
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Suggestions for permanently blocked commands
|
|
262
|
+
BLOCKED_COMMAND_SUGGESTIONS = {
|
|
263
|
+
# AWS suggestions
|
|
264
|
+
"aws ec2 delete-vpc": "[BLOCKED] VPC deletion is irreversible - use Terraform/Terragrunt",
|
|
265
|
+
"aws ec2 delete-subnet": "[BLOCKED] Subnet deletion is irreversible - use Terraform/Terragrunt",
|
|
266
|
+
"aws ec2 delete-internet-gateway": "[BLOCKED] Internet gateway deletion is irreversible - use Terraform/Terragrunt",
|
|
267
|
+
"aws ec2 delete-route-table": "[BLOCKED] Route table deletion is irreversible - use Terraform/Terragrunt",
|
|
268
|
+
"aws ec2 delete-route": "[BLOCKED] Route deletion should be done via Terraform/Terragrunt",
|
|
269
|
+
"aws ec2 terminate-instances": "[BLOCKED] Instance termination is irreversible - use Terraform/Terragrunt",
|
|
270
|
+
"aws rds delete-db-instance": "[BLOCKED] Use Terraform/Terragrunt for RDS lifecycle management",
|
|
271
|
+
"aws rds delete-db-cluster": "[BLOCKED] Use Terraform/Terragrunt for RDS cluster management",
|
|
272
|
+
"aws dynamodb delete-table": "[BLOCKED] Table deletion loses all data - use Terraform/Terragrunt",
|
|
273
|
+
"aws s3 rb": "[BLOCKED] Bucket removal is irreversible - use Terraform/Terragrunt",
|
|
274
|
+
"aws s3api delete-bucket": "[BLOCKED] Bucket deletion is irreversible - use Terraform/Terragrunt",
|
|
275
|
+
"aws eks delete-cluster": "[BLOCKED] Use Terraform/Terragrunt for EKS management",
|
|
276
|
+
"aws elasticache delete": "[BLOCKED] Use Terraform/Terragrunt for ElastiCache management",
|
|
277
|
+
"aws kms schedule-key-deletion": "[BLOCKED] KMS key deletion renders all encrypted data permanently unrecoverable",
|
|
278
|
+
"aws organizations delete-organization": "[BLOCKED] Organization deletion is irreversible",
|
|
279
|
+
"aws route53 delete-hosted-zone": "[BLOCKED] DNS zone deletion causes widespread outage",
|
|
280
|
+
|
|
281
|
+
# Azure suggestions
|
|
282
|
+
"az group delete": "[BLOCKED] Resource group deletion destroys all contained resources - use Terraform/Terragrunt",
|
|
283
|
+
"az network vnet delete": "[BLOCKED] VNet deletion is irreversible - use Terraform/Terragrunt",
|
|
284
|
+
"az network vnet subnet delete": "[BLOCKED] Subnet deletion is irreversible - use Terraform/Terragrunt",
|
|
285
|
+
"az network nsg delete": "[BLOCKED] NSG deletion removes all security rules - use Terraform/Terragrunt",
|
|
286
|
+
"az vm delete": "[BLOCKED] VM deletion is irreversible - use Terraform/Terragrunt",
|
|
287
|
+
"az vmss delete": "[BLOCKED] Scale set deletion is irreversible - use Terraform/Terragrunt",
|
|
288
|
+
"az disk delete": "[BLOCKED] Disk deletion loses all data - use Terraform/Terragrunt",
|
|
289
|
+
"az sql server delete": "[BLOCKED] SQL Server deletion destroys all databases - use Terraform/Terragrunt",
|
|
290
|
+
"az sql db delete": "[BLOCKED] Database deletion loses all data - use Terraform/Terragrunt",
|
|
291
|
+
"az cosmosdb delete": "[BLOCKED] CosmosDB deletion is irreversible - use Terraform/Terragrunt",
|
|
292
|
+
"az redis delete": "[BLOCKED] Redis deletion loses all cached data - use Terraform/Terragrunt",
|
|
293
|
+
"az storage account delete": "[BLOCKED] Storage account deletion destroys all data - use Terraform/Terragrunt",
|
|
294
|
+
"az aks delete": "[BLOCKED] AKS cluster deletion is irreversible - use Terraform/Terragrunt",
|
|
295
|
+
"az acr delete": "[BLOCKED] Container registry deletion destroys all images - use Terraform/Terragrunt",
|
|
296
|
+
"az keyvault delete": "[BLOCKED] Key Vault deletion can render encrypted data unrecoverable",
|
|
297
|
+
"az functionapp delete": "[BLOCKED] Function App deletion is irreversible - use Terraform/Terragrunt",
|
|
298
|
+
"az webapp delete": "[BLOCKED] Web App deletion is irreversible - use Terraform/Terragrunt",
|
|
299
|
+
"az ad app delete": "[BLOCKED] App registration deletion breaks all dependent services",
|
|
300
|
+
"az ad sp delete": "[BLOCKED] Service principal deletion breaks authentication for dependent services",
|
|
301
|
+
"az servicebus namespace delete": "[BLOCKED] Service Bus namespace deletion is irreversible - use Terraform/Terragrunt",
|
|
302
|
+
"az eventhubs namespace delete": "[BLOCKED] Event Hubs namespace deletion is irreversible - use Terraform/Terragrunt",
|
|
303
|
+
|
|
304
|
+
# GCP suggestions
|
|
305
|
+
"gcloud projects delete": "[BLOCKED] Project deletion is irreversible - must be done via Cloud Console",
|
|
306
|
+
"gcloud container clusters delete": "[BLOCKED] Use Terraform/Terragrunt for GKE management",
|
|
307
|
+
"gcloud sql instances delete": "[BLOCKED] Use Terraform/Terragrunt for Cloud SQL management",
|
|
308
|
+
"gcloud sql databases delete": "[BLOCKED] Database deletion loses all data - use Terraform/Terragrunt",
|
|
309
|
+
"gcloud services disable": "[BLOCKED] Disabling services can break dependent resources",
|
|
310
|
+
"gsutil rb": "[BLOCKED] Bucket removal is irreversible",
|
|
311
|
+
"gsutil rm -r": "[BLOCKED] Recursive deletion of cloud storage is irreversible",
|
|
312
|
+
|
|
313
|
+
# Kubernetes suggestions
|
|
314
|
+
"kubectl delete namespace": "[BLOCKED] Namespace deletion destroys all resources within it",
|
|
315
|
+
"kubectl delete ns": "[BLOCKED] Namespace deletion destroys all resources within it",
|
|
316
|
+
"kubectl delete node": "[BLOCKED] Node deletion removes the node from the cluster",
|
|
317
|
+
"kubectl delete cluster": "[BLOCKED] Cluster deletion is irreversible and impacts all workloads",
|
|
318
|
+
"kubectl delete pv": "[BLOCKED] Persistent volume deletion loses data",
|
|
319
|
+
"kubectl delete pvc": "[BLOCKED] PVC deletion can trigger PV reclaim and data loss",
|
|
320
|
+
"kubectl delete crd": "[BLOCKED] CRD deletion destroys all custom resources of that type",
|
|
321
|
+
"kubectl delete mutatingwebhookconfiguration": "[BLOCKED] Webhook deletion can break admission control and cluster safety",
|
|
322
|
+
"kubectl delete validatingwebhookconfiguration": "[BLOCKED] Webhook deletion can break admission control and cluster safety",
|
|
323
|
+
"kubectl drain": "[BLOCKED] Node draining can cause service disruption",
|
|
324
|
+
"kubectl delete --all": "[BLOCKED] Bulk deletion of all resources is irreversible",
|
|
325
|
+
|
|
326
|
+
# Terraform / Terragrunt suggestions
|
|
327
|
+
"terraform destroy": "[BLOCKED] terraform destroy without -target destroys entire state - use terraform destroy -target=<resource>",
|
|
328
|
+
"terragrunt destroy": "[BLOCKED] terragrunt destroy without -target destroys entire state",
|
|
329
|
+
"terragrunt run-all destroy": "[BLOCKED] Recursive destruction of all modules",
|
|
330
|
+
"terragrunt destroy-all": "[BLOCKED] Recursive destruction of all modules",
|
|
331
|
+
|
|
332
|
+
# Docker suggestions
|
|
333
|
+
"docker system prune": "[BLOCKED] docker system prune with -a or --volumes removes all unused resources",
|
|
334
|
+
"docker volume prune": "[BLOCKED] docker volume prune removes all unused volumes and data",
|
|
335
|
+
|
|
336
|
+
# Flux suggestions
|
|
337
|
+
"flux uninstall": "[BLOCKED] flux uninstall removes ALL Flux components from the cluster",
|
|
338
|
+
|
|
339
|
+
# Git suggestions
|
|
340
|
+
"git push --force": "[BLOCKED] Force push rewrites history - use git push --force-with-lease",
|
|
341
|
+
"git push -f": "[BLOCKED] Force push rewrites history - use git push --force-with-lease",
|
|
342
|
+
"git reset --hard": "[BLOCKED] git reset --hard permanently discards uncommitted changes",
|
|
343
|
+
|
|
344
|
+
# GitHub/GitLab suggestions
|
|
345
|
+
"gh repo delete": "[BLOCKED] Repository deletion is irreversible - destroys all code and history",
|
|
346
|
+
"glab project delete": "[BLOCKED] Project deletion is irreversible - destroys all code and history",
|
|
347
|
+
|
|
348
|
+
# npm suggestions
|
|
349
|
+
"npm unpublish": "[BLOCKED] npm unpublish without @version removes entire package from registry",
|
|
350
|
+
|
|
351
|
+
# SQL suggestions
|
|
352
|
+
"drop database": "[BLOCKED] DROP DATABASE is irreversible - destroys all data",
|
|
353
|
+
"drop table": "[BLOCKED] DROP TABLE is irreversible - destroys all data",
|
|
354
|
+
|
|
355
|
+
# Disk operations
|
|
356
|
+
"dd": "[BLOCKED] Low-level disk operations can destroy data",
|
|
357
|
+
"fdisk": "[BLOCKED] Disk partitioning can destroy data",
|
|
358
|
+
"mkfs": "[BLOCKED] Filesystem creation destroys all data on target",
|
|
359
|
+
|
|
360
|
+
# rm critical
|
|
361
|
+
"rm -rf /": "[BLOCKED] Filesystem destruction is irreversible",
|
|
362
|
+
"rm -rf ~": "[BLOCKED] Home directory destruction is irreversible",
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# Structured deny signatures complement the raw regex patterns above.
|
|
366
|
+
# They make the deny list resilient to extra flag/value pairs inserted before
|
|
367
|
+
# the real subcommand, e.g.:
|
|
368
|
+
# kubectl --context prod delete namespace
|
|
369
|
+
# aws --profile prod ec2 delete-vpc
|
|
370
|
+
# git -C repo push origin main --force
|
|
371
|
+
SEMANTIC_BLOCKED_RULES = (
|
|
372
|
+
# AWS
|
|
373
|
+
SemanticBlockedRule("aws_critical", ("aws", "ec2", "delete-vpc"), "aws ec2 delete-vpc"),
|
|
374
|
+
SemanticBlockedRule("aws_critical", ("aws", "ec2", "delete-subnet"), "aws ec2 delete-subnet"),
|
|
375
|
+
SemanticBlockedRule("aws_critical", ("aws", "ec2", "delete-internet-gateway"), "aws ec2 delete-internet-gateway"),
|
|
376
|
+
SemanticBlockedRule("aws_critical", ("aws", "ec2", "delete-route-table"), "aws ec2 delete-route-table"),
|
|
377
|
+
SemanticBlockedRule("aws_critical", ("aws", "ec2", "delete-route"), "aws ec2 delete-route"),
|
|
378
|
+
SemanticBlockedRule("aws_critical", ("aws", "ec2", "terminate-instances"), "aws ec2 terminate-instances"),
|
|
379
|
+
SemanticBlockedRule("aws_critical", ("aws", "rds", "delete-db-instance"), "aws rds delete-db-instance"),
|
|
380
|
+
SemanticBlockedRule("aws_critical", ("aws", "rds", "delete-db-cluster"), "aws rds delete-db-cluster"),
|
|
381
|
+
SemanticBlockedRule("aws_critical", ("aws", "dynamodb", "delete-table"), "aws dynamodb delete-table"),
|
|
382
|
+
SemanticBlockedRule("aws_critical", ("aws", "s3", "rb"), "aws s3 rb"),
|
|
383
|
+
SemanticBlockedRule("aws_critical", ("aws", "s3api", "delete-bucket"), "aws s3api delete-bucket"),
|
|
384
|
+
SemanticBlockedRule(
|
|
385
|
+
"aws_critical",
|
|
386
|
+
("aws", "elasticache", "delete-cache-cluster"),
|
|
387
|
+
"aws elasticache delete",
|
|
388
|
+
),
|
|
389
|
+
SemanticBlockedRule(
|
|
390
|
+
"aws_critical",
|
|
391
|
+
("aws", "elasticache", "delete-replication-group"),
|
|
392
|
+
"aws elasticache delete",
|
|
393
|
+
),
|
|
394
|
+
SemanticBlockedRule("aws_critical", ("aws", "eks", "delete-cluster"), "aws eks delete-cluster"),
|
|
395
|
+
SemanticBlockedRule("aws_critical", ("aws", "kms", "schedule-key-deletion"), "aws kms schedule-key-deletion"),
|
|
396
|
+
SemanticBlockedRule("aws_critical", ("aws", "organizations", "delete-organization"), "aws organizations delete-organization"),
|
|
397
|
+
SemanticBlockedRule("aws_critical", ("aws", "route53", "delete-hosted-zone"), "aws route53 delete-hosted-zone"),
|
|
398
|
+
|
|
399
|
+
# Azure
|
|
400
|
+
SemanticBlockedRule("azure_critical", ("az", "group", "delete"), "az group delete"),
|
|
401
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "vnet", "delete"), "az network vnet delete"),
|
|
402
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "vnet", "subnet", "delete"), "az network vnet subnet delete"),
|
|
403
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "nsg", "delete"), "az network nsg delete"),
|
|
404
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "public-ip", "delete"), "az network vnet delete"),
|
|
405
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "application-gateway", "delete"), "az network vnet delete"),
|
|
406
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "lb", "delete"), "az network vnet delete"),
|
|
407
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "dns", "zone", "delete"), "az network vnet delete"),
|
|
408
|
+
SemanticBlockedRule("azure_critical", ("az", "network", "private-dns", "zone", "delete"), "az network vnet delete"),
|
|
409
|
+
SemanticBlockedRule("azure_critical", ("az", "vm", "delete"), "az vm delete"),
|
|
410
|
+
SemanticBlockedRule("azure_critical", ("az", "vmss", "delete"), "az vmss delete"),
|
|
411
|
+
SemanticBlockedRule("azure_critical", ("az", "disk", "delete"), "az disk delete"),
|
|
412
|
+
SemanticBlockedRule("azure_critical", ("az", "snapshot", "delete"), "az disk delete"),
|
|
413
|
+
SemanticBlockedRule("azure_critical", ("az", "image", "delete"), "az disk delete"),
|
|
414
|
+
SemanticBlockedRule("azure_critical", ("az", "sql", "server", "delete"), "az sql server delete"),
|
|
415
|
+
SemanticBlockedRule("azure_critical", ("az", "sql", "db", "delete"), "az sql db delete"),
|
|
416
|
+
SemanticBlockedRule("azure_critical", ("az", "cosmosdb", "delete"), "az cosmosdb delete"),
|
|
417
|
+
SemanticBlockedRule("azure_critical", ("az", "redis", "delete"), "az redis delete"),
|
|
418
|
+
SemanticBlockedRule("azure_critical", ("az", "storage", "account", "delete"), "az storage account delete"),
|
|
419
|
+
SemanticBlockedRule("azure_critical", ("az", "storage", "container", "delete"), "az storage account delete"),
|
|
420
|
+
SemanticBlockedRule("azure_critical", ("az", "storage", "blob", "delete-batch"), "az storage account delete"),
|
|
421
|
+
SemanticBlockedRule("azure_critical", ("az", "aks", "delete"), "az aks delete"),
|
|
422
|
+
SemanticBlockedRule("azure_critical", ("az", "aks", "nodepool", "delete"), "az aks delete"),
|
|
423
|
+
SemanticBlockedRule("azure_critical", ("az", "acr", "delete"), "az acr delete"),
|
|
424
|
+
SemanticBlockedRule("azure_critical", ("az", "keyvault", "delete"), "az keyvault delete"),
|
|
425
|
+
SemanticBlockedRule("azure_critical", ("az", "keyvault", "key", "delete"), "az keyvault delete"),
|
|
426
|
+
SemanticBlockedRule("azure_critical", ("az", "keyvault", "secret", "delete"), "az keyvault delete"),
|
|
427
|
+
SemanticBlockedRule("azure_critical", ("az", "functionapp", "delete"), "az functionapp delete"),
|
|
428
|
+
SemanticBlockedRule("azure_critical", ("az", "webapp", "delete"), "az webapp delete"),
|
|
429
|
+
SemanticBlockedRule("azure_critical", ("az", "ad", "app", "delete"), "az ad app delete"),
|
|
430
|
+
SemanticBlockedRule("azure_critical", ("az", "ad", "sp", "delete"), "az ad sp delete"),
|
|
431
|
+
SemanticBlockedRule("azure_critical", ("az", "servicebus", "namespace", "delete"), "az servicebus namespace delete"),
|
|
432
|
+
SemanticBlockedRule("azure_critical", ("az", "eventhubs", "namespace", "delete"), "az eventhubs namespace delete"),
|
|
433
|
+
|
|
434
|
+
# GCP
|
|
435
|
+
SemanticBlockedRule("gcp_critical", ("gcloud", "projects", "delete"), "gcloud projects delete"),
|
|
436
|
+
SemanticBlockedRule(
|
|
437
|
+
"gcp_critical",
|
|
438
|
+
("gcloud", "container", "clusters", "delete"),
|
|
439
|
+
"gcloud container clusters delete",
|
|
440
|
+
),
|
|
441
|
+
SemanticBlockedRule("gcp_critical", ("gcloud", "sql", "instances", "delete"), "gcloud sql instances delete"),
|
|
442
|
+
SemanticBlockedRule("gcp_critical", ("gcloud", "sql", "databases", "delete"), "gcloud sql databases delete"),
|
|
443
|
+
SemanticBlockedRule("gcp_critical", ("gcloud", "services", "disable"), "gcloud services disable"),
|
|
444
|
+
SemanticBlockedRule("gcp_critical", ("gsutil", "rb"), "gsutil rb"),
|
|
445
|
+
SemanticBlockedRule("gcp_critical", ("gsutil", "rm"), "gsutil rm -r", required_flags=("-r",)),
|
|
446
|
+
|
|
447
|
+
# Kubernetes
|
|
448
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "delete", "namespace"), "kubectl delete namespace"),
|
|
449
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "delete", "ns"), "kubectl delete ns"),
|
|
450
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "delete", "node"), "kubectl delete node"),
|
|
451
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "delete", "cluster"), "kubectl delete cluster"),
|
|
452
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "delete", "pv"), "kubectl delete pv"),
|
|
453
|
+
SemanticBlockedRule(
|
|
454
|
+
"kubernetes_critical",
|
|
455
|
+
("kubectl", "delete", "persistentvolume"),
|
|
456
|
+
"kubectl delete pv",
|
|
457
|
+
),
|
|
458
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "delete", "pvc"), "kubectl delete pvc"),
|
|
459
|
+
SemanticBlockedRule(
|
|
460
|
+
"kubernetes_critical",
|
|
461
|
+
("kubectl", "delete", "persistentvolumeclaim"),
|
|
462
|
+
"kubectl delete pvc",
|
|
463
|
+
),
|
|
464
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "delete", "crd"), "kubectl delete crd"),
|
|
465
|
+
SemanticBlockedRule(
|
|
466
|
+
"kubernetes_critical",
|
|
467
|
+
("kubectl", "delete", "customresourcedefinition"),
|
|
468
|
+
"kubectl delete crd",
|
|
469
|
+
),
|
|
470
|
+
SemanticBlockedRule(
|
|
471
|
+
"kubernetes_critical",
|
|
472
|
+
("kubectl", "delete", "mutatingwebhookconfiguration"),
|
|
473
|
+
"kubectl delete mutatingwebhookconfiguration",
|
|
474
|
+
),
|
|
475
|
+
SemanticBlockedRule(
|
|
476
|
+
"kubernetes_critical",
|
|
477
|
+
("kubectl", "delete", "validatingwebhookconfiguration"),
|
|
478
|
+
"kubectl delete validatingwebhookconfiguration",
|
|
479
|
+
),
|
|
480
|
+
SemanticBlockedRule("kubernetes_critical", ("kubectl", "drain"), "kubectl drain"),
|
|
481
|
+
|
|
482
|
+
# Terraform / Terragrunt (semantic rules use forbidden_flags to allow -target through)
|
|
483
|
+
SemanticBlockedRule(
|
|
484
|
+
"terraform_destroy",
|
|
485
|
+
("terraform", "destroy"),
|
|
486
|
+
"terraform destroy",
|
|
487
|
+
forbidden_flags=("-target", "--target"),
|
|
488
|
+
),
|
|
489
|
+
SemanticBlockedRule(
|
|
490
|
+
"terraform_destroy",
|
|
491
|
+
("terragrunt", "destroy"),
|
|
492
|
+
"terragrunt destroy",
|
|
493
|
+
forbidden_flags=("-target", "--target"),
|
|
494
|
+
),
|
|
495
|
+
SemanticBlockedRule("terraform_destroy", ("terragrunt", "run-all", "destroy"), "terragrunt run-all destroy"),
|
|
496
|
+
SemanticBlockedRule("terraform_destroy", ("terragrunt", "destroy-all"), "terragrunt destroy-all"),
|
|
497
|
+
|
|
498
|
+
# Docker
|
|
499
|
+
SemanticBlockedRule("docker_critical", ("docker", "system", "prune"), "docker system prune", required_flags=("-a",)),
|
|
500
|
+
SemanticBlockedRule("docker_critical", ("docker", "system", "prune"), "docker system prune", required_flags=("--all",)),
|
|
501
|
+
SemanticBlockedRule("docker_critical", ("docker", "system", "prune"), "docker system prune", required_flags=("--volumes",)),
|
|
502
|
+
SemanticBlockedRule("docker_critical", ("docker", "volume", "prune"), "docker volume prune"),
|
|
503
|
+
|
|
504
|
+
# Flux
|
|
505
|
+
SemanticBlockedRule("flux_critical", ("flux", "uninstall"), "flux uninstall"),
|
|
506
|
+
|
|
507
|
+
# Git
|
|
508
|
+
SemanticBlockedRule("git_destructive", ("git", "push"), "git push --force", required_flags=("--force",)),
|
|
509
|
+
SemanticBlockedRule("git_destructive", ("git", "push"), "git push -f", required_flags=("-f",)),
|
|
510
|
+
SemanticBlockedRule("git_destructive", ("git", "reset"), "git reset --hard", required_flags=("--hard",)),
|
|
511
|
+
|
|
512
|
+
# GitHub/GitLab
|
|
513
|
+
SemanticBlockedRule("repo_delete", ("gh", "repo", "delete"), "gh repo delete"),
|
|
514
|
+
SemanticBlockedRule("repo_delete", ("glab", "project", "delete"), "glab project delete"),
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def get_blocked_patterns() -> List[re.Pattern]:
|
|
519
|
+
"""
|
|
520
|
+
Get flat list of all blocked patterns (pre-compiled).
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
List of compiled regex patterns
|
|
524
|
+
"""
|
|
525
|
+
patterns = []
|
|
526
|
+
for category_patterns in BLOCKED_PATTERNS.values():
|
|
527
|
+
patterns.extend(category_patterns)
|
|
528
|
+
return patterns
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def is_blocked_command(command: str) -> BlockedCommandResult:
|
|
533
|
+
"""
|
|
534
|
+
Check if command matches any blocked pattern.
|
|
535
|
+
|
|
536
|
+
Args:
|
|
537
|
+
command: Shell command to check
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
BlockedCommandResult with match details
|
|
541
|
+
"""
|
|
542
|
+
if not command or not command.strip():
|
|
543
|
+
return BlockedCommandResult(is_blocked=False)
|
|
544
|
+
|
|
545
|
+
command = command.strip()
|
|
546
|
+
|
|
547
|
+
semantic_rule = _match_semantic_block_rule(command)
|
|
548
|
+
if semantic_rule is not None:
|
|
549
|
+
suggestion = BLOCKED_COMMAND_SUGGESTIONS.get(semantic_rule.suggestion_key)
|
|
550
|
+
return BlockedCommandResult(
|
|
551
|
+
is_blocked=True,
|
|
552
|
+
pattern_matched=f"semantic:{' '.join(semantic_rule.sequence)}",
|
|
553
|
+
category=semantic_rule.category,
|
|
554
|
+
suggestion=suggestion,
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
for category, patterns in BLOCKED_PATTERNS.items():
|
|
558
|
+
for pattern in patterns:
|
|
559
|
+
if pattern.search(command):
|
|
560
|
+
# Find suggestion for this command
|
|
561
|
+
suggestion = None
|
|
562
|
+
for cmd_prefix, cmd_suggestion in BLOCKED_COMMAND_SUGGESTIONS.items():
|
|
563
|
+
if cmd_prefix in command.lower():
|
|
564
|
+
suggestion = cmd_suggestion
|
|
565
|
+
break
|
|
566
|
+
|
|
567
|
+
return BlockedCommandResult(
|
|
568
|
+
is_blocked=True,
|
|
569
|
+
pattern_matched=pattern.pattern,
|
|
570
|
+
category=category,
|
|
571
|
+
suggestion=suggestion,
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
return BlockedCommandResult(is_blocked=False)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _match_semantic_block_rule(command: str) -> Optional[SemanticBlockedRule]:
|
|
579
|
+
"""Return the first semantic deny rule that matches a command."""
|
|
580
|
+
semantics = analyze_command(command)
|
|
581
|
+
for rule in SEMANTIC_BLOCKED_RULES:
|
|
582
|
+
if rule.matches(semantics):
|
|
583
|
+
return rule
|
|
584
|
+
return None
|