If you believe you have found a security issue in the BeamNG Server Panel, please do not open a public GitHub issue. Instead, send the details by email:
- Email:
ayozetr@proton.me - Subject: start with
[SECURITY]
Please include:
- The version (commit hash or release tag) you were testing against.
- A clear description of the issue and the impact you believe it has.
- Steps to reproduce, or a minimal proof-of-concept where applicable.
- Whether the issue requires authenticated access (and which role) or is reachable anonymously.
- Any suggested fix you have in mind.
- An acknowledgement within 5 working days.
- A first triage assessment within 14 working days.
- For confirmed issues, a coordinated disclosure timeline. The Panel is a single-author hobby project — patches usually ship within days for high-severity findings, but please do not assume same-day turnaround.
- Public credit in the changelog if you wish (please tell us how you would like to be credited).
- The HTTP API exposed by
server.js. - The frontend bundled into
dist/(XSS, auth bypass, CSRF, prototype pollution). - The mod-extraction pipeline (
processModBufferand the ZIP/RAR/7z extractors). - The session, audit log, and rate-limit subsystems.
- Vulnerabilities in third-party libraries (
better-sqlite3,node-stream-zip,node-unrar-js,node-7z,7zip-bin,dotenv,esbuild, React, ReactDOM). Please report those upstream. - The BeamMP-Server binary itself — that is a BeamMP project product; report upstream.
- BeamNG.drive itself — that is a BeamNG GmbH product; report upstream.
- Issues that require root/host access on the machine where the Panel is installed.
- Behaviour explicitly documented as accepted operator-side risk (for example: panel admins can install mods, and BeamMP mods running inside the game client are not sandboxed by the panel).
- Denial-of-service via authenticated abuse beyond the per-user upload quota and rate-limit windows already enforced in code.
The author makes a good-faith effort that each tagged release is free of known vulnerabilities at the time it is pushed. There is no warranty, express or implied, about future versions or about vulnerabilities discovered after a release is published. See LICENSE (sections 5 and 6) for the full disclaimer.
scryptpassword hashing with constant-time compare; cost parameters pinned in aSCRYPT_PARAMSconstant and passed explicitly to both hash and verify so the comparison never silently relies on Node's defaults. Legacy PBKDF2 hashes are upgraded on the next successful login.ADMIN_TOKENheader (when configured) is compared in constant time viacrypto.timingSafeEqualto avoid byte-by-byte timing leaks.HttpOnly; SameSite=Strictsession cookies; theSecureflag is appended automatically when the request arrived over HTTPS (direct or viaX-Forwarded-Proto: https). CSRF rejected by anOrigin/Referercheck on every unsafe HTTP method.- CSP without
unsafe-eval/unsafe-inlinefor scripts, plusPermissions-Policy,X-Frame-Options: DENY,Referrer-Policy, and HSTS when proxied via HTTPS. - Static-file allow-list — only
/dist/,/src/assets/,/src/styles.css,/index.html,/sw.js, and/manifest.webmanifestare reachable as static. - INI value sanitisation, archive entry-count and aggregate-size caps, strict zip-slip abort, streaming multipart upload to disk.
- Per-IP rate limits on login, change-password, server start/stop/restart, config writes, mod uploads. Login lockouts persist across restarts. Per-user concurrent SSE log-stream cap (6 connections) so an authenticated client cannot pin unlimited file descriptors.
- Audit log with hash-chained rows (tamper-evident); rows are canonicalised as
JSON.stringify([chain_version, prev, ts, actor, action, target, detail])so a|in a field cannot collide with a different field assignment. Legacy|-joined rows still validate viachain_version = 0. Verify withnode tools/verify-audit.js. - Forced password change for the seeded admin until a new password is set, enforced server-side on every authenticated route. The panel refuses to delete or demote the last remaining admin so an operator cannot accidentally lock everyone out.
If you are deploying the Panel in production, please also read README.md and ROADMAP.md. Per-feature deployment docs will be rewritten for the BeamMP target as ROADMAP phase 7 lands; until then .env.example, the upstream BeamMP docs at docs/beammp/, and the Dockerfile / docker-compose.yml are the authoritative sources.
npm supply-chain attacks against the open-source ecosystem are recurring: maintainer accounts get compromised (the Sep 2025 chalk/debug/ansi-styles incident, the Jul 2025 Shai-Hulud worm, the May 2024 rxnt-* typosquats, and so on). The Panel ships defences against the most common patterns:
-
npm ciin production. Refuses to install ifpackage-lock.jsonandpackage.jsondisagree — a hostile actor cannot just amendpackage.jsonand have your next deploy pull their malicious version. -
Pinned
package-lock.jsonwith integrity hashes. Every dependency in the lockfile carries a sha512 hash that npm verifies against the downloaded tarball. A tampered tarball fails the install. -
npm run audit:depsruns three independent checks before any release:- Scans the lockfile against a hand-curated list of versions known to be compromised in past incidents (kept in
tools/audit-deps.js; update by appending). - Runs
npm audit --audit-level=moderateand fails on moderate-or-higher. - Compares each top-level package's lockfile integrity hash against what
registry.npmjs.orgcurrently reports. A mismatch means either the tarball was retroactively swapped (very rare) or the lockfile was hand-edited (more common — and almost always a mistake).
Run it as part of every release checklist; consider wiring into a pre-push git hook if you push from an automated environment.
- Scans the lockfile against a hand-curated list of versions known to be compromised in past incidents (kept in
-
No
postinstallscripts in this project. The panel's ownpackage.jsononly declaresprestart(runsbuild.js— purely local) and a couple oftools/*.jsrunners. Transitive deps may run their own scripts during install; if you want to opt out of that surface in production add--ignore-scriptstonpm ci, and document any postinstall step the deps actually need (currently onlybetter-sqlite3's prebuild download, which fetches a signed binary from GitHub Releases — not arbitrary code). -
npm-shrinkwrap.jsonis not used intentionally: the lockfile is the source of truth and ships with the repo. If you fork the project, do not deletepackage-lock.jsonto "force a fresh install" — that disables every integrity check above.
If you discover that a package in our lockfile has been compromised after publication, please open a security advisory immediately so we can bump it. Do not push a fix yourself without coordinating — an emergency major-version bump of a transitive dep can break the build chain in subtle ways.