Skip to content

Commit 342d4a6

Browse files
authored
Merge pull request #19122 from tamasvajk/tamasvajk/blazor/parameter-passing-jumpnode
C#: Blazor: Add non-local jump node for parameter passing
2 parents 4356766 + 42278eb commit 342d4a6

File tree

18 files changed

+217
-1
lines changed

18 files changed

+217
-1
lines changed

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public IEnumerable<string> RunSourceGenerator(IEnumerable<string> additionalFile
3737
{
3838
try
3939
{
40-
var relativePathToCsProj = Path.GetRelativePath(sourceDir, csprojFile);
40+
var relativePathToCsProj = Path.GetRelativePath(sourceDir, csprojFile)
41+
.Replace('\\', '/'); // Ensure we're generating the same hash regardless of the OS
4142
var name = FileUtils.ComputeHash($"{relativePathToCsProj}\n{this.GetType().Name}");
4243
using var tempDir = new TemporaryDirectory(Path.Join(FileUtils.GetTemporaryWorkingDirectory(out _), "source-generator"), "source generator temporary", logger);
4344
var analyzerConfigPath = Path.Combine(tempDir.DirInfo.FullName, $"{name}.txt");

csharp/ql/integration-tests/all-platforms/blazor/BlazorTest/Components/Pages/TestPage.razor

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
<MyOutput Value="@InputValue6" />
8282
</div>
8383

84+
<div>
85+
<MyOutput Value="@QueryParam" />
86+
</div>
87+
8488
@code {
8589

8690
public class Container
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#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 |
3+
| 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 |
4+
| 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 |
5+
edges
6+
| BlazorTest/Components/Pages/TestPage.razor:85:23:85:32 | access to property QueryParam : String | BlazorTest/obj/Debug/net9.0/generated/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:569:16:577:13 | call to method TypeCheck<String> : String | provenance | Src:MaD:2 MaD:3 |
7+
| BlazorTest/obj/Debug/net9.0/generated/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:569:16:577: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 |
12+
nodes
13+
| BlazorTest/Components/MyOutput.razor:5:53:5:57 | access to property Value | semmle.label | access to property Value |
14+
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | semmle.label | access to property UrlParam |
15+
| 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/net9.0/generated/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:569:16:577:13 | call to method TypeCheck<String> : String | semmle.label | call to method TypeCheck<String> : String |
18+
subpaths
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: Security Features/CWE-079/XSS.ql
2+
postprocess: utils/test/PrettyPrintModels.ql

csharp/ql/integration-tests/all-platforms/blazor_build_mode_none/BlazorTest/Components/Pages/TestPage.razor

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
<MyOutput Value="@InputValue6" />
8282
</div>
8383

84+
<div>
85+
<MyOutput Value="@QueryParam" />
86+
</div>
87+
8488
@code {
8589

8690
public class Container
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#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 |
3+
| 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 |
4+
| 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 |
5+
edges
6+
| BlazorTest/Components/Pages/TestPage.razor:85:23:85:32 | access to property QueryParam : String | test-db/working/razor/AC613014E59A413B9538FF8068364499/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:569:16:577:13 | call to method TypeCheck<String> : String | provenance | Src:MaD:2 MaD:3 |
7+
| test-db/working/razor/AC613014E59A413B9538FF8068364499/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:569:16:577: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 |
12+
nodes
13+
| BlazorTest/Components/MyOutput.razor:5:53:5:57 | access to property Value | semmle.label | access to property Value |
14+
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | semmle.label | access to property UrlParam |
15+
| 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+
| test-db/working/razor/AC613014E59A413B9538FF8068364499/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Components_Pages_TestPage_razor.g.cs:569:16:577:13 | call to method TypeCheck<String> : String | semmle.label | call to method TypeCheck<String> : String |
18+
subpaths
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: Security Features/CWE-079/XSS.ql
2+
postprocess: utils/test/PrettyPrintModels.ql

csharp/ql/integration-tests/all-platforms/blazor_net_8/BlazorTest/Components/Pages/TestPage.razor

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
<MyOutput Value="@InputValue6" />
8282
</div>
8383

84+
<div>
85+
<MyOutput Value="@QueryParam" />
86+
</div>
87+
8488
@code {
8589

8690
public class Container
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#select
2+
| 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 |
3+
| 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 |
4+
edges
5+
nodes
6+
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | semmle.label | access to property UrlParam |
7+
| BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | semmle.label | access to property QueryParam |
8+
subpaths
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: Security Features/CWE-079/XSS.ql
2+
postprocess: utils/test/PrettyPrintModels.ql
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Modeled parameter passing between Blazor parent and child components.

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll

+10
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,16 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
147147
pragma[inline]
148148
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
149149

150+
/**
151+
* A module importing the modules that provide non local jump node declarations,
152+
* ensuring that they are visible to the taint tracking / data flow library.
153+
*/
154+
private module JumpNodes {
155+
private import semmle.code.csharp.frameworks.microsoft.aspnetcore.Components
156+
private import semmle.code.csharp.frameworks.Razor
157+
private import semmle.code.csharp.frameworks.NHibernate
158+
}
159+
150160
/**
151161
* A data flow node that jumps between callables. This can be extended in
152162
* framework code to add additional data flow steps.

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

+51
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ class MicrosoftAspNetCoreComponentsComponent extends Class {
112112
}
113113
}
114114

115+
/**
116+
* The `Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder::AddComponentParameter` method.
117+
*/
118+
private class MicrosoftAspNetCoreComponentsAddComponentParameterMethod extends Method {
119+
MicrosoftAspNetCoreComponentsAddComponentParameterMethod() {
120+
this.hasFullyQualifiedName("Microsoft.AspNetCore.Components.Rendering", "RenderTreeBuilder",
121+
"AddComponentParameter")
122+
}
123+
}
124+
115125
private module Sources {
116126
private import semmle.code.csharp.security.dataflow.flowsources.Remote
117127

@@ -133,3 +143,44 @@ private module Sources {
133143
override string getSourceType() { result = "ASP.NET Core component route parameter" }
134144
}
135145
}
146+
147+
private module JumpNodes {
148+
/**
149+
* A call to `Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder::AddComponentParameter` which
150+
* sets the value of a parameter.
151+
*/
152+
private class ParameterPassingCall extends Call {
153+
ParameterPassingCall() {
154+
this.getTarget() instanceof MicrosoftAspNetCoreComponentsAddComponentParameterMethod
155+
}
156+
157+
/**
158+
* Gets the property whose value is being set.
159+
*/
160+
Property getParameterProperty() {
161+
result.getAnAttribute() instanceof MicrosoftAspNetCoreComponentsParameterAttribute and
162+
exists(NameOfExpr ne | ne = this.getArgument(1) | result.getAnAccess() = ne.getAccess())
163+
}
164+
165+
/**
166+
* Gets the value being set.
167+
*/
168+
Expr getParameterValue() { result = this.getArgument(2) }
169+
}
170+
171+
private class ComponentParameterJump extends DataFlow::NonLocalJumpNode {
172+
Property prop;
173+
174+
ComponentParameterJump() {
175+
exists(ParameterPassingCall call |
176+
prop = call.getParameterProperty() and
177+
this.asExpr() = call.getParameterValue()
178+
)
179+
}
180+
181+
override DataFlow::Node getAJumpSuccessor(boolean preservesValue) {
182+
preservesValue = true and
183+
result.asExpr() = prop.getAnAccess()
184+
}
185+
}
186+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace VulnerableBlazorApp.Components
2+
{
3+
using Microsoft.AspNetCore.Components;
4+
5+
public partial class Name : Microsoft.AspNetCore.Components.ComponentBase
6+
{
7+
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder)
8+
{
9+
if (TheName is not null)
10+
{
11+
builder.OpenElement(0, "div");
12+
builder.OpenElement(1, "p");
13+
builder.AddContent(2, (MarkupString)TheName);
14+
builder.CloseElement();
15+
builder.CloseElement();
16+
}
17+
}
18+
19+
[Parameter]
20+
public string TheName { get; set; }
21+
}
22+
}
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("/names/{name?}")]
7+
public partial class NameList : 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, nameof(VulnerableBlazorApp.Components.Name.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, nameof(VulnerableBlazorApp.Components.Name.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
@@ -0,0 +1,12 @@
1+
edges
2+
| 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 |
3+
nodes
4+
| Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | semmle.label | access to property UrlParam |
5+
| Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | semmle.label | access to property QueryParam |
6+
| Name.cs:13:53:13:59 | access to property TheName | semmle.label | access to property TheName |
7+
| NameList.cs:31:99:31:102 | access to property Name : String | semmle.label | access to property Name : String |
8+
subpaths
9+
#select
10+
| 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 |
11+
| 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 |
12+
| 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 |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security Features/CWE-079/XSS.ql

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

+3
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
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+
| NameList.cs:31:99:31:102 | access to property Name | ASP.NET Core component route parameter |
6+
| NameList.cs:41:17:41:20 | access to property Name | ASP.NET Core component route parameter |
7+
| NameList.cs:43:27:43:30 | access to property Name | ASP.NET Core component route parameter |

0 commit comments

Comments
 (0)