diff --git a/.github/workflows/sanitization.yml b/.github/workflows/sanitization.yml index c10cdc8..434057f 100644 --- a/.github/workflows/sanitization.yml +++ b/.github/workflows/sanitization.yml @@ -24,13 +24,20 @@ jobs: with: python-version: "3.12" - - name: Install semantic-layer dep - run: pip install anthropic + - name: Install deps (semantic layer + self-tests) + run: pip install anthropic pytest - name: Scanner self-tests (deterministic, no key needed) run: python -m pytest tests/test_check_sanitization.py -q - name: Sanitization gate (diff) + # The semantic (LLM) layer runs automatically when ANTHROPIC_API_KEY is set + # as a repo Actions secret — strongly recommended, since it's the layer that + # catches engagement particulars the deterministic regexes can't. Without the + # key it skips with a loud warning (deterministic layer still hard-fails on + # secrets/PII). To make the semantic layer MANDATORY once your key is wired, + # append `--require-semantic` below: the gate then fails closed if the key is + # ever missing instead of silently passing deterministic-only. env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a015f24..c76c183 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,9 +60,14 @@ flag is **not** an automatic rejection — it routes the PR to a human maintaine makes the call. If your PR is flagged and you believe it's clean, say so in the PR description; a maintainer reviews the flagged content and decides. -The semantic layer runs when `ANTHROPIC_API_KEY` is set in CI secrets. Without the -key it skips with a warning (and the `--require-semantic` flag turns that skip into a -hard failure, for maintainers who need the full gate in CI). +The semantic layer needs `ANTHROPIC_API_KEY`. Set it as a repo Actions secret (this is +on the onboarding checklist) so the layer runs in CI — it's the part that catches +engagement particulars the regexes can't. Without the key it skips with a loud warning +(the deterministic layer still hard-fails on secrets/PII), so local/offline runs work. +**Once your key is wired, make the semantic layer mandatory** by appending +`--require-semantic` to the gate step in `.github/workflows/sanitization.yml`: the gate +then fails closed if the key is ever missing, instead of silently passing +deterministic-only. To tune what counts as a particular for this domain, edit `sanitize.config.json` — that is the single configuration surface for the gate. See the inline comments in that diff --git a/sanitize.config.json b/sanitize.config.json index 5ff9790..010ee5d 100644 --- a/sanitize.config.json +++ b/sanitize.config.json @@ -19,6 +19,7 @@ "personal names of subjects, clients, targets, or end-users tied to a specific engagement", "specific organization/company names tied to one active engagement (not generic/public reference)", "document IDs, file names, dataset names, account handles, or URLs specific to one engagement", + "a messaging or account identifier tied to a real person — a Signal/Telegram UUID, a contact phone, an allowlisted user ID (these belong in deployment config/secrets, never in the package)", "a codename or label that identifies one particular engagement (e.g. used as a skill name or heading)", "dates, locations, monetary figures, or case details that only make sense for one engagement" ], diff --git a/scripts/check_sanitization.py b/scripts/check_sanitization.py index 55213fc..ab33baa 100644 --- a/scripts/check_sanitization.py +++ b/scripts/check_sanitization.py @@ -59,6 +59,9 @@ def load_config(root="."): ("private-key-block", re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----")), ("email", re.compile(r"\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b")), ("us-phone", re.compile(r"(?