Repository navigation
analytics: cross-project data leakage — source_id accepted 'for API consistency' then ignored; call-graph/endpoint-coverage analyse AutoBot's own root regardless of selected source (56 of 84 ops unscoped) #12330
Description
Summary
Several Codebase Analytics endpoints accept a source_id query parameter, declare it "for API consistency", and then never use it. The handlers analyse AutoBot's own project root instead of the selected code source. So opening /analytics/codebase/<source-id> shows panels populated with data from a different codebase entirely.
This matches the observed behaviour that "some repos leak other project data into analytics" — some panels are correctly scoped, others silently are not.
Proof — the parameter is accepted and ignored
api/codebase_analytics/endpoints/call_graph.py:727:
async def get_call_graph(
refresh: bool = Query(False, description="Force refresh, bypass cache"),
source_id: str | None = Query(None, description="#1772: source_id for API consistency"),
):
"""Get function call graph."""
project_root = get_project_root() # <-- AutoBot's own root, NOT source_id
if not refresh:
cached_data = await _get_cached_call_graph(str(project_root)) # cache keyed on that root
...
python_files = await _get_python_files(project_root)source_id never appears again in the body. The cache key is the AutoBot project root, so every source shares one cache entry — selecting a different project returns the same graph.
api/codebase_analytics/endpoints/api_endpoints.py:98:
async def get_endpoint_coverage(
source_id: str | None = Query(None, description="#1772: source_id for API consistency"),
) -> JSONResponse:
checker = _get_checker() # APIEndpointChecker() — no source argument
analysis = await asyncio.to_thread(checker.run_full_analysis)Same pattern: parameter accepted, never read. get_api_endpoints() and get_frontend_api_calls() in the same module take no source_id at all and are documented as returning "all FastAPI route definitions found in the backend" / "all API calls found in frontend TypeScript/Vue files" — i.e. AutoBot's backend and frontend.
Scope of the gap
Of the 84 /api/analytics/codebase/* operations in the live OpenAPI spec, 28 accept source_id and 56 do not. Counting source_id references per module shows a half-finished migration:
| module | source_id refs |
state |
|---|---|---|
sources.py |
41 | scoped |
duplicates.py |
34 | scoped |
stats.py |
29 | scoped |
ownership.py |
20 | scoped |
charts.py |
15 | scoped |
indexing.py |
12 | scoped |
report.py |
11 | scoped |
declarations.py |
5 | partial |
environment.py |
3 | partial |
api_endpoints.py |
1 | accepted-but-ignored |
call_graph.py |
1 | accepted-but-ignored |
dependencies.py |
1 | only on /cached |
import_tree.py |
1 | only on /cached |
cross_language_patterns.py |
1 | unscoped |
Note dependencies.py and import_tree.py scope only their /cached variants (get_cached_dependency_result(source_id: str = "")), while the primary GET /analytics/dependencies and GET /analytics/import-tree take no source at all — so the cached and live paths for the same panel disagree about whether they are per-project.
The scoped modules carry explicit comments about this exact bug class:
stats.py:128: Issue #3685: Filters by source_id to prevent cross-project leakage.
stats.py:205: Issue #1710: source_id filters to per-project stats.
stats.py:282: key_prefix = f"codebase:{source_id}" if source_id else "codebase"
So cross-project leakage was recognised and fixed in #1710/#3685 — but only for part of the surface, and #1772 then added source_id parameters to some remaining endpoints without wiring them up, which makes the unfixed ones look fixed from the outside.
Why "sometimes"
That last point explains the intermittency. A panel backed by a scoped endpoint is correct; a panel backed by an accepted-but-ignored one shows AutoBot's own codebase. Both are on the same page, both receive the same source_id, and only some honour it — so the page appears mostly right with some sections showing foreign data.
Note the fallback in stats.py:282 — when source_id is absent the key prefix degrades to the global "codebase" bucket rather than failing. Unscoped endpoints therefore read shared/global state by default, which is the leak mechanism.
Recommendation
- Wire
source_idthrough incall_graph.pyandapi_endpoints.py— resolve the source's root path from the source registry instead ofget_project_root(), and includesource_idin every cache key. - Remove the "for API consistency" parameters until they work. An accepted-and-ignored filter is worse than an absent one: callers reasonably assume the response is scoped. If a handler cannot honour it yet, it should return
400/501rather than silently returning another project's data. - Audit the remaining 56 unscoped operations and decide per endpoint: scope it, or document it as genuinely global (e.g. self-introspection of AutoBot itself) and keep it off per-project pages.
- Make the global fallback explicit —
key_prefix = "codebase"whensource_idisNonesilently mixes projects. Prefer requiringsource_idon any per-project route. - Add a regression test with two indexed sources asserting that every per-project endpoint returns different data for each — the current single-source test environment cannot detect this class of bug at all.
Verification note
On the reporting deployment only one source is indexed (f5673506-…) and its analytics collections are empty, so a runtime A/B comparison between two sources was not possible here; the finding above is established from the route contract and handler source rather than differential output. Reproducing with two indexed repositories should show the call-graph and endpoint-coverage panels returning identical AutoBot-internal data for both.
Impact
High — analytics silently attributes one project's structure, call graph, and API surface to another. Results look plausible, so the data is misleading rather than obviously broken, and any decision made from these panels is unsound.
Environment
install.sh deploy at /opt/autobot; deployed tree matches origin/Dev_new_gui. Observed via /analytics/codebase/f5673506-a355-4c8a-8ee3-963fcdb54e7c.
Activity
fix(security): cross-project analytics data leakage — scope source_id…
mrveiss commentedon Jul 24, 2026
Resolved by #12355 (squash-merged, code-reviewed, all required checks + SAST green). Cross-project analytics leak fixed across 5 endpoints (resolve_scan_root(source_id) + per-source cache + ChromaDB where-filter; e2e isolation test). Sub-items tracked: #12356 cross_language, #12358 authz (part of #12364 analytics redesign).
fix(analytics): graceful /cached no_data + daily off-peak cache popul…
refactor(analytics): converge codebase-analytics onto the indexed sto…
fix(analytics): stop /report and /duplicates hanging, and scope the o…