mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-27 14:02:09 +01:00
My Meta library: Sign in with Meta lets frames load any HTTPS page
Meta's login can embed captcha and CDN frames from other sites (Arkose Labs, hCaptcha, reCAPTCHA, fbcdn), so frames inside the sign-in window may now go to any https: address (or about:blank/about:srcdoc); other schemes (http:, data:, blob:, javascript:, file:...) are still stopped. The window itself stays on the Meta/Oculus/Facebook allow-list. Frames can't reach the cookie read or IPC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f5bed08931
commit
be2eda8304
3 files changed
+41
-17
No files matched your search
+14
-3
@@ -32,6 +32,16 @@ function allowedUrl(raw) {
|
||||
return underDomain(u.hostname, ALLOWED);
|
||||
}
|
||||
|
||||
// Frames inside the page: any HTTPS address (Meta's login can embed captcha and CDN
|
||||
// frames from other sites), or an empty frame; never data:, blob:, javascript:, http:,
|
||||
// file: or any other scheme. Frames can't reach the cookie read or IPC (main process only).
|
||||
function allowedSubframeUrl(raw) {
|
||||
if (/^about:(?:blank|srcdoc)$/.test(String(raw))) return true;
|
||||
let u;
|
||||
try { u = new URL(String(raw)); } catch { return false; }
|
||||
return u.protocol === "https:" && !u.username && !u.password;
|
||||
}
|
||||
|
||||
const onOculus = (raw) => { try { return underDomain(new URL(raw).hostname, ["oculus.com"]); } catch { return false; } };
|
||||
|
||||
// A cheap first look, so a signed-out or app value is never sent anywhere; the server's
|
||||
@@ -147,10 +157,11 @@ function createMetaSignIn({ BrowserWindow, session, shell, connect, cancel, pare
|
||||
|
||||
// Navigation stays on Meta's login sites. A link elsewhere opens in the system
|
||||
// browser; a redirect elsewhere is just stopped (it may carry sign-in data).
|
||||
// Every frame too: an iframe can't wander off the list either (it is just stopped).
|
||||
// Every frame: the window itself stays on the list; frames inside it may load any
|
||||
// HTTPS page (captchas), and one going anywhere else is just stopped.
|
||||
wc.on("will-navigate", (e, target) => { if (!allowedUrl(target)) { e.preventDefault(); openOutside(target); } });
|
||||
wc.on("will-frame-navigate", (e) => {
|
||||
if (allowedUrl(e.url) || (!e.isMainFrame && /^about:(?:blank|srcdoc)$/.test(String(e.url)))) return;
|
||||
if (e.isMainFrame ? allowedUrl(e.url) : allowedSubframeUrl(e.url)) return;
|
||||
e.preventDefault();
|
||||
});
|
||||
wc.on("will-redirect", (e, target) => { if (!allowedUrl(target)) e.preventDefault(); });
|
||||
@@ -194,4 +205,4 @@ function createMetaSignIn({ BrowserWindow, session, shell, connect, cancel, pare
|
||||
return { signIn, signOut, isOpen: () => !!active };
|
||||
}
|
||||
|
||||
module.exports = { createMetaSignIn, allowedUrl, looksLikeUserToken, scrub, PARTITION, LOGIN_URL, COOKIE, ALLOWED };
|
||||
module.exports = { createMetaSignIn, allowedUrl, allowedSubframeUrl, looksLikeUserToken, scrub, PARTITION, LOGIN_URL, COOKIE, ALLOWED };
|
||||
@@ -288,19 +288,27 @@ function frameNav(wc, url, isMainFrame) {
|
||||
return prevented;
|
||||
}
|
||||
|
||||
test("every frame stays on Meta's sites; a frame going elsewhere is stopped, not opened", async () => {
|
||||
test("the window stays on Meta's sites; frames inside it may load any HTTPS page, nothing else", async () => {
|
||||
const f = fakes();
|
||||
f.flow.signIn();
|
||||
const wc = f.windows[0].webContents;
|
||||
assert.strictEqual(frameNav(wc, "https://evil.example/phish", false), true);
|
||||
assert.strictEqual(frameNav(wc, "http://www.facebook.com/plugin", false), true);
|
||||
assert.strictEqual(frameNav(wc, "https://www.facebook.com/plugin", false), false);
|
||||
assert.strictEqual(frameNav(wc, "about:blank", false), false);
|
||||
assert.strictEqual(frameNav(wc, "about:srcdoc", false), false);
|
||||
assert.strictEqual(frameNav(wc, "about:blank", true), true); // the window itself: Meta's sites only
|
||||
// Frames: captcha and CDN frames from other sites load; other schemes are stopped.
|
||||
for (const ok of ["https://client-api.arkoselabs.com/fc/gc/", "https://newassets.hcaptcha.com/captcha/v1/x",
|
||||
"https://www.google.com/recaptcha/api2/anchor", "https://static.xx.fbcdn.net/x",
|
||||
"https://www.facebook.com/plugin", "about:blank", "about:srcdoc"]) {
|
||||
assert.strictEqual(frameNav(wc, ok, false), false, ok);
|
||||
}
|
||||
for (const bad of ["http://www.facebook.com/plugin", "data:text/html,<script>x</script>", "blob:https://evil.example/1",
|
||||
"javascript:alert(1)", "file:///etc/passwd", "ftp://example.com/", "https://u:p@evil.example/",
|
||||
"chrome://settings", "not a url"]) {
|
||||
assert.strictEqual(frameNav(wc, bad, false), true, bad);
|
||||
}
|
||||
// The window itself: Meta's sites only.
|
||||
assert.strictEqual(frameNav(wc, "about:blank", true), true);
|
||||
assert.strictEqual(frameNav(wc, "https://evil.example/", true), true);
|
||||
assert.strictEqual(frameNav(wc, "https://client-api.arkoselabs.com/", true), true);
|
||||
assert.strictEqual(frameNav(wc, "https://auth.meta.com/login/", true), false);
|
||||
assert.deepStrictEqual(f.opened, []);
|
||||
assert.deepStrictEqual(f.opened, []); // stopped, never opened outside
|
||||
f.windows[0].destroy();
|
||||
});
|
||||
|
||||
|
||||
+11
-6
@@ -45,9 +45,14 @@ closes the window. The window:
|
||||
or check (camera, notifications…) refused;
|
||||
- only goes to `https://` pages on `oculus.com`, `meta.com` and `facebook.com`
|
||||
(and their subdomains). A link anywhere else opens in your system browser; a
|
||||
redirect anywhere else is stopped. Frames inside the page are held to the
|
||||
same list (plus `about:blank`), and one going elsewhere is just stopped.
|
||||
Pop-ups on those sites open in the same window;
|
||||
redirect anywhere else is stopped. Pop-ups on those sites open in the same
|
||||
window. Frames inside the page may load any `https://` page (Meta's login
|
||||
can embed captcha and CDN frames from other sites, such as Arkose Labs,
|
||||
hCaptcha, reCAPTCHA or fbcdn) or an empty frame (`about:blank`,
|
||||
`about:srcdoc`); a frame going to any other scheme (`http:`, `data:`,
|
||||
`blob:`, `javascript:`, `file:`…) is just stopped. Frames can't reach the
|
||||
token: the cookie is read by Frame Control's main process, and the window
|
||||
has no IPC;
|
||||
- sends a cookie value on only if it looks like a user token (`OC` or `FRL`,
|
||||
mixed case, no `%`, `|` or `:`), and each value once. If the server refuses
|
||||
it, the window stays open; close it to see why;
|
||||
@@ -306,7 +311,8 @@ and cookies (`app/test/meta-signin.test.js`): the window's options (its own
|
||||
partition, sandbox, context isolation, no Node, no preload) and Meta's login
|
||||
address, no developer tools; permission requests and checks refused; the
|
||||
navigation allow-list (links elsewhere open in the system browser, redirects
|
||||
and frames going elsewhere are stopped, pop-ups never become windows); a
|
||||
elsewhere are stopped, pop-ups never become windows); frames allowed any
|
||||
HTTPS page but no other scheme, the window itself only Meta's sites; a
|
||||
page's beforeunload not keeping the window open; the cookie read only from
|
||||
`persist:meta-signin` and only for `oculus.com`, even when the default
|
||||
session and another partition hold one; signed-out values ignored; a value
|
||||
@@ -331,8 +337,7 @@ working token.
|
||||
Meta account was used, and nothing was sent to Meta): whether Meta's login
|
||||
still goes only through `oculus.com`, `meta.com` and `facebook.com`, whether
|
||||
it uses pop-ups (they open in the same window here, which would break a
|
||||
pop-up that reports back to its opener), whether it needs frames from other
|
||||
sites such as a captcha (those are stopped), whether `oc_ac_at` is still set on
|
||||
pop-up that reports back to its opener), whether `oc_ac_at` is still set on
|
||||
`oculus.com` after login, and whether Meta accepts the window (Frame Control
|
||||
removes Electron's name from the user agent as a precaution). A real Meta token and Meta's real answers (none was
|
||||
available; nothing was sent to Meta), real downloads and their redirects,
|
||||
|
||||
Reference in new issue
Block a user