Skip to content

Commit 29bcb4e

Browse files
Merge branch 'upstream-develop' into HEAD
# Conflicts: # phpunit.xml
2 parents f840f2a + 410e948 commit 29bcb4e

85 files changed

Lines changed: 6933 additions & 434 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/syntax.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
# runs-on: ubuntu-latest
4444
# steps:
4545
# - name: Checkout
46-
# uses: actions/checkout@v4
46+
# uses: actions/checkout@v6
4747

4848
# - name: Install codespell
4949
# run: pip install codespell==2.4.1
@@ -79,7 +79,7 @@ jobs:
7979
name: PHP ${{ matrix.php }} Test on ${{ matrix.os }}
8080
steps:
8181
- name: Checkout
82-
uses: actions/checkout@v4
82+
uses: actions/checkout@v6
8383

8484
- name: Install PHP ${{ matrix.php }}
8585
uses: shivammathur/setup-php@v2

.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ include/vendor/csrf/csrf-secret.php
9999
# Ignore visual studio code
100100
.vscode/**
101101

102+
# Ignore local developer tooling state (Claude Code, oh-my-claudecode, git worktrees, local notes)
103+
.claude/
104+
.omc/
105+
.worktrees/
106+
notepad.md
107+
102108
# Ignore vendor folders
103109
include/vendor/**
104110
vendor/**
@@ -112,3 +118,19 @@ docker-compose.override.yml
112118
# Ignore Claude and Worktrees
113119
.worktrees/**
114120
.claude/**
121+
122+
# Security research artifacts (never commit)
123+
CLAUDE.md
124+
reproduce_ghsa_*.php
125+
verify_task*.php
126+
.gemini_security/
127+
128+
# Test/build artifacts
129+
/tests/composer.lock
130+
/tests/e2e/playwright-report/
131+
/tests/e2e/test-results/
132+
/tests/.phpunit.result.cache
133+
134+
# Local autopilot/session data
135+
/.omc/
136+
/docs/superpowers/

CHANGELOG

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Cacti CHANGELOG
22

33
1.3.0-dev
4+
-issue#7131: Fix data_input_data column-expansion bugs in change_data_template and api_data_source_duplicate
5+
-issue#7137: Fix eleven PHPStan Level 6 errors in form_save error-redirect URLs and lib/html.php right-tab block
46
-issue#6762: Harden lib/ping.php: apply cacti_escapeshellarg() to hostname in native ping and fping shell commands
57
-issue#6760: Harden link.php: apply sanitize_uri() to HTTP_REFERER before redirect
68
-issue#6761: Harden auth_changepassword.php: apply sanitize_uri() to HTTP_REFERER in all redirect paths
@@ -209,11 +211,45 @@ Cacti CHANGELOG
209211
-feature: Continue to support MD5() for the password verifications but also support SHA256
210212

211213
1.2.31
212-
-issue#6168: RRD cleaner does not purge files
213-
-issue#6202: Adding devices via automation fails
214-
-issue#6204: Automation breaks if the field does not previously exist in the host_snmp_cache table
215-
-issue#6210: Fix Error: You have an error in your SQL syntax due to using table field without backtick.
216-
-issue#6240: Fix sorting exception in rrdtool_function_create function
214+
-security#GHSA-6RVG-2VM8-5WRF: CVE-2026-22802 Authentication Bypass leads to information disclosure
215+
-security#GHSA-9wvp-cfx6-hj7p: CVE-2026-24482 1st argument to preg_replace is not sanitized and given from user_inputs
216+
-security#GHSA-j3p9-q28q-8j33: CVE-2026-24483 Unsafe serialization usage in the maint plugin on user supplied POST data
217+
-security#GHSA-23g4-vf2j-94w4: CVE-2026-39894 RRDtool metric shift via LC_NUMERIC locale comma decimal formatting
218+
-security#GHSA-wpjq-m269-mghj: CVE-2026-39895 Second-order RCE via unescaped log path in exec_background shell redirection
219+
-security#GHSA-hr82-h9vr-587w: CVE-2026-39896 TOCTOU race in auth_process_lockout allows brute-force lockout bypass
220+
-security#GHSA-2j98-xfjq-gw39: CVE-2026-39897 Reflected XSS in html_auth_footer error message output
221+
-security#GHSA-fwh3-8c8r-378r: CVE-2026-39898 Reflected XSS via rfilter parameter in aggregate_graphs.php input value
222+
-security#GHSA-pr9x-34w8-4mf7: CVE-2026-39899 Path traversal via filename parameter in package_import.php
223+
-security#GHSA-34rf-frc3-v48r: CVE-2026-39900 Reflected XSS via tab parameter in auth_profile.php JavaScript context
224+
-security#GHSA-w47c-53f9-w47g: CVE-2026-39947 RRDtool IPC pipe poisoning via is_numeric newline bypass in rrdtool_function_update
225+
-security#GHSA-9jqv-4cpm-vm2c: CVE-2026-39948 SQL Injection via rfilter parameter in RLIKE clauses
226+
-security#GHSA-xq98-376r-hv9j: CVE-2026-40079 Command Injection via escape_command() no-op in RRDtool execution
227+
-security#GHSA-69gg-mjfm-jjpc: CVE-2026-39893 Pre-authentication SQL injection via rfilter RLIKE clause in graph_view.php
228+
-security#GHSA-c4qp-j9r9-fq24: CVE-2026-39902 Authenticated RCE on Data Input
229+
-security#GHSA-rm7p-qcqm-x5m6: CVE-2026-39938 Unauthenticated LFI via graph_theme and rrdtool IPC serialization hardening
230+
-security#GHSA-vp35-4h28-r883: CVE-2026-39939 Path traversal in Package Import file write allows arbitrary file creation in webroot
231+
-security#GHSA-8522-5p3m-754c: CVE-2026-39949 Authenticated Remote Code Execution via Host Variable Injection
232+
-security#GHSA-j696-m433-87qq: CVE-2026-39950 Arbitrary PHP file write via Plugin Archive extraction leading to RCE
233+
-security#GHSA-pf37-v86f-5xwp: CVE-2026-39951 Stored SQL Injection via graph_name_regexp in Reports feature
234+
-security#GHSA-6233-v5hc-6gvf: CVE-2026-39952 Stored XSS in Report Tree expansion titles
235+
-security#GHSA-xrh3-6pfg-ff35: CVE-2026-39953 Unauthenticated Stored SQL Injection via graph_name_regexp in reports.php
236+
-security#GHSA-gp82-qhrg-crv7: CVE-2026-39955 Pre-Authentication SQL Injection via unanchored FILTER_VALIDATE_REGEXP in graph_view.php
237+
-security#GHSA-84q3-92xc-c3pf: CVE-2026-40078 Backend ORDER BY SQL Injection
238+
-security#GHSA-6gr7-53g8-vchq: CVE-2026-40080 Open Redirect via HTTP_REFERER substring check in auth_login_redirect
239+
-security#GHSA-8p2f-6jvx-j75j: CVE-2026-40081 Reports IDOR allows any authenticated user to modify other users' reports (CWE-639)
240+
-security#GHSA-273r-qr93-wgcp: CVE-2026-40082 Session Fixation via missing session_regenerate_id() after login
241+
-security#GHSA-j9jv-6xjq-9hhj: CVE-2026-40083 SQL Injection in managers.php via uncast array values in IN clauses
242+
-security#GHSA-mjvw-mhj5-9jcj: CVE-2026-40084 Arbitrary File Read via path traversal in Report format_file parameter
243+
-security#GHSA-274c-97hj-pv2v: CVE-2026-40941 Package Import Signature Validation Bypass allows self-signed packages
244+
-security#GHSA-g37j-39f4-6r4j: CVE-2026-41884 Arbitrary File Read via Reports format_file path traversal
245+
-security: CVE-2026-1513 billboard.js before 3.18.0 Improper Input Sanitization Allows Remote JavaScript Execution
246+
-security: CVE-2026-40194, CVE-2026-32935 in phpseclib - This is breaking change for RRDProxy
247+
-security: CVE-2026-XXXXX - SQL Injection in automation_tree_rules.php
248+
-issue#6168: When purging RRD files, paths are not correctly handled
249+
-issue#6202: When using automation, devices may not be added as expected
250+
-issue#6204: Attempting to match a field in automation may cause unexpected errors
251+
-issue#6210: Ensure column names are escaped to prevent reserved word issues
252+
-issue#6240: Improve sort order for incorrect RRA's
217253
-issue#6249: Unable to send Email to users without a domain name
218254
-issue#6251: Cacti can issue a warning in a case where a user attempts to view a graph that no longer exists
219255
-issue#6253: Unification of i18n behaviour when the input is null

aggregate_graphs.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,11 @@ function form_save() : void {
291291
} elseif (isrv('save_component_item')) {
292292
global $graph_item_types;
293293

294+
/* sql_save() inside the items foreach below assigns this; if the
295+
* loop never enters the !is_error_message() branch we still need a
296+
* defined value for the error-redirect URL fallback. */
297+
$graph_template_item_id = 0;
298+
294299
$items[0] = [];
295300

296301
// handle saving aggregate graph items in separate function
@@ -333,12 +338,15 @@ function form_save() : void {
333338
];
334339
}
335340

341+
$graph_template_item_id = '';
342+
336343
foreach ($items as $item) {
337344
// generate a new sequence if needed
338345
if (ierv('sequence')) {
339346
$sequence = gfrv('sequence');
340347
srv('sequence', get_sequence($sequence, 'sequence', 'graph_templates_item', 'local_graph_id=' . grv('local_graph_id')));
341348
}
349+
342350
$save['id'] = gfrv('graph_template_item_id');
343351
$save['graph_template_id'] = gfrv('graph_template_id');
344352
$save['local_graph_template_item_id'] = gfrv('local_graph_template_item_id');

automation_tree_rules.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,29 @@ function form_save() : void {
362362

363363
$automation_graph_rule_item_id = null;
364364

365+
$field_name = str_replace(['ht.', 'h.', 'gt.', 'gtg.'], '', $save['field']);
366+
367+
$exists = db_fetch_cell_prepared('SELECT field_name
368+
FROM host_snmp_cache
369+
WHERE field_name = ?
370+
LIMIT 1',
371+
[$field_name]);
372+
373+
if (!$exists) {
374+
// check the case where there is no entry in the host_snmp_cache table yet
375+
if ("'$field_name'" != db_qstr($field_name)) {
376+
if (!db_column_exists('host', $field_name) && !db_column_exists('host_template', $field_name) && !db_column_exists('graph_templates', $field_name) && !db_column_exists('graph_templates_graph', $field_name)) {
377+
raise_message('sql_injection', __('An attempt was made to perform a SQL injection in Graph Tree automation'), MESSAGE_LEVEL_ERROR);
378+
379+
cacti_log(sprintf('ERROR: An attempt was made to perform a SQL Injection in Graph Tree Automation from client address \'%s\'', get_client_addr()), false, 'SECURITY');
380+
381+
header('Location: automation_tree_rules.php?header=false&action=edit&id=' . get_request_var('id'));
382+
383+
exit;
384+
}
385+
}
386+
}
387+
365388
if (!is_error_message()) {
366389
$automation_graph_rule_item_id = sql_save($save, 'automation_tree_rule_items');
367390

cli/upgrade_database.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
db_execute_prepared('UPDATE version SET cacti = ?', [$cacti_upgrade_version]);
176176

177177
if (cacti_version_compare(CACTI_VERSION, $cacti_upgrade_version, '=')) {
178-
db_execute("UPDATE version SET cacti = '" . CACTI_VERSION_FULL . "'");
178+
db_execute_prepared('UPDATE version SET cacti = ?', [CACTI_VERSION_FULL]);
179179

180180
break;
181181
}

cmd_realtime.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@
4444
$graph_id = (int)$_SERVER['argv'][2];
4545
$interval = (int)$_SERVER['argv'][3];
4646

47+
if (!preg_match('/^(?:[0-9]+|[A-Fa-f0-9]{64})$/', $poller_id)) {
48+
print "Invalid poller_id specified.\n\n";
49+
50+
exit(-1);
51+
}
52+
4753
if ($graph_id <= 0) {
4854
print "Invalid graph_id specified.\n\n";
4955

color_templates.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,16 @@ function form_save() : void {
214214
gfrv('sequence');
215215
// ====================================================
216216

217+
/* sql_save() inside the items foreach below assigns this; if the
218+
* loop never enters the !is_error_message() branch we still need a
219+
* defined value for the error-redirect URL fallback. */
220+
$color_template_item_id = 0;
221+
217222
$items[0] = [];
218223
$sequence = gnrv('sequence');
219224

225+
$color_template_item_id = '';
226+
220227
foreach ($items as $item) {
221228
// generate a new sequence if needed
222229
if (empty($sequence)) {

composer.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,29 @@
5353
"php-mqtt/client": "^2.2",
5454
"phpmailer/phpmailer": "^7.0.2",
5555
"phpseclib/phpseclib": "^3.0",
56+
"psr/log": "^1.1",
5657
"slim/slim": "^4.14",
5758
"stevenmaguire/oauth2-keycloak": "^6.1.0",
58-
"stevenmaguire/oauth2-microsoft": "^2.2"
59+
"stevenmaguire/oauth2-microsoft": "^2.2",
60+
"symfony/filesystem": "^6.0",
61+
"symfony/http-foundation": "^6.0"
5962
},
6063
"require-dev": {
64+
"composer/xdebug-handler": "^3.0",
6165
"friendsofphp/php-cs-fixer": "^3.86",
6266
"overtrue/phplint": "^9.6",
63-
"phpstan/phpstan": "^2.1.21",
6467
"pestphp/pest": "^2",
6568
"pestphp/pest-plugin-drift": "^2.0",
66-
"composer/xdebug-handler": "^3.0",
69+
"phpstan/phpstan": "^2.1.21",
6770
"rector/rector": "^2.1.2"
6871
},
6972
"scripts": {
70-
"lint": "phplint --no-cache --exclude=include/vendor ",
73+
"lint": "phplint --no-cache --exclude=include/vendor --exclude=plugins ",
7174
"phpstan": "phpstan analyse --level 6 ",
7275
"php-cs-fixer": "php-cs-fixer fix --allow-unsupported-php-version=yes --dry-run --diff --config=./.php-cs-fixer.php ",
7376
"phpcsfixer": "@php-cs-fixer",
7477
"php-cs-fixit": "php-cs-fixer fix ",
78+
"install-hooks": "bash -c 'install -m 755 tests/tools/pre-commit \"$(git rev-parse --git-path hooks/pre-commit)\"'",
7579
"test": "include/vendor/bin/pest --display-warnings"
7680
},
7781
"authors": [
@@ -92,7 +96,7 @@
9296
"sort-packages": true,
9397
"vendor-dir": "./include/vendor",
9498
"audit": {
95-
"block-insecure": false
99+
"block-insecure": false
96100
},
97101
"allow-plugins": {
98102
"pestphp/pest-plugin": true

data_debug.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@
109109
case 'ajax_hosts':
110110
$sql_where = '';
111111

112-
if (grv('site_id') > 0) {
113-
$sql_where = 'site_id = ' . grv('site_id');
112+
if (gfrv('site_id') > 0) {
113+
$sql_where = 'site_id = ' . gfrv('site_id');
114114
}
115115

116116
get_allowed_ajax_hosts(true, true, $sql_where);
@@ -119,8 +119,8 @@
119119
case 'ajax_hosts_noany':
120120
$sql_where = '';
121121

122-
if (grv('site_id') > 0) {
123-
$sql_where = 'site_id = ' . grv('site_id');
122+
if (gfrv('site_id') > 0) {
123+
$sql_where = 'site_id = ' . gfrv('site_id');
124124
}
125125

126126
get_allowed_ajax_hosts(false, true, $sql_where);

0 commit comments

Comments
 (0)