Agentic Covenants

Detect (DE) · Authorization

Authorization at the client side layer

deterministic · Outside the model's reasoning

If this concern is breached, how do we know?

What this cell does

Hook decision events (allow/ask/deny/error); auditd watches hook config edits and --no-verify; SIEM rule for multi-deny patterns.

Artifacts (3)

auditd-auth.rulesview on GitHub
# ABOUTME: auditd rules for authorization-layer events: hook config edits, allowlist edits, Claude settings edits, --no-verify execve.
# ABOUTME: Drop in /etc/audit/rules.d/ and reload with `augenrules --load`. Confirm with `auditctl -l`.

# Hook directory and individual scripts.
-w /etc/agents/hooks/ -p wa -k hook_config_edit

# MCP allowlist.
-w /etc/agents/mcp-allowlist.json -p wa -k allowlist_edit

# Claude Code per-user settings (if the agent runs as agent-runner). Adjust
# path for other agent users or for project-local .claude/settings.json by
# adding additional -w lines.
-w /home/agent-runner/.claude/settings.json -p wa -k claude_settings_edit
-w /etc/agents/claude-code-prod/config.json -p wa -k claude_settings_edit

# Tier config for the tiered approval hook.
-w /etc/agents/tier-config.yaml -p wa -k tier_config_edit

# --no-verify in any execve. Both long form and short form.
-a always,exit -F arch=b64 -S execve -F a1=*--no-verify* -k git_no_verify
-a always,exit -F arch=b64 -S execve -F a1=*-n* -F exe=/usr/bin/git -k git_no_verify_short

# core.hooksPath manipulation (the documented bypass for pre-commit hooks).
-a always,exit -F arch=b64 -S execve -F a1=*core.hooksPath* -k git_hooks_path_bypass
hook-decision-emit.shview on GitHub
#!/usr/bin/env bash
# ABOUTME: Append-only snippet for the deny-then-ask-then-allow hook. Emits a structured decision event before exit.
# ABOUTME: Source this from pre_tool_use.sh after the decision is reached but before the script exits.

emit_decision() {
  # Args:
  #   $1 = decision (allow|ask|deny|error)
  #   $2 = pattern (the rule that matched, or "default" for no-match)
  local decision="$1"
  local pattern="${2:-default}"

  # SESSION_ID, TOOL_NAME, TOOL_INPUT are set by the calling hook.
  local event
  event="$(jq -n \
    --arg session "${SESSION_ID:-unknown}" \
    --arg tool "${TOOL_NAME:-unknown}" \
    --arg input "${TOOL_INPUT:-}" \
    --arg decision "$decision" \
    --arg pattern "$pattern" \
    --arg ts "$(date -Iseconds)" \
    '{event: "hook_decision", session: $session, tool: $tool, input: $input, decision: $decision, pattern: $pattern, ts: $ts}')"

  logger -t agent-sentinel -p user.info "$event"
}

# Usage in your pre_tool_use.sh:
#
#   . /etc/agents/hooks/hook-decision-emit.sh
#
#   for pattern in "${DENY_PATTERNS[@]}"; do
#     if echo "$TOOL_INPUT" | grep -qE "$pattern"; then
#       emit_decision deny "$pattern"
#       exit 2
#     fi
#   done
#
#   emit_decision allow "default"
#   exit 0
sigma-multi-deny.yamlview on GitHub
# ABOUTME: Sigma rule that fires when a single agent session accumulates >5 deny events from the PreToolUse hook.
# ABOUTME: Probing pattern indicator. Tune the threshold to your environment's normal deny rate before alerting.
title: Agent session with multi-deny pattern (probing indicator)
id: 4d2c1e8a-9b7f-4c5d-8e6f-2a1b3c4d5e6f
status: experimental
description: >
  Detects an agent session that hits more than five deny events from the
  PreToolUse hook within a 10-minute window. A correctly behaving agent
  encounters denies occasionally; an agent probing for a bypass triggers
  many denies in a row. Tune the threshold and window for your environment.
references:
  - https://github.com/peopleforrester/agentic-covenants/blob/main/SENTINELS_MATRIX.md
author: agentic-covenants
date: 2026/05/08
logsource:
  category: agent_hook
detection:
  selection:
    event: hook_decision
    decision: deny
  timeframe: 10m
  condition: selection | count(session) > 5
falsepositives:
  - Operator iterating on a denylist pattern (mute the alert manually).
  - First-run misconfiguration where the deny list is too aggressive (treat as a tuning indicator).
level: medium
tags:
  - agent
  - authorization
  - sentinels

Cell notes

Sentinels, Authorization / Client-side

Control. Hook decision events (allow/ask/deny/error) emitted as structured JSON. Auditd watches for hook config edits and --no-verify. SIEM rule for multi-deny patterns in a single session.

Strength. Deterministic when shipped. Failure modes: decision events not correlated with tool input (counts denies, doesn't reconstruct intent); auditd rule format errors silently disable the rule (verify with auditctl -l); --no-verify rule misses git commit -n short form.

Tooling

Files in this directory

Verification


# 1. Hook decision logged
echo '{"session_id":"test","tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
  | /etc/agents/hooks/pre_tool_use.sh
journalctl -t agent-sentinel --since "1 minute ago" | grep hook_decision
# expected: event with decision: deny

# 2. Auditd catches hook config edit
sudo touch /etc/agents/hooks/test
ausearch -k hook_config_edit --start recent
# expected: write event

# 3. Auditd catches --no-verify
git commit --no-verify -m "test" 2>/dev/null || true
ausearch -k git_no_verify --start recent
# expected: execve with --no-verify in args

Common mistakes

  • - Decision events not correlated with the underlying tool input (useful for counting denies, not for forensics).
  • - auditctl -l not run after rule install, typo'd rules silently fail.
  • - --no-verify rule misses git commit -n. Add both forms.

Citation

NIST CSF 2.0 DE.CM-01, DE.CM-03, DE.CM-09.

Primary failure modes

Documented, not hypothetical. A control whose bypass is undocumented is worse than no control, because somebody trusted it.

  • decision events not correlated with tool input
  • auditd rule format errors silently disable the rule
  • --no-verify rule misses git commit -n short form

Crosswalk

NIST CSF 2 0DE.CM-01, DE.CM-03, DE.CM-09