Skip to content

fix: reject dot segments in request paths - #306

Open
RishavTiwari25 wants to merge 1 commit into
resend:mainfrom
RishavTiwari25:fix/reject-dot-segments
Open

RishavTiwari25 wants to merge 1 commit into
resend:mainfrom
RishavTiwari25:fix/reject-dot-segments

Conversation

@RishavTiwari25

Copy link
Copy Markdown

Problem

Path parameters go into the URL as they are passed (f"/domains/{domain_id}"). Both HTTP clients the SDK uses resolve . and .. segments before sending the request:

>>> urllib3.util.parse_url("https://api.resend.com/domains/../api-keys/k").path
'/api-keys/k'
>>> httpx.URL("https://api.resend.com/domains/../api-keys/k").path
'/api-keys/k'

So when an app passes user input as an id, the call can leave its own endpoint:

  • resend.Emails.cancel("..") sends POST /cancel
  • resend.Domains.remove("../api-keys/KEY_ID") sends DELETE /api-keys/KEY_ID, which deletes an API key

resend-node fixed the same problem in resend/resend-node#1139. This PR ports that fix to Python.

Fix

Request.perform() and AsyncRequest.perform() now call ensure_no_dot_segments(self.path) before making the request. If any path segment is . or .., it raises ValueError("Path parameters cannot be .or...") and nothing is sent. The percent-encoded forms (%2E%2E, .%2e) count too, because servers decode them into the same dot segments. The check reuses the regex from the Node SDK and ignores the query string and the fragment.

I used ValueError because the ResendError docstring says that client-side argument errors raise ValueError, and Webhooks.verify already does the same. I can switch it to a ResendError subclass if you'd rather match the Node invalid_parameter error.

Nothing that works today changes. A dot segment always reached the wrong endpoint. Ids that only contain dots (example.com, ..., john.doe@example.com) are still sent as they are.

Tests

tests/dot_segment_path_test.py:

  • Emails.get(".."), Emails.cancel(".."), Domains.remove("."), Domains.remove("../api-keys/key_id") and Domains.get("%2E%2E") raise and never reach the HTTP client. The same goes for Domains.remove_async. All six fail on main.
  • Dotted ids are still sent.
  • has_dot_segment gets the same table of cases as has-dot-segment.spec.ts in resend-node.
pytest tests              # 870 passed
black / isort / flake8 --max-line-length=130 ./resend/   # clean
mypy --ignore-missing-imports resend/                    # no issues

requests/urllib3 and httpx resolve '.' and '..' path segments before
sending, so an id such as '../api-keys/key_id' passed to
Domains.remove() sends DELETE /api-keys/key_id. Raise ValueError
before any request is made when a path segment is '.' or '..',
including the percent-encoded forms. Mirrors resend-node#1139.
@github-actions github-actions Bot added the linear-synced PR has been synced to Linear label Oct 26, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

linear-synced PR has been synced to Linear

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant