Skip to content
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

C#: Add a few more sanitizers to cs/web/unvalidated-url-redirection #15596

Merged
merged 7 commits into from
Feb 15, 2024

Conversation

erik-krogh
Copy link
Contributor

I was looking at the QHelp for cs/web/unvalidated-url-redirection, but it was hard to write good fix examples that weren't flagged by the query.

So I've added a few new sanitizers to the query.

I've worked very little with queries that use the shared dataflow library, and I think this might be my first "real" C# PR.
What I'm saying is to look closely, because I might have made some silly mistake.

@github-actions github-actions bot added the C# label Feb 13, 2024
@erik-krogh erik-krogh marked this pull request as ready for review February 13, 2024 12:45
@erik-krogh erik-krogh requested a review from a team as a code owner February 13, 2024 12:45
@michaelnebel michaelnebel self-requested a review February 14, 2024 10:52
Copy link
Contributor

@michaelnebel michaelnebel left a comment

Choose a reason for hiding this comment

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

Thank you for doing this @erik-krogh !!

This looks plausible to me.
I have added some comments.
We should probably also

  • Add a change note.
  • Run DCA to check if there are any changes to performance or results.


public class UrlRedirectHandler2 : IHttpHandler
{
private const String VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html";
Copy link
Contributor

Choose a reason for hiding this comment

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

unused

// BAD: a request parameter is incorporated without validation into a URL redirect
ctx.Response.Redirect(ctx.Request.QueryString["page"]);

List<string> VALID_REDIRECTS = new List<string>{ "http://cwe.mitre.org/data/definitions/601.html", "http://cwe.mitre.org/data/definitions/79.html" };
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider moving this declaration to a private const field (it looks like this was the original intention :-) )

@@ -139,6 +139,75 @@ class LocalUrlSanitizer extends Sanitizer {
LocalUrlSanitizer() { this = DataFlow::BarrierGuard<isLocalUrlSanitizer/3>::getABarrierNode() }
}

/**
* A argument to a call to `List.Contains()` that is a sanitizer for URL redirects.
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
* A argument to a call to `List.Contains()` that is a sanitizer for URL redirects.
* An argument to a call to `List.Contains()` that is a sanitizer for URL redirects.

}

/**
* A URL argument to a call to `List.Contains()` that is a sanitizer for URL redirects.
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
* A URL argument to a call to `List.Contains()` that is a sanitizer for URL redirects.
* An URL argument to a call to `List.Contains()` that is a sanitizer for URL redirects.

private predicate isContainsUrlSanitizer(Guard guard, Expr e, AbstractValue v) {
exists(MethodCall method | method = guard |
exists(Method m | m = method.getTarget() |
m.hasName("Contains") and
Copy link
Contributor

@michaelnebel michaelnebel Feb 14, 2024

Choose a reason for hiding this comment

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

This is any method with the name Contains. Should we narrow this to Contains methods defined on specific classes or interfaces? Did you intend to only have this to apply to the Contains method on ICollection<T>?
Maybe we should consider to leave it as is. There are lots of collection like interfaces and classes that have a Contains method. We can narrow this in case we get false negative reports.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought a little about this, and I agree that we should keep it wide, so we also find Contains method in other interfaces.

I'll add a comment about it.

Comment on lines 146 to 152
exists(MethodCall method | method = guard |
exists(Method m | m = method.getTarget() |
m.hasName("Contains") and
e = method.getArgument(0)
) and
v.(AbstractValues::BooleanValue).getValue() = true
)
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
exists(MethodCall method | method = guard |
exists(Method m | m = method.getTarget() |
m.hasName("Contains") and
e = method.getArgument(0)
) and
v.(AbstractValues::BooleanValue).getValue() = true
)
guard =
any(MethodCall method |
exists(Method m | m = method.getTarget() |
m.hasName("Contains") and
e = method.getArgument(0)
) and
v.(AbstractValues::BooleanValue).getValue() = true
)

Comment on lines 168 to 173
exists(PropertyAccess access | access = guard |
access.getProperty().getName() = "IsAbsoluteUri" and
access.getQualifier().getType().getFullyQualifiedName() = "System.Uri" and
e = access.getQualifier() and
v.(AbstractValues::BooleanValue).getValue() = false
)
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
exists(PropertyAccess access | access = guard |
access.getProperty().getName() = "IsAbsoluteUri" and
access.getQualifier().getType().getFullyQualifiedName() = "System.Uri" and
e = access.getQualifier() and
v.(AbstractValues::BooleanValue).getValue() = false
)
guard =
any(PropertyAccess access |
access.getProperty().hasFullyQualifiedName("System", "Uri", "IsAbsoluteUri") and
e = access.getQualifier() and
v.(AbstractValues::BooleanValue).getValue() = false
)

Comment on lines 190 to 199
exists(EqualityOperation comparison | comparison = guard |
exists(PropertyAccess access | access = comparison.getAnOperand() |
access.getProperty().getName() = "Host" and
access.getQualifier().getType().getFullyQualifiedName() = "System.Uri" and
e = access.getQualifier()
) and
if comparison instanceof EQExpr
then v.(AbstractValues::BooleanValue).getValue() = true
else v.(AbstractValues::BooleanValue).getValue() = false
)
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
exists(EqualityOperation comparison | comparison = guard |
exists(PropertyAccess access | access = comparison.getAnOperand() |
access.getProperty().getName() = "Host" and
access.getQualifier().getType().getFullyQualifiedName() = "System.Uri" and
e = access.getQualifier()
) and
if comparison instanceof EQExpr
then v.(AbstractValues::BooleanValue).getValue() = true
else v.(AbstractValues::BooleanValue).getValue() = false
)
guard =
any(EqualityOperation comparison |
exists(PropertyAccess access | access = comparison.getAnOperand() |
access.getProperty().hasFullyQualifiedName("System", "Uri", "Host") and
e = access.getQualifier()
) and
if comparison instanceof EQExpr
then v.(AbstractValues::BooleanValue).getValue() = true
else v.(AbstractValues::BooleanValue).getValue() = false
)

Copy link
Contributor

@michaelnebel michaelnebel left a comment

Choose a reason for hiding this comment

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

Looks good to me!
Lets wait and see what DCA says before merging.

@erik-krogh
Copy link
Contributor Author

erik-krogh commented Feb 15, 2024

Lets wait and see what DCA says before merging.

DCA looks fine: https://github.com/github/codeql-dca-main/tree/data/erik-krogh/PR-15596-0-csharp__3/reports

(I had some trouble with an outdated local copy of DCA, that's why there are a bunch of failed experiments).

@michaelnebel
Copy link
Contributor

Lets wait and see what DCA says before merging.

DCA looks fine: https://github.com/github/codeql-dca-main/tree/data/erik-krogh/PR-15596-0-csharp__3/reports

(I had some trouble with an outdated local copy of DCA, that's why there are a bunch of failed experiments).

Great - thank you @erik-krogh !!

@erik-krogh erik-krogh merged commit 7c05572 into github:main Feb 15, 2024
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants