diff --git a/csharp/ql/lib/change-notes/2025-02-03-blazor-routing-parameters.md b/csharp/ql/lib/change-notes/2025-02-03-blazor-routing-parameters.md new file mode 100644 index 000000000000..00afc5867c62 --- /dev/null +++ b/csharp/ql/lib/change-notes/2025-02-03-blazor-routing-parameters.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Blazor `[Parameter]` fields bound to a variable from the route specified in the `@page` directive are now modeled as remote flow sources. diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/aspnetcore/Components.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/aspnetcore/Components.qll new file mode 100644 index 000000000000..6e37fc0480fb --- /dev/null +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/aspnetcore/Components.qll @@ -0,0 +1,135 @@ +/** Provides classes for working with `Microsoft.AspNetCore.Components` */ + +import csharp +import semmle.code.csharp.frameworks.Microsoft +import semmle.code.csharp.frameworks.microsoft.AspNetCore + +/** The `Microsoft.AspNetCore.Components` namespace */ +class MicrosoftAspNetCoreComponentsNamespace extends Namespace { + MicrosoftAspNetCoreComponentsNamespace() { + this.getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and + this.hasName("Components") + } +} + +/** + * A class in the `Microsoft.AspNetCore.Components` namespace. + */ +private class MicrosoftAspNetCoreComponentsClass extends Class { + MicrosoftAspNetCoreComponentsClass() { + this.getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace + } +} + +/** The `Microsoft.AspNetCore.Components.CascadingParameterAttributeBase` class. */ +class MicrosoftAspNetCoreComponentsCascadingParameterAttributeBaseClass extends MicrosoftAspNetCoreComponentsClass +{ + MicrosoftAspNetCoreComponentsCascadingParameterAttributeBaseClass() { + this.hasName("CascadingParameterAttributeBase") + } +} + +/** The `Microsoft.AspNetCore.Components.ComponentBase` class. */ +class MicrosoftAspNetCoreComponentsComponentBaseClass extends MicrosoftAspNetCoreComponentsClass { + MicrosoftAspNetCoreComponentsComponentBaseClass() { this.hasName("ComponentBase") } +} + +/** The `Microsoft.AspNetCore.Components.IComponent` interface. */ +class MicrosoftAspNetCoreComponentsIComponentInterface extends Interface { + MicrosoftAspNetCoreComponentsIComponentInterface() { + this.getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace and + this.hasName("IComponent") + } +} + +/** The `Microsoft.AspNetCore.Components.RouteAttribute` attribute. */ +private class MicrosoftAspNetCoreComponentsRouteAttribute extends Attribute { + MicrosoftAspNetCoreComponentsRouteAttribute() { + this.getType().getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace and + this.getType().hasName("RouteAttribute") + } +} + +/** The `Microsoft.AspNetCore.Components.ParameterAttribute` attribute. */ +private class MicrosoftAspNetCoreComponentsParameterAttribute extends Attribute { + MicrosoftAspNetCoreComponentsParameterAttribute() { + this.getType().getNamespace() instanceof MicrosoftAspNetCoreComponentsNamespace and + this.getType().hasName("ParameterAttribute") + } +} + +/** An ASP.NET Core (Blazor) component. */ +class MicrosoftAspNetCoreComponentsComponent extends Class { + MicrosoftAspNetCoreComponentsComponent() { + this.getABaseType+() instanceof MicrosoftAspNetCoreComponentsComponentBaseClass or + this.getABaseType+() instanceof MicrosoftAspNetCoreComponentsIComponentInterface + } + + /** Gets a property whose value cascades down the component hierarchy. */ + Property getACascadingParameterProperty() { + result = this.getAProperty() and + result.getAnAttribute().getType().getBaseClass() instanceof + MicrosoftAspNetCoreComponentsCascadingParameterAttributeBaseClass + } + + /** Gets the url for the route from the `Microsoft.AspNetCore.Components.RouteAttribute` of the component. */ + private string getRouteAttributeUrl() { + exists(MicrosoftAspNetCoreComponentsRouteAttribute a | a = this.getAnAttribute() | + result = a.getArgument(0).getValue() + ) + } + + /** + * Gets a route parameter from the `Microsoft.AspNetCore.Components.RouteAttribute` of the component. + * + * A route parameter is defined in the URL by wrapping its name in a pair of { braces } when adding a component's @page declaration. + * There are various extensions that can be added next to the parameter name, such as `:int` or `?` to make the parameter optional. + * Optionally, the parameter name can start with a `*` to make it a catch-all parameter. + * + * An example of a route parameter is `@page "/counter/{id:int}/{other?}/{*rest}"`, from this we're getting the `id`, `other` and `rest` parameters. + */ + pragma[nomagic] + private string getARouteParameter() { + exists(string s | + s = this.getRouteAttributeUrl().splitAt("{").regexpCapture("\\*?([^:?}]+)[:?}](.*)", 1) and + result = s.toLowerCase() + ) + } + + /** Gets a property attributed with `[Parameter]` attribute. */ + pragma[nomagic] + private Property getAParameterProperty(string name) { + result = this.getAProperty() and + result.getAnAttribute() instanceof MicrosoftAspNetCoreComponentsParameterAttribute and + name = result.getName().toLowerCase() + } + + /** Gets a property whose value is populated from route parameters. */ + Property getARouteParameterProperty() { + exists(string name | name = this.getARouteParameter() | + result = this.getAParameterProperty(name) + ) + } +} + +private module Sources { + private import semmle.code.csharp.security.dataflow.flowsources.Remote + + /** + * A property with a `[Parameter]` attribute in an ASP.NET Core component which + * is populated from a route parameter. + */ + private class AspNetCoreComponentRouteParameterFlowSource extends AspNetRemoteFlowSource, + DataFlow::ExprNode + { + AspNetCoreComponentRouteParameterFlowSource() { + exists(MicrosoftAspNetCoreComponentsComponent c, Property p | + p = c.getARouteParameterProperty() + | + this.asExpr() = p.getGetter().getACall() + ) + } + + override string getSourceType() { result = "ASP.NET Core component route parameter" } + } +} diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll index dc2fb36c47ae..2906fde4e1de 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll @@ -26,7 +26,8 @@ abstract class RemoteFlowSource extends SourceNode { * A module for importing frameworks that defines remote flow sources. */ private module RemoteFlowSources { - private import semmle.code.csharp.frameworks.ServiceStack + private import semmle.code.csharp.frameworks.ServiceStack as ServiceStack + private import semmle.code.csharp.frameworks.microsoft.aspnetcore.Components as Blazor } /** A data flow source of remote user input (ASP.NET). */ diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_MyInput_razor.g.cs b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_MyInput_razor.g.cs new file mode 100644 index 000000000000..bdff55cfc1cc --- /dev/null +++ b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_MyInput_razor.g.cs @@ -0,0 +1,124 @@ +// +#pragma warning disable 1591 +namespace BlazorTest.Components +{ +#line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; +#nullable restore + using System.Net.Http + +#nullable disable + ; +#nullable restore + using System.Net.Http.Json + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Forms + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Routing + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Web + +#nullable disable + ; +#nullable restore + using static Microsoft.AspNetCore.Components.Web.RenderMode + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Web.Virtualization + +#nullable disable + ; +#nullable restore + using Microsoft.JSInterop + +#nullable disable + ; +#nullable restore + using BlazorTest + +#nullable disable + ; +#nullable restore + using BlazorTest.Components + +#line default +#line hidden +#nullable disable + ; + [global::BlazorTest.Components.MyInput.__PrivateComponentRenderModeAttribute] +#nullable restore + public partial class MyInput : global::Microsoft.AspNetCore.Components.ComponentBase +#nullable disable + { +#pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.OpenElement(0, "input"); + __builder.AddAttribute(1, "value", global::Microsoft.AspNetCore.Components.BindConverter.FormatValue( +#nullable restore +Param1 + +#line default +#line hidden +#nullable disable + )); + __builder.AddAttribute(2, "onchange", global::Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredBindSetter(callback: __value => + { + Param1 = __value; return global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.InvokeAsynchronousDelegate(callback: +#nullable restore +Fire + +#line default +#line hidden +#nullable disable + ); + }, value: Param1), Param1)); + __builder.SetUpdatesAttributeName("value"); + __builder.CloseElement(); + } +#pragma warning restore 1998 +#nullable restore + + [Parameter] + public string? Param1 { get; set; } = ""; + + [Parameter] + public EventCallback ValueChanged { get; set; } + + [Parameter] + public EventCallback Param1Changed { get; set; } + + private void Fire() + { + ValueChanged.InvokeAsync(Param1); + Param1Changed.InvokeAsync(Param1); + } + +#line default +#line hidden +#nullable disable + + private sealed class __PrivateComponentRenderModeAttribute : global::Microsoft.AspNetCore.Components.RenderModeAttribute + { + private static global::Microsoft.AspNetCore.Components.IComponentRenderMode ModeImpl => InteractiveServer + ; + public override global::Microsoft.AspNetCore.Components.IComponentRenderMode Mode => ModeImpl; + } + } +} +#pragma warning restore 1591 diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_MyOutput_razor.g.cs b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_MyOutput_razor.g.cs new file mode 100644 index 000000000000..698aceba27cf --- /dev/null +++ b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_MyOutput_razor.g.cs @@ -0,0 +1,115 @@ +// +#pragma warning disable 1591 +namespace BlazorTest.Components +{ +#line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; +#nullable restore + using System.Net.Http + +#nullable disable + ; +#nullable restore + using System.Net.Http.Json + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Forms + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Routing + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Web + +#nullable disable + ; +#nullable restore + using static Microsoft.AspNetCore.Components.Web.RenderMode + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Web.Virtualization + +#nullable disable + ; +#nullable restore + using Microsoft.JSInterop + +#nullable disable + ; +#nullable restore + using BlazorTest + +#nullable disable + ; +#nullable restore + using BlazorTest.Components + +#line default +#line hidden +#nullable disable + ; + [global::BlazorTest.Components.MyOutput.__PrivateComponentRenderModeAttribute] +#nullable restore + public partial class MyOutput : global::Microsoft.AspNetCore.Components.ComponentBase +#nullable disable + { +#pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.OpenElement(0, "div"); + __builder.OpenElement(1, "p"); + __builder.AddContent(2, "Value from InputText: "); + __builder.AddContent(3, +#nullable restore +Value + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(4, "\n "); + __builder.OpenElement(5, "p"); + __builder.AddContent(6, "Raw value from InputText: "); + __builder.AddContent(7, +#nullable restore +new MarkupString(Value) + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + } +#pragma warning restore 1998 +#nullable restore + + [Parameter] + public string Value { get; set; } = ""; + +#line default +#line hidden +#nullable disable + + private sealed class __PrivateComponentRenderModeAttribute : global::Microsoft.AspNetCore.Components.RenderModeAttribute + { + private static global::Microsoft.AspNetCore.Components.IComponentRenderMode ModeImpl => InteractiveServer + ; + public override global::Microsoft.AspNetCore.Components.IComponentRenderMode Mode => ModeImpl; + } + } +} +#pragma warning restore 1591 diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_Pages_TestPage_razor.g.cs b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_Pages_TestPage_razor.g.cs new file mode 100644 index 000000000000..8732f8c7e4be --- /dev/null +++ b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/Components_Pages_TestPage_razor.g.cs @@ -0,0 +1,567 @@ +// +#pragma warning disable 1591 +namespace BlazorTest.Components.Pages +{ +#line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; +#nullable restore + using System.Net.Http + +#nullable disable + ; +#nullable restore + using System.Net.Http.Json + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Forms + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Routing + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Web + +#nullable disable + ; +#nullable restore + using static Microsoft.AspNetCore.Components.Web.RenderMode + +#nullable disable + ; +#nullable restore + using Microsoft.AspNetCore.Components.Web.Virtualization + +#nullable disable + ; +#nullable restore + using Microsoft.JSInterop + +#nullable disable + ; +#nullable restore + using BlazorTest + +#nullable disable + ; +#nullable restore + using BlazorTest.Components + +#line default +#line hidden +#nullable disable + ; + [global::Microsoft.AspNetCore.Components.RouteAttribute( + // language=Route,Component +#nullable restore +"/" + +#line default +#line hidden +#nullable disable + )] + [global::Microsoft.AspNetCore.Components.RouteAttribute( + // language=Route,Component +#nullable restore +"/test/{urlParam?}" + +#line default +#line hidden +#nullable disable + )] + [global::BlazorTest.Components.Pages.TestPage.__PrivateComponentRenderModeAttribute] +#nullable restore + public partial class TestPage : global::Microsoft.AspNetCore.Components.ComponentBase +#nullable disable + { +#pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.OpenComponent(0); + __builder.AddAttribute(1, "ChildContent", (global::Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => + { + __builder2.AddContent(2, "TestPage"); + } + )); + __builder.CloseComponent(); + __builder.AddMarkupContent(3, "\n\n"); + __builder.OpenElement(4, "div"); + __builder.AddMarkupContent(5, "

Route parameter

\n "); + __builder.OpenElement(6, "p"); + __builder.AddContent(7, "Go to: "); + __builder.OpenElement(8, "a"); + __builder.AddAttribute(9, "href", "/test/" + ( +#nullable restore +XssUrl + +#line default +#line hidden +#nullable disable + )); + __builder.AddContent(10, "/test/"); + __builder.AddContent(11, +#nullable restore +XssUrl + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(12, "\n "); + __builder.OpenElement(13, "p"); + __builder.AddContent(14, "Parameter from URL: "); + __builder.AddContent(15, +#nullable restore +UrlParam + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(16, "\n "); + __builder.OpenElement(17, "p"); + __builder.AddContent(18, "Raw parameter from URL: "); + __builder.AddContent(19, +#nullable restore +(MarkupString)UrlParam + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(20, "\n\n
\n\n"); + __builder.OpenElement(21, "div"); + __builder.AddMarkupContent(22, "

Query parameter

\n "); + __builder.OpenElement(23, "p"); + __builder.AddContent(24, "Go to: "); + __builder.OpenElement(25, "a"); + __builder.AddAttribute(26, "href", "/test/?qs=" + ( +#nullable restore +XssUrl + +#line default +#line hidden +#nullable disable + )); + __builder.AddContent(27, "/test/?qs="); + __builder.AddContent(28, +#nullable restore +XssUrl + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(29, "\n "); + __builder.OpenElement(30, "p"); + __builder.AddContent(31, "Parameter from query string: "); + __builder.AddContent(32, +#nullable restore +QueryParam + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(33, "\n "); + __builder.OpenElement(34, "p"); + __builder.AddContent(35, "Raw parameter from query string: "); + __builder.AddContent(36, +#nullable restore +new MarkupString(QueryParam) + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(37, "\n\n
\n\n"); + __builder.OpenElement(38, "div"); + __builder.AddMarkupContent(39, "

Bind InputText component

\n "); + __builder.OpenComponent(40); + __builder.AddComponentParameter(41, nameof(global::Microsoft.AspNetCore.Components.Forms.InputText. +#nullable restore +Value + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck( +#nullable restore +InputValue1 + +#line default +#line hidden +#nullable disable + )); + __builder.AddComponentParameter(42, nameof(global::Microsoft.AspNetCore.Components.Forms.InputText.ValueChanged), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck>(global::Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => InputValue1 = __value, InputValue1)))); + __builder.AddComponentParameter(43, nameof(global::Microsoft.AspNetCore.Components.Forms.InputText.ValueExpression), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck>>(() => InputValue1)); + __builder.CloseComponent(); + __builder.AddMarkupContent(44, "\n "); + __builder.OpenElement(45, "p"); + __builder.AddContent(46, "Value from InputText: "); + __builder.AddContent(47, +#nullable restore +InputValue1 + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(48, "\n "); + __builder.OpenElement(49, "p"); + __builder.AddContent(50, "Raw value from InputText: "); + __builder.AddContent(51, +#nullable restore +new MarkupString(InputValue1) + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(52, "\n\n
\n\n"); + __builder.OpenElement(53, "div"); + __builder.AddMarkupContent(54, "

Bind input element

\n "); + __builder.OpenElement(55, "input"); + __builder.AddAttribute(56, "value", global::Microsoft.AspNetCore.Components.BindConverter.FormatValue( +#nullable restore +InputValue2 + +#line default +#line hidden +#nullable disable + )); + __builder.AddAttribute(57, "onchange", global::Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => InputValue2 = __value, InputValue2)); + __builder.SetUpdatesAttributeName("value"); + __builder.CloseElement(); + __builder.AddMarkupContent(58, "\n "); + __builder.OpenElement(59, "p"); + __builder.AddContent(60, "Value from InputText: "); + __builder.AddContent(61, +#nullable restore +InputValue2 + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(62, "\n "); + __builder.OpenElement(63, "p"); + __builder.AddContent(64, "Raw value from InputText: "); + __builder.AddContent(65, +#nullable restore +new MarkupString(InputValue2) + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(66, "\n\n
\n\n"); + __builder.OpenElement(67, "div"); + __builder.AddMarkupContent(68, "

Bind through object property

\n "); + __builder.OpenElement(69, "input"); + __builder.AddAttribute(70, "value", global::Microsoft.AspNetCore.Components.BindConverter.FormatValue( +#nullable restore +Container1.Value + +#line default +#line hidden +#nullable disable + )); + __builder.AddAttribute(71, "onchange", global::Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => Container1.Value = __value, Container1.Value)); + __builder.SetUpdatesAttributeName("value"); + __builder.CloseElement(); + __builder.AddMarkupContent(72, "\n "); + __builder.OpenElement(73, "p"); + __builder.AddContent(74, "Value from InputText: "); + __builder.AddContent(75, +#nullable restore +Container1.Value + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(76, "\n "); + __builder.OpenElement(77, "p"); + __builder.AddContent(78, "Raw value from InputText: "); + __builder.AddContent(79, +#nullable restore +new MarkupString(Container1.Value) + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(80, "\n\n
\n\n"); + __builder.OpenElement(81, "div"); + __builder.AddMarkupContent(82, "

Input component with custom event

\n "); + __builder.OpenComponent(83); + __builder.AddComponentParameter(84, nameof(global::BlazorTest.Components.MyInput. +#nullable restore +Param1 + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck( +#nullable restore +InputValue3 + +#line default +#line hidden +#nullable disable + )); + __builder.AddComponentParameter(85, nameof(global::BlazorTest.Components.MyInput. +#nullable restore +ValueChanged + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck>(global::Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, +#nullable restore +MyInputChanged + +#line default +#line hidden +#nullable disable + ))); + __builder.CloseComponent(); + __builder.AddMarkupContent(86, "\n "); + __builder.OpenElement(87, "p"); + __builder.AddContent(88, "Value from InputText: "); + __builder.AddContent(89, +#nullable restore +InputValue3 + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(90, "\n "); + __builder.OpenElement(91, "p"); + __builder.AddContent(92, "Raw value from InputText: "); + __builder.AddContent(93, +#nullable restore +new MarkupString(InputValue3) + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(94, "\n\n
\n\n"); + __builder.OpenElement(95, "div"); + __builder.AddMarkupContent(96, "

Input component with binding

\n "); + __builder.OpenComponent(97); + __builder.AddComponentParameter(98, nameof(global::BlazorTest.Components.MyInput. +#nullable restore +Param1 + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck( +#nullable restore +InputValue4 + +#line default +#line hidden +#nullable disable + )); + __builder.AddComponentParameter(99, nameof(global::BlazorTest.Components.MyInput.Param1Changed), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck>(global::Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => InputValue4 = __value, InputValue4)))); + __builder.CloseComponent(); + __builder.AddMarkupContent(100, "\n "); + __builder.OpenElement(101, "p"); + __builder.AddContent(102, "Value from InputText: "); + __builder.AddContent(103, +#nullable restore +InputValue4 + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.AddMarkupContent(104, "\n "); + __builder.OpenElement(105, "p"); + __builder.AddContent(106, "Raw value from InputText: "); + __builder.AddContent(107, +#nullable restore +new MarkupString(InputValue4) + +#line default +#line hidden +#nullable disable + ); + __builder.CloseElement(); + __builder.CloseElement(); + __builder.AddMarkupContent(108, "\n\n
\n\n"); + __builder.OpenElement(109, "div"); + __builder.AddMarkupContent(110, "

Input, Output components

\n "); + __builder.OpenComponent(111); + __builder.AddComponentParameter(112, nameof(global::BlazorTest.Components.MyInput. +#nullable restore +Param1 + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck( +#nullable restore +InputValue5 + +#line default +#line hidden +#nullable disable + )); + __builder.AddComponentParameter(113, nameof(global::BlazorTest.Components.MyInput.Param1Changed), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck>(global::Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => InputValue5 = __value, InputValue5)))); + __builder.CloseComponent(); + __builder.AddMarkupContent(114, "\n "); + __builder.OpenComponent(115); + __builder.AddComponentParameter(116, nameof(global::BlazorTest.Components.MyOutput. +#nullable restore +Value + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck( +#nullable restore +InputValue5 + +#line default +#line hidden +#nullable disable + )); + __builder.CloseComponent(); + __builder.CloseElement(); + __builder.AddMarkupContent(117, "\n\n
\n\n"); + __builder.OpenElement(118, "div"); + __builder.AddMarkupContent(119, "

Bind InputText, Output component

\n "); + __builder.OpenComponent(120); + __builder.AddComponentParameter(121, nameof(global::Microsoft.AspNetCore.Components.Forms.InputText. +#nullable restore +Value + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck( +#nullable restore +InputValue6 + +#line default +#line hidden +#nullable disable + )); + __builder.AddComponentParameter(122, nameof(global::Microsoft.AspNetCore.Components.Forms.InputText.ValueChanged), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck>(global::Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => InputValue6 = __value, InputValue6)))); + __builder.AddComponentParameter(123, nameof(global::Microsoft.AspNetCore.Components.Forms.InputText.ValueExpression), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck>>(() => InputValue6)); + __builder.CloseComponent(); + __builder.AddMarkupContent(124, "\n "); + __builder.OpenComponent(125); + __builder.AddComponentParameter(126, nameof(global::BlazorTest.Components.MyOutput. +#nullable restore +Value + +#line default +#line hidden +#nullable disable + ), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck( +#nullable restore +InputValue6 + +#line default +#line hidden +#nullable disable + )); + __builder.CloseComponent(); + __builder.CloseElement(); + } +#pragma warning restore 1998 +#nullable restore + + + public class Container + { + public string? Value { get; set; } = ""; + } + + private const string XssUrl = "aaaa<%2Fb>"; + private const string XssUrl2 = "aaaa"; + + [Parameter] + public string UrlParam { get; set; } = ""; + + [SupplyParameterFromQuery(Name = "qs")] + public string QueryParam { get; set; } = ""; + + public string InputValue1 { get; set; } = ""; + public string InputValue2 { get; set; } = ""; + public string InputValue3 { get; set; } = ""; + public string InputValue4 { get; set; } = ""; + public string InputValue5 { get; set; } = ""; + public string InputValue6 { get; set; } = ""; + + public Container Container1 { get; set; } = new Container(); + + protected override void OnInitialized() + { + InputValue1 = XssUrl2; + InputValue2 = XssUrl2; + Container1.Value = XssUrl2; + InputValue3 = XssUrl2; + InputValue4 = XssUrl2; + InputValue5 = XssUrl2; + InputValue6 = XssUrl2; + + } + + private void MyInputChanged(string value) + { + InputValue3 = value; + } + +#line default +#line hidden +#nullable disable + + private sealed class __PrivateComponentRenderModeAttribute : global::Microsoft.AspNetCore.Components.RenderModeAttribute + { + private static global::Microsoft.AspNetCore.Components.IComponentRenderMode ModeImpl => InteractiveServer + ; + public override global::Microsoft.AspNetCore.Components.IComponentRenderMode Mode => ModeImpl; + } + } +} +#pragma warning restore 1591 diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/options b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/options new file mode 100644 index 000000000000..d44a4aa5500b --- /dev/null +++ b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/options @@ -0,0 +1,2 @@ +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj +semmle-extractor-options: /nostdlib /noconfig \ No newline at end of file diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/remoteFlowSource.expected b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/remoteFlowSource.expected new file mode 100644 index 000000000000..2c845e8e4001 --- /dev/null +++ b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/remoteFlowSource.expected @@ -0,0 +1,4 @@ +| Components_Pages_TestPage_razor.g.cs:126:1:126:8 | access to property UrlParam | ASP.NET Core component route parameter | +| Components_Pages_TestPage_razor.g.cs:138:15:138:22 | access to property UrlParam | ASP.NET Core component route parameter | +| Components_Pages_TestPage_razor.g.cs:176:1:176:10 | access to property QueryParam | external | +| Components_Pages_TestPage_razor.g.cs:188:18:188:27 | access to property QueryParam | external | diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/remoteFlowSource.ql b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/remoteFlowSource.ql new file mode 100644 index 000000000000..afccffd11fec --- /dev/null +++ b/csharp/ql/test/library-tests/frameworks/microsoft/aspnetcore/blazor/remoteFlowSource.ql @@ -0,0 +1,7 @@ +import semmle.code.csharp.security.dataflow.flowsources.Remote + +from RemoteFlowSource source, File f +where + source.getLocation().getFile() = f and + f.fromSource() +select source, source.getSourceType()