Implement header based safety features

Using the `helmet` package, a certain set of security critical headers
are set to prevent XSS attacks and the like.
This commit is contained in:
Wolvan 2022-01-30 16:59:11 +01:00
parent 26a42333fe
commit afc7bbad01
8 changed files with 114 additions and 98 deletions

View file

@ -9,11 +9,11 @@
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/main.css"> <link rel="stylesheet" href="/static/css/main.css">
<link rel="stylesheet" href="/static/css/index.css"> <link rel="stylesheet" href="/static/css/index.css">
<script type="text/javascript"> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript">
const MAX_POLL_OPTIONS = "{{ MAX_POLL_OPTIONS }}"; const MAX_POLL_OPTIONS = "{{ MAX_POLL_OPTIONS }}";
const MAX_CHARACTER_LENGTH = "{{ MAX_CHARACTER_LENGTH }}"; const MAX_CHARACTER_LENGTH = "{{ MAX_CHARACTER_LENGTH }}";
</script> </script>
<script type="text/javascript" src="/static/js/index.js" defer="true" async="true"></script> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/js/index.js" defer="true" async="true"></script>
</head> </head>
<body> <body>
<header> <header>

View file

@ -9,8 +9,8 @@
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/main.css"> <link rel="stylesheet" href="/static/css/main.css">
<link rel="stylesheet" href="/static/css/vote.css"> <link rel="stylesheet" href="/static/css/vote.css">
<script type="text/javascript" src="/static/vendor/js/textfit.js" defer="true"></script> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/vendor/js/textfit.js" defer="true"></script>
<script type="text/javascript"> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript">
const textFitOptions = { const textFitOptions = {
multiLine: true multiLine: true
}; };

View file

@ -9,14 +9,14 @@
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/main.css"> <link rel="stylesheet" href="/static/css/main.css">
<link rel="stylesheet" href="/static/css/result.css"> <link rel="stylesheet" href="/static/css/result.css">
<script type="text/javascript" src="/static/vendor/js/textfit.js" defer="true"></script> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/vendor/js/textfit.js" defer="true"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js" defer="true"></script> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="https://www.gstatic.com/charts/loader.js" defer="true"></script>
<script type="text/javascript"> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript">
const POLL_VOTE_DATA_STRING = `{{ POLL_OPTION_VOTES }}`; const POLL_VOTE_DATA_STRING = `{{ POLL_OPTION_VOTES }}`;
const POLL_BACKEND_URL = "{{ BACKEND_BASE_PATH }}"; const POLL_BACKEND_URL = "{{ BACKEND_BASE_PATH }}";
const POLL_ID = "{{ POLL_ID }}"; const POLL_ID = "{{ POLL_ID }}";
</script> </script>
<script type="text/javascript" src="/static/js/result.js" defer="true"></script> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/js/result.js" defer="true"></script>
</head> </head>
<body> <body>
<header> <header>

164
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "poll-horse", "name": "poll-horse",
"version": "1.0.1", "version": "1.0.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "poll-horse", "name": "poll-horse",
"version": "1.0.1", "version": "1.0.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"commander": "^8.3.0", "commander": "^8.3.0",
@ -14,6 +14,7 @@
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"express": "^4.17.2", "express": "^4.17.2",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"helmet": "^5.0.2",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"node-fetch": "^2.6.6", "node-fetch": "^2.6.6",
"node-persist": "^3.1.0" "node-persist": "^3.1.0"
@ -830,10 +831,16 @@
} }
}, },
"node_modules/chokidar": { "node_modules/chokidar": {
"version": "3.5.2", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true, "dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": { "dependencies": {
"anymatch": "~3.1.2", "anymatch": "~3.1.2",
"braces": "~3.0.2", "braces": "~3.0.2",
@ -1715,9 +1722,9 @@
} }
}, },
"node_modules/glob": { "node_modules/glob": {
"version": "7.1.7", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
@ -1834,6 +1841,14 @@
"he": "bin/he" "he": "bin/he"
} }
}, },
"node_modules/helmet": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-5.0.2.tgz",
"integrity": "sha512-QWlwUZZ8BtlvwYVTSDTBChGf8EOcQ2LkGMnQJxSzD1mUu8CCjXJZq/BXP8eWw4kikRnzlhtYo3lCk0ucmYA3Vg==",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
@ -2232,32 +2247,32 @@
} }
}, },
"node_modules/mocha": { "node_modules/mocha": {
"version": "9.1.3", "version": "9.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz",
"integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@ungap/promise-all-settled": "1.1.2", "@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1", "ansi-colors": "4.1.1",
"browser-stdout": "1.3.1", "browser-stdout": "1.3.1",
"chokidar": "3.5.2", "chokidar": "3.5.3",
"debug": "4.3.2", "debug": "4.3.3",
"diff": "5.0.0", "diff": "5.0.0",
"escape-string-regexp": "4.0.0", "escape-string-regexp": "4.0.0",
"find-up": "5.0.0", "find-up": "5.0.0",
"glob": "7.1.7", "glob": "7.2.0",
"growl": "1.10.5", "growl": "1.10.5",
"he": "1.2.0", "he": "1.2.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"log-symbols": "4.1.0", "log-symbols": "4.1.0",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"ms": "2.1.3", "ms": "2.1.3",
"nanoid": "3.1.25", "nanoid": "3.2.0",
"serialize-javascript": "6.0.0", "serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1", "strip-json-comments": "3.1.1",
"supports-color": "8.1.1", "supports-color": "8.1.1",
"which": "2.0.2", "which": "2.0.2",
"workerpool": "6.1.5", "workerpool": "6.2.0",
"yargs": "16.2.0", "yargs": "16.2.0",
"yargs-parser": "20.2.4", "yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0" "yargs-unparser": "2.0.0"
@ -2274,29 +2289,6 @@
"url": "https://opencollective.com/mochajs" "url": "https://opencollective.com/mochajs"
} }
}, },
"node_modules/mocha/node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/mocha/node_modules/debug/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/mocha/node_modules/ms": { "node_modules/mocha/node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -2379,9 +2371,9 @@
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.1.25", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
"integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
"dev": true, "dev": true,
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
@ -2405,14 +2397,22 @@
} }
}, },
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.6.6", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": { "dependencies": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
}, },
"engines": { "engines": {
"node": "4.x || >=6.0.0" "node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
} }
}, },
"node_modules/node-persist": { "node_modules/node-persist": {
@ -3420,9 +3420,9 @@
} }
}, },
"node_modules/workerpool": { "node_modules/workerpool": {
"version": "6.1.5", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz",
"integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==",
"dev": true "dev": true
}, },
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
@ -4149,9 +4149,9 @@
"dev": true "dev": true
}, },
"chokidar": { "chokidar": {
"version": "3.5.2", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true, "dev": true,
"requires": { "requires": {
"anymatch": "~3.1.2", "anymatch": "~3.1.2",
@ -4844,9 +4844,9 @@
"dev": true "dev": true
}, },
"glob": { "glob": {
"version": "7.1.7", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
@ -4929,6 +4929,11 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true "dev": true
}, },
"helmet": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-5.0.2.tgz",
"integrity": "sha512-QWlwUZZ8BtlvwYVTSDTBChGf8EOcQ2LkGMnQJxSzD1mUu8CCjXJZq/BXP8eWw4kikRnzlhtYo3lCk0ucmYA3Vg=="
},
"http-errors": { "http-errors": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
@ -5220,54 +5225,37 @@
"dev": true "dev": true
}, },
"mocha": { "mocha": {
"version": "9.1.3", "version": "9.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz",
"integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@ungap/promise-all-settled": "1.1.2", "@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1", "ansi-colors": "4.1.1",
"browser-stdout": "1.3.1", "browser-stdout": "1.3.1",
"chokidar": "3.5.2", "chokidar": "3.5.3",
"debug": "4.3.2", "debug": "4.3.3",
"diff": "5.0.0", "diff": "5.0.0",
"escape-string-regexp": "4.0.0", "escape-string-regexp": "4.0.0",
"find-up": "5.0.0", "find-up": "5.0.0",
"glob": "7.1.7", "glob": "7.2.0",
"growl": "1.10.5", "growl": "1.10.5",
"he": "1.2.0", "he": "1.2.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"log-symbols": "4.1.0", "log-symbols": "4.1.0",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"ms": "2.1.3", "ms": "2.1.3",
"nanoid": "3.1.25", "nanoid": "3.2.0",
"serialize-javascript": "6.0.0", "serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1", "strip-json-comments": "3.1.1",
"supports-color": "8.1.1", "supports-color": "8.1.1",
"which": "2.0.2", "which": "2.0.2",
"workerpool": "6.1.5", "workerpool": "6.2.0",
"yargs": "16.2.0", "yargs": "16.2.0",
"yargs-parser": "20.2.4", "yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0" "yargs-unparser": "2.0.0"
}, },
"dependencies": { "dependencies": {
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dev": true,
"requires": {
"ms": "2.1.2"
},
"dependencies": {
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
},
"ms": { "ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -5341,9 +5329,9 @@
} }
}, },
"nanoid": { "nanoid": {
"version": "3.1.25", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
"integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
"dev": true "dev": true
}, },
"natural-compare": { "natural-compare": {
@ -5358,9 +5346,9 @@
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"node-fetch": { "node-fetch": {
"version": "2.6.6", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": { "requires": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
} }
@ -6056,9 +6044,9 @@
"dev": true "dev": true
}, },
"workerpool": { "workerpool": {
"version": "6.1.5", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz",
"integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==",
"dev": true "dev": true
}, },
"wrap-ansi": { "wrap-ansi": {

View file

@ -66,6 +66,7 @@
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"express": "^4.17.2", "express": "^4.17.2",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"helmet": "^5.0.2",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"node-fetch": "^2.6.6", "node-fetch": "^2.6.6",
"node-persist": "^3.1.0" "node-persist": "^3.1.0"

View file

@ -110,6 +110,10 @@ export default async function init(router: Router, polls: Storage): Promise<void
return null; return null;
} }
router.all("*", (req, res, next) => {
res.header("Content-Type", "application/json; charset=utf-8");
next();
});
// #region API // #region API
router.get("/api/poll/:id", async (req, res) => { router.get("/api/poll/:id", async (req, res) => {
try { try {

View file

@ -116,6 +116,10 @@ function xss(unsafe: string) {
} }
export default function init(router: Router): void { export default function init(router: Router): void {
router.all("*", (req, res, next) => {
res.header("Content-Type", "text/html; charset=utf-8");
next();
});
router.get("/:id/r", async (req, res) => { router.get("/:id/r", async (req, res) => {
const id = req.params.id; const id = req.params.id;
try { try {
@ -145,6 +149,7 @@ export default function init(router: Router): void {
"BACKEND_BASE_PATH": (program.opts().backendBaseUrl || ""), "BACKEND_BASE_PATH": (program.opts().backendBaseUrl || ""),
"POLL_OPTION_VOTES": Buffer.from(JSON.stringify(Object.entries(poll.votes))).toString("base64"), "POLL_OPTION_VOTES": Buffer.from(JSON.stringify(Object.entries(poll.votes))).toString("base64"),
"QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`, "QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`,
"CORS_SCRIPT_NONCE": res.locals.cspNonce
}); });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@ -182,6 +187,7 @@ export default function init(router: Router): void {
"FORM_SUBMISSION_ERROR": xss(req.query.error + ""), "FORM_SUBMISSION_ERROR": xss(req.query.error + ""),
"FORM_SUBMISSION_ERROR_SHOWN_CLASS": req.query.error ? "error-visible" : "", "FORM_SUBMISSION_ERROR_SHOWN_CLASS": req.query.error ? "error-visible" : "",
"QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`, "QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`,
"CORS_SCRIPT_NONCE": res.locals.cspNonce
}); });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@ -214,7 +220,8 @@ export default function init(router: Router): void {
"FORM_CAPTCHA": req.query.captcha === "true" ? "checked" : "", "FORM_CAPTCHA": req.query.captcha === "true" ? "checked" : "",
"FORM_OPTION_DIVS": pollOptionDivs, "FORM_OPTION_DIVS": pollOptionDivs,
"MAX_POLL_OPTIONS": MAX_POLL_OPTIONS, "MAX_POLL_OPTIONS": MAX_POLL_OPTIONS,
"MAX_CHARACTER_LENGTH": MAX_CHARACTER_LENGTH "MAX_CHARACTER_LENGTH": MAX_CHARACTER_LENGTH,
"CORS_SCRIPT_NONCE": res.locals.cspNonce
}); });
}); });
} }

View file

@ -2,13 +2,15 @@
"use strict"; "use strict";
import loadConfig from "./config-loader"; import loadConfig from "./config-loader";
import { program } from "commander"; import { program } from "commander";
import express from "express"; import express, { Response } from "express";
import compression from "compression"; import compression from "compression";
import cookiepaser from "cookie-parser"; import cookiepaser from "cookie-parser";
import { resolve } from "path"; import { resolve } from "path";
import FlatFileStorage from "./FlatFileStorage"; import FlatFileStorage from "./FlatFileStorage";
import Storage from "./Storage"; import Storage from "./Storage";
import MySQLStorage from "./MySQLStorage"; import MySQLStorage from "./MySQLStorage";
import helmet from "helmet";
import crypto from "crypto";
async function main(): Promise<void> { async function main(): Promise<void> {
await loadConfig([ await loadConfig([
@ -32,6 +34,20 @@ async function main(): Promise<void> {
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }));
app.use(compression()); app.use(compression());
app.use(cookiepaser()); app.use(cookiepaser());
app.use((req, res, next) => {
res.locals.cspNonce = crypto.randomBytes(16).toString("hex");
next();
});
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "'strict-dynamic'", "https: 'self'", "http: 'self'", (req, res) => `'nonce-${(res as Response).locals.cspNonce}'`],
imgSrc: ["'self'", "data:", "https://chart.googleapis.com"],
}
},
crossOriginEmbedderPolicy: false
}));
const storage: Storage = (opts.useMysql) ? const storage: Storage = (opts.useMysql) ?
new MySQLStorage({ new MySQLStorage({