security: replace rand() with hrtime(true) for graph cache-buster (GHSA-vhwj-hfwg-gfg3) - #806
Conversation
somethingwithproof
left a comment
There was a problem hiding this comment.
random_int() can throw Random\RandomException when the platform CSPRNG fails, which would make threshold editing fatal merely to produce a non-security cache-buster. This URL parameter is not a secret or security boundary, so a CSPRNG is the wrong reliability tradeoff. Please use a non-throwing cache version (for example a timestamp/monotonic value) or catch the exception, add a focused Pest assertion through Cacti Composer, and add the CHANGELOG entry. The integration matrix is currently red.
eb78754 to
8d3abd0
Compare
|
The latest revision fixes the non-throwing cache-buster implementation, but the regression test still exercises mt_rand() while production now calls hrtime(true), so it cannot fail if the production line regresses. The CHANGELOG also contains both an mt_rand entry and an hrtime entry. Please make the test assert the actual thold.php behavior, keep one accurate CHANGELOG entry, and remove the unrelated duplicated workflow changes (including removal of the advisory develop matrix) from this security fix. |
…245) Replaces the non-cryptographic PRNG rand() with hrtime(true), a monotonic nanosecond clock that never throws. This URL parameter is a cache-buster, not a security boundary, so a CSPRNG (random_int) is the wrong reliability tradeoff — it can throw Random\RandomException on CSPRNG failure, making threshold editing fatal. hrtime(true) provides: - Never throws (no CSPRNG dependency) - Monotonic (never goes backwards on NTP step) - Nanosecond resolution (no collisions on fast page renders) - Returns int (clean URL query parameter) Fixes GHSA-vhwj-hfwg-gfg3 Rule: php:S2245 (CWE-338)
b231ac0 to
c50565c
Compare
|
Rebased onto develop to clear the conflict. Your commit and authorship are unchanged; only the CHANGELOG resolution differs. The conflict was the top line of the develop section, which every open PR here edits. Two of the three entries the branch added were dropped:
Your own entry further down the file is the one that survived, unedited. The second commit, Two things left alone because they are yours to decide:
|
These security/robustness fixes were already documented in CHANGELOG.md and
covered by tests (GetAllowedThresholdsTest, TholdCommandExecutionTest,
TholdReplaceThresholdTagsTest, TholdExpressionMathRpnTest,
OptionalCoreFunctionTest), but the corresponding thold_functions.php changes
were missing, leaving the Pest suite failing on every PR built on develop.
- get_allowed_thresholds()/get_allowed_threshold_logs(): bind graph_id and
caller-supplied params instead of interpolating them into the SQL text,
and use the *_prepared fetchers.
- thold_replace_threshold_tags(): add a \ flag that quotes
device/threshold free-text values with cacti_escapeshellarg() when the
substituted text is a trigger command; email/notification callers are
unaffected.
- thold_command_execution(): pass shell=true when building the three
trigger commands, and fix a topic-string typo ('thold' vs 'thold_cmd')
that silently suppressed exit-status logging for inline (non-queued)
command runs.
- thold_expression_math_rpn(): remove a stray break that exited the
operator switch before pushing the 0/0 result, flag modulo-by-zero
instead of letting PHP throw DivisionByZeroError, reject non-numeric
unary operands before eval(), and flag SQRT/LOG results that are NAN or
INF instead of pushing them onto the stack.
thold_functions.php includes includes/arrays.php with a plain include() (not include_once()) from several of its own functions (e.g. thold_log()), keyed off \['base_path']. Once any test in the shared Pest process exercised one of those call sites, PHP's include-once registry considered the file already included: a later thold_test_load() (require_once) on the same resolved path silently no-op'd and never (re)published \ to \, depending on which order the test files happened to run in. This intermittently broke every test that reads \ (TholdReplaceThresholdTagsTest's THOLDTYPE substitution) as well as tests several call frames downstream of thold_log()/thold_check_threshold() (NotificationEmailDeduplicationTest, ThresholdTimeBasedCharacterizationTest), depending on file execution order. Add thold_test_load_always()/loadPluginSourceAlways(), which use a plain require() instead of require_once(), for plugin files that only assign file-scope variables (no function/class declarations) and are therefore safe to load more than once. Switch the four test classes that load includes/arrays.php to the new helper.
Adds stderr diagnostics behind THOLD_CI_DEBUG to see the real values computed on the Linux Pest CI run for the two failures that do not reproduce locally on Windows. Will be removed once the root cause is identified.
This reverts commit 7187747.
…t failures" This reverts commit 76d05ad.
This reverts commit 7ce80b2.
This reverts commit 9aff7a4.
This reverts commit 4bb7f44.
# Conflicts: # CHANGELOG.md # thold_functions.php
Giving copilot the wheel on this one.
# Conflicts: # CHANGELOG.md
Security Fix — GHSA-vhwj-hfwg-gfg3
Fixes GHSA-vhwj-hfwg-gfg3
Summary
The threshold editing page in
thold.phpused PHP'srand()function (line 1278) to generate a cache-buster query parameter for a graph image URL.rand()is not a cryptographically secure pseudo-random number generator (CSPRNG).Vulnerability Details
php:S2245(SonarQube)thold.php, line 1278Changes
Replaced
rand()withhrtime(true), a monotonic nanosecond clock that never throws.Why not
random_int()? An initial fix usedrandom_int(0, PHP_INT_MAX), but reviewer feedback correctly identified thatrandom_int()can throwRandom\RandomExceptionwhen the platform CSPRNG fails. This URL parameter is a cache-buster, not a security boundary, so a CSPRNG is the wrong reliability tradeoff — it would make threshold editing fatal merely to produce a non-security cache-buster.hrtime(true)provides:php:S2245entirely (no "random" function to flag)Testing
php -l thold.php)php:S2245is CLOSED