fix(processes): address review findings

- Fill TimeZoneId only for a configured session/host zone with a resolvable IANA id
  (IExtensionHost.TimeZoneId); otherwise children inherit the host's TZ instead of TZ=Local/UTC.
- Host hardening logs a warning instead of throwing when libc/prctl can't be resolved.
- A hook or process_spawn handler returning a null or incomplete context fails the spawn with
  InvalidOperationException, so bash reports "process environment hook failed".
- Move tool-result redaction into the Agent slice with a behavioural no-change check.
- ProcessSpawnContext.ApplyTo(psi) sets cwd and replaces the environment; used by bash and ExecAsync.
- Split the Telegram process environment types into their own files; move the Extensions.Tests
  NoHostHardening under Support/; check the Claude Code assembly never references the spawn policy.
- Spec and docs: TZ inheritance, incomplete contexts, ApplyTo, async spawn handlers, root skip.
This commit is contained in:
EugeneTes
2026-09-26 19:32:40 +02:00
parent 5f33d139b3
commit 76bda6cf55
25 changed files with 289 additions and 95 deletions

View File

@@ -151,25 +151,26 @@ var spawn = await ctx.PrepareProcessAsync(new ProcessSpawnRequest
Origin = "my-extension",
}, ct);
var psi = new ProcessStartInfo(spawn.Command) { WorkingDirectory = spawn.Cwd, UseShellExecute = false };
var psi = new ProcessStartInfo(spawn.Command) { UseShellExecute = false };
foreach (var a in spawn.Arguments) psi.ArgumentList.Add(a);
psi.Environment.Clear();
foreach (var (k, v) in spawn.Environment) psi.Environment[k] = v;
spawn.ApplyTo(psi); // WorkingDirectory = spawn.Cwd, Environment cleared, then spawn.Environment copied in
```
- In a session, call `ctx.PrepareProcessAsync`. It fills `Scope`, `SessionId` and (unless you set it)
`TimeZoneId` from the session, runs the policy, then emits `ProcessSpawnEvent` to extensions in load order.
`TimeZoneId` is filled only when the session or host configured a zone; otherwise the child inherits the host's `TZ`.
Without a session (during activation, or from a process-wide service), call `api.Processes.Prepare(request)`:
only the host hooks run.
- The returned `ProcessSpawnContext.Environment` is the **complete** environment. Clear
`ProcessStartInfo.Environment` and copy it in; don't merge it with the inherited one.
- The returned `ProcessSpawnContext.Environment` is the **complete** environment. `spawn.ApplyTo(psi)` clears
`ProcessStartInfo.Environment` and copies it in (and sets the working directory); don't merge it with the inherited one.
- The policy starts from the host's environment, removes what the deny-list (`*_API_KEY`, `*_TOKEN`, `*_SECRET`,
`*_PASSWORD`, their `Section__Key` forms, and a few names), `Allow` and `SecretValues` reject, then applies
`request.Environment`, then sets `TZ` from `TimeZoneId`, then runs the host hooks.
- **Variables you pass explicitly are applied after the deny-list and never filtered.** A process that needs a
token gets it only when you pass it, and no other process sees it. A null value removes the variable.
- Host hooks and `ProcessSpawnEvent` handlers are synchronous rewrites of command, arguments, cwd and environment;
do any I/O in the spawner. A hook or handler that throws fails the spawn (the exception comes out of
- Host hooks (`ProcessSpawnHook`) are synchronous, pure rewrites of command, arguments, cwd and environment.
`ProcessSpawnEvent` handlers are ordinary (async) extension handlers that may replace `Context`; keep them free of
slow I/O, because they delay the spawn. A hook or handler that throws fails the spawn (the exception comes out of
`PrepareProcessAsync`), and nothing falls back to the inherited environment. Report it the way you report a
failed start; the built-in `bash` returns `process environment hook failed: <message>`.
- `ExecAsync` already does all of this with `Purpose = ExtensionExec` and `Origin` = your extension's name.