What
Generated *Params classes build their URL by interpolating the path parameter directly, with no percent-encoding and no dot-segment check. Example, src/Anthropic/Models/Beta/Vaults/Credentials/CredentialRetrieveParams.cs:135:
public override Uri Url(ClientOptions options)
{
var queryString = this.QueryString(options);
return new UriBuilder(
options.BaseUrl.ToString().TrimEnd('/')
+ string.Format("/v1/vaults/{0}/credentials/{1}", this.VaultID, this.CredentialID)
)
{
Query = string.IsNullOrEmpty(queryString) ? "beta=true" : ("beta=true&" + queryString),
}.Uri;
}
System.UriBuilder canonicalises per RFC 3986, so a ../ in the value silently changes which endpoint the request reaches.
106 parameter classes are affected. Note that a same-line search undercounts: rg -l 'string\.Format\("/v1/' returns 91, while rg -l -U 'string\.Format\(\s*"/v1/' returns 106, because 15 classes put the format string on the next line (e.g. MemoryRetrieveParams.cs:158, CertificateRetrieveParams.cs:139). None of the 106 calls any encoder.
Reproduction
var p = new CredentialRetrieveParams {
VaultID = "vault_A",
CredentialID = "../../vault_B/credentials/cred_x"
};
// p.Url(options).AbsolutePath
// actual: /v1/vaults/vault_B/credentials/cred_x
// expected: something under /v1/vaults/vault_A/credentials/
Three input forms normalise, which is relevant if an application tries to filter them:
../../vault_B/...
%2e%2e/%2e%2e/vault_B/... (survives a literal .. denylist)
..\..\vault_B\... (.NET converts backslashes first)
A # in the value also truncates a fixed suffix, so at a template like /v1/files/{0}/content the request lands on the parent resource instead. Of 64 distinct path templates under src/Anthropic/Models, 42 have a fixed suffix after the placeholder (e.g. /v1/agents/{0}/archive, /v1/deployments/{0}/pause) and 22 end at the placeholder.
Uri.EscapeDataString is already used in this repository (12 occurrences, in Anthropic.Aws/AwsSigner.cs and Anthropic.Bedrock/MantleAwsSigner.cs for SigV4 canonicalisation), just not at these call sites. SecurityHelpers.ValidateProfileName (src/Anthropic/Credentials/SecurityHelpers.cs:41) also already rejects .., /, \ and nulls for credential profile names, so the pattern exists in the codebase.
Suggested change
A shared helper applied at the generated Url() call sites:
- Percent-encode each path parameter to the RFC 3986 section 3.3
pchar set, treating / as unsafe.
- After interpolation, reject any
. or .. segment (literal or %2e-encoded) remaining in the assembled path.
Since these files are generated, this is presumably a template change rather than 106 edits.
Test coverage note
Url() output is asserted in many src/Anthropic.Tests/Models/**/*ParamsTest.cs files, so URL construction is already a tested concern, but every case uses a benign literal such as TunnelID = "tunnel_id". No test exercises ../, #, or %2e in a path parameter.
Verified against da57c281, src/Anthropic 12.42.0, .NET 8 (SDK 8.0.424).
What
Generated
*Paramsclasses build their URL by interpolating the path parameter directly, with no percent-encoding and no dot-segment check. Example,src/Anthropic/Models/Beta/Vaults/Credentials/CredentialRetrieveParams.cs:135:System.UriBuildercanonicalises per RFC 3986, so a../in the value silently changes which endpoint the request reaches.106 parameter classes are affected. Note that a same-line search undercounts:
rg -l 'string\.Format\("/v1/'returns 91, whilerg -l -U 'string\.Format\(\s*"/v1/'returns 106, because 15 classes put the format string on the next line (e.g.MemoryRetrieveParams.cs:158,CertificateRetrieveParams.cs:139). None of the 106 calls any encoder.Reproduction
Three input forms normalise, which is relevant if an application tries to filter them:
../../vault_B/...%2e%2e/%2e%2e/vault_B/...(survives a literal..denylist)..\..\vault_B\...(.NET converts backslashes first)A
#in the value also truncates a fixed suffix, so at a template like/v1/files/{0}/contentthe request lands on the parent resource instead. Of 64 distinct path templates undersrc/Anthropic/Models, 42 have a fixed suffix after the placeholder (e.g./v1/agents/{0}/archive,/v1/deployments/{0}/pause) and 22 end at the placeholder.Uri.EscapeDataStringis already used in this repository (12 occurrences, inAnthropic.Aws/AwsSigner.csandAnthropic.Bedrock/MantleAwsSigner.csfor SigV4 canonicalisation), just not at these call sites.SecurityHelpers.ValidateProfileName(src/Anthropic/Credentials/SecurityHelpers.cs:41) also already rejects..,/,\and nulls for credential profile names, so the pattern exists in the codebase.Suggested change
A shared helper applied at the generated
Url()call sites:pcharset, treating/as unsafe..or..segment (literal or%2e-encoded) remaining in the assembled path.Since these files are generated, this is presumably a template change rather than 106 edits.
Test coverage note
Url()output is asserted in manysrc/Anthropic.Tests/Models/**/*ParamsTest.csfiles, so URL construction is already a tested concern, but every case uses a benign literal such asTunnelID = "tunnel_id". No test exercises../,#, or%2ein a path parameter.Verified against
da57c281,src/Anthropic12.42.0, .NET 8 (SDK 8.0.424).