Exgit is in active pre-1.0 development. Security fixes are made
against main. No back-porting to older 0.x releases is currently
offered.
| Version | Supported |
|---|---|
| main | ✅ |
| 0.1.x | ✅ |
| < 0.1 | ❌ |
Do not open a public issue for security vulnerabilities.
Email ivar@ivarvong.com with:
- A description of the issue
- Exact steps to reproduce (a minimal test case is best)
- The affected version / commit
- Any known mitigations
You should receive an acknowledgement within 3 business days. We'll work with you on a disclosure timeline — typically 30–90 days depending on severity and complexity of the fix.
We request embargo until the fix is released.
Exgit is a git client library. The trust boundaries we enumerate and defend:
| Boundary | Input | Defenses |
|---|---|---|
| Remote server → client | ls-refs output, pack bytes, redirects |
Exgit.RefName validates ref names at the wire perimeter; RefStore.Disk re-validates defense-in-depth on every read/resolve/write/delete (#1). Exgit.Object.Tree.decode/1 validates each entry name against path-traversal rules — rejects .., /, NUL, .git/.gitmodules in any case (#2). Exgit.Object.Commit.decode/1 and Tag.decode/1 validate hex in header values so accessor calls (Commit.tree/1, Commit.parents/1, Tag.object) are infallible — a hostile remote cannot DoS a walk/diff/FS operation by shipping a structurally-valid commit with non-hex headers (#23). Pack.Reader bounds memory via :max_pack_bytes / :max_object_bytes / :max_resolved_bytes (#11/#35) and never raises on hostile input. Credentials are host-bound by default with ASCII-case-folding + trailing-dot-stripping normalization (#5); redirects are disabled unless explicitly opted-in. |
| User-supplied URL | Transport construction | No special validation — it's the caller's responsibility to avoid SSRF-class risks from user-controlled URLs. Host-bound credentials limit damage. |
| User-supplied credential | PATs, basic auth, callbacks | %Exgit.Credentials{} and %Exgit.Transport.HTTP{} both implement custom Inspect protocols that redact auth values. Crash logs, SASL reports, and IEx sessions do not leak tokens. |
| Local filesystem (Disk store) | Object/ref files on disk | SHA verification on read detects bit-rot and tampering. :zlib.uncompress/1 is wrapped in try/rescue so corrupt loose objects return {:error, :zlib_error} instead of crashing the caller (#3). Ref-store resolve_ref/2 re-validates symbolic targets read from disk so a ref: ../../etc/passwd file cannot escape the repo root (#1). |
| Local filesystem (config) | .git/config |
.git/config is treated as caller-controlled input, not remote-controlled. Config.parse/1 returns `{:ok, _} |
| User-supplied pack for push | Exgit.push/3 arguments |
Caller-generated input is within the trust boundary for push. Note that Exgit.push/3 reads objects from the local store, which may contain objects that came from a remote via an earlier clone/fetch — objects ingress through Pack.Reader which enforces the bounds above. A hostile pack cached earlier cannot trigger traversal at push time because tree entry names have already been validated at fetch-time decode. |
Not defended: the agent/caller can push arbitrary content to a remote if it has a write-scoped credential. That's by design — our job is to prevent a remote from attacking the client, not to sandbox the caller.
The test suite includes explicit regression tests for each CVE-worthy finding. Per review:
| Finding | Test file |
|---|---|
| #1 Ref-store disk path escape | test/exgit/security/ref_escape_test.exs + test/exgit/security/ref_store_disk_boundary_test.exs |
| #2 Tree entry path traversal | test/exgit/security/tree_entry_name_test.exs |
| #3 Loose object zlib raise | test/exgit/security/zlib_error_test.exs + test/exgit/security/loose_object_test.exs |
| #4 Pack inflate desync | test/exgit/pack/inflate_tracked_test.exs |
| #5 Credential host confusion | test/exgit/security/credential_host_normalization_test.exs + test/exgit/credentials_host_test.exs |
| #23 Commit hex DoS | test/exgit/security/malformed_hex_commit_test.exs |
| #23 (Tag sibling) | test/exgit/security/tag_malformed_hex_test.exs |
Any future security fix MUST land with a regression test in
test/exgit/security/ that would fail without the fix.
None yet. This file will list them with CVE IDs when applicable.
req— network client. Minimum version pinned to one with known-good cross-origin auth-stripping default. Bumping Req's major version requires re-running the cross-origin leak test suite before merging. (Our host-bound%Exgit.Credentials{}is the primary enforcement; Req's behavior is a belt-and-suspenders layer.)telemetry— BEAM-standard instrumentation. Low risk.opentelemetry_*— dev/test only; not present in production runtime.stream_data,dialyxir,credo— dev/test only.
A full SBOM is generated as part of the release process.
Exgit uses :crypto.hash(:sha, _) for git object SHA-1s. SHA-1 is
not collision-resistant in the modern cryptographic sense, but git
itself relies on SHA-1 and so does exgit. If you are working with a
SHA-256 git repository (rare, opt-in at repo creation), exgit does
not yet support it (tracked as a v0.2+ item).
We follow coordinated disclosure. Reporters are credited in the CHANGELOG unless they request anonymity.