Skip to content

Obsolete XmlSecureResolver #73676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 12, 2022

Conversation

GrabYourPitchforks
Copy link
Member

Resolves #73636.
Resolves #73637.
Resolves CVE-2022-34716.

Obsoletes the System.Xml.XmlSecureResolver type and introduces a new accelerator XmlResolver.ThrowingResolver for callers who need to forbid external entity resolution.

@GrabYourPitchforks GrabYourPitchforks added Security area-System.Xml breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. labels Aug 10, 2022
@GrabYourPitchforks GrabYourPitchforks added this to the 7.0.0 milestone Aug 10, 2022
@GrabYourPitchforks GrabYourPitchforks self-assigned this Aug 10, 2022
@ghost
Copy link

ghost commented Aug 10, 2022

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Aug 10, 2022

Tagging subscribers to this area: @dotnet/area-system-xml
See info in area-owners.md if you want to be subscribed.

Issue Details

Resolves #73636.
Resolves #73637.
Resolves CVE-2022-34716.

Obsoletes the System.Xml.XmlSecureResolver type and introduces a new accelerator XmlResolver.ThrowingResolver for callers who need to forbid external entity resolution.

Author: GrabYourPitchforks
Assignees: GrabYourPitchforks
Labels:

Security, area-System.Xml, breaking-change

Milestone: 7.0.0

@ghost ghost added the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Aug 10, 2022
@ghost
Copy link

ghost commented Aug 10, 2022

Added needs-breaking-change-doc-created label because this PR has the breaking-change label.

When you commit this breaking change:

  1. Create and link to this PR and the issue a matching issue in the dotnet/docs repo using the breaking change documentation template, then remove this needs-breaking-change-doc-created label.
  2. Ask a committer to mail the .NET Breaking Change Notification DL.

Tagging @dotnet/compat for awareness of the breaking change.

@GrabYourPitchforks GrabYourPitchforks removed the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Aug 10, 2022
Copy link
Contributor

@campersau campersau left a comment

Choose a reason for hiding this comment

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

Make Singelton fields readonly?

// An XmlResolver that forbids all external entity resolution.
private sealed class XmlThrowingResolver : XmlResolver
{
internal static XmlThrowingResolver Singleton = new();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
internal static XmlThrowingResolver Singleton = new();
internal static readonly XmlThrowingResolver Singleton = new();

// (Copied from XmlResolver.ThrowingResolver.cs.)
private sealed class XmlThrowingResolver : XmlResolver
{
internal static XmlThrowingResolver Singleton = new();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
internal static XmlThrowingResolver Singleton = new();
internal static readonly XmlThrowingResolver Singleton = new();

Comment on lines 62 to 63
if (xmlResolver == null || xmlResolver == XmlResolver.ThrowingResolver)
origResolver = new XmlUrlResolver();
Copy link
Member

Choose a reason for hiding this comment

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

this seems a bit weird, if user explicitly chooses throwing resolver we will still go with XmlUrlResolver?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, thought the same. It looks like a bug, but perhaps I was being too deferential to preserving back-compat. Maybe killing that clause is the best option.

Copy link
Member

Choose a reason for hiding this comment

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

yes, I'd honestly go with just:
xmlResolver ??= XmlResolver.ThrowingResolver or xmlResolver ??= new XmlUrlResolver() depending if we expect unsafe inputs in those API

Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like that line is meant to override whatever the "default" resolver is. For example, if you call XslCompiledTransform.Load(string), it will eventually hit:

private static XmlResolver CreateDefaultResolver()
{
if (LocalAppContextSwitches.AllowDefaultResolver)
{
return new XmlUrlResolver();
}
else
{
return XmlNullResolver.Singleton;
}
}

And the "ignore the null resolver" logic was apparently meant to work around this. This seems completely broken to me, since why bother putting in a secure default if we're just going to ignore it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like it was added because the URL to the stylesheet itself (the arg passed to XslCompiledTransform.Load(string)) also goes through the resolver for some reason. The system needs to strip it off temporarily - just while loading the outermost stylesheet - so that resolution can take place.

GrabYourPitchforks and others added 2 commits August 10, 2022 21:57
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
Copy link
Member

@jeffhandley jeffhandley left a comment

Choose a reason for hiding this comment

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

Has my approval once the build errors are resolved.

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

Other than the test compilation issues, LGTM.

- Also improves error message on failure
@GrabYourPitchforks
Copy link
Member Author

GrabYourPitchforks commented Aug 12, 2022

Note to reviewers (@bartonjs @krwq) - the latest iteration does three things.

  1. Clean up build errors.
  2. Flow the original resolver through the XSLT loader (see the giant comment in XsltLoader.cs).
  3. Include the URI of the banned external entity as part of the error message.

That third point really helped me with debugging the issues here, and if we're concerned that real-world customers might run into this as a regression then the improved error message can help them as well.

(I'll merge once CI is green and there's an additional affirmative review.)

(matches existing code patterns in this file)

public override Task<object> GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
throw new XmlException(SR.Format(SR.Xml_NullResolver, absoluteUri));
Copy link
Member

Choose a reason for hiding this comment

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

Do we care about inside vs outside the task here?

It looks like other implementations are throwing XmlException "outside" the task, so I guess this isn't entirely wrong.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's one of those weird edge cases that doesn't cleanly fit into any bucket. If I squint I can kinda claim that it's a usage error, since the caller shouldn't be invoking it in the first place. But ultimately I followed the pattern that the previous implementation threw the exception synchronously rather than wrapped within a task.

Copy link
Member

@krwq krwq left a comment

Choose a reason for hiding this comment

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

Thanks, LGTM

@GrabYourPitchforks GrabYourPitchforks merged commit d2afae4 into dotnet:main Aug 12, 2022
@GrabYourPitchforks GrabYourPitchforks deleted the xmlresolver branch August 12, 2022 19:55
@ghost ghost locked as resolved and limited conversation to collaborators Sep 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Xml breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. new-api-needs-documentation Security
Projects
None yet
Development

Successfully merging this pull request may close these issues.

API Proposal: XmlResolver.NullResolver API Proposal: Obsolete XmlSecureResolver
6 participants