Repository: PostHog/posthog
Pull request #30710: fix: improve chrome extension denylist
Author: login: pauldambra
Created: 2025-04-02T16:59:49Z | Merged: 2025-04-03T09:10:02Z

already had someone report this again for a different extension

https://posthoghelp.zendesk.com/agent/tickets/28266

let's extend the denylist

confirmed this works on the previous and new examples locally

also...

* for some reason if you had no events then we wouldn't display doctor items, that confused me
* we would stay in "show matching events only" mode even when there were no events, silly
* we wouldn't let you inspect the DOM when loaded from a file, made me sad
* we didn't report what extensions we were stripping, so let's add them to the doctor

![Screenshot 2025-04-02 at 18 38 37](https://github.com/user-attachments/assets/7a164bda-d1b3-4b31-ab5f-e5670924820f)



Diff:
diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorBottomSettings.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorBottomSettings.tsx
index b6287ef3fdb..7d421fe4fb3 100644
--- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorBottomSettings.tsx
+++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorBottomSettings.tsx
@@ -91,7 +91,7 @@ function ShowOnlyMatching(): JSX.Element {
                     : 'Show only events that match the current filters'
             }
             label="Show only matching events"
-            active={showOnlyMatching}
+            active={hasEventsToDisplay && showOnlyMatching}
             onClick={() => {
                 setShowOnlyMatching(!showOnlyMatching)
             }}
diff --git a/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.test.ts b/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.test.ts
index 05858ffe5ea..e6d6b4ca657 100644
--- a/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.test.ts
+++ b/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.test.ts
@@ -1,3 +1,4 @@
+import { InspectorListItemPerformance } from 'scenes/session-recordings/apm/performanceEventDataLogic'
 import { filterInspectorListItems } from 'scenes/session-recordings/player/inspector/inspectorListFiltering'
 import { SharedListMiniFilter } from 'scenes/session-recordings/player/inspector/miniFiltersLogic'
 import {
@@ -29,6 +30,7 @@ describe('filtering inspector list items', () => {
                 showOnlyMatching: false,
                 allowMatchingEventsFilter: false,
                 trackedWindow: null,
+                hasEventsToDisplay: false,
             })
         ).toHaveLength(0)
     })
@@ -55,6 +57,7 @@ describe('filtering inspector list items', () => {
                 showOnlyMatching: false,
                 allowMatchingEventsFilter: false,
                 trackedWindow: null,
+                hasEventsToDisplay: true,
             }).map((item) => item.type)
         ).toEqual(['browser-visibility', 'offline-status', 'comment', 'events'])
     })
@@ -73,6 +76,7 @@ describe('filtering inspector list items', () => {
             showOnlyMatching: false,
             allowMatchingEventsFilter: false,
             trackedWindow: null,
+            hasEventsToDisplay: true,
         })
         expect(filteredItems).toHaveLength(expectedLength)
     })
@@ -96,6 +100,7 @@ describe('filtering inspector list items', () => {
                 showOnlyMatching: false,
                 allowMatchingEventsFilter: false,
                 trackedWindow: 'a different window',
+                hasEventsToDisplay: true,
             })
         ).toHaveLength(1)
     })
@@ -113,6 +118,7 @@ describe('filtering inspector list items', () => {
                 showOnlyMatching: false,
                 allowMatchingEventsFilter: false,
                 trackedWindow: null,
+                hasEventsToDisplay: true,
             })
         ).toHaveLength(0)
     })
@@ -133,7 +139,35 @@ describe('filtering inspector list items', () => {
                 showOnlyMatching: false,
                 allowMatchingEventsFilter: false,
                 trackedWindow: null,
+                hasEventsToDisplay: true,
             })
         ).toHaveLength(expectedLength)
     })
+
+    it('only shows matching events when show matching events is true', () => {
+        expect(
+            filterInspectorListItems({
+                allItems: [
+                    {
+                        type: FilterableInspectorListItemTypes.EVENTS,
+                        data: { event: '$exception' } as unknown as PerformanceEvent,
+                        highlightColor: 'primary',
+                    } as unknown as InspectorListItemEvent,
+                    {
+                        type: FilterableInspectorListItemTypes.NETWORK,
+                        data: { event: '$pageview' } as unknown as PerformanceEvent,
+                    } as unknown as InspectorListItemPerformance,
+                    {
+                        type: FilterableInspectorListItemTypes.DOCTOR,
+                        data: { event: '$pageview' } as unknown as PerformanceEvent,
+                    } as unknown as InspectorListItemDoctor,
+                ],
+                miniFiltersByKey: { 'events-exceptions': { enabled: true } as unknown as SharedListMiniFilter },
+                showOnlyMatching: true,
+                allowMatchingEventsFilter: true,
+                trackedWindow: null,
+                hasEventsToDisplay: true,
+            })
+        ).toHaveLength(1)
+    })
 })
diff --git a/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.ts b/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.ts
index e7a0859f9c0..e00e1b64491 100644
--- a/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.ts
+++ b/frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.ts
@@ -171,6 +171,7 @@ export function filterInspectorListItems({
     allowMatchingEventsFilter,
     showOnlyMatching,
     trackedWindow,
+    hasEventsToDisplay,
 }: {
     allItems: InspectorListItem[]
     miniFiltersByKey:
@@ -181,6 +182,7 @@ export function filterInspectorListItems({
     allowMatchingEventsFilter: boolean
     showOnlyMatching: boolean
     trackedWindow: string | null
+    hasEventsToDisplay: boolean
 }): InspectorListItem[] {
     const items: InspectorListItem[] = []
 
@@ -201,9 +203,7 @@ export function filterInspectorListItems({
         const itemFilter = itemToMiniFilter(item, miniFiltersByKey)
         include = isContextItem(item) || !!itemFilter?.enabled
 
-        // what about isOfflineStatusChange(item) || isBrowserVisibilityEvent(item) || isComment(item)
-
-        if (allowMatchingEventsFilter && showOnlyMatching) {
+        if (allowMatchingEventsFilter && showOnlyMatching && hasEventsToDisplay) {
             // Special case - overrides the others
             include = include && item.highlightColor === 'primary'
         }
diff --git a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts
index 781a72a383a..dc34e959ffc 100644
--- a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts
+++ b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts
@@ -753,13 +753,21 @@ export const playerInspectorLogic = kea<playerInspectorLogicType>([
         ],
 
         filteredItems: [
-            (s) => [s.allItems, s.miniFiltersByKey, s.showOnlyMatching, s.allowMatchingEventsFilter, s.trackedWindow],
+            (s) => [
+                s.allItems,
+                s.miniFiltersByKey,
+                s.showOnlyMatching,
+                s.allowMatchingEventsFilter,
+                s.trackedWindow,
+                s.hasEventsToDisplay,
+            ],
             (
                 allItems,
                 miniFiltersByKey,
                 showOnlyMatching,
                 allowMatchingEventsFilter,
-                trackedWindow
+                trackedWindow,
+                hasEventsToDisplay
             ): InspectorListItem[] => {
                 const filteredItems = filterInspectorListItems({
                     allItems,
@@ -767,7 +775,9 @@ export const playerInspectorLogic = kea<playerInspectorLogicType>([
                     allowMatchingEventsFilter,
                     showOnlyMatching,
                     trackedWindow,
+                    hasEventsToDisplay,
                 })
+
                 // need to collapse adjacent inactivity items
                 // they look werong next to each other
                 return filteredItems.reduce((acc, item, index) => {
@@ -791,13 +801,15 @@ export const playerInspectorLogic = kea<playerInspectorLogicType>([
                 s.showOnlyMatching,
                 s.allowMatchingEventsFilter,
                 s.trackedWindow,
+                s.hasEventsToDisplay,
             ],
             (
                 allItems,
                 miniFiltersForTypeByKey,
                 showOnlyMatching,
                 allowMatchingEventsFilter,
-                trackedWindow
+                trackedWindow,
+                hasEventsToDisplay
             ): (InspectorListItemEvent | InspectorListItemComment)[] => {
                 const eventFilteredItems = filterInspectorListItems({
                     allItems,
@@ -805,6 +817,7 @@ export const playerInspectorLogic = kea<playerInspectorLogicType>([
                     allowMatchingEventsFilter,
                     showOnlyMatching,
                     trackedWindow,
+                    hasEventsToDisplay,
                 })
 
                 let items: (InspectorListItemEvent | InspectorListItemComment)[] = eventFilteredItems.filter(
diff --git a/frontend/src/scenes/session-recordings/player/player-meta/PlayerMetaBottomSettings.tsx b/frontend/src/scenes/session-recordings/player/player-meta/PlayerMetaBottomSettings.tsx
index 4a793d45f3d..18087f49a9c 100644
--- a/frontend/src/scenes/session-recordings/player/player-meta/PlayerMetaBottomSettings.tsx
+++ b/frontend/src/scenes/session-recordings/player/player-meta/PlayerMetaBottomSettings.tsx
@@ -109,7 +109,7 @@ function InspectDOM(): JSX.Element {
             data-attr="explore-dom"
             onClick={() => openExplorer()}
             disabledReason={
-                sessionPlayerMetaData?.snapshot_source === 'web' ? undefined : 'Only available for web recordings'
+                sessionPlayerMetaData?.snapshot_source === 'mobile' ? 'Only available for web recordings' : undefined
             }
             icon={<IconSearch />}
         />
diff --git a/frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts b/frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
index a0f92a1d4b9..b1797c10ad6 100644
--- a/frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
+++ b/frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
@@ -8,7 +8,12 @@ import { RecordingSnapshot } from '~/types'
 // we have seen some chrome extensions
 // that break playback of session recordings
 // let's try to strip them out
-const CHROME_EXTENSION_DENY_LIST = ['dji-sru']
+const CHROME_EXTENSION_DENY_LIST: Record<string, string> = {
+    'dji-sru': 'snap and read',
+    mloajfnmjckfjbeeofcdaecbelnblden: 'snap and read',
+    aitopia: 'aitopia',
+    becfinhbfclcgokjlobojlnldbfillpf: 'aitopia',
+}
 
 interface IsStrippable {
     textContent: string
@@ -18,7 +23,9 @@ interface IsStrippable {
 
 function safelyCheckCSSAttribute(
     node: serializedNodeWithId,
-    attribute: string
+    attribute: string,
+    needles: string[],
+    matchedExtensions: Set<string>
 ): node is IsStrippable & serializedNodeWithId {
     const hasAttributes = 'attributes' in node && attribute in node.attributes && !!node.attributes[attribute]
     if (!hasAttributes) {
@@ -28,13 +35,23 @@ function safelyCheckCSSAttribute(
     if (typeof attributeValue !== 'string') {
         return false
     }
-    return (
-        attributeValue.includes('chrome-extension://') &&
-        CHROME_EXTENSION_DENY_LIST.some((deny) => attributeValue.includes(deny))
-    )
+
+    if (attributeValue.includes('chrome-extension://')) {
+        for (const needle of needles) {
+            if (attributeValue.includes(needle)) {
+                matchedExtensions.add(CHROME_EXTENSION_DENY_LIST[needle] || needle)
+                return true
+            }
+        }
+    }
+    return false
 }
 
-function safelyCheckClassAttribute(node: serializedNodeWithId): node is IsStrippable & serializedNodeWithId {
+function safelyCheckClassAttribute(
+    node: serializedNodeWithId,
+    needle: string,
+    matchedExtensions: Set<string>
+): node is IsStrippable & serializedNodeWithId {
     const hasAttributes = 'attributes' in node && 'class' in node.attributes && !!node.attributes['class']
     if (!hasAttributes) {
         return false
@@ -43,55 +60,109 @@ function safelyCheckClassAttribute(node: serializedNodeWithId): node is IsStripp
     if (typeof attributeValue !== 'string') {
         return false
     }
-    return attributeValue.includes('dji-sru')
+    if (attributeValue.includes(needle)) {
+        matchedExtensions.add(CHROME_EXTENSION_DENY_LIST[needle] || needle)
+        return true
+    }
+    return false
 }
 
-function safelyCheckTagName(node: serializedNodeWithId): node is IsStrippable & serializedNodeWithId {
+function safelyCheckTagName(
+    node: serializedNodeWithId,
+    needles: string[],
+    matchedExtensions: Set<string>
+): node is IsStrippable & serializedNodeWithId {
     const hasTagName = 'tagName' in node && typeof node.tagName === 'string'
     if (!hasTagName) {
         return false
     }
-    return node.tagName.includes('dji-sru')
+    for (const needle of needles) {
+        if (node.tagName.includes(needle)) {
+            matchedExtensions.add(CHROME_EXTENSION_DENY_LIST[needle] || needle)
+            return true
+        }
+    }
+    return false
 }
 
-function safelyCheckDivNode(node: serializedNodeWithId): node is IsStrippable & serializedNodeWithId {
+function safelyCheckDivNode(
+    node: serializedNodeWithId,
+    needles: string[],
+    matchedExtensions: Set<string>
+): node is IsStrippable & serializedNodeWithId {
     const hasTagName = 'tagName' in node && typeof node.tagName === 'string'
     if (!hasTagName) {
         return false
     }
     if (node.tagName === 'DIV') {
-        return safelyCheckClassAttribute(node)
+        for (const needle of needles) {
+            if (safelyCheckClassAttribute(node, needle, matchedExtensions)) {
+                return true
+            }
+        }
     }
     return false
 }
 
-function stripChromeExtensionDataFromNode(node: serializedNodeWithId): boolean {
+function safelyCheckIDAttribute(
+    node: serializedNodeWithId,
+    needles: string[],
+    matchedExtensions: Set<string>
+): node is IsStrippable & serializedNodeWithId {
+    const hasID = 'attributes' in node && 'id' in node.attributes && !!node.attributes['id']
+    if (!hasID) {
+        return false
+    }
+    const idValue = node.attributes['id']
+    if (typeof idValue !== 'string') {
+        return false
+    }
+    for (const needle of needles) {
+        if (idValue.includes(needle)) {
+            matchedExtensions.add(CHROME_EXTENSION_DENY_LIST[needle] || needle)
+            return true
+        }
+    }
+    return false
+}
+
+function stripChromeExtensionDataFromNode(
+    node: serializedNodeWithId,
+    needles: string[],
+    matchedExtensions: Set<string>
+): boolean {
     let stripped = false
 
-    if (safelyCheckCSSAttribute(node, 'textContent')) {
+    if (safelyCheckCSSAttribute(node, 'textContent', needles, matchedExtensions)) {
         node.textContent = ''
         stripped = true
     }
-    if (safelyCheckCSSAttribute(node, '_cssText')) {
+    if (safelyCheckCSSAttribute(node, '_cssText', needles, matchedExtensions)) {
         node.attributes._cssText = ''
         stripped = true
     }
-    if (safelyCheckClassAttribute(node)) {
-        node.attributes['class'] = node.attributes['class'].replace('dji-sru', '')
+    if (safelyCheckIDAttribute(node, needles, matchedExtensions)) {
+        node.childNodes = []
         stripped = true
     }
-    if (safelyCheckDivNode(node)) {
+    for (const needle of needles) {
+        if (safelyCheckClassAttribute(node, needle, matchedExtensions)) {
+            node.attributes['class'] = node.attributes['class'].replace(needle, '')
+            stripped = true
+        }
+    }
+    if (safelyCheckDivNode(node, needles, matchedExtensions)) {
         node.childNodes = []
         stripped = true
     }
-    if (safelyCheckTagName(node)) {
+    if (safelyCheckTagName(node, needles, matchedExtensions)) {
         node.childNodes = []
         stripped = true
     }
 
     if ('childNodes' in node) {
         for (const childNode of node.childNodes) {
-            if (stripChromeExtensionDataFromNode(childNode)) {
+            if (stripChromeExtensionDataFromNode(childNode, needles, matchedExtensions)) {
                 stripped = true
             }
         }
@@ -107,13 +178,22 @@ export function stripChromeExtensionData(snapshots: RecordingSnapshot[]): Record
     // if we find it, we're going to remove it and all of its children
     // we're going to do this in place and return the modified array
     let strippedChromeExtensionData = false
+    const matchedExtensions = new Set<string>()
 
     for (const snapshot of snapshots) {
         if (snapshot.type !== EventType.FullSnapshot) {
             continue
         }
         const fullSnapshot = snapshot as RecordingSnapshot & fullSnapshotEvent & eventWithTime
-        if (stripChromeExtensionDataFromNode(fullSnapshot.data.node)) {
+        // it's slightly yucky that we rely on the identity of matchedExtensions here to gather matches
+        // but way simpler than trying to narrow types and return a value
+        if (
+            stripChromeExtensionDataFromNode(
+                fullSnapshot.data.node,
+                Object.keys(CHROME_EXTENSION_DENY_LIST),
+                matchedExtensions
+            )
+        ) {
             strippedChromeExtensionData = true
         }
     }
@@ -124,7 +204,9 @@ export function stripChromeExtensionData(snapshots: RecordingSnapshot[]): Record
             type: EventType.Custom,
             data: {
                 tag: 'chrome-extension-stripped',
-                payload: {},
+                payload: {
+                    extensions: Array.from(matchedExtensions),
+                },
             },
             timestamp: snapshots[0].timestamp,
             windowId: snapshots[0].windowId,


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

This PR refines session recording filtering and extends the chrome extension denylist logic. The changes ensure correct toggle behavior and improved consistency when no events are available.

• Updated filtering in /frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.ts by adding the new hasEventsToDisplay parameter.  
• Modified /frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorBottomSettings.tsx to better handle the “Show only matching events” state.  
• Passed hasEventsToDisplay state in /frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts for consistent filtering.  
• Adjusted tests in /frontend/src/scenes/session-recordings/player/inspector/inspectorListFiltering.test.ts.  
• Extended denylist and refactored helper functions in /frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts.

<sub>6 file(s) reviewed, no comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=posthog_30710)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T17:01:40Z
user: login: pauldambra
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T17:58:05Z
user: login: pauldambra
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T17:58:21Z
user: login: pauldambra
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T17:58:41Z
user: login: pauldambra
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T17:59:02Z
user: login: veryayskiy
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T08:35:27Z
user: login: veryayskiy
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T08:35:37Z
user: login: pauldambra
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T08:57:18Z
user: login: pauldambra
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T08:57:21Z
user: login: veryayskiy
body: Cannot think about edge cases though
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T09:09:35Z

Review comments:
diff_hunk: @@ -8,7 +8,13 @@ import { RecordingSnapshot } from '~/types'
 // we have seen some chrome extensions
 // that break playback of session recordings
 // let's try to strip them out
-const CHROME_EXTENSION_DENY_LIST = ['dji-sru']
+const CHROME_EXTENSION_DENY_LIST: Record<string, string> = {
+    'dji-sru': 'snap and read',
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: pauldambra
body: this way we can track what extensions the magic keys represent - and report on them
created_at: 2025-04-02T17:58:05Z
author_association: MEMBER
diff_hunk: @@ -18,7 +24,9 @@ interface IsStrippable {
 
 function safelyCheckCSSAttribute(
     node: serializedNodeWithId,
-    attribute: string
+    attribute: string,
+    needles: string[],
+    matchedExtensions: Set<string>
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: pauldambra
body: started passing this set around to keep track of what we're stripping
created_at: 2025-04-02T17:58:21Z
author_association: MEMBER
diff_hunk: @@ -18,7 +24,9 @@ interface IsStrippable {
 
 function safelyCheckCSSAttribute(
     node: serializedNodeWithId,
-    attribute: string
+    attribute: string,
+    needles: string[],
+    matchedExtensions: Set<string>
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: pauldambra
body: passing a set around because splitting the type narrowing out of these checks made the code pretty ugly
created_at: 2025-04-02T17:58:41Z
author_association: MEMBER
diff_hunk: @@ -8,7 +8,13 @@ import { RecordingSnapshot } from '~/types'
 // we have seen some chrome extensions
 // that break playback of session recordings
 // let's try to strip them out
-const CHROME_EXTENSION_DENY_LIST = ['dji-sru']
+const CHROME_EXTENSION_DENY_LIST: Record<string, string> = {
+    'dji-sru': 'snap and read',
+    mloajfnmjckfjbeeofcdaecbelnblden: 'snap and read',
+    aitopia: 'aitopia',
+    becfinhbfclcgokjlobojlnldbfillpf: 'aitopia',
+    liecbddmkiiihnedobmlmillhodjkdmb: 'loom',
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: pauldambra
body: ```suggestion
```
created_at: 2025-04-02T17:59:02Z
author_association: MEMBER
diff_hunk: @@ -8,7 +8,12 @@ import { RecordingSnapshot } from '~/types'
 // we have seen some chrome extensions
 // that break playback of session recordings
 // let's try to strip them out
-const CHROME_EXTENSION_DENY_LIST = ['dji-sru']
+const CHROME_EXTENSION_DENY_LIST: Record<string, string> = {
+    'dji-sru': 'snap and read',
+    mloajfnmjckfjbeeofcdaecbelnblden: 'snap and read',
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: veryayskiy
body: is it a real name? 😂
created_at: 2025-04-03T08:35:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,7 +8,12 @@ import { RecordingSnapshot } from '~/types'
 // we have seen some chrome extensions
 // that break playback of session recordings
 // let's try to strip them out
-const CHROME_EXTENSION_DENY_LIST = ['dji-sru']
+const CHROME_EXTENSION_DENY_LIST: Record<string, string> = {
+    'dji-sru': 'snap and read',
+    mloajfnmjckfjbeeofcdaecbelnblden: 'snap and read',
+    aitopia: 'aitopia',
+    becfinhbfclcgokjlobojlnldbfillpf: 'aitopia',
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: veryayskiy
body: is it also a real name??
created_at: 2025-04-03T08:35:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,7 +8,12 @@ import { RecordingSnapshot } from '~/types'
 // we have seen some chrome extensions
 // that break playback of session recordings
 // let's try to strip them out
-const CHROME_EXTENSION_DENY_LIST = ['dji-sru']
+const CHROME_EXTENSION_DENY_LIST: Record<string, string> = {
+    'dji-sru': 'snap and read',
+    mloajfnmjckfjbeeofcdaecbelnblden: 'snap and read',
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: pauldambra
body: yep, they're copied out of the recordings
created_at: 2025-04-03T08:57:18Z
author_association: MEMBER
diff_hunk: @@ -8,7 +8,12 @@ import { RecordingSnapshot } from '~/types'
 // we have seen some chrome extensions
 // that break playback of session recordings
 // let's try to strip them out
-const CHROME_EXTENSION_DENY_LIST = ['dji-sru']
+const CHROME_EXTENSION_DENY_LIST: Record<string, string> = {
+    'dji-sru': 'snap and read',
+    mloajfnmjckfjbeeofcdaecbelnblden: 'snap and read',
+    aitopia: 'aitopia',
+    becfinhbfclcgokjlobojlnldbfillpf: 'aitopia',
path: frontend/src/scenes/session-recordings/player/snapshot-processing/chrome-extension-stripping.ts
user: login: pauldambra
body: yep, they're copied out of the recordings
created_at: 2025-04-03T08:57:21Z
author_association: MEMBER

PR comments:
user: login: posthog-bot
created_at: 2025-04-02T17:10:02Z
body: ## 📸 UI snapshots have been updated

**1** snapshot changes in total. **0** added, **1** modified, **0** deleted:

- **`chromium`**: **0** added, **1** modified, **0** deleted ([diff for shard 5](https://github.com/PostHog/posthog/pull/30710/commits/6d56f0db5e7d0a90031e6456bfeaf0ebaca84fb7))
- `webkit`: **0** added, **0** modified, **0** deleted

Triggered by [this commit](https://github.com/PostHog/posthog/pull/30710/commits/7aeb537d53c5ac72d648e72128da7802dce92130).

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/30710/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR
user: login: posthog-bot
created_at: 2025-04-02T17:19:09Z
body: ## 📸 UI snapshots have been updated

**1** snapshot changes in total. **0** added, **1** modified, **0** deleted:

- **`chromium`**: **0** added, **1** modified, **0** deleted ([diff for shard 5](https://github.com/PostHog/posthog/pull/30710/commits/9eebd79e99b405552f05f6066adf5fdf300a8e1d))
- `webkit`: **0** added, **0** modified, **0** deleted

Triggered by [this commit](https://github.com/PostHog/posthog/pull/30710/commits/02bc1d58e660e759d6dde50990d71e309d0f082f).

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/30710/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-02T17:30:09Z
body: **Size Change:** +1 kB (+0.01%) 

**Total Size:** 9.97 MB

| Filename | Size | Change |
| :--- | :---: | :---: |
| `frontend/dist/toolbar.js` | 9.97 MB | +1 kB (+0.01%) |

<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: CONTRIBUTOR