Improved backend security

- added max values for all bingo graphql endpoints
- added sqlite3 session-store with `connect-sqlite3`
pull/7/head
Trivernis 5 years ago
parent 20e1540175
commit 96425d563a

2
.gitignore vendored

@ -4,3 +4,5 @@ node_modules
scripts/*
tmp
config.yaml
sessions
sessions-journal

@ -5,6 +5,7 @@ const createError = require('http-errors'),
logger = require('morgan'),
compileSass = require('express-compile-sass'),
session = require('express-session'),
SQLiteStore = require('connect-sqlite3')(session),
fsx = require('fs-extra'),
yaml = require('js-yaml'),
graphqlHTTP = require('express-graphql'),
@ -39,6 +40,7 @@ app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
store: new SQLiteStore,
secret: settings.sessions.secret,
resave: false,
saveUninitialized: true,

185
package-lock.json generated

@ -474,6 +474,11 @@
"upath": "^1.1.1"
}
},
"chownr": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
},
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@ -567,6 +572,14 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"connect-sqlite3": {
"version": "0.9.11",
"resolved": "https://registry.npmjs.org/connect-sqlite3/-/connect-sqlite3-0.9.11.tgz",
"integrity": "sha1-TlQVXcLq3ypZmDhbLzXoPdkkCy0=",
"requires": {
"sqlite3": "^4.0.0"
}
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@ -675,6 +688,11 @@
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"define-property": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@ -737,6 +755,11 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"doctypes": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
@ -1131,6 +1154,14 @@
"universalify": "^0.1.0"
}
},
"fs-minipass": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"requires": {
"minipass": "^2.2.1"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -1832,6 +1863,14 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"requires": {
"minimatch": "^3.0.4"
}
},
"in-publish": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
@ -1859,6 +1898,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"inline-source-map-comment": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/inline-source-map-comment/-/inline-source-map-comment-1.0.5.tgz",
@ -2341,6 +2385,30 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"minipass": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
},
"dependencies": {
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
}
}
},
"minizlib": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"requires": {
"minipass": "^2.2.1"
}
},
"mixin-deep": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
@ -2422,6 +2490,31 @@
}
}
},
"needle": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.3.1.tgz",
"integrity": "sha512-CaLXV3W8Vnbps8ZANqDGz7j4x7Yj1LW4TWF/TQuDfj7Cfx4nAPTvw98qgTevtto1oHDrh3pQkaODbqupXlsWTg==",
"requires": {
"debug": "^4.1.0",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@ -2453,6 +2546,53 @@
}
}
},
"node-pre-gyp": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
"integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
},
"dependencies": {
"nopt": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
}
},
"tar": {
"version": "4.4.8",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
}
}
},
"node-sass": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
@ -2525,6 +2665,20 @@
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"npm-bundled": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
},
"npm-packlist": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz",
"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
}
},
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
@ -2896,6 +3050,17 @@
"unpipe": "1.0.0"
}
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
}
},
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
@ -3127,6 +3292,11 @@
}
}
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"scss-tokenizer": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
@ -3371,6 +3541,16 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sqlite3": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.8.tgz",
"integrity": "sha512-kgwHu4j10KhpCHtx//dejd/tVQot7jc3sw+Sn0vMuKOw0X00Ckyg9VceKgzPyGmmz+zEoYue9tOLriWTvYy0ww==",
"requires": {
"nan": "^2.12.1",
"node-pre-gyp": "^0.11.0",
"request": "^2.87.0"
}
},
"sshpk": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
@ -3461,6 +3641,11 @@
"get-stdin": "^4.0.1"
}
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"sum-up": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz",

@ -6,6 +6,7 @@
"start": "node ./bin/www"
},
"dependencies": {
"connect-sqlite3": "^0.9.11",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"express": "~4.16.1",

@ -76,8 +76,9 @@ async function createFollowup() {
*/
async function submitUsername() {
let unameInput = document.querySelector('#username-input');
let username = unameInput.value;
let response = await postGraphqlQuery(`
let username = unameInput.value.replace(/^\s+|\s+$/g, '');
if (username.length > 1 && username !== 'anonymous') {
let response = await postGraphqlQuery(`
mutation($username:String!) {
bingo {
setUsername(input: {username: $username}) {
@ -86,16 +87,19 @@ async function submitUsername() {
}
}
}`, {
username: username
},`/graphql?game=${getGameParam()}`);
if (response.status === 200) {
unameInput.value = '';
unameInput.placeholder = response.data.username;
document.querySelector('#username-form').remove();
document.querySelector('.greyover').remove();
username: username
},`/graphql?game=${getGameParam()}`);
if (response.status === 200) {
unameInput.value = '';
unameInput.placeholder = response.data.username;
document.querySelector('#username-form').remove();
document.querySelector('.greyover').remove();
} else {
showError(`Failed to submit username. HTTP Error: ${response.status}`);
console.error(response);
}
} else {
showError(`Failed to submit username. HTTP Error: ${response.status}`);
console.error(response);
showError('You need to provide a username (minimum 2 characters)!');
}
}
@ -306,12 +310,10 @@ function addChatMessage(messageObject) {
if (messageObject.type === "USER") {
msgSpan.innerHTML = `
<span class="chatUsername">${messageObject.username}:</span>
<span class="chatMessageContent">${messageObject.htmlContent}</span>
`;
<span class="chatMessageContent">${messageObject.htmlContent}</span>`;
} else {
msgSpan.innerHTML = `
<span class="chatMessageContent ${messageObject.type}">${messageObject.htmlContent}</span>
`;
<span class="chatMessageContent ${messageObject.type}">${messageObject.htmlContent}</span>`;
}
let chatContent = document.querySelector('#chat-content');
chatContent.appendChild(msgSpan);
@ -334,6 +336,8 @@ window.addEventListener("unhandledrejection", function(promiseRejectionEvent) {
});
window.onload = () => {
if (document.querySelector('#chat-container'))
refresh();
if (window && !document.querySelector('#bingoform')) {
refrInterval = setInterval(refresh, 1000); // global variable to clear
}

@ -23,14 +23,17 @@ textarea
grid-template-columns: 0 100% !important
grid-template-rows: 10% 80% 10% !important
#players-container div
display: none
#players-container #chat-container
display: none !important
padding: 0
#username-form
width: calc(100% - 2rem) !important
left: 0 !important
#hide-player-container-button
display: none
.popup
width: calc(100% - 2rem) !important
left: 0 !important
@ -173,6 +176,7 @@ textarea
height: 100%
border: 1px solid $inactive
margin: 0 0.5rem
word-break: break-word
#chat-content
height: calc(100% - 3.5rem)

@ -33,6 +33,9 @@ class BingoSession {
addUser(user) {
let id = user.id;
this.users[id] = user;
if (user.username !== 'anonymous') {
this.chatMessages.push(new BingoChatMessage(`**${user.username}** joined.`, "INFO"));
}
}
/**
@ -57,7 +60,7 @@ class BingoSession {
this.followup = followup.id;
bingoSessions[followup.id] = followup;
followup.chatMessages = this.chatMessages;
followup.chatMessages.push(new BingoChatMessage('--- Rematch ---', "INFO"));
followup.chatMessages.push(new BingoChatMessage('**Rematch**', "INFO"));
return followup;
}
@ -84,7 +87,7 @@ class BingoSession {
*/
sendToggleInfo(base64Word, bingoUser) {
let word = Buffer.from(base64Word, 'base64').toString();
let toggleMessage = new BingoChatMessage(`${bingoUser.username} toggled phrase "${word}"`, "INFO");
let toggleMessage = new BingoChatMessage(`**${bingoUser.username}** toggled phrase "${word}".`, "INFO");
this.chatMessages.push(toggleMessage);
}
}
@ -343,6 +346,7 @@ router.graphqlResolver = (req, res) => {
});
let size = input.size;
if (words.length > 0 && size < 10 && size > 0) {
words = words.slice(0, 10000); // only allow up to 10000 words in the bingo
let game = new BingoSession(words, size);
bingoSessions[game.id] = game;
@ -404,7 +408,7 @@ router.graphqlResolver = (req, res) => {
}
},
sendChatMessage: ({input}) => {
input.message = replaceTagSigns(input.message);
input.message = replaceTagSigns(input.message).substring(0, 250);
if (bingoSession && input.message) {
let userMessage = new BingoChatMessage(input.message, 'USER', bingoUser.username);
bingoSession.chatMessages.push(userMessage);

@ -4,7 +4,7 @@ block content
if username === 'anonymous'
div(class='greyover')
div(id='username-form', onkeypress='submitOnEnter(event, submitUsername)')
input(type='text', id='username-input', placeholder=username)
input(type='text', id='username-input', placeholder=username, maxlength="30")
span Maximum is 30 characters.
button(onclick='submitUsername()') Set Username
div(id='content-container')
@ -15,7 +15,7 @@ block content
span(class='player-name-span')= player.username
div(id='chat-container')
div(id='chat-content')
input(id='chat-input' type='text', placeholder='chat', onkeypress='submitOnEnter(event, sendChatMessage)')
input(id='chat-input' type='text', placeholder='chat', onkeypress='submitOnEnter(event, sendChatMessage)' maxlength="250")
div(id='words-container')
each val in grid
div(class='bingo-word-row')

@ -10,4 +10,4 @@ block content
div(class='stretchDiv')
button(onclick='submitBingoWords()') Submit
span(id='word-count') Please provide at least 9 phrases:
textarea(id='bingo-textarea', placeholder='Bingo Words')
textarea(id='bingo-textarea', placeholder='Bingo Words (max 10,000)', maxlength=1000000)

Loading…
Cancel
Save