Merge pull request #29381 from backstage/renovate/npm-dompurify-vulnerability
fix(deps): update dependency dompurify to v3.2.4 [security]
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Updated dependency `dompurify` to `^3.2.4`.
|
||||
@@ -81,7 +81,7 @@
|
||||
"@material-ui/lab": "4.0.0-alpha.61",
|
||||
"@material-ui/styles": "^4.10.0",
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"dompurify": "^3.0.0",
|
||||
"dompurify": "^3.2.4",
|
||||
"git-url-parse": "^15.0.0",
|
||||
"jss": "~10.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { UponSanitizeAttributeHook } from 'dompurify';
|
||||
|
||||
/**
|
||||
* Removes attributes that should only be present on meta tags from other elements.
|
||||
* This ensures that http-equiv and content attributes are only allowed on meta tags
|
||||
* where they are required for the redirect feature.
|
||||
*/
|
||||
export const removeRestrictedAttributes: UponSanitizeAttributeHook = (
|
||||
node,
|
||||
data,
|
||||
) => {
|
||||
if (node.tagName !== 'META') {
|
||||
if (data.attrName === 'http-equiv' || data.attrName === 'content') {
|
||||
node.removeAttribute(data.attrName);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isElement } from '../utils';
|
||||
|
||||
/**
|
||||
* Checks whether a node is iframe or not.
|
||||
* @param node - can be any element.
|
||||
@@ -42,9 +44,10 @@ const isSafe = (node: Element, hosts: string[]) => {
|
||||
* @param node - can be any element.
|
||||
* @param hosts - list of allowed hosts.
|
||||
*/
|
||||
export const removeUnsafeIframes = (hosts: string[]) => (node: Element) => {
|
||||
export const removeUnsafeIframes = (hosts: string[]) => (node: Node) => {
|
||||
if (!isElement(node)) return;
|
||||
|
||||
if (isIframe(node) && !isSafe(node, hosts)) {
|
||||
node.remove();
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
@@ -16,3 +16,5 @@
|
||||
|
||||
export { removeUnsafeLinks } from './links';
|
||||
export { removeUnsafeIframes } from './iframes';
|
||||
export { removeUnsafeMetaTags } from './metatags';
|
||||
export { removeRestrictedAttributes } from './attributes';
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isElement } from '../utils';
|
||||
|
||||
const MKDOCS_CSS = /main\.[A-Fa-f0-9]{8}\.min\.css$/;
|
||||
const GOOGLE_FONTS = /^https:\/\/fonts\.googleapis\.com/;
|
||||
const GSTATIC_FONTS = /^https:\/\/fonts\.gstatic\.com/;
|
||||
@@ -41,11 +43,11 @@ const isSafe = (node: Element) => {
|
||||
/**
|
||||
* Function that removes unsafe link nodes.
|
||||
* @param node - can be any element.
|
||||
* @param hosts - list of allowed hosts.
|
||||
*/
|
||||
export const removeUnsafeLinks = (node: Element) => {
|
||||
export const removeUnsafeLinks = (node: Node) => {
|
||||
if (!isElement(node)) return;
|
||||
|
||||
if (isLink(node) && !isSafe(node)) {
|
||||
node.remove();
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { UponSanitizeElementHook } from 'dompurify';
|
||||
import { isElement } from '../utils';
|
||||
|
||||
/**
|
||||
* Checks if a meta tag is a refresh redirect tag that should be allowed.
|
||||
* These tags are required for the TechDocs redirect feature.
|
||||
*/
|
||||
const isAllowedMetaRefreshTag = (element: Element): boolean => {
|
||||
const httpEquiv = element.getAttribute('http-equiv');
|
||||
const content = element.getAttribute('content');
|
||||
|
||||
return httpEquiv === 'refresh' && content?.includes('url=') === true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes unsafe meta tags from the DOM while preserving allowed refresh redirect tags.
|
||||
* Only meta tags used for page refreshing/redirects are allowed as they are required
|
||||
* for the TechDocs redirect feature.
|
||||
*/
|
||||
export const removeUnsafeMetaTags: UponSanitizeElementHook = (node, data) => {
|
||||
if (!isElement(node)) return;
|
||||
|
||||
if (data.tagName === 'meta' && !isAllowedMetaRefreshTag(node)) {
|
||||
node.parentNode?.removeChild(node);
|
||||
}
|
||||
};
|
||||
@@ -20,7 +20,12 @@ import { useCallback, useMemo } from 'react';
|
||||
import { configApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { Transformer } from '../transformer';
|
||||
import { removeUnsafeIframes, removeUnsafeLinks } from './hooks';
|
||||
import {
|
||||
removeRestrictedAttributes,
|
||||
removeUnsafeIframes,
|
||||
removeUnsafeLinks,
|
||||
removeUnsafeMetaTags,
|
||||
} from './hooks';
|
||||
|
||||
/**
|
||||
* Returns html sanitizer configuration
|
||||
@@ -51,26 +56,9 @@ export const useSanitizerTransformer = (): Transformer => {
|
||||
DOMPurify.addHook('beforeSanitizeElements', removeUnsafeIframes(hosts));
|
||||
}
|
||||
|
||||
// Only allow meta tags if they are used for refreshing the page. They are required for the redirect feature.
|
||||
DOMPurify.addHook('uponSanitizeElement', (currNode, data) => {
|
||||
if (data.tagName === 'meta') {
|
||||
const isMetaRefreshTag =
|
||||
currNode.getAttribute('http-equiv') === 'refresh' &&
|
||||
currNode.getAttribute('content')?.includes('url=');
|
||||
if (!isMetaRefreshTag) {
|
||||
currNode.parentNode?.removeChild(currNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
DOMPurify.addHook('uponSanitizeElement', removeUnsafeMetaTags);
|
||||
|
||||
// Only allow http-equiv and content attributes on meta tags. They are required for the redirect feature.
|
||||
DOMPurify.addHook('uponSanitizeAttribute', (currNode, data) => {
|
||||
if (currNode.tagName !== 'META') {
|
||||
if (data.attrName === 'http-equiv' || data.attrName === 'content') {
|
||||
currNode.removeAttribute(data.attrName);
|
||||
}
|
||||
}
|
||||
});
|
||||
DOMPurify.addHook('uponSanitizeAttribute', removeRestrictedAttributes);
|
||||
|
||||
const tagNameCheck = config?.getOptionalString(
|
||||
'allowedCustomElementTagNameRegExp',
|
||||
@@ -122,7 +110,7 @@ export const useSanitizerTransformer = (): Transformer => {
|
||||
? new RegExp(attributeNameCheck)
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
}) as Element;
|
||||
},
|
||||
[config],
|
||||
);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const isElement = (node: Node): node is Element => {
|
||||
return node.nodeType === Node.ELEMENT_NODE;
|
||||
};
|
||||
@@ -7289,7 +7289,7 @@ __metadata:
|
||||
"@testing-library/user-event": "npm:^14.0.0"
|
||||
"@types/dompurify": "npm:^3.0.0"
|
||||
"@types/react": "npm:^18.0.0"
|
||||
dompurify: "npm:^3.0.0"
|
||||
dompurify: "npm:^3.2.4"
|
||||
git-url-parse: "npm:^15.0.0"
|
||||
jss: "npm:~10.10.0"
|
||||
lodash: "npm:^4.17.21"
|
||||
@@ -21537,10 +21537,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/trusted-types@npm:*":
|
||||
version: 2.0.3
|
||||
resolution: "@types/trusted-types@npm:2.0.3"
|
||||
checksum: 10/4794804bc4a4a173d589841b6d26cf455ff5dc4f3e704e847de7d65d215f2e7043d8757e4741ce3a823af3f08260a8d04a1a6e9c5ec9b20b7b04586956a6b005
|
||||
"@types/trusted-types@npm:*, @types/trusted-types@npm:^2.0.7":
|
||||
version: 2.0.7
|
||||
resolution: "@types/trusted-types@npm:2.0.7"
|
||||
checksum: 10/8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -28453,10 +28453,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dompurify@npm:^3.0.0, dompurify@npm:^3.1.7":
|
||||
version: 3.1.7
|
||||
resolution: "dompurify@npm:3.1.7"
|
||||
checksum: 10/dc637a064306f83cf911caa267ffe1f973552047602020e3b6723c90f67962813edf8a65a0b62e8c9bc13fcd173a2691212a3719bc116226967f46bcd6181277
|
||||
"dompurify@npm:^3.1.7, dompurify@npm:^3.2.4":
|
||||
version: 3.2.6
|
||||
resolution: "dompurify@npm:3.2.6"
|
||||
dependencies:
|
||||
"@types/trusted-types": "npm:^2.0.7"
|
||||
dependenciesMeta:
|
||||
"@types/trusted-types":
|
||||
optional: true
|
||||
checksum: 10/b91631ed0e4d17fae950ef53613cc009ed7e73adc43ac94a41dd52f35483f7538d13caebdafa7626e0da145fc8184e7ac7935f14f25b7e841b32fda777e40447
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user