Skip to content

Commit 642c992

Browse files
authored
Merge pull request #10050 from michaelnebel/csharp/asproutingendpoints
C#: ASP.NET MapGet Routing endpoints (Remote Flow Sources)
2 parents b944005 + c3e0388 commit 642c992

File tree

5 files changed

+116
-2
lines changed

5 files changed

+116
-2
lines changed

csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,36 @@ class MicrosoftAspNetCoreHttpHtmlString extends Class {
357357
this.hasQualifiedName("Microsoft.AspNetCore.Html", "HtmlString")
358358
}
359359
}
360+
361+
/**
362+
* The `Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions` class.
363+
*/
364+
class MicrosoftAspNetCoreBuilderEndpointRouteBuilderExtensions extends Class {
365+
MicrosoftAspNetCoreBuilderEndpointRouteBuilderExtensions() {
366+
this.hasQualifiedName("Microsoft.AspNetCore.Builder", "EndpointRouteBuilderExtensions")
367+
}
368+
369+
/** Gets the `Map` extension method. */
370+
Method getMapMethod() { result = this.getAMethod("Map") }
371+
372+
/** Gets the `MapGet` extension method. */
373+
Method getMapGetMethod() { result = this.getAMethod("MapGet") }
374+
375+
/** Gets the `MapPost` extension method. */
376+
Method getMapPostMethod() { result = this.getAMethod("MapPost") }
377+
378+
/** Gets the `MapPut` extension method. */
379+
Method getMapPutMethod() { result = this.getAMethod("MapPut") }
380+
381+
/** Gets the `MapDelete` extension method. */
382+
Method getMapDeleteMethod() { result = this.getAMethod("MapDelete") }
383+
384+
/** Get a `Map` like extenion methods. */
385+
Method getAMapMethod() {
386+
result =
387+
[
388+
this.getMapMethod(), this.getMapGetMethod(), this.getMapPostMethod(),
389+
this.getMapPutMethod(), this.getMapDeleteMethod()
390+
]
391+
}
392+
}

csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,35 @@ class ActionMethodParameter extends RemoteFlowSource, DataFlow::ParameterNode {
171171
/** A data flow source of remote user input (ASP.NET Core). */
172172
abstract class AspNetCoreRemoteFlowSource extends RemoteFlowSource { }
173173

174+
private predicate reachesMapGetArg(DataFlow::Node n) {
175+
exists(MethodCall mc |
176+
mc.getTarget() = any(MicrosoftAspNetCoreBuilderEndpointRouteBuilderExtensions c).getAMapMethod() and
177+
n.asExpr() = mc.getArgument(2)
178+
)
179+
or
180+
exists(DataFlow::Node mid | reachesMapGetArg(mid) |
181+
DataFlow::localFlowStep(n, mid) or
182+
n.asExpr() = mid.asExpr().(DelegateCreation).getArgument()
183+
)
184+
}
185+
186+
/** A parameter to a routing method delegate. */
187+
class AspNetCoreRoutingMethodParameter extends AspNetCoreRemoteFlowSource, DataFlow::ParameterNode {
188+
AspNetCoreRoutingMethodParameter() {
189+
exists(DataFlow::Node n, Callable c |
190+
reachesMapGetArg(n) and
191+
c.getAParameter() = this.asParameter() and
192+
c.isSourceDeclaration()
193+
|
194+
n.asExpr() = c
195+
or
196+
n.asExpr().(CallableAccess).getTarget().getUnboundDeclaration() = c
197+
)
198+
}
199+
200+
override string getSourceType() { result = "ASP.NET Core routing endpoint." }
201+
}
202+
174203
/**
175204
* Data flow for ASP.NET Core.
176205
*
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Parameters of delegates passed to routing endpoint calls like `MapGet` in ASP.NET Core are now considered remote flow sources.

csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using Microsoft.AspNetCore.Builder;
12
using Microsoft.AspNetCore.Mvc;
3+
using System;
24

35
namespace Testing
46
{
@@ -20,4 +22,40 @@ public object MyAction(ViewModel viewModel)
2022
throw null;
2123
}
2224
}
25+
26+
public class Item
27+
{
28+
public string Tainted { get; set; }
29+
}
30+
31+
public class AspRoutingEndpoints
32+
{
33+
public delegate void MapGetHandler(string param);
34+
35+
public void HandlerMethod(string param) { }
36+
37+
public void M1(string[] args)
38+
{
39+
var builder = WebApplication.CreateBuilder(args);
40+
var app = builder.Build();
41+
42+
// The delegate parameters are considered flow sources.
43+
app.MapGet("/api/redirect/{newUrl}", (string newUrl) => { });
44+
app.MapGet("/{myApi}/redirect/{myUrl}", (string myApi, string myUrl) => { });
45+
46+
Action<string> handler = (string lambdaParam) => { };
47+
app.MapGet("/api/redirect/{lambdaParam}", handler);
48+
49+
MapGetHandler handler2 = HandlerMethod;
50+
app.MapGet("/api/redirect/{mapGetParam}", handler2);
51+
52+
app.MapPost("/api/redirect/{mapPostParam}", (string mapPostParam) => { });
53+
app.MapPut("/api/redirect/{mapPutParam}", (string mapPutParam) => { });
54+
app.MapDelete("/api/redirect/{mapDeleteParam}", (string mapDeleteParam) => { });
55+
56+
app.MapPost("/items", (Item item) => { });
57+
58+
app.Run();
59+
}
60+
}
2361
}
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
remoteFlowSourceMembers
2-
| AspRemoteFlowSource.cs:8:23:8:31 | RequestId |
2+
| AspRemoteFlowSource.cs:10:23:10:31 | RequestId |
3+
| AspRemoteFlowSource.cs:28:23:28:29 | Tainted |
34
remoteFlowSources
4-
| AspRemoteFlowSource.cs:18:42:18:50 | viewModel |
5+
| AspRemoteFlowSource.cs:20:42:20:50 | viewModel |
6+
| AspRemoteFlowSource.cs:35:42:35:46 | param |
7+
| AspRemoteFlowSource.cs:43:58:43:63 | newUrl |
8+
| AspRemoteFlowSource.cs:44:61:44:65 | myApi |
9+
| AspRemoteFlowSource.cs:44:75:44:79 | myUrl |
10+
| AspRemoteFlowSource.cs:46:46:46:56 | lambdaParam |
11+
| AspRemoteFlowSource.cs:52:65:52:76 | mapPostParam |
12+
| AspRemoteFlowSource.cs:53:63:53:73 | mapPutParam |
13+
| AspRemoteFlowSource.cs:54:69:54:82 | mapDeleteParam |
14+
| AspRemoteFlowSource.cs:56:41:56:44 | item |

0 commit comments

Comments
 (0)