mirror of
https://github.com/BillyOutlast/posthog.git
synced 2026-02-04 03:01:23 +01:00
Fix XSS in PersonalAPIKey tooltip (#35174)
This commit is contained in:
committed by
GitHub
parent
9a0099f89b
commit
51552c4000
@@ -139,7 +139,7 @@
|
||||
"d3": "^7.9.0",
|
||||
"d3-sankey": "^0.12.3",
|
||||
"dayjs": "1.11.11",
|
||||
"dompurify": "^3.0.6",
|
||||
"dompurify": "^3.2.6",
|
||||
"elkjs": "^0.10.0",
|
||||
"emoji-regex": "^10.4.0",
|
||||
"eventsource-parser": "^3.0.0",
|
||||
|
||||
@@ -2157,3 +2157,10 @@ export function getRelativeNextPath(nextPath: string | null | undefined, locatio
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function escapeHtml(raw: string): string {
|
||||
// renders a string safe to use in dangerouslySetInnerHTML
|
||||
const div = document.createElement('div')
|
||||
div.textContent = raw
|
||||
return div.innerHTML
|
||||
}
|
||||
|
||||
@@ -15,13 +15,14 @@ import {
|
||||
Tooltip,
|
||||
} from '@posthog/lemon-ui'
|
||||
import clsx from 'clsx'
|
||||
import DOMPurify from 'dompurify'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { Form } from 'kea-forms'
|
||||
import { IconErrorOutline } from 'lib/lemon-ui/icons'
|
||||
import { LemonButton } from 'lib/lemon-ui/LemonButton'
|
||||
import { LemonField } from 'lib/lemon-ui/LemonField'
|
||||
import { API_KEY_SCOPE_PRESETS, API_SCOPES, MAX_API_KEYS_PER_USER } from 'lib/scopes'
|
||||
import { capitalizeFirstLetter, humanFriendlyDetailedTime } from 'lib/utils'
|
||||
import { capitalizeFirstLetter, escapeHtml, humanFriendlyDetailedTime } from 'lib/utils'
|
||||
import { Fragment, useEffect } from 'react'
|
||||
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
|
||||
|
||||
@@ -298,13 +299,25 @@ function PersonalAPIKeysTable(): JSX.Element {
|
||||
const orgNames = restrictedOrgs.map((org: any) => org.name)
|
||||
const tooltipMessage =
|
||||
orgNames.length === 1
|
||||
? `Organization <strong>${orgNames[0]}</strong> has restricted the use of personal API keys.`
|
||||
: `Organizations <strong>${orgNames.join(
|
||||
', '
|
||||
? `Organization <strong>${escapeHtml(
|
||||
orgNames[0]
|
||||
)}</strong> has restricted the use of personal API keys.`
|
||||
: `Organizations <strong>${escapeHtml(
|
||||
orgNames.join(', ')
|
||||
)}</strong> have restricted the use of personal API keys.`
|
||||
|
||||
return (
|
||||
<Tooltip title={<span dangerouslySetInnerHTML={{ __html: tooltipMessage }} />}>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(tooltipMessage, {
|
||||
ALLOWED_TAGS: ['strong'],
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LemonTag type="danger">Disabled</LemonTag>
|
||||
</Tooltip>
|
||||
)
|
||||
@@ -319,17 +332,21 @@ function PersonalAPIKeysTable(): JSX.Element {
|
||||
const restrictedOrgNames = restrictedOrgs.map((org: any) => org.name)
|
||||
|
||||
if (restrictedOrgNames.length === 1 && restrictedTeamNames.length === 1) {
|
||||
tooltipMessage = `Organization <strong>${restrictedOrgNames[0]}</strong> has restricted the use of personal API keys. This key will not work for project <strong>${restrictedTeamNames[0]}</strong>.`
|
||||
} else if (restrictedOrgNames.length === 1) {
|
||||
tooltipMessage = `Organization <strong>${
|
||||
tooltipMessage = `Organization <strong>${escapeHtml(
|
||||
restrictedOrgNames[0]
|
||||
}</strong> has restricted the use of personal API keys. This key will not work for projects: <strong>${restrictedTeamNames.join(
|
||||
', '
|
||||
)}</strong> has restricted the use of personal API keys. This key will not work for project <strong>${escapeHtml(
|
||||
restrictedTeamNames[0]
|
||||
)}</strong>.`
|
||||
} else if (restrictedOrgNames.length === 1) {
|
||||
tooltipMessage = `Organization <strong>${escapeHtml(
|
||||
restrictedOrgNames[0]
|
||||
)}</strong> has restricted the use of personal API keys. This key will not work for projects: <strong>${escapeHtml(
|
||||
restrictedTeamNames.join(', ')
|
||||
)}</strong>.`
|
||||
} else {
|
||||
// Multiple organizations affecting projects
|
||||
tooltipMessage = `Multiple organizations have restricted personal API keys. This key will not work for projects: <strong>${restrictedTeamNames.join(
|
||||
', '
|
||||
tooltipMessage = `Multiple organizations have restricted personal API keys. This key will not work for projects: <strong>${escapeHtml(
|
||||
restrictedTeamNames.join(', ')
|
||||
)}</strong>.`
|
||||
}
|
||||
}
|
||||
@@ -338,16 +355,28 @@ function PersonalAPIKeysTable(): JSX.Element {
|
||||
const restrictedOrgNames = restrictedOrgs.map((org: any) => org.name)
|
||||
|
||||
if (restrictedOrgNames.length === 1) {
|
||||
tooltipMessage = `Organization <strong>${restrictedOrgNames[0]}</strong> has restricted the use of personal API keys.`
|
||||
tooltipMessage = `Organization <strong>${escapeHtml(
|
||||
restrictedOrgNames[0]
|
||||
)}</strong> has restricted the use of personal API keys.`
|
||||
} else {
|
||||
tooltipMessage = `Organizations <strong>${restrictedOrgNames.join(
|
||||
', '
|
||||
tooltipMessage = `Organizations <strong>${escapeHtml(
|
||||
restrictedOrgNames.join(', ')
|
||||
)}</strong> have restricted the use of personal API keys.`
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip title={<span dangerouslySetInnerHTML={{ __html: tooltipMessage }} />}>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(tooltipMessage, {
|
||||
ALLOWED_TAGS: ['strong'],
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LemonTag type="warning">Partial restrictions</LemonTag>
|
||||
</Tooltip>
|
||||
)
|
||||
|
||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -764,8 +764,8 @@ importers:
|
||||
specifier: 1.11.11
|
||||
version: 1.11.11(patch_hash=lbfir4woetqmvzqg7l4q5mjtfq)
|
||||
dompurify:
|
||||
specifier: ^3.0.6
|
||||
version: 3.0.6
|
||||
specifier: ^3.2.6
|
||||
version: 3.2.6
|
||||
elkjs:
|
||||
specifier: ^0.10.0
|
||||
version: 0.10.0
|
||||
@@ -7868,6 +7868,9 @@ packages:
|
||||
'@types/trusted-types@2.0.4':
|
||||
resolution: {integrity: sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
'@types/unist@2.0.6':
|
||||
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
|
||||
|
||||
@@ -9929,8 +9932,8 @@ packages:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
dompurify@3.0.6:
|
||||
resolution: {integrity: sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==}
|
||||
dompurify@3.2.6:
|
||||
resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==}
|
||||
|
||||
domutils@1.7.0:
|
||||
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
|
||||
@@ -24815,6 +24818,9 @@ snapshots:
|
||||
|
||||
'@types/trusted-types@2.0.4': {}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
optional: true
|
||||
|
||||
'@types/unist@2.0.6': {}
|
||||
|
||||
'@types/uuid@10.0.0': {}
|
||||
@@ -27413,7 +27419,9 @@ snapshots:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
dompurify@3.0.6: {}
|
||||
dompurify@3.2.6:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
|
||||
domutils@1.7.0:
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user