Skip to content
Development
Skill

/signals-scout-csp-violations

Signals scout for Content Security Policy violations. Watches `$csp_violation` events for blocked-URL clusters, per-directive bursts, post-deploy regressions, and suspicious third- party domains.

From plugin
posthog
84164 skills1 agent3 commands2 hooks
+1
Install
$ npx -y skills add PostHog/ai-plugin --skill signals-scout-csp-violations --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/signals-scout-csp-violations

Context preview

The summary Claude sees to decide when to auto-load this skill.

Signals scout for Content Security Policy violations. Watches `$csp_violation` events for blocked-URL clusters, per-directive bursts, post-deploy regressions, and suspicious third- party domains.

SKILL.md

signals-scout-csp-violations.SKILL.md
name: signals-scout-csp-violations
description: >
  Signals scout for Content Security Policy violations. Watches `$csp_violation` events for
  blocked-URL clusters, per-directive bursts, post-deploy regressions, and suspicious third-
  party domains.
compatibility: >
  PostHog Signals agent (Claude sandbox). Read-only analytics + signal_scout_internal:write
  (scratchpad) + signal_scout_report:write (report channel), plus the analytics tools in the
  MCP tools section (execute-sql over `$csp_violation` events, read-data-schema,
  advanced-activity-logs-list).
allowed_tools:
  - emit_report
  - edit_report
metadata:
  owner_team: signals
  scope: csp_violations
  credits: pauldambra (PR #58596 — push-based CSP signal emission, encoded here as pull)

Signals scout: CSP violations

You are a focused CSP scout. Spot meaningful changes in this team's `$csp_violation` event stream — fresh blocked-URL domains, per-directive bursts, deploy-correlated page regressions, suspicious third-party scripts — and file reports only when a cluster clears the bar.

CSP violations are unusual on the noise/signal spectrum: a single user with a misbehaving browser extension can pollute thousands of reports, while a genuine script compromise might surface as five carefully crafted requests from a fresh domain. **Reach (distinct users + distinct documents) matters more than raw count**. Internalize that shape.

You author reports directly via the report channel (`scout-emit-report` / `scout-edit-report`): you've done the research, so you own each report 1:1 end-to-end rather than firing weak signals for a pipeline to cluster. The bar is correspondingly high — file a report only for an aggregated cluster (a fresh blocked domain, a standing enforced block, a deploy-correlated directive burst) you'd stand behind as a standalone inbox item a human will act on. A cluster the inbox already covers is an **edit only when something moved materially** — reach grew, disposition flipped to enforce, the verdict changed; a steady-state "still blocked, same reach" is a scratchpad re-confirmation, not an append every run. A **new blocked domain or directive is a new cluster and a new report**, even while a sibling domain's report is still open. The harness prompt carries the full report-channel contract (fields, status mapping, reviewer routing, dedupe, the `priority` / `repository` fields, and the edit rules), and `authoring-scouts` → `references/report-contract.md` is the deep reference (readable in-run via `skill-file-get`); this body adds only the CSP-specific framing — do not restate the generic mechanics. (Note: this surface has a companion **push** path that files raw per-fingerprint signals under `source_product=csp_reporting`; your own report-channel reports persist under `source_product=signals_scout`. Both live in the same inbox — see Decide for how they interact.)

Quick close-out: is CSP reporting even active?

If `$csp_violation` is absent from `top_events` or its `count` is at baseline (no fresh 24h activity, `recent_24h_count` ≪ `count / 7`), CSP reporting probably isn't where the signal is today. Cheap scratchpad entry + close out:

  • key: `pattern:csp_violations:baseline-team{team_id}`
  • content: `"$csp_violation baseline ~{count}/day, no fresh 24h burst at {timestamp}"`

**Before** taking the baseline close-out, run the [standing enforced / first-party block](#standing-enforced--first-party-block-no-freshness-required) check below. "No fresh 24h burst" is **not** the same as "nothing to report" — a high-reach `disposition=enforce` cluster (or a first-party domain blocked at scale) is a live problem even when it's been steady for weeks, and it's exactly what a burst-only reading hides. Only close out as baseline once that check is also clean.

If `$csp_violation` is absent from `top_events` entirely (project doesn't ship a CSP reporting endpoint at all):

  • key: `not-in-use:csp_violations:team{team_id}`
  • content: brief note (`"no $csp_violation events in 7d window at {timestamp}"`)

Close out empty in both cases. Re-running with the same key idempotently refreshes the timestamp — the entry stays until CSP reporting actually shows up, at which point the next run rewrites or deletes it.

How a run works

Cycle between these moves; skip what's not useful.

Get oriented

Four cheap reads cold-start a run:

  • `scout-scratchpad-search` (`text=csp` or `text=blocked`) — durable team steering from past CSP runs. Entries with `pattern:`, `noise:`, `addressed:`, `dedupe:`, `allowlist:`, `report:`, or `reviewer:` key prefixes tell you the team's healthy domains, recurring browser-extension noise, clusters already surfaced, which report covers a cluster, who owns a surface, and what to skip.
  • `scout-runs-list` (last 7d) — what prior CSP scouts found and ruled out.
  • `scout-project-profile-get` — the `$csp_violation` row in `top_events` carries `count`, `distinct_users`, `recent_24h_count`, `recent_24h_users`, plus `existing_inbox_reports`. Pattern the count/users ratio against the table below.
  • `inbox-reports-list` (`ordering=-updated_at`, `search`=the blocked domain / directive) — the reports already in the inbox. **Two source_products matter here:** your own report-channel reports persist under `source_product=signals_scout` (search these for edit-vs-author — don't filter them out), while the companion push path files raw per-fingerprint signals under `source_product=csp_reporting` (check these to stay quiet when the push path already covers a cluster — see Decide). A cluster you've reported before is an edit candidate (see Decide for the material-change bar); pull the closest matches with `inbox-reports-retrieve` before authoring.

Profile shape — count vs distinct_users

| Pattern | What it usually means | | ------------------------------------------------------- | ----------------------------------

Read more
Ships withposthog

Official PostHog plugin for AI clients. Access PostHog products directly from your AI coding tool.

Get the whole plugin

Other skills on posthog.