ref:6454040c85100613e885478898a19cbcb1fad9e8

Fix red team cycle 3: tree depth limits, OFS offset limit, SHA validation

- diff.ex: Add @max_tree_depth 64 to diff_tree_entries matching merge.ex - upload_pack.ex: Add depth limit to collect_reachable/collect_tree_objects - upload_pack.ex: Validate want/have SHAs match 40-char lowercase hex - reader.ex: Add depth limit to parse_ofs_continuation 452 tests, 0 failures. Fixes #6. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SHA: 6454040c85100613e885478898a19cbcb1fad9e8
Author: Cole Christensen <cole.christensen@macmillan.com>
Date: 2026-02-11 03:42
Parents: 6a6e0b8
5 files changed +453 -17
Type
RED_TEAM_JOURNAL.md +28 −0
@@ -386,3 +386,31 @@
| NEW-H3 | `parse_entries` now builds SHA-to-offset cache incrementally as it parses non-delta objects. Cache passed to `resolve_delta_entries` → `read_object`, eliminating redundant `build_sha_index` calls. | cycle2_fixes_test.exs (4 tests) |
| NEW-H4 | `encode_raw_from_type/2` guard changed from `is_atom(type)` to `type in [:blob, :commit, :tree, :tag]`. Invalid types now raise `FunctionClauseError`. | cycle2_fixes_test.exs (17 tests) |
---
## Red Team Cycle 3
**Auditors**: 2 parallel auditors verified all cycle 2 fixes correct.
### New findings from cycle 3:
| ID | Severity | Finding | Disposition |
|----|----------|---------|-------------|
| C3-HIGH-1 | High | `parse_ofs_continuation` in reader.ex has no recursion depth limit | **Fixed in cycle 3** — added guard `consumed > @max_header_continuation_bytes` |
| C3-HIGH-3 | High | `diff_tree_entries` in diff.ex recurses into subdirectories with no depth limit | **Fixed in cycle 3** — added `@max_tree_depth 64` matching merge.ex pattern |
| C3-HIGH-4 | High | `collect_reachable`/`collect_tree_objects`/`collect_tree_entry_objects` in upload_pack.ex have no tree depth limit | **Fixed in cycle 3** — added depth parameter with `@max_tree_depth 64` |
| C3-HIGH-5 | High | Protocol `want`/`have` SHAs not validated for hex format | **Fixed in cycle 3** — added `~r/\A[0-9a-f]{40}\z/` validation |
---
## Fix Cycle 3 (this commit)
**4 fixes.** 452 tests, 0 failures.
| Finding | Fix Summary | Tests |
|---------|-------------|-------|
| C3-HIGH-1 | `parse_ofs_continuation` now has a guard `consumed > @max_header_continuation_bytes` matching the existing `parse_size_continuation` pattern. Returns `{:error, :ofs_offset_too_long}`. | cycle3_fixes_test.exs (6 tests) |
| C3-HIGH-3 | `diff_tree_entries` now takes a depth parameter, starting at 0 from `diff_trees`. At depth > 64, throws `{:error, :max_tree_depth_exceeded}` caught by try/catch in `diff_trees`. Matches existing pattern in merge.ex. | cycle3_fixes_test.exs (4 tests) |
| C3-HIGH-4 | `collect_reachable`, `collect_tree_objects`, `collect_tree_entry_objects` now take a depth parameter. At depth > 64, raises (caught by existing try/rescue in `collect_objects`). | cycle3_fixes_test.exs (2 tests) |
| C3-HIGH-5 | `parse_want_lines` and `parse_have_lines` now validate SHAs with `~r/\A[0-9a-f]{40}\z/`. Invalid SHAs return `{:error, {:invalid_want_sha, sha}}` or `{:error, {:invalid_have_sha, sha}}`. | cycle3_fixes_test.exs (7 tests) |