Compare commits
1
Commits
95c9127e8e
...
hardening
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d606e11de4 |
No files matched your search
@@ -4,7 +4,7 @@ const users = require('./users');
|
||||
const auth = require('./auth');
|
||||
const audit = require('./audit');
|
||||
|
||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
||||
const { readBody } = require('./body');
|
||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||
const row = u => ({ ...users.publicView(u), tabsChosen: u.tabs || [], passkeys: auth.passkeyCount(u.id) });
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ const rp = req => {
|
||||
};
|
||||
const equalStr = (a, b) => { const x = Buffer.from(String(a)), y = Buffer.from(String(b)); return x.length === y.length && crypto.timingSafeEqual(x, y); };
|
||||
|
||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
||||
const { readBody } = require('./body');
|
||||
const send = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json', ...(res.getHeader('Set-Cookie') ? { 'Set-Cookie': res.getHeader('Set-Cookie') } : {}) }); res.end(JSON.stringify(obj)); };
|
||||
const credsOf = uid => store.creds.filter(c => c.uid === uid);
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
// Reads a request body as text, and refuses one that is too big, so a huge upload cannot use up the server's memory.
|
||||
function readBody(req, max = 2e6) {
|
||||
return new Promise((ok, no) => {
|
||||
const c = []; let n = 0, done = false;
|
||||
req.on('data', d => { if (done) return; n += d.length; if (n > max) { done = true; c.length = 0; const e = new Error('That is too large'); e.status = 413; no(e); return; } c.push(d); });
|
||||
req.on('end', () => { if (!done) ok(Buffer.concat(c).toString()); });
|
||||
req.on('error', e => { if (!done) no(e); });
|
||||
});
|
||||
}
|
||||
module.exports = { readBody };
|
||||
@@ -306,7 +306,7 @@ function feed(req, res) {
|
||||
}
|
||||
|
||||
// ---- HTTP API (called by server.js, behind the passkey gate)
|
||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
||||
const readBody = req => require('./body').readBody(req, 8e6);
|
||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||
|
||||
/** Every event in the window from all calendars (ICS links, iCloud, NE Hub, shared): { events, errors } */
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"test": "for t in smoke tasks push users cal plex player lyrics signal mail mail-imap files podcast abapi abdirect books store librivox radio vbv savefiles bible lrclib visualizer; do node tools/$t-test.js || exit 1; done",
|
||||
"test": "for t in smoke tasks push users cal plex player lyrics signal mail mail-imap files podcast abapi abdirect books store librivox radio vbv savefiles bible lrclib visualizer body; do node tools/$t-test.js || exit 1; done",
|
||||
"audit": "npm audit --omit=dev --audit-level=high"
|
||||
},
|
||||
"keywords": [],
|
||||
|
||||
@@ -166,7 +166,7 @@ const sessions = new Map(); // callback id -> { token, repo, path, exp }; keeps
|
||||
setInterval(() => { for (const [k, v] of sessions) if (v.exp < Date.now()) sessions.delete(k); }, 3600e3).unref();
|
||||
const DOC_TYPE = { word: 'doc docx odt rtf txt', cell: 'xls xlsx ods csv', slide: 'ppt pptx odp' };
|
||||
const docType = e => Object.keys(DOC_TYPE).find(k => DOC_TYPE[k].split(' ').includes(e));
|
||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
||||
const { readBody } = require('./body');
|
||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||
const sfGet = async (p, token) => {
|
||||
const r = await fetch(SEAFILE_URL + p, { headers: { Authorization: 'Token ' + token } });
|
||||
@@ -661,9 +661,13 @@ async function handleRequest(req, res) {
|
||||
http.createServer(async (req, res) => {
|
||||
try { await handleRequest(req, res); }
|
||||
catch (e) {
|
||||
if (e && e.status === 413) { if (!res.headersSent) { res.writeHead(413, { 'content-type': 'application/json', connection: 'close' }); res.end('{"error":"That is too large"}'); } return; }
|
||||
console.error('[error]', req.method, req.url.split('?')[0], e && e.stack || e);
|
||||
if (!res.headersSent) { res.writeHead(500, { 'content-type': 'application/json' }); res.end(JSON.stringify({ error: 'Server error: ' + (e && e.message || 'unknown') })); } else res.destroy();
|
||||
}
|
||||
}).listen(PORT, () => console.log(`Hub on :${PORT} (Seafile ${SEAFILE_URL})`));
|
||||
process.on('unhandledRejection', e => console.error('[unhandledRejection]', e && e.stack || e));
|
||||
process.on('uncaughtException', e => console.error('[uncaughtException]', e && e.stack || e));
|
||||
|
||||
// Docker sends SIGTERM on stop: leave at once instead of waiting to be killed.
|
||||
for (const sig of ['SIGTERM', 'SIGINT']) process.on(sig, () => { console.log('[stop] ' + sig); process.exit(0); });
|
||||
@@ -12,7 +12,7 @@ const users = require('./users');
|
||||
const file = () => path.join(users.dir(), 'tasks.json'); // each person has their own
|
||||
const load = () => { try { return JSON.parse(fs.readFileSync(file(), 'utf8')); } catch { return { lists: [{ id: 'inbox', name: 'Tasks' }], tasks: [] }; } };
|
||||
const save = d => atomicWrite(file(), JSON.stringify(d, null, 2), { mode: 0o600 });
|
||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
||||
const { readBody } = require('./body');
|
||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||
const isLocal = id => String(id || '').startsWith('local:');
|
||||
const COLORS = ['#4f7cff', '#2f9e6e', '#e0742b', '#d9453a', '#8a5cf0', '#e5a00d'];
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
const http = require('http'), assert = require('assert'), { readBody } = require('../body');
|
||||
const s = http.createServer(async (req, res) => { try { const t = await readBody(req, 1000); res.end('ok:' + t.length); } catch (e) { res.writeHead(e.status || 500); res.end(e.message); } });
|
||||
s.listen(0, async () => {
|
||||
const P = s.address().port, post = b => fetch(`http://127.0.0.1:${P}/`, { method: 'POST', body: b }).then(async r => [r.status, await r.text()]);
|
||||
assert.deepStrictEqual(await post('x'.repeat(900)), [200, 'ok:900']);
|
||||
const big = await post('x'.repeat(5000)); assert.strictEqual(big[0], 413);
|
||||
console.log('body test ok'); s.close();
|
||||
});
|
||||
Reference in new issue
Block a user