All skills
wshobson avatar

/signed-audit-trails-recipe

@8b957cd
by Seth Hobsonwshobson/agents40k stars
4,281

Step-by-step cookbook for setting up cryptographically signed audit trails on Claude Code tool calls. Use when explaining, evaluating, or demonstrating the pattern before committing to the protect-mcp runtime hooks. Covers Cedar policy, Ed25519 receipts, offline verification, tamper detection, CI/CD integration, and SLSA composition.

Use this Skill: https://skilld.dev/gh/wshobson/agents/signed-audit-trails-recipe

This session only. Nothing lands on disk.

referencescedar-policy.md

≈998 tokens on demand. Your agent reads this file only when SKILL.md points to it.

Cedar policy for the signed audit trail recipe

The example ./protect.cedar for Step 2 of the recipe. protect-mcp 0.7 evaluates each tool call against it before the tool runs.

// protect-mcp >= 0.7 evaluates each call as Action::"MCP::Tool::call" on
// Tool::"<name>", with the tool input at context.input.

// Allow all read-oriented tools by default.
permit (principal, action == Action::"MCP::Tool::call", resource) when {
    resource == Tool::"Read" || resource == Tool::"Glob" ||
    resource == Tool::"Grep" || resource == Tool::"WebSearch"
};

// Allow Bash commands from a safe list only (prefix match; git limited to
// read subcommands). Package-manager and build permits (npm, pnpm, yarn,
// make) run arbitrary code, so they are only as safe as the project's scripts.
permit (principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
    context has input && context.input has command &&
    (context.input.command like "git status*" || context.input.command like "git diff*" ||
     context.input.command like "git log*" || context.input.command like "git show*" ||
     context.input.command like "npm*" ||
     context.input.command like "pnpm*" || context.input.command like "yarn*" ||
     context.input.command like "ls*" || context.input.command like "cat*" ||
     context.input.command like "pwd*" || context.input.command like "echo*" ||
     context.input.command like "test*" || context.input.command like "make*")
};

// Explicit deny on destructive commands. Cedar deny is authoritative.
// Substring matching on shell commands is best-effort.
forbid (principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
    context has input && context.input has command &&
    (context.input.command like "*rm -rf*" || context.input.command like "*dd if=*" ||
     context.input.command like "*mkfs*" || context.input.command like "*shred*")
};

// No chaining, `$` expansion, redirection, or file output, so a permitted
// prefix cannot carry a second command, read a secret such as
// `echo "$API_TOKEN"`, or write a file (`git diff --output`). `&` also covers
// `&&` and denies `2>&1`, `$` covers `$(`, and `<` covers input redirects,
// `<(` and `<<`, which suits a strict allow list.
forbid (principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
    context has input && context.input has command &&
    (context.input.command like "*;*" || context.input.command like "*&*" ||
     context.input.command like "*|*" || context.input.command like "*$*" ||
     context.input.command like "*`*" || context.input.command like "*>*" ||
     context.input.command like "*<*" || context.input.command like "*\n*" ||
     context.input.command like "*--output*")
};

// Restrict writes to the project (Claude Code passes absolute paths).
// `like` matches the raw string, so it is not a path containment check:
// the forbid rejects `..` segments.
permit (principal, action == Action::"MCP::Tool::call", resource) when {
    (resource == Tool::"Write" || resource == Tool::"Edit") &&
    context has input && context.input has file_path &&
    context.input.file_path like "/path/to/project/*"
};
forbid (principal, action == Action::"MCP::Tool::call", resource) when {
    (resource == Tool::"Write" || resource == Tool::"Edit") &&
    context has input && context.input has file_path &&
    (context.input.file_path like "*/../*" || context.input.file_path like "*/..")
};

Six rules:

  • Read-oriented tools always allowed
  • Bash allowed for safe command patterns (git status, npm, etc.)
  • Bash rm -rf and similar destructive commands explicitly denied
  • Shell chaining, substitution, redirection, and --output explicitly denied
  • Writes allowed only within the project (its path prefix)
  • Writes through a .. path segment explicitly denied

Cedar forbid rules take precedence over permit rules, so destructive commands cannot be bypassed by a later permissive rule.

Source: SKILL.md on GitHub

No alerts4d3 checks · Risk SAFE
  • Gen Agent Trust Hub4d

    The skill is a comprehensive guide for implementing cryptographically signed audit trails and policy-based governance for AI agent tool calls. It follows security best practices, including input validation and proper secret management, and utilizes well-known package registries for its tooling.

  • Socket4d

    No alerts

  • Snyk4d

    Risk: LOW · No issues

Signed by skilld at 8b957cd. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 5 days ago

README badge

README badge for wshobson/agents/signed-audit-trails-recipe