Reveal URLs — Arkkitehtuuri

Tämä asiakirja kuvaa, kuinka Reveal URLs -laajennus on rakennettu ja kuinka sen ydinlogiikka toimii, osallistujille ja teknisille lukijoille. Se viittaa todellisiin tiedostoihin ja symboleihin kauttaaltaan (muodossa path symbol), jotta mikä tahansa väite voidaan tarkistaa lähdekoodia vasten. Käyttäjälle suunnatun kuvauksen löydät käyttöoppaasta; manuaaliset varmennusvaiheet löydät manuaalisesta testaussuunnitelmasta.

Yleiskatsaus

Reveal URLs on yksittäinen MV3-WebExtension, joka on rakennettu useille moottoreille — Chrome, Edge, Opera, Firefox ja Thunderbird — yhdestä jaetusta, puhtaasta koodiytimestä. (Safari on lykätty: sillä on manifesti ja se on koottavissa, kun se nimetään erikseen, mutta se on jätetty pois oletuskoonnista ja CI:stä.) Laajennus paljastaa kunkin linkin kohde-URL-osoitteen linkin vieressä hahmonnetussa sähköpostissa ja merkitsee linkin, jonka näkyvä teksti nimeää eri rekisteröitävän verkkotunnuksen kuin sen href — klassinen tietojenkalastelun merkki.

Tietovarasto on pnpm/TypeScript-monorepo (pnpm-workspace.yaml, package.json jossa "private": true). Niputus on yksittäinen esbuild-pohjainen skripti, tooling/build.mjs, joka kääntää jaetut paketit ja kunkin moottorin ohuet sisääntulopisteet ladattavaan dist/<target>/-hakemistoon. webextension-polyfill niputetaan jokaiseen skriptiin sen sijaan, että siihen luotettaisiin ajonaikaisena globaalina, joten sama lähdekoodi toimii muuttumattomana Chromiumissa ja Geckossa.

Kaksi natiivia sähköpostilisäosaa laajentaa samaa tunnistusydintä pinnoille, joihin WebExtension ei yllä: Outlook-lisäosa (Office.js-tehtäväruutu, joka tavoittaa Outlookin verkossa/Windowsissa/Macissa/iOS:ssä/Androidissa ja Outlook.comin) ja Gmail-lisäosa (Apps Script CardService, joka tavoittaa Gmailin verkko-/Android-/iOS-sovellukset). Molemmat käyttävät uudelleen vain PUHDASTA analyysiä — uusi packages/core/src/findings.ts-moduuli (analyseAnchors/analyseHtml, alustaneutraali Finding[]-malli), joka käyttää uudelleen olemassa olevaa hostMismatch-logiikkaa ja tldts-rekisteröitävän verkkotunnuksen johtamista. Vain isäntäsovitin (kuinka runko luetaan ja jäsennetään) ja esitystapa eroavat pinnoittain: Outlook-tehtäväruutu jäsentää rungon omalla DOMParser-jäsentimellään ja toimii asiakaspuolella; Gmail-lisäosa jäsentää node-html-parser-kirjastolla ja toimii palvelinpuolella Googlen Apps Script V8 -infrastruktuurissa (ilmainen isännöinti, koska Googlella on jo posti hallussaan; otetaan käyttöön paikallis-interaktiivisesti clasp-työkalulla). Koska kumpikaan kehys ei voi muuttaa hahmonnettua, lukutilan viesti-DOMia (Officen setAsync/prependAsync ovat vain laatimista varten; CardService hahmontaa kortteja, ei viestin HTML:ää), molemmat lisäosat esittävät havaintojen paneelin/kortin, eivät sisällytettyä merkintää — olemassa olevaa linkProcessor.ts-DOM-muutospolkua ja REVEAL_URLS_CSS:ää EI käytetä uudelleen kummassakaan lisäosassa. Suhteellinen href ratkaistaan vain luotettavaa sähköpostin sisäistä <base href>-elementtiä vasten ja muutoin ohitetaan, ei koskaan postilaatikon/palveluntarjoajan alkuperää vasten (mikä keksisi kohteen).

Koodikanta erottaa vastuualueet jyrkästi:

Tietovaraston rakenne

packages/core — puhdas ydin

Jokainen tämän moduuli ei tuo mitään laajennus-APIa (browser.*/chrome.*/messenger.*) eikä käytä mitään merkkijonosta-merkkaukseen DOM-nielua (innerHTML/insertAdjacentHTML). Tämän sopimuksen valvoo mekaanisesti packages/core/test/purity.test.ts, joka käy läpi jokaisen src/**/*.ts-tiedoston, riisuu kommenttirungot omalla stripComments-toiminnollaan (jotta dokumentaatiolohkot, jotka laillisesti NIMEÄVÄT kielletyt tokenit, eivät laukaise vartijaa) ja toteaa sitten, ettei EXTENSION_API_PATTERN eikä UNSAFE_DOM_PATTERN täsmää mihinkään moduuliin. Julkinen pinta viedään uudelleen packages/core/src/index.ts-tiedostosta. Ainoa ajonaikainen riippuvuus on tldts (julkisen päätteen tunteva verkkotunnusvertailu).

Moduulit ovat:

packages/webext — selain-API-kääreet

Tämän moduulit VOIVAT käyttää browser.*-rajapintaa ja ovat ainoa paikka, jossa WebExtension-APeihin kosketaan. Julkinen pinta viedään uudelleen packages/webext/src/index.ts-tiedostosta. Moduulit ovat: content.ts (sisältöelinkaari), contentRegistration.ts (dynaaminen rekisteröinti), storage.ts (määritysten pysyväistallennus), background.ts (asennuksenaikainen käyttäytyminen), toolbarAction.ts (kytkin + merkki), messageDisplay.ts ja messageDisplayBackground.ts (Thunderbird) sekä options/options.ts (asetussivun ohjain).

extensions/<engine> — ohuet sisääntulopisteet ja manifestit

Kukin moottori kantaa oman manifest.json-tiedostonsa ja icons/-kansionsa. Chrome, Firefox ja Thunderbird toimittavat lisäksi sisääntulopisteiden src/-kansion; Edge, Opera ja Safari kantavat vain manifestin ja kuvakkeet ja KÄYTTÄVÄT UUDELLEEN Chromen src/-kansiota (julistettu koontikuvaajassa — katso alla). Sisääntulopisteet ovat tarkoituksella pieniä: esimerkiksi extensions/chrome/src/content.ts on vain void createContentController().bootstrap();, ja extensions/chrome/src/background.ts kutsuu registerBackground, registerContentReconciliation ja registerToolbarToggle. Firefoxin taustasisääntulopiste on identtinen Chromen kanssa; Thunderbirdin (extensions/thunderbird/src/background.ts) kutsuu sen sijaan registerBackground ja registerMessageDisplay (sillä ei ole sisältöskriptejä). extensions/_template on kopioimisvalmis Chromium-teline, ei koontikohde.

tooling, features ja savutestit

tooling sisältää build.mjs, version.mjs ja icons.mjs. features sisältää Gherkin-skenaariot, niiden askelmäärittelyt ja uskolliset testikaksoset tiedostoissa features/support/webext.ts ja features/support/world.ts. Kukin moottori kantaa myös extensions/<engine>/test/manifest.smoke.test.mjs (_template:n on template.smoke.test.mjs) — nämä ovat sitova manifestisopimus (katso "Moottorikohtainen koonti ja manifestisopimus" alla).

Paljastusputki

Sisältöpolku alkaa moottorin sisääntulopisteestä kuten extensions/chrome/src/content.ts, joka kutsuu createContentController().bootstrap() tiedostosta packages/webext/src/content.ts.

Käynnistys

createContentController bootstrap suoritetaan kerran kehystä kohden:

  1. Se varaa kehyksen synkronisesti, ennen mitään await-kutsua, claimBootstrap-toiminnolla, joka lukee/asettaa BOOTSTRAP_MARKER-lipun kehyksen window-objektiin. Staattinen sisäänrakennettu content_scripts-merkintä ja päällekkäinen dynaaminen käyttäjäskripti voivat molemmat injektoida content.js:n samaan kehykseen; jaettu eristetyn maailman window tekee tästä oikean kerran-vain-vartijan, ja varaaminen ennen mitään await-kutsua tarkoittaa, että kaksi lähes samanaikaista injektiota eivät voi molemmat läpäistä sitä.
  2. Se lataa pysyväistallennetun määrityksen ENSIN loadConfigWithRetry-toiminnolla (BOOTSTRAP_CONFIG_RETRIES ylimääräistä yritystä ohimenevän hylkäyksen yhteydessä), pitäen merkkiä yritysten ajan, jotta päällekkäinen injektio, joka on jo no-op'annut, ei jää jumiin. Vasta kun luku onnistuu, se injektoi tyylitiedoston (injectStyles, joka asettaa REVEAL_URLS_CSS:n textContent-kautta), joten epäonnistunut luku ei lisää <style>-elementtiä eikä uudelleenyritys voi kahdentaa sitä.
  3. Jos määrityksen lataus epäonnistuu jokaisella yrityksellä, releaseBootstrap palauttaa merkin (vain tässä, ennen kuin merkintä alkaa), jotta myöhempi uudelleeninjektio voi yrittää uudelleen; kun start on suoritettu, merkin on säilyttävä, koska tuore merkitsijä ei omistaisi edellisen läpikäynnin solmuja.
  4. Kun config.enabled, se kutsuu start-toimintoa, ja se rekisteröi aina onConfigChanged-kuuntelijan, joka soveltaa määrityksen uudelleen (kattaen pois käytöstä→käytössä -siirtymän).

Aktiivisen sivustosäännön ratkaiseminen

start (ja applyConfig) kutsuu resolveSiteRule-toimintoa päättääkseen, merkitäänkö — ja missä. resolveSiteRule kokeilee ensin suoritettavan asiakirjan OMAA sijaintia resolveForHref-toiminnolla, joka suodattaa config.sites:n käytössä oleviin sääntöihin ja delegoi ytimen selectMostSpecific-toiminnolle, jotta tarkin täsmäys voittaa (tietty sisäänrakennettu voittaa laajan käyttäjän jokerimerkin). Kun oma sijainti ei täsmää mihinkään JA asiakirjan oma alkuperä on läpinäkymätön tai peritty — rajattu hasOpaqueOrigin-toiminnolla, tosi vain about:blank/about:srcdoc:lle (peritty INHERITED_ORIGIN_URLS-kautta) tai blob:/data::lle (OPAQUE_ORIGIN_SCHEMES) — se siirtyy varajärjestyksessä YLIMPÄÄN kehykseen (readTopHref, sisäänrakennettu Proton-viesti-iframe-tapaus), AVAAJA-ikkunaan (readOpenerHref, Outlookin ponnahdusikkunan lukuikkuna, joka avataan window.open("about:blank")-kutsulla) ja lopulta document.referrer-arvoon (readReferrer). Jokainen ulkoinen luku on suojattu eri alkuperän pääsyä vastaan (mikä heittää poikkeuksen) ja tuottaa ehdokkaan vain, kun se on luettavissa. TAVALLINEN täsmäämätön asiakirja on sitova eikä koskaan peri sääntöä ympäröivästä kontekstista, jättäen ohjaimen toimettomaksi.

Sisältöjuureen rajaaminen ja muutosten tarkkailu

start tallentaa täsmänneen säännön contentRoot-valitsimen, luo merkitsijän createAnnotator(config)-kautta ja käsittelee kunkin täsmänneen juuren selectRoots(doc, contentRoot)-kutsusta (querySelectorAll, joka on kääritty try/catch-rakenteeseen, jotta virheellinen valitsin epäonnistuu turvallisesti arvoon []). Sitten se tarkkailee doc.body-elementtiä (vakaa säiliö) OBSERVER_OPTIONS-asetuksilla ({ childList: true, subtree: true }).

Tarkkailija syöttää processMutations-toiminnon, joka rajaa jokaisen muutoksen sisältöjuureen: se käsittelee uudelleen muutoksen target-kohteen vain, kun se on Element juuren sisällä (isWithinRoot, closest-kautta), ja reitittää kunkin lisätyn Elementin processAddedNode-toiminnon kautta, joka merkitsee solmun, joka on juuren sisällä, ON juuri tai SISÄLTÄÄ sellaisen (laiska SPA-kiinnitys). Joten juuren sisäinen kuhina ja myöhemmin kiinnittyvä sisarviestin runko jäävät molemmat kiinni, kun taas juurten ulkopuolinen sovelluskehys jätetään koskemattomaksi. Tyhjä merkitsijä (laajennus on pysäytetty) on no-op.

applyConfig sovittaa elävän määritysmuutoksen: se purkaa, kun mikään sääntö ei täsmää tai käytössä-lippu on pois; käynnistää uudelleen (palautus + uudelleentarkkailu), kun ratkaistu contentRoot muuttui; ja muutoin uudelleenvirtaa paikalla — annotator.setConfig ja sitten annotator.process kunkin täsmänneen juuren yli — käynnistämättä tarkkailijaa uudelleen.

Yksittäisen ankkurin merkitseminen

Merkitsijä sijaitsee tiedostossa packages/core/src/linkProcessor.ts. createAnnotator palauttaa Annotator-objektin sulkeumatilan yli, jota sivu ei voi lukea: vahva Map, joka on avaimitettu ankkurin identiteetillä, merkitsijäkohtainen data-ru-token generateToken-kautta (crypto.getRandomValues) ja nykyinen määritys. Sen process(root)-metodi:

  1. Karsii rekisterimerkinnät, joiden ankkuri ei ole enää yhdistetty.
  2. Kerää ehdokasankkurit collectAnchors-toiminnolla (jälkeläiset a[href] plus juuri itse, kun se on a[href], kahdennukset poistettuina).
  3. Ratkaisee kunkin ankkurin kohteen resolveAnnotatableUrl-toiminnolla, joka palauttaa null (ohita) tyhjälle/sivunsisäiselle hrefille, jäsentymättömälle hrefille, mille tahansa muulle mallille kuin http:/https: tai isännälle, joka on yhtä suuri tai ignoreHosts-merkinnän alainen; se ratkaisee suhteelliset hrefit baseURI-arvoa vasten, joten tulos on absoluuttinen.
  4. Ottaa idempotentin nopean polun, kun isAnnotationIntact vahvistaa, että hahmonnussormenjälki on muuttumaton ja jokainen luotu solmu on edelleen yhdistetty ja paikallaan; muutoin se palauttaa vanhentuneen merkinnän ja hahmontaa uudelleen.
  5. Laskee täsmäämättömyyden hostMismatch-toiminnolla puhtaalle ankkuritekstille, kunnioittaa showOnlyOnMismatch-asetusta (jättäen rehelliset linkit koskemattomiksi) ja kutsuu annotateAnchor.

annotateAnchor hahmontaa vain TURVALLISTA DOMia. "inline"-tilassa se lisää eteen <span class="reveal-urls-url">-elementin, jonka URL asetetaan textContent-kautta (etuliitteenä nuoli ja U+00A0 sitomaton välilyönti, lyhennettynä truncateUrl-toiminnolla), jota seuraa <br>, jättäen linkin omat lapset koskemattomiksi; yhdistetty span saa sitten kontrastitaustan applyContrastBackdrop-kautta. "title"-tilassa se tallentaa alkuperäisen title-arvon (TitleSave), ylikirjoittaa sen URL:llä — ainoa kirjoitus ankkuriin — ja korostetun täsmäämättömyyden yhteydessä lisää sisar- <span class="reveal-urls-warn">-merkin, joka kantaa REVEAL_URLS_WARN_LABEL-arvoa. Jokainen luotu solmu merkitään data-ru="<token>"-attribuutilla (ei koskaan ankkuri), ja kukin merkintä tallentaa configFingerprint-arvonsa, jotta hahmonnusmäärityksen muutos pakottaa uudelleenvirtauksen. Värit ja fonttiohitukset sovelletaan typitettyjen element.style.*-ominaisuuksien kautta (applyColour/applyFontOverrides), ei koskaan interpoloituna CSS-merkkijonoon. revertOwned poistaa luodut solmut viitteen perusteella ja palauttaa tallennetun otsikon; revertAll tekee tämän jokaiselle omistetulle ankkurille ja tyhjentää rekisterin.

truncateUrl toimii Unicode-KOODIPISTEIDEN yli ([...displayHref]), joten leikkaus ei koskaan halkaise sijaisparia; se säilyttää mallin-ja-isännän alkuperän ja lyhentää loput perässä olevalla URL_ELLIPSIS-arvolla.

Isännän täsmäämättömyys

packages/core/src/hostMismatch.ts hostMismatch vertaa linkin näkyvää tekstiä sen href-arvoon rekisteröitävän verkkotunnuksen, ei raa'an isäntänimen perusteella, joten rehellistä aliverkkotunnusta ei merkitä, kun taas samannäköinen merkitään. extractHostCandidates jakaa tekstin ja pelkistää kunkin tokenin hostCandidate-toiminnolla (joka vaatii pisteen ja tldts-tunnistetun ICANN-julkisen päätteen, hyläten tavallisen pisteellisen tekstin kuten e.g). Sekä href-isäntä että kukin ehdokas pelkistetään rekisteröitäväksi verkkotunnukseksi tldts.getDomain-toiminnolla, ja MIKÄ TAHANSA ehdokas, jonka verkkotunnus eroaa hrefin verkkotunnuksesta, tuottaa täsmäämättömyyden — joten todellisen haitallisen isännän nimeäminen houkutusisännän rinnalla ei voi vaimentaa varoitusta. Se ei koskaan heitä poikkeusta.

Tyylit ja kontrasti

packages/core/src/styles.ts sisältää injektoitavan REVEAL_URLS_CSS:n (kiinteä asettelu, paksuus, muoto ja kiinteä valkoinen, täysin läpinäkymätön sirun tausta — background: #ffffff, opacity: 1; värejä EI KOSKAAN interpoloida siihen) ja ajonaikaiset applyColour/applyFontOverrides/applyContrastBackdrop-apurit. applyContrastBackdrop lukee elementin ratkaistun värin ja ensimmäisen läpinäkymättömän taustan, joka löytyy kävelemällä elementistä ITSESTÄÄN ylöspäin (sen oma tausta — sirun kiinteä valkoinen oletus tai isäntäsivun tarkempi ohitus — ennen mitään esivanhempaa) standardin getComputedStyle-kautta (lähteenä element.ownerDocument?.defaultView, joten se on kehysturvallinen ja tynkättävissä) ja, kun packages/core/src/contrast.ts needsWhiteBackdrop raportoi, että teksti epäonnistuu WCAG AA:ssa (CONTRAST_THRESHOLD) kyseistä taustaa vasten JA valkoinen todella auttaa, asettaa style.backgroundColor = "white". contrast.ts tarjoaa parseColour, relativeLuminance ja contrastRatio; se jäsentää rgb()/rgba()/heksamuodot, jotka getComputedStyle palauttaa, ja käsittelee täysin läpinäkyvää väriä "ei löytynyt".

Määritettävät isännät ja sisällön rajaaminen

SiteRule (packages/core/src/config.ts) sanoo MISSÄ merkintä suoritetaan (match, allFrames) ja MIKÄ säiliö sen rajaa (contentRoot), plus enabled ja builtin-lippu. Toimitettu DEFAULT_SITES kattaa Gmailin, Protonin (allFrames-lipulla) ja molemmat Outlook-isännät. Config kokoaa globaalit kytkimet, värit, fonttiohitukset ja sites-taulukon.

Täsmäyskaavamoottori

packages/core/src/matchPattern.ts sisältää tarkoituksella RAJOITTAVAN lisäysaikaisen kieliopin MATCH_PATTERN (vain http/https, valinnainen alussa oleva *.-isäntäjokerimerkki, glob-polku; ei porttia, ei *-mallia, ei <all_urls>). matchesPattern täsmää URL:n validoitua kaavaa vasten, delegoiden polun pathGlobMatches-toiminnolle (kukin * täsmää minkä tahansa merkkijonon); se epäonnistuu turvallisesti arvoon false. parsePatternParts jakaa validoidun kaavan osiin host/path/scheme/wildcardHost.

Päällekkäiset säännöt ratkeavat "tarkin voittaa" -periaatteella: compareSiteSpecificity järjestää (1) tarkka isäntä jokeri-isännän edelle, (2) pidempi literaali-isäntä, (3) literaalisempi polku (literalPathLength), sitten (4) deterministinen ASCII-ratkaisu; selectMostSpecific palauttaa tarkimman käytössä olevan säännön, joka täsmää URL:ään.

Myönnetyn alkuperän kattavuus on ERILLINEN, tarkoituksella LAAJEMPI täsmääjä. permissions.getAll() voi raportoida myönnöt koko WebExtension-kieliopilla (<all_urls>, *://*/*, https://*/*, *://*.host/*, https://host/*), jotka rajoittava MATCH_PATTERN hylkäisi. parseGrantedOrigin jäsentää nämä GrantedOriginParts-muotoon, ja originCovers raportoi, kattaako myönnetty alkuperä säännön match-arvon (esim. laaja https://*/* myöntö todella kattaa jokaisen https-säännön). matchAllowsOriginFallback raportoi, onko säännön polku tarkalleen ORIGIN_FALLBACK_PATH (/*) — Chromiumin ja Gecko/Firefox 128:n rajoitusten turvallinen leikkaus dynaamisessa läpinäkymättömän alkuperän varatoiminnossa.

Dynaaminen rekisteröinti

packages/webext/src/contentRegistration.ts rekisteröi yhden dynaamisen sisältöskriptin kutakin käyttäjän lisäämää sääntöä kohden. Sisäänrakennettuja palvelevat staattiset content_scripts-merkinnät, eikä niitä KOSKAAN rekisteröidä dynaamisesti. desiredContentScripts suodattaa config.sites:n ei-sisäänrakennettuihin, käytössä oleviin sääntöihin, joiden alkuperä isOriginGranted (delegoiden originCovers-toiminnolle), kuvaa kunkin RegisteredContentScript-objektiksi vakaalla id:llä (contentScriptId, matchin FNV-1a-tiiviste, jotta id käyttää vain API-turvallista merkistöä) ja asettaa matchOriginAsFallback: true VAIN, kun matchAllowsOriginFallback(rule.match) pätee (/*-polku). Lipun rajaaminen tällä tavoin estää yhtä ei-yhteensopivan polun sääntöä aiheuttamasta koko erärekisteröinnin hylkäystä; tällainen sääntö rekisteröityy ilman ponnahduskattavuutta suunnitellusti.

reconcileContentScriptsOnce lukee myönnetyt alkuperät, tallennetun määrityksen ja elävät rekisteröinnit, sitten suppenee OMINAISUUSTIETOISELLA erotuksella: sameRegistration projisoi sekä halutun kuvaajan että elävän takaisinluvun normalisoituun muotoon (soveltaen kunkin kentän todellista WebExtension-oletusta — allFrames/matchOriginAsFallback oletus epätosi, persistAcrossSessions oletus TOSI, runAt oletus document_idle), jotta tuoreesti rekisteröity skripti vertautuu YHTÄ SUUREKSI sen tuottaneen kuvaajan kanssa eikä uudelleenrekisteröintisilmukkaa voi syntyä. Koska updateContentScripts-toimintoa ei ole, muuttunut skripti poisrekisteröidään ja rekisteröidään sitten uudelleen. reconcileContentScripts kietoo tämän käynnissä olevaan lupaukseen (inFlight), jotta kaksi lähes samanaikaista laukaisua (määritysmuutos JA permissions.onAdded molemmat laukeavat sivuston lisäyksessä) sarjallistetaan kilpailemisen sijaan. registerContentReconciliation johdottaa laukaisimet (permissions.onAdded/onRemoved, onConfigChanged) ja suppenee kerran käynnistyksessä; sen tuovat vain Chromen ja Firefoxin taustasisääntulopisteet.

Vapaaehtoinen lupaprosessi

Staattiset manifestit julistavat optional_host_permissions (http://*/*, https://*/*), jotta käyttäjä voi myöntää mielivaltaisen lisäverkkoisännän ajonaikaisesti. packages/webext/src/options/options.ts addSite validoi KOKO ehdokassäännön (match JA sisältöjuuri) normaliseSiteRule-toiminnolla ENNEN minkään luvan pyytämistä, johtaa isäntäalkuperän matchOrigin-toiminnolla ja kutsuu browser.permissions.request-toimintoa Lisää-painikkeen napsautuseleen sisältä; vain myönnön yhteydessä se lisää builtin:false-rivin ja pysyväistallentaa kanonisen kirjoituspolun kautta.

Määritys, tallennus ja kanoninen malli

Yksittäinen validointisuppilo

normaliseConfig tiedostossa packages/core/src/config.ts on yksi kanoninen lukupolku: se pakottaa ja rajaa jokaisen kentän, palaten oletukseen kaikessa virheellisessä, eikä koskaan heitä poikkeusta. Se rakennetaan kenttäkohtaisista validaattoreista — normaliseBoolean, normaliseRenderMode, normaliseMaxLength, normaliseIgnoreHosts (pelkistäen kunkin merkinnän paljaaksi punycode-isäntänimeksi bareHostname-kautta), normaliseMatchColour/normaliseMismatchColour (rajattu safeColour.ts isSafeColour-toiminnolla), normaliseCssSize, normaliseFontWeight, normaliseContentRoot (rajattu CONTENT_ROOT_PATTERN/CONTENT_ROOT_MAX_LENGTH-arvoilla), normaliseMatchPattern (rajattu MATCH_PATTERN-arvolla) ja normaliseSites. normaliseSites pudottaa hylkäykset, poistaa kahdennukset match-arvon perusteella, PAKOTTAA builtin: true-arvon jokaiseen sääntöön, jonka match on sama kuin sisäänrakennetun (sulkien tarkan-täsmäyksen-varjostaa-sisäänrakennetun / kaksoisinjektion vektorin), uudelleenkylvää minkä tahansa puuttuvan sisäänrakennetun (jotta peukaloitu varasto ei voi pudottaa ydintarjoajaa) ja järjestää aakkosnumeerisesti match-arvon perusteella kanonisen järjestyksen vuoksi. configFingerprint tiivistää vain hahmonnukseen vaikuttavat kentät (pois lukien enabled, ignoreHosts ja sites, jotka ohjaavat käynnistystä/pysäytystä ja ohitusta uudelleenvirtauksen sijaan) fnv1a-kautta.

Tallennus

packages/webext/src/storage.ts kietoo WebExtension-tallennus-APIn. configArea käyttää browser.storage.sync-tallennusta, kun se on saatavilla (jotta asetukset vaeltavat), ja palaa browser.storage.local-tallennukseen (esim. Thunderbirdissä); configAreaName raportoi, kumpi on aktiivinen. getConfig, setConfig ja onConfigChanged kaikki suppiloivat raaka-arvonsa normaliseConfig-toiminnon läpi, joten väärin muotoiltu tai peukaloitu varasto ei voi koskaan luovuttaa virheellistä Config-objektia kutsujalle. onConfigChanged lisäksi ohittaa tapahtumat EPÄAKTIIVISESTA tallennusalueesta ja muutokset asiaankuulumattomiin avaimiin.

Kaksi lisäosaa käyttää uudelleen samaa luku -> normaliseConfig -> palautus -suppiloa omien isäntävarastojensa yli. Outlook-tehtäväruudun extensions/outlook/src/roamingStorage.ts kietoo Office.context.roamingSettings-objektin (synkroninen get/set, vahvistettu saveAsync-kautta); Gmail-lisäosan packages/gmail/src/propertiesStorage.ts kietoo PropertiesService.getUserProperties()-objektin (käyttäjäkohtainen, vaeltaa kyseisen käyttäjän laitteiden välillä, JSON-koodattu yhden avaimen alle eikä tarvitse YHTÄÄN ylimääräistä OAuth-laajuutta — ei koskaan jaettua ScriptProperties-objektia). Molemmat paljastavat getConfig/setConfig plus korttirelevantit getIgnoreHosts/getHighlightMismatch-käyttäjät ja suojaavat käytettävissä olemattoman isäntäpinnan erillisellä virheellä. Gmailin kotisivulaukaisin hahmontaa CardService-asetuskortin getConfig-arvosta, ja onSaveSettings-lomakelähetyskäsittelijä yhdistää jäsennetyn ignoreHosts/highlightMismatch-arvon nykyiseen määritykseen ja pysyväistallentaa sen setConfig-kautta; kontekstuaalinen laukaisin pujottaa ignoreHosts-arvon sovittimeen ja highlightMismatch-arvon kortinrakentajaan, lukien puolustavasti, jotta tallennusvirhe palautuu oletuksiin sen sijaan, että rikkoisi viestianalyysin.

Kanoninen-määritys + kohdennettu-päivitys -malli

Kolme kirjoituspolkua lukee kanonisen määrityksen, muuttaa tarkalleen yhden asian ja kirjoittaa sen takaisin — ei koskaan vahvistaen tallentamattomia lomakemuokkauksia tai hahmontaen lomaketta uudelleen:

Työkalupalkin toiminnon oma toggleEnabled (packages/webext/src/toolbarAction.ts) noudattaa samaa mallia taustapuolelta.

Moottorikohtainen koonti ja manifestisopimus

tooling/build.mjs ajaa yhden esbuild-koonnin jokaiselle WebExtension-kohteelle. TARGETS-kuvaaja nimeää kunkin kohteen LÄHDEKOHTEEN: Chrome, Firefox ja Thunderbird toimittavat oman src/-kansionsa; Edge, Opera ja Safari julistavat chrome-kohteen lähteekseen; Outlook (Office.js) -lisäosa on oma lähteensä. ACTIVE_TARGETS (Chrome, Edge, Firefox, Opera ja Thunderbird — Safari ja Outlook on jätetty pois) on se, mitä --all/--package kokoaa; Safari ja Outlook koottavat vain, kun ne nimetään erikseen, ja Gmail (Apps Script) -lisäosa on erillinen esbuild-niputus (make build-gmail) --all-silmukan ulkopuolella. Tuo Gmail-niputus kohdistuu Apps Scriptin V8-ajoympäristöön, jolla ei ole ES-moduuleja eikä natiivia URL-objektia: se rakennetaan ESM:nä ja sitten sen perässä oleva export {…}-lause poistetaan (jotta laukaisinfunktiot pysyvät ylätason globaaleina, joita Apps Script voi kutsua), ja se niputtaa pienen URL-polyfillin (asennettu vain, kun URL puuttuu), jotta jaetun ytimen new URL(...) ratkaisee linkit palvelinpuolella. buildTarget siivoaa dist/<target>/-kansion, kopioi kohdekohtaisen manifest.json-tiedoston ja icons/-kansion sanatarkasti, niputtaa kunkin lähdeskriptin IIFE:nä (injektoidut INJECTED_SCRIPTS content.ts/messageDisplay.ts eivät voi olla ES-moduuleja, ja taustat ovat myös klassisia työntekijöitä/tapahtumasivuja) ja jaetun asetussivun ESM:nä sekä kopioi options.html/options.css kansiosta packages/webext/src/options/. webextension-polyfill niputetaan jokaiseen skriptiin.

tooling/version.mjs leimaa versiot: se ottaa MAJOR.MINOR-arvon juuren package.json-tiedostosta ja lisää automaattisesti kasvavan BUILD-numeron (luettu kohdemanifestien korkeimmasta olemassa olevasta kolmannesta komponentista) jokaiseen kohdemanifestiin pitäen ne tahdissa. Useimmat kohteet kantavat yhden manifest.json-tiedoston; Outlook-lisäosa kantaa kaksi versioitua manifestia (manifest.json ja manifest.xml) ja molemmat leimataan, ja _template-teline leimataan myös tahdissa. Juuren package.json pidetään samassa versiossa, mutta pysyy validina semverinä (neliosainen versio sallitaan manifestissa, ei koskaan package.json-tiedostossa). tooling/icons.mjs (ajetaan make icons-kautta) rasteroi yksittäisen vektorilähteen assets/icon.svg kunkin kohteen icons/icon48.png- ja icons/icon128.png-tiedostoiksi, kokeillen cairosvg, sitten inkscape, sitten ImageMagick.

Manifestisopimus — tarkat vaaditut kentät moottoreittain — on yksi totuuden lähde kussakin extensions/<engine>/test/manifest.smoke.test.mjs-tiedostossa (esim. extensions/chrome/test/manifest.smoke.test.mjs-tiedoston assertManifestShape, joka myös rakentaa kohteen ja vahvistaa, että jokainen viitattu tiedosto ratkeaa distissä). Projektin kahdennuksenpoistokäytännön mukaisesti kenttäkohtaista muotoa EI luetella uudelleen tässä; viittaa noihin savutesteihin ja "Manifestisopimus"-huomautukseen manuaalisessa testaussuunnitelmassa.

Kansainvälistäminen (i18n)

Reveal URLs on lokalisoitu monelle kielelle englannin lisäksi. Joukko on yksilähteistetty tiedostoon packages/core/src/locales.json (SUPPORTED_LOCALES, natiivinimet ja oletus), jonka sekä laajennusniput (ratkaistun JSON-tuonnin kautta) että puhdas-Node-verkkosivuston koonti lukevat. Jokainen ei-englanninkielinen merkkijono on konekäännetty ja odottaa ihmisen tarkistusta; alkuperämerkintä on muotokohtainen (_locales käyttää kunkin viestin description-kenttää, verkkosivuston kehyssanakirjat tallentavat sen tiedostoon site/i18n/README.md, ja käännetyt dokumenttilähteet kantavat ensimmäisen rivin HTML-kommenttia).

Lokalisointipintoja on kolme riippumatonta, tarkoituksella ei ristiinkytkettyä, ja kukin on jaettu koonti-aika / ajonaikainen -linjaa pitkin.

Sähköpostiohjelman "Aktiiviset sivustot" -vaimennus (AD-3)

Sähköpostiohjelmakohde (Thunderbird tänään) näkee jo jokaisen hahmonnetun viestin, joten isäntäkohtainen "Aktiiviset sivustot" -editori on siellä tarpeeton ja poistetaan. Päätöstä kantaa dokumentoitu kohdekohtainen kuvaajalippu, mailClient: true, tooling/build.mjs:n TARGETS.thunderbird- kohteessa — ei koskaan kovakoodattu kohdenimi koonti- tai käyttöliittymälogiikassa — ja se toteutetaan kahdessa puolikkaassa:

Tietoturva- ja tietosuoja-asento

Testaus