diff --git a/.changeset/calm-ravens-protect.md b/.changeset/calm-ravens-protect.md deleted file mode 100644 index 7f09e0b03f..0000000000 --- a/.changeset/calm-ravens-protect.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"rrweb": minor -"rrweb-snapshot": minor ---- - -Add an opt-in, versioned privacy policy with strict, balanced, custom, and -legacy presets. Policies consistently protect text, form values, sensitive -attributes, URLs, snapshots, and incremental mutations while preserving the -existing rrweb masking options as the backwards-compatible legacy path. The -vendor-neutral `data-privacy="exclude|mask|allow"` HTML binding works without -recorder-specific configuration. Add fail-closed canvas-region masking for -complex canvas applications, suppress unmasked full-snapshot canvas stills, -and provide coarse and callback-based final attribute masking escape hatches. diff --git a/.changeset/khaki-hoops-smile.md b/.changeset/khaki-hoops-smile.md deleted file mode 100644 index a845151cc8..0000000000 --- a/.changeset/khaki-hoops-smile.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/kind-pumas-detect.md b/.changeset/kind-pumas-detect.md deleted file mode 100644 index 324d54b127..0000000000 --- a/.changeset/kind-pumas-detect.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@rrweb/rrweb-plugin-privacy-detectors": minor -"rrweb": minor -"rrweb-snapshot": minor -"@rrweb/types": minor ---- - -Move Highlight-style heuristic PII auto-detection out of `balanced`/`strict` -defaults and into an opt-in `@rrweb/rrweb-plugin-privacy-detectors` plugin. -Presets still mask form values and honor policy rules; email/phone/card/SSN/IP -text matching is enabled only by the plugin or `applyPrivacyDetectors`. diff --git a/.changeset/loud-lions-protect.md b/.changeset/loud-lions-protect.md deleted file mode 100644 index da76e192f2..0000000000 --- a/.changeset/loud-lions-protect.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"rrweb": patch -"rrweb-snapshot": patch ---- - -Harden privacy detector compilation against nested and high-quantifier ReDoS -patterns, lookaround, and named backreferences. Apply capture policies to CSS -text, `_cssText`, inline style, and stylesheet mutations, and keep custom -detector schema constraints aligned with runtime validation. diff --git a/.changeset/privacy-at-capture-integration.md b/.changeset/privacy-at-capture-integration.md new file mode 100644 index 0000000000..ddc48e5574 --- /dev/null +++ b/.changeset/privacy-at-capture-integration.md @@ -0,0 +1,65 @@ +--- +"rrweb-snapshot": minor +"rrweb": minor +"@rrweb/types": major +"@rrweb/rrweb-plugin-privacy-detectors": minor +"@rrweb/utils": minor +--- + +Privacy at Capture: policies now compile onto rrweb's existing masking +primitives. + +- BREAKING: `@rrweb/rrweb-plugin-privacy-detectors`'s API and the + `RecordPlugin.applyPrivacyPolicy` hook are new, and several masking + decisions that used to have inconsistent defaults are now always enforced + (e.g. protected inputs, mask/unmask ties) -- the default preset introduced + by this change is named `minimal`, and recognizing another session-replay + tool's privacy classes is opt-in via `vendorCompat` rather than a + managed-preset default -- see the guide's migration section for the full + list. It covers the verified mask/block conventions of twenty-five + session-replay tools (each token sourced in the guide's "Vendor class + recognition" table), taking `true` for every vendor or an array of vendor + ids for just the named ones (an unknown id is dropped with a `console.warn` + naming it), and can only ever increase masking or blocking, never reveal: + no foreign tool's unmask or input-ignore convention is ever honored, under + any form of the setting. `needMaskingText`'s fail-closed catch and an explicit + `recordCanvas: true` under `strict` each log a one-time `console.warn` + instead of failing silently. +- Add an opt-in, versioned `privacyPolicy` with `strict`/`balanced`/`minimal` + presets, the vendor-neutral `data-privacy` HTML binding (the severity + ladder `unmask` < `mask` < `ignore` < `block`, nearest annotated ancestor + deciding, with any unrecognized value failing closed to `mask`; `ignore` + masks like `mask` and additionally emits no input events for the subtree + at all, unlike the legacy events-only, per-element `.rr-ignore`), + fail-closed `canvasMasking`, and + attribute-masking escape hatches; existing masking options remain the + `minimal` default. Every masking decision fails closed: a callback that + throws or returns a non-string yields stars, an input whose + `type`/`autocomplete` cannot be read is treated as protected, a block + selector that throws while matching blocks with a one-time `console.warn`, + canvas mask regions scale against the layout content box (immune to CSS + transforms), and configuring `canvasMasking` forces the FPS capture path + on its own. `data-privacy` and the `rr-*` classes are managed-preset + features: under `minimal` a rule compiles to its bare selector and switches + nothing else on. The guide's new "For event consumers" section documents + what changes on the wire for anyone consuming the event stream directly + (nullable attributes, SVG media placeholders, starred text, the + `data-privacy` collision with pre-existing attributes, canvas keyframes + under a `canvasMasking` adapter -- `strict` emits no canvas events at all + -- and canvas-in-shadow-DOM discovery now requiring a native shadow root, + so polyfilled shadow canvases are no longer captured). +- **Experimental:** opt-in heuristic PII detectors (email/phone/card/SSN/IP) + via `@rrweb/rrweb-plugin-privacy-detectors`, which logs a one-time + `console.info` the first time it applies its policy. +- **Experimental, no vendor precedent:** recorded-DOM URL sanitization under + `balanced`/`strict`. **Open design question for upstream:** the Meta + event's own `href` is scoped like `balanced` (blocked-list-only param + masking) even under `strict`, since it is the recording's own address + rather than page-author markup; every DOM URL attribute keeps `strict`'s + normal mask-everything-unless-allowlisted treatment. A relative URL in an + attribute rrweb does not already absolutify (`