diff --git a/client/package-lock.json b/client/package-lock.json
index 62cebb9..2e4ebf8 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -10,6 +10,7 @@
"classnames": "^2.3.2",
"date-fns": "^2.30.0",
"dequal": "^2.0.3",
+ "dompurify": "^3.1.0",
"easymde": "^2.18.0",
"history": "^5.3.0",
"i18next": "^23.7.6",
@@ -7016,6 +7017,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
+ "node_modules/dompurify": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz",
+ "integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA=="
+ },
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
diff --git a/client/package.json b/client/package.json
index 25cdce0..7fb0164 100755
--- a/client/package.json
+++ b/client/package.json
@@ -63,6 +63,7 @@
"classnames": "^2.3.2",
"date-fns": "^2.30.0",
"dequal": "^2.0.3",
+ "dompurify": "^3.1.0",
"easymde": "^2.18.0",
"history": "^5.3.0",
"i18next": "^23.7.6",
diff --git a/client/src/components/CardModal/Tasks/Item.jsx b/client/src/components/CardModal/Tasks/Item.jsx
index a34beb0..ecaa059 100755
--- a/client/src/components/CardModal/Tasks/Item.jsx
+++ b/client/src/components/CardModal/Tasks/Item.jsx
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Draggable } from 'react-beautiful-dnd';
import { Button, Checkbox, Icon } from 'semantic-ui-react';
+import { sanitize } from 'dompurify';
import { usePopup } from '../../../lib/popup';
import NameEdit from './NameEdit';
@@ -15,11 +16,14 @@ const Item = React.memo(
({ id, index, name, isCompleted, isPersisted, canEdit, onUpdate, onDelete }) => {
const nameEdit = useRef(null);
- const handleClick = useCallback(() => {
- if (isPersisted && canEdit) {
- nameEdit.current.open();
- }
- }, [isPersisted, canEdit]);
+ const handleClick = useCallback(
+ (event) => {
+ if (!event.target.closest('a') && isPersisted && canEdit) {
+ nameEdit.current.open();
+ }
+ },
+ [isPersisted, canEdit],
+ );
const handleNameUpdate = useCallback(
(newName) => {
@@ -40,6 +44,16 @@ const Item = React.memo(
nameEdit.current.open();
}, []);
+ const parseLinks = (text) => {
+ const regex = /(http[s]?:\/\/[^\s]+)/g;
+ return sanitize(text).replace(regex, (match) => {
+ const url = new URL(match);
+ return `${
+ url.hostname === window.location.hostname ? url.pathname : match
+ }`;
+ });
+ };
+
const ActionsPopup = usePopup(ActionsStep);
return (
@@ -64,9 +78,11 @@ const Item = React.memo(
className={classNames(styles.text, canEdit && styles.textEditable)}
onClick={handleClick}
>
-
- {name}
-
+
{isPersisted && canEdit && (