Skip to content

Reject control chars in request headers#4565

Open
pepone wants to merge 2 commits intoicerpc:mainfrom
pepone:reject-control-chars-in-request-headers
Open

Reject control chars in request headers#4565
pepone wants to merge 2 commits intoicerpc:mainfrom
pepone:reject-control-chars-in-request-headers

Conversation

@pepone
Copy link
Copy Markdown
Member

@pepone pepone commented Apr 28, 2026

Validate the path and operation name of incoming requests at decode time so peer-controlled control characters cannot reach loggers and other downstream text-based sinks.

Fix #4432

pepone added 2 commits April 28, 2026 18:42
Validate the path and operation name of incoming requests at decode time
so peer-controlled control characters cannot reach loggers and other
downstream text-based sinks. Sanitize the offending value embedded in
the FormatException message to stop the same payload from leaking into
log output via the wrapped exception.
The path/operation only fails this check on bogus or malicious requests,
and echoing the value back in the exception message risks leaking the
peer-controlled string into logs that render the wrapped exception.
Removing it is simpler than sanitizing.
Copy link
Copy Markdown

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

This PR hardens request header decoding by validating incoming request paths and operation names so peer-controlled control characters (and other non-printable/non-ASCII characters) can’t flow into loggers or other text-based sinks, addressing #4432.

Changes:

  • Add ServiceAddress.CheckOperation to validate operation names as printable ASCII (or empty).
  • Validate decoded Path/Operation at decode time for IceRpc, and decoded Operation for Ice, mapping failures to InvalidDataException.
  • Add regression tests ensuring invalid path/operation headers are rejected/abort as expected.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/IceRpc.Tests/IceRpcProtocolConnectionTests.cs Adds IceRpc tests that invalid path/operation values are refused at decode time.
tests/IceRpc.Tests/IceProtocolConnectionTests.cs Adds Ice test ensuring invalid operation aborts the server connection and dispatcher is not invoked.
src/IceRpc/ServiceAddress.cs Removes path echo from CheckPath error; adds CheckOperation validation helper for operation names.
src/IceRpc/Internal/IceRpcProtocolConnection.cs Validates decoded IceRpc request Path and Operation and rejects invalid headers early.
src/IceRpc/Internal/IceProtocolConnection.cs Validates decoded Ice request Operation and converts invalid values into decode-time failures.

// prevents control characters (notably CR/LF/NUL) from flowing into loggers and other downstream sinks.
try
{
ServiceAddress.CheckPath(header.Path);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure that IceRPC has the same restriction as Ice here. i.e. ascii only characters.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For Path we already have this check in ServiceAddress, so it makes sense we don't accept paths that are not valid according to the ServiceAddress rules.

For Operation I guess we didn't really specify what a valid operation names is in the "icerpc" protocol, at the protocol level is just a string. And this should probably be handled in the integration layer if at all, for example Slice operations are derived from Slice identifiers and have the same limitations that the identifier has. Same for Protobuf, they are derived from the Protobuf IDL and have its own rules.

I think is probably wrong to check the operation name here, as the protocol layer doesn't specify what the operation name is, checking the path seems correct because ServiceAddress format is specified in the protocol.

Copy link
Copy Markdown
Member

@externl externl Apr 29, 2026

Choose a reason for hiding this comment

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

Ok, I couldn't find anywhere in the docs were we way we only accept ASCII chars (but maybe its there?).

Is this check really required here? ServiceAddress' Path init function already does this check, no?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is this check really required here? ServiceAddress' Path init function already does this check, no?

ServiceAddress is not used here, we decode the Path as a string, using IceRpcRequestHeader

[cs::readonly]
compact struct IceRpcRequestHeader {
path: string
operation: string
// fields: Dictionary<RequestFieldKey, Sequence<uint8>> (encoded/decoded manually)
}

Then the path string is assigned to IncomingRequest.Path, another string without further validation.

public string Path { get; init; } = "/";

Maybe is clear to move the validation to IncomingRequest.Path init

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right. Yeah it would make sense to also have it there.

Reminds me of https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate

Copy link
Copy Markdown
Member

@InsertCreativityHere InsertCreativityHere left a comment

Choose a reason for hiding this comment

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

Code changes look good to me!

But I think I will blog about what comprises a valid operation name in Slice.
I also don't think checking it at the IceRpc level is quite right, but it fine for now.

I'd prefer operation names to just be "strings comprised of alphanumeric characters".
And not limit them to purely printable ASCII characters. The fact Slice identifiers are ASCII only is more a reflection of my inability to write a super cool parser : v) not of the Slice spec.

{
throw new FormatException(
$"Invalid path '{path}'; a valid path starts with '/' and contains only unreserved characters, '%', and reserved characters other than '?' and '#'.");
"Invalid path; a valid path starts with '/' and contains only unreserved characters, '%', and reserved characters other than '?' and '#'.");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think printing the 'invalid path' (like we did before) would be a valuable piece of information for debugging.

I recognize there's a tradeoff where: if there's control characters, the message might look weird. but I think the benefit of seeing "oh, 𒀗𒀝/𒀱 was the bad path" outweighs the very unlikely situation where you've gotten control or formatting characters in here.

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.

[Audit-Medium] LoggerMiddleware logs peer-controlled operation names…

4 participants