fix: constrain cross-reference resolution to repo root (#91)
`(file_path.parent / link_path).resolve()` could silently follow a crafted `../../outside/path` link that escapes the repository root. Paths that resolve outside the repo root are now skipped rather than checked for existence. Co-authored-by: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,7 @@ def strip_code_blocks(content: str) -> str:
|
|||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
|
repo_root = Path().resolve()
|
||||||
|
|
||||||
for file_path in iter_md_files():
|
for file_path in iter_md_files():
|
||||||
content = file_path.read_text(encoding="utf-8")
|
content = file_path.read_text(encoding="utf-8")
|
||||||
@@ -68,12 +69,13 @@ def main() -> int:
|
|||||||
# from documentation examples inside code fences.
|
# from documentation examples inside code fences.
|
||||||
scannable = strip_code_blocks(content)
|
scannable = strip_code_blocks(content)
|
||||||
|
|
||||||
# Relative .md links must resolve
|
# Relative .md links must resolve and must stay within the repo root
|
||||||
errors.extend(
|
for link_path in re.findall(r"\[[^\]]+\]\(([^)#]+\.md)[^)]*\)", scannable):
|
||||||
f"{file_path}: broken cross-reference → '{link_path}'"
|
resolved = (file_path.parent / link_path).resolve()
|
||||||
for link_path in re.findall(r"\[[^\]]+\]\(([^)#]+\.md)[^)]*\)", scannable)
|
if not resolved.is_relative_to(repo_root):
|
||||||
if not (file_path.parent / link_path).resolve().exists()
|
continue
|
||||||
)
|
if not resolved.exists():
|
||||||
|
errors.append(f"{file_path}: broken cross-reference → '{link_path}'")
|
||||||
|
|
||||||
# In-page anchors must match a real heading
|
# In-page anchors must match a real heading
|
||||||
anchors = re.findall(r"\[[^\]]+\]\(#([^)]+)\)", scannable)
|
anchors = re.findall(r"\[[^\]]+\]\(#([^)]+)\)", scannable)
|
||||||
|
|||||||
Reference in New Issue
Block a user