Repository navigation
test(kb): a shrink-only guard over every knowledge-base read entry point #16667
Description
Activity
- addedbugSomething isn't workingSomething isn't working
on Sep 13, 2026 AC1 is still open after the first guard PR. That PR installs a call-site guard (
repo_tests/kb_read_visibility_guard_test.py) with a frozen, shrink-only allowlist of 84 entries. Its AST detector finds<kb receiver>.<read primitive>(...)calls, plusadvanced_searchon RAG receivers, but it cannot see:- a KB method stored as a bound method and called later (
HybridSearcherkeepsself.search,knowledge/search.py:141-144); - polymorphic backend dispatch (
VectorSearchEngine._select_backend→backend.search); - filters hidden in
**kwargs(services/knowledge_base_adapter.py:82/85,api/knowledge_search.py:606/610); - the infra MCP server's HTTP hop to
/mcp/search_knowledge_base; - reads that bypass the primitives:
- direct
fact:*Redis reads (api/knowledge_vectorization.py:616,background_vectorization.py:290,api/knowledge_organization.py:158/277); - raw ChromaDB collection reads (
api/knowledge_maintenance.py:1909,api/knowledge_chroma.py:264/350); - the summary and temporal search services (
api/knowledge_graph_routes.py);
- direct
- receivers outside the known set (
kb,self.kb,knowledge_base,kb_to_use, …), and reads outside any function.
To close AC1, the detector must cover these, or each one gets its own guard. Until then the per-path sub-issues (#16664, #16665, #16666) carry the paths themselves.
- a KB method stored as a bound method and called later (
This landed via PR #16674 (merged 2026-09-14, branch
pr16674newassigned to this issue is a stale local snapshot of it — checked, superseded, not reviving) and has been extended further since. Verifying each AC against currentorigin/main(repo_tests/kb_read_visibility_guard_test.py/kb_read_visibility_allowlist.py):- Static-analysis guard over every production entry point.
kb_reads()/_scan()/_production_files()(kb_read_visibility_guard_test.py:174-327) walk production source with Python'sastmodule — no hand-kept list of entry points, the scan finds them.test_the_scan_is_not_blindandtest_every_scan_root_is_reachedguard against the scan silently reaching nothing. - Every entry point filters or sits in a reasoned, shrink-only allowlist; fails both directions.
test_every_unfiltered_kb_read_is_allowlistedcatches a new bypass;test_every_allowlist_entry_is_still_an_unfiltered_readcatches an allowlisted entry that started filtering but is still listed (exactly AC2's two-directional requirement);test_every_allowlist_reason_is_classifiedrequires each entry carry a real reason. - Ratchet enforces shrink, never re-grows silently.
UNFILTERED_READ_CEILING = 95(:168) plus_ceiling_verdict/test_the_detected_unfiltered_reads_sit_at_the_recorded_ceiling/test_the_ceiling_verdict_turns_both_ways(:363-389) fail on growth AND on an unlowered ceiling after a shrink — the "landed early as a ratchet so every later sub-issue shrinks it" framing this issue exists for is live and enforced now, which is what this issue's own scope was (the shrinking itself is security(kb): fact visibility is not enforced on the canonical read paths — knowledge_search and the grounded-agent RAG path never call check_access #16654's later sub-issues' job, tracked there).
All three ACs met with code evidence on current main. Closing.
- Static-analysis guard over every production entry point.
Parent: #16654. This is #16654's AC3, landed early as a ratchet so that every later sub-issue shrinks it.
Problem
No test enumerates the knowledge-base read entry points. A new route, tool or prompt path can read facts unfiltered, and nothing notices. Today only
/api/knowledge/search/scopedand/rag/scopedfilter; every other path listed in #16654's sub-issues does not.Acceptance criteria
Refs #16654