]> git.codecow.com Git - libnemo.git/commitdiff
Clean up CSS and fix XSS vectors.
authorChris Duncan <chris@codecow.com>
Wed, 29 Apr 2026 04:01:36 +0000 (21:01 -0700)
committerChris Duncan <chris@codecow.com>
Wed, 29 Apr 2026 04:01:36 +0000 (21:01 -0700)
src/lib/wallet/sign.ts

index 5bae8bf61a7e84bd4fc7f9360b79f44dad774fe4..30a6f5f18191b36aac254dd679de1bf356bc104d 100644 (file)
@@ -86,26 +86,36 @@ export async function _signBlock (wallet: Wallet, vault: Vault, index: unknown,
 
 async function confirm (id: string, address: string, message: Block | string[]): Promise<string | null> {
        BROWSER: return new Promise((resolve, reject) => {
-               const cssHeading = 'color:black !important;display:block !important;font-family:sans-serif !important;font-size=1rem !important;font-weight:bold !important;margin-top:1rem !important;margin-right:1rem !important;margin-bottom:1rem !important;margin-left:1rem !important;padding-top:0 !important;padding-right:0 !important;padding-bottom:0 !important;padding-left:0 !important;text-align:center !important;visibility:visible !important;'
-               const cssBody = 'color:grey !important;display:block !important;font-family:sans-serif !important;font-size:1rem !important;font-weight:normal !important;margin-top:1rem !important;margin-right:1rem !important;margin-bottom:0 !important;margin-left:1rem !important;padding-top:0 !important;padding-right:0 !important;padding-bottom:0 !important;padding-left:0 !important;visibility:visible !important;'
-               const cssCode = 'color:black !important;display:block !important;font-family:monospace !important;font-size=1rem !important;font-weight:normal !important;margin-top:0 !important;margin-right:1rem !important;margin-bottom:0 !important;margin-left:1rem !important;padding-top:0 !important;padding-right:0 !important;padding-bottom:0 !important;padding-left:0 !important;visibility:visible !important;'
-               const cssButton = 'color:black !important;display:inline-block !important;font-family:sans-serif !important;font-size=1rem !important;font-weight:bold !important;margin-top:1rem !important;margin-right:1rem !important;margin-bottom:1rem !important;margin-left:1rem !important;padding-top:1rem !important;padding-right:1rem !important;padding-bottom:1rem !important;padding-left:1rem !important;text-align:center !important;visibility:visible !important;'
+               const elementId = crypto.randomUUID()
+               const cssContainer = 'background-color:white !important;display:block !important;margin-top:auto !important;margin-right:auto !important;margin-bottom:auto !important;margin-left:auto !important;min-height:100px !important;min-width:100px !important;position:initial !important;opacity:1 !important;padding-top:0 !important;padding-right:0 !important;padding-bottom:0 !important;padding-left:0 !important;visibility:visible !important;'
+               const cssHeading = 'color:black !important;display:block !important;font-family:sans-serif !important;font-size=1rem !important;font-weight:bold !important;margin-top:1rem !important;margin-right:1rem !important;margin-bottom:1rem !important;margin-left:1rem !important;min-height:10px !important;min-width:10px !important;opacity:1 !important;padding-top:0 !important;padding-right:0 !important;padding-bottom:0 !important;padding-left:0 !important;position:initial !important;text-align:center !important;visibility:visible !important;'
+               const cssBody = 'color:grey !important;display:block !important;font-family:sans-serif !important;font-size:1rem !important;font-weight:normal !important;margin-top:1rem !important;margin-right:1rem !important;margin-bottom:0 !important;margin-left:1rem !important;min-height:10px !important;min-width:10px !important;opacity:1 !important;padding-top:0 !important;padding-right:0 !important;padding-bottom:0 !important;padding-left:0 !important;position:initial !important;visibility:visible !important;'
+               const cssCode = 'color:black !important;display:block !important;font-family:monospace !important;font-size=1rem !important;font-weight:normal !important;margin-top:0 !important;margin-right:1rem !important;margin-bottom:0 !important;margin-left:1rem !important;min-height:10px !important;min-width:10px !important;opacity:1 !important;padding-top:0 !important;padding-right:0 !important;padding-bottom:0 !important;padding-left:0 !important;position:initial !important;visibility:visible !important;white-space:pre-wrap !important;word-break:break-all !important;'
+               const cssButton = 'color:black !important;display:inline-block !important;font-family:sans-serif !important;font-size=1rem !important;font-weight:bold !important;margin-top:1rem !important;margin-right:1rem !important;margin-bottom:1rem !important;margin-left:1rem !important;min-height:10px !important;min-width:10px !important;opacity:1 !important;padding-top:1rem !important;padding-right:1rem !important;padding-bottom:1rem !important;padding-left:1rem !important;position:initial !important;text-align:center !important;visibility:visible !important;'
                const dialog = document.createElement('dialog')
+               dialog.style.cssText = cssContainer
                dialog.innerHTML = `
-               <form method="dialog" style="${cssBody}background-color:white !important;">
+               <form method="dialog" style="${cssContainer}">
                        <p style="${cssHeading}">Review Transaction</p>
-                       <hr style="${cssBody}"/>
+                       <hr style="${cssBody}min-height:0 !important;"/>
                        <p style="${cssBody}">Signing account</p>
-                       <pre style="${cssCode}">${address}</pre>
+                       <pre style="${cssCode}" id="address-${elementId}"></pre>
                        <p style="${cssBody}">Message to sign</p>
-                       <pre style="${cssCode}">${JSON.stringify(message, null, '\t')}</pre>
-                       <hr style="${cssBody}"/>
+                       <pre style="${cssCode}" id="message-${elementId}"></pre>
+                       <hr style="${cssBody}min-height:0 !important;"/>
                        <p style="${cssHeading}">Sign transaction?</p>
                        <div style="${cssHeading}">
                                <button value="no" autofocus style="${cssButton}">NO, cancel</button>
                                <button value="yes" style="${cssButton}font-weight:normal !important;">YES, sign</button>
                        </div>
                </form>`
+               const addressElement = dialog.querySelector(`#address-${elementId}`)
+               const messageElement = dialog.querySelector(`#message-${elementId}`)
+               if (addressElement == null || messageElement == null) {
+                       throw new DOMException('Failed to find signature confirmation dialog element')
+               }
+               addressElement.textContent = address
+               messageElement.textContent = JSON.stringify(message, null, 2)
                dialog.addEventListener('close', (ev) => {
                        dialog.remove()
                        if (ev.isTrusted && navigator.userActivation?.isActive) {