mirror of
https://github.com/Cacti/cacti.git
synced 2026-11-10 06:23:13 +09:00
develop
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
edeaf135a1 |
fix(ui): use the three-bar action menu on the Details and Time View graph pages (#8641)
* fix(ui): use the three-bar action menu on the Details and Time View graph pages
The graph drilldown icons were switched to a hidden three-bar (hamburger)
action menu for the thumbnail/preview/tree graph areas, but the Details
(zoom) and Time View (utility) pages still rendered the raw glyph anchors.
Give those two areas the same graphDrillDownToggle treatment so the action
glyphs stay hidden behind the hamburger until it is activated, render the
drilldown container id through get_filter_request_var('local_graph_id'), and
move the Details graph title outside the toggle's positioning container
(matching html_graph_area() in lib/html.php) so a wide title no longer pushes
the hamburger off the graph corner.
Use the native Element.matches(':hover') in graphDrilldownStillHovered()
instead of jQuery's :hover: jQuery delegates :hover to Sizzle for a node
outside the document and throws "unsupported pseudo: hover" when a pending
close timer runs after the Details/Time View area reloads. A detached node
is treated as not hovered, so the native check cannot throw.
Add a coverage fixture that renders html_graph_single_view() and
html_graph_zoom() with their I/O stubbed and asserts the menu structure and
title placement, so the changed PHP output - not just handwritten browser
HTML - is exercised by the changed-line and request coverage gates.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(changelog): add issue#8641 entry
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* security(html_graph): html-escape local_graph_id in drilldown container id
SonarCloud/Semgrep flag the raw get_filter_request_var(local_graph_id)
echoed into the drilldown container id attribute in html_graph_single_view()
and html_graph_zoom() as reflected XSS. The value is already int-validated,
but wrap it in htmle() so the request-to-response taint path is closed and
the scanners are satisfied.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: TheWitness <copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||
|
|
4e5b2852dc |
security: harden tokens, imports, script substitution and SpikeKill mutations (#8237)
* security(batch12): constant-time 2FA cookie and reset-token compares (GHSA-4qx8-jj2h-p7q2) auth_2fa.php used === to compare the 2FA bypass-cookie HMAC and auth_resetpassword.php used != for the reset-token check; both are byte-by-byte and leak timing. Switch to hash_equals() so the comparison is constant-time. Develop-only paths (no released 1.2.x equivalent). * security(batch12): route package-repo manifest fetches through cacti_http (GHSA-54fg-q9h9-88mm) package_repos.php fetched the package.manifest for the GitHub and Direct-URL (repo_type 0/2) repository types with raw file_get_contents() - the Direct-URL path additionally disabled SSL verification - which bypassed the hardened cacti_http() client and allowed SSRF (cloud metadata, internal host/port probing) plus MITM. Both paths now use cacti_http(), which restricts the scheme to http/https, blocks private/loopback/link-local targets, pins DNS to prevent rebinding, and verifies TLS. The GitHub Bearer token is passed via the headers option. Develop-only (package_repos.php does not exist in released 1.2.x). * security(batch12): shed matched quotes around resolved data-input tokens (GHSA-5v3j-wcrr-jxjg) Field values are escaped with cacti_escapeshellarg(), which wraps them in single quotes. When a Data Input template author placed the <field> placeholder inside a matched pair of quotes (e.g. "<arg1>"), those surrounding quotes made the value's own quoting literal, so $(...) or backticks in the value executed via the shell on the next poll - a realm-3 user reaching OS command execution, bypassing the CVE-2026-39902 hardening and not addressed by the single-pass fq9x fix. substitute_script_path() now consumes one matched pair of surrounding quotes when a token resolves, so the escaped value's own quoting is authoritative. Unresolved tokens keep their literal form (including any quotes), and the single-pass behaviour that fixed GHSA-fq9x is retained. Affects released 1.2.x (<= 1.2.31) and develop; this is the develop fix, to be backported to 1.2.x. * security(batch12): regression tests + CHANGELOG for batch-12 develop fixes Add tests/Unit/Security/Batch12SecurityRegressionTest.php guarding the batch-12 sinks (GHSA-4qx8, GHSA-54fg, GHSA-5v3j) with a behavioral proof that a double-quoted Data Input placeholder sheds its quotes, plus a confirmation test that poller_spikekill.php escapes --rrdfile (GHSA-vhh2, already fixed in #7235). List the four GHSAs in the CHANGELOG. * security(batch12): update batch-11 fq9x regression test for quote-aware substitute_script_path The GHSA-5v3j quote-shedding changed substitute_script_path()'s regex and capture group, which the batch-11 fq9x test pinned verbatim. Update those two assertions to the quote-aware pattern marker and $matches[2]; the single-pass guarantee the test covers is unchanged. * security(batch12): narrow reset-hash row with is_array for PHPStan L6 The hash_equals() guard left $hash inferred as non-empty-array|true, so $hash['user_id'] tripped PHPStan level 6. Guard with is_array() instead of a truthy check; db_fetch_row_prepared returns false for no row, so behavior is unchanged. * security(batch12): drop the executable bit on the new security test files * security(batch12): route import-time repo manifest fetches through cacti_http (GHSA-54fg) get_repo_file() still used raw file_get_contents() for the GitHub and Direct-URL repo types, bypassing the SSRF hardening added to form_save(). Route both remote branches through cacti_http() (scheme/host validation, private-range block, DNS pinning, TLS verify; Bearer token via the headers option) and add a regression assertion covering the import sink. * security(batch12): make substitute_script_path fully quote-aware (GHSA-5v3j) The matched-pair quote shed only neutralised a token the template wrapped exactly ("<arg1>"). A token embedded in a larger quoted word ("prefix<arg1>", "<arg1>suffix", "<a><b>") kept the outer double quotes, so backticks/$(...) in the already-escaped value stayed live - the bypass Copilot flagged. Replace the regex with a single-pass shell-quote-state scanner: it still sheds an exact wrapping pair, but for any token inside a larger quoted word it briefly closes the template's quoting around the escaped value so it cannot be re-interpreted. Values are never re-scanned (single-pass, GHSA-fq9x retained). Updates the batch-11/12 source assertions, adds prefix/suffix/mixed regression cases, and drops the Batch12 eval fallback (the unit bootstrap already loads lib/functions.php via include/global.php). * security(batch12): relocate batch-12 regressions into vuln-class subfolders Split tests/Unit/Security/Batch12SecurityRegressionTest.php into behavior-named files under the existing vulnerability-class layout, per the review feedback: Auth/TwoFaCookieAndResetTokenConstantTimeTest.php GHSA-4qx8 Ssrf/PackageRepoManifestFetchTest.php GHSA-54fg ShellInjection/DataInputQuotedPlaceholderTest.php GHSA-5v3j ShellInjection/SpikekillRrdfileEscapeTest.php GHSA-vhh2 Each file reaches the repo root via dirname(__DIR__, 4) for its one subfolder-deep location. No assertion changes; pure relocation. * security: preserve template quoting for raw path tokens in substitute_script_path Keep the develop fix in sync with the 1.2.x backport (#8238): substitute_script_path() only sheds/closes the template's quotes for self-quoting (cacti_escapeshellarg) values. Trusted raw path_* tokens are substituted in place so a quoted "<path_php_binary>" keeps its quotes for the shell and a bare <path_cacti>/scripts/x.php stays unquoted for the PHP script server, which resolves the first token as a filesystem path. Reformatted per php-cs-fixer and added a raw-path-token regression test. * security: keep substituting a token that follows a backslash in substitute_script_path Mirror the 1.2.x fix (#8238): the scanner's backslash branches consumed the next character unconditionally, so a legacy/custom template token such as \<arg> never reached the matcher and the trailing cleanup stripped it to a bare backslash. Both branches now only pair the backslash with a following quote/backslash (so an escaped quote still cannot toggle quote-state); any other character - notably a <token> start - leaves the backslash on its own and is processed on the next iteration. Reformatted per php-cs-fixer and added a backslash-before-token regression test. * security: normalize dangling backslash before substituted value in substitute_script_path (GHSA-5v3j) A template backslash immediately before a <field> token (\<arg1>) escaped the already-escaped value's opening quote, producing \'...' which the shell rejects with an unmatched-quote error (odd backslash runs) or prepended a stray literal backslash (even runs). The quote-aware scanner now sheds one backslash when the run immediately preceding a self-quoted value is odd, in both the unquoted in-place branch and the close-around branch, so the value's own quotes always function as the shell's quotes and $(...)/backticks inside it can never re-activate. The backslash regression test now executes each substituted template through /bin/sh (printf %s) and asserts a clean parse plus a literal round-trip across odd/even backslash counts, rather than pinning the generated string. * fix(security): preserve literal backslashes in quoted script fields Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): cover changed guards and gate PR coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: measure committed PR changes on a policy-compliant runner Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * security: address review - nested cmd-sub/backtick handling, coverage gate, test skip substitute_script_path() now follows the template's own $(...) and backtick command substitutions as nested parsing contexts with their own quote state, so a token placed inside one ("$(printf %s <field>)") is judged against that fresh context instead of a suspended outer double quote. This closes the nested-reinterpretation bypass where an escaped value sitting inside a command substitution could be re-activated by an outer double quote. Backslash pairing in both the unquoted and double-quoted contexts now also neutralizes $ and backtick, so an escaped command substitution (\$(...) or \`...`) is not mis-detected as live. .github/workflows/pr-changed-coverage.yml: drop the hard-coded PR-number allowlist so the job runs whenever the path filter selects it, rather than silently skipping every unlisted PR once these merge. tests/Unit/Security/ShellInjection/ScriptFieldShellRoundTripTest.php: skip when either exec() or proc_open() is unavailable (the test launches the fixture with exec() while the fixture itself uses proc_open()). The fixture adds 7 nested command-substitution and backtick round-trip cases (90 -> 97), each proving the field value echoes back verbatim with nothing inside it executing. * fix(security): clear quote-empty flag on single-quote content; cover new parser branches The stack-based substitute_script_path() rewrite left quoteEmpty set while emitting literal content inside a single-quoted region, so a template like '\<field>' wrongly took the exact-wrap branch and dropped the backslash (ScriptSubstitutionMatrix 'shell 56'). Clear the frame's quoteEmpty whenever a single-quoted character is emitted, matching the pre-rewrite fallthrough. ScriptSubstitutionMatrix.php: add 8 explicit parser cases exercising the \/backtick nested contexts and escaped \$ / backtick pairing so every rewritten line is covered by the changed-coverage probe (1285 -> 1293). * style: short array syntax and php-cs-fixer formatting in substitute_script_path * test(security): tolerate formatting in spikekill escaping contract Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(spikekill): bind menu requests and protect mutations on develop Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(spikekill): validate real CSRF tokens and inline coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci(coverage): install locked dependencies for Symfony CSRF probes Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * perf(spikekill): skip disabled float debug formatting Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(spikekill): enforce mutation scope and propagate RRD failures Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(spikekill): escape repair notes and cover window boundaries Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: remove unused SpikeKill preference binding Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(security): reject unsupported nested shell grammar in #8237 (#8359) * fix(security): reject unsupported nested shell grammar Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): exercise nested grammar rejection in CI coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(security): reject script fields in backtick substitutions Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: retain native security fixture evidence in changed coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(security): reject ambiguous shell expansion contexts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(ssrf): assemble insecure manifest schemes at runtime The SSRF negative-test inputs in PackageManifestMatrix.php are rejected by the import preflight policy, but storing them as clear-text URL literals (http://, ftp://, file://) tripped SonarCloud php:S5332 and dropped the New Code security rating to B. Join the scheme separator at runtime so the fixture no longer carries clear-text literals while the assembled values keep the exact insecure schemes the policy must reject. All 32 manifest cases still pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(spikekill): drop dangling href in removeSpikes redirect check The CSRF POST rewrite of removeSpikes replaced the GET href builder but left checkForRedirects(data, href) referencing the now-undefined href, throwing ReferenceError once a POST completed. Call checkForRedirects(data) so a redirect response reloads (a POST has no GET href to follow). Update the merged dom_dynamic_boundaries boundary test to exercise the POST path: define csrfMagicToken and mock $.post instead of $.getJSON, so it still verifies DOMPurify sanitization of spike results. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <copilot@users.noreply.github.com> Co-authored-by: TheWitness <1439914+TheWitness@users.noreply.github.com> Co-authored-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
ff31e051dd |
fix(a11y): [develop] correct labels, document semantics and duplicate IDs (#8295)
* fix(a11y): [develop] correct labels, document semantics and duplicate IDs Associates form controls with their labels, replaces obsolete tt markup, removes duplicate element IDs, and adds a native accessibility browser suite with its database fixture. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(a11y): validate label IDs and recognize attribute escaping Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
6254b2c17d |
fix(package): name template archives without export-form fields (#8431)
* fix(packaging): name archives from supplied metadata Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(packaging): make archive leaf-name confinement explicit Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(e2e): move the pointer off the Deepness multiselect button Deepness sets a zero body margin, so the unhovered #ms-button sits at the viewport origin and headless Chromium on Linux intermittently applies :hover to it. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: pull pinned integration database from authenticated mirror Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
2761fdc226 |
fix(rrd): [develop] retain source identity after CF fallback (#8339)
* fix(rrd): [develop] retain source identity after CF fallback Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: measure native consolidation fallback coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
5afe0f8f68 |
Add one-click RRDfile repair (Apply Suggested Fixes) to Data Source Info mode (#8401)
* Add one-click RRDfile repair to Data Source Info mode The Data Source Info mode already compares the RRDfile against the Cacti definition and prints the rrdtool tune recommendations (add a missing Data Source with DS:name:type:heartbeat:min:max, remove an extra one with DEL:name, and correct the type, heartbeat, minimum and maximum). It did not, however, offer a way to apply them from the page the way data_debug.php does. - Add an "Apply Suggested Fixes" link to the "RRDtool Tune Info" box header when the web server can write the RRDfile; when it cannot, the header falls back to "Run Fix from Command Line" and the commands remain listed for manual use. - Add a ds_repair action that runs the same tune recommendations server-side (gated on data_source_authorized() and on the RRDfile being writable, and skipping any recommendation carrying control characters), logs each command to DSDEBUG, then reloads the Data Source so the result is visible. This mirrors data_debug.php's run_repair / "Apply Suggested Fixes". - Harden the Info-mode guard so a missing/unreadable RRDfile (rrdtool info returning false) no longer triggers an array-offset warning. * Address review: POST-guard ds_repair and harden repair result check - Route the "Apply Suggested Fixes" action through a CSRF-protected POST: the link now uses the cactiPostAction handler (href="#", data-url) instead of a GET navigation, and ds_repair is added to the $bad_actions guard in include/global.php so a tokenless request fails closed with 405. ds_repair is added to the curated CSRF action test for regression coverage. - Use a strict comparison on the rrdtool_execute() result: it can return null on a failed exec/proxy connection, and null == '' would have reported a failed repair as "All RRDfile repairs succeeded." - Refresh the sink inventory baseline for the new header redirects in ds_repair. * Add request-coverage probe for the ds_repair RRDfile repair handler The Data Source Info mode repair handler ds_repair() is a POST action that the page-level e2e suites do not drive (it needs a Data Source whose RRDfile mismatches its Cacti definition). Add a request-coverage fixture that loads the handler in isolation via cacti_fixture_source_function(), stubs its I/O boundaries, and drives every branch (unauthorized, unreadable RRDfile, no RRA, nothing to tune, not writable, a control-character recommendation that is skipped, and both tune success and failure). Register it in run_request_coverage.sh for data_sources.php changes so the changed-line coverage gate sees the handler exercised. * Address review: use defined helpers and boolean repair result - Replace the undefined cacti_has_control_chars() check with a comparison against rrd_strip_control_chars() (loaded from lib/rrd.php) to skip any tune recommendation that carries control characters. - Execute each tune with RRDTOOL_OUTPUT_BOOLEAN and treat a strict true as success so proxy storage (which returns the raw 'OK u:...' status under RRDTOOL_OUTPUT_RETURN_STDERR) is not misreported as a failure. - Add the new translatable strings to locales/po/cacti.pot and reuse the existing failure msgid. - Update the request-coverage fixture to the new boundary contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
315964cae8 |
Log administrative user-account actions on develop (#8281)
* log user action (create, delete, locked, ..) * change log * update changelog * style(auth): apply coding standards to user action logging Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Audit password changes only after successful account saves Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(auth): sanitize audit fields and confirm account removals Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover native account audit state transitions Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: synchronize native coverage fixture dispatch Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: measure coverage at the exact PR revision Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
ffd7a81109 |
Scope the tree Graph Templates filter to the drilled Device Template (#8370)
* Scope the tree Graph Templates filter to the drilled Device Template When drilling a Site Branch into a Device Template (host_id=-1, host_template_id set), create_tree_filter() fell through to listing every Graph Template. Scope the dropdown to the Graph Templates present on that Device Template's devices (within the drilled Site when set), matching the graphs the branch actually shows. * docs(CHANGELOG): add issue#8370 entry * test: cover the tree Device Template graph-template scope; fix CHANGELOG ordering Add an isolated source-slice probe that drives create_tree_filter()'s new host_template_id branch so the changed lines meet the >=80% changed-line coverage gate, wired into run_request_coverage.sh. Move the issue#8370 CHANGELOG entry into the issue block so it no longer precedes the security entries. * docs(CHANGELOG): append issue#8370 at the end of the issue block |
||
|
|
2fdc8747af |
Cross-filter Device and Graph Template on Preview and List views (#8363)
* Cross-filter Device and Graph Template on Preview and List views
Forward the selected Graph Template(s) to the ajax_hosts Device callback and filter the device list accordingly, so choosing a Graph Template narrows the Device list. Device-to-template narrowing already exists on these views.
* Use line comment style to satisfy php-cs-fixer
* fix(graph_view): validate raw graph_template_id against filter allowlist in html_make_device_where
The ajax_hosts endpoint calls html_make_device_where() before the graph filter is sanitized, so gnrv('graph_template_id') is raw request data. An array value such as graph_template_id[]=cg_1 reached html_transform_graph_template_ids()'s str_contains() and raised a TypeError (HTTP 500). Reject non-string input and validate against the same regexp allowlist the filter uses before transforming.
* test(graph_view): add request-coverage probe for the device/template cross-filter
The pr-php-coverage gate requires >=80 percent of changed executable PHP lines to be exercised. The new html_make_device_where() graph_template_id branch and the request_vars changes in create_graphs_preview_filter()/create_listview_filter() are only reached when a graph_template_id filter is present, which the page-level e2e suites do not drive. Add an isolated source-slice probe that runs those production functions with stubbed I/O boundaries and wire it into run_request_coverage.sh so the changed lines are covered.
|
||
|
|
4d11070ced |
feat: expose RRDtool 1.11 graph axis controls (#8356)
* feat: expose RRDtool 1.11 graph axis controls Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: constrain axis template inheritance checkboxes Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(rrd): exercise native aggregate validation and enforce coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: share changed-code coverage contracts and enforce shell policy Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(ci): run request coverage with portable path matching Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: retain bootstrap coverage and validate request dispatch Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: synchronize native coverage fixture dispatch Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: measure coverage at the exact PR revision Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
8acc5ec916 |
feature: theme-aware graph drilldown action menu (per-theme CSS palette) (#8355)
Label the open graph drilldown strip (layout.js labelGraphDrilldownIcons), shorten the action titles, and restyle the strip as a table menu. Each theme's main.css declares the --cacti-dd-* custom properties (foreground, hamburger/panel backgrounds, hairline border, hover accent) that include/css/graph_drilldown.css consumes via var() with a dark fallback, so each theme gets an appropriate light or dark look with its own accent and the hamburger reads correctly on light themes. Themes without a block (e.g. midwinter) use the dark fallback. Regenerates the affected locales/po/cacti.pot entries for the shortened titles. Co-authored-by: TheWitness <1439914+TheWitness@users.noreply.github.com> |
||
|
|
f39e74c65c |
Graph drilldown: keep hamburger fixed, icons reachable, hide on graph leave (#8315)
* Graph drilldown: keep hamburger fixed, icons reachable, hide on graph leave The action-icon strip now slides down below the hamburger (flex column) instead of the hamburger dropping to the bottom of the icons. Closing is driven by leaving the whole graph rather than the small icon strip, so the mouse can move from the hamburger onto the icons without the strip closing. Leaving the graph also blurs the hamburger so it no longer lingers after interacting with it. * changelog: graph drilldown interaction fixes (#8315) * Graph drilldown: tolerate tooltip-induced mouseleave, drop hamburger title A native tooltip appearing over a drilldown icon fires a spurious mouseleave on the graph (relatedTarget null), which closed the strip. Defer the close and re-check the graph is genuinely not hovered first, so the icon tooltips no longer destabilize it. Also drop the hamburger's redundant title (its aria-label stays). * Graph drilldown: widen the close grace period to 500ms * Graph drilldown: tie hamburger visibility to the graph image Reveal the hamburger while the pointer is over the graph image (or the toggle overlaid on it) rather than anywhere in the graph cell, so moving off the image hides it after the grace period. Entering a different graph now immediately hides and closes any other graph's open strip. * Graph drilldown: lift the open graph so its strip clears thumbnails below With thumbnails a short graph's open icon strip overflows onto the graph below; sibling graphs paint in DOM order, so that graph rendered on top of the strip, making the icons unclickable and stealing the hover (which closed the strip). Raise the open graph (.graphDrillActive, position:relative; z-index) so its overflowing strip stays on top and the graph beneath is not hovered. * Graph drilldown: reveal via :focus-visible and stop blurring to preserve keyboard focus * fix(ui): preserve graph action focus during pointer navigation Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(ui): cover graph drilldown interactions and gate PR coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: measure committed PR changes on a policy-compliant runner Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
34fc3f8642 |
Stop flagging a not-yet-created RRA subdirectory as a data_source_path escape (#8313)
* Stop data_source_path_within_rra() flagging a not-yet-created intermediate directory as an RRA escape get_data_source_path() routed a legitimate <path_rra>/<host_id>/<local_data_id>.rrd through the generated-path fallback and logged a bogus 'SECURITY: ... escapes the RRA directory' message whenever the host_id subdirectory did not exist yet (first poll, or host_id 0). data_source_path_within_rra() only tolerated a missing final RRD file: for a missing file it resolved realpath(dirname(path)), which returns false when that parent directory has not been created, so the lexically-contained path was wrongly rejected. Walk to the deepest ancestor that actually exists and confirm it resolves inside the RRA root instead. Lexical containment (no .., '.', or empty segments) and per-segment symlink rejection are unchanged, so traversal, lookalike-prefix, absolute, and symlink-pivot paths are still rejected. Adds a regression test for a not-yet-created intermediate directory. * Drop explanatory comments from the RRA containment fix * Add CHANGELOG fragment for the RRA containment fix * Reject a dangling-symlink segment independently of file_exists() * test(paths): cover RRA filesystem boundaries and gate PR coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: measure committed PR changes on a policy-compliant runner Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
08d0f94f24 |
Fix develop client behavior, popup isolation and PHP method visibility (#8292)
* fix(core): correct client state and declare public method contracts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(security): isolate Midwinter external homepage popup Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(client): isolate menu state and simplify validated JavaScript guards Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(auth): use certificate expiry truth in rendering assertions Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * refactor(js): remove unused local storage while retaining initializer calls Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(js): encode inline translated values through the shared JSON boundary Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * style(js): wrap encoded translation literals without changing messages Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(client): remove unused checkbox reads from graph resizing Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * refactor(client): declare repeated function locals once Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(ui): run column width regression in browser CI Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |