* fix(graphs): reveal graph content area after tree branch loads
The jstree select_node handler hides .cactiGraphContentArea (#navigation_right) before loading a branch's graphs via AJAX to avoid flashing the previous branch, but the only re-show was coupled to a window resize event that does not fire after the AJAX load. On themes that hide .cactiGraphContentArea by default (e.g. Deepness) the content area stayed display:none and the graphs/filters were invisible. Re-show it on loadUrl completion.
* fix(filter): keep the Edit Filter dialog columns from collapsing
The modern filter Edit dialog is created with jQuery UI width:'auto'. Its body is a table-based label/field grid, which jQuery UI measures at min-content, collapsing the dialog content to a sliver and pushing the fields off to the right. Floor the content min-width so the columns lay out. Validated in Deepness and Dark.
* fix(graphs): centre single-column graphs in the graph view
graphWrapperOuter > div:first-child is width:fit-content inside a (full-width for one column) flex wrapper, so the graph rendered flush-left. Add auto horizontal margins so it centres; the absolutely-positioned drilldown toggle continues to track the graph.
* fix(graphs): don't re-show content area on aborted branch load
A superseding tree branch click aborts the in-flight loadUrl; its .always() must not re-show the content area the new selection just hid, or stale graphs flash while the new branch loads. Skip completions whose textStatus is 'abort'.
* 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.
* 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>
* 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>
* fix(auth): audit only confirmed account persistence
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: Petr Macek <petr.macek@kostax.cz>
* perf: add disposable browser and SQL/RRD profiling
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: record profiling harness in the changelog
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* style(php): wrap CLI and poller calls without changing execution
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(ci): review security inventories after poller wrapping
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(boost): compare handoff source contracts by PHP tokens
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* style(php): wrap poller constant strings without changing values
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* style(php): wrap poller concatenations with token-preserving source contracts
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(cli): assert CDEF repair queries after constant folding
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: verify PHP formatting preserves parsed behavior
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: install locked shared plugin test dependencies
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: explain pinned tooling checkout for plugin CI
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
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>
* security: require CSRF-protected POST for link and plugin reordering (#7454)
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): share POST and token guards for reorder actions
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(auth): preserve SAML POST callback sessions
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(auth): filter SAML inputs before bootstrap
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(poller): retain output until bounded storage batches succeed
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(poller): refresh SQL audit after exact-key acknowledgements
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(boost): preserve handoff contracts with explicit acknowledgements
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(poller): retain failed timestamp ordering across batches
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(poller): acknowledge complete timestamp groups together
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(poller): retry failed source metadata reads on develop
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(poller): preload standalone script exclusions once per process
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(poller): bound metadata failures and validate acknowledgement queries
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): require POST for administrative mutation endpoints
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(csrf): support token-checking mutation helper
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(theme): enable graph opacity without selectmenu
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): describe theme fix
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(graph): restore redraw URLs and retry realtime failures
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(theme): restore Midwinter navigation and kiosk controls
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): describe theme fix
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(navigation): preserve filters and restore search and keyboard controls
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(theme): preserve Midwinter authentication controls
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): describe theme fix
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(ui): preserve form state and notification lifecycles
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix: replace the blinking timer during skin setup
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* 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>
* 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>
* Filter dialog: theme-aware close glyph, titlebar height matches tableHeader, Deepness input theming
- Replace the non-theme-aware inverted sprite close icon with a theme-coloured x glyph (color: inherit) across all 10 themes that style cactiFilterDialog.
- Set the titlebar height/line-height to match each theme's cactiTableTitleRow height instead of 10px padding that made the dialog header taller than table headers.
- Deepness: re-apply theme field colours to dialog inputs/selects/textareas with higher specificity so jQuery UI .ui-widget-content .ui-state-default no longer overrides them.
* changelog: filter dialog theme consistency (#8312)
* Filter dialog: theme select focus border; raspberry titlebar 28px to match table header box
* test(ui): cover filter dialogs across all changed themes
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>
* fix(core): preserve dsstats type and render retention fallbacks
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>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(aggregate): honor skipped item sequences in totals and percentiles
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: tighten SQL baseline for aggregate prepared queries
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): defer release entries to final backlog PR
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security: harden browser history and dependency option merging
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: record CodeQL triage decisions and follow-up scope
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: wait for history reload before inspecting browser storage
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security: isolate HTMX history from the legacy navigation handler
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: build CodeQL assets through controlled dependency installs
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security: render form validation messages as plain text
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: verify drag rows and color input security boundaries
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: verify tree navigation rename and datetime input boundaries
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security: harden menu rendering and complete CodeQL caller review
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security: centralize menu rendering and stabilize certificate CI
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: align certificate expectation with parallel web PR
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* fix(plugins): grant existing realms for CLI allperms
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci(plugins): pin database images and enforce strict shell mode
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(plugins): stop automatic grants after CLI setup failures
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): defer release entries to final backlog PR
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* 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>
* test(poller): gate regressions by changes and run weekly scale checks
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(poller): satisfy workflow policy and bound cold-cache queries
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(poller): retry failed template metadata reads
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): defer release entries to final backlog PR
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* refactor(html): share sortable column state and sort history
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor(html): normalize sortable metadata before resolving state
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(installer): preserve forced replay on already upgraded schemas
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): record forced upgrade replay repair
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Persist table column widths across request actions
Column widths are stored in localStorage keyed by the table id, but
html_start_box() derives that id from the request action/report/tab, so a
filter refresh (action change/go) reloads the same table under a different
id and the saved widths are not restored. Emit a stable, action-independent
data-colsizing key on each cactiTable and use it in saveTableWidths(), while
keeping the DOM id unchanged.
* changelog: 8309 table column width persistence
* Column width persist: keep per-view key, normalize only filter-refresh actions
The previous key dropped the action/report/tab entirely, so distinct table schemas at the same page+ordinal (e.g. support.php processes vs background tabs, utilities.php view_snmp_cache vs view_poller_cache) collapsed into one localStorage entry and applied widths from the wrong schema.
Retain the stable logical-view discriminator (tab/report, or a non-transient action) in the data-colsizing key and normalize only the transient filter-refresh actions (go/change/clear/reset), so widths still survive a filter change but never cross unrelated schemas.
* Use short array syntax for the colsizing normalization list (php-cs-fixer)
* Graph drilldown: common hamburger toggle across all themes
The graph action icons were revealed by hovering to the right of the graph, implemented per-theme (dark/midwinter/sunrise main.js) and always-on elsewhere.
Hide the icons by default behind a small fa-bars hamburger overlaid on the graph's top-right. Clicking it opens the icon strip, which stays until a second click, leaving the strip, or an outside click. The behaviour is now centralized in include/layout.js (initGraphDrilldown) and include/css/graph_drilldown.css, and the per-theme hover JS is removed.
* changelog: 8308 feature fragment
* Graph drilldown: address review (a11y, positioning, spikekill, POT)
- Anchor the toggle to the shrink-wrapped graph holder (width:fit-content) so the hamburger sits on the graph's top-right, not the full-width column edge.
- Keep the hamburger focusable while visually hidden (opacity instead of display:none); reveal on hover, keyboard focus, open, and hover-less devices.
- Expose aria-expanded on the toggle and update it in every open/close path; add aria-haspopup/aria-label.
- Close any open SpikeKill submenu (spikeKillClose) when the strip closes or switches, matching the removed per-theme handlers.
- Remove the now-duplicate Midwinter top-right ellipsis pseudo-element.
- Add the new 'Graph Actions' msgid to locales/po/cacti.pot.
* fix(installer): preserve upgrade state and propagate failures
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(changelog): record installer recovery fixes for #8266
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(security): refresh installer sink inventories
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(installer): retain merge precedence for browser parameters
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(installer): defer debug settings until the process lock is held
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(installer): enforce table conversion failures and postconditions
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(installer): refresh SQL audit baseline after clause refactor
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Harmonize DateTimePicker styling with each theme palette
The jQuery UI datepicker and timepicker addon rendered with the stock ThemeRoller texture-image backgrounds and a generic gray/green look that did not match any theme (most visible in the deepness dark theme). Add per-theme, picker-scoped overrides to every theme's jquery.timepicker.css so the popup surface, header, weekday labels, day cells, selected/hover/today states, Now/Done buttons, and the time sliders use each theme's own accent and content colors. Rules are scoped to .ui-datepicker so other jQuery UI widgets are unaffected.
* Address DateTimePicker a11y review (develop)
- Reset the nav-icon filter on prev/next hover (cacti, paw, raspberry) so the dark jQuery UI sprite stays visible on the pale hover tint.
- Give carrot and hollyberry a dark header title + dark nav icons so the month/year text clears AA on the light-ish accent header.
- Carrot: move to the theme's darker orange (#d2691e) for the today outline and sliders, and use a dark foreground on the orange selected-day/button fills.
- cacti and hollyberry: dark foreground on the accent selected-day/button fills to clear 4.5:1.
- paper-plane and sunrise: invert the icon sprite to white so the arrows are visible on their dark headers.
- dark and modern: add an explicit focus outline on the time-slider handles (both themes suppress the browser outline globally).
- midwinter: rebuild the override on the theme palette variables so the picker follows dark/light mode, with the theme blue as a fixed AA accent.
* Add a jQuery rebind() helper and roll it out across the core UI
Add $.fn.rebind(events[, selector], handler...) to include/layout.js as a
shorthand for the common off(events).on(events, ...) re-attach idiom, then
convert the matching chained calls across the core JavaScript and inline
PHP handlers to use it. Only pairs whose off() and on() events (and
delegated selector) match are converted; off() with no arguments is left
as-is since it intentionally clears every handler.
* changelog: add 8279.feature fragment
* tests: update layout.js JS tests for the rebind() helper
* tests: assert the cactiPostAction handler via rebind()
* rebind: preserve the delegated selector for the two-argument event-map overload; add helper tests
* Fix false-positive RRA-escape warning when path_rra has a trailing slash
A path_rra setting saved with a trailing slash makes CACTI_PATH_RRA end in
'/', so the '<path_rra>/' -> CACTI_PATH_RRA . '/' expansion that every data
source consumer performs yields a doubled slash ('<rra>//0/1457.rrd').
data_source_path_within_rra() split the expanded path on '/' and rejected any
empty segment, so the doubled slash was read as a traversal and a perfectly
legitimate RRD file was reported as escaping the RRA directory:
SECURITY: Data source 1457 has a data_source_path that escapes the RRA
directory (<path_rra>/0/1457.rrd). Using the generated path instead.
get_data_source_path() then returned the generated fallback path, which the
poller had never created, producing the follow-on 'RRD file does not exist'
graph warnings (note the tell-tale 'rra//0' doubled slash in those lines).
Collapse repeated slashes and strip a trailing slash from the RRA root before
the containment comparison. A '//' is an empty segment, not a parent
reference, so normalising it keeps the file contained while '..' traversal,
absolute paths elsewhere, lookalike prefixes and symlink pivots are still
rejected (asserted by the expanded regression test).
* Keep data_source_path_within_rra() normalisation strictly string-typed
preg_replace() is typed array<string>|string|null, so feeding its result to
rtrim()/strlen() tripped PHPStan (level 6/8). Cast CACTI_PATH_RRA and both
preg_replace() results to string; runtime behaviour is unchanged.
* Cover the trailing-slash CACTI_PATH_RRA half of the fix in an isolated process
The existing cases insert '//' into the target only; data_source_path_test_base()
uses realpath(), which strips the trailing slash, so they never exercise the
rtrim() on \. CACTI_PATH_RRA is immutable, so define it with a trailing
slash in a fresh PHP process and assert a clean/doubled-slash file stays
contained while a traversal is still rejected.
---------
Co-authored-by: TheWitness <1439914+TheWitness@users.noreply.github.com>