Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

--- develop ---

* security: Replace the confirmation pages' inline `onClick='cactiReturnTo()'` Cancel buttons with the CSP-safe `cactiReturnTo` class so the pages no longer trip Cacti's Content-Security-Policy script-src-attr directive; see the README for the one-time `applySkin()` snippet needed when running Cacti below 1.2.31 (1.2.31+ binds the class automatically)
* security: Move the filter selects' inline onChange handlers into the ready block (and bind them via change, not click) so the page no longer trips Cacti's Content-Security-Policy script-src-attr directive
* dev: Measure CI coverage with xdebug instead of pcov so the plugin's own sources are instrumented (pcov auto-scopes to the Composer root and skipped cacti/plugins/, leaving the patch-coverage gate with nothing to measure)
* dev: Enforce patch coverage of changed lines in CI and remove the inert COMPOSER_ROOT_VERSION env from the Pest step
* security: Add a version-safe CSP nonce (`plugin_tags_csp_nonce()`) to every inline `<script>` tag so pages stay compatible with Cacti's Content-Security-Policy nonce enforcement, while falling back cleanly on older Cacti releases that lack the `CactiSecureHeaders` class
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ Instead, they are summarised and only the most recent few are shown.
It is also possible to archive old tags. These are then not displayed in the graph.


## Cacti compatibility

If you are running a version of Cacti below 1.2.31, please add the function
below to the `applySkin()` function in `include/layout.js` to enable the Cancel
buttons on forms to work:

```js
$(document).off('click.cactiReturnTo', '.cactiReturnTo')
.on('click.cactiReturnTo', '.cactiReturnTo', function(event) {
event.preventDefault();
cactiReturnTo($(this).attr('data-url'));
});
```


## Installation

To install the Tags plugin, simply copy the plugin_tags directory to
Expand Down
2 changes: 1 addition & 1 deletion locales/po/cacti.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Cacti \n"
"Report-Msgid-Bugs-To: developers@cacti.net\n"
"POT-Creation-Date: 2026-09-28 16:08-0400\n"
"POT-Creation-Date: 2026-10-07 16:11-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Cacti Developers <developers@cacti.net>>\n"
"Language-Team: Cacti Developers <developers@cacti.net>\n"
Expand Down
20 changes: 10 additions & 10 deletions tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ function form_actions(): void {
</td>
</tr>";

$save_html = "<input type='button' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Delete item', 'Delete items', cacti_sizeof($items_array)) . "'>";
$save_html = "<input class='cactiReturnTo' type='button' value='" . __esc('Cancel') . "'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Delete item', 'Delete items', cacti_sizeof($items_array)) . "'>";
} elseif (get_filter_request_var('drp_action') == 2) { // disable
print "><tr>
<td class='topBoxAlt'>
Expand All @@ -183,7 +183,7 @@ function form_actions(): void {
</td>
</tr>";

$save_html = "<input type='button' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Disable item', 'Disable items', cacti_sizeof($items_array)) . "'>";
$save_html = "<input class='cactiReturnTo' type='button' value='" . __esc('Cancel') . "'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Disable item', 'Disable items', cacti_sizeof($items_array)) . "'>";
} elseif (get_filter_request_var('drp_action') == 3) { // enable
print "><tr>
<td class='topBoxAlt'>
Expand All @@ -192,7 +192,7 @@ function form_actions(): void {
</td>
</tr>";

$save_html = "<input type='button' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Enable item', 'Enable items', cacti_sizeof($items_array)) . "'>";
$save_html = "<input class='cactiReturnTo' type='button' value='" . __esc('Cancel') . "'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Enable item', 'Enable items', cacti_sizeof($items_array)) . "'>";
} elseif (get_filter_request_var('drp_action') == 4) { // archive
print "><tr>
<td class='topBoxAlt'>
Expand All @@ -201,7 +201,7 @@ function form_actions(): void {
</td>
</tr>";

$save_html = "<input type='button' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Archive item', 'Archive items', cacti_sizeof($items_array)) . "'>";
$save_html = "<input class='cactiReturnTo' type='button' value='" . __esc('Cancel') . "'>&nbsp;<input type='submit' value='" . __esc('Continue') . "' title='" . __esc_n('Archive item', 'Archive items', cacti_sizeof($items_array)) . "'>";
}
} else {
raise_message(40);
Expand Down Expand Up @@ -747,7 +747,7 @@ function tags_filter(): void {
<?php print __('Target', 'tags'); ?>
</td>
<td>
<select id='target' onChange='applyFilter()'>
<select id='target'>
<?php
print "<option value='-1'" . (get_request_var('rows') == -1 ? ' selected' : '') . '>' . __('Any', 'tags') . '</option>';

Expand All @@ -767,7 +767,7 @@ function tags_filter(): void {
<?php print __('Type', 'tags'); ?>
</td>
<td>
<select id='type' onChange='applyFilter()'>
<select id='type'>
<?php
print "<option value='-1'" . (get_request_var('rows') == -1 ? ' selected' : '') . '>' . __('Any', 'tags') . '</option>';

Expand All @@ -787,7 +787,7 @@ function tags_filter(): void {
<?php print __('Tags', 'tags'); ?>
</td>
<td>
<select id='rows' onChange='applyFilter()'>
<select id='rows'>
<?php
print "<option value='-1'" . (get_request_var('rows') == -1 ? ' selected' : '') . '>' . __('Default', 'tags') . '</option>';

Expand Down Expand Up @@ -926,15 +926,15 @@ function clearFilter() {
}

$(function() {
$('#rows').click(function() {
$('#rows').change(function() {
applyFilter();
});

$('#target').click(function() {
$('#target').change(function() {
applyFilter();
});

$('#type').click(function() {
$('#type').change(function() {
applyFilter();
});

Expand Down
3 changes: 3 additions & 0 deletions tests/bin/patch-coverage.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ function changed_lines($base_ref) {
* Empty by default; add entries per repository as the need arises.
*/
$unmeasured_allowlist = [
// Web UI entry point: chdir + require of include/auth.php and dispatches on
// request vars, so it is not loaded into the isolated unit process.
'tags.php',
];
$unmeasured = array_values(array_diff(array_keys($changed), array_keys($measured)));
$unexpected_unmeasured = array_values(array_diff($unmeasured, $unmeasured_allowlist));
Expand Down
Loading