Please report security issues privately. Email
sandals-shad.0o@icloud.com with subject fnd: <short description>.
Expect an initial reply within 5 business days. If you'd like to encrypt
the report, attach a request for a PGP key and one will be returned.
Please do not open a public GitHub issue, draft pull request, or discussion-thread comment for security-sensitive findings, even when the underlying behaviour seems minor. Public disclosure is coordinated after a fix ships.
We aim to acknowledge the issue, agree on a CVSS-ish severity, and publish a fix within 90 days of the initial report. If the report involves a third-party dependency (pymupdf, python-docx, python-pptx, tantivy, ...) and that upstream chooses a longer window, ours extends to match, and we will not publish details of an unpatched upstream issue.
If you intend to publish independently regardless, please tell us when so the public advisory and the patch can land together.
In scope:
- Anything that lets an attacker move from "user indexes my document" to "code runs on the user's machine" or "data leaks off the user's machine." Parser exploits in our indexed file types (PDF, DOCX, PPTX, MD, TXT) sit at the top of the list, even when the root cause is in an upstream library we depend on.
- Anything that lets a non-owner local user read fnd's on-disk state. The state can disclose the paths (and partial content via the index) of the owner's documents.
- Filename / shell-argument injection through the "open in app" flow.
- Adversarial config files dropped in
~/Library/Application Support/fnd/that escalate beyond the permissions of the current user. - Supply-chain integrity of the published Homebrew bottle and PyPI wheel (codesigning bypass, mismatched SHA-256 in the formula, poisoned upstream release).
Out of scope (today):
- Network-layer attacks. fnd opens no sockets, listens on no ports, speaks no HTTP. If you find network activity, that is in scope; please report it.
- DoS via genuinely huge documents that exceed the configured size
caps (
fnd/extract/_limits.py). The cap is the defence; raise an issue if a real-world legitimate doc trips it. - Theoretical query-DSL malice. fnd is a local tool whose queries are typed by the same user who owns the data. We do still bound query complexity as a hygiene exercise.
- Other-user-on-same-Mac escalation that depends on a setuid bug or a macOS isolation hole; those are macOS issues. We do harden the state directory to 0o700 so a different mechanism would be needed to reach our files in the first place.
- Side-channel attacks against indexed text.
- Latest released version on the
mainbranch: supported. - Pre-release /
-rctags: supported until the correspondingv*ships, then drop. - Older minor versions: best effort; we will fix where the diff is small but expect users to upgrade for everything else.
- Runtime deps are pinned with the compatible-release operator
(
~=) inpyproject.tomlso anuv lock --upgradecannot silently absorb a surprise major from a security-sensitive parser. Concrete pins live inuv.lock(the source of truth at install time). - CI runs
pip-audit --strictagainst the exported lockfile on every push and weekly on a cron (.github/workflows/security.yml). The build fails on any HIGH/CRITICAL CVE in a direct dep. - Quarterly we run
uv lock --upgrade-package <name>on each security-critical parser (pymupdf, python-docx, python-pptx, tantivy, markdown-it-py) and ship the bump as a patch release if the changelog discloses anything new.
uv sync --frozenis the supported install path. It readsuv.lockand refuses to resolve new versions.- Pinned toolchain: Python 3.13 (the one
uv python install 3.13resolves),uv >= 0.4. CI uses the same. - Pure-Python wheels build deterministically from the same sdist on
the same Python minor. Native-extension deps (
pymupdf,tantivy) ship as prebuilt wheels from PyPI; those are reproducible at the byte level for users on macOS arm64 / x86_64, identical to what CI uses. - For a release built locally instead of via CI, set
SOURCE_DATE_EPOCHto the commit timestamp beforeuv buildso the sdist metadata matches what CI would produce. - Tarball integrity: every GitHub Release ships a SHA-256 of the
sdist and a SLSA build-provenance attestation. Verify with
shasum -a 256 fnd-<ver>.tar.gzandgh attestation verify fnd-<ver>.tar.gz --repo ben-dev-au/fnd. - For verifying a Homebrew tap install, the formula pins both
urlandsha256;brew audit --strictvalidates the pin matches the downloaded tarball.
- Adversarial-document tests in
tests/test_extract_safety.pycover decompression-bomb shape, parser-crash conversion, encrypted-PDF refusal, and "one bad file doesn't kill the index build." - Filename-injection tests in
tests/test_opener_injection.pycover the macOS-specific osascript / argv escape concerns. - Symlink containment in
tests/test_walk_symlinks.py. - Collection-name regex in
tests/test_collection_name_validation.py. - Permission posture on the state dir in
tests/test_perms.py.
If a CVE or behaviour you encounter isn't covered by one of those, a
test failing on main and passing on the fix is the right shape for a
report.