Skip to content

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

Closed

Description

@mrveiss

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

  1. Wire source_id through in call_graph.py and api_endpoints.py — resolve the source's root path from the source registry instead of get_project_root(), and include source_id in every cache key.
  2. 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/501 rather than silently returning another project's data.
  3. 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.
  4. Make the global fallback explicit — key_prefix = "codebase" when source_id is None silently mixes projects. Prefer requiring source_id on any per-project route.
  5. 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

  1. mrveiss commented on Jul 24, 2026

    @mrveiss
    OwnerAuthor

    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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions