# SSN guard

Stops any AI agent working in this folder from writing a full Social Security
number into a file or a command.

## What it blocks

A full number: three digits, then two digits, then four digits, with a dash, a
space, or nothing between the groups. The match needs a non-digit (or the start
or end of the text) on each side, so the nine digits must stand on their own.

Pattern: `(^|[^0-9])[0-9]{3}[- ]?[0-9]{2}[- ]?[0-9]{4}([^0-9]|$)`

When it blocks, the message is:
"Blocked: a full Social Security number. Use only the last four digits."

## What it allows

- The last four digits on their own.
- Shorter numbers such as the phone number 555-0142.
- Digit runs inside the Claude Code fields `session_id`, `transcript_path`,
  `cwd`, and `tool_use_id`. The script blanks those four values with `sed`
  before matching (jq is not required). Everything else, including the tool
  input, is checked.
- Digit runs longer than nine digits, such as a ten digit phone number with no
  separators, because the match needs a non-digit on each side.

## Which file each tool reads

| Tool | File it reads | Runs |
|---|---|---|
| Claude Code | `.claude/settings.json` | PreToolUse hook, matcher `Write\|Edit\|MultiEdit\|Bash`, command `bash "$CLAUDE_PROJECT_DIR/guard/ssn-check.sh"`. Exit 2 blocks the call and passes the message back to the agent. |
| OMP | `.omp/hooks/pre/ssn-guard.ts` | Default export `(pi)` that calls `pi.on("tool_call", ...)` and returns `{ block: true, reason }` when `JSON.stringify(event.input)` matches the same pattern. Imports nothing. |
| Both | `guard/ssn-check.sh` | The Claude Code hook calls it; you can also pipe text into it by hand. |

## Tests

Each line was piped into `guard/ssn-check.sh` on its own. The test numbers
come from the never issued block with area 987, group 65, and serials 4320 to
4329. They are described here by their parts, so this file holds no full
number.

| # | Input | Expected | Real exit status |
|---|---|---|---|
| 1 | Test number with dashes (987, 65, 4321 joined by dashes) | Blocked | 2 |
| 2 | Test number with spaces (987, 65, 4322 joined by spaces) | Blocked | 2 |
| 3 | Nine digits run together (987, 65, 4323 with nothing between) | Blocked | 2 |
| 4 | Last four digits only: 4324 | Allowed | 0 |
| 5 | Phone number: 555-0142 | Allowed | 0 |

Extra checks run at the same time:

- Claude Code style JSON with nine digit runs in all four id and path fields
  and only 4325 in the tool input: exit 0.
- Claude Code style JSON with a dashed test number in `tool_input.content`:
  exit 2.
- The OMP hook, loaded with a stub `pi`, returned `{ block: true, reason }` for
  tests 1 to 3 and nothing for tests 4 and 5.
- The live Claude Code hook blocked two of my own tool calls during setup (a
  Bash command and a first draft of this README) because each held a full
  number, which confirms it is active.
