Skip to content

test(saas): memory round-trip isolation across profiles - #7271

Merged
senamakel merged 13 commits into
tinyhumansai:mainfrom
senamakel:saas-isolation-memory
Oct 10, 2026
Merged

senamakel merged 13 commits into
tinyhumansai:mainfrom
senamakel:saas-isolation-memory

Conversation

@senamakel

@senamakel senamakel commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

What

A memory round-trip isolation e2e for SaaS profiles. It runs through the real openhuman-core run --mode saas binary against a backend that actually serves /memory/*.

  • tests/saas_mode/mock_memory.rs is a standalone, stateful Rust mock of the hosted memory routes (experience, events, recall, answer, forget, scopes). It is modelled on scripts/mock-api/routes/memory.mjs. It records every request's bearer and the scopes it names. Anything outside /memory/* gets an immediate 500. It does not depend on test(saas): adversarial cross-profile isolation e2e (fuzzed user surface, eviction storm, policy probes) #7251.
  • tests/saas_mode/memory.rs adds memory::profiles_round_trip_their_own_memory_and_never_anothers, wired into tests/saas_mode_e2e.rs.

Why

The existing SaaS e2e tests only see /memory/* answer 500, so nothing showed that a profile's memory actually round-trips or stays inside its user:<id> root. Profiles run the hosted tinyhumans engine on the legacy layout, confined to user:<id> (profiles::layout, memory::user_scope, memory::confine).

The mock deliberately keeps one store for every bearer. The real backend keeps each credential apart, and that would hide a core that reads or writes outside the root. Here the core's own confinement is the only thing keeping profiles apart. That is the shared-engine case memory::user_scope defends.

What the test asserts

The test uses three profiles: ann, anna and bob. ann is a string prefix of anna, and scope listing is a prefix match.

  • Each profile stores a canary through memory_learn, and the canary lands only under app:tinymemory/user:<id>.

  • Each profile reads its own canary back via items_list, fetch and recall. No items_list, fetch, recall or explore result (facets namespace, kind and agent) ever contains another profile's canary or names another root.

  • An attacker profile tries to escape its root:

    • It aims filter.reach at the victim's root, at a node below it, with and without ancestors, and at the global root. It sends this to items_list, fetch, recall and explore.
    • It steps the explorer path into a foreign namespace.
    • It forgets the victim's item ids, with and without a reach.
    • It learns into the victim's namespace.

    None of this returns, plants into or forgets the victim's memory. The victim still holds its canary.

  • A profile forgets its own item, and the others keep theirs.

  • On the wire, every memory request carries the credential of a provisioned profile, and every scope it names is inside that profile's root. The one exception is below.

Mutation check: with user_scope::confinement and confine::allowed_reach disabled, the test fails at once (items_list as ann leaked anna's memory). The hook checkpointed that mutation and its revert as f21dff4365 / fea7255ea8 under misleading fix(memory) subjects. Their net diff is zero, and this PR changes no crate source.

Finding: forget-by-id sweeps the whole tree (read-only, no leak observed)

memory_forget by id first narrows the ids through get within the caller's reach (memory::ops::forget), which is correct. It then calls engine.forget(ForgetTarget::Ids). TinyMemory's contract treats that as unscoped ("ids are not scoped by namespace"), so the cortex engine's every_scope() lists from app:tinymemory and reads every other profile's scopes with the caller's credential.

Why this is not a leak:

  • Nothing read during the sweep reaches the caller.
  • Nothing outside the root is forgotten, because an item id is a fingerprint of the item including its namespace, so no other tree holds the same id.
  • With per-user backend credentials, the normal deployment, the backend already scopes those reads to the caller.

It is still a confinement gap on a shared engine: cross-tree reads, and cost that grows with the number of users. The fix belongs in tinymemory, for example a reach on ForgetTarget::Ids. The test allows only that sweep's GET listings outside the root, and only inside the forget call's request window. Its forget, and every other write, must stay inside the root.

Tests

  • cargo test -p openhuman-cli --test saas_mode_e2e: 15 passed, including the new test.
  • cargo check --all-targets: OK.
  • cargo fmt --all --check, pnpm rust:layout, node scripts/ci/check-feature-forwarding.mjs and pnpm saas:ambient: OK. The vendor submodules are clean.
  • cargo clippy -p openhuman-cli --test saas_mode_e2e -- -D warnings is clean for the new code. Two clippy errors already in the core lib stop the full clippy command: needless_return in agent/orchestration/background_delivery.rs:203 and type_complexity in channels/providers/relay/store.rs:42. Neither file is touched here.
  • I did not run cargo check --manifest-path crates/openhuman-app/Cargo.toml, because this PR changes only tests/.

Limits: the mock follows the hosted wire as memory.mjs models it, not the real backend. Recall ranking and answers are deterministic stand-ins.

Summary by CodeRabbit

  • Tests
    • Expanded automated coverage for SaaS memory isolation, verifying that profiles can access their own memories without exposing other profiles’ data.
    • Added checks for cross-profile reads, writes, and forgetting attempts, including whether other profiles’ memories remain intact.
    • Broadened coverage of memory access boundaries, credential use, and memory operations against a simulated backend.
    • Updated the gated-test allowlist to include profile surface tests.

senamakel and others added 9 commits October 10, 2026 06:45
Introduce a mock memory backend under the saas_mode test suite so tests can
exercise memory-dependent behaviour without a real store.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a mock implementation of the saas backend so tests can exercise
subscription and billing flows without hitting a real service.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Wire the memory and mock_memory helper modules into the saas mode end-to-end test binary so the new memory-related scenarios can be exercised.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add an opt-in debug dump of the captured memory requests, gated on the
DUMP_MEMORY_WIRE environment variable, so the wire traffic can be inspected
when the profile isolation assertion fails.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The memory isolation test now records the request range of the forget-by-id call and permits read-only GETs in that range to name scopes outside the caller's root, since TinyMemory's id-based forget sweeps the whole tree by contract. The wire assertions were also tightened to report the offending request index, and the debug dump behind DUMP_MEMORY_WIRE was dropped.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Memory access is no longer restricted to the resolved scope's subtree, and
confinement is now disabled regardless of mode. This restores the intended
unconfined behaviour while the scope resolution call is kept for its side
effects.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
allowed_reach now derives its subtree from the current scope's root instead of the global namespace root, and the always-true condition in confinement_in was removed so the saas check actually gates confinement.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the manual descending comparator with sort_by_key using Reverse so the intent of ordering by score is clearer.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T08:31:07.279388Z 60e5886 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0890074c-5ef8-4df3-89d6-d052138067d0

📥 Commits

Reviewing files that changed from the base of the PR and between d366f17 and 60e5886.


📒 Files selected for processing (1)
  • scripts/ci/check-gated-test-allowlist.sh

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.



📝 Walkthrough

Walkthrough

The change adds a stateful mock memory backend and SaaS end-to-end tests. The tests provision three profiles, exercise memory operations, check for cross-profile data exposure, and inspect recorded backend requests.

Changes

Memory Isolation

Layer / File(s) Summary
Mock server and request capture
tests/saas_mode/mock_memory.rs
Adds a loopback HTTP mock with shared state, request logging, request parsing, dispatch, and bearer checks.
Mock memory operations
tests/saas_mode/mock_memory.rs
Implements event creation, listing, fetching, recall, forgetting, scope listing, and answers in the mock.
SaaS profile isolation tests
tests/saas_mode/memory.rs, tests/saas_mode_e2e.rs, scripts/ci/check-gated-test-allowlist.sh
Registers the test modules and adds multi-profile checks for reads, writes, forgetting, response contents, and backend requests. The CI allowlist includes profiles/surface_tests.rs.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Other

Suggested reviewers: m3ga-mind


Merge Risk: ⚪ Minimal · up to 60e58

The PR adds SaaS memory-isolation tests and aligns the gated-test allowlist; no actionable merge risk is established, so it appears ready subject to normal checks.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 46.88% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: an SaaS memory round-trip isolation test across profiles.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.

  • Fix all pre-merge checks with AI
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

A rabbit checks each memory trail,
Three profiles keep their secrets veiled.
The mock records each request’s course,
While tests probe scope and source.
One canary rests; the others stay,
And hopping checks complete the day.

Comment @coderabbitai help to get the list of available commands.

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

@tinysweeper

tinysweeper Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper completed its review; deterministic results follow.

State: Reviewing pending checks
Priority: medium
Reviewed head: 60e588610285
Updated: 1791620799 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 0 Active findings 1
Tests 4 Noted findings 0
Documentation 0 Resolved findings 43
Configuration 0 Pending checks/questions 4

Completeness: Complete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

This revision adds an end-to-end SaaS memory isolation harness. tests/saas_mode/memory.rs introduces `Stack::start` (spawns the real openhuman-core binary with `BACKEND_URL` pointed at the mock, polls `/health`), `provision` (per-user profile plus session credential via `openhuman.profiles_set_credential`), `call`/`ok` JSON-RPC helpers, and `holds_only_own` (asserts no other user's canary secret appears in a response). The test `profiles_round_trip_their_own_memory_and_never_anothers` uses users `ann`, `anna`, `bob` (deliberately, `ann` is a string prefix of `anna`) with per-user canary secrets; each profile learns, lists, fetches, recalls, and explores its own canary while responses must not contain others' secrets; an attacker profile aims reach filters, foreign paths, foreign ids, and planted namespaces at victims and must gain nothing. A wire-level check asserts every request to the mock carried one profile's credential and named only scopes inside that profile's root, with a documented read-only exception for the forget-by-id sweep (`GET` /memory/scopes and /memory/events under `app:tinymemory` during the attacker's own forget). tests/saas_mode/mock_memory.rs implements the mock backend: a single shared store for every bearer (worst case for confinement), request logging with a one-way `credential_id` hash of the bearer, scope extraction from body/query, idempotency-key and fingerprint replay with conflict handling, routes for experience/events/recall/forget/scopes/answer, and pack invalidation on forget. tests/saas_mode_e2e.rs registers the two new modules. scripts/ci/check-gated-test-allowlist.sh adds `profiles/surface_tests.rs` to the allowlist.

Features

  • Added — SaaS memory isolation E2E harness: Exercises the real core binary end to end (spawn, /health polling, JSON-RPC over HTTP) through provisioning, learn/list/fetch/recall/explore/forget, cross-profile isolation attacks, and wire-level credential/scope assertions against a shared-store mock backend. (tests/saas_mode/memory.rs, tests/saas_mode_e2e.rs)
  • Added — Stateful /memory/* mock backend: Provides a TinyHumans memory mock whose single shared store makes core-side confinement the only thing separating profiles; records each request's method, path, one-way bearer hash, and named scopes; enforces idempotency semantics; drops recall packs holding forgotten events. (tests/saas_mode/mock_memory.rs)
  • Added — Gated-test allowlist entry: Adds profiles/surface_tests.rs to the CI gated-test allowlist. (scripts/ci/check-gated-test-allowlist.sh#mcp/server/resources.rs)

Tests

  • addition — `profiles_round_trip_their_own_memory_and_never_anothers` provisions three profiles (ann, anna, bob) on one shared engine, has each learn a unique canary, verifies via the mock that canaries landed inside each profile's root, then asserts list/fetch/recall/explore return only the caller's own secret. It then runs an attacker through foreign reach filters, foreign paths, victim ids with and without reach, and planted namespaces, asserting no leaks, no foreign forgetting (forgotten == 0), and that planted writes stay in the attacker's root. Finally it checks the wire log: every request bore a recognized profile credential and only in-root scopes, with an explicit, documented read-only exception for the forget-by-id scope sweep.: Behavioural and adversarial: the confinement claim is pinned by per-request scope assertions and by every victim still reading their own canary afterwards. The mock's shared-store design makes this the worst case for the `memory::user_scope` confinement claim. No test was weakened in this revision per the tests and e2e lanes; actual execution is pending on the listed CI jobs. (tests/saas_mode/memory.rs, tests/saas_mode/mock_memory.rs)
  • addition — The mock backend test infrastructure enforces idempotency (body key plus Idempotency-Key header claims with replay-to-body-key semantics and conflict responses), replicates listing behavior (each record emitted twice), supports recall scoring and pack storage, and on forget removes events and any derived recall packs containing them; it returns 401 without a bearer and 500 outside /memory/*.: Supports the isolation test realistically; the critique lane retains one finding here (stale derived data on derived-only forgets) even though the tests, description, and e2e lanes report pack dropping on forget as implemented. (tests/saas_mode/mock_memory.rs)

Findings

  • medium · critique · Delete derived packs during derived-only forgets — After `recall` creates a pack, a forget request such as `{ "scope": "user:1", "confirm_all": true, "cascade": "derived_only" }` returns without removing any packs. The same pack ca (tests/saas\_mode/mock\_memory\.rs:547)

Resolved this pass

  • critical — Add the referenced SaaS memory module files
  • Add the referenced SaaS memory module files
  • Remove forgotten events from stored packs
  • Include stored context in idempotency fingerprints
  • Allow repeated header claims to reach idempotency replay
  • Invalidate packs when forgetting their events
  • Do not retain bearer tokens in the request log
  • Add the referenced SaaS memory module files
  • Remove forgotten events from stored packs
  • Include stored context in idempotency fingerprints
  • Allow repeated header claims to reach idempotency replay
  • Invalidate packs when forgetting their events
  • Do not retain bearer tokens in the request log
  • critical — Add the referenced SaaS memory module files
  • high — Remove forgotten events from stored packs
  • medium — Include stored context in idempotency fingerprints
  • medium — Allow repeated header claims to reach idempotency replay
  • medium — Invalidate packs when forgetting their events
  • medium — Do not retain bearer tokens in the request log
  • critical — Add the referenced SaaS memory module files
  • high — Remove forgotten events from stored packs
  • medium — Include stored context in idempotency fingerprints
  • medium — Allow repeated header claims to reach idempotency replay
  • medium — Invalidate packs when forgetting their events
  • medium — Do not retain bearer tokens in the request log
  • Add the referenced SaaS memory module files
  • Remove forgotten events from stored packs
  • Include stored context in idempotency fingerprints
  • Allow repeated header claims to reach idempotency replay
  • Invalidate packs when forgetting their events
  • Do not retain bearer tokens in the request log
  • Add the referenced SaaS memory module files
  • Remove forgotten events from stored packs
  • Include stored context in idempotency fingerprints
  • Allow repeated header claims to reach idempotency replay
  • Invalidate packs when forgetting their events
  • Do not retain bearer tokens in the request log
  • Add the referenced SaaS memory module files
  • Remove forgotten events from stored packs
  • Include stored context in idempotency fingerprints
  • Allow repeated header claims to reach idempotency replay
  • Invalidate packs when forgetting their events
  • Do not retain bearer tokens in the request log

Pending checks: Rust E2E (mock backend), Build Playwright E2E Artifact, E2E (Playwright / web lane), Desktop E2E (full suite, 3 OS)

Before merge

  • Wait for Rust E2E (mock backend), Build Playwright E2E Artifact, E2E (Playwright / web lane), Desktop E2E (full suite, 3 OS).
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 3 files; 1 finding. _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
  • Evidence: tests/saas\_mode/mock\_memory\.rs — Delete derived packs during derived-only forgets

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The security lane reviewed the changed files and reported zero findings.
  • Lane summary: Reviewed 3 files; 0 findings. _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: All six findings from the prior revision are addressed in this revision: the memory module and mock now exist, the mock drops recall packs carrying forgotten events, the idempotency fingerprint includes the stored context, repeated header claims fall through to the body-key replay, and the request log keeps a one-way hash instead of the bearer token.
  • Positive: The tests are behavioural — the confinement claim in the sweep-exception comment is pinned by the per-request scope assertions and by every victim still reading their own canary afterwards — and the tests lane found nothing new to raise.
  • Lane summary: This revision adds the SaaS memory E2E module and its mock backend plus one CI allowlist entry. All six earlier findings are addressed in the new files: the memory module and mock now exist, the mock drops recall packs carrying forgotten events, its idempotency fingerprint includes the stored context, repeated header claims fall through to the body-key replay, and the request log keeps a one-way hash instead of the bearer token. The tests are behavioural — the confinement claim in the sweep-exception comment is pinned by the per-request scope assertions and by every victim still reading their own canary afterwards — and I found nothing new to raise. _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Positive: Nothing sensitive found in what this pull request commits.
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The description accurately covers the test's assertions, the known forget-by-id sweep exception, and the clippy status; nothing new to raise.
  • Lane summary: The new SaaS memory isolation e2e and its mock are now present and match the description: all six earlier findings are fixed in this revision — the module files exist, the mock hashes bearer tokens into the request log, the idempotency fingerprint includes the stored context, repeated header claims only conflict when bound to a different body key, and forgetting drops recall packs that carried the removed events. The description accurately covers the test's assertions, the known forget-by-id sweep exception, and the clippy status. Nothing new to raise. _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

e2e

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: This revision adds the SaaS memory e2e harness: a stateful mock of the TinyHumans /memory/* backend and a test that drives the real openhuman-core binary through provisioning, learn/list/fetch/recall/explore/forget, cross-profile isolation attacks, and a wire-level check of credentials and scopes. All six earlier findings are fixed in this revision: the referenced memory modules now exist, the mock drops recall packs holding forgotten events, the idempotency fingerprint includes the stored context, repeated Idempotency-Key header claims reach the body-key replay path, and the request log stores a one-way credential_id hash instead of the bearer token. The end-to-end coverage is real (spawns the binary, polls /health, drives JSON-RPC over HTTP) and no changed test was weakened. The change looks sound to merge, subject to the pending CI jobs concluding green. Waiting on end-to-end jobs: `Rust E2E (mock backend)`, `Build Playwright E2E Artifact`, `E2E (Playwright / web lane)`, `Desktop E2E (full suite, 3 OS)`.
  • Unresolved questions/checks: Rust E2E (mock backend), Build Playwright E2E Artifact, E2E (Playwright / web lane), Desktop E2E (full suite, 3 OS)
Evidence and run details
  • Models: gpt-5.6-luna, glm-5.3-flash
  • Spend: $0.026226
  • Tokens: 367460 input · 21987 output · 26484 cached · 0 embedding
Head State Pass summary
9569c3a5ba5f changes requested 6 active finding(s), 0 resolved finding(s) (at 1791605935)
60e588610285 pending 1 active finding(s), 43 resolved finding(s) (at 1791620799)

tinysweeper 0.1.0

coderabbitai[bot]
coderabbitai Bot previously approved these changes Oct 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9569c3a5ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/saas_mode/memory.rs Resolved

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 1 lane(s) blocking, worst finding is critical.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0195 · 276,177 in / 17,335 out · 31,925 cached (12%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0113 · 154,429 in / 11,962 out · 23,384 cached (15%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0079 · 89,715 in  / 4,973 out  · 7,133 cached (8%)   · gpt-5.6-luna
description: $0.0001 · 16,780 in  / 186 out    · 1,408 cached (8%)   · glm-5.3-flash

Comment thread tests/saas_mode_e2e.rs Resolved
Comment thread tests/saas_mode/mock_memory.rs Resolved
Comment thread tests/saas_mode/mock_memory.rs Resolved
Comment thread tests/saas_mode/mock_memory.rs Resolved
Comment thread tests/saas_mode/mock_memory.rs Resolved
Comment thread tests/saas_mode/mock_memory.rs Resolved
@tinysweeper tinysweeper Bot added the priority: p0 Drop what you are doing. Data loss, a live break, or an exploitable hole. label Oct 10, 2026
senamakel and others added 2 commits October 10, 2026 07:30
…emory.rs

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Oct 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d366f17a09

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/saas_mode/memory.rs
Comment on lines +236 to +237
for (method, params) in [
("openhuman.memory_items_list", json!({ "filter": filter })),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add cross-profile items_get probes

If confine::items_get loses its reach check, this suite will still pass: openhuman.memory_items_get is a separate public ID-based read (crates/openhuman-core/src/memory/confine.rs:175-184), while this attack matrix tests list/fetch/recall/explore and only gives learned[victim] to memory_forget, whose independent ops::within_reach precheck can remain correct. The victim ID and mock /memory/events/:id route already exist, so probe items_get directly with the hostile reaches and with no reach, and assert that the victim secret is absent.

AGENTS.md reference: AGENTS.md:L368-L374

Useful? React with 👍 / 👎.

senamakel and others added 2 commits October 10, 2026 11:21
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Main fails static:gated-test-allowlist since the file landed.

Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previously-blocking findings are resolved. Clearing the changes request.

             $0.0262 · 367,460 in / 21,987 out · 26,484 cached (7%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0120 · 139,028 in / 10,443 out · 12,677 cached (9%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0132 · 156,523 in / 6,679 out  · 10,479 cached (7%) · gpt-5.6-luna
tests:       $0.0002 · 17,067 in  / 2,130 out  · 1,536 cached (9%)  · glm-5.3-flash
description: $0.0001 · 17,759 in  / 410 out    · 64 cached (0%)     · glm-5.3-flash
e2e:         $0.0002 · 20,505 in  / 593 out    · 1,728 cached (8%)  · glm-5.3-flash

}
let cascade = body["cascade"].as_str().unwrap_or("derived_only");
match cascade {
"derived_only" => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique likely

Delete derived packs during derived-only forgets

After recall creates a pack, a forget request such as { "scope": "user:1", "confirm_all": true, "cascade": "derived_only" } returns without removing any packs. The same pack can then be passed to answer and still serve the forgotten context. derived_only should preserve source events while removing derived artifacts, including packs associated with the forgotten scope or selected event IDs.

[RULE] stale-derived-data ·

@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p0 Drop what you are doing. Data loss, a live break, or an exploitable hole. labels Oct 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60e5886102

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/saas_mode/memory.rs
for step in [foreign.clone(), String::new()] {
let body = s.call(
attacker,
"openhuman.memory_items_list",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Probe foreign paths through memory_explore

If a regression affects only namespace-path handling in confine::explore, this E2E still passes: the hostile memory_explore calls above exercise only filter.reach, while this foreign-path loop invokes memory_items_list even though the two RPCs have separate confinement wrappers. Add an openhuman.memory_explore call with the same foreign path (and a facet), otherwise a path-based escape from the per-user root is not covered.

AGENTS.md reference: AGENTS.md:L368-L373

Useful? React with 👍 / 👎.

Comment thread tests/saas_mode/memory.rs
);
let sweep = sweep_start..s.mock.requests().len();
assert_eq!(forgotten["forgotten"], json!(1), "{forgotten}");
for (user, _) in profiles.iter().skip(1) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify the owner's canary is gone after forget

If hosted forget returns forgotten: 1 while the item remains readable (for example because event or cache invalidation is wrong), this test still passes: after the assertion the only listings iterate profiles.iter().skip(1), so the attacker whose item was supposedly deleted is never queried again. Read the attacker's list after this call and assert its canary is absent to make the advertised forget round trip meaningful.

Useful? React with 👍 / 👎.

@senamakel
senamakel merged commit a8fc01b into tinyhumansai:main Oct 10, 2026
26 of 32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant