WIP checkup improvements

This commit is contained in:
ansuz 2022-02-10 13:00:16 +05:30
parent 5bf21a25c0
commit 8eefeace43
4 changed files with 245 additions and 43 deletions

View File

@ -14,7 +14,7 @@ html, body {
.report {
font-size: 30px;
max-width: 26em;
max-width: 30em;
margin: auto;
padding-top: 15px;
}

View File

@ -1,5 +1,7 @@
define([
], function () {
'jquery',
'/common/common-util.js',
], function ($, Util) {
var Tools = {};
Tools.supportsSharedArrayBuffers = function () {
try {
@ -51,5 +53,25 @@ define([
return navigator.userAgent + "\n" + navigator.vendor;
};
Tools.cacheBuster = function (url) {
if (/\?/.test(url)) { return url; }
return url + '?test=' + (+new Date());
};
var common_map = {};
Tools.common_xhr = function (url, _cb) {
var cb = Util.once(Util.once(Util.mkAsync(_cb)));
var ready = common_map[url];
if (ready) { return void ready.reg(cb); }
ready = common_map[url] = Util.mkEvent(true);
ready.reg(cb);
return void $.ajax(Tools.cacheBuster(url), {
dataType: 'text',
complete: function (xhr) {
ready.fire(xhr);
},
});
};
return Tools;
});

View File

@ -67,9 +67,7 @@ define([
$(msg).removeClass('cp-danger').addClass('cp-warning');
};
var cacheBuster = function (url) {
return url + '?test=' + (+new Date());
};
var cacheBuster = Tools.cacheBuster;
var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin);
var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin);
@ -834,7 +832,7 @@ define([
});
});
assert(function (cb, msg) {
assert(function (cb, msg) { // XXX this test has been superceded
// check that the sandbox domain is included in connect-src
msg.appendChild(h('span', [
"This instance's ",
@ -848,20 +846,17 @@ define([
" See the provided NGINX configuration file for an example of how to set this header correctly.",
]));
$.ajax(cacheBuster('/'), {
dataType: 'text',
complete: function (xhr) {
var CSP = parseCSP(xhr.getResponseHeader('content-security-policy'));
var connect = (CSP && CSP['connect-src']) || "";
if (connect.includes(trimmedSafe)) {
return void cb(true);
}
cb(CSP);
},
Tools.common_xhr('/', function (xhr) {
var CSP = parseCSP(xhr.getResponseHeader('content-security-policy'));
var connect = (CSP && CSP['connect-src']) || "";
if (connect.includes(trimmedSafe)) {
return void cb(true);
}
cb(CSP);
});
});
assert(function (cb, msg) {
assert(function (cb, msg) { // XXX this test has been superceded
var directives = [
'img-src',
'media-src',
@ -882,24 +877,26 @@ define([
code('frame-src'),
" in the provided NGINX configuration file for an example of how to set these headers correctly.",
]));
$.ajax(cacheBuster('/'), {
dataType: 'text',
complete: function (xhr) {
var CSP = parseCSP(xhr.getResponseHeader('content-security-policy'));
// check that the relevant CSP directives are defined
// and that none of them permit general remote content via '*'
if (directives.every(function (k) {
return typeof(CSP[k]) === 'string' && !/ \* /.test(CSP[k]);
})) {
return void cb(true);
}
cb(CSP);
},
Tools.common_xhr('/', function (xhr) {
var CSP = parseCSP(xhr.getResponseHeader('content-security-policy'));
// check that the relevant CSP directives are defined
// and that none of them permit general remote content via '*'
if (directives.every(function (k) {
return typeof(CSP[k]) === 'string' && !/ \* /.test(CSP[k]);
})) {
return void cb(true);
}
cb(CSP);
});
});
assert(function (cb, msg) {
msg.appendChild(h('span', 'pewpew'));
msg.appendChild(h('span', [ // XXX
code('/api/config'),
" returned an HTTP status code other than ",
code('200'),
' when accessed from the sandbox domain.',
]));
deferredPostMessage({
command: 'CHECK_HTTP_STATUS',
content: {
@ -910,6 +907,195 @@ define([
});
});
/*
assert(function (cb, msg) {
msg.appendChild(h('span', [
'all headers',
]));
Tools.common_xhr('/', function (xhr) {
var all_headers = xhr.getAllResponseHeaders().split(/\r|\n/).filter(Boolean);
cb(all_headers);
});
});
*/
var validateCSP = function (raw, expected) {
var CSP = parseCSP(raw);
var checkRule = function (attr, rules) {
var h = CSP[attr];
// return `true` if you fail this test...
if (typeof(h) !== 'string' || !h) { return true; }
var l = rules.length;
for (var i = 0;i < l;i++) {
if (!h.includes(rules[i])) {
console.log("BAD_HEADER", rules[i]);
return true;
}
h = h.replace(rules[i], '');
}
return h.trim();
};
if (Object.keys(expected).some(function (dir) {
var result = checkRule(dir, expected[dir]);
if (result) {
console.log('BAD_HEADER:', {
rule: dir,
expected: expected[dir],
result: result,
});
}
return result;
})) {
return parseCSP(raw);
}
return true;
};
assert(function (_cb, msg) {
var url = '/sheet/inner.html';
var cb = Util.once(Util.mkAsync(_cb));
msg.appendChild(CSP_WARNING(url));
deferredPostMessage({
command: 'GET_HEADER',
content: {
url: url,
header: 'content-security-policy',
},
}, function (raw) {
var $outer = trimmedUnsafe;
var $sandbox = trimmedSafe;
var result = validateCSP(raw, {
'default-src': ["'none'"],
'style-src': ["'unsafe-inline'", "'self'", $outer],
'font-src': ["'self'", 'data:', $outer],
'child-src': ["'self'", 'blob:', $outer, $sandbox],
'frame-src': ["'self'", 'blob:', $outer, $sandbox],
'script-src': ["'self'", 'resource:', $outer,
"'unsafe-eval'", // XXX sloppy onlyoffice BS
"'unsafe-inline'", // XXX sloppy onlyoffice BS
],
'connect-src': [
"'self'",
'blob:',
$outer,
$sandbox,
/https:/.test($outer)? '': 'ws:', // XXX warn about ws: unless the origin is unencrypted
'wss:', // XXX always accept wss: ???
],
'img-src': ["'self'", 'data:', 'blob:', $outer],
'media-src': ['blob:'],
'frame-ancestors': ['*'], // XXX IFF you want to support remote embedding
'worker-src': ["'self'", $outer, $sandbox],
});
cb(result);
});
});
assert(function (cb, msg) {
var header = 'content-security-policy';
msg.appendChild(h('span', [
header,
]));
Tools.common_xhr('/', function (xhr) {
var raw = xhr.getResponseHeader(header);
var $outer = trimmedUnsafe;
var $sandbox = trimmedSafe;
var result = validateCSP(raw, {
'default-src': ["'none'"],
'style-src': ["'unsafe-inline'", "'self'", $outer],
'font-src': ["'self'", 'data:', $outer],
'child-src': ["'self'", 'blob:', $outer, $sandbox],
'frame-src': ["'self'", 'blob:', $outer, $sandbox],
'script-src': ["'self'", 'resource:', $outer],
'connect-src': [
"'self'",
'blob:',
$outer,
$sandbox,
/https:/.test($outer)? '': 'ws:', // XXX warn about ws: unless the origin is unencrypted
'wss:', // XXX always accept wss: ???
],
'img-src': ["'self'", 'data:', 'blob:', $outer],
'media-src': ['blob:'],
'frame-ancestors': ['*'], // XXX IFF you want to support remote embedding
'worker-src': ["'self'", $outer, $sandbox],
});
cb(result);
});
});
assert(function (cb, msg) {
var header = 'access-control-allow-origin';
msg.appendChild(h('span', [
header,
]));
Tools.common_xhr('/', function (xhr) {
var raw = xhr.getResponseHeader(header);
cb(raw === "*" || raw); // XXX
});
});
assert(function (cb, msg) {
var header = 'cross-origin-embedder-policy';
msg.appendChild(h('span', [
header,
]));
Tools.common_xhr('/', function (xhr) {
var raw = xhr.getResponseHeader(header);
cb(raw === 'require-corp' || raw); // XXX
});
});
assert(function (cb, msg) {
var header = 'cross-origin-resource-policy';
msg.appendChild(h('span', [
header,
]));
Tools.common_xhr('/', function (xhr) {
var raw = xhr.getResponseHeader(header);
cb(raw === 'cross-origin' || raw); // XXX
});
});
assert(function (cb, msg) {
var header = 'X-Content-Type-Options';
msg.appendChild(h('span', [
header,
]));
Tools.common_xhr('/', function (xhr) {
var raw = xhr.getResponseHeader(header);
cb(raw === 'nosniff' || raw); // XXX
});
});
assert(function (cb, msg) {
var header = 'Cache-Control';
msg.appendChild(h('span', [
header,
]));
// Cache-Control should be 'no-cache' unless the URL includes ver=
Tools.common_xhr('/', function (xhr) {
var raw = xhr.getResponseHeader(header);
cb(raw === 'no-cache' || raw); // XXX
});
});
assert(function (cb, msg) {
var header = 'Cache-Control';
msg.appendChild(h('span', [
header,
]));
// Cache-Control should be 'max-age=<number>' if the URL includes 'ver='
Tools.common_xhr('/?ver=' +(+new Date()), function (xhr) {
var raw = xhr.getResponseHeader(header);
cb(/max\-age=\d+$/.test(raw) || raw); // XXX
});
});
/*
assert(function (cb, msg) {
setWarningClass(msg);

View File

@ -11,13 +11,10 @@ define([
window.parent.postMessage(JSON.stringify(content), '*');
};
postMessage({ command: "READY", });
var getHeaders = function (url, cb) {
$.ajax(url + "?test=" + (+new Date()), {
dataType: 'text',
complete: function (xhr) {
var allHeaders = xhr.getAllResponseHeaders();
return void cb(void 0, allHeaders, xhr);
},
var getHeaders = function (url, cb) { // XXX reuse XHR objects?
Tools.common_xhr(url, function (xhr) {
var allHeaders = xhr.getAllResponseHeaders();
return void cb(void 0, allHeaders, xhr);
});
};
var COMMANDS = {};
@ -49,11 +46,8 @@ define([
};
COMMANDS.CHECK_HTTP_STATUS = function (content, cb) {
$.ajax(content.url, {
dataType: 'text',
complete: function (xhr) {
cb(xhr.status);
},
Tools.common_xhr(content.url, function (xhr) {
cb(xhr.status);
});
};