* feat(auth): rename User Domains to Login Providers
Collapses user_domains/user_domains_ldap into a single login_providers table with a JSON parameters column, migrated in place during the 1.3.0 upgrade. Renames the admin page, menu, breadcrumbs, and help links to match.
* feat(auth): rework login providers as Cacti\Auth classes
Introduces LoginProviderInterface with credential (LDAP/AD) and redirect (SAML2/OpenID) flavors, replacing the procedural domains_login_process()/domains_ldap_*() functions. Adds blank-field group membership gating shared by every provider type.
* feat(auth): add SAML2 and OpenID Connect login providers
Adds onelogin/php-saml and firebase/php-jwt, a new cacti_http() SSRF-hardened outbound request helper for every OpenID discovery/token/JWKS call, and login_sso.php to handle the redirect-based SSO flow. Login page now shows a button per enabled SAML2/OpenID provider, and per-provider auth cookie opt-out is enforced on both the password and SSO login paths.
* chore(i18n): regenerate cacti.pot
* feat(auth): add Local and Basic auth as Cacti\Auth providers
LocalAuthLoginProvider (CredentialLoginProviderInterface) and BasicAuthLoginProvider (new PreAuthenticatedLoginProviderInterface, since Basic Auth has no password for Cacti to check) are thin adapters over the existing local_auth_login_process()/basic_auth_login_process() functions, which are left untouched to avoid disturbing their regression-tested lockout/timing/rehash behavior.
* feat(auth): move local password verification into LocalAuthLoginProvider
secpass_login_process() and local_auth_login_process() are retired; their lockout, constant-time verification, forced-password-change, and rehash-on-login logic now live end to end in the class (verifyCredential/enforcePasswordPolicy/rehashIfNeeded). secpass_check_pass() stays a shared lib/auth.php function since password change/reset/user_admin also call it outside any login flow. Local auth still has no login_providers row: isEnabled() is always true and it cannot be disabled or deleted.
* feat(auth): move Login Provider CRUD into Cacti\Auth classes
AbstractLoginProvider gains static deleteById()/enableById()/disableById()/makeDefaultById()/persist(); each concrete provider owns its own collectParameters(), dispatched via LoginProviderFactory. login_providers.php's form_save()/form_actions() call these instead of doing raw SQL, and the retired provider_remove()/provider_disable()/provider_enable()/provider_default()/provider_collect_parameters() procedural functions are gone. Local/Basic auth are unaffected: they never extend AbstractLoginProvider, so they structurally cannot expose delete/disable.
* fix(auth): address Copilot review findings on SSO/SAML/OIDC security
Fixes callback/ACS/SLS/metadata URLs that used the raw provider id instead of the login realm (broke every SSO login), adds InResponseTo correlation for SAML and sub/azp validation for OIDC, requires HTTPS for OIDC discovery/endpoints, pins cacti_http() connections against DNS rebinding via CURLOPT_RESOLVE, exempts login_sso.php from the authenticated-page redirect guard and gates it on auth_method, wraps provider dispatch in a exception boundary, adds a dedicated SAML SLO path instead of routing it through login completion, makes remember-me an explicit opt-in for SSO, fixes disabled/deleted providers not revoking existing auth cookies, restricts the default-realm action to LDAP/AD, and fixes the 1.3.0 migration silently enabling group enforcement for installs that had it off. Also updates the E2E seed script for the login_providers schema.
* chore(auth): refresh CI security baselines and document no-op validation
Regenerate the sink inventory baseline for the new/renamed lib/Auth/* and login_providers.php files, and add '// no-validation:' comments on the intentionally-unconstrained admin/free-text/secret fields (LDAP DNs/filters, SAML certs/keys, OIDC client credentials, provider display fields) flagged by the validator constraint ratchet.
Refreshes the validator baseline to drop the deleted user_domains.php entries.
* refactor(ldap): remove dead DB-lookup path from Ldap::__construct
The domain_id>0 branch duplicated the JSON-parameter-to-property mapping already owned by LdapLoginProvider::buildLdap(), and nothing constructs Ldap with a real id anymore (all call sites pass 0). Drop the parameter and the branch so Ldap stays a pure connection/protocol class with no DB coupling.
Also fixes a latent bug: group_require is a typed bool property, but LdapLoginProvider::authenticate() assigned it an int (1/0) from a strict_types=1 file, which throws a TypeError on every LDAP/AD login attempt. Assign a real bool instead.
* fix(auth): fix LDAP login, upgrade ordering, and hardening guard failures
LdapLoginProvider compared LdapError::GetErrorDetails()'s int error_num against the string '0' with strict equality, so it never matched and every LDAP/AD login was rejected regardless of valid credentials. Compare against int 0 instead.
install/upgrades/1_3_0.php called login_providers_convert_1_3_0() (which reads and then drops user_domains/user_domains_ldap) before the pre-existing legacy ALTER TABLE statements that add the debug/network_timeout/bind_timeout columns those reads depend on, so upgrades failed once the tables were already gone. Moved the call to run after those ALTERs.
OpenIdLoginProvider::complete() read state/code from raw \ instead of gnrv(), tripping the hardening pattern guard; switched to the request helper.
* style(auth): apply php-cs-fixer formatting and normalize line endings to LF
Runs php-cs-fixer across the new lib/Auth/* classes, login_providers.php, login_sso.php, and the other touched files, fixing indentation/alignment/docblock style to match project convention. Also normalizes several files that had picked up CRLF line endings back to the LF the project's .php-cs-fixer.php config (setLineEnding) and the rest of the tracked tree actually use.
* fix(auth): address second Copilot review round (9 high/medium findings)
- Make login_providers_convert_1_3_0() resumable: CREATE TABLE IF NOT EXISTS and INSERT ... ON DUPLICATE KEY UPDATE so an interrupted upgrade can be re-run instead of leaving a half-migrated, unrecoverable table.
- SamlLoginProvider::complete() now fails closed when no saved AuthnRequest ID exists instead of passing null to processResponse(), which skipped InResponseTo validation and accepted unsolicited IdP-initiated assertions.
- LoginResult gains an isCredentialFailure flag; only a genuine wrong-password LDAP bind (LdapError::Failure) counts toward the lockout counter now, not connection errors or group-membership denials.
- Fixed button_label/debug being read from the JSON parameters column in OpenIdLoginProvider/SamlLoginProvider/LdapLoginProvider when they are actually top-level login_providers columns; AbstractLoginProvider now hydrates and exposes both.
- Removed SamlLoginProvider's wantMessagesSigned, which silently required the outer SAML message (not just the assertion) to be signed, rejecting IdPs that only sign the assertion as the UI promises.
- Added getTemplateUserId() to CredentialLoginProviderInterface and implemented it on LocalAuthLoginProvider so the contract (not just incidental AbstractLoginProvider inheritance) guarantees the method lib/auth.php calls.
- cacti_http()'s CURLOPT_RESOLVE entries now bracket IPv6 literals correctly, and safe-IP validation strips parse_url()'s brackets before checking, fixing IPv6 target pinning.
- login_providers.php's type dropdown now swaps the Full Name attribute default (cn/displayName) on an actual user-driven type change, not on page load, so a new Active Directory provider does not silently save the LDAP default.
- Extracted auth_apply_group_login_opts() and call it from both auth_login.php and login_sso.php, so SSO logins honor a user's group-derived login_opts the same way password logins already do.
* fix(auth): fix PHPStan level 8 errors and pest harness stub drift
Test harness fix: the LoginProvidersLoginRealmValidationTest stub LoginResult class was missing the isCredentialFailure property added to the real class, causing 'Undefined property' warnings that broke the harness's JSON output. Added the property and set it on the simulated bad-password failure.
PHPStan (48+ errors, all newly introduced by this branch's rewrite, never actually verified until now): narrowed db_fetch_row_prepared()/db_fetch_assoc()'s array|bool (including bare true, which survives a '?: []' short-circuit - fixed with explicit is_array() checks instead) at every call site across auth_login.php, login_sso.php, lib/auth.php, lib/Auth/LocalAuthLoginProvider.php, lib/Auth/LoginProviderFactory.php, and install/upgrades/1_3_0.php.
Fixed OpenIdLoginProvider::discover()'s return shape to match its declared docblock instead of returning the raw decoded JSON. Fixed lib/functions.php's cacti_http() to narrow curl_exec()'s string|bool result with is_string() (not ===false, which still leaves 'true' as a residual type) and guard / as non-empty for curl_setopt_array()'s stubs.
Fixed a real bug in SamlLoginProvider::processLogout(): processSLO(true) was passing 'true' as \ (the first parameter) instead of \ (the fifth), so it always redirected/exited internally with the default \=false, making Cacti's own logout.php never regain control. Now calls processSLO(keepLocalSession: true, stay: true) and is typed ': never' to match; login_sso.php's now-genuinely-unreachable break statements after processLogout()/initiate() are removed.
Fixed login_providers.php passing a expression (not a variable) to inject_form_variables()'s by-reference parameter.
Verified: PHPStan analyse clean (0 errors), full Pest Unit/Security suite passes (128 tests), hardening guard/sink inventory/validator ratchet all still clean.
* fix(auth): fix ambiguous column and array+bool crash in user_log filter
The user filter dropdown query selected a bare 'id' column after joining user_auth and login_providers, which is ambiguous now that login_providers also has an id column (unlike the old user_domains.domain_id) - MySQL rejected the query outright. Qualified as ua.id.
Also narrow db_fetch_assoc()'s array|bool result before the \ + \ + \ merge, which was throwing a TypeError whenever the query failed instead of surfacing the real SQL error.
* fix(auth): address third Copilot review round (5 findings + tests)
- AbstractLoginProvider::persist() now disables the template account for every provider type; SAML2/OpenID also copy it via user_copy() in login_sso_complete() and were left directly usable as a local login.
- cacti_http_resolve_safe_ips() now detects IPv4-mapped IPv6 literals (::ffff:0:0/96) via inet_pton() and validates the embedded IPv4 address, closing an SSRF bypass via e.g. ::ffff:169.254.169.254.
- login_providers_convert_1_3_0() now tracks db_install_execute()'s status for every row and only drops the legacy user_domains/user_domains_ldap tables once every row (and the final row count) is confirmed migrated.
- Ldap::Getcn() now looks up ldap_get_entries()'s always-lowercased attribute keys case-insensitively while still storing the result under the originally-requested key, fixing AD's displayName (and any other mixed-case configured attribute) always resolving to a blank full name.
- SamlLoginProvider::processLogout() now redirects to the LogoutResponse URL processSLO() returns for a LogoutRequest (stay:true), instead of discarding it and always redirecting to Cacti's own logout page - federated logout was never actually acknowledged to the IdP.
- Added tests/Unit/Security/Auth/OpenIdLoginProviderTest.php (13 tests) covering verifyIdToken() signature/issuer/audience/azp/nonce validation, discover()'s HTTPS enforcement, and complete()'s callback-state and UserInfo-subject-matching checks, against a locally-generated RSA keypair/JWKS.
- Regenerated the sink inventory baseline for the new header_redirect call in SamlLoginProvider::processLogout().
Verified: PHPStan clean, full Pest Security/Auth suite passes (830 tests including the 13 new ones), php-cs-fixer/hardening-guard/sink-inventory/validator-ratchet all clean.
* fix(boost): harden data handoffs and cache writes
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Define $updates_ok when there is nothing to flush
PHPStan level 6 also flags the poller ownership check, which is the POLLER_ID
constant case the file already annotates above.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Use a line comment to match the fixer
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Record the boost process columns in the audit schema
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: resolve includes through Cacti path constants
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Fix Boost handoff review concerns
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: seed settings in Boost database fixture
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: refresh SQL interpolation baseline
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs: record Boost handoff hardening
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix: stop boost discarding a cycle and wedging a shard
Three defects in the handoff hardening.
A rejected ownership handoff returned false, which the poller reads as boost
having consumed the data, so it skipped the RRD write. The poller_output rows
were already deleted by then, so a stale poller_item copy on a remote poller
silently lost the whole cycle. Return true instead and let the poller write
directly, matching the database failure path.
Any non-OK RRDtool response retained the shard, and the retry then replayed rows
already written, which RRDtool rejects as an illegal update time. That failed
again on every pass, so the archive tables accumulated and boost never drained.
Treat an at-or-behind timestamp as applied, since the sample is in the RRD;
genuine errors still retain the shard.
The two new poller_output_boost_processes columns passed 'null' where
db_install_update_table reads 'NULL', so NOT NULL was never emitted and upgraded
installs diverged from cacti.sql.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix: spell the NULL column key so upgrades emit NOT NULL
db_add_column() reads $column['NULL'] and PHP array keys are case sensitive, so
the thirteen definitions in the 1.3.0 upgrade that passed a lowercase 'null' key
had the flag discarded and created nullable columns. Fresh installs built from
cacti.sql got NOT NULL, upgraded installs did not, and Cacti's own schema audit
reports the difference.
The same file already spells the key in upper case 144 times, and the wrapper's
own docblock example uses the upper case form.
Guard the class with a test over every upgrade file rather than only the one
that was wrong.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: assert the NULL key plainly
toContain() takes needles rather than a failure message.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): close remaining handoff review gaps
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): guard nullable graph output
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): keep in-flight completion rows through the 1.3.0 upgrade
The truncate only existed so the run_child UNIQUE key could build over rows
that predate run_id; deleting just those rows leaves a running boost its
completion records.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor(boost): drop the poller ownership gate
Its boost_poller_on_demand() call sits behind the boost_redirect early return
that poller.php forces on for every multi-collector install, and its recovery
call turns a device deletion on main into a recovery that never brings the
collector back online.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): bound atomic cache write allocations
Guard upgrade source reads so failures report clearly, write graph cache data in fixed-size chunks, and exercise a multi-chunk payload.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): support Windows cache replacement
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(boost): expand handoff failure coverage
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(boost): support ANSI_QUOTES upgrade checks
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): stop staging after chunk failure
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(recovery): use numeric poller status
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(sql): support ANSI_QUOTES in recovery paths
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): preserve cache on replacement failure
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): retain readable cache permissions
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): repair completion schema at runtime
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(recovery): bound timestamp batch fetches
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): satisfy runtime schema analysis
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(recovery): restore status after transfer failure
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(boost): rely on unit bootstrap helpers
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): refresh generated cache secret
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(boost): normalize cache path separators
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(boost): fail clearly on unreadable fixtures
* fix(boost): name ignored duplicate sample keys
The staging warning only said rows were dropped, so a retry could not be told from a colliding writer.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Lack of pre-commit hook execution allowed lint failure
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* feat(notify): let one notification address several recipients
Symfony calls a channel once per recipient, so a caller that needs a single
message sent to several people could not express it. The email 'to' option
folds the extra addresses into the same mailer() call and drops any that
match the recipient.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(notify): compare the local part of an address exactly
RFC 5321 leaves local part case to the receiving host, so folding the whole
address deduplicated OPS@example.com and ops@example.com into one mailbox.
Only the domain folds now.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor(tests): reorganize unit tests into domain category folders
- Consolidate per-advisory GHSA regression tests into Security/ subfolders by vuln class
- Group all 186 test files into domain folders: DataCollection/, Ui/, Core/,
Database/, Installer/, Scripts/, Security/, Plugin/
- Strip GitHub issue/GHSA numbers from filenames; keep IDs in test descriptions
- Fix all __DIR__ relative paths to match new folder nesting depth
- Update CI workflow path triggers and phpunit XML configs for new file locations
- Update .github/copilot-instructions.md with unit test organization guidelines
- Add tests/vendor/ to .gitignore
* add gaurds
* fix up guard for unit tests
* fix redeclaration of cacti_log in tests
* fix updates to unit tests
* fix test patjhs
* fix: update test __DIR__ paths for reorganized test structure
* fix: conditionally define read_user_setting to prevent redeclaration in tests
* fix: conditionally define functions to prevent redeclaration in tests
- Wrap clean_up_lines, raise_message, cacti_debug_backtrace, and get_debug_prefix
with function_exists() checks to prevent conflicts when test stubs are loaded
before lib/functions.php in the same PHP process
* fix: add unit test bootstrap to prevent function redeclaration errors
- Create tests/bootstrap-unit.php to load core libraries before tests
- Ensures lib/functions.php loads before test stubs define stub functions
- Test stubs use function_exists() checks, so they detect pre-loaded functions
- Update phpunit.xml to use bootstrap-unit.php for Unit testsuite
- Fixes 'Cannot redeclare read_user_setting()' errors when tests run in same process
* fix: load test stubs before global.php in unit test bootstrap
- Reorder bootstrap to load CactiStubs.php and UnitStubs.php first
- Ensures stub functions like __() are available when global.php loads
- global.php calls get_cacti_version_text() which needs __() for translations
- Fixes 'Call to undefined function __()' error during bootstrap
* fix: remove function conflicts from test stubs
- Remove stub definitions for functions already in lib/functions.php:
cacti_sizeof, cacti_log, cacti_strtolower, read_config_option,
read_user_setting, cacti_count
- Keep translation function stubs (__(), __esc()) needed by global.php
- Update bootstrap to load lib/functions.php after stubs
- This prevents function redeclaration errors while maintaining test isolation
* fix: prevent constant and variable redeclaration in bootstrap
- Remove explicit PHP_TESTING and CACTI_TEST_BOOTSTRAP defines from bootstrap
(CactiStubs.php now handles this with guards)
- Add guards to CactiStubs.php to prevent redefinition of PHP_TESTING
- Only set CACTI_TEST_BOOTSTRAP env vars if not already set
- Fixes PHP warnings about 'Constant X already defined'
* fix: remove redundant lib/functions.php load from bootstrap
include/global.php already loads lib/functions.php at lines 287-288.
The bootstrap was trying to load it again after global.php, but global.php
needs format_cacti_version() to be available on lines 293-294 to define
CACTI_VERSION and CACTI_VERSION_FULL constants.
Solution: Let global.php handle loading lib/functions.php itself, and don't
load it separately in the bootstrap.
* fix: remove more conflicting function stubs from test helpers
- Remove stubs for functions also defined in lib/functions.php:
raise_message, clean_up_lines, cacti_debug_backtrace, get_debug_prefix
- Remove POLLER_ID constant stub (defined unconditionally by global.php)
- Keep only translation functions and test-specific helpers
- This prevents function/constant redeclaration errors
* fix: minimize test stubs to only translation functions
Remove ALL helper functions and constants from UnitStubs.php that are:
- Defined in lib/functions.php (raise_message, clean_up_lines, etc.)
- Defined in lib/html_utility.php (srv, set_request_var)
- Defined in global_constants.php (POLLER_VERBOSITY_*, MESSAGE_LEVEL_*)
- Defined in global.php (POLLER_ID)
Keep ONLY translation functions (__(), __esc()) that are:
- Called by global.php during initialization
- Not defined elsewhere
- Required for proper bootstrap sequence
This eliminates all constant and function redeclaration errors.
* fix: remove __esc stub - already defined in global_languages.php
Keep only __() translation function stub, which is needed by global.php
and not defined elsewhere in Cacti core.
* fix: handle no-arg case in __() translation stub
The __() function may be called with just a string and no format arguments.
Previously, vsprintf() would fail with 'arguments array must contain 2 items'.
Now, if no args are provided, return the text as-is.
* security: add nonce CSP mode for inline JavaScript protection
Reworks the content_security_policy_script setting into a three-mode Inline JavaScript Protection dropdown (HTMLX default, Nonce, None) and ports the per-request nonce, strict-dynamic policy, and CSP violation report endpoint from 1.2.x. The stored option key and its default are unchanged, so existing installs keep their current policy byte-for-byte; nonce mode is opt-in.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* security: stage nonce CSP enforcement safely
Add report-only migration, gated enforcement, nonce-aware shared emitters, Linux Docker validation, and operator documentation.\n\nRefs #7703
* security: validate CSP header sources
Reject malformed alternate-source configurations at the CSP boundary, validate fallback report URIs, and remove the obsolete Psalm header-taint baseline entry.\n\nRefs #7705
* chore: drop the composer.json change from this PR
Dependency and manifest changes are a maintainer call and do not belong in
this one.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* feat(validator): add settings constraint validation pilot
Routes saved settings through Symfony Validator so include/global_settings.php
can declare per-setting constraints inline. The save handler runs one
validator pass and surfaces violations via the existing raise_message()
pattern, replacing scattered ad-hoc checks. Pilot covers 12 high-value
settings; the rest stay on the existing implicit form-render checks.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci(infection): allow infection/extension-installer composer plugin
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* docs(validator): document must-be-posted-to-validate contract
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(bootstrap): guard Symfony autoload in global_settings before use declarations
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(test): remove redundant autoload require in CactiSettingsTest
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(test): restore require_once for CactiSettings (not in Composer autoload)
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* feat(validator): fold helper into settings pilot
* fix(validator): translate default Symfony constraint messages
Default Symfony NotBlank/Length/Range/Choice/Positive messages render
in English regardless of Cacti locale because the validator runs
without a Symfony Translator. Pass explicit __()-wrapped message,
minMessage, maxMessage, and notInRangeMessage options on each bare
constraint in include/global_settings.php so violations participate
in Cacti's gettext pipeline. Documents the no-Translator decision in
CactiValidator so future contributors know to translate at the call
site.
Addresses Copilot PR #7077 review feedback.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(graph): improve remote agent parsing and RRD path validation
* feat(validator): migrate automation_graph_rules.php to symfony validator
* refactor(validator): move automation validator helper to shared library and enhance tests
* feat(validator): migrate automation_networks.php to symfony validator
* feat(validator): migrate automation_snmp.php to symfony validator
* feat(validator): migrate automation_templates.php to symfony validator
* feat(validator): migrate automation_tree_rules.php to symfony validator
* refactor(automation): move api_networks_save to shared library for testability
* feat(validator): add validateInput helper and migrate color_templates.php
* feat(validator): migrate host_templates.php to symfony validator
* feat(validator): migrate graph_templates.php to symfony validator
* feat(validator): migrate data_templates.php to symfony validator
* feat(validator): migrate data_input.php to symfony validator
* feat(validator): migrate data_queries.php to symfony validator
* feat(validator): migrate graphs_new.php to symfony validator
* feat(validator): migrate graphs.php to symfony validator
* feat(validator): migrate data_sources.php to symfony validator
* feat(validator): migrate user_group_admin.php to symfony validator
* feat(validator): migrate managers.php to symfony validator
* feat(validator): migrate package_repos.php to symfony validator
* Refactor pollers.php to use CactiValidator
* fix(security): close validator migration regressions
* test(snmp): add spine docker harness
* fix(review): address validator migration feedback
* fix(deps): include validator runtime packages
* chore(deps): normalize composer.json require-block indentation
The validator migration introduced tab-indented entries in an otherwise
space-indented require block. Re-indent the stevenmaguire/* and symfony/*
lines to 8 spaces so the block is consistent.
Co-Authored-By: Oz <oz-agent@warp.dev>
* fix(graphs): repair graph json response branch
* fix(validation): apply php cs fixer
* ci(codeql): skip autobuild for interpreted scans
* fix(link): satisfy phpstan path guard
* chore(pr): remove branch pollution
* ci(codeql): align autobuild pin with init and analyze
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor(automation): use CactiValidator::validateInput, drop wrapper
automation_validate_input() in lib/api_automation.php was a byte-for-byte
duplicate of CactiValidator::validateInput() (identical session handling and
raise_message() semantics, and it already delegated to CactiValidator::validate
internally). Per review feedback, use the shared class directly everywhere
instead of a parallel automation-only wrapper.
- Remove automation_validate_input() from lib/api_automation.php
- Replace all call sites with CactiValidator::validateInput()
(automation_templates/graph_rules/tree_rules/snmp and api_networks_save)
- Update the automation validator unit tests to match
- Add @param/@return PHPDoc to CactiValidator specific-validator methods
Co-Authored-By: Oz <oz-agent@warp.dev>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: Thomas Vincent <thomas@vincent.dev>
Co-authored-by: Oz <oz-agent@warp.dev>
Co-authored-by: TheWitness <thewitness@cacti.net>
* test: shared hand-off + mutation infrastructure for feature PRs
Consolidates infection/infection dev dep, allow-plugins entry, baseline
infection.json5, tests/HandOff/HandOffHelpers.php with reusable stubs
(cacti_log capture, temp-file fixtures, minimal-zip builder), HandOff
testsuite registration, and the documented pattern. Feature PRs that
add hand-off coverage rebase onto this and contribute only ONE
HandOffTest file plus an optional infection.json5 filter override.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: consolidate per-feature hand-off suites into shared infra
Each test guards on its feature file and skips when not present on develop.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(test): replace eval stub, unify log buffer ref, guard ZipArchive add
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(ci): correct infection vendor path in infection.json5
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci(phpstan): baseline upstream undefined-variable and isset errors
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: add HandOff and Integration testsuites; add regression guards
- phpunit.xml: add Integration testsuite alongside existing Unit/HandOff
- tests/Integration/: DbDumpIntegrationTest, PingIntegrationTest (from develop), CactiProcessIntegrationTest, SqlScriptsIntegrationTest
- tests/HandOff/RegressionGuardTest: guards against backsliding on shell_exec, password-in-argv, RLIKE concat, and open-redirect patterns
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore(test): pest test infrastructure and PSR-4 autoload
Add Pest 2 dev dependency, regenerate composer.lock under PHP 8.1, declare PSR-4 autoload for the Cacti namespace, and seed the test bootstrap with three unit tests plus an orb integration check. Pest stays on the v2 line because v3 transitively requires PHP 8.2 and breaks the 8.1 matrix entry.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore(phpstan): catch up baseline with 11 upstream-detected entries
Cacti Commit Audit fails on develop with 11 PHPStan errors that are
not yet in phpstan-baseline.neon. Append the matching entries so the
PR-A branch passes Run PHPStan at Level 6. Same set of entries
already exists on PR #7077; this is a transient catch-up that
upstream will absorb when those entries land on develop.
Files: aggregate_graphs.php, color_templates.php, graph_templates.php,
graphs.php, lib/html.php (3 entries).
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore(test): drop composer.lock; let CI resolve per PHP version
The lock file pinned brianium/paratest v7.3.1 which only supports
PHP 8.1-8.3, so the 8.4 matrix entry rejected the lock with
"Your lock file does not contain a compatible set of packages".
A single lock cannot satisfy 8.1 (paratest 7.3.x) and 8.4 (paratest
7.4.x) at the same time because pestphp/pest 2.36.0 is the last
2.x release that supports 8.1 and the 8.1-compatible paratest tag
predates 8.4 support.
Match upstream develop's behaviour: no committed lock; let
`composer install` in CI resolve per matrix entry. Pest stays on
the v2 line in composer.json so 8.1 still gets a compatible Pest.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(test): apply Copilot review feedback for test infra
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore(test): drop psr-4 autoload and align deps with canonical PRs
Match the flat un-namespaced lib/CactiX.php convention used by #7088,
#7073, #7077, #7075. Drop symfony/process (added by #7073) and
symfony/validator (added by #7077) to avoid composer.json conflicts at
merge time.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: skip integration suites and regression guards pending feature PRs
Guards integration tests and regression guards from #7083 against
absence of CactiProcess and other feature-PR hardening, so the
consolidated foundation PR is green on develop. Suites activate
automatically once their feature PRs merge.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(ci): drop infection/infection to unbreak PHP 8.4 matrix
infection/infection ^0.27 transitively pins thecodingmachine/safe
^2.1.2, resolving to v2.5.0. Its generated stubs use implicit-nullable
parameter declarations, which PHP 8.4 emits as deprecations into
cacti.log and trips the log-quiet assertion. infection has no config
or test wiring in this repo, so removing the dev dep clears the
transitive pin without losing functionality.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
* This feature enhancement will support the upcoming release of the streams feature in Cacti.
* Expect more updates related to this feature enhancement.
* The default for last_updated in the plugin_config table was wrong
* Re-arrange some columns for archived and available plugin versions
* Add the Archive/Available size to the respective table views.
* Fixed missing column definitions for rows caches
* Fixed column definitions for data source statistics
* Made the dsstats_upgrade() run regarless and run error free
* Increase the column size for search_filter
* Remove spurious cacti_log function call
* Make PDO::MYSQL_ATTR_SSL_CAPATH and PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT
accessible thru $database_ssl_capath and $database_ssl_verify_server_cert
configuration options.
* typo: wrong variable name
* fixing typo
---------
Co-authored-by: Jose Kahan <jose.kahan@w3.org>