mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-10-26 05:30:18 +00:00
3ecb954f28
* fix(gitops): guard automatic acceptance against stateful withdrawal Automatic GitOps acceptance had no stateful check: a candidate that removed, renamed, or stripped the volumes of a stateful service could be accepted and applied unattended, destroying or orphaning that service's data. The poll acceptance path and the automatic webhook apply path now share one guard that diffs the candidate's stateful service set against the generation in force, reading managed evidence only. When the candidate withdraws a stateful service, or when that cannot be proven, the candidate is held for review: nothing is accepted or applied, a durable block reason is recorded, and the projection names the block as its own attention reason, so the workplace shows it instead of an unexplained review. Webhook auto-apply reports the hold in its delivery result. Repeat pulls of the same held commit reuse the staged candidate, so the hold survives polling instead of churning generations, and a stored redelivery keeps its automated authority even if the policy moved since. First acceptance under a source with no accepted generation still defines the baseline, since there is no managed state to compare against. Validation: backend tsc, 9479 backend tests, frontend tsc, lint, and tests, with new coverage for the poll hold, rename, non-withdrawing acceptance, no re-evaluation, unreadable evidence, the webhook hold, a stored redelivery after a policy change, and repeat-pull reuse. The full backend run has one pre-existing environment failure in the SSH integration suite because port 22 is already in use on this machine. * security(gitops): contain generation reads under the managed root CodeQL flagged three path-injection sinks in the new stateful guard: the staged compose reads, the staged .env read, and the generation-in-force compose reads each joined a row- or user-derived relative path into a read without a containment check the analyzer can see. Each read now resolves its target and checks it inline at the sink, the pattern the repository already uses where CodeQL credits the guard, and every generation directory is contained under the managed root before anything inside it is read. Escapes are treated as unreadable evidence, so the guard holds rather than reads outside the managed area. No behavior change for legitimate paths. * test(gitops): cover stateful guard edge cases and review block lifecycle Pin the guard's hold on stripped volumes and unparseable candidates, the first-acceptance baseline, and every exit from the durable review block, including that automatic acceptance resumes once a newer candidate clears it.