Skip to content

Commit befc2fd

Browse files
authored
Merge pull request #19145 from tamasvajk/tamasvajk/blazor/parameter-passing-jumpnode-2
C#: Blazor: Support string literals as property names in jump nodes
2 parents 5c42c0b + a570a72 commit befc2fd

File tree

6 files changed

+150
-1
lines changed

6 files changed

+150
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
#select
2+
| BlazorTest/Components/MyOutput.razor:5:53:5:57 | access to property Value | BlazorTest/Components/Pages/TestPage.razor:85:23:85:32 | access to property QueryParam : String | BlazorTest/Components/MyOutput.razor:5:53:5:57 | access to property Value | $@ flows to here and is written to HTML or JavaScript. | BlazorTest/Components/Pages/TestPage.razor:85:23:85:32 | access to property QueryParam : String | User-provided value |
23
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | $@ flows to here and is written to HTML or JavaScript. | BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | User-provided value |
34
| BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | $@ flows to here and is written to HTML or JavaScript. | BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | User-provided value |
45
edges
6+
| BlazorTest/Components/Pages/TestPage.razor:85:23:85:32 | access to property QueryParam : String | BlazorTest/obj/Debug/net8.0/generated/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:497:59:505:13 | call to method TypeCheck<String> : String | provenance | Src:MaD:2 MaD:3 |
7+
| BlazorTest/obj/Debug/net8.0/generated/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:497:59:505:13 | call to method TypeCheck<String> : String | BlazorTest/Components/MyOutput.razor:5:53:5:57 | access to property Value | provenance | Sink:MaD:1 |
8+
models
9+
| 1 | Sink: Microsoft.AspNetCore.Components; MarkupString; false; MarkupString; (System.String); ; Argument[0]; html-injection; manual |
10+
| 2 | Source: Microsoft.AspNetCore.Components; SupplyParameterFromQueryAttribute; false; ; ; Attribute.Getter; ReturnValue; remote; manual |
11+
| 3 | Summary: Microsoft.AspNetCore.Components.CompilerServices; RuntimeHelpers; false; TypeCheck<T>; (T); ; Argument[0]; ReturnValue; value; manual |
512
nodes
13+
| BlazorTest/Components/MyOutput.razor:5:53:5:57 | access to property Value | semmle.label | access to property Value |
614
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | semmle.label | access to property UrlParam |
715
| BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | semmle.label | access to property QueryParam |
16+
| BlazorTest/Components/Pages/TestPage.razor:85:23:85:32 | access to property QueryParam : String | semmle.label | access to property QueryParam : String |
17+
| BlazorTest/obj/Debug/net8.0/generated/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:497:59:505:13 | call to method TypeCheck<String> : String | semmle.label | call to method TypeCheck<String> : String |
818
subpaths
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Blazor support can now better recognize when a property being set is specified with a string literal, rather than referenced in a `nameof` expression.

csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/aspnetcore/Components.qll

+80-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,38 @@ private class MicrosoftAspNetCoreComponentsAddComponentParameterMethod extends M
122122
}
123123
}
124124

125+
/**
126+
* The `Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder::OpenComponent<TComponent>` method.
127+
*/
128+
private class MicrosoftAspNetCoreComponentsOpenComponentTComponentMethod extends Method {
129+
MicrosoftAspNetCoreComponentsOpenComponentTComponentMethod() {
130+
this.hasFullyQualifiedName("Microsoft.AspNetCore.Components.Rendering", "RenderTreeBuilder",
131+
"OpenComponent`1") and
132+
this.getNumberOfParameters() = 1
133+
}
134+
}
135+
136+
/**
137+
* The `Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder::OpenComponent` method.
138+
*/
139+
private class MicrosoftAspNetCoreComponentsOpenComponentMethod extends Method {
140+
MicrosoftAspNetCoreComponentsOpenComponentMethod() {
141+
this.hasFullyQualifiedName("Microsoft.AspNetCore.Components.Rendering", "RenderTreeBuilder",
142+
"OpenComponent") and
143+
this.getNumberOfParameters() = 2
144+
}
145+
}
146+
147+
/**
148+
* The `Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder::CloseComponent` method.
149+
*/
150+
private class MicrosoftAspNetCoreComponentsCloseComponentMethod extends Method {
151+
MicrosoftAspNetCoreComponentsCloseComponentMethod() {
152+
this.hasFullyQualifiedName("Microsoft.AspNetCore.Components.Rendering", "RenderTreeBuilder",
153+
"CloseComponent")
154+
}
155+
}
156+
125157
private module Sources {
126158
private import semmle.code.csharp.security.dataflow.flowsources.Remote
127159

@@ -144,6 +176,37 @@ private module Sources {
144176
}
145177
}
146178

179+
/**
180+
* Holds for matching `RenderTreeBuilder.OpenComponent` and `RenderTreeBuilder.CloseComponent` calls with index `openCallIndex` and `closeCallIndex` respectively
181+
* within the `enclosing` enclosing callabale. The `componentType` is the type of the component that is being opened and closed.
182+
*/
183+
private predicate matchingOpenCloseComponentCalls(
184+
MethodCall openCall, int openCallIndex, MethodCall closeCall, int closeCallIndex,
185+
Callable enclosing, Type componentType
186+
) {
187+
(
188+
openCall.getTarget().getUnboundDeclaration() instanceof
189+
MicrosoftAspNetCoreComponentsOpenComponentTComponentMethod and
190+
openCall.getTarget().(ConstructedGeneric).getTypeArgument(0) = componentType
191+
or
192+
openCall.getTarget() instanceof MicrosoftAspNetCoreComponentsOpenComponentMethod and
193+
openCall.getArgument(1).(TypeofExpr).getTypeAccess().getTarget() = componentType
194+
) and
195+
openCall.getEnclosingCallable() = enclosing and
196+
closeCall.getTarget() instanceof MicrosoftAspNetCoreComponentsCloseComponentMethod and
197+
closeCall.getEnclosingCallable() = enclosing and
198+
exists(BlockStmt block |
199+
block = closeCall.getParent().getParent() and
200+
block = openCall.getParent().getParent() and
201+
block.getChildStmt(openCallIndex) = openCall.getParent() and
202+
closeCallIndex =
203+
min(int closeCallIndex0 |
204+
block.getChildStmt(closeCallIndex0) = closeCall.getParent() and
205+
closeCallIndex0 > openCallIndex
206+
)
207+
)
208+
}
209+
147210
private module JumpNodes {
148211
/**
149212
* A call to `Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder::AddComponentParameter` which
@@ -159,7 +222,23 @@ private module JumpNodes {
159222
*/
160223
Property getParameterProperty() {
161224
result.getAnAttribute() instanceof MicrosoftAspNetCoreComponentsParameterAttribute and
162-
exists(NameOfExpr ne | ne = this.getArgument(1) | result.getAnAccess() = ne.getAccess())
225+
(
226+
exists(NameOfExpr ne | ne = this.getArgument(1) | result.getAnAccess() = ne.getAccess())
227+
or
228+
exists(
229+
string propertyName, MethodCall openComponent, BlockStmt block, int openIdx, int closeIdx,
230+
int thisIdx
231+
|
232+
propertyName = this.getArgument(1).(StringLiteral).getValue() and
233+
result.hasName(propertyName) and
234+
matchingOpenCloseComponentCalls(openComponent, openIdx, _, closeIdx,
235+
this.getEnclosingCallable(), result.getDeclaringType()) and
236+
block = this.getParent().getParent() and
237+
block = openComponent.getParent().getParent() and
238+
block.getChildStmt(thisIdx) = this.getParent() and
239+
thisIdx in [openIdx + 1 .. closeIdx - 1]
240+
)
241+
)
163242
}
164243

165244
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
namespace VulnerableBlazorApp.Components
2+
{
3+
using System.Collections.Generic;
4+
using Microsoft.AspNetCore.Components;
5+
6+
[RouteAttribute("/names2/{name?}")]
7+
public partial class NameList2 : Microsoft.AspNetCore.Components.ComponentBase
8+
{
9+
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder)
10+
{
11+
if (Names is not null)
12+
{
13+
builder.OpenElement(0, "div");
14+
builder.OpenElement(1, "ul");
15+
foreach (var name in Names)
16+
{
17+
builder.OpenElement(2, "li");
18+
builder.OpenComponent<VulnerableBlazorApp.Components.Name>(3);
19+
builder.AddComponentParameter(4, "TheName", name);
20+
builder.CloseComponent();
21+
builder.CloseElement();
22+
}
23+
builder.CloseElement();
24+
builder.CloseElement();
25+
}
26+
27+
builder.OpenElement(5, "div");
28+
builder.OpenElement(6, "p");
29+
builder.AddContent(7, "Name: ");
30+
builder.OpenComponent<VulnerableBlazorApp.Components.Name>(8);
31+
builder.AddComponentParameter(9, "TheName", Name);
32+
builder.CloseComponent();
33+
builder.CloseElement();
34+
}
35+
36+
[Parameter]
37+
public string Name { get; set; }
38+
39+
protected override void OnParametersSet()
40+
{
41+
if (Name is not null)
42+
{
43+
Names.Add(Name);
44+
}
45+
}
46+
47+
48+
public List<string> Names { get; set; } = new List<string>();
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
edges
2+
| NameList2.cs:31:57:31:60 | access to property Name : String | Name.cs:13:53:13:59 | access to property TheName | provenance | Sink:MaD:149 |
23
| NameList.cs:31:99:31:102 | access to property Name : String | Name.cs:13:53:13:59 | access to property TheName | provenance | Sink:MaD:149 |
34
nodes
45
| Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | semmle.label | access to property UrlParam |
56
| Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | semmle.label | access to property QueryParam |
67
| Name.cs:13:53:13:59 | access to property TheName | semmle.label | access to property TheName |
8+
| NameList2.cs:31:57:31:60 | access to property Name : String | semmle.label | access to property Name : String |
79
| NameList.cs:31:99:31:102 | access to property Name : String | semmle.label | access to property Name : String |
810
subpaths
911
#select
1012
| Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | $@ flows to here and is written to HTML or JavaScript. | Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | User-provided value |
1113
| Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | $@ flows to here and is written to HTML or JavaScript. | Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | User-provided value |
14+
| Name.cs:13:53:13:59 | access to property TheName | NameList2.cs:31:57:31:60 | access to property Name : String | Name.cs:13:53:13:59 | access to property TheName | $@ flows to here and is written to HTML or JavaScript. | NameList2.cs:31:57:31:60 | access to property Name : String | User-provided value |
1215
| Name.cs:13:53:13:59 | access to property TheName | NameList.cs:31:99:31:102 | access to property Name : String | Name.cs:13:53:13:59 | access to property TheName | $@ flows to here and is written to HTML or JavaScript. | NameList.cs:31:99:31:102 | access to property Name : String | User-provided value |

csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/remoteFlowSource.expected

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
| Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | ASP.NET Core component route parameter |
33
| Components_Pages_TestPage_razor.g.cs:176:1:176:10 | access to property QueryParam | external |
44
| Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | external |
5+
| NameList2.cs:31:57:31:60 | access to property Name | ASP.NET Core component route parameter |
6+
| NameList2.cs:41:17:41:20 | access to property Name | ASP.NET Core component route parameter |
7+
| NameList2.cs:43:27:43:30 | access to property Name | ASP.NET Core component route parameter |
58
| NameList.cs:31:99:31:102 | access to property Name | ASP.NET Core component route parameter |
69
| NameList.cs:41:17:41:20 | access to property Name | ASP.NET Core component route parameter |
710
| NameList.cs:43:27:43:30 | access to property Name | ASP.NET Core component route parameter |

0 commit comments

Comments
 (0)