From 321d0a3799c0e0cfa469941964191b09ee53d67e Mon Sep 17 00:00:00 2001 From: Cam Proudlock Date: Tue, 11 Aug 2026 10:08:59 -0400 Subject: [PATCH] Backups: collection interval is actually readable --- CHANGELOG.md | 5 +++++ plugins/backups/plugin.py | 8 ++++++++ tests/test_plugins/test_backups.py | 15 +++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 304a2b8a..19ea416a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,11 @@ ADR-007 and ADR-002. PC's revisions can no longer push out a quiet PC's only backup, and the revision diff compares against the same PC's previous revision rather than another PC's. +- The backup collection interval could not actually be changed. The collecting + script reads `backups_intervalhours` from the public settings endpoint before + it holds any credential, but the plugin did not declare the key as public, so + the endpoint never returned it and the script fell back to its built-in 24 + hours - silently. The setting looked configurable in the UI and was not. - A backup export containing a value with an empty right-hand side (`Name=`) failed to parse, and with it the whole file, so that machine could never be backed up. The form is not strictly legal but occurs in real exports. It is diff --git a/plugins/backups/plugin.py b/plugins/backups/plugin.py index 1fc8de1d..69ea9e75 100644 --- a/plugins/backups/plugin.py +++ b/plugins/backups/plugin.py @@ -144,6 +144,14 @@ class BackupsPlugin(BasePlugin): 'description': 'Minimum hours between collection attempts on a ' 'PC. GE-Enforce runs every cycle; the collector ' 'skips until this much time has passed.', + # MUST be public. The collecting script reads this from + # /api/settings/public BEFORE it has any credential, and its + # Get-IntervalHours falls back to 24 on any failure - silently. + # Left off the allowlist, the setting looked configurable and + # was not: every PC used 24 no matter what the UI said. A + # collection cadence is not a secret; the share root beneath it + # is internal topology and stays private. + 'public': True, }, { 'key': 'backups_shareroot', diff --git a/tests/test_plugins/test_backups.py b/tests/test_plugins/test_backups.py index 69876a93..3b3945e7 100644 --- a/tests/test_plugins/test_backups.py +++ b/tests/test_plugins/test_backups.py @@ -847,3 +847,18 @@ def test_retention_prunes_each_pc_separately(bk_app, bk_plugin): rows = _db.session.query(BackupRevision).all() sources = [row.sourcehostname for row in rows] assert 'QUIET' in sources, 'the quiet PC lost its only backup' + + +def test_the_collection_interval_is_readable_without_a_login(bk_app): + """The collecting script reads backups_intervalhours from + /api/settings/public before it holds any credential, and its + Get-IntervalHours falls back to 24 on ANY failure - silently. Off the + allowlist, the setting looks configurable in the UI and is not: every PC + keeps using 24. The share root must NOT be public; it is internal topology. + """ + from plugins.backups.plugin import BackupsPlugin + + declared = {entry['key']: entry for entry in + BackupsPlugin().get_settings_defaults()} + assert declared['backups_intervalhours'].get('public') is True + assert not declared['backups_shareroot'].get('public')