Skip to content

Commit c9bbd88

Browse files
authored
Merge pull request #1449 from DeusData/fix/1416-activation-refusal-attribution
fix(cli): attribute activation refusals to the recorded safety check (#1416)
2 parents 24bee57 + 2044c5c commit c9bbd88

4 files changed

Lines changed: 72 additions & 0 deletions

File tree

‎src/cli/activation_transaction.c‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,18 @@ const char *cbm_activation_transaction_refusal_note(void) {
110110
return g_activation_refusal_note;
111111
}
112112

113+
#ifdef CBM_ENABLE_TEST_SEAMS
114+
/* #1416 test seam: install a refusal note so the CLI attribution path is
115+
* testable without constructing a real Windows ACL refusal. */
116+
void cbm_activation_transaction_note_refusal_for_testing(const char *predicate,
117+
unsigned long os_error) {
118+
activation_refusal_clear();
119+
if (predicate && predicate[0]) {
120+
activation_note_refusal(predicate, os_error);
121+
}
122+
}
123+
#endif
124+
113125
#ifdef _WIN32
114126
typedef HANDLE activation_native_file_t;
115127
#define ACTIVATION_INVALID_FILE INVALID_HANDLE_VALUE

‎src/cli/activation_transaction.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,9 @@ const char *cbm_activation_transaction_status_message(cbm_activation_transaction
9797
* entry; single-threaded like the rest of the transaction API. */
9898
const char *cbm_activation_transaction_refusal_note(void);
9999

100+
#ifdef CBM_ENABLE_TEST_SEAMS
101+
void cbm_activation_transaction_note_refusal_for_testing(const char *predicate,
102+
unsigned long os_error);
103+
#endif
104+
100105
#endif /* CBM_ACTIVATION_TRANSACTION_H */

‎src/cli/cli.c‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,24 @@ static const char *g_cli_activation_runtime_parent_for_test = NULL;
197197

198198
static void cli_activation_diagnostic(const cbm_cli_activation_ops_t *ops, const char *message) {
199199
const char *diagnostic = message ? message : CLI_ACTIVATION_REFUSED_MESSAGE;
200+
/* #1416: when the transaction recorded a concrete refusal (an ACL or
201+
* filesystem safety check), say THAT. The generic text blames "active CBM
202+
* sessions" for what is a validation refusal - reporters rebooted, killed
203+
* every process, and hunted phantom handles because the message pointed at
204+
* sessions that did not exist. The sessions wording remains for genuine
205+
* stop/reservation failures, which record no refusal note. */
206+
char attributed[CBM_SZ_1K];
207+
const char *note = cbm_activation_transaction_refusal_note();
208+
if (diagnostic == CLI_ACTIVATION_REFUSED_MESSAGE && note && note[0]) {
209+
(void)snprintf(attributed, sizeof(attributed),
210+
"error: activation was refused by a filesystem safety check before any "
211+
"change was made: %s\n"
212+
"error: this is not a session problem. If the flagged directory is one you "
213+
"trust, remove the flagged permission grant (icacls <dir> /remove:g <sid>) "
214+
"or use an owner-private directory for --dir/CBM_CACHE_DIR, then retry.",
215+
note);
216+
diagnostic = attributed;
217+
}
200218
if (ops && ops->visible_diagnostic) {
201219
ops->visible_diagnostic(ops->context, diagnostic);
202220
return;

‎tests/test_cli.c‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "test_framework.h"
1515
#include "test_helpers.h"
1616
#include <cli/agent_profiles.h>
17+
#include <cli/activation_transaction.h>
1718
#include <cli/cli.h>
1819
#include <cli/progress_sink.h>
1920
#include <daemon/bootstrap.h>
@@ -646,6 +647,41 @@ TEST(cli_activation_refuses_when_cohort_does_not_drain) {
646647
PASS();
647648
}
648649

650+
/* Regression for #1416: when the activation transaction recorded a concrete
651+
* refusal (e.g. the Windows ACL safety check), the CLI must attribute the
652+
* failure to that check instead of blaming "active CBM sessions" - reporters
653+
* rebooted and hunted phantom handles because no sessions existed. The
654+
* sessions wording must remain for refusals with no recorded note. */
655+
TEST(cli_activation_refusal_note_reaches_diagnostic_issue1416) {
656+
cbm_activation_transaction_note_refusal_for_testing(
657+
"acl-grants-cross-account-mutation to S-1-5-11", 0UL);
658+
cli_activation_fake_t fake = {
659+
.participants_active = true,
660+
.mutation_reserve_result = 0,
661+
};
662+
cbm_cli_activation_ops_t ops = {
663+
.context = &fake,
664+
.reserve_for_mutation = cli_activation_fake_reserve_mutation,
665+
.mutation_lease_release = cli_activation_fake_release_mutation,
666+
.visible_diagnostic = cli_activation_fake_diagnostic,
667+
};
668+
ASSERT_EQ(cbm_cli_activation_guard_with_ops(&ops, cli_activation_fake_mutation, &fake), 1);
669+
ASSERT_NOT_NULL(strstr(fake.diagnostic, "acl-grants-cross-account-mutation"));
670+
ASSERT_NOT_NULL(strstr(fake.diagnostic, "not a session problem"));
671+
ASSERT_NULL(strstr(fake.diagnostic, "could not be stopped safely"));
672+
673+
/* No note recorded -> the sessions wording is still the right message. */
674+
cbm_activation_transaction_note_refusal_for_testing(NULL, 0UL);
675+
cli_activation_fake_t plain = {
676+
.participants_active = true,
677+
.mutation_reserve_result = 0,
678+
};
679+
ops.context = &plain;
680+
ASSERT_EQ(cbm_cli_activation_guard_with_ops(&ops, cli_activation_fake_mutation, &plain), 1);
681+
ASSERT_NOT_NULL(strstr(plain.diagnostic, "could not be stopped safely"));
682+
PASS();
683+
}
684+
649685
TEST(cli_activation_refuses_unsafe_cohort_reservation) {
650686
cli_activation_fake_t fake = {
651687
.mutation_reserve_result = -1,
@@ -11891,6 +11927,7 @@ SUITE(cli) {
1189111927
/* Mandatory daemon activation safety */
1189211928
RUN_TEST(cli_activation_quiesces_active_cohort_before_mutation);
1189311929
RUN_TEST(cli_activation_refuses_when_cohort_does_not_drain);
11930+
RUN_TEST(cli_activation_refusal_note_reaches_diagnostic_issue1416);
1189411931
RUN_TEST(cli_activation_refuses_unsafe_cohort_reservation);
1189511932
RUN_TEST(cli_activation_releases_maintenance_lease_after_success);
1189611933
RUN_TEST(cli_activation_releases_maintenance_lease_when_mutation_fails);

0 commit comments

Comments
 (0)