diff --git a/package.json b/package.json index 9df6fa263..c79b6e8ea 100644 --- a/package.json +++ b/package.json @@ -235,6 +235,7 @@ "framer-motion": "^12.37.0", "helmet": "^8.1.0", "ioredis": "^5.10.1", + "isomorphic-dompurify": "^3.7.1", "jigsawstack": "^0.4.3", "jotai": "^2.18.1", "jsonwebtoken": "^9.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a044e3e5b..d0f8c6861 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -334,6 +334,9 @@ importers: ioredis: specifier: ^5.10.1 version: 5.10.1 + isomorphic-dompurify: + specifier: ^3.7.1 + version: 3.7.1(@noble/hashes@2.0.1) jigsawstack: specifier: ^0.4.3 version: 0.4.3(encoding@0.1.13) @@ -6414,6 +6417,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/ungap__structured-clone@1.2.0': resolution: {integrity: sha512-ZoaihZNLeZSxESbk9PUAPZOlSpcKx81I1+4emtULDVmBLkYutTcMlCj2K9VNlf9EWODxdO6gkAqEaLorXwZQVA==} @@ -8355,6 +8361,9 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.3.3: + resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -10017,6 +10026,10 @@ packages: resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} engines: {node: '>=18'} + isomorphic-dompurify@3.7.1: + resolution: {integrity: sha512-ChhzwwCm7k8h8ANiq1Vc7geCWeHGaAPusgXU5N4mu7Y2wChgn2JHvbUe6aH/XQOUG3+KV+GmqSq95MntW/V1ng==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + isomorphic-fetch@3.0.0: resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} @@ -10119,6 +10132,15 @@ packages: canvas: optional: true + jsdom@29.0.1: + resolution: {integrity: sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsep@1.4.0: resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} engines: {node: '>= 10.16.0'} @@ -21880,6 +21902,9 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/trusted-types@2.0.7': + optional: true + '@types/ungap__structured-clone@1.2.0': {} '@types/unist@2.0.11': {} @@ -24078,6 +24103,10 @@ snapshots: dependencies: domelementtype: 2.3.0 + dompurify@3.3.3: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -26144,6 +26173,14 @@ snapshots: isexe@3.1.5: optional: true + isomorphic-dompurify@3.7.1(@noble/hashes@2.0.1): + dependencies: + dompurify: 3.3.3 + jsdom: 29.0.1(@noble/hashes@2.0.1) + transitivePeerDependencies: + - '@noble/hashes' + - canvas + isomorphic-fetch@3.0.0(encoding@0.1.13): dependencies: node-fetch: 2.7.0(encoding@0.1.13) @@ -26268,6 +26305,32 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + jsdom@29.0.1(@noble/hashes@2.0.1): + dependencies: + '@asamuzakjp/css-color': 5.0.1 + '@asamuzakjp/dom-selector': 7.0.3 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1) + '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) + css-tree: 3.2.1 + data-urls: 7.0.0(@noble/hashes@2.0.1) + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0(@noble/hashes@2.0.1) + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.7 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.1 + undici: 7.24.4 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1(@noble/hashes@2.0.1) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + jsep@1.4.0: {} jsesc@3.1.0: {} diff --git a/src/components/consent/ResearchConsentForm.tsx b/src/components/consent/ResearchConsentForm.tsx index 55c0bb664..20e8285e3 100644 --- a/src/components/consent/ResearchConsentForm.tsx +++ b/src/components/consent/ResearchConsentForm.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react' import { authClient } from '@/lib/auth-client' import { consentService } from '@/lib/security/consent/ConsentService' +import DOMPurify from 'isomorphic-dompurify' import type { UserConsentStatus } from '@/lib/security/consent/types' interface ResearchConsentFormProps { @@ -279,7 +280,7 @@ export function ResearchConsentForm({