mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Combined webxdc patches (#2465)
* re-applying patch from @Hocuri from #2458 * apply sandbox isolation patch from @WofWca * wait for gc as suggested by @WofWca, @Simon-Laux and others * Update res/raw/sandboxed_iframe_rtcpeerconnection_check.html Co-authored-by: WofWca <wofwca@protonmail.com> * Update res/raw/sandboxed_iframe_rtcpeerconnection_check.html Co-authored-by: WofWca <wofwca@protonmail.com> * improve progress bar, inspired by @Simon-Laux and @WofWca * comment about the gist of setNetworkAvailable() * unify url-loading; this cleans up things and makes adding CSP more straight-forward --------- Co-authored-by: WofWca <wofwca@protonmail.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
window.addEventListener("message", e => {
|
||||
// Currently this never happens because we don't load other scripts that could `postMessage`
|
||||
// until all checks have passed, but let's play it safe.
|
||||
if (
|
||||
event.origin !== location.origin
|
||||
|| event.source !== parent
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data === "performCheck") {
|
||||
let rtcpcCreationFailed = false;
|
||||
try {
|
||||
new RTCPeerConnection();
|
||||
} catch (e) {
|
||||
rtcpcCreationFailed = true;
|
||||
}
|
||||
|
||||
parent.postMessage({ msgType: "result", rtcpcCreationFailed }, location.origin);
|
||||
}
|
||||
});
|
||||
|
||||
parent.postMessage({ msgType: "ready" }, location.origin);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,145 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.iframe-container {
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
position: relative;
|
||||
}
|
||||
.iframe-container iframe {
|
||||
border: 0;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#progress {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="loading">
|
||||
<progress id="progress" max="500" value="0"></progress>
|
||||
</div>
|
||||
<div class="iframe-container">
|
||||
<iframe id="frame"></iframe>
|
||||
</div>
|
||||
<iframe
|
||||
id="test-isolated-sandbox-context"
|
||||
sandbox="allow-scripts"
|
||||
src="./sandboxed_iframe_rtcpeerconnection_check_5965668501706.html"
|
||||
style="display: none"
|
||||
></iframe>
|
||||
<script>
|
||||
const thisUnchangable = (async () => {
|
||||
const connections = [];
|
||||
const loadingProgress = document.getElementById("progress");
|
||||
const loadingDiv = document.getElementById("loading");
|
||||
const iframe = document.getElementById("frame");
|
||||
const isolatedContextTest = document.getElementById(
|
||||
"test-isolated-sandbox-context"
|
||||
);
|
||||
|
||||
const cert = {
|
||||
certificates: [
|
||||
await RTCPeerConnection.generateCertificate({
|
||||
name: "ECDSA",
|
||||
namedCurve: "P-256",
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
console.log("WEBRTC-WG: allocating");
|
||||
loadingProgress.value = 0;
|
||||
while (connections.length < 500) {
|
||||
try {
|
||||
connections.push(new RTCPeerConnection(cert));
|
||||
if (connections.length%50 == 0) {
|
||||
loadingProgress.value = connections.length;
|
||||
await new Promise((res) => setTimeout(res));
|
||||
}
|
||||
} catch (error) {
|
||||
loadingProgress.value++;
|
||||
new Array(1024*1024).fill(0);
|
||||
await new Promise((res) => setTimeout(res, 500));
|
||||
console.log("WEBRTC-WG: waiting for gc");
|
||||
}
|
||||
}
|
||||
console.log("WEBRTC-WG: done");
|
||||
|
||||
try {
|
||||
connections.push(new RTCPeerConnection());
|
||||
console.log("Error: was able to create more than 500 connections");
|
||||
loadingDiv.innerText =
|
||||
"Error: was not able to block webrtc ERROR_A";
|
||||
} catch (error) {
|
||||
/** @type {Promise<boolean>} */
|
||||
const sandboxedIframeCheckIsGoodPromise = new Promise(resolve => {
|
||||
/** @type {HTMLIFrameElement} */
|
||||
const sandboxedIframe = document.getElementById("test-isolated-sandbox-context");
|
||||
const askIframeToPerformCheck = () => {
|
||||
// Why `"*"`? See the comment below.
|
||||
sandboxedIframe.contentWindow.postMessage("performCheck", "*");
|
||||
};
|
||||
/** @type {(e: MessageEvent) => void} */
|
||||
const messageListener = (e) => {
|
||||
// Checking `event.origin !== location.origin` just in case would be safer,
|
||||
// but `sandbox`ed iframes seem to send messages with `origin` set to `null`,
|
||||
// so we skip the check, relying on the fact that we don't actually load any
|
||||
// untrusted scripts that could `postMessage` before this check is completed.
|
||||
// And we can't just add `sandbox="allow-same-origin` because it could turn off
|
||||
// process-isolation:
|
||||
// https://chromium-review.googlesource.com/c/chromium/src/+/3416475
|
||||
if (event.source !== sandboxedIframe.contentWindow) {
|
||||
return;
|
||||
}
|
||||
switch (event.data.msgType) {
|
||||
case "ready": {
|
||||
askIframeToPerformCheck();
|
||||
break;
|
||||
}
|
||||
case "result": {
|
||||
resolve(event.data.rtcpcCreationFailed);
|
||||
|
||||
sandboxedIframe.remove();
|
||||
window.removeEventListener("message", messageListener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
window.addEventListener("message", messageListener);
|
||||
askIframeToPerformCheck();
|
||||
});
|
||||
|
||||
if (await sandboxedIframeCheckIsGoodPromise !== true) {
|
||||
console.log(
|
||||
"Error: was able to create more than 500 connections, iframe is probably isolated"
|
||||
);
|
||||
loadingDiv.innerText =
|
||||
"Error: was not able to block webrtc ERROR_C";
|
||||
} else {
|
||||
loadingDiv.innerHTML = "";
|
||||
iframe.src = "index.html";
|
||||
iframe.contentWindow.webxdc_internal = window.webxdc_internal;
|
||||
iframe.contentWindow.webxdc = window.webxdc;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
len: () => {
|
||||
return connections.length;
|
||||
},
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcEventDelegate {
|
||||
@@ -127,9 +128,10 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
webSettings.setAllowUniversalAccessFromFileURLs(false);
|
||||
webSettings.setDatabaseEnabled(true);
|
||||
webSettings.setDomStorageEnabled(true);
|
||||
webView.setNetworkAvailable(internetAccess); // this does not block network but sets `window.navigator.isOnline` in js land
|
||||
webView.addJavascriptInterface(new InternalJSApi(), "InternalJSApi");
|
||||
|
||||
webView.loadUrl(this.baseURL + "/index.html");
|
||||
webView.loadUrl(this.baseURL + "/webxdc_bootstrap324567869.html");
|
||||
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
final DcChat chat = dcContext.getChat(dcAppMsg.getChatId());
|
||||
@@ -170,10 +172,6 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
|
||||
@Override
|
||||
protected boolean openOnlineUrl(String url) {
|
||||
if (url.startsWith(baseURL +"/")) {
|
||||
// internal page, continue loading in the WebView
|
||||
return false;
|
||||
}
|
||||
if (url.startsWith("mailto:")) {
|
||||
return super.openOnlineUrl(url);
|
||||
}
|
||||
@@ -193,6 +191,12 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
if (path.equalsIgnoreCase("/webxdc.js")) {
|
||||
InputStream targetStream = getResources().openRawResource(R.raw.webxdc);
|
||||
return new WebResourceResponse("text/javascript", "UTF-8", targetStream);
|
||||
} else if (path.equalsIgnoreCase("/webxdc_bootstrap324567869.html")) {
|
||||
InputStream targetStream = getResources().openRawResource(R.raw.webxdc_wrapper);
|
||||
return new WebResourceResponse("text/html", "UTF-8", targetStream);
|
||||
} else if (path.equalsIgnoreCase("/sandboxed_iframe_rtcpeerconnection_check_5965668501706.html")) {
|
||||
InputStream targetStream = getResources().openRawResource(R.raw.sandboxed_iframe_rtcpeerconnection_check);
|
||||
return new WebResourceResponse("text/html", "UTF-8", targetStream);
|
||||
} else {
|
||||
byte[] blob = this.dcAppMsg.getWebxdcBlob(path);
|
||||
if (blob == null) {
|
||||
|
||||
Reference in New Issue
Block a user