this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add domain validation / length checks

+62 -28
+62 -28
content.js
··· 5 5 typeof browser !== "undefined" ? browser.storage.local : chrome.storage.local 6 6 7 7 // Regular expression to validate the DID format 8 + // Make sure that we don't DoS the regex if someone supplies too large of a DID. 9 + const MAX_DID_LENGTH = 1024 8 10 const didRegex = 9 11 /^did:plc:([a-zA-Z0-9._-]+(:[a-zA-Z0-9._-]+)*|((%[0-9A-Fa-f]{2})|[a-zA-Z0-9._-])+(:((%[0-9A-Fa-f]{2})|[a-zA-Z0-9._-])+)*$)/ 10 12 11 13 // Function to validate the DID string 12 14 function isValidDID(didString) { 13 - return didRegex.test(didString) 15 + return didString.length <= MAX_DID_LENGTH && didRegex.test(didString) 14 16 } 15 17 16 18 // Function to get the domain name from the current hostname 17 19 function getDomainName() { 18 20 const hostname = window.location.hostname 19 21 return hostname.replace(/^www\./, "") 22 + } 23 + 24 + // Function to validate the domain name 25 + function isValidDomain(domain) { 26 + const MAX_DOMAIN_LENGTH = 255 27 + 28 + if (domain.length > MAX_DOMAIN_LENGTH) { 29 + return false 30 + } 31 + 32 + try { 33 + // Use the build in URL constructor to validate the URL, if doesn't throw an error, the domain is valid 34 + // This is a better choice than a regex since it should properly support punycode/international domains 35 + new URL(`https://${domain}`) 36 + return true 37 + } catch (error) { 38 + // The URL constructor threw an error, so the domain is not valid 39 + return false 40 + } 20 41 } 21 42 22 43 // Function to check for a DID in the domain's TXT records 23 44 async function checkForDIDDNS(domain) { 24 - // We use Google's DNS over HTTPS API to resolve the TXT record 25 - const response = await fetch( 26 - `https://dns.google/resolve?name=_atproto.${domain}&type=TXT` 27 - ) 28 - const data = await response.json() 45 + try { 46 + const response = await fetch( 47 + `https://dns.google/resolve?name=_atproto.${domain}&type=TXT` 48 + ) 49 + const data = await response.json() 29 50 30 - // We use the TXT record type to avoid CORS issues 31 - const records = data?.Answer?.filter((record) => record.type === 16) || [] 51 + // We use the TXT record type to avoid CORS issues 52 + const records = data?.Answer?.filter((record) => record.type === 16) || [] 32 53 33 - // We filter out all records that are not TXT records 34 - const didRecord = records.find((record) => 35 - record.data.includes("did=did:plc:") 36 - ) 54 + // We filter out all records that are not TXT records 55 + const didRecord = records.find((record) => 56 + record.data.includes("did=did:plc:") 57 + ) 37 58 38 - // We return the DID if we found one and it's valid 39 - return didRecord && isValidDID(didRecord.data.replace("did=", "")) 40 - ? didRecord.data.replace("did=", "") 41 - : null 59 + // We return the DID if we found one and it's valid 60 + return didRecord && isValidDID(didRecord.data.replace("did=", "")) 61 + ? didRecord.data.replace("did=", "") 62 + : null 63 + } catch (error) { 64 + return null 65 + } 42 66 } 43 67 44 68 // Function to check for a DID in the well-known (not .well-known) location ··· 47 71 const response = await fetch( 48 72 `https://${domain}/xrpc/com.atproto.identity.resolveHandle` 49 73 ) 74 + 75 + if (!response.headers.get("Content-Type")?.includes("application/json")) { 76 + throw new Error("Invalid Content-Type") 77 + } 78 + 50 79 const data = await response.json() 51 80 return data.did && isValidDID(data.did) ? data.did : null 52 81 } catch (error) { ··· 61 90 // We check for a DID on the current domain 62 91 ;(async function () { 63 92 const domain = getDomainName() 64 - const domainDID = await checkForDIDDNS(domain) 65 - const httpsDID = await checkForDIDHTTPS(domain) 93 + if (isValidDomain(domain)) { 94 + const domainDID = await checkForDIDDNS(domain) 95 + const httpsDID = await checkForDIDHTTPS(domain) 66 96 67 - if (domainDID) { 68 - runtime.sendMessage({ type: "DID_FOUND", did: domainDID }) 69 - } else if (httpsDID) { 70 - runtime.sendMessage({ type: "DID_FOUND", did: httpsDID }) 71 - } else { 72 - runtime.sendMessage({ type: "DID_NOT_FOUND" }) 97 + if (domainDID) { 98 + runtime.sendMessage({ type: "DID_FOUND", did: domainDID }) 99 + } else if (httpsDID) { 100 + runtime.sendMessage({ type: "DID_FOUND", did: httpsDID }) 101 + } else { 102 + runtime.sendMessage({ type: "DID_NOT_FOUND" }) 103 + } 73 104 } 74 105 })() 75 106 76 107 // We listen for messages from the background script 77 108 runtime.onMessage.addListener((message, sender, sendResponse) => { 78 109 if (message.type === "GET_DID") { 79 - checkForDIDDNS(getDomainName()) 80 - .then((did) => sendResponse({ did })) 81 - .catch(() => sendResponse({ did: null })) 82 - return true // Indicate that the response will be sent asynchronously. 110 + const domain = getDomainName() 111 + if (isValidDomain(domain)) { 112 + checkForDIDDNS(domain) 113 + .then((did) => sendResponse({ did })) 114 + .catch(() => sendResponse({ did: null })) 115 + return true // Indicate that the response will be sent asynchronously. 116 + } 83 117 } 84 118 }) 85 119 } else {