Skip to content

Strip credentials from cross-origin request clones (nextLink follow-up) - #1551

Merged
Yabo Hu (VeryEarly) merged 5 commits into
Azure:mainfrom
YangAn-microsoft:fix/icm-nextlink-credential-forwarding
Aug 10, 2026
Merged

Strip credentials from cross-origin request clones (nextLink follow-up)#1551
Yabo Hu (VeryEarly) merged 5 commits into
Azure:mainfrom
YangAn-microsoft:fix/icm-nextlink-credential-forwarding

Conversation

@YangAn-microsoft

@YangAn-microsoft YangAn-microsoft commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes credential forwarding where a server-supplied @odata.nextLink (or other cross-origin follow-up) could cause the generated pipeline to forward the caller's Authorization: Bearer token — plus Cookie / Proxy-Authorization — to a different origin than the original request.

What changed

Runtime Clone (resources/runtime/csharp/pipeline/ISendAsync.cs):

  • Compute same-origin for the clone target: same scheme + host + port as the original request and target scheme is https.
  • When the clone is not same-origin, skip copying Authorization, Proxy-Authorization, and Cookie.
  • Same-origin clones (429/5xx retries, same-host pagination) are unchanged and keep their headers.

This is the standard "do not forward credentials across origins" mitigation, applied at the client-runtime layer.

Live test (real generated module, end-to-end)

Because Az PowerShell modules authenticate via Az.Accounts, we validated the whole behavior end-to-end using a real generated module built from this branch — Az.MSGraph (the Azure PowerShell Resources module) — driven by the real Get-AzADUser cmdlet against Microsoft Graph with a real signed-in context.

A temporary trace (guarded by AZ_NEXTLINK_TRACE) records the hashed credential at three points:

  • PreAuth — pipeline entry, before the Az.Accounts auth step;
  • PostAuth — pipeline terminal, emitted right before the request is sent on the wire (after auth);
  • Clone — the retarget decision (strip vs retain).

Tokens shown are truncated SHA-256 prefixes (no secrets, no tenant data).

Steps

  1. Build this branch's generator (npm run build).
  2. Generate Az.MSGraph with the local extension (autorest --use:<local powershell package>); verified the generated runtime contains this PR's Clone fix.
  3. Build and import the standalone module; Get-AzADUser resolves from Az.MSGraph.
  4. Run Get-AzADUser -First 150 (crosses a real @odata.nextLink page boundary).

Normal path — real same-origin nextLink

PreAuth  auth=<none>       # before auth
PostAuth auth=c634394d     # after auth, right before the wire send
Clone    target=graph.microsoft.com isSameOrigin=True  authClone=c634394d   # same-origin -> token retained
PreAuth  auth=c634394d
PostAuth auth=c634394d

Real Graph nextLinks are same-origin, so this PR retains the token (isSameOrigin=True); paging returns 150. No regression.

Cross-origin strip — same run, with a test-only toggle that forces the cross-origin branch on the absolute nextLink (real Graph never returns a cross-origin nextLink, so the decision is forced; the strip code exercised is this PR's unmodified logic):

PreAuth  auth=<none>
PostAuth auth=c634394d
Clone    target=graph.microsoft.com isSameOrigin=False authClone=<none>   # this PR STRIPS the token on the clone
PreAuth  auth=<none>        # stripped continuation enters the pipeline with no token
PostAuth auth=c634394d      # Az.Accounts auth step RE-STAMPS it before the send

COUNT=150 — the call still succeeds.

Conclusion

  • This PR's cross-origin strip works at clone time (authClone=<none>).
  • However, the Az.Accounts per-request auth step re-applies the token on the cross-origin follow-up (PostAuth carries it again), so for Az PowerShell modules the runtime strip alone does not stop the forward — the auth step overrides it. Effective enforcement must live at (or be gated by) the Az.Accounts auth step; that change is tracked separately in Azure/azure-powershell.
  • For standalone generated SDK usage that does not run the Az.Accounts re-stamp, this runtime strip is effective and delivers the recommended mitigation.
  • With the trace/toggle disabled, the runtime behaves exactly as this PR: the additions are additive and env-gated, and the committed isSameOrigin/strip logic is unchanged.

Testing

  • Emitter baselines updated for the runtime change (43 ISendAsync.cs targets). test-emitter compares freshly generated output against these committed baselines, so removing or altering the cross-origin credential strip in the runtime is caught by CI — i.e. the mitigation is continuously verified via the baselines.

@YangAn-microsoft
YangAn-microsoft marked this pull request as ready for review August 5, 2026 03:48
@YangAn-microsoft YangAn-microsoft changed the title Strip credentials from cross-origin request clones (nextLink follow-up) Gate cross-origin nextLink credential forwarding via marker + Az.Accounts (plain clone) Aug 6, 2026
@YangAn-microsoft
YangAn-microsoft force-pushed the fix/icm-nextlink-credential-forwarding branch from abc27c3 to e75a7d4 Compare August 6, 2026 04:55
@YangAn-microsoft YangAn-microsoft changed the title Gate cross-origin nextLink credential forwarding via marker + Az.Accounts (plain clone) Strip credentials from cross-origin request clones (nextLink follow-up) Aug 6, 2026

Copilot AI 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.

Pull request overview

This PR mitigates credential leakage when a server-supplied follow-up URL (for example @odata.nextLink) changes the request’s origin, by preventing cloned requests from carrying sensitive credential headers across origins in the generated C# runtime.

Changes:

  • Adds same-origin (scheme/host/port) + HTTPS gating to the runtime HttpRequestMessage.Clone logic.
  • Skips copying Authorization, Proxy-Authorization, and Cookie headers on non-same-origin clones.
  • Updates emitter baselines so generated runtimes reflect the new clone behavior.

Reviewed changes

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

Show a summary per file
File Description
powershell/resources/runtime/csharp/pipeline/ISendAsync.cs Runtime clone logic: compute same-origin + strip credential headers on cross-origin clones.
tests-upgrade/tests-emitter/Workloads.SAPVirtualInstance.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/StorageMover.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/StorageAction.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/StandbyPool.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Sphere.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/ServiceFabricManagedClusters.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/RecoveryServices.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Qumulo.Storage.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Pinecone.VectorDb.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Oracle.Database.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/NetworkAnalytics.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Neon.Postgres.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Microsoft.RecoveryServices.RecoveryServicesBackup.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Microsoft.DevOpsInfrastructure.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Microsoft.DBforMySQL.FlexibleServers.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Microsoft.AVS.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Liftr.WeightsAndBiases.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/LambdaTest.HyperExecute.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/KubernetesRuntime.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/KeyVault.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Informatica.DataManagement.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/HybridKubernetes.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Help.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/HealthDataAIServices.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/HardwareSecurityModules.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Fleet.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/ElasticSan.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/EdgeZones.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/DocumentDB.MongoCluster.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/DeviceRegistry.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/DeviceProvisioningServices.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/DataReplication.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/DataProtection.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/DataBox.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Dashboard.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/ComputeSchedule.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/CodeSigning.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Chaos.Management.brown/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/AzureLargeInstance.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/AzureFleet.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/AzureAI.Assets/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/Astronomer.Astro.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.
tests-upgrade/tests-emitter/AppComplianceAutomation.Management/target/generated/runtime/ISendAsync.cs Baseline update for runtime clone credential stripping.

Comment thread powershell/resources/runtime/csharp/pipeline/ISendAsync.cs Outdated
Comment thread powershell/resources/runtime/csharp/pipeline/ISendAsync.cs Outdated
…solved cross-origin/non-https targets

Addresses review feedback: relative same-origin nextLinks were misclassified as cross-origin (creds stripped); now resolve the target against the original request and compare the effective absolute origin. Null target (retry/same request) keeps headers. Runtime + 43 emitter baselines.
@VeryEarly
Yabo Hu (VeryEarly) merged commit a17c707 into Azure:main Aug 10, 2026
3 checks passed
@YangAn-microsoft
YangAn-microsoft deleted the fix/icm-nextlink-credential-forwarding branch August 10, 2026 02:47
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.

3 participants