Summary
This advisory covers a set of security vulnerabilities identified by CodeQL static analysis in lightNVR. All issues were initially addressed in commit d558e69 and are fully resolved in the 0.28.0 release, which also includes additional security hardening from a broader code audit.
Vulnerabilities
CWE-476: Null Pointer Dereference (code cleanup)
- File:
src/video/stream_protocol.c:510
- Alert #39: CodeQL flagged a redundant null check on
input_ctx after the pointer had already been validated at function entry. The redundant guard was not itself a vulnerability — the entry-point check already prevented null dereference — but the dead branch was misleading and has been removed for clarity.
- Fix: Removed the redundant
if (input_ctx) check; validation occurs at function entry.
CWE-89: SQL Injection via Unvalidated Migration File Path
- File:
src/database/sqlite_migrate.c
- Alert #16: Migration file content was read and passed directly to
sqlite3_exec() without verifying the file came from the trusted migrations directory.
- Original fix (d558e69): Added
strncmp()-based path prefix validation in apply_migration() and rollback_migration().
- Hardened fix (1bdf6fe): Replaced
strncmp() with realpath() canonicalization of both the file path and the migrations directory before comparison, preventing /../ traversal sequences from bypassing the prefix check. An additional boundary check ensures the resolved path is strictly within (not merely a prefix match of) the migrations directory. The validate_migration_file_security() function also verifies the file is a regular file (via lstat()) and is not world-writable, and validate_migration_sql() allowlists permitted SQL statement types.
CWE-134: Buffer Overflow from Unchecked snprintf Return Values
- Files:
src/database/db_query_builder.c:83,88, src/database/db_zones.c:31,33, src/video/go2rtc/go2rtc_api.c:376,378
- Alerts #40-45:
snprintf() return values were not checked; a truncated write could cause silent data corruption or logic errors.
- Fix: Added
if (written < 0 || (size_t)written >= remaining) checks after every snprintf() call in the affected functions, returning an error on truncation.
CWE-367: TOCTOU Race Conditions (Time-of-check Time-of-use)
chmod via path instead of fd
- Files:
src/core/daemon.c:266, src/video/hls/hls_directory.c:84,96, src/video/hls_writer.c:457
- Alerts #25, #32, #33, #34:
chmod(path, mode) called after file creation; an attacker could replace the path between the creation and the chmod.
- Fix: Replaced with
open(O_RDONLY|O_DIRECTORY|O_NOFOLLOW) + fchmod(fd, mode) + close(fd) pattern.
stat() check before file operation
- Files:
src/database/db_backup.c:110, src/storage/storage_manager.c:225, src/web/api_handlers_recordings_backend_agnostic.c:249,338,474, src/web/api_handlers_recordings_files_backend_agnostic.c:119
- Alerts #26, #28, #35, #36, #37, #38:
stat() used to check existence before fopen() or unlink(); file state can change between the check and the operation.
- Fix: Removed
stat() checks; attempt the operation directly and handle errno == ENOENT for the "does not exist" case.
stat() following symlinks before unlink/chmod
- Files:
src/storage/storage_manager.c:460, src/video/ffmpeg_utils.c:416, src/video/hls/hls_directory.c:439,476
- Alerts #27, #29, #30, #31:
stat() was used before unlink() or chmod() operations; by following symlinks it could be deceived into operating on the wrong file.
- Fix: Replaced
stat() with lstat() to avoid following symlinks; added S_ISREG / S_ISLNK checks before destructive operations.
CWE-78: Command Injection via popen() Shell Commands
- File:
src/web/api_handlers_system.c
- User-controlled
storage_path was interpolated into du -sb <path> and find <path> ... shell commands via popen().
- Fix: Replaced all three
popen() disk-size calls with a native get_directory_size() function that uses opendir()/readdir()/lstat() recursion.
- File:
src/web/api_handlers_settings.c
- The
storage_path and storage_path_hls settings accepted arbitrary user input.
- Fix: Added
is_safe_storage_path() validator that rejects paths containing shell metacharacters before the value is stored.
Affected Versions
All versions prior to 0.28.0 (i.e., 0.27.4 and earlier).
Patched Version
0.28.0 — includes the original fix commit d558e69 (security: fix CodeQL alerts #16,#25-45), the hardened migration path validation in 1bdf6fe, plus additional security hardening from a comprehensive code audit.
Acknowledgements
- Vulnerabilities identified via CodeQL static analysis.
- Isaac Richter for reporting that the initial
strncmp()-based migration path validation was insufficient against /../ traversal, and for feedback on the CWE-476 advisory description.
Summary
This advisory covers a set of security vulnerabilities identified by CodeQL static analysis in lightNVR. All issues were initially addressed in commit d558e69 and are fully resolved in the 0.28.0 release, which also includes additional security hardening from a broader code audit.
Vulnerabilities
CWE-476: Null Pointer Dereference (code cleanup)
src/video/stream_protocol.c:510input_ctxafter the pointer had already been validated at function entry. The redundant guard was not itself a vulnerability — the entry-point check already prevented null dereference — but the dead branch was misleading and has been removed for clarity.if (input_ctx)check; validation occurs at function entry.CWE-89: SQL Injection via Unvalidated Migration File Path
src/database/sqlite_migrate.csqlite3_exec()without verifying the file came from the trusted migrations directory.strncmp()-based path prefix validation inapply_migration()androllback_migration().strncmp()withrealpath()canonicalization of both the file path and the migrations directory before comparison, preventing/../traversal sequences from bypassing the prefix check. An additional boundary check ensures the resolved path is strictly within (not merely a prefix match of) the migrations directory. Thevalidate_migration_file_security()function also verifies the file is a regular file (vialstat()) and is not world-writable, andvalidate_migration_sql()allowlists permitted SQL statement types.CWE-134: Buffer Overflow from Unchecked snprintf Return Values
src/database/db_query_builder.c:83,88,src/database/db_zones.c:31,33,src/video/go2rtc/go2rtc_api.c:376,378snprintf()return values were not checked; a truncated write could cause silent data corruption or logic errors.if (written < 0 || (size_t)written >= remaining)checks after everysnprintf()call in the affected functions, returning an error on truncation.CWE-367: TOCTOU Race Conditions (Time-of-check Time-of-use)
chmod via path instead of fd
src/core/daemon.c:266,src/video/hls/hls_directory.c:84,96,src/video/hls_writer.c:457chmod(path, mode)called after file creation; an attacker could replace the path between the creation and the chmod.open(O_RDONLY|O_DIRECTORY|O_NOFOLLOW)+fchmod(fd, mode)+close(fd)pattern.stat() check before file operation
src/database/db_backup.c:110,src/storage/storage_manager.c:225,src/web/api_handlers_recordings_backend_agnostic.c:249,338,474,src/web/api_handlers_recordings_files_backend_agnostic.c:119stat()used to check existence beforefopen()orunlink(); file state can change between the check and the operation.stat()checks; attempt the operation directly and handleerrno == ENOENTfor the "does not exist" case.stat() following symlinks before unlink/chmod
src/storage/storage_manager.c:460,src/video/ffmpeg_utils.c:416,src/video/hls/hls_directory.c:439,476stat()was used beforeunlink()orchmod()operations; by following symlinks it could be deceived into operating on the wrong file.stat()withlstat()to avoid following symlinks; addedS_ISREG/S_ISLNKchecks before destructive operations.CWE-78: Command Injection via popen() Shell Commands
src/web/api_handlers_system.cstorage_pathwas interpolated intodu -sb <path>andfind <path> ...shell commands viapopen().popen()disk-size calls with a nativeget_directory_size()function that usesopendir()/readdir()/lstat()recursion.src/web/api_handlers_settings.cstorage_pathandstorage_path_hlssettings accepted arbitrary user input.is_safe_storage_path()validator that rejects paths containing shell metacharacters before the value is stored.Affected Versions
All versions prior to 0.28.0 (i.e., 0.27.4 and earlier).
Patched Version
0.28.0 — includes the original fix commit d558e69 (
security: fix CodeQL alerts #16,#25-45), the hardened migration path validation in 1bdf6fe, plus additional security hardening from a comprehensive code audit.Acknowledgements
strncmp()-based migration path validation was insufficient against/../traversal, and for feedback on the CWE-476 advisory description.