Skip to content

refactor: extract SSRF-safe dial logic into core/network package and replace duplicate implementations in fetch.go and skills_serving.go - #5246

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
07-14-refactor_share_ssrf-safe_outbound_dialer_via_core_network
Jul 17, 2026
Merged

refactor: extract SSRF-safe dial logic into core/network package and replace duplicate implementations in fetch.go and skills_serving.go#5246
Pratham-Mishra04 merged 1 commit into
devfrom
07-14-refactor_share_ssrf-safe_outbound_dialer_via_core_network

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

The SSRF protection logic (IP classification and safe dial context) was duplicated across core/providers/utils/fetch.go and transports/bifrost-http/handlers/skills_serving.go, with the skills handler using a weaker isPrivateIP check that missed CGNAT, IPv6 transition addresses, site-local IPv6, broadcast, and interface-local multicast. This PR extracts a single, hardened implementation into core/network and replaces both call sites with it.

Changes

  • Added core/network/ssrf.go with IsPublicIP and SSRFSafeDialContext as the canonical SSRF-safe dialing primitives. IsPublicIP blocks loopback, RFC 1918, CGNAT (RFC 6598), link-local, site-local (RFC 3879), multicast, broadcast, unspecified, and IPv4 addresses smuggled inside IPv6 transition representations (6to4 2002::/16, NAT64 64:ff9b::/96, NAT64 local-use 64:ff9b:1::/48). SSRFSafeDialContext resolves the host, validates every returned IP against IsPublicIP, and dials the first validated IP directly — eliminating the DNS-rebinding TOCTOU window.
  • Removed the private isPublicIP/embeddedIPv4 functions from fetch.go and the weaker ssrfSafeDialContext/isPrivateIP functions from skills_serving.go; both now call network.SSRFSafeDialContext.
  • Moved tests from core/providers/utils/fetch_test.go into core/network/ssrf_test.go, expanded them to cover the additional blocked ranges (CGNAT, broadcast, interface-local multicast, IPv6 site-local), and added dial-path tests for DNS error propagation, empty resolution, missing port, re-resolution per dial, and the DNS-rebinding TOCTOU guard.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./core/network/... ./core/providers/utils/... ./transports/bifrost-http/...

The TestSSRFSafeDialContextBlocksLoopbackLiteral test exercises the exported constructor end-to-end against the real resolver and will fail if the dial gate is not applied. All other tests use a fakeResolver and require no network access.

Screenshots/Recordings

N/A

Breaking changes

  • Yes
  • No

Related issues

N/A

Security considerations

The skills handler previously used isPrivateIP, which did not block CGNAT addresses (used as pod IPs on EKS), IPv6 site-local (fec0::/10), broadcast (255.255.255.255), interface-local multicast, or IPv4 addresses wrapped in 6to4/NAT64 IPv6 representations. An admin-controlled source_url or a user-controlled fetch URL could have reached internal infrastructure through any of those gaps. The consolidated IsPublicIP closes all of them. The DNS-rebinding TOCTOU fix (dial the already-resolved IP directly) was present in fetch.go but absent in the skills handler; it is now enforced in both paths via the shared implementation.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Pratham-Mishra04 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Security Enhancements
    • Strengthened protection against server-side request forgery (SSRF) when fetching remote URLs.
    • Blocks connections to private, loopback, link-local, multicast, reserved, and other non-public IP addresses.
    • Validates DNS results before connecting and prevents DNS rebinding during requests.
    • Applied consistent SSRF protection across URL fetching and skills-related HTTP requests.
  • Tests
    • Added coverage for public IP validation, DNS resolution failures, malformed addresses, and blocked connection scenarios.

Walkthrough

Changes

SSRF protection

Layer / File(s) Summary
IP safety validation
core/network/ssrf.go
Adds public-IP classification and unwraps supported IPv6 transition addresses before applying unsafe-range checks.
Validated DNS dialing
core/network/ssrf.go, core/network/ssrf_test.go
Adds resolver-injected safe dialing that validates every DNS result, dials the validated IP directly, and tests blocking, rebinding protection, and error paths.
HTTP client integration
core/providers/utils/fetch.go, transports/bifrost-http/handlers/skills_serving.go
Replaces duplicated local SSRF dialers with network.SSRFSafeDialContext.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant SSRFSafeDialContext
  participant Resolver
  participant netDialer
  Caller->>SSRFSafeDialContext: dial host and port
  SSRFSafeDialContext->>Resolver: resolve hostname
  Resolver-->>SSRFSafeDialContext: resolved IP addresses
  SSRFSafeDialContext->>SSRFSafeDialContext: validate every IP with IsPublicIP
  SSRFSafeDialContext->>netDialer: dial first validated IP and port
  netDialer-->>Caller: connection or dial error
Loading

Suggested reviewers: akshaydeo, danpiths

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main refactor: centralizing SSRF-safe dial logic and replacing duplicate implementations.
Description check ✅ Passed The description matches the template closely and covers summary, changes, testing, security, and checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 07-14-refactor_share_ssrf-safe_outbound_dialer_via_core_network

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

The standalone transport build needs dependency alignment before merging.

  • The shared SSRF logic preserves the existing provider fetch behavior.
  • The skills path gains stricter address checks and direct validated-IP dialing.
  • The transport module's pinned core version does not provide the new dialer API.

transports/go.mod must reference a core release containing SSRFSafeDialContext.

Important Files Changed

Filename Overview
core/network/ssrf.go Adds the shared IP classifier and DNS-rebinding-safe dial function.
core/network/ssrf_test.go Covers restricted address ranges, resolver failures, repeated resolution, and direct IP dialing.
core/providers/utils/fetch.go Replaces equivalent local SSRF logic with the shared core implementation.
transports/bifrost-http/handlers/skills_serving.go Uses the stronger shared dialer, but the transport module remains pinned to a core release without the new symbol.

Reviews (1): Last reviewed commit: "refactor: share SSRF-safe outbound diale..." | Re-trigger Greptile

Comment thread transports/bifrost-http/handlers/skills_serving.go

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/network/ssrf.go`:
- Around line 62-68: Update embeddedIPv4 and its callers to accept the active
operator-configured NAT64/Pref64 prefixes, extract the embedded IPv4 address for
those prefixes, and apply the existing SSRF validation to that address before
allowing the request. Preserve handling for 6to4 and the standard NAT64
prefixes, and add a regression test proving a custom prefix targeting a
private/IMDS IPv4 address is rejected.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a0a9eef1-bf00-4ab9-80c8-08e6dc53d4a4

📥 Commits

Reviewing files that changed from the base of the PR and between c0909f9 and 968d6bf.

📒 Files selected for processing (5)
  • core/network/ssrf.go
  • core/network/ssrf_test.go
  • core/providers/utils/fetch.go
  • core/providers/utils/fetch_test.go
  • transports/bifrost-http/handlers/skills_serving.go
💤 Files with no reviewable changes (1)
  • core/providers/utils/fetch_test.go

Comment thread core/network/ssrf.go

Pratham-Mishra04 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Jul 17, 8:36 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 17, 8:37 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04
Pratham-Mishra04 merged commit 18942c3 into dev Jul 17, 2026
14 of 15 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 07-14-refactor_share_ssrf-safe_outbound_dialer_via_core_network branch July 17, 2026 08:37
akshaydeo pushed a commit that referenced this pull request Jul 17, 2026
…d replace duplicate implementations in `fetch.go` and `skills_serving.go` (#5246)

## Summary

The SSRF protection logic (IP classification and safe dial context) was duplicated across `core/providers/utils/fetch.go` and `transports/bifrost-http/handlers/skills_serving.go`, with the skills handler using a weaker `isPrivateIP` check that missed CGNAT, IPv6 transition addresses, site-local IPv6, broadcast, and interface-local multicast. This PR extracts a single, hardened implementation into `core/network` and replaces both call sites with it.

## Changes

- Added `core/network/ssrf.go` with `IsPublicIP` and `SSRFSafeDialContext` as the canonical SSRF-safe dialing primitives. `IsPublicIP` blocks loopback, RFC 1918, CGNAT (RFC 6598), link-local, site-local (RFC 3879), multicast, broadcast, unspecified, and IPv4 addresses smuggled inside IPv6 transition representations (6to4 `2002::/16`, NAT64 `64:ff9b::/96`, NAT64 local-use `64:ff9b:1::/48`). `SSRFSafeDialContext` resolves the host, validates every returned IP against `IsPublicIP`, and dials the first validated IP directly — eliminating the DNS-rebinding TOCTOU window.
- Removed the private `isPublicIP`/`embeddedIPv4` functions from `fetch.go` and the weaker `ssrfSafeDialContext`/`isPrivateIP` functions from `skills_serving.go`; both now call `network.SSRFSafeDialContext`.
- Moved tests from `core/providers/utils/fetch_test.go` into `core/network/ssrf_test.go`, expanded them to cover the additional blocked ranges (CGNAT, broadcast, interface-local multicast, IPv6 site-local), and added dial-path tests for DNS error propagation, empty resolution, missing port, re-resolution per dial, and the DNS-rebinding TOCTOU guard.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/network/... ./core/providers/utils/... ./transports/bifrost-http/...
```

The `TestSSRFSafeDialContextBlocksLoopbackLiteral` test exercises the exported constructor end-to-end against the real resolver and will fail if the dial gate is not applied. All other tests use a `fakeResolver` and require no network access.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

The skills handler previously used `isPrivateIP`, which did not block CGNAT addresses (used as pod IPs on EKS), IPv6 site-local (`fec0::/10`), broadcast (`255.255.255.255`), interface-local multicast, or IPv4 addresses wrapped in 6to4/NAT64 IPv6 representations. An admin-controlled `source_url` or a user-controlled fetch URL could have reached internal infrastructure through any of those gaps. The consolidated `IsPublicIP` closes all of them. The DNS-rebinding TOCTOU fix (dial the already-resolved IP directly) was present in `fetch.go` but absent in the skills handler; it is now enforced in both paths via the shared implementation.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akshaydeo pushed a commit that referenced this pull request Jul 18, 2026
…d replace duplicate implementations in `fetch.go` and `skills_serving.go` (#5246)

## Summary

The SSRF protection logic (IP classification and safe dial context) was duplicated across `core/providers/utils/fetch.go` and `transports/bifrost-http/handlers/skills_serving.go`, with the skills handler using a weaker `isPrivateIP` check that missed CGNAT, IPv6 transition addresses, site-local IPv6, broadcast, and interface-local multicast. This PR extracts a single, hardened implementation into `core/network` and replaces both call sites with it.

## Changes

- Added `core/network/ssrf.go` with `IsPublicIP` and `SSRFSafeDialContext` as the canonical SSRF-safe dialing primitives. `IsPublicIP` blocks loopback, RFC 1918, CGNAT (RFC 6598), link-local, site-local (RFC 3879), multicast, broadcast, unspecified, and IPv4 addresses smuggled inside IPv6 transition representations (6to4 `2002::/16`, NAT64 `64:ff9b::/96`, NAT64 local-use `64:ff9b:1::/48`). `SSRFSafeDialContext` resolves the host, validates every returned IP against `IsPublicIP`, and dials the first validated IP directly — eliminating the DNS-rebinding TOCTOU window.
- Removed the private `isPublicIP`/`embeddedIPv4` functions from `fetch.go` and the weaker `ssrfSafeDialContext`/`isPrivateIP` functions from `skills_serving.go`; both now call `network.SSRFSafeDialContext`.
- Moved tests from `core/providers/utils/fetch_test.go` into `core/network/ssrf_test.go`, expanded them to cover the additional blocked ranges (CGNAT, broadcast, interface-local multicast, IPv6 site-local), and added dial-path tests for DNS error propagation, empty resolution, missing port, re-resolution per dial, and the DNS-rebinding TOCTOU guard.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/network/... ./core/providers/utils/... ./transports/bifrost-http/...
```

The `TestSSRFSafeDialContextBlocksLoopbackLiteral` test exercises the exported constructor end-to-end against the real resolver and will fail if the dial gate is not applied. All other tests use a `fakeResolver` and require no network access.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

The skills handler previously used `isPrivateIP`, which did not block CGNAT addresses (used as pod IPs on EKS), IPv6 site-local (`fec0::/10`), broadcast (`255.255.255.255`), interface-local multicast, or IPv4 addresses wrapped in 6to4/NAT64 IPv6 representations. An admin-controlled `source_url` or a user-controlled fetch URL could have reached internal infrastructure through any of those gaps. The consolidated `IsPublicIP` closes all of them. The DNS-rebinding TOCTOU fix (dial the already-resolved IP directly) was present in `fetch.go` but absent in the skills handler; it is now enforced in both paths via the shared implementation.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
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.

2 participants