Respond (RS) · Authorization
Authorization at the client side layer
deterministic · Outside the model's reasoning
How do I stop the bleeding now?
What this cell does
Force-replace local hook config with deny-all, lock with chattr +i, kill running agent.
Artifacts (3)
agent-deny-all-localview on GitHub#!/usr/bin/env bash
# ABOUTME: Local authorization-shrink runbook. Replaces hook + settings with deny-all, sets chattr +i, kills agent.
# ABOUTME: Pre-stage deny-all-hook.sh and settings-deny-all.json under /etc/agents/emergency/ before depending on this runbook.
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: agent-deny-all-local <AGENT_NAME>" >&2
exit 64
fi
AGENT_NAME="$1"
INCIDENT_ID="$(uuidgen 2>/dev/null || python3 -c 'import uuid; print(uuid.uuid4())')"
EMERGENCY_DIR="${EMERGENCY_DIR:-/etc/agents/emergency}"
# Sanity-check pre-staged artifacts. Refuse if missing rather than silently
# leaving a half-applied state.
for src in "$EMERGENCY_DIR/deny-all-hook.sh" "$EMERGENCY_DIR/settings-deny-all.json"; do
if [[ ! -r "$src" ]]; then
echo "REFUSING: pre-staged artifact missing: $src" >&2
echo "Pre-stage emergency artifacts before relying on this runbook." >&2
exit 1
fi
done
# 1. If hook file currently has the immutable bit set (from a prior incident),
# clear it before overwriting.
chattr -i /etc/agents/hooks/pre_tool_use.sh 2>/dev/null || true
# 2. Replace hook config with deny-all template.
cp "$EMERGENCY_DIR/deny-all-hook.sh" /etc/agents/hooks/pre_tool_use.sh
chmod 0755 /etc/agents/hooks/pre_tool_use.sh
chown root:root /etc/agents/hooks/pre_tool_use.sh
# 3. Make the hook config immutable so the agent (and any compromised
# process running as the agent's user) cannot edit it back.
if ! chattr +i /etc/agents/hooks/pre_tool_use.sh 2>/dev/null; then
echo "WARN: chattr not supported on this filesystem; immutability skipped" >&2
fi
# 4. Replace settings.json with the deny-all version.
cp "$EMERGENCY_DIR/settings-deny-all.json" "/etc/agents/$AGENT_NAME/settings.json"
chmod 0644 "/etc/agents/$AGENT_NAME/settings.json"
# 5. Kill any in-flight agent so the new state applies on next launch.
pkill -KILL -f "claude.*$AGENT_NAME" 2>/dev/null || true
logger -t agent-incident -p user.warning \
"$(jq -n \
--arg event "authorization_locked_local" \
--arg agent "$AGENT_NAME" \
--arg incident "$INCIDENT_ID" \
--arg actor "$(whoami)" \
--arg ts "$(date -Iseconds)" \
'{event:$event, agent:$agent, incident:$incident, actor:$actor, ts:$ts}')"
echo "Local authorization locked down for $AGENT_NAME (incident $INCIDENT_ID)"
deny-all-hook.shview on GitHub#!/usr/bin/env bash
# ABOUTME: Pre-staged emergency PreToolUse hook. Always denies. Applied during incident response.
# ABOUTME: Pre-stage at /etc/agents/emergency/deny-all-hook.sh; agent-deny-all-local copies it into place.
# Read stdin to drain the hook input even though we are not using it. Some
# agent runtimes block on stdin not being consumed.
cat >/dev/null
echo "BLOCKED: agent in emergency lockdown" >&2
exit 2
settings-deny-all.jsonview on GitHub{
"_comment": "Emergency lockdown. dontAsk auto-denies everything that would prompt; the empty allow list leaves nothing pre-approved; deny [*] blocks every tool outright in every mode. Deny rules and the PreToolUse hook below both still apply even if someone re-enters a looser mode, which is the point.",
"permissions": {
"defaultMode": "dontAsk",
"allow": [],
"ask": [],
"deny": ["*"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "/etc/agents/hooks/pre_tool_use.sh"
}
]
}
]
}
}
Cell notes
Interventions, Authorization / Client-side
Trigger. Sentinels alert: hook decision spike (multiple denies in short window suggesting probing); hook config tampering detected; auditd watch fires on /etc/agents/hooks/.
Authority. On-call, no second approval.
Speed target. Under 5 seconds.
Tooling
- - The hook layout from
../../../controls/authorization/client-side/(the runbook overwrites it). - -
chattr(Linux ext4/xfs); not all filesystems support it. Verify your/etc/agents/mount.
Files in this directory
- -
agent-deny-all-local, runbook script. Replaces the hook with the deny-all template, setschattr +i, replacessettings.jsonwith the deny-all version, kills any in-flight agent so the new state applies. - -
deny-all-hook.sh, pre-staged hook template. Always returns exit code 2 with a "BLOCKED: agent in emergency lockdown" message. Pre-stage at/etc/agents/emergency/deny-all-hook.sh. - -
settings-deny-all.json, pre-staged Claude Code settings withdefaultMode: deny, empty allow/ask, deny*. Pre-stage at/etc/agents/emergency/settings-deny-all.json.
Verification
# 1. Hook is the deny-all version (md5 must match)
md5sum /etc/agents/hooks/pre_tool_use.sh /etc/agents/emergency/deny-all-hook.sh
# 2. Immutable bit set
lsattr /etc/agents/hooks/pre_tool_use.sh
# expected: 'i' attribute present
# 3. New agent launch is blocked
sudo -u agent-runner /usr/local/bin/claude --print "test" 2>&1 | grep "emergency lockdown"
Common mistakes
- -
chattr +idoes not work on every filesystem (tmpfs, NFS, FAT). Verify your/etc/agents/mount. - - Pre-staged emergency template not in source control means you discover the typo during the incident.
- - Forgetting to kill the running agent, the new hook applies only to new sessions.
Citation
NIST CSF 2.0 RS.MI-01, RS.MI-02. NIST AI RMF MANAGE 4.1. OWASP ASI02, ASI05.
Primary failure modes
Documented, not hypothetical. A control whose bypass is undocumented is worse than no control, because somebody trusted it.
- chattr +i not supported on tmpfs/NFS/FAT
- in-flight agent uses old hook until killed
Crosswalk
| NIST CSF 2 0 | RS.MI-01, RS.MI-02 |
|---|---|
| NIST AI RMF | MANAGE 4.1 |
| OWASP AGENTIC | ASI02, ASI05 |
Cite this cell:
https://agenticcovenants.com/respond/authorization/client-side/