Enable bounded browser account administration for Phase 8D.7

Allow other-account add/password and forced delete/role commands through
shared dispatcher and handler policy. Keep self-target,
generated-secret,
key, bootstrap, and recovery workflows blocked.

Revalidate currentness after password prompts and before database API
admission. Document that admitted mutations may finish after disconnect,
while subsequent stale operations must reject.

Add policy, transaction-failure, cleanup, and targeted-revocation
regressions. Record completed review, passing host tests and firmware
build, with target validation and M2 acceptance still pending.
This commit is contained in:
2026-09-07 10:03:45 +02:00
parent 326119812f
commit fe1e2d98b4
11 changed files with 533 additions and 13 deletions
+36
View File
@@ -132,6 +132,36 @@ static void test_currentness(void)
admin_ssh_console_close(&a);
puts("PASS: owner stale/account current isolation, callback close/reuse, revoked submitted prompts, periodic unanswered invalidation/stale wake, UART recovery, consumed output wiping");
}
static void test_dispatch_currentness(void)
{
++a.slot_generation; live[0]=true; principal_current=true;
assert(admin_ssh_console_open_owned(&a,&admin,&owner)==ESP_OK);
setup_dispatch();
assert(admin_ssh_console_dispatch_is_current());
current_task=(void *)2;
assert(!admin_ssh_console_dispatch_is_current());
current_task=s_task;
live[0]=false;
assert(!admin_ssh_console_dispatch_is_current());
assert(!s_sessions[0].active);
secure_wipe(&s_sessions[0],sizeof(s_sessions[0]));
++a.slot_generation; live[0]=true;
assert(admin_ssh_console_open_owned(&a,&admin,&owner)==ESP_OK);
setup_dispatch(); principal_current=false;
assert(!admin_ssh_console_dispatch_is_current());
assert(!s_sessions[0].active);
secure_wipe(&s_sessions[0],sizeof(s_sessions[0])); principal_current=true;
++a.slot_generation;
assert(admin_ssh_console_open_owned(&a,&admin,&owner)==ESP_OK);
setup_dispatch(); s_dispatch_token.slot_generation--;
assert(!admin_ssh_console_dispatch_is_current());
assert(s_sessions[0].active); /* Stale dispatch cannot close replacement. */
secure_wipe(&s_sessions[0],sizeof(s_sessions[0]));
s_dispatch_remote=false;
assert(admin_ssh_console_dispatch_is_current()); /* Trusted UART0. */
puts("PASS: handler currentness API rejects wrong task, stale owner/account/token; preserves replacement and UART0");
}
static void test_shared_admission(void)
{
admin_ssh_console_token_t web={.slot_index=255, .session_id=7,
@@ -220,7 +250,12 @@ int main(void)
++a.slot_generation; assert(admin_ssh_console_open_owned(&a,&admin,&owner)==ESP_OK);
setup_dispatch(); clear_output(&a); prompt_hook=hidden_reply;
uint8_t secret[32]; size_t n;
uint8_t history_before[sizeof(s_sessions[0].history)];
memcpy(history_before,s_sessions[0].history,sizeof(history_before));
assert(admin_ssh_console_dispatch_is_current());
assert(admin_ssh_console_dispatch_read_input("Password: ",secret,sizeof(secret),true,&n)==ESP_OK);
assert(!memcmp(history_before,s_sessions[0].history,sizeof(history_before)));
for (size_t i=0;i<sizeof(s_sessions[0].prompt_input);++i) assert(!s_sessions[0].prompt_input[i]);
assert(n==6 && !memcmp(secret,"secret",6));
assert(s_sessions[0].output_length==strlen("Password: \r\n"));
clear_output(&a);
@@ -277,5 +312,6 @@ int main(void)
assert(ssh_output_write(&a,"x",1)==-1 && errno==EPIPE);
assert(!lock_depth);
test_currentness();
test_dispatch_currentness();
puts("PASS: admission/identity, two owners, completion contention/reopen, history, queued stale/revoked work, UART dispatch, hidden/disconnected prompts, exit-to-SELF_CLOSE, deferred rejection/drain/close, 5s output backpressure");
}