Repository navigation
RepoNeedle 1.2.0: version bump, release documentation and directory-link traversal fix - #10
Merged
Merged
Conversation
- Set the version to 1.2.0 in pyproject.toml and reponeedle/__init__.py.
- Add CHANGELOG.md for 1.2.0 (reviewed against the code): CLI JSON and
anchors, MCP server, indexing security, indexing diagnostics, abstention
disabled by default, optional extras, backward compatibility, migration
and known limitations. No retrieval improvement is claimed.
- Replace pre-release wording ("planned 1.2.0", "development branch only",
"from source only") in README, KNOWN_LIMITATIONS, CLAIMS_AND_EVIDENCE and
docs/. MCP installation points to `pip install "reponeedle[mcp]>=1.2.0"`,
with 1.2.0 marked upcoming until it is published and a from-source
fallback. Historical benchmark provenance is unchanged.
- tests/test_documentation_consistency.py: replace the check that skipped
itself once the packaged version reached 1.2 (a skip fails the strict
Linux CI job) with checks that apply to any version, and fail if
"planned <packaged version>" wording remains.
The demo API and Hugging Face Space still bundle the 1.1.0 engine and are
not changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rsal RepoNeedleEngine.from_directory walked with os.walk(followlinks=False), which decides via os.path.islink(). That is False for Windows NTFS junctions, so junctions were descended into: - A junction pointing to an ancestor directory caused repeated traversal until the path length limit (reproduced: 83 directory visits for 3 real directories, each file indexed up to 27 times). - A junction pointing outside the repository caused outside directories to be listed (15 in the reproduction), and outside file names were recorded in indexing diagnostics. Outside file contents were already not read (file-level containment). On Linux and macOS, directory symlinks were already not followed. Fix (from_directory traversal only; ranking, packing, abstention and from_documents are unchanged): - Directory symlinks and junctions are never traversed on any platform. is_directory_link() uses os.lstat (S_ISLNK, or the mount-point reparse tag on Windows), so Python 3.10-3.12 share one code path. It fails closed: an entry that cannot be inspected is treated as a link. - Each physical directory (st_dev, st_ino) is entered at most once, so cycles that are not links (e.g. bind mounts) are also bounded. - Skipped directories are recorded by in-repository path only: skipped_directories, skipped_directory_paths and skipped_directories_outside_repository (new, defaulted fields). A count-only warning, CLI notice and MCP startup note are emitted for links resolving outside the repository. - Files under an in-repository link target are indexed once, under their real path. A directory reachable only through a link is not indexed (a behaviour change on Windows only; documented). Tests: platform-neutral directory-link tests in test_indexing_security.py (real symlinks on Linux in the strict no-skip CI job; junction fallback on Windows), including a simulated repeated-physical-identity test (os.stat patched; not a real bind mount) and a fail-closed detection test. Windows-only junction tests are in test_windows_junctions.py, which is not part of the strict job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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
Release preparation for RepoNeedle 1.2.0, plus one security fix found during final release checks. This PR does not tag, publish to PyPI or update the Hugging Face Space; those are separate steps after merge.
The PR has two commits.
1.
376f092— Bump version to 1.2.0 and update release documentation1.2.0inpyproject.tomlandreponeedle/__init__.py.CHANGELOG.md, reviewed against the code. It covers CLI JSON and anchors, the MCP server, indexing security, diagnostics, abstention disabled by default, optional extras, backward compatibility, migration and known limitations. No retrieval improvement is claimed.docs/.pip install "reponeedle[mcp]>=1.2.0", with 1.2.0 marked upcoming until published and a from-source fallback.tests/test_documentation_consistency.py: replaced a check that skipped itself once the version reached 1.2, because a skip fails the strict Linux job. The new checks apply to any version.2.
2b4da11— Never traverse directory links during indexing; bound directory traversalProblem.
from_directoryusedos.walk(followlinks=False), which relies onos.path.islink(). That returns False for Windows NTFS junctions, so junctions were descended into. Reproduced with synthetic files:src/uprecordedOutside file contents were already not read (file-level containment from the earlier security fix). On Linux and macOS, directory symlinks were already not followed.
Fix. Changes are limited to
from_directorytraversal. Ranking, packing, abstention andfrom_documents(used by all benchmarks) are unchanged.is_directory_link()usesos.lstat:S_ISLNK, or the mount-point reparse tag on Windows. Python 3.10–3.12 share one code path, which matters because Python < 3.12 has noos.path.isjunction.(st_dev, st_ino)is entered at most once, so traversal is bounded by the real directory tree.skipped_directories,skipped_directory_pathsandskipped_directories_outside_repository. They record in-repository paths only. A count-only warning, CLI notice and MCP startup note appear for links that resolve outside the repository.Compatibility. The new model fields have defaults. Existing keys and their order are unchanged. JSON without the new keys still validates. The
from_directorysignature is unchanged.Tests
tests/test_indexing_security.py: platform-neutral tests (real symlinks on Linux; junction fallback on Windows):The last test is simulated:
os.statis patched so one directory reports the repository root's identity. It is not a real bind mount, which would need root privileges.tests/test_windows_junctions.py: Windows-only junction tests, including detection withoutos.path.isjunction. They skip on Linux, so this file is not in the strict no-skip job.The strict
mcp-security-linuxjob (REPONEEDLE_FAIL_ON_SKIP=1) runstests/test_indexing_security.py, so the directory tests run there with real Linux symlinks.Local validation (Windows)
mcp+apiextras), final tree376f092alone, full suiteos.path.isjunction): security, junction and diagnostics suitesmcpnot installed; file symlinks)twine check --strict(version bump step)[mcp](3.12),[api](3.12)Not included
v1.2.0to run the release workflow (version match → tests → build → publish), then remove the "upcoming" wording.🤖 Generated with Claude Code