Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| BlazorTest/Components/Pages/TestPage.razor:10:29:10:36 | access to property UrlParam | ASP.NET Core component route parameter |
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | ASP.NET Core component route parameter |
| BlazorTest/Components/Pages/TestPage.razor:19:38:19:47 | access to property QueryParam | external |
| BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | external |
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import semmle.code.csharp.security.dataflow.flowsources.Remote
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the motivation for using integration tests instead of normal QL tests? I find it quite difficult to run
(specific) integration tests locally...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was mostly because the test was originally from Tamás's WIP PR. And I wasn't sure about testing Blazor components as a QL test (I'm unsure if I'll need to have a full project). I'll experiment with moving it.

You are correct about the integration test being a more complicated way to run the test locally.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not easy to do, then let's do only the integration test.


from RemoteFlowSource source, File f
where
source.getLocation().getFile() = f and
(f.fromSource() or f.getExtension() = "razor")
select source, source.getSourceType()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| BlazorTest/Components/Pages/TestPage.razor:10:29:10:36 | access to property UrlParam | ASP.NET Core component route parameter |
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | ASP.NET Core component route parameter |
| BlazorTest/Components/Pages/TestPage.razor:19:38:19:47 | access to property QueryParam | external |
| BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | external |
Original file line number Diff line number Diff line change
@@ -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() or f.getExtension() = "razor")
select source, source.getSourceType()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| BlazorTest/Components/Pages/TestPage.razor:10:29:10:36 | access to property UrlParam | ASP.NET Core component route parameter |
| BlazorTest/Components/Pages/TestPage.razor:11:48:11:55 | access to property UrlParam | ASP.NET Core component route parameter |
| BlazorTest/Components/Pages/TestPage.razor:19:38:19:47 | access to property QueryParam | external |
| BlazorTest/Components/Pages/TestPage.razor:20:60:20:69 | access to property QueryParam | external |
Original file line number Diff line number Diff line change
@@ -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() or f.getExtension() = "razor")
select source, source.getSourceType()
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/** Provides classes for working with `Microsoft.AspNetCore.Components` */

import csharp
import semmle.code.csharp.frameworks.Microsoft

Check warning

Code scanning / CodeQL

Redundant import Warning

Redundant import, the module is already imported inside
semmle.code.csharp.frameworks.microsoft.AspNetCore
.
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.ParameterAttribute` class. */
class MicrosoftAspNetCoreComponentsParamaterAttributeClass extends MicrosoftAspNetCoreComponentsClass
{
MicrosoftAspNetCoreComponentsParamaterAttributeClass() { this.hasName("ParameterAttribute") }
}

/** 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.
*
* And example of a route parameter is `@page "/counter/{id:int}/{other?}/{*rest}"`, from this we're getting the `id`, `other` and `rest` parameters.
*/
private string getARouteParameter() {
result = this.getRouteAttributeUrl().splitAt("{").regexpCapture("\\*?([^:?}]+)[:?}](.*)", 1)
}

/** Gets a property attributed with `[Parameter]` attribute. */
Property getAParameterProperty() {
result = this.getAProperty() and
result.getAnAttribute() instanceof MicrosoftAspNetCoreComponentsParameterAttribute
}

/** Gets a property whose value is populated from route parameters. */
Property getARouteParameterProperty() {
result = this.getAParameterProperty() and
exists(string urlParamName | urlParamName = this.getARouteParameter() |
result.getName().toLowerCase() = urlParamName.toLowerCase()
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Joining on names is always a candidate for a bad join, so it is usually safer to force multi-column joins, e.g.

  pragma[nomagic]
  private Property getAParameterProperty(string name) {
    result = this.getAProperty() and
    result.getAnAttribute() instanceof MicrosoftAspNetCoreComponentsParameterAttribute and
    name = result.getName().toLowerCase()
  }

  pragma[nomagic]
  private string getARouteParameter() {
    exists(string s |
      s = this.getRouteAttributeUrl().splitAt("{").regexpCapture("\\*?([^:?}]+)[:?}](.*)", 1) and
      result = s.toLowerCase()
    )
  }

  Property getARouteParameterProperty() {
    exists(string name |
      result = this.getAParameterProperty(name) and
      name = this.getARouteParameter()
    )
  }


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" }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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). */
Expand Down
Loading