Is EXIF Viewer Classic safe?
EXIF Viewer Classic lets webpages trigger cross-origin image EXIF reads and displays the parsed metadata in the page.
Its all-sites content script watches image mouseovers and asks the service worker to fetch the image URL, parse EXIF/GPS metadata, and inject results into the page DOM. It also retrieves remote notification messages from dsnetx.web.app with a Bitbucket fallback and renders their HTML on matched pages.
Who publishes itRodrigue - 7 other listings from the same operator, 4 of them carrying a finding
Rodrigue - 7 other listings from the same operator, 4 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
2 other listings published from this account, 20k+ users between them. 2 of them carry a finding.
Same operator - 5 listings
Published under a different store account, but sharing the registered address, contact or declared legal entity this one gave the store.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote message JSON injects HTML into visited pages
On any HTTP/HTTPS page, EXIF Viewer Classic waits a few seconds, fetches a message file from dsnetx.web.app, and can add the returned HTML to the page.
Dynamic analysis observed a 200 OK GET, HTML inserted via a bootstrap notification.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You open a normal website where the extension's content script is allowed to run.
The manifest registers the content script for both HTTP and HTTPS pages.
The extension checks a remote message file and can add that message's HTML to the page.
The message is shown only when the remote configuration marks it as enabled and matching this browser version.
On every browser startup
The remote message check is scheduled once shortly after the content script finishes page setup.
200 OK observed during dynamic analysis; no request body was recorded for this GET.
- Enable switchshow: true
This decides whether a remote message is eligible to appear on pages you visit.
- Version and browser matchExifClassic-CH-v301@chrome
This lets the remote file limit a message to a particular extension version or browser.
- Message HTML<form id='exif-phish'>remote html</form>
This is the remote content that can be added to the webpage you are viewing.
- Auto-close delay5000 milliseconds
This controls how long the inserted notification stays visible on the page.
- Message IDstatic-test-301
This is stored locally so the same non-repeating message is not shown again immediately.
The shipped code fetches remote msg.json and inserts msg as HTML
Remote message hosts and fetch path
scripts/index.jsconst x = d.APP_ID, M = globalThis.CONFIGS && globalThis.CONFIGS.TEST, V = ["https://dsnetx.web.app/apps/firelinks/msg.json", "https://dsnet.bitbucket.io/apps/ext/msg/msg.json"];let F = !1;const $e = 7, Ee = 22;let u = 0;function C(e) { const t = `${V[u]}?ref=${x}&r=${Math.random()}`; fetch(t, { cache: "no-store" }).then(i => i.json()).then(i => { E(x, "Notif", "Loaded"), I(i) ? (u++, p.log(`Failed, retrying - ${u}`), u < V.length && setTimeout(C, 100)) : (u = 0, Ie(i)) }).catch(i => { console.log("error in ", i), u++, p.log(`Failed, retrying - ${u}`), u < V.length ? setTimeout(C, 100) : u = 0 })}Remote HTML passed into notification rendering
scripts/index.jsfunction ge({ message: e, delay: t}) { const i = G(), o = $(`<div id="notifyVersionView" style="z-index: 99999; margin: 0; background: #F2B600; border: 1px solid #CC9900; color: #4F3C00; padding: 8px; font: 14px Arial, sans-serif; position: absolute; left:0; right: 0; "><div style="float: left; "> <h6 style="padding-right:10px">${i.name}</h6>${e}</div><br clear="both"/></div>'}`); $(document.body).prepend(o), o.slideUp(1).slideDown(800).delay(t || 5e3).slideUp(800, q)}function A(e, t) { const i = !I(e) && e.hasOwnProperty("typeNotif") ? e.typeNotif : "WEBUI"; i === "NATIVE" || (i === "WEBALERT" ? (alert(`${e.title}: ${e.msg}`), F = !0) : Se(e))}function W(e, t) { v ? v.notify(e, t) : $.notify(e, t)}function Se(e, t) { if (!v && !$.notify.getStyle("bootstrap-html")) { const i = $.notify.getStyle("bootstrap"); i.html = `<div><span data-notify-html></span></div>`, $.notify.addStyle("bootstrap-html", i) } try { if (e.autoclose) { const i = { autoHide: !0, autoHideDelay: e.autoclose, className: e.type, style: "bootstrap-html", type: e.type }; W(e.msg, i) } else { const i = { autoHide: !1, className: e.type, style: "bootstrap-html", type: e.type }; W(e.msg, i), $("span[data-notify-text]").css({ "font-size": "medium", "font-weight": "400" }) } E(x, "Notif-FinalMessageSent", e.msgid) } catch (i) { console.log(i), E(x, "Notif-Failed-vnotify", e.msgid) }}Five-second scheduler
scripts/index.jsfunction Ae() { try { const e = { notify: (t, i) => { ge({ message: t, delay: i.autoHideDelay }) } }; Ve(j, y.label.Content, e), C() } catch (e) { y.gaEventALV(`${y.label.Content}-LoadNitfy`, y.vals.Fail, e) }}function Ce() { return j.load().then(() => { oe(), te(), ve() }).finally(() => { setTimeout(Ae, 5e3) })}- dsnetx.web.app
Primary host for the remote msg.json file observed returning 200 OK.
- dsnet.bitbucket.io
Fallback host used by the same fetch loop if the primary response is empty or fails.
What it can do
Permissions this extension asks for, as declared in version 3.0.1. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every site you visit
http://*/*
Read and change your data on every secure site you visit
https://*/*
Store an unlimited amount of data in your browser
unlimitedStorage
Store data in your browser
storage
Where it sends data
Destinations our analysis observed EXIF Viewer Classic contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- dsnet.bitbucket.io
EXIF Viewer Classic sends data to dsnet.bitbucket.io. 2 other extensions we have analyzed send data here.
- dsnetx.web.app
EXIF Viewer Classic sends data to dsnetx.web.app. 2 other extensions we have analyzed send data here.