Repository navigation
Releases
v3.2.0
Visilant 3.2.0 treats www.example.com and example.com as one site, rewrites every warning in plain words, and makes the look-alike warnings name the site they are about instead of describing it.
www.example.com and example.com are one site
Visits were counted per exact hostname, so a bank you read every day at www. looked unfamiliar the first time a link dropped the prefix. A warning that is wrong that often teaches people to click through it.
Records are now keyed by the name without a leading www., as long as what is left is still somebody's site, so www.com and www.co.uk stay as they are. Counting, the badge, verdicts, silencing, the history import, the popup and the check panel all use the same key. A link from www.example.com to example.com no longer gets the tooltip or stops behind the click dialog as if it led somewhere new.
Records from earlier versions are merged once, when the extension starts, before any other visit is written. Visits add up, the first and last dates take the widest span, and active days take the larger count. A site silenced under either name stays silenced. An interrupted merge does not count anything twice.
Every warning says what is true where it appears
The link dialog said "Unfamiliar site ahead" whatever the link was. Its title now follows the destination: an unfamiliar site, a short link not unfolded yet, or a familiar site with a plain "Go to link". A short link that could not be unfolded says what went wrong. With "resolve any link" on, a link that stays on a familiar host opens without a dialog.
Every text was rewritten for readers with B1 English and no technical background. The copy warning names the real risk, a swapped clipboard and the Win+R trick. The tampering notification no longer suggests switching protection off.
Also: notifications name the site, a QR code that cannot be read is no longer shown as a security warning, an email address is never called "never visited", the bare "Total" is gone from the check panel, and "Mark as shortener" appears only after a check.
Look-alike warnings name the site they imitate
The reason used to sit on a line of its own and say "that name", which told a first-time reader nothing about which name was meant. Every reason is now one sentence: it names the familiar site in a filled block and says with "but" how the address differs from it. The visit count after it is gone, because a number at the end of a sentence about another address read as that address's visits.
An address with a whole domain placed in front of the real one used to read "Domain ending used as a subdomain: com", as though com were the subdomain. It now reads "Looks like yourbank.com, but the real site is pay-verify.top". A customer of a hosting platform is named as that customer, never as the bare platform. When the domain in front is one you know, the look-alike line already explains the trick, so this marker steps aside. The real site is drawn in red and the familiar name in grey.
A look-alike written in xn-- form is now explained as a swapped letter. The domain was compared in its encoded form against labels decoded to Unicode, so the two never matched and the site's own name was taken for a subdomain.
Silences on sites that are familiar by now
A silence on a familiar site holds nothing back until the familiarity rules are made stricter, and then it quietly keeps that site from warning. The settings page names those sites and removes them with one button. A new setting, off by default, removes a site from the list on your next visit once it is familiar, and clears the familiar ones straight away when switched on. A silence put on a site that is already familiar is left alone.
The right-click link check reads the link text
"Check link safety" from the context menu now compares the link text with where the link goes. The text is taken at the moment of the right-click, so the page cannot rewrite it first. It answers for links within the same site and for mailto: links too, where it used to do nothing.
Fixed
- The popup's domain card marks a domain that is a link shortener, and says that the visit history says nothing about where its links go.
- The base domain card keeps its gap when the checked host is the only one on record, and says that nothing else is recorded under the domain.
- The warning and anti-tampering switches no longer draw blue on blue in the dark theme.
- The import texts no longer promise that imported sites are familiar before the second pass has filled in their dates.
- The check field no longer spell-checks addresses. A red wave under a domain read as the field marking the wrong part of it.
- The subdomain depth is drawn as a plain number rather than through the address highlighter, which painted its digits blue as though the count were a suspect character.
Under the hood
adm-zipis held at 0.6.1 (GHSA-7q85-xj36-vmfc) andimage-sizeat 2.0.4 (GHSA-5p2g-fcmc-qvqq, GHSA-w3rx-r6r6-pgpr). Both arrive only throughweb-extandaddons-linter, development tools that never ship in the extension.- A commit message longer than 1000 characters is rejected by a hook.
- The Android end-to-end suite follows the current popup and texts again, and passes on a real phone.
Verify this build
Both archives rebuild byte for byte from this tag:
nix build github:ikskoder/visilant/v3.2.0#firefox
sha256sum result/*.xpi # compare with SHA256SUMS on this releasescripts/verify.sh <downloaded.xpi> compares the copy your browser installed against a fresh build, file by file. Full instructions are in REPRODUCE.md. Release archives also carry a Sigstore-signed provenance attestation tying them to this commit and the workflow that built them.
Full Changelog: v3.1.3...v3.2.0
v3.1.3
Visilant 3.1.3 closes the last way a page could talk a user into standing the guard down, stops an embedded frame raising a warning nobody could act on, and settles a question the popup had been answering two ways at once: which name a number is actually about.
Turning a site's warnings off moved out of the page
The in-page warning carried a "Don't show again" button and the paste dialog a tickbox doing the same, and either one silenced every warning about that hostname for good. A page cannot press them – the interface sits in a closed shadow root – but it never had to. Writing "press Don't show again to continue" beside the warning was enough, and one honest click stood the guard down on the attacker's own hostname permanently.
The switch is in the popup now, where no page can reach it, and the name being silenced is the one the popup spells out rather than the one the page claims. The settings page lists everything currently silenced, so an exception can be found and lifted. It used to be a one-way door whose only exit was deleting every visit record in the profile.
An iframe is judged by the page it sits in, not by its own address
The frame guard warned about every cross-origin frame, and that warning could never be settled: visits are counted for top-level navigations, so a host that only ever appears inside a frame sits at zero for ever. It was also a warning nobody could act on, since anyone who can put a frame on a site you know already owns that site. Events caught inside a frame still count – the top document cannot see them – and the verdict is now the page's.
Every figure says which name it is about
The check field folded the whole domain family and printed the result under a subdomain's name, a few pixels above the card printing that host's own record. One screen could carry two rows headed Visits with different numbers in them, and nothing said which was which.
The checked-domain card now answers for that exact host and nothing else. The base domain has a card of its own carrying the family's figures, with every column saying whether it is a total or the strongest single host, because visits add up and the other checks cannot. First visit reads in days with the date on hover, rather than as a date to count back from.
Fixed
- A site that draws its own pages no longer trips the tampering alarm. A router throws its body away on every route, taking our container with it, which is the same event as a page tearing the panel out. What separates them is what follows: put back once, the container stays back, while a page that wants it gone has to keep taking it away.
- A link that redirects to the host it started from no longer prints that host and its verdict a second time.
- The box you paste an address into grows to fit it, so a long URL is readable in full instead of scrolling out of sight.
- Anything carrying a tooltip says so with the cursor. Half the facts here have one – the bar a check has to clear, the date a day count was measured from – and nothing marked them, so you had to already know.
- The related-domain list draws whichever fact you pick, chosen from a dropdown, and ordering it alphabetically no longer forces the number back to visits.
- The settings page stops spending width on empty space, and with explanations off each familiarity check states its whole rule in one line.
One thing worth knowing rather than discovering: the caveat about following a lookup link moved onto the "Check this domain elsewhere" control as a tooltip, and a tooltip never appears on a touchscreen. On Android that text is out of reach. The links themselves still say that nothing is sent by us.
Under the hood
- Every commit is checked for secrets, host paths and dependency advisories, and CI runs the same check before any artifact exists rather than after.
- The host-path checker no longer declares itself untrustworthy on a machine with no configured git identity, which is every CI runner. It takes the committer's address from the tip commit where the configuration has none. This is what stopped 3.1.2.
- The end-to-end suite checks what it says it checks. Two tests asked only whether the extension's container was in the page, which it is on every page, so the frame guard's own test would have passed with the guard deleted outright.
js-yamlmoved to 4.3.2 (GHSA-2883-xcg3-v3hh). It is a development dependency and does not ship inside the extension. This is what stopped 3.1.1.- The everyday build and test loop runs in a hardened container, the artifact upload runs on Node 24, and the Gitpod files the template left behind are gone.
Verify this build
Both archives rebuild byte for byte from this tag:
nix build github:ikskoder/visilant/v3.1.3#firefox
sha256sum result/*.xpi # compare with SHA256SUMS on this releasescripts/verify.sh <downloaded.xpi> compares the copy your browser installed against a fresh build, file by file. Full instructions are in REPRODUCE.md. Release archives also carry a Sigstore-signed provenance attestation tying them to this commit and the workflow that built them.
Full Changelog: v3.1.0...v3.1.3
v3.1.0
Visilant 3.1.0 closes the gap the extension had: a login form served inside an iframe never reached a single one of its warnings. It also stops the toolbar promising a guard that was stood down, and brings the display controls to the page where an unknown address is actually read.
Warnings now reach inside frames and shadow roots
Events do not cross a frame boundary. The content script ran in the top document only, so typing into an iframe, pasting into a form in one, or copying out of one raised nothing at all – and a great many login forms are served exactly that way.
A separate frame guard now runs in every frame. It is 12 kB against the 220 kB of the main script: no Vue, no stylesheet, no domain lists, and it asks the background for nothing until a pointerdown or a focusin says somebody is using the frame. It draws no interface of its own, because a dialog inside a 200 pixel advert would be clipped to nothing. What it catches is handed to the top document instead.
A frame the page wrote rather than fetched – about:blank, srcdoc, a blob – is judged as the page that made it, and is still guarded. Links and events inside open shadow roots and custom elements are covered by the same change.
A silenced site keeps its verdict, said in grey
Red on the toolbar is a promise that something is on duty. On a site whose warnings you turned off there is nothing on duty, so the icon and the badge now draw the same verdict in grey. Familiar stays green either way, because silencing warnings does not make a site less known. The tamper alarm keeps its own red: not being warned about a site is not consent to the page tearing the extension out of it.
The controls for an address sit where the address is
Case, highlighting and the spelling of an international name used to be three buttons inside the subdomain list, which is not on screen until a site has visits – so the check page, where somebody reads an address they do not know, could not reach any of them. They now live in the header.
The account name of an email address has a control of its own, in its own row beside the name, with three positions rather than two: a domain reads the same in either case, while the capitals somebody chose are part of how a name was written down, so it is left as typed until you ask otherwise.
Fixed
- A paste into a field inside a closed shadow root is held like any other. The field is invisible to
composedPath(), so the guard used to see the host element, find nothing editable, and let it through. - A paste the page raised itself is no longer taken for one you made.
- A background that could not answer is reported as a check that failed rather than as a site that is safe. A storage error used to read as "this site is fine" and took the warnings and the paste guard down with it.
- A frame is judged by what the browser says it is rather than by the address it sends about itself, and a stale yes is no longer trusted.
- Every recipient of a
mailto:is checked, not only the first, and every address a link wears is read rather than the first one found. - The site boundary is drawn where the ownership changes, so two tenants of one hosting platform are two different sites and a tenant keeps its own subdomains.
- A visit count is coloured by the check it belongs to rather than by the whole verdict, and by the address it belongs to.
- One tab that cannot be drawn no longer ends the sweep, so a closed tab stops leaving every tab after it on a stale verdict.
- Dialog close buttons are named, and focus goes back where it came from. Both intercept dialogs can be answered from the keyboard alone.
- One writer for the settings and one for the visit records. A window left open used to write back the copy it had read, quietly undoing whatever had been changed elsewhere since.
- Answers that were replaced while in flight are dropped instead of overwriting the current one, and the link cache is bounded.
Under the hood
- Every dependency advisory in the development tree is closed except two in
image-size, which has no fixed release. Nothing fromnode_modulesreaches the packaged extension, so no shipped build was ever affected. - Node, pnpm, Vite and the rest are pinned through
flake.lockandpnpm-lock.yaml, and both targets still rebuild byte for byte.
Verify this build
nix build github:ikskoder/visilant/v3.1.0#firefox
sha256sum result/*.xpi # compare with SHA256SUMS belowscripts/verify.sh <downloaded.xpi> compares the copy your browser installed against a fresh build, file by file. Full instructions are in REPRODUCE.md.
Full Changelog: v3.0.0...v3.1.0
v3.0.0
Visilant 3.0.0
The biggest release so far. Knowing a site is no longer a single number, email addresses are checked like any other address, an address that looks like a site you use is called out, and the whole thing now runs on Firefox for Android.
Before you update
- The extension now asks to read your browsing history, so your browser will hold the update until you accept the new permission. It is read once, on your device, and never leaves it. Only a visit count, a first and a last date and a number of active days are kept per site, with no page addresses, no titles and no search terms.
- Nothing you already know turns unfamiliar. Your existing threshold is carried over as it was, and the two new checks stay switched off for a profile that is only arriving here now, because your old records have no dates for them to read.
- Your counts are left alone. On an update the automatic import runs only if the extension has no visits recorded at all, so an existing profile is never rewritten from the browser behind your back.
- English only. Translations were dropped rather than kept half checked, and a profile that used one now falls back to English.
What is new
- Familiarity is three checks, not one. Visits, the number of separate days you have been there, and how long the site has been known, each with its own number and its own on/off switch. Choose whether a site has to pass all of them, any one of them, or a set number, such as two of three. Everything else the extension does follows from this one answer.
- Every warning shows its reasoning. The link tooltip, both confirmation dialogs, the comparison table, the in-page panel and the dashboard all list the facts the verdict was drawn from, and only the checks you have switched on. A switch decides whether they come as plain numbers or as a small table with the mark each one has to clear.
- Email addresses are checked like any other address, plus what is particular to mail: services anyone can sign up to are marked as such, throwaway mailboxes are named as themselves, and Gmail is finally counted at mail.google.com instead of showing a zero that could never have moved.
- Addresses built to deceive are called out on their structure: a familiar name parked in front of an @ (paypal.com@evil.net), a familiar name buried in the middle so the real one comes last (paypal.com.evil.net), an address that is only a number, unusually deep nesting, and a single word written in two alphabets at once.
- Look-alike detection now uses your own history. An address one character away from a site you actually visit is called out, whether it is a foreign letter that looks Latin, a 1 standing in for an l, a plain typo, or the familiar name padded out (paypal-secure.com). Because the comparison is against your history and not a shipped list of brands, nobody can test a fake against it in advance.
- Check anything by hand. A field in the dashboard takes a link, a bare domain, an email address or a QR code image, pasted, dropped in or picked from disk. Selected text and images on a page can be checked from the right-click menu without leaving the page.
- Second-opinion links. Under any checked address there is a collapsed list of links to VirusTotal, urlscan.io and Google Safe Browsing. They are links, not integrations, so nothing is sent until you click one, and the list is yours to edit.
- Hold pastes on sites you do not know, off by default. The first paste is stopped and nothing is inserted until you have looked at the address. Allowing it pastes nothing by itself, you press paste again and the page stops asking.
- Browser history import. It reads real first-visit dates and active days, not just counts, runs on install, can be cancelled and resumed, and lives in Your data in the settings, where you can also erase every visit the extension has recorded.
- Firefox for Android. The extension now actually works there, the popup is usable with a fingertip, dialogs stay on the screen they are shown on, and the settings page no longer offers a history import the phone cannot run.
- The settings page was rebuilt around its sections, with a table of contents, a reset in the corner of every card that puts that section back to how it ships, and a switch that hides the explanatory lines once you know them.
- The toolbar counter is configurable: it can show any check you have switched on, or how many of them the site passes.
- Domain highlighting was reworked to mark only the characters worth a second look, meaning digits, separators and anything outside the Latin alphabet. Latin letters are deliberately left alone, since colouring them reads as a verdict on the ordinary part of an address.
- On a new profile the link check now waits to be asked for: right-click is the default trigger instead of hover. An existing setting is kept as it is.
Fixes
- Erasing your visits no longer takes your own lists with it.
- The language shown on screen is the one reported in the settings.
- A one-off migration is marked done only once it has actually run.
- The popup stays on the screen, the wordmark stays readable, and the in-page panel no longer falls back to a serif font.
Verifiable builds
Every release can now be rebuilt from this repository byte for byte, so you do not have to take the store's word for what it installed:
nix build github:ikskoder/visilant/v3.0.0#firefox
sha256sum result/*.xpi
Compare the result with SHA256SUMS on the release. scripts/verify.sh <downloaded.xpi> goes further and compares the copy your browser installed with a fresh build, file by file. The archives also carry a Sigstore-signed provenance attestation tying them to the commit and workflow that built them, and anyone can rerun the build in a fork through the reproduce workflow. Full instructions are in REPRODUCE.md.
If you want the new checks
Active days and first-visit dates only exist for sites recorded since those fields were added. Run the import once from Your data in the settings, which fills them in for every site your browser still remembers and never lowers a count you already have, and then switch the two new checks on in the Familiarity section.
Full Changelog: v2.0.1...v3.0.0