Scopes session cleanup and log retention to a single session, disables global GC paths, and retires legacy global schedules.
Refs #707
Task: TESS-M1-SEC-006
Scopes session cleanup and log retention to a single session, disables global GC paths, and retires legacy global schedules.
REQUEST CHANGES reviewer-of-record [W-jarvis:reviewer] head a4b6665bd7
CI is green on exact head (ci/woodpecker/pr/ci pipeline 1713), and the direction is right: global /gc, cold-start full GC, and scheduled global GC are disabled; log demotion now uses promoteSessionToWarm(sessionId, cutoff); SEC-001 command approval tests are present and pass in CI.
Blockers before I can attest SEC-006:
SessionGCService.collect() still builds a Redis SCAN MATCH glob from the raw session id: mosaic:session:${sessionId}:*. WebSocket ingress accepts any string conversationId, and that value can become the session id. A session id containing Redis glob metacharacters (for example *) broadens the scan/delete outside the single session namespace, violating the per-session retention boundary. Please escape/canonicalize session ids before using them in Redis glob patterns, or avoid glob matching for session cleanup, and add a regression test with glob metacharacters proving only the requested session's keys are deleted.
The requested durable-approval regression is not proven yet. The current test asserts the scan pattern is mosaic:session:abc:* and that del was not called with a command-approval string, but it does not create a valid SEC-001 approval, run a GC pass, and prove the approval still authorizes afterward. Please add that regression so a valid tess:command-approval:<id> survives session GC.
Once those are covered, I can re-review the new head.
REQUEST CHANGES reviewer-of-record [W-jarvis:reviewer] head a4b6665bd76a23b75fbebee5535a7cc1a420dd9f
CI is green on exact head (`ci/woodpecker/pr/ci` pipeline 1713), and the direction is right: global `/gc`, cold-start full GC, and scheduled global GC are disabled; log demotion now uses `promoteSessionToWarm(sessionId, cutoff)`; SEC-001 command approval tests are present and pass in CI.
Blockers before I can attest SEC-006:
1. `SessionGCService.collect()` still builds a Redis `SCAN MATCH` glob from the raw session id: `mosaic:session:${sessionId}:*`. WebSocket ingress accepts any string `conversationId`, and that value can become the session id. A session id containing Redis glob metacharacters (for example `*`) broadens the scan/delete outside the single session namespace, violating the per-session retention boundary. Please escape/canonicalize session ids before using them in Redis glob patterns, or avoid glob matching for session cleanup, and add a regression test with glob metacharacters proving only the requested session's keys are deleted.
2. The requested durable-approval regression is not proven yet. The current test asserts the scan pattern is `mosaic:session:abc:*` and that `del` was not called with a `command-approval` string, but it does not create a valid SEC-001 approval, run a GC pass, and prove the approval still authorizes afterward. Please add that regression so a valid `tess:command-approval:<id>` survives session GC.
Once those are covered, I can re-review the new head.
VERIFIED APPROVE reviewer-of-record [W-jarvis:reviewer] head 37be090e7b
Evidence:
Exact PR/branch head verified: 37be090e7bcbe2666535f1b3f2a53658535d599e.
PR CI green: ci/woodpecker/pr/ci pipeline 1715 success.
Re-reviewed remediation for prior blockers: SessionGCService.collect() now escapes Redis glob metacharacters before building the SCAN MATCH pattern, with a regression for abc*?[tenant]\\escape; this would fail if the raw session id were used.
Durable approval survival is covered by creating a real CommandAuthorizationService approval, running a session GC pass, confirming the tess:command-approval:<id> entry remains, then authorizing with that approval successfully.
SEC-001 abuse coverage remains: valid durable approval is still required/consumed, but /gc no longer executes an unaudited global sweep; it returns disabled and does not call sweepOrphans.
SEC-006 retention boundary remains scoped: session GC deletes only the requested session namespace, demotes logs through promoteSessionToWarm(sessionId, cutoff), exposes no fullCollect/sweepOrphans, and startup removes legacy repeatable global GC schedules.
git diff --check clean; no secrets in diff.
VERIFIED APPROVE reviewer-of-record [W-jarvis:reviewer] head 37be090e7bcbe2666535f1b3f2a53658535d599e
Evidence:
- Exact PR/branch head verified: `37be090e7bcbe2666535f1b3f2a53658535d599e`.
- PR CI green: `ci/woodpecker/pr/ci` pipeline 1715 success.
- Re-reviewed remediation for prior blockers: `SessionGCService.collect()` now escapes Redis glob metacharacters before building the `SCAN MATCH` pattern, with a regression for `abc*?[tenant]\\escape`; this would fail if the raw session id were used.
- Durable approval survival is covered by creating a real `CommandAuthorizationService` approval, running a session GC pass, confirming the `tess:command-approval:<id>` entry remains, then authorizing with that approval successfully.
- SEC-001 abuse coverage remains: valid durable approval is still required/consumed, but `/gc` no longer executes an unaudited global sweep; it returns disabled and does not call `sweepOrphans`.
- SEC-006 retention boundary remains scoped: session GC deletes only the requested session namespace, demotes logs through `promoteSessionToWarm(sessionId, cutoff)`, exposes no `fullCollect`/`sweepOrphans`, and startup removes legacy repeatable global GC schedules.
- `git diff --check` clean; no secrets in diff.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #707
Task: TESS-M1-SEC-006
Scopes session cleanup and log retention to a single session, disables global GC paths, and retires legacy global schedules.
5ad29926d9tof17e70c145REQUEST CHANGES reviewer-of-record [W-jarvis:reviewer] head
a4b6665bd7CI is green on exact head (
ci/woodpecker/pr/cipipeline 1713), and the direction is right: global/gc, cold-start full GC, and scheduled global GC are disabled; log demotion now usespromoteSessionToWarm(sessionId, cutoff); SEC-001 command approval tests are present and pass in CI.Blockers before I can attest SEC-006:
SessionGCService.collect()still builds a RedisSCAN MATCHglob from the raw session id:mosaic:session:${sessionId}:*. WebSocket ingress accepts any stringconversationId, and that value can become the session id. A session id containing Redis glob metacharacters (for example*) broadens the scan/delete outside the single session namespace, violating the per-session retention boundary. Please escape/canonicalize session ids before using them in Redis glob patterns, or avoid glob matching for session cleanup, and add a regression test with glob metacharacters proving only the requested session's keys are deleted.The requested durable-approval regression is not proven yet. The current test asserts the scan pattern is
mosaic:session:abc:*and thatdelwas not called with acommand-approvalstring, but it does not create a valid SEC-001 approval, run a GC pass, and prove the approval still authorizes afterward. Please add that regression so a validtess:command-approval:<id>survives session GC.Once those are covered, I can re-review the new head.
VERIFIED APPROVE reviewer-of-record [W-jarvis:reviewer] head
37be090e7bEvidence:
37be090e7bcbe2666535f1b3f2a53658535d599e.ci/woodpecker/pr/cipipeline 1715 success.SessionGCService.collect()now escapes Redis glob metacharacters before building theSCAN MATCHpattern, with a regression forabc*?[tenant]\\escape; this would fail if the raw session id were used.CommandAuthorizationServiceapproval, running a session GC pass, confirming thetess:command-approval:<id>entry remains, then authorizing with that approval successfully./gcno longer executes an unaudited global sweep; it returns disabled and does not callsweepOrphans.promoteSessionToWarm(sessionId, cutoff), exposes nofullCollect/sweepOrphans, and startup removes legacy repeatable global GC schedules.git diff --checkclean; no secrets in diff.