Skip to content

fix(yara): scan only edit replacement text + tighten PII/host rule precision#818

Closed
gewenyu99 wants to merge 1 commit into
mainfrom
posthog-code/yara-precision
Closed

fix(yara): scan only edit replacement text + tighten PII/host rule precision#818
gewenyu99 wants to merge 1 commit into
mainfrom
posthog-code/yara-precision

Conversation

@gewenyu99

Copy link
Copy Markdown
Collaborator

Item D from the 2026-07-07 triage — YARA precision on the pi path (109 pii_in_capture_call blocks across 26 runs; 22 hardcoded_posthog_host blocks across 11 runs).

Repo note: the triage plan filed this under warlock, but the pi fence scans with the wizard-local src/lib/yara-scanner.ts (the regex port, no WASM) — all five remark-reported FP classes trace to this repo. Warlock's posthog_pii_in_capture_call already has the tighter shape; this PR ports that discipline back.

1. pi edit scanning covered the OLD text (pi/security.ts)

preExecutionYaraBlock scanned JSON.stringify(input.edits) — and pi's edit input is [{oldText, newText}], so the scan included pre-existing file content being replaced. Two FP classes from the remarks fall out of that directly:

  • "capture edits adjacent to existing identify() PII" — violation in the anchor context, not the change
  • "pre-existing violations in the repo blocking their own replacement" — the edit that would fix it can't land

Now scans only the joined newText, matching the anthropic path (which scans new_string/content only).

2. pii_in_capture_call tightened to warlock's shape

The local patterns matched a PII word anywhere within 200 chars of .capture( — so event names (capture('email_verified')), substrings of benign keys (emailFieldCount), minimal-payload auth events, and $set payloads all fired. The $set match was self-defeating: PI_RUNTIME_NOTES tells the agent a PII block means "move that field onto the person via identify()/$set" — complying produced another block, which is a chunk of the ~4.2-retries-per-affected-run loop.

Ported warlock's top-level-key-in-first-object patterns (same documented FN tradeoff: top-level PII alongside $set in one call), kept identify() scoped to sensitive PII only, dropped the $set patterns.

3. hardcoded_posthog_host permits fallback position

process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com' is the documented pattern — the env var still wins. A lookbehind skips literals directly preceded by ||, ??, or python or; bare literals (api_host: 'https://us.i.posthog.com') still block.

Tests

Every remark-reported FP is a fixture in the new src/lib/__tests__/yara-scanner.test.ts (benign location, auth events, event names, $set, fallback hosts), plus true-positive regressions and two pi-security tests for the oldText/newText scoping. pnpm build && pnpm test green (1222 tests).

Verify in the field: blocks-per-affected-run on the switchboard YARA tile (target <1.5, currently ~4.2), combined with the repeat-block escalation in #816.

🤖 Generated with Claude Code

…ecision

The pi-path YARA false positives from the 2026-07-07 triage (109 blocks / 26 runs on pii_in_capture_call, 22/11 on hardcoded_posthog_host). The triage plan assumed these were warlock rules, but the pi fence scans with the wizard-local yara-scanner — all five remark-reported FP classes trace here:

1. pi edit scanning covered oldText: security.ts scanned JSON.stringify(edits), which includes the pre-existing file content being replaced. That blocked edits whose surroundings held a violation ("capture edit adjacent to existing identify() PII") and even edits REMOVING one ("pre-existing violations blocking their own replacement"). Now scans only the newText of each edit — same semantics as the anthropic path, which scans new_string only.

2. pii_in_capture_call was far looser than warlock's equivalent: /\.capture\([^)]{0,200}email/i matched the word anywhere in the call — event names (capture('email_verified')), substrings of benign keys, minimal-payload auth events, and $set payloads. The $set match was the worst: our own runtime notes tell the agent to move PII onto the person via identify()/$set, so complying produced another block. Ported warlock's top-level-key-in-first-object patterns (with the same known-FN tradeoff) and dropped the $set patterns.

3. hardcoded_posthog_host now permits host literals in fallback position (process.env.X || '…', ?? '…', python or '…') — the documented pattern where the env var still wins.

Every remark-reported FP is a test fixture in the new yara-scanner.test.ts; true-positive regression cases included.

Generated-By: PostHog Code
Task-Id: bcb96bcb-e37f-421d-841d-738647c73b8e
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🧙 Wizard CI

Run 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:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci basic-integration
  • /wizard-ci error-tracking-upload-source-maps
  • /wizard-ci mcp-analytics
  • /wizard-ci misc
  • /wizard-ci revenue

Test an individual app:

  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
Show more apps
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci error-tracking-upload-source-maps/android
  • /wizard-ci error-tracking-upload-source-maps/cicd-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-nested-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-single-stage-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-gitlab-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-monorepo-pnpm-node-react
  • /wizard-ci error-tracking-upload-source-maps/cicd-monorepo-raw-node-react
  • /wizard-ci error-tracking-upload-source-maps/cicd-ssh-vps-node-raw
  • /wizard-ci error-tracking-upload-source-maps/flutter
  • /wizard-ci error-tracking-upload-source-maps/ios
  • /wizard-ci error-tracking-upload-source-maps/next
  • /wizard-ci error-tracking-upload-source-maps/next-no-posthog
  • /wizard-ci error-tracking-upload-source-maps/node-raw
  • /wizard-ci error-tracking-upload-source-maps/node-rollup
  • /wizard-ci error-tracking-upload-source-maps/node-rollup-typescript-plugin
  • /wizard-ci error-tracking-upload-source-maps/node-webpack
  • /wizard-ci error-tracking-upload-source-maps/nuxt-3-6
  • /wizard-ci error-tracking-upload-source-maps/nuxt-4-3
  • /wizard-ci error-tracking-upload-source-maps/react-native
  • /wizard-ci error-tracking-upload-source-maps/react-vite
  • /wizard-ci error-tracking-upload-source-maps/rust
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci misc/quack-quack
  • /wizard-ci revenue/stripe

Results will be posted here when complete.

@gewenyu99

Copy link
Copy Markdown
Collaborator Author

BAd PR

@gewenyu99 gewenyu99 closed this Jul 7, 2026
gewenyu99 added a commit that referenced this pull request Jul 7, 2026
…(stacked on #804)

The YARA guard split out of #816, rebuilt on top of #804's warlock-based pi fence, plus the one surviving piece of the closed #818:

1. Repeat-block escalation: agents retry identically-blocked payloads (~4.2 blocks per affected run) despite the prompt saying not to. Both fences (pi pre-execution scan, anthropic PreToolUse Bash hook) now hash the payloads they block; an identical retry gets an escalated reason ("this exact content was ALREADY blocked — change the code, not the retry"), and from the third attempt the agent is told to note it in the setup report and move on. The escalation decorates #804's remediation-bearing block message, never replaces it. Tracker lives in yara-hooks, shared by both fences; policy denies are unaffected.

2. Edit scanning scoped to replacement text: #804 still scans JSON.stringify(input.edits), which includes each edit's oldText — pre-existing file content. That blocked edits whose surroundings contained a violation and even edits REMOVING one (field FPs: capture edits adjacent to existing identify() PII; pre-existing violations blocking their own replacement). Now scans only the joined newText, matching the anthropic path which scans new_string only.

Generated-By: PostHog Code
Task-Id: bcb96bcb-e37f-421d-841d-738647c73b8e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant