* 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>
* 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>
* 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>
* 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(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>
* test(cdef): enforce resolver coverage
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci(cdef): pin cache action correctly
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: isolate CDEF coverage bootstrap
* fix(cdef): fail closed on invalid recursive definitions
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): log corrupt resolver records
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): bound and isolate recursive resolution
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): preserve resolver failure state
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): isolate invalid graph expressions
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): abort unsafe consumer operations
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): close consumer failure paths
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): make aggregate updates atomic
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): keep repair queries auditable
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): bind bulk deletes and escape item types
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cdef): guard corrupt types and repair reporting
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(aggregate): propagate push-out failures
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(aggregate): document mutable message checks
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* build: add symfony/security-csrf
Backs the CSRF token core that replaces the vendored csrf-magic fork.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: fix security-csrf package path assertion
symfony/security-csrf ships CsrfTokenManager.php at the package root, not under src/.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor: register the composer autoloader before the CSRF include
CSRF validation ran before the autoloader existed, which blocks any class-based guard.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* feat(csrf): add CactiCsrfGuard over symfony/security-csrf
Issues and validates session-scoped tokens; not yet wired into the request path.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* feat(csrf): accept pre-upgrade csrf-magic tokens during the grace window
Legacy tokens carry their own expiry, so the fallback closes itself without a config flag.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* feat(csrf): port the output buffer rewriter to CactiCsrfGuard
Keeps the hidden field name and JS globals that third-party plugins rely on.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): use json_encode for the JS script context in rewriteBuffer
htmlspecialchars() protects the HTML attribute context but not the
<script> string literal, which relied on Symfony's token generator
never emitting a quote rather than being safe by construction.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor(csrf): move the XHR shim to include/js as Cacti-owned code
It has been maintained by the Cacti Group since 2020 and is not an upstream package.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor(csrf): retire the vendored csrf-magic fork
The procedural csrf_* surface stays as a deprecated shim so plugins keep working.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* feat(csrf): validate requests through CactiCsrfGuard
csrf-magic self-invoked from its own file; the guard is called explicitly.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(install): stop gating the permission step on the csrf secret
Retiring csrf-magic removed the directory its default path lives in, and setStep() clamps to the first errored step, so no fresh install could advance.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): key the legacy fallback on the raw secret bytes
refresh_csrf.php writes PHP source with a trailing newline, and trimming that yields a different HMAC key than csrf-magic used.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): log when the guard degrades to disabled in a web context
The installer is the only web path that can reach it, and failing open in silence is not something csrf-magic could do.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: changelog for the CSRF library replacement
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): mint the token before the session write callback closes it
csrf_startup() minted lazily from the output buffer callback, which runs after session_write_close()'s shutdown-function registration, so a token minted there on a legacy-fallback POST was silently dropped.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): make refresh_csrf.php a no-op
Its default secret path no longer ships and live tokens are session-bound, not HMACs over the secret, so rotation neither worked nor did anything meaningful; running it during the upgrade grace window used to delete the legacy secret and kill validation for pre-upgrade tokens outright.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): fall back to the old default secret path when reading it
csrf-magic tried its own directory as a second candidate regardless of $path_csrf_secret; the new reader only tried the configured path, so a site pointing $path_csrf_secret at a file that was never created lost the legacy-token grace window entirely.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(csrf): point the JS header at its BSD license sidecar
The header reduces the original BSD terms to prose and the actual text sits in an unreferenced csrf.js.LICENSE, so add a pointer to it.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): restore csrf_check()'s bool return and $fatal contract
The shim ignored $fatal and always returned void, so audit/audit.php's `!csrf_check(false)` evaluated `!null` (always true) and denied every request instead of getting a real pass/fail result back.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: reference the pull request number in the changelog entry
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: resolve includes through Cacti path constants
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(csrf): enforce full coverage
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Harden CSRF fallback handling
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(csrf): assert the component stays out of the index
Committing it would leave a second copy to drift against the lock.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: align CSRF checks with PHP 8.2
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: normalize the CSRF changelog entry
* Fix CHANGELOG entry ordering
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(csrf): support constrained test environments
* fix(csrf): narrow nullable guard state
* test: place CSRF coverage in security domain
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security(test): match disabled exec exactly
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: run CSRF coverage on PHP 8.3
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* fix(auth): reject malformed remember cookies
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: load cookie contracts lazily
* test(cookie): fail clearly when contract markers move
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(auth): align persistent credential lifecycle
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(auth): find cookie function boundaries robustly
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* hardening: validate the login realm and escape the LDAP username per context
domains_login_process() now rejects a realm get_auth_realms() does not list and
fails closed on one that is listed but has no domain behind it, and the Ldap
class escapes the username for the DN and the search filter separately rather
than relying on a filter-only blocklist.
- an unlisted realm sent with a non-empty password used to match no branch at
all and returned an empty user with no error, which auth_login.php reads as
an authenticated user and provisions from the template account
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security: reject LDAP logins that skip a domain bind
Posted realms outside user_domains no longer fall through to template or guest login.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(ldap): align escaping contract with username lookup
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: cover LDAP domain login through OpenLDAP
The e2e stack now binds a real directory user and checks that a forged realm cannot skip that bind.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(ldap): address Copilot review feedback
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix: substitute LDAP filter placeholders in one pass
LDAP_ESCAPE_FILTER leaves angle brackets intact, so a looped str_replace can rescan one value as another key.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Change realm retrieval function from gnrv to gfrv
* tests: add gfrv() stub to harnesses that exercise gfrv()-calling code
domains_login_process() and api_aggregate_convert_template() call gfrv() for
realm/aggregate_template_id, but their test harnesses only stubbed gnrv(),
so Pest failed with 'Call to undefined function gfrv()'.
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* fix(csv): align export and graph sample windows
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci(csv): satisfy release policy checks
* test(csv): align legacy capacity contract
* test(csv): add standard license headers
* fix(rrd): infer step from trailing fetch row
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci(csv): trigger on resolution query coverage
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(rrd): mirror fetch helper arguments
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(rrd): isolate percentile globals
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(rrd): annotate intentional eval
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(rrd): restore integration config
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(rrd): enable strict types
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(rrd): restore font config
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: align CSV coverage with supported PHP versions
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* fix(csrf): require POST tokens for item actions
* test(csrf): cover every guarded action path
* test(csrf): keep legacy guard contract extensible
* fix(csrf): hide POST-only targets from crawlers
* test(csrf): reject every crawlable state-action link
* fix(csrf): fail closed without logging crawler probes
* fix(csrf): post data-query item actions safely
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): normalize stripped token separators
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): distinguish method and token failures
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): escape POST action URL attributes
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): delegate POST action events
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(csrf): post data template add action
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* style(html): align POST button class
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* feat(cli): add unified command frontend and normalize legacy failures
Add the Symfony-backed bin/cacti entry point while preserving legacy argument, terminal, signal, and exit-status behavior. Fold in the focused legacy CLI correctness fixes from #7906 without adding its unused parser abstraction.
Keep path containment and the PHP 8.3 floor out of this review; the latter remains in #7939.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(cli): enforce PHP floor and deny web dispatch
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* build: raise the 1.3 floor to PHP 8.3
RHEL 10 ships 8.3 in AppStream and dropped modularity, so an 8.4 floor would
put every RHEL, Alma and Rocky 10 install on Remi. Ubuntu 24.04 is 8.3 as
well, which makes it the lowest common denominator of the current LTS fleet.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: raise the documented PHP floor to 8.3
The 1.2.x column keeps 8.1, which still matches that branch's composer constraint.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: run process lock coverage on PHP 8.3
* fix: finish PHP 8.3 floor cleanup
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
process-only was empty, so lock-threads walked discussions as well, and the
job has no discussions permission. The exempt lists also matched only the
flat label names, which would have exposed 238 open enhancements to the
stale closer once those labels are retired.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Serialize process registry admission with Symfony Lock
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Fix PDO process lock mode handling
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: run process lock coverage on PHP 8.2
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix: confirm a pid is still ours before signalling it
#7574 added the still-running guard to dsstats but left six byte-identical
siblings signalling a pid straight out of the processes table. After a crash or
a power loss the row survives, the pid is recycled by an unrelated daemon, and
the next start SIGTERMs a stranger. That is issue #7425, still live in rrdcheck,
boost, poller_commands, float_rrdfiles, poller_reindex_hosts and
rebuild_poller_cache.
Wrap each kill in cacti_process_still_running(), matching the dsstats shape, and
leave unregister_process() outside the guard so a stale row is still cleaned up.
Cover all seven with a test that walks every kill site, so a new variant cannot
reintroduce it.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: assert the guard plainly
toContain() takes needles rather than a failure message, so the file name was
being asserted as a second required substring and the test always failed.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(poller): describe required lock parameters
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: share one body across the per-script coverage workflows
Eight of the coverage workflows were the same file with four things changed:
the PHPUnit configuration, the job name, occasionally the PHP version, and
occasionally the snmp extension. Everything else, the triggers, permissions,
concurrency, checkout, dependency setup and the Pest invocation, was copied
between them, so a change to any of it meant eight edits and eight chances to
miss one.
They now call a reusable workflow and supply only what differs. Each caller
keeps its own path filters and concurrency group, since those are genuinely
per-script.
installer, snmp and rrd-graph-item are left alone. They run more than one
suite or produce clover output, so folding them in would mean parameterising
the shared body until it stopped being shared.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: rebuild the callers when the shared coverage body changes
Each caller watched its own file but not the reusable one, so editing the
shared body would not have run any of the suites it drives.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
There was no root CONTRIBUTING.md and no way to run the CI gates locally in one
step, so reproducing a CI failure meant reading workflow YAML. Add a
'composer check' target that chains the gates, expose the sink and hotspot
guards, the changelog validator and the JavaScript tests as their own targets,
and document setup and the changelog.d fragment workflow.
Pin Node in .nvmrc to the version CI uses, and move the one remaining
setup-node still on 20 to 22 so every workflow agrees.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The recursive wget crawl is the single most expensive thing in CI. Measured over
the last 120 runs, the Commit Audit workflow is half of all CI minutes, the
locked integration job is 72 percent of that workflow, and this one step is 60
percent of that job: 422 seconds of a 16 minute run.
It walks the authenticated UI one request at a time, so nothing it covers can
change unless server-rendered code, assets or the schema did. Skip it when a
pull request touches only tests, workflows, translations or documentation.
The step is gated rather than the job, so the required-check aggregator still
sees a real result rather than a skip. The decision fails open: an unreadable or
empty diff, or a push rather than a pull request, runs the crawl.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore: manage front-end JavaScript libraries with npm and Dependabot
Move all third-party front-end JavaScript to npm so Dependabot tracks it, and
drop the committed copies in favour of a build. package.json pins jquery,
jquery-ui, jstree (3.3.12 -> 3.3.17), billboard.js, pace-js, tablesorter and
jquery-validation; build/sync-js.mjs copies their dist files into include/js/
and runs on npm install via postinstall.
pace-js carries a one-line local fix (a stray regex alternation that made it
match the empty string) applied through patch-package
(patches/pace-js+1.2.4.patch); the fix should be sent upstream so the patch can
be dropped. Only Cacti's own themed jquery-ui/jstree CSS stays committed.
An npm ecosystem is added to dependabot.yml for develop. The release pipeline
and any workflow that serves Cacti pages must run 'npm ci' (which builds via
postinstall) before use, since the generated files are no longer in the tree.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* hardening: remove vendored development artifacts
* feat(release): attach the built artifact to the release
The workflow already builds an attested, checksummed archive carrying the
Composer dependencies, then discards it after thirty days.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: fail the publish step when the bundle is empty
An unmatched glob would otherwise reach gh as a literal path.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: treat the maintenance lock notice as expected log output
The line records the lock working, so a second maintenance run overlapping
in CI failed unrelated pull requests.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: consolidate release dependency changelog
* ci: build managed JavaScript in release artifacts
* test: cover managed JavaScript build end to end
* deps: complete phpseclib 4 migration
* deps: update patch-package to audited release
* ci: satisfy workflow shell policy
* chore: source DOMPurify from npm and update it to 3.4.14
Add dompurify to the npm-managed set (was committed at 3.4.7) and bump to 3.4.14.
DOMPurify is the client-side XSS sanitizer used on tree labels, device tooltips
and plugin messages; keeping it current with upstream bypass fixes matters more
than for the UI libraries.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: preserve release changelog ordering
* test: preserve DOMPurify source map in npm build
* chore: source htmx, d3, big.js, lzjs and screenfull from npm
Add the remaining unmodified third-party libraries to the npm-managed build:
htmx.org 2.0.6, d3 7.8.2, big.js 6.2.2, lzjs 1.3.1 and screenfull 3.3.3. Each
was verified byte-identical (lzjs differs only in a JSDoc comment) to the copies
being removed. htmx in particular moves from a hand-tracked SHA pin to npm with
lockfile integrity.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: cover phpseclib 4 integration
* ci: build managed assets before htmx tests
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* issue#7355: add a vendor-neutral ENTITY-SENSOR-MIB script data query
ENTITY-SENSOR-MIB (RFC 3433) exposes physical sensors (temperature, fan, PSU,
voltage, current) on most modern gear, so one data query covers F5, Juniper,
Arista, Cisco NX-OS and others. ss_entity_sensor collects the sensor table and
scales each reading through entPhySensorScale and entPhySensorPrecision; the
SNMP I/O sits behind an injectable walker so every logic branch is unit tested.
A dedicated coverage job enforces 100% line coverage on the collector.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* issue#7355: add a JUNIPER-MIB jnxOperatingTable script data query
Junos routers and switches expose per-subject health (Routing Engine, FPC, PSU,
fan) through JUNIPER-MIB jnxOperatingTable, which is the signature Juniper data
beyond the IF-MIB and HOST-RESOURCES standards. ss_juniper_operating collects
CPU, temperature, buffer and state; the multi-index table is keyed on the full
OID suffix. SNMP I/O sits behind an injectable walker so every branch is unit
tested, and a dedicated job enforces 100% line coverage.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* issue#7355: add an F5-BIGIP-SYSTEM-MIB per-CPU utilization script data query
BIG-IP reports per-CPU utilization as sliding 5s/1m/5m averages in
sysMultiHostCpuTable, the signature F5 metric beyond the IF-MIB and
HOST-RESOURCES standards. ss_f5_cpu collects that multi-indexed table, keyed on
the full OID suffix. Every OID was resolved from the published
F5-BIGIP-SYSTEM-MIB with snmptranslate. SNMP I/O sits behind an injectable
walker so every branch is unit tested, and a dedicated job enforces 100% line
coverage.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* style: apply php-cs-fixer to ss_entity_sensor.php
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Use Symfony Finder for MD5 traversal
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore: add the CHANGELOG entry
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(md5sum): reject a bad ignore pattern instead of ignoring every file
preg_match() returns false on a bad pattern and false is not 0, so the old
test skipped the whole tree and wrote an empty manifest.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* style: use a line comment to satisfy php-cs-fixer
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: resolve includes through Cacti path constants
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(md5): enforce full finder coverage
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Move Finder import before CLI bootstrap
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* build: lock Symfony Finder runtime dependency
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* build: leave Composer autoload metadata generated
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: run Finder coverage on PHP 8.2
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix: widen the symfony/finder constraint for PHP 8.5
Pinning ^6.4 left the dev dependency graph unsatisfiable on PHP 8.5 in the native-dependencies job.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Make installer completion fail closed
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Add installer unit and Docker integration gates
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Require installer coverage evidence
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Stabilize installer failure scenarios
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Address installer review feedback
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Reject incomplete installer selection payloads
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Match table validation to rendered controls
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Protect long-running installer migrations
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Load installer table metadata in one query
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Optimize installer completion queries
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Batch installer default settings writes
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Batch installer color upserts
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Batch installer template and poller lookups
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Refresh SQL interpolation baseline after develop rebase
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: use Cacti Composer test setup
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: resolve includes through Cacti path constants
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: align installer workflows with policy
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: refresh security baselines after develop rebase
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: reuse TCP database credentials for installer checks
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(security): refresh sink inventory baseline after rebase onto develop
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: run installer coverage on PHP 8.2
The 8.1 pin is below the ^8.2 composer requirement, so pest died in platform_check before any test ran.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
PHP 8.1 left security support in December 2025 and Symfony 7.4 requires 8.2,
so the platform floor has to move before any component can advance.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore: resolve concurrent CHANGELOG appends with a union merge
Every PR lands on the same two anchors, so merging one reliably conflicts
the others on CHANGELOG alone.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
(cherry picked from commit 6bc8e63ecb)
* feat(changelog): take entries as fragment files instead of CHANGELOG edits
Every pull request edited one of two lines, so merging one conflicted the
rest. A file per change gives concurrent PRs nothing to collide over.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
(cherry picked from commit a8a0bfa29a)
* chore(security): drop line numbers from the tracked sink baselines
Both verifiers already strip the line before comparing, so the tracked
numbers carried no signal and rewrote 212 lines of baseline on any edit that
shifted code, conflicting every open pull request.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
(cherry picked from commit ccfd3d374b)
* fix(ci): rebase stale pull request branches instead of merging them
A merge update adds a commit per run, so branches accumulate snapshots of
old develop and re-conflict on every shared file they carry.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
(cherry picked from commit e9e33c77cd)
* test: match the escaping call rather than the exact source line
The literal assertions here required the uncast form while
PhpBinaryPathNullCoercionTest required the cast, so the two could not both
pass and whichever landed second broke the other.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
(cherry picked from commit 98cfc84756)
* fix(changelog): handle security entries in fragments and validation
Review caught the missing type. Security entries also take GHSA identifiers
and open a release section ahead of issue and feature entries, none of which
the first pass handled.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: require Composer-managed dependencies
Prevent agents from committing third-party package trees and require composer.json plus composer.lock updates instead.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>