* feat: add JWT expiry validation and refactor magic numbers
- Decode JWT in WebSocket auth to reject expired tokens and log auth failures.
- Introduce constants `OUTPUT_BUFFER_MAX_LENGTH`, `BACKUP_UPDATE_DELAY_MS`, and `JWT_EXP_MS_FACTOR`.
- Replace hardcoded buffer length and delay values with the new constants.
- Coerce `AUTH_ENABLED` and `AUTH_SETUP_COMPLETED` to boolean and add `AUTH_SESSION_DURATION_DAYS` env.
* feat(auth): sync process.env and mark setup completed after credentials update
Add a `syncProcessEnvFromFile()` function that reads the .env file and updates
process.env and the cached JWT secret, ensuring that in-memory state matches
the persisted environment without requiring a server restart.
Call this function after writing credentials or toggling authentication
settings to keep the running server in sync. Also set the setup completed flag
when credentials are updated, and clear it when disabling auth to force the
fresh setup flow on re-enable.
---------
Co-authored-by: Vivelis <¨maceo.jalbert@gmail.com¨>
* fix(security): protect servers api and redact secrets
* feat: add authentication checks to various API routes
- Implemented `requireApiAuth` in multiple settings routes to enforce authentication.
- Added checks for setup completion in the authentication setup route.
- Enhanced error handling and response messages for better clarity.
- Ensured that sensitive information is only exposed to authenticated users.
* fix: update auth module import from .ts to .js
* fix: enhance error handling in auto-sync and normalize alpine CT slug
* fix: remove unnecessary whitespace in script handling functions
---------
Co-authored-by: ProxmoxVE Developer <dev@localhost>
Whiptail dialog positioning depends on COLUMNS/LINES matching the actual
rendered terminal size. When the window opens at a fixed 1000x640px and
the user resizes, the PTY needs a resize signal (currently broken on
old deployments). Opening maximized avoids the need to resize entirely:
the PTY spawns at full-screen dimensions, whiptail is correctly centered,
and resize is not needed for normal use.
User can still restore/drag to a smaller window after the script finishes.
The ref was only updated via a useEffect, which runs asynchronously after
the state update. If ResizeObserver fires in the gap between setExecutionId()
and the effect, the resize message carries the stale initial executionId
instead of the one registered with the server -> resizeExecution silently
fails and PTY never gets the SIGWINCH.
Setting executionIdRef.current directly in ws.onopen ensures the ref is
correct before any resize messages can be sent.
install.func: use _CORE_FUNC_LOADED guard instead of BASH_SOURCE[0]
check. When sourced via herestring (pct exec), BASH_SOURCE[0] is empty
string which slipped past the /dev/stdin check, causing /dev/core.func
not found errors. _CORE_FUNC_LOADED is always set when core.func is
already loaded as part of the combined FUNCTIONS_FILE_PATH.
handler.ts + Terminal.tsx: move the xterm clear (ESC[2J ESC[H) from
server-side to client-side, sent immediately before the resize WS
message. Server-side clear had a race: whiptail SIGWINCH redraw
(PTY -> SSH -> WS) could arrive before or after our server clear,
causing it to wipe the correctly redrawn dialog. Client-side clear
is synchronous and always precedes the server's SIGWINCH redraw.
install.func: move load_functions/catch_errors outside the stdin guard.
The guard only prevents sourcing files from /dev path when running via
stdin, but load_functions (which calls icons() setting CM) and
catch_errors must always run. Previously they were inside the guard,
so CM was never set when running via pct exec -> unbound variable crash.
Terminal.tsx: add executionIdRef and include executionId in resize WS
messages. Without it the handler can't find the active PTY execution,
so pty.resize() and the screen-clear after resize were both silent no-ops.
After pty.resize() sends SIGWINCH to the remote process, ncurses redraws
the dialog at the new position but does NOT erase the old character cells.
By sending ESC[2J ESC[H to the client immediately after resize, xterm is
cleared before whiptail's SIGWINCH redraw arrives (~10-50ms SSH roundtrip),
so the new dialog renders on a clean screen with no ghost artifacts.
- ResizeObserver now debounces 150ms before sending 'resize' to server,
preventing Unknown action spam during window drag
- Start message delayed 300ms after WS open so fitAddon.fit() has run
and actual cols/rows are read, fixing whiptail dialog misalignment
Fixes syntax error 'unexpected token fi' caused by empty if...fi block.
Source calls for core.func/error-handler.func were outside the guard,
causing them to run unconditionally (including via /dev/stdin).
Bug Fixes
---------
- fix: use exact match for config file existence check in isVM (#584)
Destroy container was calling qm destroy on LXC containers because
'not_exists'.includes('exists') is true. Fixed sentinel to 'not_found'.
- fix: allow nested repository paths in URL validator (#585)
Regex required exactly two path segments, rejecting GitLab nested
groups like https://git.example.com/group/sub-group/repo.
- fix: terminal window fills floating shell properly (#586)
Fixed-height xterm classes overflowed the FloatingShell window,
cutting off content and overlapping the button bar. Added fillParent
prop so the terminal fills available space via flexbox.
- fix: cleanupOrphanedScripts wrongly deletes VMs (#589)
Two bugs: ID regex \d{3,4} dropped IDs >=10000; config path used
server.name (display label) instead of the real Proxmox hostname,
causing config lookups to always return not_found and VMs to be
incorrectly removed from the database on every startup.
- fix: generate key pair hangs when key files already exist (#590)
ssh-keygen prompts 'Overwrite (y/n)?' when target file exists;
stdin never receives input so the process hangs indefinitely.
Now deletes existing key files before spawning ssh-keygen.
Script Engine Updates
---------------------
- add tools.func from upstream
- update build.func
- add vm-core
- add install.func from upstream
- add node heap space in error-handler
- minor fixes and upgrade pve version for core
- prefer jq in json_escape with robust awk fallback
- improve LXC motd profile and OS detection
Misc
----
- bump nodejs
- fix duplicate permissions in release-drafter.yml
- Just removing a doubled word in filebrowser-quantum description (#583)
Prevent non-interactive shells from printing the motd by adding a terminal check ([ -t 1 ] || return 0). Replace fragile /etc/os-release parsing with a sourced fallback that uses PRETTY_NAME/NAME (defaulting to "Unknown OS") for more robust OS display. Overall cleans up how the container profile prints OS information while preserving hostname and IP output.
Prefer jq for JSON string encoding when available (uses jq -Rs) to guarantee correct escaping; otherwise use a safer awk-based fallback. Inputs are pre-sanitized to strip ANSI sequences and control characters, and the new awk routine processes characters one-by-one to correctly escape backslashes, quotes and tabs and to join lines as \n, avoiding gsub replacement pitfalls from the previous implementation.
ssh-keygen prompts 'Overwrite (y/n)?' on stdout when the target file
exists. With stdio: ['pipe','pipe','pipe'] nothing is written to stdin,
so the process blocks indefinitely.
Delete both the private key and its .pub counterpart before spawning
ssh-keygen so the overwrite prompt is never triggered.
Two bugs caused existing VMs to be removed from the database on startup:
1. parseListOutput used /^\d{3,4}$/ which silently dropped IDs outside the
3-4 digit range (e.g. 10000+). Changed to /^\d+$/ to accept any
numeric Proxmox ID (valid range 1-999999999).
2. The config-file double-check path used server.name as the Proxmox node
name, but server.name is a user-chosen display label that often differs
from the real hostname. The wrong path always returned 'not_found', so
every VM whose ID was missed by the regex was unconditionally deleted.
Now the real hostname is resolved once via SSH (same approach used in
batchDetectContainerTypes) before the per-ID path checks.
The Terminal component used fixed Tailwind height classes (h-[16rem],
sm:h-[24rem], lg:h-[32rem]) which overflowed the fixed-size FloatingShell
window (820x520 px), causing the content to be cut off and the button bar
to overlap with the xterm canvas.
Add a fillParent prop to Terminal. When true the outer wrapper becomes
flex h-full flex-col and the xterm div uses flex-1 min-h-0, letting it
fill whatever height the parent provides. FloatingShell passes fillParent
so the terminal now properly occupies the remaining space after the
title bar.
The previous regex required exactly three path segments (host/owner/repo),
rejecting nested GitLab group structures such as:
https://git.example.com/group/sub-group/repo
Replace with a looser pattern that only requires a host and at least one
path segment after it, which covers GitHub, GitLab, Bitbucket and
self-hosted instances with arbitrarily nested namespaces.
The checkPathExists helper echoed 'not_exists' but checked via
data.includes('exists'), which matched both 'exists' AND 'not_exists'.
This caused isVM() to always return true, making pct destroy fail
because the code ran qm destroy on LXC containers instead.
Change the echo sentinel to 'not_found' and compare with trim() === 'exists'
to ensure an unambiguous match.