lets-blade/blade

ctx.html injects scripts into my HTML

janligudzinski opened this issue · 6 comments

  • System Version (e.g. Mac Os 10.14.3): Arch Linux
  • Build tools (e.g. maven/gradle): Maven
  • JDK Version (e.g. e.g 2.0.8-R1): 11
  • Database Version: irrelevant

Describe the bug

My controller class loads a static index page:
indexPage = Files.readString(Paths.get("src/main/resources/static/index.html"));
Then, on GET requests for /, it sends it:

@GetRoute("/")
public void index(RouteContext ctx) {
    ctx.html(indexPage);
}

My page has no <script> tags, but when loaded in the browser from my Blade app, it has two of them between the head and body tags:

window[Symbol.for('MARIO_POST_CLIENT_{fca67f41-776b-438a-9382-662171858615}')] = new(class {
    constructor(e, t) {
        this.name = e, this.destination = t, this.serverListeners = {}, this.bgRequestsListeners = {}, this.bgEventsListeners = {}, window.addEventListener("message", e => {
            const t = e.data,
                n = !(t.destination && t.destination === this.name),
                s = !t.event;
            if (!n && !s)
                if ("MARIO_POST_SERVER__BG_RESPONSE" === t.event) {
                    const e = t.args;
                    if (this.hasBgRequestListener(e.requestId)) {
                        try {
                            this.bgRequestsListeners[e.requestId](e.response)
                        } catch (e) {}
                        delete this.bgRequestsListeners[e.requestId]
                    }
                } else if ("MARIO_POST_SERVER__BG_EVENT" === t.event) {
                const e = t.args;
                if (this.hasBgEventListener(e.event)) try {
                    this.bgEventsListeners[t.id](e.payload)
                } catch (e) {}
            } else if (this.hasServerListener(t.event)) try {
                this.serverListeners[t.event](t.args)
            } catch (e) {}
        })
    }
    emitToServer(e, t) {
        const n = this.generateUIID(),
            s = {
                args: t,
                destination: this.destination,
                event: e,
                id: n
            };
        return window.postMessage(s, location.origin), n
    }
    emitToBg(e, t) {
        const n = this.generateUIID(),
            s = {
                bgEventName: e,
                requestId: n,
                args: t
            };
        return this.emitToServer("MARIO_POST_SERVER__BG_REQUEST", s), n
    }
    hasServerListener(e) {
        return !!this.serverListeners[e]
    }
    hasBgRequestListener(e) {
        return !!this.bgRequestsListeners[e]
    }
    hasBgEventListener(e) {
        return !!this.bgEventsListeners[e]
    }
    fromServerEvent(e, t) {
        this.serverListeners[e] = t
    }
    fromBgEvent(e, t) {
        this.bgEventsListeners[e] = t
    }
    fromBgResponse(e, t) {
        this.bgRequestsListeners[e] = t
    }
    generateUIID() {
        return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (function(e) {
            const t = 16 * Math.random() | 0;
            return ("x" === e ? t : 3 & t | 8).toString(16)
        }))
    }
})('MARIO_POST_CLIENT_{fca67f41-776b-438a-9382-662171858615}', 'MARIO_POST_SERVER_{fca67f41-776b-438a-9382-662171858615}')
const hideMyLocation = new(class {
    constructor(t) {
        this.clientKey = t, this.watchIDs = {}, this.client = window[Symbol.for(t)];
        const e = navigator.geolocation.getCurrentPosition,
            n = navigator.geolocation.watchPosition,
            o = navigator.geolocation.clearWatch,
            r = this;
        navigator.geolocation.getCurrentPosition = function(t, n, o) {
            r.handle(e, "GET", t, n, o)
        }, navigator.geolocation.watchPosition = function(t, e, o) {
            return r.handle(n, "WATCH", t, e, o)
        }, navigator.geolocation.clearWatch = function(t) {
            if (-1 === t) return;
            const e = r.watchIDs[t];
            return delete r.watchIDs[t], o.apply(this, [e])
        }
    }
    handle(t, e, n, o, r) {
        const i = this.client.emitToBg("HIDE_MY_LOCATION__GET_LOCATION");
        let a = this.getRandomInt(0, 1e5);
        if (this.client.fromBgResponse(i, i => {
                if (i.enabled)
                    if ("SUCCESS" === i.status) {
                        const t = this.map(i);
                        n(t)
                    } else {
                        const t = this.errorObj();
                        o(t), a = -1
                    }
                else {
                    const i = [n, o, r],
                        c = t.apply(navigator.geolocation, i);
                    "WATCH" === e && (this.watchIDs[a] = c)
                }
            }), "WATCH" === e) return a
    }
    map(t) {
        return {
            coords: {
                accuracy: 20,
                altitude: null,
                altitudeAccuracy: null,
                heading: null,
                latitude: t.latitude,
                longitude: t.longitude,
                speed: null
            },
            timestamp: Date.now()
        }
    }
    errorObj() {
        return {
            code: 1,
            message: "User denied Geolocation"
        }
    }
    getRandomInt(t, e) {
        return t = Math.ceil(t), e = Math.floor(e), Math.floor(Math.random() * (e - t + 1)) + t
    }
})('MARIO_POST_CLIENT_{fca67f41-776b-438a-9382-662171858615}')

Explain this. Why does Blade feel the need to inject geolocation code or that other thing into pages?

To Reproduce

Steps to reproduce the behavior:

  1. Operating system and its version: Arch Linux
  2. Build tools (e.g. maven or gradle): maven
  3. JDK version and blade version(e.g 2.0.8-R1): 11, 2.0.15.BETA
  4. See error

Retrieve an HTML page from a Blade app, either as a static file or as an HTML response.
Open the inspector and observe as two suspicious-looking <script> tags appear out of nowhere.

Expected behavior:

I expected my page to be served as is, without any tampering. If this is some kind of development-only feature that is meant to ease debugging, I'd expect it to be documented and possible to opt out of.

Screenshots:

HTML page served as such with ctx.html:
Served as such
Page served from Blade.of().addStatics:
Served as static

Nevermind, Webpack is the culprit. Sorry.

Hi @oreganoli

I'm so sorry to re-open this thread. But it comes with a potential warning on privacy and malware.

There's nothing wrong with your code or webpack, or even the application itself. I noticed the same on my code... decided to check if it happened on any other website (stackoverflow... or my facebook account) ... and there was something injecting this code in any site I visited, that's when I got very concerned about.

Analyzing it, it sends to some server data about geolocation, UUID and other sensitive information, When I googled it, this is the only post where anyone has noticed about that injection.

I guess you have UrbanVPN extension (or any other free VPN, Proxy, etc.) In your browser or your OS. At the moment I disabled the VPN extension, code stopped being injected on every site.

Please, be very careful when installing these kind of add-ons / software. I've been using that extension for over a year and now i'm concerned about my personal/banking data. If you still have it on your OS, unsintall everything related to it because it's a potential security breach.

Hi, I have the same code injection. I have Urban shield extension already installed (Firefox).
Disabling this extension, the code code injection has disappeared.

Indeed. It's not about blade. But about Urban VPN addon.

As mentioned above, I noticed the same code injection and yes it was the Urban VPN extension. If you still want to use the VPN extension, there is an option on chrome where you can disable the extension and select to change site data only when you click on the extension. So whenever you need to use the VPN just enable it and allow the extension to work on a specific site. The default option is set to allow on all sites.