Conversation
…500) Closes the kin gap PR #499 left on the namespace CRUD surface: even after #496 / #499 gated every caller-supplied ``namespace=`` / ``target=`` override on session-start and mem_agent_share, the ``mem_ns_*`` tools still wrote user-supplied strings into either app state or the storage row with only an ``if not strip()`` empty-check. The transitive bypass this closes: mem_ns_set(namespace="agent-runtime:foo:bar") # accepted today mem_session_start(agent_id="default") # falls through to # current_namespace -> sessions row lands with the same shape #496 closed elsewhere ``mem_session_start``'s priority chain treats ``app.current_namespace`` as the step-3 fallback when ``agent_id == "default"``. With ns_set ungated, an attacker who controls the value reaching it could land a malformed ``current_namespace`` and have it round-trip into the next session row through the fallback. Same user-visible outcome as the ``namespace=`` override #499 closes — different door. This applies ``validate_namespace`` (the strict gate from #499) to every public ``mem_ns_*`` surface that takes a user-supplied namespace string before storage / state is touched: * mem_ns_set(namespace=) — closes the transitive bypass. * mem_ns_delete(namespace=) — read-shaped today but pulled under the gate for consistency. * mem_ns_rename(old=, new=) — both arms gated. * mem_ns_assign(namespace=, old_namespace=) — both arms gated. * mem_ns_update(namespace=) — lookup key gated. The ``.strip()`` calls the prior code applied silently to caller input go away — the validator's contract is forward-shield-on-input, and silent stripping of whitespace-padded values is exactly the kind of "sanitise and accept" behaviour the namespace gate is closing across surfaces. Existing accepted shapes (``default``, ``shared``, ``archive:summary``, ``claude-memory:project-x``, ``agent-runtime:planner``, etc.) round-trip unchanged — pinned by the ACCEPTED_NAMESPACES suite in test_validate_namespace.py. CLI mirror: there is no ``mm ns`` command surface today (the CLI exposes ``mm agent register/share/list/migrate`` only, not the ns CRUD tools), so the issue's "CLI mirror" clause is vacuous for this set. If a ``mm ns`` surface lands later, it must inherit the gate via the existing pattern in ``mm session start --namespace`` / ``mm agent register``. Out of scope: migrating any malformed namespace rows already in storage. Same forward-only contract as #496 / #499 — this is a write shield, not a corrective sweep. Tests: * test_validate_namespace_ns_tools.py — per-tool hostile rejection parametrised over the existing HOSTILE_NAMESPACES suite, plus the end-to-end transitive-bypass regression: ns_set -> session_start(agent_id="default") cannot land an ``agent-runtime:foo:bar`` session row. * Re-runs cleanly under the existing ``test_validate_namespace.py`` suite (no overlap; the ns-tool boundary was previously uncovered). * Full ``pytest -m "not ollama"`` green (2850 passed). Co-Authored-By: Claude <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #500 — the kin gap PR #499 left on the namespace CRUD surface. After #496 / #499 gated every caller-supplied
namespace=/target=override on session-start andmem_agent_share, themem_ns_*tools still wrote user-supplied strings into either app state or the storage row with only anif not strip()empty-check.The transitive bypass this closes
mem_session_start's priority chain (server/tools/session.py:101-112) treatsapp.current_namespaceas the step-3 fallback whenagent_id == "default". Withmem_ns_setungated, an attacker who controls the value reaching it could land a malformedcurrent_namespaceand have it round-trip into the next session row through the fallback. Same user-visible outcome as thenamespace=override #499 closes — different door.What's gated
validate_namespace(the strict gate from #499) is now applied at every publicmem_ns_*surface that accepts a user-supplied namespace string, before storage / state is touched:mem_ns_set(namespace=)— closes the transitive bypass.mem_ns_delete(namespace=)— read-shaped today, pulled under the gate for consistency.mem_ns_rename(old=, new=)— both arms gated.mem_ns_assign(namespace=, old_namespace=)— both arms gated.mem_ns_update(namespace=)— lookup key gated.The pre-existing
.strip()calls on caller input go away — the validator's contract is forward-shield-on-input, and silent stripping of whitespace-padded values is exactly the kind of "sanitise and accept" behaviour the namespace gate is closing across surfaces.Out of scope (matches #496 / #499)
mm nscommand surface today (CLI exposesmm agent register/share/list/migrateonly, not the ns CRUD tools), so the issue's "CLI mirror" clause is vacuous for this set. Ifmm nslands later, it inherits the gate via the existing pattern inmm session start --namespace/mm agent register.Test plan
tests/test_validate_namespace_ns_tools.py— per-tool hostile rejection parametrised over the existingHOSTILE_NAMESPACESsuite, plus the end-to-end transitive-bypass regression:mem_ns_set("agent-runtime:foo:bar")→mem_session_start(agent_id="default")cannot land anagent-runtime:foo:barsession row.tests/test_validate_namespace.pyre-runs cleanly (no overlap; the ns-tool boundary was previously uncovered).uv run ruff check packages/memtomem/src && uv run ruff format --check packages/memtomem/src packages/memtomem/tests— clean.uv run pytest -m "not ollama"— 2850 passed, 46 deselected.🤖 Generated with Claude Code