Spaces:
Running
Running
File size: 6,762 Bytes
30c32c8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
const uid = require('./uid');
// probably good
const generateQuadUid = () => uid() + uid() + uid() + uid();
// idk i just copied this lol
const none = "'none'";
const featurePolicy = {
'accelerometer': none,
'ambient-light-sensor': none,
'battery': none,
'camera': none,
'display-capture': none,
'document-domain': none,
'encrypted-media': none,
'fullscreen': none,
'geolocation': none,
'gyroscope': none,
'magnetometer': none,
'microphone': none,
'midi': none,
'payment': none,
'picture-in-picture': none,
'publickey-credentials-get': none,
'speaker-selection': none,
'usb': none,
'vibrate': none,
'vr': none,
'screen-wake-lock': none,
'web-share': none,
'interest-cohort': none
};
// idk i just copied this lol
const generateAllow = () => Object.entries(featurePolicy)
.map(([name, permission]) => `${name} ${permission}`)
.join('; ');
const createFrame = () => {
const element = document.createElement("iframe");
const frameId = generateQuadUid(); // this is how we differentiate iframe messages from other messages
// hopefully pm doesnt do sonme weird stuff that makes this not work lol
// console.log(frameId); // remove later lol
element.dataset.id = frameId;
element.style.display = "none";
element.setAttribute('aria-hidden', 'true');
// allow modals so people can use alert & stuff
element.sandbox = 'allow-scripts allow-modals';
element.allow = generateAllow();
document.body.append(element);
return element;
};
const origin = window.origin;
/**
* vscode give me autofill
* @param {MessageEvent} event
* @param {HTMLIFrameElement} iframe
* @param {Function} removeHandler
* @returns nothing
*/
const messageHandler = (event, iframe, removeHandler) => new Promise(resolve => {
// console.log(event.origin) // remove later
// this might not work first try cuz idk what event.origin is
// if (event.origin !== iframe.contentDocument.location.origin) return;
// yea event origin is just location
// console.log(event.origin, origin)
// why is event.origin null
// ok we arent checking origin because its just null for some reason
// if (event.origin !== origin) return;
// console.log(event.data.payload)
if (!event.data.payload) return;
// console.log({ payload: event.data.payload.id, iframe: iframe.dataset.id })
if (event.data.payload.id !== iframe.dataset.id) return;
const data = event.data.payload;
window.removeEventListener('message', removeHandler);
try {
const url = iframe.src;
// delete object url
URL.revokeObjectURL(url);
} catch {
// honestly idk how this could fail im just doing this incase
// something stupid happens and people cant use eval anymore
console.warn('failed to revoke url of iframe sandboxed eval');
}
iframe.remove();
// send back data
resolve(data);
});
/**
* generates a string that can be placed into the iframe src
* @param {string} code the code
* @returns the code that can be placed into the eval in the iframe src
*/
const prepareCodeForEval = (code) => {
const escaped = JSON.stringify(code);
// when the html encounters a closing script tag, itll end the script
// so just put a backslash before it and it should be fine
const scriptEscaped = escaped.replaceAll('<\/script>', '<\\/script>');
return scriptEscaped;
}
const generateEvaluateSrc = (code, frame) => {
// this puts some funny stuff in the iframe src
// so that it actually works
const runnerCode = `(async () => {
let result = null;
let success = true;
try {
// techincally eval can also postMessage
// and also modify success & result probably
// but theres no real reason to prevent it
// nor does the user have any reason to do it
result = await eval(${prepareCodeForEval(code)});
} catch (err) {
success = false;
result = err;
}
const parent = window.parent;
const origin = '*';
// console.log(result,success);
console.log(origin);
try {
parent.postMessage({
payload: {
success: success,
value: result,
id: ${JSON.stringify(frame.dataset.id)}
},
}, origin);
} catch (topLevelError) {
// couldnt clone likely
try {
parent.postMessage({
payload: {
success: success,
value: JSON.stringify(result),
id: ${JSON.stringify(frame.dataset.id)}
},
}, origin);
} catch (err) {
// ok we cant stringify it just error
parent.postMessage({
payload: {
success: false,
value: [String(topLevelError), String(err)].join("; "),
id: ${JSON.stringify(frame.dataset.id)}
},
}, origin);
}
}
})();`;
const html = [
'<!DOCTYPE html>',
'<html lang="en-US">',
// the html head isnt required i just think its sily to add and shouldnt affect anything
'<head>',
'<title>the an one of an iframe</title>',
'</head>',
// same story with adding actual elements
'<body>',
'<h1><p>epic computing in progress...</p></h1>',
// removed for being not cool!
// '<img src="https://media.tenor.com/jXQiJUuqfM8AAAAd/type-emoji.gif">',
'<script>',
runnerCode,
'</script>',
'</body>',
'</html>'
].join("\n");
const blob = new Blob([html], { type: 'text/html;charset=UTF-8' });
const url = URL.createObjectURL(blob);
return url;
};
class SandboxRunner {
static execute(code) {
return new Promise(resolve => {
const frame = createFrame();
/**
* please vscode show me the autofill
* @param {MessageEvent} e
*/
const trueHandler = e => {
// this code is weird but we need to remove
// event handler ladter
// console.log(e); // debug
messageHandler(e, frame, trueHandler).then(payload => {
// console.log(payload)
resolve({
success: payload.success,
value: payload.value
});
});
};
window.addEventListener('message', trueHandler);
frame.src = generateEvaluateSrc(code, frame);
});
}
}
module.exports = SandboxRunner;
|