domanchi / bookmarklets

Collection of bookmarklets for speedier execution
GNU General Public License v3.0
0 stars 0 forks source link

Get `jukebox` working again #1

Open domanchi opened 3 years ago

domanchi commented 3 years ago

Jukebox recently launched a v3.0, and as such, we need to modify our bookmarklet to be compatible with that API. This may also be a good time to re-evaluate the current solution, and see whether it meets our needs.

domanchi commented 3 years ago

OK, bizarro. I got a working PoC, but then out of nowhere, it stopped working. After digging into it, I'm hypothesizing that it should never have worked in the first place?!?

tl;dr: I need an established webpage as a proxy, and as such, this is going back on the backlog for another time period.

(Was Working) Proof of Concept

function xfetch(...args) {
    const popup = window.open(
        "http://example.org",
        "_blank",
        "toolbar=no,titlebar=no,status=no,menubar=no,resizable=no,width=1,height=1",
    );

    //  Establish a MessageChannel so that the popup can communicate back to close accordingly.
    //  It will be communicating on port1, and we will be listening on port2.
    const channel = new MessageChannel();

    //  We add a delay here, so that the window has time to open, before we send
    //  messages to it.
    return delay(200)
        .then(() => {
            popup.addEventListener("message", async (event) => {
                const [url, options] = event.data;
                let response = await fetch(url, options);
                if (!response.ok) {
                    event.ports[0].postMessage([response.status, response.statusText]);
                    return;
                }

                event.ports[0].postMessage([response.status]);
            }
        })
        .then(() => {
                return new Promise((resolve, reject) => {
                    //  We need to establish the message listener here, since we want
                    //  to return the resolved promise.
                    channel.port2.onmessage = ({data}) => {
                        if (data[0] == 200) {
                            resolve();
                        } else {
                            reject();
                        }
                    };

                    //  Only after we setup the message listener, to we trigger the actual
                    //  initial message.
                    popup.postMessage(args, "*", [channel.port1]);
                });
        })
        .then(() => {
            popup.close();
        });
}

xfetch("<url>", {method: "PUT"});

And it was working. Until quite suddenly, I started getting this error:

SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a cross-origin frame.

I even changed the domain to another local server, and it still didn't work. I have no clue why.

SOP kinda makes sense, but when it was working, the fetch command executed in the popup obeyed the parent window's SOP. That is, if I tried to fetch("http://example.org") and exfiltrate it back to the parent, it would fail SOP since the browser recognized that the command was actually being called by the parent. This makes sense to me.

But blocking the eventListener altogether? Man, I gotta find another way now.

Proposal

If I can't add eventlisteners to domains I don't control, then I need to pre-establish the event listener on a webpage I control, then hard-code that webpage as the proxy for the window.open target. This means that this will be blocked until I throw some webpage on that is publicly accessible, that is HTTP (my own version of example.org).

And thaaaat's going to have to wait.