Skip to content

fix: mitigate TOCTOU DNS rebinding gap in git clone SSRF prevention#633

Merged
Aureliolo merged 6 commits intomainfrom
fix/toctou-dns-rebinding
Mar 20, 2026
Merged

fix: mitigate TOCTOU DNS rebinding gap in git clone SSRF prevention#633
Aureliolo merged 6 commits intomainfrom
fix/toctou-dns-rebinding

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

@Aureliolo Aureliolo commented Mar 20, 2026

Summary

  • HTTPS URLs: Pin git clone to validated IPs via -c http.curloptResolve=host:port:ip (git >= 2.22), eliminating the TOCTOU gap entirely
  • SSH/SCP URLs: Double-resolve with subset comparison immediately before execution, narrowing the rebinding window
  • Literal IP URLs: Skipped (no DNS resolution, immune to rebinding)
  • Configurable: GitCloneNetworkPolicy.dns_rebinding_mitigation toggle (default: enabled) for CDN/geo-DNS environments
  • New DnsValidationOk model carries resolved IPs from validator to clone tool
  • build_curl_resolve_value helper constructs the curloptResolve config string
  • verify_dns_consistency performs the SSH/SCP double-resolve check
  • Updated CLAUDE.md and docs/design/operations.md with SSRF/TOCTOU documentation

Test plan

  • All 9719 existing + new tests pass (pytest -n auto)
  • 93.89% coverage (threshold: 80%)
  • mypy strict passes on all 1143 source files
  • ruff lint + format clean
  • Pre-commit + pre-push hooks pass
  • New test classes: TestDnsValidationOk, TestBuildCurlResolveValue, TestVerifyDnsConsistency, TestGitCloneToolToctou
  • TOCTOU-specific tests split to test_git_url_validator_toctou.py (all files < 800 lines)

Review coverage

Pre-reviewed by 11 agents: code-reviewer, security-reviewer, python-reviewer, conventions-enforcer, pr-test-analyzer, silent-failure-hunter, type-design-analyzer, async-concurrency-reviewer, logging-audit, docs-consistency, issue-resolution-verifier. 13 findings addressed.

Closes #508

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Stronger git-clone protections against DNS rebinding and SSRF: HTTPS IP pinning and SSH/SCP double-resolution consistency checks, enabled by default and configurable.
  • Observability

    • New events for DNS pinning, rebinding detection, and TOCTOU-skip situations.
  • Documentation

    • Expanded operational guidance on git-clone SSRF/DNS-rebinding mitigations and configuration guidance.
  • Tests

    • Added and extended unit tests covering DNS validation, pin formatting, and rebinding behaviors.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 20, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 20, 2026

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

Adds TOCTOU DNS-rebinding mitigations to git clone SSRF prevention. git_url_validator now exposes DnsValidationOk and GitCloneNetworkPolicy.dns_rebinding_mitigation (default true), returns validated hostname, port, resolved_ips, and is_https, and provides build_curl_resolve_value and verify_dns_consistency. GitCloneTool applies mitigation: HTTPS clones get http.curloptResolve=host:port:ip prepending; SSH/SCP clones perform a re-resolve and fail on unexpected IPs. New observability events and tests plus documentation updates added.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: mitigating TOCTOU DNS rebinding vulnerabilities in git clone SSRF prevention.
Linked Issues check ✅ Passed The PR implements all key mitigation objectives from issue #508: HTTPS pinning via curloptResolve, SSH/SCP double-resolve verification, literal-IP skip logic, configurable dns_rebinding_mitigation toggle, and comprehensive documentation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing TOCTOU DNS rebinding mitigations per issue #508: new models, validators, helpers, observability events, documentation, and corresponding tests.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 40.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 20, 2026 06:36 — with GitHub Actions Inactive
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on enhancing the security of git clone operations by mitigating TOCTOU DNS rebinding vulnerabilities. It introduces measures to ensure that the IP addresses used during validation remain consistent throughout the clone process, preventing potential SSRF attacks. The changes include new configurations, validation methods, and updated documentation to reflect the enhanced security measures.

Highlights

  • TOCTOU Mitigation: This PR implements Time-of-Check Time-of-Use (TOCTOU) mitigation for DNS rebinding attacks in git clone operations, enhancing security.
  • HTTPS URL Handling: For HTTPS URLs, the PR pins git clone to validated IPs using -c http.curloptResolve, eliminating the TOCTOU gap.
  • SSH/SCP URL Handling: For SSH/SCP URLs, the PR performs double-resolution with subset comparison immediately before execution, narrowing the rebinding window.
  • Configuration: A new GitCloneNetworkPolicy.dns_rebinding_mitigation toggle is introduced, allowing users to configure the DNS rebinding mitigation (default: enabled).
  • Testing and Documentation: The PR includes new test classes and updates documentation to reflect the changes in SSRF/TOCTOU handling.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 85.71429% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.47%. Comparing base (5394ed7) to head (52b11dc).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/tools/git_url_validator.py 78.04% 7 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #633      +/-   ##
==========================================
- Coverage   92.49%   92.47%   -0.03%     
==========================================
  Files         554      554              
  Lines       27836    27872      +36     
  Branches     2670     2677       +7     
==========================================
+ Hits        25748    25775      +27     
- Misses       1647     1654       +7     
- Partials      441      443       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive mitigation strategy for TOCTOU DNS rebinding vulnerabilities in git clone operations. The solution effectively addresses the time-of-check-to-time-of-use gap by pinning validated IPs for HTTPS URLs using http.curloptResolve and performing a double-resolve consistency check for SSH/SCP URLs. The changes include new Pydantic models for carrying validation results, helper functions for constructing curloptResolve values, and a dedicated function for verifying DNS consistency. Documentation in CLAUDE.md and docs/design/operations.md has been updated to reflect these security enhancements. The new functionality is thoroughly covered by unit tests, ensuring correctness and robustness.

Comment on lines +688 to +691
GIT_COMMAND_START,
hostname=validation.hostname,
note="toctou_mitigation_skipped",
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using GIT_COMMAND_START for logging when TOCTOU mitigation is skipped might be slightly misleading. GIT_COMMAND_START typically indicates that a command is about to be executed. Consider introducing a more specific log event, such as GIT_CLONE_TOCTOU_MITIGATION_SKIPPED, to clearly differentiate this scenario from an actual command initiation. This would improve log clarity and make it easier to filter relevant events.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Line 138: The note incorrectly points at git_url_validator; update the wording
to point readers to GitCloneTool (the class responsible for injecting
http.curloptResolve) instead, clarifying that git_url_validator only returns
validation data and helpers while GitCloneTool performs the actual HTTPS clone
path resolution; change the reference text to mention GitCloneTool (and
optionally its module synthorg.tools.git_tools) and explain that the
curloptResolve injection occurs there so future maintainers edit the correct
location.

In `@src/synthorg/tools/git_tools.py`:
- Around line 685-692: The branch that returns early when
validation.resolved_ips is empty is emitting GIT_COMMAND_START even though no
git subprocess is launched; change this to emit a dedicated skip event (e.g.,
GIT_MITIGATION_SKIPPED or GIT_CLONE_MITIGATION_SKIPPED) instead of
GIT_COMMAND_START in the logger.debug call inside the if not
validation.resolved_ips block, keeping the same metadata (hostname and note) and
returning args as before; add the new event constant if it doesn't exist and
update any event consumers/tests accordingly to expect the mitigation-skip event
rather than a git.command.start event.
- Around line 694-706: The DNS-pinning branch currently unconditionally injects
http.curloptResolve (in the block using validation.is_https,
build_curl_resolve_value, and logging GIT_CLONE_DNS_PINNED) but must first
verify the Git binary supports that config (support added in Git v2.37.0); add a
runtime capability check (e.g., call a helper like get_git_version or run `git
--version` and parse/compare to 2.37.0) before returning ["-c",
f"http.curloptResolve={resolve_value}", *args], and if the Git version is older,
raise/return a clear error explaining that DNS-pinning requires Git >= 2.37.0
(do not silently fall back). Ensure the check happens at clone time in the same
code path so the clone fails fast with a descriptive message when unsupported.

In `@src/synthorg/tools/git_url_validator.py`:
- Around line 310-312: Before raising the ValueError in the block that checks
"if not ips" (the code that sets msg = "ips must not be empty" then raises
ValueError), log the error with context at WARNING or ERROR level (e.g.,
logger.error or logger.warning) including the message and relevant context (the
ips variable and any request/remote info available) and then raise the
ValueError as before; ensure you use the module logger (logger =
logging.getLogger(__name__) if not already defined) and keep the log message and
the raised msg consistent.

In `@tests/unit/tools/git/test_git_clone.py`:
- Around line 341-366: The test test_mitigation_disabled_skips_pinning currently
never verifies that GitCloneTool forwards
GitCloneNetworkPolicy(dns_rebinding_mitigation=False) into
validate_clone_url_host because the stubbed _mock_validate returns
resolved_ips=() unconditionally; update the test so the validator stub asserts
it received the policy (or make _mock_validate accept an expected_policy and
raise/assert if received.policy.dns_rebinding_mitigation is not False) or
replace the stub with the real validator and mock DNS resolution; ensure the
assertion verifies that validate_clone_url_host was called with a policy whose
dns_rebinding_mitigation is False (referencing GitCloneTool,
GitCloneNetworkPolicy, validate_clone_url_host, _mock_validate, and captured).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4bdf83d7-9994-4132-9dda-de24a49d5114

📥 Commits

Reviewing files that changed from the base of the PR and between 1a36eca and 9e7d844.

📒 Files selected for processing (9)
  • CLAUDE.md
  • docs/design/operations.md
  • src/synthorg/observability/events/git.py
  • src/synthorg/tools/git_tools.py
  • src/synthorg/tools/git_url_validator.py
  • tests/unit/tools/git/conftest.py
  • tests/unit/tools/git/test_git_clone.py
  • tests/unit/tools/git/test_git_url_validator.py
  • tests/unit/tools/git/test_git_url_validator_toctou.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/synthorg/tools/git_tools.py (1)

696-709: ⚠️ Potential issue | 🟠 Major

Gate DNS pinning on Git version capability check.

The comment states "git >= 2.22" but http.curloptResolve was introduced in Git v2.37.0. On older versions, the config is silently ignored—Git will not error, but DNS pinning fails silently, leaving HTTPS clones unprotected against DNS rebinding.

Either:

  1. Add a runtime version check and fail with a clear error if Git < 2.37.0, or
  2. At minimum, correct the comment and document in operations.md that this protection requires Git >= 2.37.0
Git http.curloptResolve minimum version requirement
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/tools/git_tools.py` around lines 696 - 709, The DNS-pinning
branch currently assumes http.curloptResolve is supported but that option only
exists in Git >= 2.37.0, so update the logic that runs when validation.is_https
(the block using build_curl_resolve_value and returning ["-c",
f"http.curloptResolve={resolve_value}", *args]) to first check Git’s runtime
version (e.g., run and parse `git --version`) and require >= 2.37.0; if the
installed Git is older, either raise a clear error explaining the requirement or
log an explicit failure and abort the clone instead of silently returning the
config, and keep the GIT_CLONE_DNS_PINNED log to include version info so
operators know why DNS pinning wasn’t applied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/design/operations.md`:
- Around line 514-515: The docs incorrectly state that git >= 2.22 supports
http.curloptResolve; update the text to require git >= 2.37.0 and adjust the
sentence referencing `http.curloptResolve=host:port:ip` (and any other
occurrences of "git >= 2.22") to read "git >= 2.37.0" so the documented minimum
Git version matches the actual introduction of that feature.

In `@src/synthorg/tools/git_url_validator.py`:
- Around line 564-568: This path that returns for an invalid https port should
log a WARNING with context before returning: when raw_port is not None and
raw_port <= 0, emit a warning (using the same logger used elsewhere in this
module, e.g., logger.warning or process_logger.warning) including the clone URL
and the invalid raw_port value, then return the error string; mirror the
wording/format used in the other error paths (lines ~543-560) for consistency
while keeping the return value unchanged.

In `@tests/unit/tools/git/test_git_clone.py`:
- Around line 165-174: The helper _mock_validate currently types the inner
coroutine parameter policy as Any; update the signature so policy is typed as
GitCloneNetworkPolicy to match the real validate_clone_url_host and catch future
signature mismatches—i.e., change the async def _inner(url: str, policy: Any) ->
DnsValidationOk to use GitCloneNetworkPolicy for policy (and add/import the type
where needed) while keeping the returned DnsValidationOk and the
monkeypatch.setattr(git_tools_module, "validate_clone_url_host", _inner) logic
unchanged.

---

Duplicate comments:
In `@src/synthorg/tools/git_tools.py`:
- Around line 696-709: The DNS-pinning branch currently assumes
http.curloptResolve is supported but that option only exists in Git >= 2.37.0,
so update the logic that runs when validation.is_https (the block using
build_curl_resolve_value and returning ["-c",
f"http.curloptResolve={resolve_value}", *args]) to first check Git’s runtime
version (e.g., run and parse `git --version`) and require >= 2.37.0; if the
installed Git is older, either raise a clear error explaining the requirement or
log an explicit failure and abort the clone instead of silently returning the
config, and keep the GIT_CLONE_DNS_PINNED log to include version info so
operators know why DNS pinning wasn’t applied.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe06fc60-28ff-462b-8a45-7402c4ddf5b8

📥 Commits

Reviewing files that changed from the base of the PR and between 9e7d844 and 2360a23.

📒 Files selected for processing (9)
  • CLAUDE.md
  • docs/design/operations.md
  • src/synthorg/observability/events/git.py
  • src/synthorg/tools/git_tools.py
  • src/synthorg/tools/git_url_validator.py
  • tests/unit/tools/git/conftest.py
  • tests/unit/tools/git/test_git_clone.py
  • tests/unit/tools/git/test_git_url_validator.py
  • tests/unit/tools/git/test_git_url_validator_toctou.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/tools/git_url_validator.py`:
- Around line 562-574: The code accesses urlparse(url).port directly which can
raise ValueError for malformed ports; update the is_https branch to wrap the
port extraction in a try/except ValueError block so malformed ports are caught,
log the event via logger.warning using the existing GIT_CLONE_SSRF_BLOCKED,
hostname=normalized, port=raw_port (or the raw string), reason="invalid_port",
and return the same "Invalid port in clone URL" message; ensure port is only set
to raw_port or 443 when no exception occurs.
- Around line 303-329: The function build_curl_resolve_value currently treats
IPv6 addresses as not requiring brackets; update its implementation and
docstring to wrap any ip containing ':' in square brackets before joining into
the curl resolve string (so addresses become "[ipv6]" while IPv4 remains
unchanged), and keep the existing empty-ips check/logger behavior
(GIT_CLONE_DNS_FAILED and the ValueError) intact; ensure the returned format
from build_curl_resolve_value is "hostname:port:addr1,addr2,..." with IPv6
entries bracketed to satisfy libcurl parsing.

In `@tests/unit/tools/git/test_git_clone.py`:
- Around line 375-407: The test currently stubs verify_dns_consistency with
mock_verify that returns None but never asserts it was invoked; update the
test_scp_double_resolve_passes setup so the stub records calls (e.g. append
(hostname, expected_ips, dns_timeout) to a list or replace
verify_dns_consistency with a MagicMock) and then assert the recorded call(s)
include the expected hostname "example.com" (and/or expected_ips), or
alternately add a separate assertion or negative-case where mock_verify returns
a non-None string to exercise the TOCTOU mitigation; reference
verify_dns_consistency and mock_verify when making the changes and add an
assertion after tool.execute that the mock was called with the SCP hostname.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 49736e21-32e9-4c67-8b63-a90ce362f718

📥 Commits

Reviewing files that changed from the base of the PR and between 2360a23 and 3c61c89.

📒 Files selected for processing (2)
  • src/synthorg/tools/git_url_validator.py
  • tests/unit/tools/git/test_git_clone.py

Aureliolo and others added 5 commits March 20, 2026 08:39
HTTPS URLs: pin git to validated IPs via -c http.curloptResolve
(git >= 2.22). SSH/SCP URLs: double-resolve with subset comparison
immediately before execution. Both mitigations configurable via
GitCloneNetworkPolicy.dns_rebinding_mitigation (default True,
disable for CDN/geo-DNS environments).

Closes #508

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DnsValidationOk.hostname uses NotBlankStr, port has gt=0/le=65535
- Rename _build_curl_resolve_value to public (cross-module API)
- Add empty ips precondition check in build_curl_resolve_value
- Extract _ok helper to reduce validate_clone_url_host length
- Remove redundant "://" condition in HTTPS port extraction
- Add debug log when TOCTOU mitigation is skipped
- Split TOCTOU tests into test_git_url_validator_toctou.py (<800 lines)
- Deduplicate mock_run_git in test_git_clone.py
- Update CLAUDE.md tools/ description with TOCTOU mitigation
- Add Git Clone SSRF Prevention section in operations design spec

Pre-reviewed by 11 agents, 13 findings addressed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…bbit

- Add GIT_CLONE_TOCTOU_SKIPPED event constant (replaces misused GIT_COMMAND_START)
- Move DnsValidationOk out of TYPE_CHECKING guard to runtime import
- Add control-char check on hostname before curloptResolve interpolation
- Guard against port 0 from URL raising unhandled ValidationError
- Document SSH TOCTOU residual window as known limitation in module docstring
- Document dns_resolution_timeout doubled for SSH in field description
- Document block_private_ips=False implicitly disabling TOCTOU mitigation
- Improve verify_dns_consistency error message (subset semantics)
- Fix _resolve_dns return type ordering for consistency
- Add log before ValueError raise in build_curl_resolve_value
- Use `is not None` instead of truthiness for rebind_err check
- Update execute docstring validation order (add TOCTOU step)
- Add comment explaining ValueError unreachability in HTTPS path
- Clarify CLAUDE.md tools/ description re curloptResolve injection site
- Extract _dns_result helpers to conftest.py (DRY across test files)
- Fix module docstring (unit + integration, not just integration)
- Fix mock return type annotations to match real signatures
- Fix mock _inner parameter types (GitCloneTool, Path | None)
- Add SCP-like URL TOCTOU wiring test
- Add verify_dns_consistency timeout re-resolution test
- Fix test_mitigation_disabled to verify policy forwarding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add warning log before returning invalid port error (coding guideline)
- Type _mock_validate policy param as GitCloneNetworkPolicy instead of Any

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Wrap urlparse().port in try/except ValueError for malformed ports
- Bracket IPv6 addresses in curloptResolve values per libcurl convention
- Assert verify_dns_consistency mock was called with expected hostname in
  SCP double-resolve test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
docs/design/operations.md (1)

514-515: ⚠️ Potential issue | 🟡 Minor

Correct the Git version requirement.

The documentation states "git >= 2.22" but http.curloptResolve was introduced in Git 2.37.0. A previous review comment flagged this same issue.

📝 Suggested fix
 - **HTTPS URLs:** Validated IPs are pinned via `git -c http.curloptResolve=host:port:ip`
-  (git >= 2.22), so git uses the same addresses the validator checked.
+  (git >= 2.37), so git uses the same addresses the validator checked.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/operations.md` around lines 514 - 515, Update the documentation
sentence that references git version support for http.curloptResolve: replace
"git >= 2.22" with the correct minimum version "git >= 2.37.0" wherever the
clause mentioning `http.curloptResolve` appears (the line describing HTTPS URLs
pinned via `git -c http.curloptResolve=host:port:ip`) so the documented Git
requirement accurately reflects when `http.curloptResolve` was introduced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/tools/git_tools.py`:
- Around line 696-709: Update the incorrect Git version note from "git >= 2.22"
to "git >= 2.37.0" in both places: the inline comment above the
http.curloptResolve usage in the code path that builds the curl resolve flag
(referenced via validation, build_curl_resolve_value, and GIT_CLONE_DNS_PINNED
in git_tools.py) and the module docstring in git_url_validator.py; additionally
add a runtime check (e.g., in the module that invokes git operations or in a
helper function used by git_url_validator) that queries the git version and
raises/returns an explicit error if git < 2.37.0 to avoid silent no-ops.

In `@tests/unit/tools/git/test_git_clone.py`:
- Around line 250-281: Add a `verify_calls` capture and assertion to the
`test_ssh_double_resolve_passes` test so it mirrors
`test_scp_double_resolve_passes`: create a list (e.g., verify_calls:
list[tuple[str, frozenset[str]]]) and a `mock_verify` that appends (hostname,
expected_ips) to it, monkeypatch `verify_dns_consistency` to that `mock_verify`,
run the test using `GitCloneTool(...).execute(...)` as already done, then assert
`verify_calls` contains the expected tuple for "example.com" to prove the TOCTOU
hook ran for SSH URLs too.

---

Duplicate comments:
In `@docs/design/operations.md`:
- Around line 514-515: Update the documentation sentence that references git
version support for http.curloptResolve: replace "git >= 2.22" with the correct
minimum version "git >= 2.37.0" wherever the clause mentioning
`http.curloptResolve` appears (the line describing HTTPS URLs pinned via `git -c
http.curloptResolve=host:port:ip`) so the documented Git requirement accurately
reflects when `http.curloptResolve` was introduced.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 164b0ec3-34e8-4066-8fce-05a0485e01df

📥 Commits

Reviewing files that changed from the base of the PR and between 3c61c89 and 4da0701.

📒 Files selected for processing (9)
  • CLAUDE.md
  • docs/design/operations.md
  • src/synthorg/observability/events/git.py
  • src/synthorg/tools/git_tools.py
  • src/synthorg/tools/git_url_validator.py
  • tests/unit/tools/git/conftest.py
  • tests/unit/tools/git/test_git_clone.py
  • tests/unit/tools/git/test_git_url_validator.py
  • tests/unit/tools/git/test_git_url_validator_toctou.py

Comment on lines +696 to +709
if validation.is_https:
# Pin git to validated IPs via curloptResolve (git >= 2.22).
# resolved_ips is guaranteed non-empty here (guard above).
resolve_value = build_curl_resolve_value(
validation.hostname,
validation.port or 443,
validation.resolved_ips,
)
logger.info(
GIT_CLONE_DNS_PINNED,
hostname=validation.hostname,
resolve_value=resolve_value,
)
return ["-c", f"http.curloptResolve={resolve_value}", *args]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

What Git version introduced http.curloptResolve config option?

💡 Result:

http.curloptResolve was introduced in Git v2.37.0. [1][2]


🏁 Script executed:

# Check the actual content at the specified lines in the file
cat -n src/synthorg/tools/git_tools.py | sed -n '696,709p'

Repository: Aureliolo/synthorg

Length of output: 762


🏁 Script executed:

# Search for any Git version checks in the codebase
rg "version|2\.37|2\.22" src/synthorg/tools/ -i -A 2 -B 2

Repository: Aureliolo/synthorg

Length of output: 5677


🏁 Script executed:

# Look for any Git version requirement documentation or validation
rg "curloptResolve|curl.*resolve" src/synthorg/tools/ -i -A 3 -B 3

Repository: Aureliolo/synthorg

Length of output: 7377


🏁 Script executed:

# Search for any Git version requirement enforcement or checks
rg "git.*version|version.*git|subprocess.*git|check.*git" src/synthorg/ -i -A 2 -B 2

Repository: Aureliolo/synthorg

Length of output: 4112


🏁 Script executed:

# Look at the git_url_validator module docstring to see what it says about version
head -100 src/synthorg/tools/git_url_validator.py

Repository: Aureliolo/synthorg

Length of output: 4264


🏁 Script executed:

# Search for any documentation on Git requirements
find docs/ -type f -name "*.md" 2>/dev/null | head -20 | xargs grep -l "git.*version\|2\.37\|2\.22" 2>/dev/null

Repository: Aureliolo/synthorg

Length of output: 46


Correct the Git version requirement in two locations: inline comment and module docstring.

The comment at line 697 and the git_url_validator.py module docstring both incorrectly state git >= 2.22. The http.curloptResolve config option was introduced in Git v2.37.0, not 2.22. Older Git versions silently ignore this config, leaving HTTPS clones unprotected.

Fix both occurrences:

  • src/synthorg/tools/git_tools.py line 697: change "git >= 2.22" to "git >= 2.37.0"
  • src/synthorg/tools/git_url_validator.py module docstring: change "git >= 2.22" to "git >= 2.37.0"

Consider adding a runtime Git version check that fails explicitly if Git < 2.37.0 rather than allowing silent failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/tools/git_tools.py` around lines 696 - 709, Update the incorrect
Git version note from "git >= 2.22" to "git >= 2.37.0" in both places: the
inline comment above the http.curloptResolve usage in the code path that builds
the curl resolve flag (referenced via validation, build_curl_resolve_value, and
GIT_CLONE_DNS_PINNED in git_tools.py) and the module docstring in
git_url_validator.py; additionally add a runtime check (e.g., in the module that
invokes git operations or in a helper function used by git_url_validator) that
queries the git version and raises/returns an explicit error if git < 2.37.0 to
avoid silent no-ops.

…tions

- Fix http.curloptResolve version from 2.22 to 2.37.0 (feature was
  introduced in git 2.37.0, not 2.22)
- Note sandbox ships git 2.39+ (Debian bookworm) so no runtime version
  check is needed -- we control the container image
- Add verify_calls capture and assertions to test_ssh_double_resolve_passes
  to prove the TOCTOU hook ran for SSH URLs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 20, 2026 07:59 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit 1846f6e into main Mar 20, 2026
30 of 33 checks passed
@Aureliolo Aureliolo deleted the fix/toctou-dns-rebinding branch March 20, 2026 08:22
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 20, 2026 08:22 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 20, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.10](v0.3.9...v0.3.10)
(2026-03-20)


### Bug Fixes

* **ci:** generate required secrets in DAST workflow
([#623](#623))
([6ae297f](6ae297f))
* **cli:** doctor image check reads compose file and fix API docs URL
([#625](#625))
([5202e53](5202e53))
* **engine:** sanitize error messages in checkpoint reconciliation and
compaction summaries
([#632](#632))
([5394ed7](5394ed7))
* mitigate TOCTOU DNS rebinding gap in git clone SSRF prevention
([#633](#633))
([1846f6e](1846f6e))
* resolve post-startup log loss, add provider model discovery, and
improve setup wizard UX
([#634](#634))
([2df8d11](2df8d11))


### Maintenance

* bump https://github.com/astral-sh/ruff-pre-commit from v0.15.6 to
0.15.7 ([#628](#628))
([c641d2c](c641d2c))
* bump python from `584e89d` to `fb83750` in /docker/backend
([#627](#627))
([1a36eca](1a36eca))
* bump python from `584e89d` to `fb83750` in /docker/sandbox
([#629](#629))
([fd3e69a](fd3e69a))
* bump the minor-and-patch group across 2 directories with 3 updates
([#630](#630))
([67d14c4](67d14c4))
* bump the minor-and-patch group with 2 updates
([#631](#631))
([2e51b60](2e51b60))
* **ci:** add timeout-minutes, harden fuzz script, extend CVE audit
([#626](#626))
([25420e2](25420e2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: mitigate TOCTOU DNS rebinding gap in git clone SSRF prevention

1 participant