feat: add pii-bouncer skill#170
Conversation
The instructions for the wizard's PII Bouncer program (PostHog/wizard#510). A docs-only remediation skill that hardens a frontend against PII leaking into session replay and autocapture: - adds the `ph-no-capture` class to sensitive elements - tightens session_recording mask config (maskTextSelector; confirms maskAllInputs is not disabled — it defaults to true) - writes posthog-pii-bouncer-report.md shared_docs inlines the authoritative privacy + js/config docs as references/ so the agent uses real option names, not guesses. Emits the [ABORT] signals the wizard program routes (no-posthog-js / no-init-call / no-frontend-templates). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🧙 Wizard CIRun the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands: Test all apps:
Test all apps in a directory:
Test an individual app:
Show more apps
Results will be posted here when complete. |
|
@joethreepwood this is a perfectly constructed context mill skill 👏 i don't even have to read it. you can tell just by the shape and how you're stitching posthog docs as a reference with your skill instructions |
sarahxsanders
left a comment
There was a problem hiding this comment.
one thing that could potentially be breaking for React projects, then you're good for ✅
| emit `[ABORT] no-posthog-js`. Find the `posthog.init(...)` call; if there | ||
| is none, emit `[ABORT] no-init-call`. Enumerate frontend templates |
There was a problem hiding this comment.
for this sentence: Find the posthog.init(...)call; if there is none, emit[ABORT] no-init-call.
React projects may not have a literal posthog.init(...) call to find, so in this case, it would hit the abort on a valid React project. I'd also have it detect <PostHogProvider> and write masking config into its options={...} prop instead. Same outcome, framework-correct
claude should be able to fix this easily ^^^
There was a problem hiding this comment.
Fixed in fd9c087. Step 1 now looks for either a posthog.init(...) call or a <PostHogProvider>, and only emits [ABORT] no-init-call if neither exists. Step 4 writes the masking config into whichever it found — the session_recording options on init, or the options={{…}} prop on the provider. (Also broadened the wizard's abort copy to match: PostHog/wizard#510.)
| |---|---| | ||
| | `type` | `password`, `email`, `tel` | | ||
| | `autocomplete` | `current-password`, `new-password`, `cc-number`, `cc-csc`, `cc-exp`, `email`, `tel`, `one-time-code`, `ssn` | | ||
| | `name` / `id` | matches `/pass(word)?\|pwd\|card\|cc[-_]?(num\|number)\|cvv\|cvc\|csc\|ssn\|sin\|nin\|pin\|dob\|birth\|tax\|passport\|iban\|account[-_]?(num\|number)/i` | |
There was a problem hiding this comment.
the alternatives in this regex have no word boundaries so it matches anywhere inside another word. for example passenger matches pass, spinner matches pin, etc. could get messy with bury sensitive stuff and add to token waste
if you wrap each alternative in \b...\b it should help:
/\b(pass(word)?|pwd|card|cc[-_]?(num|number)|cvv|cvc|csc|ssn|sin|nin|pin|dob|birth|tax|passport|iban|account[-_]?(num|number))\b/i for example
There was a problem hiding this comment.
Good catch — word-bounded in fd9c087: /\b(pass(word)?|pwd|card|cc[-_]?(num|number)|cvv|cvc|csc|ssn|sin|nin|pin|dob|birth|tax|passport|iban|account[-_]?(num|number))\b/i, with a note that it's bounded so pass doesn't match passenger, pin doesn't match spinner, etc.
Review feedback (PostHog/wizard#510): the [ABORT] signals are a contract with the wizard, but neither side named the other's file. Adds a breadcrumb to the consuming side (src/lib/programs/pii-bouncer/abort-cases.ts); the wizard side now points back here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review feedback (PostHog/wizard#510 skill): - React projects often have no literal posthog.init(...) — config goes through <PostHogProvider options={{…}}>. The skill now detects either, writes masking into whichever it finds, and only aborts (no-init-call) when neither exists. Avoids a false abort on valid React apps. - Word-bound the name/id heuristic so `pass` no longer matches `passenger`, `pin` no longer matches `spinner`, etc. — less over-masking, less noise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks @sarahxsanders — both points addressed in
Also added a reciprocal breadcrumb back to the wizard's |
Summary
The context-mill skill ("cartridge") for the wizard's PII Bouncer program — companion to PostHog/wizard#510. The wizard side is pure engine; this is the packaged-English methodology it loads at runtime.
pii-bounceris a docs-only remediation skill that hardens a frontend against PII leaking into session replay and autocapture:ph-no-captureclass to sensitive elements (the replay-masking mechanism — distinct from thedata-ph-no-captureautocapture attribute)session_recordingmask config: addsmaskTextSelectorfor sensitive text (not masked by default) and confirmsmaskAllInputsisn't disabled (it defaults totrue, so inputs are already masked — the skill doesn't restate that)posthog-pii-bouncer-report.md(elements masked + why, init changes, reviewed-but-skipped, manual follow-ups)Design
type: docs-only, single variantid: all→ skill idpii-bouncer(matches the wizard'sskillId).shared_docspulls insession-replay/privacy.md+libraries/js/config.md, bundled asreferences/so the agent uses the authoritative option names and defaults instead of guessing.[ABORT]signals the wizard routes verbatim:no-posthog-js,no-init-call,no-frontend-templates.This is complementary to the new warlock PII rules: warlock stops PII being sent as event properties; this skill stops PII being recorded in the DOM.
Test plan
pnpm buildproducesdist/skills/pii-bouncer.zipcontainingSKILL.md+references/{privacy,config}.mdpii-bouncerappears inskill-menu.jsonpnpm devserves it athttp://localhost:8765; the wizard (localMcp) resolves + downloads itpii-bounceragainst a workbench app and confirm the masking edits + report🤖 Generated with Claude Code