From 9d146d0059526b60e39cfaf5b1502c540a567d77 Mon Sep 17 00:00:00 2001 From: Simon Halvorsen Date: Fri, 21 Aug 2026 16:56:42 +0200 Subject: [PATCH 1/2] Added support for a path-augment to specify files to ship beyond just extension-matching Ticket: CFE-4708 Changelog: Added `input_paths_extra` to allow specified files to ship independent of what is specified in `input_name_patterns[_extra]` Signed-off-by: Simon Halvorsen --- cfe_internal/update/update_policy.cf | 34 ++++++++++++++++++++++++++++ controls/update_def.cf.in | 9 ++++++++ 2 files changed, 43 insertions(+) diff --git a/cfe_internal/update/update_policy.cf b/cfe_internal/update/update_policy.cf index b26a135628..0f9e6e95a5 100644 --- a/cfe_internal/update/update_policy.cf +++ b/cfe_internal/update/update_policy.cf @@ -220,6 +220,14 @@ bundle agent cfe_internal_update_policy_cpv comment => "Symlink to Python we found (if any)", handle => "cfe_internal_update_policy_python_symlink"; + "input_paths_extra_safe" -> { "CFE-4708" } + slist => filter( + "^.*\.\..*$", "update_def.input_paths_extra", "true", "true", 999 + ), + comment => "input_paths_extra with any entries containing a '..' + path-traversal segment dropped, since it's copied both + from the policy server and to every client verbatim."; + classes: "validated_updates_ready" expression => "cfengine_internal_disable_cf_promises_validated", @@ -237,6 +245,14 @@ bundle agent cfe_internal_update_policy_cpv expression => "local_files_ok", classes => u_if_else("files_ok", "files_ok"); + "have_input_paths_extra" + expression => some(".*", "input_paths_extra_safe"), + comment => concat( + "True if `input_paths_extra` (CFE-4708) has any entries", + " so the always-runs copy below can be skipped entirely", + " (no tree walk) when the feature isn't in use." + ); + files: !am_policy_hub:: # policy hub should not alter inputs/ uneccessary @@ -273,6 +289,24 @@ bundle agent cfe_internal_update_policy_cpv depth_search => u_recurse("inf"), action => u_immediate; + have_input_paths_extra:: + # Copies each input_paths_extra entry individually and runs every time, + # unlike the promise above. + # The release ID gating `validated_updates_ready` only + # reopens on new hub-side changes, so a brand-new entry + # introduced in the same run that opens the gate isn't loaded + # into memory yet (it lands in def.json partway through that + # run?) and will be stuck until a second, unrelated + # run reopens the gate again. + "$(inputs_dir)/$(input_paths_extra_safe)" + comment => "Copy an explicitly listed extra file (input_paths_extra) from master source on every run, since it isn't covered by the release ID hash that gates the promise above.", + handle => "cfe_internal_update_policy_files_input_paths_extra", + copy_from => u_remote_dcp_missing_ok( + "$(master_location)/$(input_paths_extra_safe)", + @(update_def.policy_servers) + ), + action => u_immediate; + update_inputs_not_kept:: "$(inputs_dir)/cf_promises_validated" -> { "CFE-2587" } delete => u_tidy, diff --git a/controls/update_def.cf.in b/controls/update_def.cf.in index f3d9900f60..307e26871a 100644 --- a/controls/update_def.cf.in +++ b/controls/update_def.cf.in @@ -93,6 +93,15 @@ bundle common update_def comment => "Additional filename patterns to copy during policy update.", if => not( isvariable( "input_name_patterns_extra" ) ); + "input_paths_extra" -> { "CFE-4708" } + slist => {}, + comment => concat("Additional literal file paths (relative to the", + " policy set root) to copy during policy update,", + " regardless of input_name_patterns[_extra]. Lets a", + " specific file be included without widening the", + " extension-based patterns for the whole policy set."), + if => not( isvariable( "input_paths_extra" ) ); + # the permissions for your masterfiles files (not dirs), which will propagate to inputs "masterfiles_perms_mode_not_dir" -> { "CFE-951" } string => "0600", From 82305378180dbfbfdfd25b1720c78ec3b272bcc2 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Fri, 28 Aug 2026 14:11:39 -0500 Subject: [PATCH 2/2] Gate input_paths_extra behind validated_updates_ready Move the input_paths_extra copy promise under the existing am_policy_hub|validated_updates_ready class guard instead of running unconditionally. The validated_updates_ready gate opens whenever cf_promises_validated is updated, which happens any time the hub detects file changes in inputs (via IsNewerFileTree), regardless of whether those files are in the release hash. This preserves the atomic update model without requiring the bypass. Also use concat() for multi-line comment attributes per review. --- cfe_internal/update/update_policy.cf | 30 +++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/cfe_internal/update/update_policy.cf b/cfe_internal/update/update_policy.cf index 0f9e6e95a5..82e582daed 100644 --- a/cfe_internal/update/update_policy.cf +++ b/cfe_internal/update/update_policy.cf @@ -224,9 +224,11 @@ bundle agent cfe_internal_update_policy_cpv slist => filter( "^.*\.\..*$", "update_def.input_paths_extra", "true", "true", 999 ), - comment => "input_paths_extra with any entries containing a '..' - path-traversal segment dropped, since it's copied both - from the policy server and to every client verbatim."; + comment => concat( + "input_paths_extra with any entries containing a '..'", + " path-traversal segment dropped, since it's copied both", + " from the policy server and to every client verbatim." + ); classes: "validated_updates_ready" @@ -248,9 +250,9 @@ bundle agent cfe_internal_update_policy_cpv "have_input_paths_extra" expression => some(".*", "input_paths_extra_safe"), comment => concat( - "True if `input_paths_extra` (CFE-4708) has any entries", - " so the always-runs copy below can be skipped entirely", - " (no tree walk) when the feature isn't in use." + "True if input_paths_extra (CFE-4708) has any entries", + " so the per-path copy can be skipped when the feature", + " is not in use." ); files: @@ -289,18 +291,14 @@ bundle agent cfe_internal_update_policy_cpv depth_search => u_recurse("inf"), action => u_immediate; - have_input_paths_extra:: - # Copies each input_paths_extra entry individually and runs every time, - # unlike the promise above. - # The release ID gating `validated_updates_ready` only - # reopens on new hub-side changes, so a brand-new entry - # introduced in the same run that opens the gate isn't loaded - # into memory yet (it lands in def.json partway through that - # run?) and will be stuck until a second, unrelated - # run reopens the gate again. "$(inputs_dir)/$(input_paths_extra_safe)" - comment => "Copy an explicitly listed extra file (input_paths_extra) from master source on every run, since it isn't covered by the release ID hash that gates the promise above.", + comment => concat( + "Copy an explicitly listed extra file (input_paths_extra)", + " from the master source when a new policy validation is", + " acquired, for files not covered by input_name_patterns." + ), handle => "cfe_internal_update_policy_files_input_paths_extra", + if => "have_input_paths_extra", copy_from => u_remote_dcp_missing_ok( "$(master_location)/$(input_paths_extra_safe)", @(update_def.policy_servers)