Skip to content

Revalidate fetch redirects against network policy (#647) - #667

Open
leynos wants to merge 3 commits into
mainfrom
issue-647-revalidate-every-fetch-redirect-against-networkpolicy
Open

Revalidate fetch redirects against network policy (#647)#667
leynos wants to merge 3 commits into
mainfrom
issue-647-revalidate-every-fetch-redirect-against-networkpolicy

Conversation

@leynos

@leynos leynos commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

This branch makes NetworkPolicy an invariant of every outbound fetch
hop, preventing an allowed origin from redirecting a manifest request to a
blocked, non-allowlisted, or disallowed-scheme destination. It disables ureq
automatic redirects, validates each resolved target before connection, bounds
chains, detects loops, and redacts redirect diagnostics.

Closes #647.

Review walkthrough

Validation

  • make check-fmt: passed
  • make lint: passed
  • make doc-coverage: passed (99.14%)
  • make test: passed (2,820 tests, 3 skipped, plus doctests)
  • make markdownlint: passed
  • make nixie: passed

References

Summary by Sourcery

Enforce NetworkPolicy on every fetch redirect hop before opening the next connection.

New Features:

  • Apply network policy validation to every outbound fetch redirect destination.
  • Add bounded redirect handling with relative URL resolution, loop detection, credential stripping, and distinct diagnostics.
  • Preserve original fetch URLs as cache identities while validating complete redirect chains before caching responses.

Bug Fixes:

  • Prevent allowed endpoints from redirecting fetch requests to blocked, non-allowlisted, or disallowed-scheme destinations before a connection is made.

Enhancements:

  • Add policy-aware redirect telemetry with redacted URL and credential information.
  • Expand HTTP test fixtures to support response sequences and request counting.
  • Document the redirect security decision, behavior, and cache semantics.

Documentation:

  • Document per-hop redirect policy enforcement, redirect limits, diagnostics, and cache behavior in user and developer references.
  • Record the redirect security decision in ADR-020 and mark the redirect policy bypass as remediated in the network security audit.

Tests:

  • Add unit, integration, cache, telemetry, and fixture tests covering allowed redirects, denied targets, loops, redirect limits, and zero requests to rejected destinations.

Chores:

  • Add localized message keys for redirect validation failures.

@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 13 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

This review ran on the open-source allowance, not this organization's plan, because the pull request author doesn't have an assigned seat. Waiting won't change this — ask an organization admin to assign them a seat, or add seats in Billing if every seat is already assigned, then retry.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 5bbad72e-cda9-409c-a4d4-9a95f03a2061

📥 Commits

Reviewing files that changed from the base of the PR and between 924cb21 and aa00f2c.

📒 Files selected for processing (53)
  • docs/adr-020-revalidate-fetch-redirects.md
  • docs/contents.md
  • docs/developers-guide.md
  • docs/netsuke-design.md
  • docs/security-network-command-audit.md
  • docs/users-guide.md
  • locales/ar/messages.ftl
  • locales/cs/messages.ftl
  • locales/cy/messages.ftl
  • locales/da/messages.ftl
  • locales/de/messages.ftl
  • locales/el/messages.ftl
  • locales/en-GB/messages.ftl
  • locales/en-US/messages.ftl
  • locales/es-419/messages.ftl
  • locales/es-ES/messages.ftl
  • locales/fa/messages.ftl
  • locales/fi/messages.ftl
  • locales/fr/messages.ftl
  • locales/gd/messages.ftl
  • locales/he/messages.ftl
  • locales/hi/messages.ftl
  • locales/hu/messages.ftl
  • locales/id/messages.ftl
  • locales/it/messages.ftl
  • locales/ja/messages.ftl
  • locales/ko/messages.ftl
  • locales/nb/messages.ftl
  • locales/nl/messages.ftl
  • locales/pl/messages.ftl
  • locales/pt-BR/messages.ftl
  • locales/pt-PT/messages.ftl
  • locales/ro/messages.ftl
  • locales/ru/messages.ftl
  • locales/sv/messages.ftl
  • locales/th/messages.ftl
  • locales/tr/messages.ftl
  • locales/uk/messages.ftl
  • locales/vi/messages.ftl
  • locales/zh-Hans/messages.ftl
  • locales/zh-Hant/messages.ftl
  • src/localization/keys.rs
  • src/stdlib/network/cache.rs
  • src/stdlib/network/mod.rs
  • src/stdlib/network/observability_tests.rs
  • src/stdlib/network/redirect.rs
  • src/stdlib/network/redirect_tests.rs
  • test_support/src/http/mod.rs
  • test_support/src/http/response.rs
  • test_support/src/http/server.rs
  • test_support/src/http/tests.rs
  • tests/std_filter_tests.rs
  • tests/std_filter_tests/network_redirect_tests.rs

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

@sourcery-ai

sourcery-ai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Fetch now follows redirects manually so every outbound hop is resolved, policy-checked before connection, bounded, loop-safe, credential-sanitized, and redacted; cache identity remains tied to the original URL, with fixture and integration tests documenting the security and behavioral guarantees.

Sequence diagram for policy-checked fetch redirects

sequenceDiagram
    participant Fetch as fetch()
    participant Adapter as redirect::dispatch_request
    participant Policy as NetworkPolicy
    participant Server as HTTP server

    Fetch->>Adapter: dispatch_request(url, policy, impure)
    Adapter->>Policy: evaluate(original_url)
    Policy-->>Adapter: allowed
    Adapter->>Server: GET original_url
    Server-->>Adapter: redirect response with Location
    Adapter->>Adapter: Url::join(location)
    Adapter->>Adapter: redact_cross_origin_userinfo()
    Adapter->>Policy: evaluate(redirect_target)
    alt target allowed
        Policy-->>Adapter: allowed
        Adapter->>Server: GET redirect_target
        Server-->>Adapter: final response
        Adapter-->>Fetch: response body
    else target rejected
        Policy-->>Adapter: violation
        Adapter-->>Fetch: redirect_disallowed error
    end
Loading

State diagram for bounded fetch redirect chains

stateDiagram-v2
    [*] --> CurrentHop
    CurrentHop --> FinalResponse: non-redirect response
    CurrentHop --> ResolveLocation: redirect response
    ResolveLocation --> Reject: missing or invalid Location
    ResolveLocation --> CheckTarget: resolved target
    CheckTarget --> Reject: NetworkPolicy rejects
    CheckTarget --> Reject: repeated target
    CheckTarget --> Reject: five-hop limit reached
    CheckTarget --> CurrentHop: allowed unseen target
    FinalResponse --> [*]
    Reject --> [*]
Loading

File-Level Changes

Change Details Files
Replaced automatic redirect following with a policy-aware, bounded manual redirect loop.
  • Disable ureq automatic redirects and dispatch each GET hop explicitly.
  • Resolve relative locations, enforce NetworkPolicy before connecting, and reject disallowed schemes or hosts.
  • Limit chains to five redirects and detect repeated targets.
  • Strip URL credentials across origin changes and redact URLs, userinfo, and redirect destinations from errors and telemetry.
src/stdlib/network/redirect.rs
src/stdlib/network/mod.rs
src/stdlib/network/observability_tests.rs
src/localization/keys.rs
locales/ar/messages.ftl
locales/cs/messages.ftl
locales/cy/messages.ftl
locales/da/messages.ftl
locales/de/messages.ftl
locales/el/messages.ftl
locales/en-GB/messages.ftl
locales/en-US/messages.ftl
locales/es-419/messages.ftl
locales/es-ES/messages.ftl
locales/fa/messages.ftl
locales/fi/messages.ftl
locales/fr/messages.ftl
locales/gd/messages.ftl
locales/he/messages.ftl
locales/hi/messages.ftl
locales/hu/messages.ftl
locales/id/messages.ftl
locales/it/messages.ftl
locales/ja/messages.ftl
locales/ko/messages.ftl
locales/nb/messages.ftl
locales/nl/messages.ftl
locales/pl/messages.ftl
locales/pt-BR/messages.ftl
locales/pt-PT/messages.ftl
locales/ro/messages.ftl
locales/ru/messages.ftl
locales/sv/messages.ftl
locales/th/messages.ftl
locales/tr/messages.ftl
locales/uk/messages.ftl
locales/vi/messages.ftl
locales/zh-Hans/messages.ftl
locales/zh-Hant/messages.ftl
Preserved original-URL cache identity while applying redirect validation to cached and uncached fetches.
  • Pass FetchContext policy and response-size limits into remote dispatch.
  • Store redirected response bodies only under the original caller URL.
  • Add coverage that cache hits and misses do not bypass initial or redirected policy checks.
src/stdlib/network/cache.rs
src/stdlib/network/mod.rs
src/stdlib/network/redirect_tests.rs
tests/std_filter_tests/network_redirect_tests.rs
Expanded HTTP test fixtures and integration coverage for redirect security and behavior.
  • Add configurable status, headers, body, sequential responses, and request counters to the test server.
  • Verify blocked and non-allowlisted targets receive zero requests.
  • Cover relative redirects, loops, redirect limits, cache behavior, and bounded redacted policy telemetry.
test_support/src/http/mod.rs
test_support/src/http/response.rs
test_support/src/http/tests.rs
tests/std_filter_tests.rs
tests/std_filter_tests/network_redirect_tests.rs
Documented the redirect security decision and added localized diagnostics.
  • Record every-hop least-privilege policy and original-URL cache semantics in ADR-018.
  • Mark redirect-policy bypass remediation in the network security audit and index the ADR.
  • Add localization keys and messages for missing, invalid, disallowed, looping, and over-limit redirects.
docs/adr-018-revalidate-fetch-redirects.md
docs/contents.md
docs/security-network-command-audit.md
src/localization/keys.rs
locales/ar/messages.ftl
locales/cs/messages.ftl
locales/cy/messages.ftl
locales/da/messages.ftl
locales/de/messages.ftl
locales/el/messages.ftl
locales/en-GB/messages.ftl
locales/en-US/messages.ftl
locales/es-419/messages.ftl
locales/es-ES/messages.ftl
locales/fa/messages.ftl
locales/fi/messages.ftl
locales/fr/messages.ftl
locales/gd/messages.ftl
locales/he/messages.ftl
locales/hi/messages.ftl
locales/hu/messages.ftl
locales/id/messages.ftl
locales/it/messages.ftl
locales/ja/messages.ftl
locales/ko/messages.ftl
locales/nb/messages.ftl
locales/nl/messages.ftl
locales/pl/messages.ftl
locales/pt-BR/messages.ftl
locales/pt-PT/messages.ftl
locales/ro/messages.ftl
locales/ru/messages.ftl
locales/sv/messages.ftl
locales/th/messages.ftl
locales/tr/messages.ftl
locales/uk/messages.ftl
locales/vi/messages.ftl
locales/zh-Hans/messages.ftl
locales/zh-Hant/messages.ftl

Assessment against linked issues

Issue Objective Addressed Explanation
#647 Ensure every fetch redirect destination is resolved and evaluated against NetworkPolicy before any connection, including scheme, host, allowlist, blocklist, and missing-host restrictions.
#647 Replace automatic redirect following with bounded, deterministic manual GET redirect handling that supports relative locations, detects loops, enforces a redirect limit, preserves cache behavior, and prevents sensitive credentials from crossing origins.
#647 Provide redacted diagnostics, documentation, and tests covering blocked targets, default-deny behavior, relative redirects, cache and uncached paths, redirect loops and limits, and zero requests to denied targets.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos force-pushed the issue-647-revalidate-every-fetch-redirect-against-networkpolicy branch from b18ba9a to 7d7e6df Compare September 2, 2026 22:07
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review September 5, 2026 22:40
@leynos
leynos force-pushed the issue-647-revalidate-every-fetch-redirect-against-networkpolicy branch from 7d7e6df to aa320d5 Compare September 5, 2026 22:40

@sourcery-ai sourcery-ai 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.

Sorry @leynos, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 4 days and 1 hour by commenting @sourcery-ai review. Upgrade to get a review now.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T22:46:20.643351Z aa320d5 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

codescene-access[bot]

This comment was marked as outdated.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa320d52ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +26 to +27
target against `NetworkPolicy`. The adapter accepts at most five redirects and
rejects a repeated target.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Document redirect behaviour in the users' guide

The checked docs/users-guide.md network section still describes only the initial fetch() policy, despite this change adding per-hop policy checks, a five-hop limit, loop rejection, credential stripping, and new user-visible diagnostics. Users configuring allowlists cannot determine from the guide why a previously successful redirected fetch now fails, so document this externally observable contract there.

AGENTS.md reference: AGENTS.md:L52-L53

Useful? React with 👍 / 👎.

forgery opportunity. Issue #647 requires the least-privilege policy to cover
each outbound hop rather than only the initial URL.

## Decision

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reference the ADR from the network design

This new ADR records a substantive architectural decision, but the commit only links it from the documentation index and updates the security audit; the primary docs/netsuke-design.md remains unaware of the redirect-handling boundary. Add a reference from the relevant network-design section so readers following the project's design source of truth can discover the decision.

AGENTS.md reference: AGENTS.md:L48-L51

Useful? React with 👍 / 👎.

Comment on lines +193 to +196
pub fn spawn_http_server_responses(
responses: impl IntoIterator<Item = HttpResponse>,
) -> io::Result<(String, Arc<AtomicUsize>, HttpServer)> {
spawn_http_server_responses_with_config(responses, HttpServerConfig::from_env())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Record the HTTP fixture helper's reuse policy

This introduces a new public test_support helper used across test targets, but a repo-wide documentation search found no architecture, design, or developers-guide entry defining its ownership, permitted call sites, or composition rules. Record that reuse policy in the appropriate indexed document rather than leaving the abstraction contract implicit in its call sites.

AGENTS.md reference: AGENTS.md:L111-L119

Useful? React with 👍 / 👎.

Comment thread src/stdlib/network/redirect.rs Outdated

/// Determine whether a response requires manual redirect handling.
fn is_redirect(response: &ureq::Response) -> bool {
(300..400).contains(&response.status())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict manual redirects to redirect status codes

When an origin returns a non-redirecting 3xx response, such as 300 Multiple Choices without a preferred Location or 304 Not Modified, this broad range sends it through RedirectState::advance() and turns it into a missing-Location error. Those responses were previously returned by the HTTP client rather than followed; limit the manual loop to the redirect statuses it supports (301, 302, 303, 307, and 308) so unrelated 3xx responses do not regress.

Useful? React with 👍 / 👎.

codescene-access[bot]

This comment was marked as outdated.

Disable automatic HTTP redirects and validate each resolved destination
before opening its connection. Bound redirect chains, redact diagnostics,
preserve original-URL cache identity, and cover policy, cache, fixture,
and observability paths.
Split fixture request serving from its public API and share direct
redirect-rejection setup. Keep observability assertions focused so the
security coverage remains readable and passes CodeScene health rules.
@leynos
leynos force-pushed the issue-647-revalidate-every-fetch-redirect-against-networkpolicy branch from aa320d5 to ccddd6e Compare September 8, 2026 13:15
codescene-access[bot]

This comment was marked as outdated.

Describe per-hop redirect handling and HTTP fixture reuse, and record the
state model in the network design. Limit manual redirects to supported HTTP
redirect statuses so unrelated 3xx responses are returned unchanged.
@leynos
leynos force-pushed the issue-647-revalidate-every-fetch-redirect-against-networkpolicy branch from ccddd6e to aa00f2c Compare September 8, 2026 13:24
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No quality gates enabled for this code.

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.

Revalidate every fetch redirect against NetworkPolicy

1 participant