Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ The e2e tests require GitHub credentials. There are three ways to provide them:

If only `E2E_GITHUB_USERNAME` and a password source are available, `make e2e-test` will automatically generate a session file before running tests. See `make help` for all available targets.

### Secure HTTP clients

**Use the `fetch` package:** `internal/fetch/` provides an SSRF-hardened HTTP client (`FetchURL`). New code that fetches external URLs should use it rather than building a custom `http.Client`.

**If you must build a custom client:** these properties are required:

- **HTTPS-only** — reject `http://` URLs.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] completeness

Proxy handling omission: Issue #376 required Transport.Proxy = nil documentation. The PR omits it. The canonical FetchURL is safe (new http.Transport{} defaults Proxy to nil), but custom clients starting from http.DefaultTransport.Clone() would inherit http.ProxyFromEnvironment, enabling proxy-based SSRF bypass.

Suggested fix: Add a bullet to the custom-client checklist: 'Disable proxy — set Transport.Proxy to nil to prevent requests from being routed through HTTP_PROXY/HTTPS_PROXY environment variables.'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] completeness

Port restriction omission: The canonical FetchURL enforces a port allowlist (defaults to 443 only). The custom-client checklist does not mention port validation, allowing custom clients to accept arbitrary ports.

Suggested fix: Add a bullet: 'Port restriction — restrict to port 443 (or an explicit allowlist) to prevent connections on unexpected ports.'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] completeness

Double-encoding rejection omission: FetchURL rejects URLs containing %25 (double-encoded percent signs) as an anti-bypass measure. The custom-client checklist does not mention this protection.

Suggested fix: Add a bullet: 'Double-encoding rejection — reject URLs containing %25 to prevent percent-encoding bypass attacks.'

- **DNS pre-resolution with IP validation** — resolve the hostname and validate the IP using `netutil.IsInternal()` / `netutil.CheckIP()` from `internal/netutil/` to reject loopback, link-local, private, and other reserved addresses.
- **IP-pinned `DialContext`** — use a custom `DialContext` that dials the pre-validated IP to prevent DNS rebinding between resolution and connection.
- **Redirect blocking** — set `CheckRedirect` to block redirects entirely or validate that redirects stay HTTPS-only and pass IP validation.
- **Explicit timeout** — 30 s default; never leave the zero value.
- **Response body limit** — wrap response bodies with `io.LimitReader` (10 MB default).
- **Domain allowlisting** — when the set of valid hosts is known, restrict to that set.
- **Disable proxy** — set `Transport.Proxy` to `nil` to prevent requests from being routed through `HTTP_PROXY`/`HTTPS_PROXY` environment variables.

**Canonical implementation:** `internal/fetch/fetch.go` (`FetchURL`) and `internal/netutil/ip.go` (`CheckIP`, `IsInternal`).

**Prohibited patterns:** Do not use `http.Get`, `http.DefaultClient`, or `&http.Client{}` with no timeout or SSRF protections for any code that contacts URLs from configuration or user input.

## Forge abstraction

All git forge operations (GitHub API calls, PR comments, issue creation, workflow dispatch, etc.) **must** go through the `forge.Client` interface defined in `internal/forge/forge.go`. This is a fundamental architectural rule — the codebase supports multiple forges (GitHub, GitLab, Forgejo) and direct coupling to any single forge breaks the abstraction.
Expand Down
Loading