Don't let browsers render untrusted HTML or SVG outside of the iframe. Do let them fetch JS and such.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4297 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
cc92d0e316
commit
c807e21c6b
92
core/core.js
92
core/core.js
@ -8,11 +8,19 @@ let gProcesses = {};
|
||||
let gStatsTimer = false;
|
||||
|
||||
const k_mime_types = {
|
||||
'json': 'text/json',
|
||||
'js': 'text/javascript',
|
||||
'html': 'text/html',
|
||||
'css': 'text/css',
|
||||
'html': 'text/html',
|
||||
'js': 'text/javascript',
|
||||
'json': 'text/json',
|
||||
'map': 'application/json',
|
||||
'svg': 'image/svg+xml',
|
||||
};
|
||||
|
||||
const k_mime_type_is_trusted = {
|
||||
'application/json': true,
|
||||
'text/css': true,
|
||||
'text/javascript': true,
|
||||
'text/json': true,
|
||||
};
|
||||
|
||||
const k_magic_bytes = [
|
||||
@ -30,6 +38,15 @@ const k_magic_bytes = [
|
||||
{bytes: [null, null, null, null, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32], type: 'video/mp4'},
|
||||
];
|
||||
|
||||
let k_static_files = [
|
||||
{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
|
||||
{uri: '/style.css', type: 'text/css; charset=UTF-8'},
|
||||
{uri: '/favicon.png', type: 'image/png'},
|
||||
{uri: '/client.js', type: 'text/javascript; charset=UTF-8'},
|
||||
{uri: '/tfrpc.js', type: 'text/javascript; charset=UTF-8', headers: {'Access-Control-Allow-Origin': 'null'}},
|
||||
{uri: '/robots.txt', type: 'text/plain; charset=UTF-8'},
|
||||
];
|
||||
|
||||
const k_global_settings = {
|
||||
index: {
|
||||
type: 'string',
|
||||
@ -459,15 +476,6 @@ function setGlobalSettings(settings) {
|
||||
}
|
||||
}
|
||||
|
||||
let kStaticFiles = [
|
||||
{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
|
||||
{uri: '/style.css', type: 'text/css; charset=UTF-8'},
|
||||
{uri: '/favicon.png', type: 'image/png'},
|
||||
{uri: '/client.js', type: 'text/javascript; charset=UTF-8'},
|
||||
{uri: '/tfrpc.js', type: 'text/javascript; charset=UTF-8', headers: {'Access-Control-Allow-Origin': 'null'}},
|
||||
{uri: '/robots.txt', type: 'text/plain; charset=UTF-8'},
|
||||
];
|
||||
|
||||
function startsWithBytes(data, bytes) {
|
||||
if (data.byteLength >= bytes.length) {
|
||||
let dataBytes = new Uint8Array(data.slice(0, bytes.length));
|
||||
@ -481,10 +489,10 @@ function startsWithBytes(data, bytes) {
|
||||
}
|
||||
|
||||
async function staticFileHandler(request, response, blobId, uri) {
|
||||
for (let i in kStaticFiles) {
|
||||
if (uri === kStaticFiles[i].uri) {
|
||||
let path = kStaticFiles[i].path || uri.substring(1);
|
||||
let type = kStaticFiles[i].type || guessType(path);
|
||||
for (let i in k_static_files) {
|
||||
if (uri === k_static_files[i].uri) {
|
||||
let path = k_static_files[i].path || uri.substring(1);
|
||||
let type = k_static_files[i].type || guessTypeFromName(path);
|
||||
|
||||
let stat = await File.stat('core/' + path);
|
||||
let id = `${stat.mtime}_${stat.size}`;
|
||||
@ -500,7 +508,7 @@ async function staticFileHandler(request, response, blobId, uri) {
|
||||
'Content-Length': data.byteLength,
|
||||
'etag': '"' + id + '"',
|
||||
},
|
||||
kStaticFiles[i].headers || {}));
|
||||
k_static_files[i].headers || {}));
|
||||
response.end(data);
|
||||
}
|
||||
return;
|
||||
@ -552,17 +560,29 @@ async function wellKnownHandler(request, response, path) {
|
||||
}
|
||||
}
|
||||
|
||||
function sendData(response, data, type, headers) {
|
||||
if (data) {
|
||||
function guessTypeFromName(path) {
|
||||
let extension = path.split('.').pop();
|
||||
return k_mime_types[extension];
|
||||
}
|
||||
|
||||
function guessTypeFromMagicBytes(data) {
|
||||
for (let magic of k_magic_bytes) {
|
||||
if (startsWithBytes(data, magic.bytes)) {
|
||||
response.writeHead(200, Object.assign({"Content-Type": magic.content_type, "Content-Length": data.byteLength}, headers || {}));
|
||||
response.end(data);
|
||||
return;
|
||||
return magic.content_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.writeHead(200, Object.assign({"Content-Type": type || "application/binary", "Content-Length": data.byteLength}, headers || {}));
|
||||
function guessTypeUntrusted(path, data) {
|
||||
let type = guessTypeFromMagicBytes(data) || guessTypeFromName(path);
|
||||
if (k_mime_type_is_trusted[type]) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
function sendData(response, data, type, headers) {
|
||||
if (data) {
|
||||
response.writeHead(200, Object.assign({"Content-Type": type || guessTypeFromMagicBytes(data) || "application/binary", "Content-Length": data.byteLength}, headers || {}));
|
||||
response.end(data);
|
||||
} else {
|
||||
response.writeHead(404, Object.assign({"Content-Type": "text/plain; charset=utf-8", "Content-Length": "File not found".length}, headers || {}));
|
||||
@ -580,35 +600,24 @@ async function getBlobOrContent(id) {
|
||||
}
|
||||
}
|
||||
|
||||
function guessType(path) {
|
||||
const k_extension_to_type = {
|
||||
'css': 'text/css',
|
||||
'html': 'text/html',
|
||||
'js': 'text/javascript',
|
||||
'svg': 'image/svg+xml',
|
||||
};
|
||||
let extension = path.split('.').pop();
|
||||
return k_extension_to_type[extension];
|
||||
}
|
||||
|
||||
async function blobHandler(request, response, blobId, uri) {
|
||||
for (let i in kStaticFiles) {
|
||||
if (uri === kStaticFiles[i].uri && kStaticFiles[i].path) {
|
||||
let stat = await File.stat('core/' + kStaticFiles[i].path);
|
||||
for (let i in k_static_files) {
|
||||
if (uri === k_static_files[i].uri && k_static_files[i].path) {
|
||||
let stat = await File.stat('core/' + k_static_files[i].path);
|
||||
let id = `${stat.mtime}_${stat.size}`;
|
||||
|
||||
if (request.headers['if-none-match'] === '"' + id + '"') {
|
||||
response.writeHead(304, {});
|
||||
response.end();
|
||||
} else {
|
||||
let data = await File.readFile('core/' + kStaticFiles[i].path);
|
||||
let data = await File.readFile('core/' + k_static_files[i].path);
|
||||
response.writeHead(200, Object.assign(
|
||||
{
|
||||
'Content-Type': kStaticFiles[i].type,
|
||||
'Content-Type': k_static_files[i].type,
|
||||
'Content-Length': data.byteLength,
|
||||
'etag': '"' + id + '"',
|
||||
},
|
||||
kStaticFiles[i].headers || {}));
|
||||
k_static_files[i].headers || {}));
|
||||
response.end(data);
|
||||
}
|
||||
return;
|
||||
@ -751,7 +760,7 @@ async function blobHandler(request, response, blobId, uri) {
|
||||
let appObject = JSON.parse(data);
|
||||
data = appObject.files[uri.substring(1)];
|
||||
data = await getBlobOrContent(data);
|
||||
type = guessType(uri);
|
||||
type = guessTypeUntrusted(uri, data);
|
||||
headers = {
|
||||
'ETag': '"' + id + '"',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
@ -769,6 +778,7 @@ async function blobHandler(request, response, blobId, uri) {
|
||||
headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
};
|
||||
type = guessTypeUntrusted(uri, data);
|
||||
sendData(response, data, type, headers);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
<iframe id="document" sandbox="allow-forms allow-scripts allow-top-navigation allow-modals allow-downloads" style="width: 100%; height: 100%; border: 0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<script>window.litDisableBundleWarning = true;</script>
|
||||
<script src="/split/split.min.js"></script>
|
||||
<script src="/smoothie/smoothie.js"></script>
|
||||
<script src="/static/client.js" type="module"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user