mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-25 04:27:56 +09:00
develop
381
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
57c0fe63d4 |
Fix restrictive graph permission display for default-deny templates (#8422)
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
5403cf75bb |
feature: user-defined filter layouts with administrator publishing (#8185)
* feature: user-defined filter layouts with administrator publishing
Add a render_layouts toggle to the CactiTableFilter class so list pages
offer Save / New / Rename / Delete controls for saved filter layouts,
modelled on the monitor plugin. Layouts persist to a new user_layouts
table (id, user_id, page, name, url). The toggle defaults on for every
page except the Console > Presets pages (CDEFs, VDEFs, Colors, GPRINTs,
Data Profiles, SNMP), where it is off.
Each user manages their own layouts from any filter. Administrators who
hold the Settings/Utilities realm can publish a layout to all users
(moving it to user_id = 0) from the filter, and from a new
Console > Presets > Filters management page (layouts.php) where they can
view and manage every user's layouts. Published layouts appear for all
users on that page.
Layout create/rename/delete/publish requests are handled centrally by
filter_layouts_handle_request(), invoked from include/auth.php after the
user has been authorized for the current page, so the controls work on any
filter page while publishing stays gated on the realm. layouts.php itself
is mapped to the Settings/Utilities realm so the Presets > Filters menu
pick is administrator-only.
Schema added to cacti.sql, install/upgrades/1_3_0.php, and
docs/audit_schema.sql. Adds tests/Unit/FilterLayoutsTest.php covering the
URL-safety validation, preset-page defaults, and the request/permission
wiring.
* changelog: 8185.feature fragment for filter layouts
* chore: enforce LF line endings via .gitattributes and normalize new files
* fix: address review - attribute-escape layout options, honor failed/non-editable layout ops, regenerate cacti.pot, move test to Unit/Ui/Html
* chore: renormalize pre-existing CRLF files to LF per .gitattributes
* chore: keep vendored JS (big.js, tablesorter) byte-identical to upstream and exclude from LF normalization
* style: apply php-cs-fixer formatting and resolve PHPStan level 8 findings in filter layouts
* ci: re-trigger checks on current head
* refactor: store filter layouts as a versioned JSON document in a TEXT data column
Replace the user_layouts.url varchar(1024) column with a data TEXT column holding a versioned {version,page,filter} document. The navigable url is rebuilt server-side from the document (page basename + rebuilt query) plus the filter_layout marker, removing the stored-url redirect/XSS surface and making the backend forward-compatible with a structured query builder without a further schema change.
* feature: add Modern/Legacy page filter format with an Edit dialog for filters
Modern (default) collapses the filter to a Layouts selector plus an Edit dialog holding the Filter Name and all filter fields; Save and Publish live in the dialog, with Edit/Rename/Delete/Save As on the bar and time controls/refresh kept on the bar. Legacy keeps the classic inline filter. The format is a per-user General setting (page_filter_format). Field rendering is factored into emit_field() so both layouts share one source.
* fix: render page-action buttons (import, export, sort) on the modern filter bar
Import, Export, Purge, Save, and sort ascending/descending are page actions, not filter inputs, so they render on the bar beside the Layouts selector instead of inside the Edit dialog. Only go/clear are dropped from the bar; the dialog keeps the Search/Clear footer.
* feature: render the modern Edit dialog one filter variable per row
The dialog no longer mirrors the inline filter's multi-field row packing; each filter variable renders on its own row (label + control) for a clean, responsive form. Hidden/validate fields stay in the form without an empty visible row. Rows carry a cactiFilterEditRow hook for label-column alignment styling.
* feature: align the modern edit dialog one-per-row and skin it per theme
Render all dialog fields in a single table so the label column aligns via the existing table-cell layout. Tag the dialog widget with cactiFilterDialog and add a scoped skin to the modern theme (slate titlebar, styled button pane, accented primary action). Add a bar separator between layout and page-action buttons.
* feature: skin the modern filter Edit dialog across the classic themes
Add the scoped .cactiFilterDialog skin (accent titlebar, white title/close, styled button pane with an accented primary action) to cacti, carrot, dark, hollyberry, paper-plane, paw, raspberry, and sunrise, each using its own brand accent. The skin is accent-only and inherits each theme's dialog surface, so it works on light and dark themes. MidWinter is intentionally skipped (its variable-based CSS is handled in a separate Epic).
* fix: delete a user's saved filter layouts when the user is removed
user_remove() now deletes the user's own user_layouts rows alongside their other per-user data. Scoped to user_id so published (user_id = 0) layouts remain available to everyone.
* fix: address PR review — PHPStan return type, escaping, callbacks, SQL columns, mb truncation, contrast, POT
- create_filter(): cast both ob_get_clean() returns to string and drop the now-unused PHPStan baseline entry (fixes Level 6 failure).
- Edit dialog title now uses html_escape_attr() (attribute-context double-encoding).
- Modern Search/Clear honor the page's configured go/clear callbacks instead of hardcoding applyFilter()/clearFilter().
- filter_layouts_get() and layouts.php select explicit columns instead of *.
- Layout name truncation uses mb_substr() to avoid cutting multibyte codepoints.
- layouts.php management callback checks result.ok before reloading.
- carrot/paper-plane dialog accents darkened to meet WCAG AA (>=4.5:1) with white text.
- Added the new translatable msgids (Page Filter Format, Filters, Edit Filter, Filter Name, Save As, ...) to cacti.pot.
* fix: resolve Semgrep reflected-xss by reading filter_layout inside layout_select
The request var is no longer passed into layout_select() as a parameter (which Semgrep tracked parameter-to-return to the print sink); it is read internally like create_filter(), and the option markup remains escaped. No behavior change.
---------
Co-authored-by: TheWitness <copilot@users.noreply.github.com>
Co-authored-by: TheWitness <TheWitness@users.noreply.github.com>
|
||
|
|
9facd9b500 |
security: batch 7 develop - consolidate remaining develop-only GHSA advisories (12 fixes + 8 confirmations) (#8149)
* security: batch 7 develop - consolidate remaining develop-only GHSA advisories (12 fixes + 8 regression confirmations) Fixes: f7jw (automation preview XSS), rp5g (db_column_exists LIKE), vx2m (automation pivot CASE WHEN), h5wg (plugin DDL identifiers), 929h (aggregate LIKE filter), mfw8 (add_graphs REGEXP), m49v (user_admin sort_column allowlist), mpfm (rrd error-image theme allowlist), m3fh (SNMPv3 control chars), cg45 (remember-me row revoke), xq26 (remember-me retention window), m67r (import xml_path containment). Confirmed already fixed on develop, pinned with regression tests: 4gph, 977w, mgcq, 5p79, cfhh, wfcf, pjq5, wp33. Excluded: 227p (1.2.x-only), mx5c (maintainer private branch). * security: address batch-7 review feedback (lexical-first xml_path containment, session-wide sort allowlist, connection-aware db_column_exists quoting, reject-not-normalize plugin DDL identifiers, behavior-based test names) * style: apply php-cs-fixer single-quote fix to api_automation pivot builders; add WSL dev-environment note to copilot instructions * fix phpstan string typing * test: refresh SQL interpolation baseline for db_column_exists db_qstr change (database.php 9->8) --------- Co-authored-by: ci <ci@local> |
||
|
|
9d628b4959 |
security: batch 5 develop - fail closed on No-Auth admin bypass (GHSA-69vw), tag GHSA-3jj2, add 1.2.32 CHANGELOG section (#8146)
* security(GHSA-69vw): fail closed on auth_method None instead of auto-granting an admin session - check_reset_no_authentication() no longer wipes the admin password, flips auth_method, or hands an unauthenticated request an admin session; it fails closed and requires a manual auth_method reset (GHSA-69vw-qxxv-p8w2). - Tag GHSA-3jj2-v5ch-wmq5 in the CHANGELOG: the domains_login_process() realm bypass is already closed by the Login Providers rewrite (realm < 1000 fails closed). - CHANGELOG: add a 1.2.32 section below 1.3.0-dev with the GHSAs also fixed in 1.2.x, and de-duplicate the security entries the #8145 merge doubled. * docs(CHANGELOG): file host_id SQLi fix under 1.2.31 as GHSA-r7hc-hrpv-52gf; drop plain issue#7693 entry |
||
|
|
3ab4c69fb7 |
PHPStan Level 8 - Phase 1 Conversion (#8135)
* PHPStan Level 8 - Phase 1 Conversion
Reduces the real (no-baseline) PHPStan level 8 error count from ~2,724 to 425
and regenerates phpstan-baseline.neon accordingly (8,941 -> 1,921 lines).
Core type-safety changes
- lib/database.php: narrow db_fetch_row()/db_fetch_row_prepared() to return
array (was bool|array) and db_fetch_assoc()/db_fetch_assoc_prepared() to
return array (was mixed); the _prepared workers coerce the connection-failure
path to an empty array so the declared type is honest. empty()/cacti_sizeof()
checks are unaffected.
- lib/database.php: narrow $db_conn guards from is_object() to instanceof PDO in
the transaction/quote/insert-id helpers so the PDO calls resolve.
- lib/mib_cache.php: the fluent MibCache methods mib()/object()/table()/row()
now return self instead of a bare object so chained calls type-check.
Type-safety casts and guards (mechanical, behaviour-preserving)
- Cast string|false/|null builtin results feeding string parameters
(json_decode, base64_encode, trim, htmle, preg_replace, explode, etc.).
- Cast int|string / int|false values feeding int id/pid parameters.
- Guard resource|false fopen()/popen() handles before fwrite/fclose/fread, and
wrap foreach over array|false/list|false results with `?: []`.
Genuine bugs found and fixed
- cli/add_tree.php / lib/api_tree.php: site tree nodes could not be created via
the CLI - the --site-id option was never parsed, $nodeTypes had no 'site'
entry (passing null to api_tree_item_save(int $type) -> TypeError), and
api_tree_item_save() never persisted site_id. All three fixed.
- tree.php: leaves_exist() is typed : int but returned db_fetch_assoc_prepared()
(an array of rows) for a SELECT COUNT(*); now uses db_fetch_cell_prepared()
and returns the scalar count.
- lib/mib_cache.php: $entries_per_object was a float (division / ceil) used as
an array key (PHP 8.1 implicit float-to-int key deprecation); now cast to int.
Config
- .phpstan.neon: add notIdentical.alwaysTrue and booleanAnd.alwaysFalse to the
existing always-true/false ignore family (same "PHPStan gets confused"
dynamic-constant cause already covered by identical.alwaysTrue,
notEqual.alwaysTrue, booleanAnd.leftAlwaysTrue, etc.).
- phpstan-baseline.neon: regenerated; the remaining 425 entries are the
untouched long tail for Phase 2.
* Remove redundant @phpstan-ignore comments
Follow-up cleanup for the level 8 Phase 1 work. Removes 14 inline
@phpstan-ignore comments that no longer suppress any reported error:
- cli/repair_database.php: `@phpstan-ignore booleanAnd.alwaysFalse` is now
covered by the global booleanAnd.alwaysFalse ignore added to .phpstan.neon.
- 13 others (host.php, include/global.php, include/session.php,
lib/api_automation.php x2, lib/functions.php x2, lib/html_utility.php x2,
lib/installer.php, lib/package.php, lib/reports.php, lib/rrd.php) no longer
match any reported error after the type-safety fixes (e.g. the
db_fetch_row_prepared() return-type narrowing removed the offset-assign
errors the api_automation ignores were suppressing).
Verified with reportUnmatchedIgnoredErrors enabled: 0 unmatched ignores
remain and the full level 8 run still reports 0 errors.
* ci: satisfy validator ratchet and refresh security sink baselines
- lib/api_tree.php: give the new tree-item site_id a real numeric
constraint ('^[-0-9]+$', matching html_reports.php) instead of an
empty regex, so the validator constraint ratchet passes without
bumping its baseline.
- tests/security/baselines: refresh the three sink/hotspot signatures
whose code text changed from the (string) casts added in this branch
(lib/poller.php setsid, cli/rrdresize.php json_encode, lib/import.php
simplexml_load_string).
* ci(js): allow DOMPurify 3.4.16 and update sync_js pin test
The dependabot bump (#8134) moved package.json/package-lock.json to
DOMPurify 3.4.16 on develop but left tests/js/sync_js.test.mjs pinned to
3.4.15, so the PR merge commit fails the 'DOMPurify is pinned' contract.
Bring package.json/package-lock.json to 3.4.16 (matching develop) and
update the pin test to expect 3.4.16.
* test: update sql.php null-handling assertion for the (string) cast
This branch changed scripts/sql.php from trim(\ ?? '') to
trim((string) \) (the phpstan-safe form that coerces both null and
false from shell_exec()). Update SqlScriptsTest's null-return assertion
to match the new cast instead of the removed '?? ''.
* fix: address Copilot review - ownership IDs, fopen guards, mib_cache bound
- chown()/chgrp(): pass integer UID/GID from fileowner()/filegroup()
(guarding the false lookup) instead of string-casting them, which made
them resolve account/group names like '1001' and fail. Covers
lib/api_device.php, lib/boost.php and poller_maintenance.php (x2).
- fopen() failure now short-circuits instead of reporting success:
lib/package.php returns false, lib/clog_webapi.php and utilities.php
render an error, and snmpagent_mibcachechild.php skips the cache write
and sentinel unlink so a concurrent writer's lock is preserved.
- lib/mib_cache.php: keep the float quotient as the loop bound and cast
only the array offset, so a non-even entries/columns split no longer
truncates the loop and drops a cached row.
* revert: keep db_fetch_* returning false on error; baseline Level 8 instead
Per review, restoring the intentional fail-safe: db_fetch_row[_prepared]()
return bool|array and db_fetch_assoc[_prepared]() return mixed again, so a
genuine SQL error (not just a missing connection) is signalled as false and
the === false checks in cdef_is_in_use(), boost_process_* and aggregate.php
work as before. Dropped the is_array() ? : [] coercion.
Level 8 stays green by regenerating phpstan-baseline.neon to capture the
re-introduced bool|array/mixed call-site errors (the ~1,850 the narrowing
had eliminated). Tightening those callers to drop them from the baseline is
Phase 2 work.
---------
Co-authored-by: TheWitness <copilot@users.noreply.github.com>
|
||
|
|
bb92c46e78 |
feat(auth): automatic User Group assignment from Login Provider groups (#8129)
* feat(auth): automatic User Group assignment from Login Provider groups Add per-Login-Provider "Auto Assignment" rules to the User Group editor so a user is added to (or removed from) a Cacti User Group at every login based on the groups their Login Provider reports. User Group editor (user_group_admin.php): - Add General / Auto Assignment / Rights spacers to the General tab and rename "General Group Options" to "Group Options". - Render one text field per eligible Login Provider under Auto Assignment (label = provider name). The admin enters a plain Group Name (e.g. "Cacti Admins"), never a DN. - Store the rules as JSON in the new user_auth_group.auto_assignments column, keyed by login_providers.id. Group resolution: - SAML2 and OpenID match the configured name against the group attribute / group claim values. - LDAP/AD resolve membership live against the directory using a raw group name plus the Search Base, trying AD (memberOf + nested LDAP_MATCHING_RULE_IN_CHAIN), FreeIPA / 389 DS (groupOfNames/groupOfUniqueNames) and RFC2307 posixGroup shapes so one plain name works everywhere. This requires a directory-read bind, so the field is only offered for Anonymous Searching or Specific Searching with a stored service account. - A directory that cannot be reached returns "undetermined" so existing memberships are left untouched rather than revoked on a transient outage. Lifecycle: - auto_assignments is reconciled on every login (lib/auth.php credential flow and login_sso.php redirect flow). - Deleting a Login Provider strips its key from every group's auto_assignments. Schema: add user_auth_group.auto_assignments (cacti.sql + 1_3_0 upgrade). * ci: document free-text group-name validateInput for constraint ratchet * fix(auth): address PR review on automatic User Group assignment - LDAP: add an Active Directory bare-name query (objectClass=group with the member:1.2.840.113556.1.4.1941 nested-membership rule) so a plain group name resolves on AD, which neither the posixGroup nor groupOfNames probe could match. - LDAP: make single-group membership tri-state and propagate an "undetermined" result, so a search that fails after binding (timeout/dropped connection) no longer looks like confirmed non-membership and revokes assignments. - SAML2/OpenID: supportsAutoAssignment() now requires a configured group claim, and applyAutoAssignments() skips any provider that is not currently capable, so a blank claim can never strip users from their groups. - Only reset the permission cache when a membership row actually changed (accumulate db_affected_rows) rather than on every login that has a rule. - Add unit tests for the reconciliation boundaries (add / remove / no-op / incapable / undetermined) and for the LDAP group-query construction (AD plain + nested, posixGroup, groupOfNames, DN mode). - Regenerate locales/po/cacti.pot for the new UI strings. * fix(phpstan): resolve level-8 errors from auto group assignment - Guard supportsAutoAssignment() behind an instanceof AbstractLoginProvider check so it is not called on the base LoginProviderInterface. - Assign the injected field list to a variable (inject_form_variables takes it by reference) and pass an is_array-guarded group row. - Bump the lib/ldap.php ldap_get_entries baseline count 3->6 for the three new group-membership query helpers. |
||
|
|
f030e5cc60 |
feat(login_providers): form field hints, cert/private-key display, and encrypted storage (#8074)
* feat(login_providers): add form hints, cert/private-key display, and Weblate strings
- Adds example.com-style placeholders across the LDAP/SAML2/OpenID Login Provider fields (server, DN/group DN/search filter, entity IDs, SSO/SLO/discovery URLs, group claims), so the expected syntax is visible without opening the docs.
- Adds a 'textcert' form method (form_cert_box()) for pasted PEM certificates: styled like filepath/dirpath, it parses the certificate and shows its expiration date, using the deviceUp/deviceDown status classes ('Good Till: YYYY-MM-DD' / 'Expired: YYYY-MM-DD'), or flags invalid input. Applied to SAML2's SP/IDP certificate fields.
- Adds a 'privkey' form method (form_privkey_box()) for the SAML2 SP Private Key: never redisplays the stored value (leaving it blank on save keeps the existing key); the status area shows '[stored]' or '[not set]' instead.
- Adds cacti_encrypt_secret()/cacti_decrypt_secret() (lib/auth.php), an AES-256-CBC envelope with a per-installation key persisted in \settings\ on first use, modeled on plugin_servcheck's servcheck_encrypt_credential()/servcheck_decrypt_credential(). The SP Private Key is now encrypted at rest; AbstractLoginProvider::existingParameter()/encryptOrKeepExisting() preserve the existing encrypted value when the privkey field is left blank.
- Regenerates locales/po/cacti.pot so Weblate picks up the new strings.
- Adds tests/Unit/Security/Auth/LoginProviderSecretFieldsTest.php (10 tests) covering the encrypt/decrypt round-trip, IV uniqueness, failure modes, and both new form widgets' rendering.
Verified: PHPStan clean, full Pest Security suite passes (835 tests including the 10 new ones), php-cs-fixer clean.
* fix(login_providers): never redisplay stored cert/key, preserve on blank save
form_cert_box() previously echoed the stored PEM certificate back into the textarea, and form_save() overwrote sp_x509cert/idp_x509cert with an empty string whenever the field was submitted blank - unlike sp_private_key, which already had blank-preserves-existing handling.
- form_cert_box() now behaves like form_privkey_box(): the textarea is always rendered blank (only repopulating from a failed same-submission's SESS_FIELD_VALUES, never the stored certificate); the expiration/status indicator is still computed from the stored value.
- Added AbstractLoginProvider::keepExistingIfBlank(), the non-secret counterpart to encryptOrKeepExisting(): a blank submission keeps the existing stored value instead of clearing it. Wired into SamlLoginProvider::collectParameters() for both sp_x509cert and idp_x509cert.
- Extended tests/Unit/Security/Auth/LoginProviderSecretFieldsTest.php: form_cert_box() never leaks the stored certificate into the rendered HTML, and keepExistingIfBlank()/encryptOrKeepExisting() resolve correctly for both a fresh submission and a blank field with no existing row (no database access needed for either case).
Verified: PHPStan clean, full Pest Security suite passes (838 tests), php-cs-fixer clean.
* fix(login_providers): address Copilot review round 1 + CI formatting
- CI: fixed a php-cs-fixer docblock indentation mismatch in lib/auth.php.
- Upgraded to actual authenticated encryption: the secret envelope is now AES-256-GCM (IV + 16-byte tag + ciphertext) instead of plain AES-256-CBC, which had no MAC/tag - a tampered database value now fails to decrypt instead of silently producing modified plaintext.
- cacti_secret_key() now fails closed on a corrupt persisted key (not valid base64, or not exactly 32 bytes) by throwing, instead of silently substituting an unpersisted random key that would desync from every existing/future ciphertext. cacti_decrypt_secret() catches this and degrades to false (logged); cacti_encrypt_secret() lets it propagate so a broken key never results in a secret being silently stored insecurely or lost.
- Added a backward-compatible read path for the SP Private Key: a provider whose parameters still hold the raw PEM from before this feature existed (cacti_decrypt_secret() returns false because it isn't a valid envelope) now falls back to using that value directly instead of losing the key. AbstractLoginProvider::encryptOrKeepExisting() opportunistically re-encrypts such a legacy value the next time the form is saved (even with the privkey field left blank), rather than requiring the admin to re-paste it.
- form_cert_box()/form_privkey_box() no longer repopulate from SESS_FIELD_VALUES: form_input_validate() writes that session value on every submission (including a successful save), and the save's redirect runs before bottom_footer() clears it - the immediate next page load was echoing the just-submitted plaintext private key/certificate back into the textarea. Both fields are now unconditionally blank.
Verified: PHPStan clean, full Pest Security suite passes (841 tests, 6 new), php-cs-fixer clean (fresh cache, whole-repo dry-run).
|
||
|
|
a52368125e |
feat(auth): rename User Domains to Login Providers, add SAML2 and OpenID Connect (#8066)
* 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. |
||
|
|
2d516ca0a9 |
security: consolidate additional verified GHSA fixes for develop (batch 2) (#8065)
* security: contain data source paths within the RRA directory (GHSA-9qff-xccr-rrc5)
* security: escape executable path in exec_background on non-win32 (GHSA-cv7j-p6fh-j4q7)
* security: stop disabled accounts authenticating via remember-me cookie and revoke on disable (GHSA-pqrp-v752-mc4f)
* security: prefer base_url over client Host for the force_https redirect (GHSA-3466-fp7g-c9rf)
* changelog: add fragments for GHSA-9qff-xccr-rrc5, GHSA-cv7j-p6fh-j4q7, GHSA-pqrp-v752-mc4f, GHSA-3466-fp7g-c9rf
* security: address review findings on GHSA batch 2 (develop)
- lib/dsstats.php, poller_maintenance.php: route through
get_data_source_path() instead of expanding data_source_path directly,
so the RRA containment check applies to these consumers too (GHSA-9qff)
- lib/functions.php: data_source_path_within_rra() now also validates
the realpath of each existing ancestor segment, closing a symlink
pivot that the purely lexical check missed (GHSA-9qff)
- include/global.php: fail closed instead of falling back to SERVER_NAME
when base_url is unset/invalid, removing the residual open-redirect
path (GHSA-3466)
- lib/html_utility.php: cacti_force_https_host()/
cacti_build_https_redirect_url() now parse a scheme-less base_url
(matching lib/reports.php's own normalization) and preserve a
configured non-default port instead of silently dropping it (GHSA-3466)
- tests: move DataSourcePathContainmentTest.php into
tests/Unit/Security/PathTraversal/, and switch its fixtures from a
fictional CACTI_PATH_RRA path to a real temp directory so the new
realpath()-based checks (including a symlink-pivot regression case)
can actually run
* test: fix CACTI_PATH_RRA constant collision in DataSourcePathContainmentTest
CI showed 2 failures: RrdProxyProtocolTest.php runs earlier in the same
Pest process and already defines CACTI_PATH_RRA to a fictional,
non-existent '/var/lib/cacti/rra', so our own definition guard never
ran and realpath(CACTI_PATH_RRA) returned false for every assertion,
failing the two tests that expect containment to be true.
Now ensure the directory backing CACTI_PATH_RRA actually exists on
disk regardless of who defined the constant first, and gracefully
skip only the fixture-dependent assertions if this process can't
create it (e.g. no permission on a system path).
* test: refresh sink inventory baseline for exec_background rename
|
||
|
|
f956ea5a5c |
Convert generic <select> elements to select2(), with new class helpers (#8053)
* feat: convert generic <select> elements to select2, with new class helpers
Replaces the jQuery UI selectmenu() catch-all in setSelectMenus() with select2,
applied uniformly to single and multi selects. Fewer than 10 options disables
the search box (minimumResultsForSearch: Infinity); 10 or more enables it -
same threshold now applied to the existing select2/select2-multi classes.
New layout.js class helper: select2-multi-count, a multi-select whose
selection summary reads "N Selected"/"All Selected" instead of one chip per
option (generic multiSelectAllText/multiSelectCountText strings in html.php,
overridable per-field via data-select-all-text/data-select-count-text/
data-select-all-value). graph_template_id (html_graph.php x2, html_tree.php)
now uses it in place of the jquery.multiselect-based .graph-multiselect,
preserving the existing "All Graph Templates"/"N Templates Selected" wording
and the -1 "all" sentinel behavior (reimplemented via select2:select/unselect
events in setSelectMenus()). The old .graph-multiselect jquery.multiselect
init block is left in place (now unreachable for core's own markup) in case
a plugin/theme still emits that class itself.
select2-callback is reworked from its previously-unused static-URL contract
into an ajax-backed replacement for the legacy .drop-callback/makeCallbacks()
autocomplete widget: data-action + data-variables build the query the same
way makeCallbacks() already did (pageName + action + live values of other
named fields + search term), and data-callback still names a JS function to
invoke on selection, matching form_callback()'s existing $on_change/$class
contract. form_callback() only renders the new <select> markup when its
$class parameter contains select2-callback (opt-in); every existing caller
that doesn't request it keeps the exact legacy output. html_graph.php's
host_id filter (both places) opts in. form_multi_dropdown() gained optional
$select_all_text/$select_count_text/$select_all_value parameters (all
additive/backward compatible) to plumb the graph_template_id wording through.
Compatibility: an org-wide code search found no plugin depending on the
.select2-callback/.graph-multiselect/.drop-callback/makeCallbacks() internals
being changed here (form_callback()'s default output is unchanged; the
.graph-multiselect jquery.multiselect init stays available). Everywhere else
in core that assumed the old jQuery UI selectmenu widget on elements now
covered by the broadened catch-all (disableField()/enableField(), the SNMP
security-level/protocol refresh logic, realtime.js's ds_step/graph_start/size
refresh, api_aggregate.php's field enable/disable, and html_reports.php's
branch_id/site_id/host_template_id handling) now branches on
.select2-hidden-accessible and uses select2's own refresh mechanism
(.trigger('change.select2')) instead, alongside the untouched legacy
selectmenu branch. #user_language and #i18n_default_language (which get
their own bespoke flag-icon languageselect widget) are excluded from the
catch-all, same as before.
The install wizard (install/install.js) has its own independent selectmenu
initialization and is intentionally left untouched.
* fix: use json_encode for select2 multi-select JS translation strings
* fix: make select2 initializers idempotent to stop duplicate widgets
applySkin() (which calls setSelectMenus(), where every select2/select2-multi/
select2-multi-count/select2-callback/catch-all initializer lives) runs more
than once per page view - once from cactiReady() on initial load, and again
from the generic AJAX-completion handler after any loadUrl()/postUrl() call,
which most list/filter pages already trigger during their own initial load.
Since calling .select2() a second time on an element that already has it
initialized creates a second, independent .select2-container next to the
first (select2 does not no-op or tear down the previous instance), every
affected <select> ended up rendering twice. Guard every initializer (plus
the graph_template_id -1 sentinel's event bindings, now namespaced and
unbound first) so a second setSelectMenus() pass skips elements already
marked .select2-hidden-accessible.
* fix: match select2 dropdown styling to the dark theme's real button look
border-radius was 4px (should be 6px, matching .ui-corner-all used
everywhere else); the selection box and dropdown panel were flat black
instead of this theme's actual .ui-button/.ui-widget-content backgrounds
(a subtle gradient-highlight image and a repeating texture image,
respectively). Also add a min-width and force the selected text/dropdown
option list to a single line instead of wrapping.
* fix: match select2 dropdown styling to each theme's real button look
Same fix as the earlier dark theme commit, applied to the remaining themes.
Every theme's select2 override previously used generic placeholder colors
(border-radius: 4px, background: #ffffff/#222222) that did not match that
theme's actual .ui-state-default/.ui-button/.ui-widget-content styling:
- cacti, carrot, hollyberry, raspberry, modern, paw: selection box now uses
the real button gradient-image background instead of flat white; dropdown
panel background already matched and is unchanged.
- sunrise: selection box background corrected to transparent (matching its
real button style) and the dropdown panel to its real black background,
instead of flat white for both.
- paper-plane: selection box background corrected to this theme's real
near-black button color instead of flat white.
- midwinter: switched from hardcoded generic colors to this theme's own
CSS custom properties (var(--border-color), var(--background-content),
var(--text-color-lighter), etc.), matching jquery-ui.midwinter.css.
border-radius is now 6px everywhere, matching .ui-corner-all. All themes
also gained a min-width and forced the selected text/dropdown option list
onto a single line instead of wrapping.
* fix: default select2's dropdownAutoWidth to true
The legacy jQuery UI selectmenu widget always sized its open dropdown to fit
the widest option, regardless of how narrow the closed control itself was.
Select2 defaults to matching the dropdown's width to the control's, so wide
option text was wrapping/truncating inside a narrow panel. Set this as a
global default once instead of touching every individual select2() call.
fix: destroy any existing select2 widget before re-initializing
applySkin() runs more than once per page view. The existing
:not(.select2-hidden-accessible) guards were meant to make re-running the
select2 initializers a no-op, but duplicate widgets were still being
reported. Explicitly tear down any select2 instance still attached right
before the initializers run again, so a widget can never end up duplicated
regardless of why applySkin() re-ran.
fix: don't select2-ify a select a plugin already widget-ified with selectmenu
Symmetric to the above: if a plugin's own script already deliberately
applied jQuery UI selectmenu to one of its selects (a few do, for their own
dynamically-created fields), skip converting it to select2 too.
* fix: convert the Device filter's legacy drop-callback widget to select2-callback
html_host_filter()'s autocomplete-enabled branch never got the select2
treatment applied to every other filter field - it still built its own
<input class='drop-callback'> wired up by the older makeCallbacks()/jQuery UI
autocomplete widget (styled with the legacy ui-selectmenu-button markup),
instead of opting into form_callback()'s newer select2-callback rendering like
everything else. Left side-by-side with now-modernized neighboring filters,
this old widget stood out as a leftover "old select". Switch it to the same
select2-callback select markup (data-action='ajax_hosts',
data-variables='site_id') the 1.2.x branch already uses, reusing the existing
ajax_hosts handlers and get_allowed_ajax_hosts()'s id/label response shape.
* fix: restore select2's gradient after closing without selecting, shrink its height, and drop the now-unneeded min-width
Every theme's active/open select2 style also matched .select2-container--focus,
but select2 leaves that class on the container after the dropdown closes (it
only drops --open), so the flat "active" background stuck around until the
next selection instead of the gradient returning right away. Scope the active
look to --open only.
Also shrink the single-select control from select2's stock 28px down to 24px
(and its arrow/rendered text to match) so it sits closer to the jQuery UI
button height it's replacing, and drop the min-width: 160px placeholder added
earlier to keep wide option text from being clipped - dropdownAutoWidth now
sizes the open dropdown to its content regardless of the closed control's
width, so the constraint is no longer needed.
* feat: add configurable select2 search threshold; drop autocomplete_enabled on develop
Add the same Select Search Minimum Rows setting added on 1.2.x, wiring it
through as the select2SearchRows JS global (include/global_session.php) and
replacing the hardcoded 10-option cutoff in every select2 initializer in
layout.js.
Remove Autocomplete Enabled entirely on develop: it only ever existed to let
the Classic theme opt out of the ajax-backed callback widgets, and Classic
doesn't exist as a develop theme. html_host_filter() and the Graph Items data
source field now always use the ajax-backed rendering (select2-callback /
drop_callback) unconditionally instead of branching on the removed setting.
* fix: keep the first 20 select2 results visible before scrolling
Select2's stock results list only guaranteed ~7-8 rows (200px) before
scrolling. Pin each result row to a consistent 24px (matching the shrunk
control height) and size the list's max-height to fit 20 of them before the
scrollbar kicks in.
* fix: repair layout.js corruption from the select2SearchRows commit, and exclude .multiselect from the catch-all
A faulty automated edit in
|
||
|
|
157a4458ae |
fix(auth): reject malformed remember cookies (#7976)
* 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> |
||
|
|
055c5ef5fa |
security: reject LDAP logins that skip a domain bind (#8009)
* 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> |
||
|
|
ba7b8ef818 |
hardening: predictable import temp name and unsanitized table-filter search (#7866)
* issue#7492: use tempnam for import temp files instead of rand() The import wrote to sys_get_temp_dir()/package_import_<rand()>, a predictable name a local user could pre-create or symlink. tempnam() creates the file atomically with an unpredictable name. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7451: sanitize the CactiTableFilter search term The shared table-filter Search box registered its term with FILTER_DEFAULT (no sanitization), so the raw term reached filter processing. Validate it with sanitize_search_string like the other search filters. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7450: confine the auth-none change-password referer redirect check_reset_no_authentication() placed the raw HTTP_REFERER into the auth_changepassword ref= parameter, an open redirect and header-injection vector. Wrap it in urlencode(validate_redirect_url()), restoring the guard 1.2.x already has (this regressed on develop). Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7490: anchor the graphs.php template_id filter regex The template_id filter used an unanchored regex, so any value containing a cg_/dq_/digit substring passed validation and reached explode()/SQL. Anchor it to match the hardened sibling filters in lib/html_graph.php. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7453: escape template and query names in the package preview get_package_contents() escaped file names with htmle() but emitted graph template, data query, and data-query graph-template names raw, so a package whose template name contains markup produced stored XSS in the import preview. Wrap the names in htmle() like the file rows. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: refresh sink inventory baseline Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
ca2e4f07c2 |
Build releases from managed dependencies (#7888)
* 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> |
||
|
|
3045b4665e |
Add fail2ban support for authentication failures (#7676)
* Add fail2ban support for authentication failures Emit a fixed, machine-parseable AUTH FAILURE line (user, realm, ip, reason) from the universal login-failure path and the 2FA branch, normalise the human AUTH strings, and ship a fail2ban filter, jail example, and README. The IP comes from get_client_addr(); a crafted username cannot forge fields. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: resolve includes through Cacti path constants Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
5727cebebd | security: forward-port 1.2.x hardening (dsstats, guest, permissions, cookie) (#7844) | ||
|
|
cdf92b7c8d | Resolve the client address through the trusted proxy list (#7658) | ||
|
|
db2ac2adf5 |
hardening: make password reset links account-wide single use (#7816)
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
d4e255b5d9 |
security: stage nonce CSP migration and gated enforcement (#7662)
* 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> |
||
|
|
8a3e26c127 |
Validate the post-login referer redirect (#7680)
* Validate the post-login referer redirect The referer branch of auth_login_redirect() emitted HTTP_REFERER as the Location header after only a str_contains(CACTI_PATH_URL) check, which an absolute URL such as https://evil.example/cacti/index.php satisfies, so a crafted referer at login became an open redirect. Route the referer through validate_redirect_url(), which rejects off-host targets and returns a safe local path. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Guard the test function-body offsets and reorder the changelog entry Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Drop the now-sanitized Location header entry from the psalm baseline Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
48bdd90ad1 |
Harden login against lockout races and username enumeration (#7678)
* Harden login against lockout races and username enumeration The failed-attempt counter was read in PHP, incremented, and written back, so concurrent guesses read the same value and undercounted past secpass_lockfailed; increment it atomically in SQL and re-read the authoritative count for the lock decision. An unknown username also returned before any password hashing while a known one ran bcrypt, and the timing delta enumerated valid users; run a throw-away verify against a fixed cost-matched bcrypt hash on the no-such-user path. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Clarify the throw-away verify comments and reorder the changelog entry Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
bebf83c619 | Scope local-login password rehash to realm 0 (#7679) | ||
|
|
a6da63ac89 |
Harden remote graph transport and response validation (#7631)
* Harden remote graph transport boundaries * Tighten remote graph SVG validation * fix: PHPStan Issues with emerging code * Refresh security baselines after develop rebase Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Match transport test to the explicit response-header default Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
b66703cfbc |
Optimize group access authorization queries (#7637)
* Optimize group access authorization queries * Exclude disabled groups from access checks --------- Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
db656e8cb1 |
fix(auth): regenerate the session id when a session becomes authenticated (#7619)
* fix(auth): regenerate the session id when a session becomes authenticated 1.2.x closed this under GHSA-273r-qr93-wgcp; the helper never reached this branch, so a session id planted before the login survived it. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(auth): refuse a locked account before rotating its cookie Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(auth): fail closed when transition user is missing * fix(auth): gate remembered login before token creation * Fix auth transition cache invalidation --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
f95fd94388 |
docs(auth): document group token invalidation (#7610)
* Document group token invalidation * Clarify group token invalidation docs |
||
|
|
ec05b4f816 | Expire auth tokens when permissions change (#7601) | ||
|
|
e96d791e2b |
fix(auth): validate measure/order before ORDER BY concatenation in get_allowed_graphs() (#7438)
* fix(auth): validate measure/order before ORDER BY concatenation in get_allowed_graphs()
get_allowed_graphs() built its dsstats ORDER BY clause by concatenating
$sql_order['measure'] and $sql_order['order'] directly into SQL. Both
values trace back to unfiltered request variables (grv('measure'),
grv('graph_order')) at several call sites in html_graph.php and
html_tree.php, none of which register the vars in a filter array.
Reachable pre-auth via graph_view.php when dsstats_enable and the guest
account are on.
Add get_dsstats_order_string() in lib/html_utility.php, next to the
existing sort_column/sort_direction validator get_order_string(), and
have get_allowed_graphs() call it instead of trusting the raw array.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* refactor(auth): validate dsstats measure/order via CactiValidator + backed enum
get_dsstats_order_string() used a hand-rolled in_array() whitelist instead
of this codebase's established CactiValidator/Symfony Assert pattern
(~334 existing call sites). Adds a DsstatsMeasure backed enum as the
single source of truth for legal measure values, feeding an
Assert\Choice constraint checked via CactiValidator::isValid() (not
validateInput(), which writes to $_SESSION and isn't appropriate for
this pure helper).
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix: guard dsstats order/measure against array input and missing require
Addresses Copilot review: require html_utility.php where
get_dsstats_order_string() is called from lib/auth.php, and reject
non-scalar measure/order values before validating/concatenating them.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test(auth): add integration coverage for dsstats ORDER BY fragment build
Exercises get_dsstats_order_string() together with dsstats_get_best_partition()
and db_qstr(), the other real functions get_allowed_graphs() calls on the
same path, reproducing the JOIN + ORDER BY fragment it concatenates.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* style(auth): order use import before require_once in html_utility.php
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
|
||
|
|
80c95c5ff8 |
fix(auth): normalize trusted-proxy match with inet_pton (#7363)
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
ccfe04a20c |
Harden remote-agent FCRDNS and basic auth identity (#7324)
* Harden remote agent and basic auth identity handling * fix(auth): gate forwarded identity headers behind trusted proxies Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * chore: Update CHANGELOG for new feature, and reorder per rules * chore: Add the issue number to CHANGELOG * chore: Update hotspots --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
2bfa84e72c |
chore: Fix php-stan errors with new release and remove phpmailer and other removed dependencies (#7317)
* chore: Fix php-stan errors with new release * fix: Comment this out till we have a project to update all files |
||
|
|
a12c2fa0ed | accept X-Forwarded-User header in Basic auth (#7314) | ||
|
|
61a542a670 |
fix(auth): correct auth system bugs (#7187)
* fix(auth): correct auth system bugs Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Address auth PR review findings Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Guard remember cookie cache deletion Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(auth): prevent remote agent lockout on FCrDNS failure and add regression tests Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs: update CHANGELOG for auth system correctness fixes Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(auth): stop returning 2fa secret from verification Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(auth): reject remote agent requests that fail forward-confirmed reverse DNS Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: ignore phpstan function.alreadyNarrowedType identifier Clears the level 6 alreadyNarrowedType error on lib/data_query.php:2591 that develop currently emits, matching the sibling narrowing identifiers already ignored. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs: reword FCRDNS change as a refactor, not a security fix Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
7a1e9bbf92 |
harden CSV encoding and process execution handling (#7196)
* fix: harden CSV encoding and process execution handling Route CSV export cells through a shared cacti_csv_cell() helper so values with quotes, commas, or leading formula characters round-trip correctly, and add defensive guards on shell construction, unchecked proc_open handles, discarded exec exit codes, and gap values in nth-percentile fetch math. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: address review feedback on CSV and process hardening - escape poller_id in cmd_realtime proc_open command to match remote_agent Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: keep numeric CSV cells unquoted and guard snmptrap and rrdtool process failures - exempt numeric values from the CSV formula-injection prefix and preserve empty snmptrap positions while bailing out when the RRDtool process fails to spawn Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: ignore phpstan function.alreadyNarrowedType identifier Clears the level 6 alreadyNarrowedType error on lib/data_query.php:2591 that develop currently emits, matching the sibling narrowing identifiers already ignored. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
f89c7d8254 |
fix: correct codespell-detected spelling errors in PHP source files (#6804)
* fix: correct codespell-detected spelling errors in PHP source files Fix 63 spelling errors identified by automated codespell scan across 23 files. All changes are in comments, docblocks, string literals, and one dead variable assignment ($flage → $flags in lib/database.php). No logic changes. Corrected words include: accommodate, authentication, complete, dependent, draggable, filtering, formatting, heartbeat, immediately, information, initialize, language, logging, parameter, please, resource, resetting, similarly, specified, SQLite, statistics, support, timeout, whether. Exclusions: parms/PARMS (Cacti CLI variable convention), tht/ot (SQL aliases), CurrOS/hasTables (PHP variables), pring (internal function). Fixes #6803 Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: address Copilot review suggestions on PR #6804 Remove dead variable assignment activated by $postion -> $position typo fix in install/upgrades/1_0_0.php. The misspelled variable was never read, so fixing the name caused it to silently overwrite the correctly computed $position before the UPDATE. Removing the line restores the original (intended) behavior. Fix docblock possessive: "it's formatting" -> "its formatting" in lib/reports.php. Refs #6803 Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * i18n: regenerate cacti.pot after msgid changes in global_settings.php "pre-selected" -> "preselected" and "re-using" -> "reusing" changed two msgid strings. Regenerate cacti.pot so translation tooling picks up the corrected English source strings. Refs #6803 Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
ecd10b1ba0 |
fix: add PHPStan level 8 type guards to lib/auth.php (#6756)
* fix: add PHPStan level 8 type guards to lib/auth.php Add defensive type checks (is_array, !== false, int casts) to resolve PHPStan level 8 errors. No behavioral changes for normal operation; only error paths (db failure, file open failure) are affected. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: guard empty array index access on SERVER vars Add ?? '' fallback when accessing [0] on potentially empty arrays. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: use cacti_sizeof() guard on 2FA empty array checks is_array() passes for empty arrays, allowing undefined index access on tfa_enabled and tfa_secret. Add cacti_sizeof() to match the existing guard pattern used elsewhere in auth.php. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(auth): add isset guards for tfa_secret and username before access Copilot noted that db_fetch_row_prepared() returns an empty array on miss, so the is_array() guard was redundant. Also adds !isset() checks for tfa_secret and username before accessing them in the else branch, matching PHPStan's type inference requirements. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: remove redundant is_array() alongside cacti_sizeof() in auth.php cacti_sizeof() returns false for non-arrays, making the paired is_array() checks at lines 140, 311, and 324 redundant. Requested by TheWitness. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: remove redundant is_array() checks alongside cacti_sizeof() in auth.php cacti_sizeof() already returns false for non-arrays, so paired is_array() checks are redundant. Flagged by TheWitness review. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: remove redundant (int) casts in is_tree_branch_empty/is_tree_allowed calls DB query already returns integers; casts are unnecessary. Signed-off-by: Thomas Vincent <thomasvincent@users.noreply.github.com> Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Signed-off-by: Thomas Vincent <thomasvincent@users.noreply.github.com> |
||
|
|
6ad201cc4f |
fix: remove deprecated curl_close() and imagedestroy() for PHP 8.5 (#6772)
* fix: remove deprecated curl_close() and imagedestroy() for PHP 8.5 PHP 8.5 deprecates curl_close(), imagedestroy(), and xml_parser_free(). Handles are automatically freed when they go out of scope. - cli/add_site.php: remove 2 curl_close() calls - lib/auth.php: remove curl_close() in secpass_check_pwned() - lib/plugins.php: remove curl_close() in plugin fetch - lib/reports.php: remove 2 imagedestroy() calls in jpeg2png/png2gif - lib/rrd.php: remove 3 imagedestroy() calls in graph rendering No behavioral change; resources are freed implicitly. Made-with: Cursor * test: add Pest tests for PHP 8.5 curl_close/imagedestroy removal - Php85DeprecationsTest: static checks that no curl_close or imagedestroy calls remain in the 5 changed files - ReportsImageConversionTest: png2jpeg/png2gif produce valid output without explicit imagedestroy (resources freed on scope exit) Made-with: Cursor --------- Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
6aefea0d98 |
hardening: add column-name whitelist to auth view and policy queries (#6697)
* cleanup: remove unreachable defensive error logs in aggregate graph item validation * hardening: add column-name whitelist to auth view and policy queries Add $allowed_views whitelist to is_view_allowed() to reject invalid column names before SQL execution. Backtick-quote $view in queries. Add Pest tests verifying whitelist rejects SQL injection, arbitrary column names, and backtick injection attempts. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore: drop unrelated lib/api_aggregate changes Keep PR scope focused on the issue for this branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * chore: drop unrelated user_admin change Keep auth column validation PR focused on auth.php behavior and tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
3e1dc3c886 |
fix: replace strtolower/strtoupper with locale-independent wrappers (#6717)
* cleanup: remove unreachable defensive error logs in aggregate graph item validation * fix: replace strtolower/strtoupper with locale-independent wrappers PHP 8.5 deprecates locale-sensitive strtolower() and strtoupper(). Add cacti_strtolower() and cacti_strtoupper() wrappers that use mb_strtolower/mb_strtoupper with explicit UTF-8 encoding, then replace all 185 call sites across 65 files. Existing mb_strtolower() calls (email normalization) are left as-is since they already specify encoding explicitly. Includes 18 Pest tests covering ASCII, UTF-8, protocol names, config values, hex strings, NaN detection, and file extension patterns. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: correct docblock references and month_shift() strpos check Fix docblocks to reference strtolower()/strtoupper() (the deprecated PHP builtins) instead of the wrapper names. Fix month_shift() to use strpos() !== false instead of > 0 so 'month' at position 0 is matched. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * chore: drop unrelated lib/api_aggregate changes Keep PR scope focused on the issue for this branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
c90ee05730 |
fix: correct JOIN condition in is_view_allowed() group membership lookup (#6701)
Refs #6698 The JOIN between user_auth_group and user_auth_group_members used uagm.user_id instead of uagm.group_id. Since uag.id is the group ID, it must match the group_id column, not the user_id column. This caused group-based view permissions to silently fail for all group members. The WHERE clause correctly uses uagm.user_id to filter by the current session user; only the ON clause was wrong. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
b03196a6c2 | Fix typos in auth, function, and spikekill log messages (#6685) | ||
|
|
fd4d9621c7 |
fix: use strict comparison and remove PHP <5.5 compat shims in auth (#6626)
* fix: use strict comparison in password verify, remove dead fallbacks - Fix loose == to strict === in compat_password_verify() md5 comparison to prevent PHP type juggling attacks - Remove dead function_exists() guards in compat_password_verify(), compat_password_hash(), and compat_password_needs_rehash() (PHP 8.1+ guarantees these functions exist) - Remove md5 fallback in compat_password_hash() - Remove unreachable return true fallback in compat_password_needs_rehash() - Keep md5/sha256 legacy hash verification for backward compatibility with pre-bcrypt user accounts; rehash-on-login upgrades them to bcrypt Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: restore function_exists('md5') guard for FIPS compliance Keep strict === comparison but restore the md5 availability check for FIPS-compliant RHEL systems where md5 is disabled at OS level. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
68e6ff94f4 |
fix: replace md5/mt_rand auth token with CSPRNG (#6625)
* fix: replace md5/mt_rand auth token with CSPRNG Replace predictable md5(REQUEST_TIME + mt_rand) + md5(client_ip) token generation in set_auth_cookie() with bin2hex(random_bytes(32)). Both produce 64 hex chars, so downstream sha512 hashing and DB storage are unaffected. Existing cookies remain valid. The only change is entropy quality: 256-bit CSPRNG vs. guessable server timestamp + weak PRNG + known client IP. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: replace md5+session_id with bin2hex(random_bytes) in generate_hash md5(session_id() . microtime() . random_int()) still leaks session entropy and produces predictable hashes. random_bytes(16) gives 128 bits of CSPRNG output, matching the existing 32-char hex return type. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
27919c50d7 | Updating Copyrights to 2026 (#6578) | ||
|
|
b993471579 |
Fix #6539 - Allow Standard Cacti Log Files to be Rotated on Demand,PHPdoc, and Typing (#6540)
* Fix #6539 - Allow Standard Cacti Log Files to be Rotated on Demand from the Cacti UI * QA: php-cs-fixer updates and documentation for lib/database.php * QA: Update .gitignore for composer files * QA: Update .gitignore for composer files * QA: Back off .gitignore change its duplicated * QA: Fix typing of db_has_permissions * QA: Fixing typing issue in db_format_index_create * Update lib/clog_webapi.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * QA: Fix additional code issues * QA: Applying more defaults to add device * QA: Fix at the database layer * QA: Fixes based upon automated testing * QA: Fix at the database layer * QA: After new SQL checking * QA: Skip MacTrack oui database download warnings * QA: Fixing type issue * QA: Recommendation from Copilot * QA: Recommendation from Copilot * QA: Recommendation from Copilot * QA: Recommendation from Copilot * QA: Some minor changes to avoid errors in testing * QA: Unused variable * QA: Recommendation from Copilot * QA: Recommendation from Copilot * QA: Recommendation from Copilot * QA: Minor introduced code defect * QA: Adding some unifying space around concatination * QA: Consistent comments and new function alias' * QA: Multi-line comment corrections * QA: Fix SQL Style * QA: Missed one function alias * QA: PHPStan Level 2 and PHPDoc fixes * QA: PHPStan Level 2 and PHPDoc Fixes * QA: PHPStan Level 6 Fixes * QA: Fix lib/xml.php to PHPStan Level 6 * QA: Wrong grep -v * PHPStan Level 6 for lib/vdef.php * PHPStan Level 8 for lib/time.php * QA: Raise PHPStan to Level 6 for lib/database.php * QA: Remove @deprecated tag from grv() * QA: Switch to htmle() alias over html_escape() * QA: More addressing PHPStan issues at Level 6 * QA: Change recommended by Copilot for Readability * QA: More PHPStan fixes * QA: More PHPStan fixes * QA: More PHPStan fixes for lib/utility.php * QA: More PHPStan fixes at Level 6 * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: More PHPStan fixes at Level 6 * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: More PHPStan fixes at Level 6 * QA: More PHPStan fixes at Level 6 * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: More PHPStan fixes at Level 6 * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: More PHPStan fixes at Level 6 * QA: Raise PHPStan to Level 6 More Functions * QA: Raise PHPStan to Level 6 More Functions * QA: More PHPStan fixes at Level 6 * QA: More PHPStan fixes at Level 6 * QA: More PHPStan fixes at Level 6 * QA: Raise PHPStan to Level 6 More Functions * QA: More PHPStan fixes at Level 6 * QA: Some phpseclib3 fixups to update to latest * QA: More fixups for graphing for magic strings * QA: More PHPStan fixes at Level 6 * QA: Fixing order string type on get_allowed_graphs() * QA: Fixing improper use of print * QA: Optimization of color_templates * QA: Copilot recommendation * QA: Copilot recommendation * QA: Copilot recommendation * QA: Copilot recommendation * QA: Copilot recommendation * QA: Copilot recommendation * QA: Copilot recommendation * QA: Copilot recommendation * QA: Raise PHPStan to Level 6 More Functions * QA: Issues insalling Cacti * QA: Issues insalling Cacti * QA: Issues insalling Cacti * QA: Issues insalling Cacti * QA: Issues insalling Cacti * QA: Issues testing Cacti * QA: Copilot recommendation * QA: More PHPStan fixes at Level 6 * QA: More PHPStan fixes at Level 6 * QA: More PHPStan fixes at Level 6 * QA: More PHPStan fixes at Level 6 * QA: More PHPStan fixes at Level 6 * QA: Installer failing * Undo ownership changes to remove tar errors * QA: Updates after php-cs-fixer * QA: Adding code checks to syntax checking * QA Change syntax flow language * QA: Reduce the number of cures * QA: Use sequentional option * Change runner to self-hosted for check job * Fix typo in OS version in workflow file * Add Git installation step in workflow Added step to install Git if not already installed. * Add '-y' flag to apt-get install and update commands Updated apt-get commands to include '-y' for automatic confirmation. * Change workflow OS version to 'ubuntu-latest' Updated the workflow to use 'ubuntu-latest' instead of 'ubuntu:latest' and modified the 'runs-on' configuration. * Remove sequential option from phpcsfixer script * Remove PHPCS fixer step from workflow Removed PHP coding standards check from workflow. * Move code checks to before plugins Moving these core checks to before the plugins are installed as we really want to check only the base Cacti here. * Add Composer validation and caching to start of workflow * QA: Addressing PHPStan additional * QA: Restore composer lock file * QA: Addressing PHPStan additional * QA: Addressing PHPStan additional * QA: Complete LDAP Project * QA: php-cs-fixer doing it's thing * QA: Syntax is already happening in PHPStan * QA: The md5 function no longer exists * QA: Update after ldap changes * QA: Undo that last password hack * QA: Attempting to switch to MariaDB 11.8 * QA: Attempting to switch to MariaDB 11.8 * QA: Resolving some issues with PHP8.4 * QA: Resolving some issues with PHP8.4 * QA: Add PHP8.5 to the matrix * QA: Removing PHP8.5 due to dependency issues * QA: Add the DB version to the testing output * QA: Switch back to MySQL for the moment * QA: More fidelity in reporting * QA: The pipe must be mixed * QA: Moving audit_schema.php changes from 1.2.x * QA: Moving audit_schema.php changes from 1.2.x * QA: Restore mysql monitoring * QA: php-cs-fixer issues * QA: Attempt to roll back to MariaDB 10.6 * QA: Attempt to fix check_all_pages.sh * QA: Make installer functional again * QA: Fix installer php-cs-fixer issues * QA: Type to a string * QA: Get the database version correctly * QA: Fix issue with reports in check_all_pages.sh * QA: Check the server version in the right place * Feature: Support SHA256 as a new hash for passwords * QA: Function definition and return typing audit * QA: Refinement of form types * QA: Refinement of form types * QA: Consistency of comments * QA: Issues with php-cs-fixer and more comments * QA: More typing issues * QA: php-cs-fixer on merge conflict resolution --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
5be28b42a2 | Fix#6523: When disabling a users, ensure that their stored authentication cookies are remove and any sessions cleared | ||
|
|
93aa2e648e |
Changed error for an invalid password to match error for an invalid username. (#6483)
Prior to this change, when logging in with an invalid username, Cacti would return "Access Denied! Login Failed!" However, if the username is valid, Cacti would instead return "Access Denied! Login failed." This change capitalizes the word "failed" when using a valid username, which makes it more difficult for an attacker to enumerate accounts with a brute force attack. |
||
|
|
ae80474037 | More PHPDoc and function parameter type declaring | ||
|
|
b9334a15d9 | QA: Updates for files for php-cs-fixer and some php-doc changes | ||
|
|
3520ac709c | QA: Restore installer functionality |