-
Notifications
You must be signed in to change notification settings - Fork 1
security
- SECURITY.md
- src/repodocs/publish.py
- src/repodocs/_util.py
RepoDocs treats the target repository as untrusted input throughout generation and publishing. The security posture documented in SECURITY.md centers on three boundaries: what the tool reads and writes locally, what it scans before anything reaches a public remote, and how it resolves repository-relative paths so agent- or repo-supplied strings can't escape the intended directory tree.
Sources: SECURITY.md:L13-L25
RepoDocs reads the target repository and writes generated output under the selected output directory, invokes a locally installed OMP, Claude Code, or Codex CLI when generation is requested, and may invoke Graphify during repodocs all. Network access is scoped to those external tools and to publishing against GitHub Pages / GitHub Wikis. The tool does not copy or bundle agent credentials during setup, and it explicitly warns that generated documentation can still expose sensitive repository content — reviewing output and repository visibility before publishing remains the operator's responsibility.
Sources: SECURITY.md:L13-L25
safe_repo_file(repo, rel) in src/repodocs/_util.py is the single chokepoint used to resolve a repo-relative path string (as might come from a citation, a plan entry, or agent output) against the actual repository root. It rejects the input outright for absolute paths and NUL bytes, then resolves both the repo root and the candidate target with Path.resolve() (which follows symlinks) and calls target.relative_to(root) to confirm the resolved target still lives under the resolved root. Any ValueError, OSError, or RuntimeError — including .. traversal, symlink escapes outside the repo, and symlink loops — causes it to return None rather than raise. It also refuses to return a path that isn't a regular file.

Because resolution happens after symlink-following, a symlink inside the repo that points outside it is caught by the relative_to check rather than trusted at face value; every caller that needs to touch a repo-relative path derived from generated or agent content is expected to go through this function rather than concatenating paths directly.
Sources: src/repodocs/_util.py:L77-L91
The publish pipeline in src/repodocs/publish.py applies the same "don't trust the filesystem tree" discipline when copying generated output into a staging directory. stage_publish refuses to stage any symlinked file or directory it encounters (put_file, put_dir, and the top-level subdirectory loop each raise ValueError on src.is_symlink()), and stage_wiki's read() helper does the same before reading a source Markdown page or plan.json. On the wiki-clone side, _reject_symlink_dest walks each path component of a destination inside the cloned wiki repo and refuses to write through any component that is itself a symlink, so a manually-tampered wiki clone can't redirect a write to an arbitrary location on disk.
Sources: src/repodocs/publish.py:L36-L78, src/repodocs/publish.py:L213-L266, src/repodocs/publish.py:L299-L308
Before any staged content can reach a public remote, staged_secret_findings walks every file in the staging directory and matches each line against PUBLISH_SECRET_PATTERNS: PEM-style private key headers, GitHub personal-access/OAuth tokens (ghp_/gho_/ghu_/ghs_/ghr_/github_pat_), and generic cloud/API key shapes (AWS AKIA... access key IDs, sk-... secret-style keys). Findings record only the file path, line number, and pattern label — the function's docstring notes it deliberately never echoes the matching value, so a caught secret isn't itself leaked into logs or error output. Both cmd_publish (GitHub Pages) and cmd_publish_wiki (GitHub Wiki) call this scan on the staged tree and abort with die(...) if any findings are present, before any push is attempted.
| Pattern label | Matches |
|---|---|
| private key | -----BEGIN (RSA/EC/OPENSSH/DSA/ENCRYPTED) PRIVATE KEY----- |
| GitHub token |
ghp_/gho_/ghu_/ghs_/ghr_/github_pat_ prefixed tokens |
| cloud/API key | AWS AKIA... access key IDs, sk-... style secret keys |
Sources: src/repodocs/publish.py:L119-L139, src/repodocs/publish.py:L179-L185, src/repodocs/publish.py:L384-L389
Publishing has several independent gates that all must pass before a push happens:
-
Protected-branch refusal.
publish_branch_safechecks the target branch (after stripping arefs/heads/prefix, case-insensitively) againstPROTECTED_PUBLISH_BRANCHES = {"main", "master", "trunk"};cmd_publishcallsdie(...)immediately if the branch is protected, before touching the network or the filesystem beyond that check. -
Citation integrity.
cmd_publishcallscitation_problemson the publishable Markdown pages anddies, listing offending citations, if any citation is missing or resolves outside the repository.cmd_publish_wikisimilarly callsenforce_citationsafter staging. -
Explicit opt-in to go public. Both
cmd_publishandcmd_publish_wikirequire--allow-public; without it, they print the staged file list and exit 0 without pushing (dry_runpath), ordiewith a reminder to rerun with--dry-runfirst. -
Non-destructive wiki pushes.
publish_wiki_pushclones the target GitHub Wiki, overwrites only the filenames actually staged (leaving any other manually-added wiki pages untouched), commits only if the tree changed, and pushes normally — never with--force. By contrast,publish_push(GitHub Pages) pushes a single orphan commit withgit push -f, but does so from a throwaway detached worktree, never touching the user's working tree or current branch. -
Bounded, credential-safe error output.
_wiki_gitraisesWikiPublishErrorwith output passed throughfailure_detail(result)rather than the raw subprocess argv, specifically so a credential-bearing remote URL embedded in a git command's arguments doesn't leak into an exception message.
Sources: src/repodocs/publish.py:L116-L143, src/repodocs/publish.py:L160-L211, src/repodocs/publish.py:L280-L349, src/repodocs/publish.py:L351-L408

Sources: src/repodocs/publish.py:L160-L211, src/repodocs/publish.py:L351-L408
SECURITY.md directs vulnerability reports to GitHub's private vulnerability reporting flow or to aryrabelo@gmail.com, explicitly asking that public issues not be opened for suspected vulnerabilities, with an acknowledgement expected within 72 hours. Security fixes are applied to the latest release and current main.
Sources: SECURITY.md:L1-L11
- Home
- Installation & Setup
- Architecture
- CLI Reference
- LLM Backend Selection
- Repository Scanning
- Wiki Page Planning
- Page Generation
- Source Citations
- Translation
- HTML Rendering
- Diagram Rendering
- Diagram Poster Tool
- Git Remote Link Resolution
- Publishing
- GitHub Wiki Integration
- Shared Utilities
- Environment Configuration
- Security & Trust Boundaries
- Limitations & Non-goals
- Testing
- Development
- Contributing
- Upgrading
- Changelog