CVE-2020-36623: Cross-Site Request Forgery (CSRF) (CWE-352)
value: A vulnerability was found in Pengu. It has been declared as problematic. Affected by this vulnerability is the function runApp of the file src/index.js. The manipulation leads to cross-site request forgery. The attack can be launched remotely. The name of the patch is aea66f12b8cdfc3c8c50ad6a9c89d8307e9d0a91. It is recommended to apply a patch to fix this issue. The associated identifier of this vulnerability is VDB-216475.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 6.5
Published: 2022-12-21T19:15Z
Repository: https://github.com/jtojnar/pengu
Language: JavaScript
Files changed: README.md, src/index.js
Commit date: 2020-11-02 14:31:56 +0100
Commit message: Add origin whitelist

To prevent CSRF attack.

Diff: diff --git a/README.md b/README.md
index 88d80fd..8280f2c 100644
--- a/README.md
+++ b/README.md
@@ -33,10 +33,11 @@ pengu uses the following environment variables for configuration:
 
 * `PORT` – The port a web server will be running on. Of not specified it defaults to `8080`.
 * `DATABASE_URL` – [Connection string](https://node-postgres.com/features/connecting/#connection-uri) for the PostgreSQL database. If omitted, persistence will be missing.
 * `NODE_ENV` – Can be set to `production` for less verbose logs. Defaults to `development`.
 * `OPENID_PROVIDER` – If this is set, pengu will use OpenID to log-in. Though it only supports using a hardcoded identity specified by this variable. User will be redirected to the provider, where they will confirm their credentials, and then be redirected back to Pengu with an access code. Pengu will then verify the access code against the OpenID verification URL and realm specified by `OPENID_VERIFY`, `OPENID_REALM` environment variables.
+* `ACCEPTED_ORIGINS` – comma-separated list of domain names that are allowed to access the WebSockets server. This is necessary to prevent [cross-site request forgery](https://en.wikipedia.org/wiki/WebSocket#Security_considerations). When the environment variable is not set, only `localhost` and `127.0.0.1` will be allowed to connect.
 
 ## Development
 
 The front-end of Pengu is stored in the `assets/` directory and needs to be rebuilt when it is changed. You can use `npm run build` command to do that, or `npm run dev` if you want it to rebuild automatically on changes. Its build artefacts end up in the `client/` directory, from where the server will serve them.
 
diff --git a/src/index.js b/src/index.js
index 59c642f..d9d0f9e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -26,10 +26,12 @@ function findById(arr, id) {
 		}
 	}
 	return null;
 }
 
+const acceptedOrigins = process.env.ACCEPTED_ORIGINS ? process.env.ACCEPTED_ORIGINS.split(',') : ['localhost', '127.0.0.1'];
+
 async function runApp() {
 	let app = express();
 
 	app.set('port', process.env.PORT ?? 8080);
 
@@ -66,12 +68,13 @@ async function runApp() {
 	let wsServer = new WebSocketServer({
 		httpServer: server,
 		autoAcceptConnections: false
 	});
 
-	function originIsAllowed() {
-		return true;
+	function originIsAllowed(origin) {
+		const url = new URL(origin);
+		return acceptedOrigins.includes(url.hostname);
 	}
 
 	let clients = [];
 	let players = {};
 	let rooms = JSON.parse(fs.readFileSync(path.join(__dirname, '../content/world/map.json'), 'utf8'), function (key, value) {

Vulnerable code: 	function originIsAllowed() {
		return true;
Fixed code: * `ACCEPTED_ORIGINS` – comma-separated list of domain names that are allowed to access the WebSockets server. This is necessary to prevent [cross-site request forgery](https://en.wikipedia.org/wiki/WebSocket#Security_considerations). When the environment variable is not set, only `localhost` and `127.0.0.1` will be allowed to connect.
const acceptedOrigins = process.env.ACCEPTED_ORIGINS ? process.env.ACCEPTED_ORIGINS.split(',') : ['localhost', '127.0.0.1'];

	function originIsAllowed(origin) {
		const url = new URL(origin);
		return acceptedOrigins.includes(url.hostname);