Skip to content

Commit c965024

Browse files
authored
Merge pull request github#18664 from egregius313/egregius313/csharp/blazor/url-param-sources
C#: Blazor: Add route parameters as remote flow sources
2 parents c4d682f + 29d03db commit c965024

File tree

9 files changed

+960
-1
lines changed

9 files changed

+960
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Blazor `[Parameter]` fields bound to a variable from the route specified in the `@page` directive are now modeled as remote flow sources.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/** Provides classes for working with `Microsoft.AspNetCore.Components` */
2+
3+
import csharp
4+
import semmle.code.csharp.frameworks.Microsoft
5+
import semmle.code.csharp.frameworks.microsoft.AspNetCore
6+
7+
/** The `Microsoft.AspNetCore.Components` namespace */
8+
class MicrosoftAspNetCoreComponentsNamespace extends Namespace {
9+
MicrosoftAspNetCoreComponentsNamespace() {
10+
this.getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and
11+
this.hasName("Components")
12+
}
13+
}
14+
15+
/**
16+
* A class in the `Microsoft.AspNetCore.Components` namespace.
17+
*/
18+
private class MicrosoftAspNetCoreComponentsClass extends Class {
19+
MicrosoftAspNetCoreComponentsClass() {
20+
this.getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace
21+
}
22+
}
23+
24+
/** The `Microsoft.AspNetCore.Components.CascadingParameterAttributeBase` class. */
25+
class MicrosoftAspNetCoreComponentsCascadingParameterAttributeBaseClass extends MicrosoftAspNetCoreComponentsClass
26+
{
27+
MicrosoftAspNetCoreComponentsCascadingParameterAttributeBaseClass() {
28+
this.hasName("CascadingParameterAttributeBase")
29+
}
30+
}
31+
32+
/** The `Microsoft.AspNetCore.Components.ComponentBase` class. */
33+
class MicrosoftAspNetCoreComponentsComponentBaseClass extends MicrosoftAspNetCoreComponentsClass {
34+
MicrosoftAspNetCoreComponentsComponentBaseClass() { this.hasName("ComponentBase") }
35+
}
36+
37+
/** The `Microsoft.AspNetCore.Components.IComponent` interface. */
38+
class MicrosoftAspNetCoreComponentsIComponentInterface extends Interface {
39+
MicrosoftAspNetCoreComponentsIComponentInterface() {
40+
this.getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace and
41+
this.hasName("IComponent")
42+
}
43+
}
44+
45+
/** The `Microsoft.AspNetCore.Components.RouteAttribute` attribute. */
46+
private class MicrosoftAspNetCoreComponentsRouteAttribute extends Attribute {
47+
MicrosoftAspNetCoreComponentsRouteAttribute() {
48+
this.getType().getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace and
49+
this.getType().hasName("RouteAttribute")
50+
}
51+
}
52+
53+
/** The `Microsoft.AspNetCore.Components.ParameterAttribute` attribute. */
54+
private class MicrosoftAspNetCoreComponentsParameterAttribute extends Attribute {
55+
MicrosoftAspNetCoreComponentsParameterAttribute() {
56+
this.getType().getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace and
57+
this.getType().hasName("ParameterAttribute")
58+
}
59+
}
60+
61+
/** An ASP.NET Core (Blazor) component. */
62+
class MicrosoftAspNetCoreComponentsComponent extends Class {
63+
MicrosoftAspNetCoreComponentsComponent() {
64+
this.getABaseType+() instanceof MicrosoftAspNetCoreComponentsComponentBaseClass or
65+
this.getABaseType+() instanceof MicrosoftAspNetCoreComponentsIComponentInterface
66+
}
67+
68+
/** Gets a property whose value cascades down the component hierarchy. */
69+
Property getACascadingParameterProperty() {
70+
result = this.getAProperty() and
71+
result.getAnAttribute().getType().getBaseClass() instanceof
72+
MicrosoftAspNetCoreComponentsCascadingParameterAttributeBaseClass
73+
}
74+
75+
/** Gets the url for the route from the `Microsoft.AspNetCore.Components.RouteAttribute` of the component. */
76+
private string getRouteAttributeUrl() {
77+
exists(MicrosoftAspNetCoreComponentsRouteAttribute a | a = this.getAnAttribute() |
78+
result = a.getArgument(0).getValue()
79+
)
80+
}
81+
82+
/**
83+
* Gets a route parameter from the `Microsoft.AspNetCore.Components.RouteAttribute` of the component.
84+
*
85+
* A route parameter is defined in the URL by wrapping its name in a pair of { braces } when adding a component's @page declaration.
86+
* There are various extensions that can be added next to the parameter name, such as `:int` or `?` to make the parameter optional.
87+
* Optionally, the parameter name can start with a `*` to make it a catch-all parameter.
88+
*
89+
* An example of a route parameter is `@page "/counter/{id:int}/{other?}/{*rest}"`, from this we're getting the `id`, `other` and `rest` parameters.
90+
*/
91+
pragma[nomagic]
92+
private string getARouteParameter() {
93+
exists(string s |
94+
s = this.getRouteAttributeUrl().splitAt("{").regexpCapture("\\*?([^:?}]+)[:?}](.*)", 1) and
95+
result = s.toLowerCase()
96+
)
97+
}
98+
99+
/** Gets a property attributed with `[Parameter]` attribute. */
100+
pragma[nomagic]
101+
private Property getAParameterProperty(string name) {
102+
result = this.getAProperty() and
103+
result.getAnAttribute() instanceof MicrosoftAspNetCoreComponentsParameterAttribute and
104+
name = result.getName().toLowerCase()
105+
}
106+
107+
/** Gets a property whose value is populated from route parameters. */
108+
Property getARouteParameterProperty() {
109+
exists(string name | name = this.getARouteParameter() |
110+
result = this.getAParameterProperty(name)
111+
)
112+
}
113+
}
114+
115+
private module Sources {
116+
private import semmle.code.csharp.security.dataflow.flowsources.Remote
117+
118+
/**
119+
* A property with a `[Parameter]` attribute in an ASP.NET Core component which
120+
* is populated from a route parameter.
121+
*/
122+
private class AspNetCoreComponentRouteParameterFlowSource extends AspNetRemoteFlowSource,
123+
DataFlow::ExprNode
124+
{
125+
AspNetCoreComponentRouteParameterFlowSource() {
126+
exists(MicrosoftAspNetCoreComponentsComponent c, Property p |
127+
p = c.getARouteParameterProperty()
128+
|
129+
this.asExpr() = p.getGetter().getACall()
130+
)
131+
}
132+
133+
override string getSourceType() { result = "ASP.NET Core component route parameter" }
134+
}
135+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ abstract class RemoteFlowSource extends SourceNode {
2626
* A module for importing frameworks that defines remote flow sources.
2727
*/
2828
private module RemoteFlowSources {
29-
private import semmle.code.csharp.frameworks.ServiceStack
29+
private import semmle.code.csharp.frameworks.ServiceStack as ServiceStack
30+
private import semmle.code.csharp.frameworks.microsoft.aspnetcore.Components as Blazor
3031
}
3132

3233
/** A data flow source of remote user input (ASP.NET). */
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// <auto-generated/>
2+
#pragma warning disable 1591
3+
namespace BlazorTest.Components
4+
{
5+
#line default
6+
using global::System;
7+
using global::System.Collections.Generic;
8+
using global::System.Linq;
9+
using global::System.Threading.Tasks;
10+
using global::Microsoft.AspNetCore.Components;
11+
#nullable restore
12+
using System.Net.Http
13+
14+
#nullable disable
15+
;
16+
#nullable restore
17+
using System.Net.Http.Json
18+
19+
#nullable disable
20+
;
21+
#nullable restore
22+
using Microsoft.AspNetCore.Components.Forms
23+
24+
#nullable disable
25+
;
26+
#nullable restore
27+
using Microsoft.AspNetCore.Components.Routing
28+
29+
#nullable disable
30+
;
31+
#nullable restore
32+
using Microsoft.AspNetCore.Components.Web
33+
34+
#nullable disable
35+
;
36+
#nullable restore
37+
using static Microsoft.AspNetCore.Components.Web.RenderMode
38+
39+
#nullable disable
40+
;
41+
#nullable restore
42+
using Microsoft.AspNetCore.Components.Web.Virtualization
43+
44+
#nullable disable
45+
;
46+
#nullable restore
47+
using Microsoft.JSInterop
48+
49+
#nullable disable
50+
;
51+
#nullable restore
52+
using BlazorTest
53+
54+
#nullable disable
55+
;
56+
#nullable restore
57+
using BlazorTest.Components
58+
59+
#line default
60+
#line hidden
61+
#nullable disable
62+
;
63+
[global::BlazorTest.Components.MyInput.__PrivateComponentRenderModeAttribute]
64+
#nullable restore
65+
public partial class MyInput : global::Microsoft.AspNetCore.Components.ComponentBase
66+
#nullable disable
67+
{
68+
#pragma warning disable 1998
69+
protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
70+
{
71+
__builder.OpenElement(0, "input");
72+
__builder.AddAttribute(1, "value", global::Microsoft.AspNetCore.Components.BindConverter.FormatValue(
73+
#nullable restore
74+
Param1
75+
76+
#line default
77+
#line hidden
78+
#nullable disable
79+
));
80+
__builder.AddAttribute(2, "onchange", global::Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredBindSetter(callback: __value =>
81+
{
82+
Param1 = __value; return global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.InvokeAsynchronousDelegate(callback:
83+
#nullable restore
84+
Fire
85+
86+
#line default
87+
#line hidden
88+
#nullable disable
89+
);
90+
}, value: Param1), Param1));
91+
__builder.SetUpdatesAttributeName("value");
92+
__builder.CloseElement();
93+
}
94+
#pragma warning restore 1998
95+
#nullable restore
96+
97+
[Parameter]
98+
public string? Param1 { get; set; } = "";
99+
100+
[Parameter]
101+
public EventCallback<string?> ValueChanged { get; set; }
102+
103+
[Parameter]
104+
public EventCallback<string?> Param1Changed { get; set; }
105+
106+
private void Fire()
107+
{
108+
ValueChanged.InvokeAsync(Param1);
109+
Param1Changed.InvokeAsync(Param1);
110+
}
111+
112+
#line default
113+
#line hidden
114+
#nullable disable
115+
116+
private sealed class __PrivateComponentRenderModeAttribute : global::Microsoft.AspNetCore.Components.RenderModeAttribute
117+
{
118+
private static global::Microsoft.AspNetCore.Components.IComponentRenderMode ModeImpl => InteractiveServer
119+
;
120+
public override global::Microsoft.AspNetCore.Components.IComponentRenderMode Mode => ModeImpl;
121+
}
122+
}
123+
}
124+
#pragma warning restore 1591
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// <auto-generated/>
2+
#pragma warning disable 1591
3+
namespace BlazorTest.Components
4+
{
5+
#line default
6+
using global::System;
7+
using global::System.Collections.Generic;
8+
using global::System.Linq;
9+
using global::System.Threading.Tasks;
10+
using global::Microsoft.AspNetCore.Components;
11+
#nullable restore
12+
using System.Net.Http
13+
14+
#nullable disable
15+
;
16+
#nullable restore
17+
using System.Net.Http.Json
18+
19+
#nullable disable
20+
;
21+
#nullable restore
22+
using Microsoft.AspNetCore.Components.Forms
23+
24+
#nullable disable
25+
;
26+
#nullable restore
27+
using Microsoft.AspNetCore.Components.Routing
28+
29+
#nullable disable
30+
;
31+
#nullable restore
32+
using Microsoft.AspNetCore.Components.Web
33+
34+
#nullable disable
35+
;
36+
#nullable restore
37+
using static Microsoft.AspNetCore.Components.Web.RenderMode
38+
39+
#nullable disable
40+
;
41+
#nullable restore
42+
using Microsoft.AspNetCore.Components.Web.Virtualization
43+
44+
#nullable disable
45+
;
46+
#nullable restore
47+
using Microsoft.JSInterop
48+
49+
#nullable disable
50+
;
51+
#nullable restore
52+
using BlazorTest
53+
54+
#nullable disable
55+
;
56+
#nullable restore
57+
using BlazorTest.Components
58+
59+
#line default
60+
#line hidden
61+
#nullable disable
62+
;
63+
[global::BlazorTest.Components.MyOutput.__PrivateComponentRenderModeAttribute]
64+
#nullable restore
65+
public partial class MyOutput : global::Microsoft.AspNetCore.Components.ComponentBase
66+
#nullable disable
67+
{
68+
#pragma warning disable 1998
69+
protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
70+
{
71+
__builder.OpenElement(0, "div");
72+
__builder.OpenElement(1, "p");
73+
__builder.AddContent(2, "Value from InputText: ");
74+
__builder.AddContent(3,
75+
#nullable restore
76+
Value
77+
78+
#line default
79+
#line hidden
80+
#nullable disable
81+
);
82+
__builder.CloseElement();
83+
__builder.AddMarkupContent(4, "\n ");
84+
__builder.OpenElement(5, "p");
85+
__builder.AddContent(6, "Raw value from InputText: ");
86+
__builder.AddContent(7,
87+
#nullable restore
88+
new MarkupString(Value)
89+
90+
#line default
91+
#line hidden
92+
#nullable disable
93+
);
94+
__builder.CloseElement();
95+
__builder.CloseElement();
96+
}
97+
#pragma warning restore 1998
98+
#nullable restore
99+
100+
[Parameter]
101+
public string Value { get; set; } = "";
102+
103+
#line default
104+
#line hidden
105+
#nullable disable
106+
107+
private sealed class __PrivateComponentRenderModeAttribute : global::Microsoft.AspNetCore.Components.RenderModeAttribute
108+
{
109+
private static global::Microsoft.AspNetCore.Components.IComponentRenderMode ModeImpl => InteractiveServer
110+
;
111+
public override global::Microsoft.AspNetCore.Components.IComponentRenderMode Mode => ModeImpl;
112+
}
113+
}
114+
}
115+
#pragma warning restore 1591

0 commit comments

Comments
 (0)