Derive URL Rewrite absolute-target mode from configured literal syntax
area-middleware
## Summary
URL Rewrite currently determines whether a rewrite replacement is absolute after evaluating dynamic replacement content. Absolute-target selection should instead come from trusted configured syntax so runtime substitutions cannot change a path rewrite into a scheme-and-host rewrite.
## What is wrong
* `RewriteRule` expands a .NET replacement and then checks the evaluated string for `Uri.SchemeDelimiter` before choosing between path-only handling and `Request.Scheme`/`Request.Host` mutation.
* Imported IIS and Apache rules similarly evaluate a segmented `Pattern` in `RewriteAction` before selecting the same behavior.
* This breaks the invariant that authority-changing behavior is selected by trusted rule configuration. Runtime data can currently supply or complete the discriminator that selects absolute-target mode.
* The construction path already retains enough provenance to enforce the invariant: direct rules retain the raw replacement, and imported rules retain ordered literal and dynamic segments.
## Why it matters (defense in depth)
* Rewrite configuration is expected to define whether a rule changes only path and query state or intentionally changes request authority.
* Deriving that choice from configured syntax strengthens the boundary between trusted configuration and runtime substitution while preserving explicit absolute rewrite templates.
* The change also makes behavior align more closely with the public `AddRewrite` description, which describes path rewriting.
## Affected code
* `src/Middleware/Rewrite/src/RewriteRule.cs:17-25` - retains the raw direct replacement at construction time
* `src/Middleware/Rewrite/src/RewriteRule.cs:27-101` - currently selects absolute handling from the evaluated replacement
* `src/Middleware/Rewrite/src/Pattern.cs:6-24` - owns ordered imported replacement segments and runtime evaluation
* `src/Middleware/Rewrite/src/PatternSegments/LiteralSegment.cs:6-19` - owns trusted configured literal text
* `src/Middleware/Rewrite/src/UrlActions/RewriteAction.cs:16-43` - receives the parsed pattern and Apache escape/query options
* `src/Middleware/Rewrite/src/UrlActions/RewriteAction.cs:45-135` - currently selects absolute handling after evaluation and optional escaping
* `src/Middleware/Rewrite/src/RewriteOptionsExtensions.cs:46-50` - constructs direct rewrite rules; no signature change is required
* `src/Middleware/Rewrite/src/IISUrlRewrite/InputParser.cs:56-83` - preserves ordered IIS replacement segments
* `src/Middleware/Rewrite/src/IISUrlRewrite/InputParser.cs:85-172` - emits dynamic IIS segments
* `src/Middleware/Rewrite/src/IISUrlRewrite/InputParser.cs:204-225` - emits maximal contiguous IIS literal segments
* `src/Middleware/Rewrite/src/IISUrlRewrite/UrlRewriteFileParser.cs:188-251` - parses replacement text and constructs imported actions; no call-site change is required
* `src/Middleware/Rewrite/src/ApacheModRewrite/TestStringParser.cs:32-80` - preserves ordered Apache replacement segments
* `src/Middleware/Rewrite/src/ApacheModRewrite/TestStringParser.cs:91-140` - emits dynamic Apache segments
* `src/Middleware/Rewrite/src/ApacheModRewrite/TestStringParser.cs:148-168` - emits maximal contiguous Apache literal segments
* `src/Middleware/Rewrite/src/ApacheModRewrite/RuleBuilder.cs:173-223` - passes Apache escape and query flags; no signature change is required
## Recommended fix
* Add an internal helper that recognizes absolute intent only when configured text begins with a syntactically valid URI scheme followed by the literal `scheme://` delimiter. Use `Uri.CheckSchemeName` for the configured prefix. Do not parse the full destination or change `UriHelper.FromAbsolute`.
* In `RewriteRule`, compute an immutable absolute-mode flag from the raw replacement in the constructor and use that flag in `ApplyRule`. All .NET replacement substitutions begin with `$`, which cannot be part of a valid scheme prefix, so runtime substitution cannot create or complete the discriminator.
* Expose the configured text from `LiteralSegment` through an internal member. In `Pattern`, compute immutable absolute intent only when the first segment is a `LiteralSegment` containing the complete discriminator. The existing IIS and Apache parsers already emit maximal literal runs, so they need no changes.
* In `RewriteAction`, compute an immutable flag from `pattern.IsAbsolute`. Disable that flag when `escapeBackReferences` is enabled because Apache `[B]` escapes the complete evaluated output before path handling today. Keep evaluation, query flags, `UriHelper.FromAbsolute`, and request assignments otherwise unchanged.
* Likely production changes are `src/Middleware/Rewrite/src/RewritePatternHelper.cs`, `src/Middleware/Rewrite/src/RewriteRule.cs`, `src/Middleware/Rewrite/src/Pattern.cs`, `src/Middleware/Rewrite/src/PatternSegments/LiteralSegment.cs`, and `src/Middleware/Rewrite/src/UrlActions/RewriteAction.cs`.
* Runtime valid-scheme validation is not sufficient because it still lets evaluated content select the mode. Removing all authority mutation is not selected because it would break established explicit absolute templates. Revalidating against inbound host policy is not selected because inbound and rewrite-destination policy are different concerns. A distinct authority-rewrite API is a clearer long-term design, but it requires public API and migration planning beyond this focused change.
* This is an intentional compatibility change for configurations that assemble any part of the scheme discriminator dynamically. Migrate those rules by placing the complete literal `scheme://` prefix at the start of trusted configuration. Dynamic host and path components after that prefix remain supported. Document possible query ordering and fragment-handling changes when a formerly absolute dynamic result moves to relative handling.
* No public API or API baseline change is required. The change is suitable for servicing as defense-in-depth hardening with release-note coverage. A future major version can consider making generic rewrites path-only and exposing authority changes through an explicit capability.
* PR #66961 is merged but absent from the verified HEAD. On branches that include or port it, retain its leading-slash normalization in the request-time flow and replace only the later absolute-mode selector.
## Acceptance criteria
* [ ] A rewrite changes `Request.Scheme` or `Request.Host` only when trusted configured syntax begins with a complete literal valid `scheme://` discriminator.
* [ ] Runtime substitutions, IIS variables/maps/transforms, and Apache variables/backreferences cannot create or complete the discriminator that selects absolute mode.
* [ ] Explicit literal absolute templates remain supported, including dynamic host or path components after the configured discriminator.
* [ ] Apache `[B]` rewrites preserve current escaped-path behavior and do not enter absolute mode.
* [ ] Direct tests cover all documented .NET replacement forms, valid literal scheme forms, and query ordering after relative handling.
* [ ] IIS parser and middleware tests cover first-literal classification, leading dynamic segment types, literal absolute compatibility, and query append/replace behavior.
* [ ] Apache parser and middleware tests cover first-literal classification, leading variables/backreferences, literal absolute compatibility, `[B]`, `QSA`, and `QSD`.
* [ ] PR #66961 leading-slash normalization remains effective wherever that change is present or ported.
* [ ] `AddRewrite` and importer public signatures and API baselines remain unchanged.
* [ ] Compatibility and migration guidance is included in release notes.
0 条评论