diff --git a/package-lock.json b/package-lock.json index 9c91664..26a975e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "proofsnap-extension", - "version": "1.1.1", + "version": "1.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "proofsnap-extension", - "version": "1.1.1", + "version": "1.1.4", "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0" @@ -49,7 +49,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -1156,7 +1155,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "dev": true, - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1267,7 +1265,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -1709,7 +1706,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "peer": true, "engines": { "node": ">=12" }, @@ -1769,7 +1765,6 @@ "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2000,7 +1995,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "dev": true, - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/src/share/share.tsx b/src/share/share.tsx index 3bdd33a..7ee9e5e 100644 --- a/src/share/share.tsx +++ b/src/share/share.tsx @@ -10,9 +10,22 @@ async function init() { // Get nid from URL params const params = new URLSearchParams(window.location.search); const nid = params.get('nid'); - + + const root = document.getElementById('root'); + if (!root) return; + if (!nid) { - document.getElementById('root')!.innerHTML = '

No asset to share

'; + const p = document.createElement('p'); + p.textContent = 'No asset to share'; + root.appendChild(p); + return; + } + + // Validate nid to only allow safe characters (alphanumeric, hyphens, underscores) + if (!/^[a-zA-Z0-9_-]+$/.test(nid)) { + const p = document.createElement('p'); + p.textContent = 'Invalid asset ID'; + root.appendChild(p); return; } @@ -22,8 +35,8 @@ async function init() { const shareText = `${settings.huntModeMessage} ${verifyUrl} ${settings.huntModeHashtags}`; const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}`; - // Render UI - document.getElementById('root')!.innerHTML = ` + // Render static UI structure (no user input interpolated into innerHTML) + root.innerHTML = `
🎯

Snap Verified!

@@ -31,10 +44,8 @@ async function init() { Your screenshot is now on the blockchain!
Share it on X to join the AI Hunt event.

- - + +
- + - + @@ -60,25 +71,47 @@ async function init() {
Link copied!
`; - // Event listeners - document.getElementById('shareX')!.addEventListener('click', () => { - window.open(twitterUrl, '_blank'); - }); - - document.getElementById('copyLink')!.addEventListener('click', async () => { - try { - await navigator.clipboard.writeText(verifyUrl); - const toast = document.getElementById('toast')!; - toast.classList.add('show'); - setTimeout(() => toast.classList.remove('show'), 2000); - } catch (err) { - console.error('Failed to copy:', err); - } - }); - - document.getElementById('closeBtn')!.addEventListener('click', () => { - window.close(); - }); + // Inject the verify link using safe DOM APIs to prevent XSS + const verifyLinkContainer = document.getElementById('verifyLinkContainer'); + if (verifyLinkContainer) { + const link = document.createElement('a'); + link.href = verifyUrl; + link.textContent = verifyUrl; + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + verifyLinkContainer.appendChild(link); + } + + // Event listeners with safe element access + const shareXBtn = document.getElementById('shareX'); + if (shareXBtn) { + shareXBtn.addEventListener('click', () => { + window.open(twitterUrl, '_blank', 'noopener,noreferrer'); + }); + } + + const copyLinkBtn = document.getElementById('copyLink'); + if (copyLinkBtn) { + copyLinkBtn.addEventListener('click', async () => { + try { + await navigator.clipboard.writeText(verifyUrl); + const toast = document.getElementById('toast'); + if (toast) { + toast.classList.add('show'); + setTimeout(() => toast.classList.remove('show'), 2000); + } + } catch (err) { + console.error('Failed to copy:', err); + } + }); + } + + const closeBtn = document.getElementById('closeBtn'); + if (closeBtn) { + closeBtn.addEventListener('click', () => { + window.close(); + }); + } } init();