this repo has no description
0
fork

Configure Feed

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

working chrome background.js

+130 -31
+128 -28
background.js
··· 7 7 const action = 8 8 typeof browser !== "undefined" ? browser.browserAction : chrome.action 9 9 10 - // On extension installation, check if privacy consent was already accepted and show it if not 11 - runtime.onInstalled.addListener(() => { 12 - storage.get("privacyConsentAccepted", ({ privacyConsentAccepted }) => { 13 - if ( 14 - typeof privacyConsentAccepted === "undefined" || 15 - !privacyConsentAccepted 16 - ) { 17 - tabs.create({ url: "privacy_consent.html" }) 18 - } 19 - }) 20 - }) 10 + // The rest of the original background.js code... 11 + 12 + // Content.js code migrated to background.js 13 + // Make sure that we don't DoS the regex if someone supplies too large of a DID 14 + const MAX_DID_LENGTH = 255 15 + 16 + // Regular expression to validate the DID format 17 + // https://w3c.github.io/did-core/#did-syntax 18 + const didRegex = 19 + /^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._-])+)*$)/ 20 + 21 + // Function to validate the DID string 22 + function isValidDID(didString) { 23 + return didString.length <= MAX_DID_LENGTH && didRegex.test(didString) 24 + } 25 + 26 + // Function to get the domain name from the current hostname 27 + function getDomainName(url) { 28 + const hostname = new URL(url).hostname 29 + return hostname.replace(/^www\./, "") 30 + } 21 31 22 - // If the message 'SHOW_CONSENT' is received, open the privacy consent tab 23 - runtime.onMessage.addListener((message) => { 24 - if (message.type === "SHOW_CONSENT") { 25 - tabs.create({ url: "privacy_consent.html" }) 32 + // Function to validate the domain name 33 + function isValidDomain(domain) { 34 + const MAX_DOMAIN_LENGTH = 255 35 + 36 + if (domain.length > MAX_DOMAIN_LENGTH) { 37 + return false 26 38 } 27 - }) 39 + 40 + try { 41 + // Use the build in URL constructor to validate the URL, if doesn't throw an error, the domain is valid 42 + // This is a better choice than a regex since it should properly support punycode/international domains 43 + new URL(`https://${domain}`) 44 + return true 45 + } catch (error) { 46 + // The URL constructor threw an error, so the domain is not valid 47 + return false 48 + } 49 + } 50 + 51 + // Function to check for a DID in the domain's TXT records 52 + async function checkForDIDDNS(domain) { 53 + try { 54 + const response = await fetch( 55 + `https://dns.google/resolve?name=_atproto.${domain}&type=TXT` 56 + ) 57 + const data = await response.json() 58 + 59 + // We use the TXT record type to avoid CORS issues 60 + const records = data?.Answer?.filter((record) => record.type === 16) || [] 61 + 62 + // We filter out all records that are not TXT records 63 + const didRecord = records.find((record) => 64 + record.data.includes("did=did:plc:") 65 + ) 66 + 67 + // We return the DID if we found one and it's valid 68 + return didRecord && isValidDID(didRecord.data.replace("did=", "")) 69 + ? didRecord.data.replace("did=", "") 70 + : null 71 + } catch (error) { 72 + return null 73 + } 74 + } 75 + 76 + // Function to check for a DID in the well-known (not .well-known) location 77 + async function checkForDIDHTTPS(domain) { 78 + try { 79 + const response = await fetch( 80 + `https://${domain}/xrpc/com.atproto.identity.resolveHandle` 81 + ) 82 + 83 + if (!response.headers.get("Content-Type")?.includes("application/json")) { 84 + throw new Error("Invalid Content-Type") 85 + } 86 + const data = await response.json() 87 + return data.did && isValidDID(data.did) ? data.did : null 88 + } catch (error) { 89 + return null 90 + } 91 + } 28 92 29 93 // Map to store tabs with DIDs 30 94 const tabsWithDID = new Map() 31 95 32 96 // URL of the Bluesky Web Applications 33 - const bskyAppUrl = "https://staging.bsky.app" 97 + const bskyAppUrl = "https://bsky.app" 34 98 35 99 // Function to set the extension icon 36 100 function setIcon(tabId, iconName) { 37 101 action.setIcon({ path: iconName, tabId }) 38 102 } 39 103 104 + // Main function to perform actions, but only if the privacy consent has been accepted 105 + function performAction(tab) { 106 + storage.get("privacyConsentAccepted", ({ privacyConsentAccepted }) => { 107 + // If the user has accepted the privacy consent 108 + if (privacyConsentAccepted) { 109 + const domain = getDomainName(tab.url) 110 + if (isValidDomain(domain)) { 111 + checkForDIDDNS(domain).then((domainDID) => { 112 + if (domainDID) { 113 + setIcon(tab.id, "logo48.png") 114 + tabsWithDID.set(tab.id, domainDID) 115 + } else { 116 + checkForDIDHTTPS(domain).then((httpsDID) => { 117 + if (httpsDID) { 118 + setIcon(tab.id, "logo48.png") 119 + tabsWithDID.set(tab.id, httpsDID) 120 + } else { 121 + setIcon(tab.id, "logo48_gray.png") 122 + tabsWithDID.delete(tab.id) 123 + } 124 + }) 125 + } 126 + }) 127 + } 128 + } 129 + }) 130 + } 131 + 132 + // Execute performAction when a tab is updated 133 + tabs.onUpdated.addListener((tabId, changeInfo, tab) => { 134 + if (changeInfo.status === "complete" && tab.active) { 135 + performAction(tab) 136 + } 137 + }) 138 + 139 + // On extension installation, check if privacy consent was already accepted and show it if not 140 + runtime.onInstalled.addListener(() => { 141 + storage.get("privacyConsentAccepted", ({ privacyConsentAccepted }) => { 142 + if ( 143 + typeof privacyConsentAccepted === "undefined" || 144 + !privacyConsentAccepted 145 + ) { 146 + tabs.create({ url: "privacy_consent.html" }) 147 + } 148 + }) 149 + }) 150 + 40 151 // On extension installation, set the icon to gray for all tabs 41 152 runtime.onInstalled.addListener(() => { 42 153 tabs.query({}, (tabs) => { 43 154 tabs.forEach((tab) => setIcon(tab.id, "logo48_gray.png")) 44 155 }) 45 - }) 46 - 47 - // When a message is received from the DNS check, set the icon color to blue. 48 - runtime.onMessage.addListener((message, sender) => { 49 - if (message.type === "DID_FOUND") { 50 - setIcon(sender.tab.id, "logo48.png") 51 - tabsWithDID.set(sender.tab.id, message.did) 52 - } else { 53 - setIcon(sender.tab.id, "logo48_gray.png") 54 - tabsWithDID.delete(sender.tab.id) 55 - } 56 156 }) 57 157 58 158 // Open the consent page if it hasn't been accepted and the user clicks on the extension icon
+2 -3
manifest.json
··· 9 9 }, 10 10 "icons": { "48": "logo48.png", "128": "logo128.png" }, 11 11 "description": "Detects Decentralized Identifiers (DIDs) in a domain's TXT records and links to the associated Bluesky profile.", 12 - "permissions": ["activeTab", "storage"], 13 - "background": { "service_worker": "background.js" }, 14 - "content_scripts": [{ "matches": ["<all_urls>"], "js": ["content.js"] }] 12 + "permissions": ["tabs", "storage"], 13 + "background": { "service_worker": "background.js" } 15 14 }