Skip to content

fix(repository): harden Repo path/network/identity handling - #594

Open
carfeii wants to merge 1 commit into
AsyncFuncAI:mainfrom
carfeii:fix/repo-identity-path-hardening
Open

carfeii wants to merge 1 commit into
AsyncFuncAI:mainfrom
carfeii:fix/repo-identity-path-hardening

Conversation

@carfeii

@carfeii carfeii commented Oct 5, 2026

Copy link
Copy Markdown

Three related hardening fixes to api/repository.py's Repo class, all on the caller-controlled repo_url trust boundary:

  • save_path no longer trusts a local repo_url unchanged. Repo.is_local treats any non-URL string as a local path with no containment, so repo_url=/ made save_path return / unchanged, letting /codemap/file and the RAG indexer read/embed any server-readable file. save_path now resolves the path and requires it stay under an allowlisted root (the deepwiki data root by default, extendable via DEEPWIKI_ALLOWED_LOCAL_ROOTS), rejecting both out-of-root paths and symlink escapes from an allowed root.
  • download() now resolves the remote host and rejects private, loopback, link-local, multicast, reserved, and unspecified addresses before git ever connects. The previous URL check only constrained the scheme (http/https/ftp), not the host, so a repo_url could point the server's own git clone at an internal service regardless of the labeled repo_type.
  • _extract_repo_name now appends a digest of the full canonical identity (type + host + path) to the derived storage key. The previous key was only the last two URL path segments, so two different repositories (different host, or a GitLab subgroup path sharing its last two segments with another repo) could collide on the same clone directory and embedding index.

Test plan

  • pytest tests/test_repository.py tests/test_extract_repo_name.py: new regression tests for all three issues pass.
  • pytest tests/: full suite passes with no regressions (same 3 pre-existing, unrelated failures as main).
  • Confirmed each new test fails against the pre-fix code (reverted api/repository.py only, re-ran).

Three related hardening fixes to api/repository.py's Repo class, all on
the caller-controlled repo_url trust boundary:

- save_path no longer trusts a local repo_url unchanged. Repo.is_local
  treats any non-URL string as a local path with no containment, so
  repo_url=/ made save_path return / unchanged, letting /codemap/file
  and the RAG indexer read/embed any server-readable file. save_path
  now resolves the path and requires it stay under an allowlisted root
  (the deepwiki data root by default, extendable via
  DEEPWIKI_ALLOWED_LOCAL_ROOTS), rejecting both out-of-root paths and
  symlink escapes from an allowed root.

- download() now resolves the remote host and rejects private,
  loopback, link-local, multicast, reserved, and unspecified addresses
  before git ever connects. The previous URL check only constrained the
  scheme (http/https/ftp), not the host, so a repo_url could point the
  server's own git clone at an internal service regardless of the
  labeled repo_type.

- _extract_repo_name now appends a digest of the full canonical
  identity (type + host + path) to the derived storage key. The
  previous key was only the last two URL path segments, so two
  different repositories (different host, or a GitLab subgroup path
  sharing its last two segments with another repo) could collide on
  the same clone directory and embedding index.

Adds regression tests for all three in tests/test_repository.py and
tests/test_extract_repo_name.py, including fail-before coverage run
against the unpatched code.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant