Skip to content

fix(connect): resolve relative Location headers on redirect#216

Merged
ian-flores merged 2 commits intomainfrom
fix-connect-relative-redirects
Apr 22, 2026
Merged

fix(connect): resolve relative Location headers on redirect#216
ian-flores merged 2 commits intomainfrom
fix-connect-relative-redirects

Conversation

@ian-flores
Copy link
Copy Markdown
Collaborator

Summary

  • ConnectClient.fetch_content raised httpcore.UnsupportedProtocol when Connect returned a relative Location header (e.g. /content/{guid}/notebook.html) because the raw path was handed to httpx.get.
  • Fix: resolve each Location value against str(resp.url) with urljoin before the same-origin check and the follow-up GET.
  • Same-origin safety is preserved — cross-origin redirects still short-circuit without leaking the API key.

Test plan

  • uv run ruff check src/ selftests/
  • uv run ruff format --check src/ selftests/
  • uv run pytest selftests/test_clients_connect.py -v — 2 new tests pass (relative redirect followed; cross-origin redirect blocked)
  • uv run pytest selftests/ -q — 312 passed, 1 known timing flake (test_1k_users)
  • vip verify --connect-url https://pub.ganso.lab.staging.posit.team --categories connect --filter "deploy and not rmarkdown" against ganso01-staging — 6 deploy tests pass (jupyter, quarto, plumber, fastapi, dash, gitbacked, all of which call fetch_content). Shiny timed out during packrat-restore, unrelated to this change.

Fixes #213

Copilot AI review requested due to automatic review settings April 22, 2026 18:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes ConnectClient.fetch_content failing on redirects when Posit Connect returns a relative Location header by resolving redirect targets to absolute URLs before performing the safety check and follow-up request.

Changes:

  • Resolve relative redirect Location values using urljoin(str(resp.url), location) before parsing and fetching.
  • Add selftests covering relative redirects (followed) and cross-origin redirects (blocked).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/vip/clients/connect.py Resolves relative redirect locations to absolute URLs before same-origin gating and follow-up GET.
selftests/test_clients_connect.py Adds regression tests for relative redirect handling and cross-origin redirect blocking.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +291 to +296
target = urlparse(absolute_location)
# Only follow redirects to the same origin.
if target.hostname and target.hostname != origin.hostname:
break
resp = httpx.get(
location,
absolute_location,
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

The redirect “same-origin” check only compares target.hostname to origin.hostname. This still allows redirects that change scheme (e.g. https → http) or port on the same host, which can undermine the stated goal of avoiding API key leakage (e.g., key sent over plain HTTP or to a different service bound to another port). Consider enforcing full origin equality (scheme + hostname + port/netloc), and optionally restricting redirects to http/https schemes only before issuing the follow-up GET.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — addressed in e692c2f. The origin check now requires full (scheme, hostname, port) equality with default-port normalization, and rejects non-http(s) schemes. Added selftests for scheme downgrade, port change, file:// scheme, and explicit :443 normalization.

@github-actions
Copy link
Copy Markdown

📸 Preview Screenshots for PR #216

8 screenshots captured from the preview deployments. All pages rendered successfully.


🌐 Website Preview

Preview URL: https://posit-dev.github.io/vip/pr-preview-site/pr-216/

Home

Getting Started

Shiny App

Tests

Feature Matrix

Report Page


📊 Report Preview

Preview URL: https://posit-dev.github.io/vip/pr-preview/pr-216/

Home (viewport)

Details Page


Source URL Status
Website Home https://posit-dev.github.io/vip/pr-preview-site/pr-216/
Website Getting Started https://posit-dev.github.io/vip/pr-preview-site/pr-216/getting-started/
Website Shiny App https://posit-dev.github.io/vip/pr-preview-site/pr-216/shiny-app/
Website Tests https://posit-dev.github.io/vip/pr-preview-site/pr-216/tests/
Website Feature Matrix https://posit-dev.github.io/vip/pr-preview-site/pr-216/feature-matrix/
Website Report https://posit-dev.github.io/vip/pr-preview-site/pr-216/report/
Report Home https://posit-dev.github.io/vip/pr-preview/pr-216/
Report Details https://posit-dev.github.io/vip/pr-preview/pr-216/details.html

Failures: None

Generated by Capture preview screenshots for PRs · ● 1.6M ·

@ian-flores ian-flores marked this pull request as ready for review April 22, 2026 19:05
@ian-flores ian-flores merged commit 7a163e5 into main Apr 22, 2026
20 checks passed
@ian-flores ian-flores deleted the fix-connect-relative-redirects branch April 22, 2026 19:05
@github-actions
Copy link
Copy Markdown

PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-04-22 19:06 UTC

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(connect): fetch_content fails on relative Location redirect headers

2 participants