Deploy & secret guard
Block irreversible deploys and secret-exfiltration commands before they run.
In an infrastructure repo the costly mistakes are a terraform destroy against prod, a deleted namespace, or a secret copied into the transcript where it lands in logs forever. This page composes three primitives to stop all three on PreToolUse. Two block_command regexes catch the known-dangerous invocations; a declarative hook pairs Tool("Bash") with a SecretsExfil custom condition that checks for exfiltration substrings in plain Python. Every block carries a reason the agent reads and acts on.
"""Block irreversible deploys and secret-exfiltration commands before they run."""
from __future__ import annotations
from captain_hook import (
Allow,
Block,
Event,
Input,
LambdaCondition,
Tool,
block_command,
hook,
)
# Block irreversible infrastructure commands before they run.
block_command(
r"terraform\s+destroy",
reason="terraform destroy tears down live infrastructure",
hint="Target a single resource, or run it in a sandbox workspace",
tests={
Input(command="terraform destroy"): Block(),
Input(command="terraform destroy -target=module.cache"): Block(),
Input(command="terraform plan"): Allow(),
},
)
block_command(
r"kubectl\s+delete\s+(namespace|ns)\b",
reason="Deleting a namespace deletes everything inside it",
hint="Delete the specific resource instead of the whole namespace",
tests={
Input(command="kubectl delete namespace prod"): Block(),
Input(command="kubectl delete pod web-123"): Allow(),
},
)
SecretsExfil = LambdaCondition(
lambda evt: any(s in evt.command.raw for s in ("get-secret-value", "AWS_SECRET", "PRIVATE_KEY"))
)
# Block commands that would copy a secret into the transcript or a log.
hook(
Event.PreToolUse,
message="BLOCKED: this prints a secret into the transcript. Read it from your secret store at runtime.",
block=True,
only_if=[Tool("Bash"), SecretsExfil],
tests={
Input(command="aws secretsmanager get-secret-value --secret-id db"): Block(pattern="secret"),
Input(command="env | grep AWS_SECRET_ACCESS_KEY"): Block(pattern="secret"),
Input(command="aws s3 ls"): Allow(),
},
)What it catches
terraform destroy # tears down live infrastructure
kubectl delete namespace prod # deletes everything inside the namespace
aws secretsmanager get-secret-value --secret-id db # prints a secret into the transcript
env | grep AWS_SECRET_ACCESS_KEY # leaks a secret into logsWhat it allows
terraform plan # read-only, no infrastructure change
kubectl delete pod web-123 # scoped delete, not a whole namespace
aws s3 ls # no secret, no destroyThe block / allow split mirrors the hook inline tests, so it stays true as the hook evolves.
Run it yourself
uvx capt-hook --hooks docs/examples test