cs: Adding sources and sinks for ASPNET.Core

Inintial query checkin.
Note: tests require Nuget packages with ASPNET and ASPNETCore in Packages directory, and won't compile without them.
The packages.config should include this:
  <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net461" />
  <package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net461" />
  <package id="Microsoft.AspNetCore.Antiforgery" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Authorization" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Cors" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Cryptography.Internal" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.DataProtection" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.DataProtection.Abstractions" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Diagnostics" version="1.1.2" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.Diagnostics.Abstractions" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Hosting" version="1.1.3" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.Hosting.Abstractions" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Hosting.Server.Abstractions" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Html.Abstractions" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Http" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Http.Abstractions" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Http.Extensions" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Http.Features" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.HttpOverrides" version="1.1.2" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.JsonPatch" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Localization" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.Abstractions" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.ApiExplorer" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.Core" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.Cors" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.DataAnnotations" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.Formatters.Json" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.Localization" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.Razor" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.Razor.Host" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.TagHelpers" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Mvc.ViewFeatures" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Razor" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Razor.Runtime" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.ResponseCaching" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.ResponseCaching.Abstractions" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.ResponseCompression" version="1.0.2" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.Rewrite" version="1.0.2" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.Routing" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Routing.Abstractions" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.AspNetCore.Server.Kestrel" version="1.1.2" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.Server.Kestrel.Https" version="1.1.2" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.Server.WebListener" version="1.1.4" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.StaticFiles" version="1.1.2" targetFramework="net452" />
  <package id="Microsoft.AspNetCore.WebUtilities" version="1.1.2" targetFramework="net451" />
  <package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.1" targetFramework="net451" />
  <package id="Microsoft.Extensions.Primitives" version="2.1.0" targetFramework="net451" />
  <package id="Microsoft.NETCore.App" version="2.0.0" />
  <package id="Microsoft.AspNetCore.Mvc" version="2.1.0" />
  <package id="Microsoft.AspNetCore.Mvc.Core" version="2.1.0" />
  <package id="Microsoft.AspNetCore.Mvc.Abstractions" version="2.1.0" />
  <package id="Microsoft.AspNetCore.Http.Extensions" version="2.1.0" />
  <package id="Microsoft.AspNetCore.Http.Abstractions" version="2.1.0" />
  <package id="Microsoft.AspNetCore.Http.Features" version="2.1.0" />
This commit is contained in:
Denis Levin
2018-08-16 16:27:52 -07:00
committed by calum
parent b130335adb
commit ba9cb5e22d
18 changed files with 862 additions and 1 deletions

View File

@@ -11,6 +11,9 @@ private import semmle.code.csharp.frameworks.system.web.Services
private import semmle.code.csharp.frameworks.system.web.ui.WebControls
private import semmle.code.csharp.frameworks.WCF
private import semmle.code.csharp.frameworks.microsoft.Owin
private import microsoft.code.csharp.frameworks.microsoft.Primitives
private import microsoft.code.csharp.frameworks.microsoft.AspNetCore
/** A data flow source of remote user input. */
abstract class RemoteFlowSource extends DataFlow::Node {
@@ -187,3 +190,46 @@ class ActionMethodParameter extends RemoteFlowSource, DataFlow::ParameterNode {
override
string getSourceType() { result = "ASP.NET MVC action method parameter" }
}
/** A data flow source of remote user input (ASP.NET Core). */
abstract class AspNetCoreRemoteFlowSource extends RemoteFlowSource { }
/** A data flow source of remote user input (ASP.NET query collection). */
class AspNetCoreQueryRemoteFlowSource extends AspNetCoreRemoteFlowSource, DataFlow::ExprNode {
AspNetCoreQueryRemoteFlowSource() {
exists(ValueOrRefType t, Call c, Access ac |
t instanceof MicrosoftAspNetCoreHttpHttpRequest or
t instanceof MicrosoftAspNetCoreHttpQueryCollection or
t instanceof MicrosoftAspNetCoreHttpQueryString |
this.getExpr() = c and
c.getTarget().getDeclaringType() = t
or
this.asExpr() = ac and
ac.getTarget().getDeclaringType() = t
or
c.getTarget().getDeclaringType().hasQualifiedName("Microsoft.AspNetCore.Http", "IQueryCollection") and
c.getTarget().getName() = "TryGetValue" and
this.asExpr() = c.getArgumentForName("value")
)
}
override
string getSourceType() { result = "ASP.NET Core query string" }
}
/**
* A parameter to an Mvc controller action method, viewed as a source of remote user input.
*/
class AspNetCoreActionMethodParameter extends RemoteFlowSource, DataFlow::ParameterNode {
AspNetCoreActionMethodParameter() {
exists(Parameter p |
p = this.getParameter() and
p.fromSource() |
p = any(MicrosoftAspNetCoreMvcController c).getAnActionMethod().getAParameter()
)
}
override
string getSourceType() { result = "ASP.NET Core MVC action method parameter" }
}

View File

@@ -0,0 +1,396 @@
/**
* Provides classes for working with Microsoft.AspNetCore.Mvc
*/
import csharp
import semmle.code.csharp.frameworks.Microsoft
class MicrosoftAspNetCoreNamespace extends Namespace {
MicrosoftAspNetCoreNamespace() {
getParentNamespace() instanceof MicrosoftNamespace and
hasName("AspNetCore")
}
}
/**
* The `Microsoft.AspNetCore.Mvc` namespace.
*/
class MicrosoftAspNetCoreMvcNamespace extends Namespace {
MicrosoftAspNetCoreMvcNamespace() {
getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and
hasName("Mvc")
}
}
/**
* The 'Microsoft.AspNetCore.Mvc.ViewFeatures' namespace
*/
class MicrosoftAspNetCoreMvcViewFeatures extends Namespace {
MicrosoftAspNetCoreMvcViewFeatures() {
getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and
hasName("ViewFeatures")
}
}
/**
* An attribute whose type is in the `Microsoft.AspNetCore.Mvc` namespace.
*/
class MicrosoftAspNetCoreMvcAttribute extends Attribute {
MicrosoftAspNetCoreMvcAttribute() {
getType().getNamespace() = any(MicrosoftAspNetCoreMvcNamespace mvc)
}
}
/**
* An HttpPost attribute in `Microsoft.AspNetCore.Mvc` namespace
*/
class MicrosoftAspNetCoreMvcHttpPostAttribute extends MicrosoftAspNetCoreMvcAttribute {
MicrosoftAspNetCoreMvcHttpPostAttribute() {
getType().hasName("HttpPostAttribute")
}
}
/**
* An HttpPut attribute in `Microsoft.AspNetCore.Mvc` namespace
*/
class MicrosoftAspNetCoreMvcHttpPutAttribute extends MicrosoftAspNetCoreMvcAttribute {
MicrosoftAspNetCoreMvcHttpPutAttribute() {
getType().hasName("HttpPutAttribute")
}
}
/**
* An HttpDelete attribute in `Microsoft.AspNetCore.Mvc` namespace
*/
class MicrosoftAspNetCoreMvcHttpDeleteAttribute extends MicrosoftAspNetCoreMvcAttribute {
MicrosoftAspNetCoreMvcHttpDeleteAttribute() {
getType().hasName("HttpDeleteAttribute")
}
}
/**
* An attribute whose type is `Microsoft.AspNetCore.Mvc.NonAction`.
*/
class MicrosoftAspNetCoreMvcNonActionAttribute extends MicrosoftAspNetCoreMvcAttribute {
MicrosoftAspNetCoreMvcNonActionAttribute() {
getType().hasName("NonActionAttribute")
}
}
/**
* The `Microsoft.AspNetCore.Antiforgery` namespace.
*/
class MicrosoftAspNetCoreAntiforgeryNamespace extends Namespace {
MicrosoftAspNetCoreAntiforgeryNamespace() {
getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and
hasName("Antiforgery")
}
}
/**
* The `Microsoft.AspNetCore.Mvc.Filters` namespace.
*/
class MicrosoftAspNetCoreMvcFilters extends Namespace {
MicrosoftAspNetCoreMvcFilters() {
getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and
hasName("Filters")
}
}
/**
* The `Microsoft.AspNetCore.Mvc.Filters.IFilterMetadataInterface` interface.
*/
class MicrosoftAspNetCoreMvcIFilterMetadataInterface extends Interface {
MicrosoftAspNetCoreMvcIFilterMetadataInterface() {
getNamespace() = any(MicrosoftAspNetCoreMvcFilters h) and
hasName("IFilterMetadata")
}
}
/**
* The `Microsoft.AspNetCore.IAuthorizationFilter` interface.
*/
class MicrosoftAspNetCoreIAuthorizationFilterInterface extends Interface {
MicrosoftAspNetCoreIAuthorizationFilterInterface() {
getNamespace() = any(MicrosoftAspNetCoreMvcFilters h) and
hasName("IAsyncAuthorizationFilter")
}
Method getOnAuthorizationMethod() {
result = getAMethod("OnAuthorizationAsync")
}
}
/**
* The `Microsoft.AspNetCore.IAntiforgery` interface.
*/
class MicrosoftAspNetCoreIAntiForgeryInterface extends Interface {
MicrosoftAspNetCoreIAntiForgeryInterface() {
getNamespace() = any(MicrosoftAspNetCoreAntiforgeryNamespace h) and
hasName("IAntiforgery")
}
/**
* Gets the `ValidateRequestAsync` method.
*/
Method getValidateMethod() {
result = getAMethod("ValidateRequestAsync")
}
}
/**
* The `Microsoft.AspNetCore.DefaultAntiForgery` class, or another user-supplied class that implements IAntiForgery
*/
class AntiForgeryClass extends Class {
AntiForgeryClass () {
getABaseInterface*() instanceof MicrosoftAspNetCoreIAntiForgeryInterface
}
/**
* Gets the `ValidateRequestAsync` method.
*/
Method getValidateMethod() {
result = getAMethod("ValidateRequestAsync")
}
}
/**
* Authorization filter class defined by AspNetCore or the user
*/
class AuthorizationFilterClass extends Class {
AuthorizationFilterClass() {
getABaseInterface*() instanceof MicrosoftAspNetCoreIAuthorizationFilterInterface
}
/** Gets the `OnAuthorization` method provided by this filter. */
Method getOnAuthorizationMethod() {
result = getAMethod("OnAuthorizationAsync")
}
}
/**
* An attribute whose type has a name like `[Auto...]Validate[...]Anti[Ff]orgery[...Token]Attribute`.
*/
class ValidateAntiForgeryAttribute extends Attribute {
ValidateAntiForgeryAttribute() {
getType().getName().matches("%Validate%Anti_orgery%Attribute")
}
}
/**
* An class that has a name like `[Auto...]Validate[...]Anti[Ff]orgery[...Token]` and implements IFilterMetadata interface
* This class can be added to a collection of global MvcOptions.Filters collection
*/
class ValidateAntiforgeryTokenAuthorizationFilter extends Class {
ValidateAntiforgeryTokenAuthorizationFilter() {
getABaseInterface*() instanceof MicrosoftAspNetCoreMvcIFilterMetadataInterface and
getName().matches("%Validate%Anti_orgery%")
}
}
/**
* The `Microsoft.AspNetCore.Mvc.Filters.FilterCollection` class
*/
class MicrosoftAspNetCoreMvcFilterCollection extends Class {
MicrosoftAspNetCoreMvcFilterCollection() {
getNamespace() = any(MicrosoftAspNetCoreMvcFilters h) and
hasName("FilterCollection")
}
Method getAddMethod() {
result = getAMethod("Add") or
result = getABaseType().getAMethod("Add")
}
}
/**
* The `Microsoft.AspNetCore.Mvc.MvcOptions` class .
*/
class MicrosoftAspNetCoreMvcOptions extends Class {
MicrosoftAspNetCoreMvcOptions() {
getNamespace() = any(MicrosoftAspNetCoreMvcNamespace mvc) and
hasName("MvcOptions")
}
Property getFilterCollectionProperty() {
result = getProperty("Filters")
}
}
/**
* The base class for controllers in MVC, i.e. `Microsoft.AspNetCore.Mvc.Controller` or `Microsoft.AspNetCore.Mvc.ControllerBase` class.
*/
class MicrosoftAspNetCoreMvcControllerBaseClass extends Class {
MicrosoftAspNetCoreMvcControllerBaseClass() {
getNamespace() = any(MicrosoftAspNetCoreMvcNamespace h) and
(hasName("Controller") or hasName("ControllerBase"))
}
}
/**
* An subtype of `Microsoft.AspNetCore.Mvc.Controller` or `Microsoft.AspNetCore.Mvc.ControllerBase`.
*/
class MicrosoftAspNetCoreMvcController extends Class {
MicrosoftAspNetCoreMvcController() {
getABaseType*() instanceof MicrosoftAspNetCoreMvcControllerBaseClass
}
/** Gets an action method for this controller. */
Method getAnActionMethod() {
result = getAMethod() and
result.isPublic() and
not result.isStatic() and
not result.getAnAttribute() instanceof MicrosoftAspNetCoreMvcNonActionAttribute
}
/**
* Gets an "action" method handling POST, PUT and DELETE request, which may be called by the MVC framework in response to a user
* request.
*/
Method getAnActionModifyingMethod() {
result = getAnActionMethod() and
(result.getAnAttribute() instanceof MicrosoftAspNetCoreMvcHttpPostAttribute or
result.getAnAttribute() instanceof MicrosoftAspNetCoreMvcHttpPutAttribute or
result.getAnAttribute() instanceof MicrosoftAspNetCoreMvcHttpDeleteAttribute)
}
/** Gets a `Redirect..` method. */
Method getARedirectMethod() {
result = this.getAMethod() and
result.getName().matches("Redirect%")
}
}
/*
* Returns a string corresponding to an HTTP method used in the action method
*/
string httpMethodType(Method m) {
m.getAnAttribute() instanceof MicrosoftAspNetCoreMvcHttpPostAttribute and result = "POST" or
m.getAnAttribute() instanceof MicrosoftAspNetCoreMvcHttpPutAttribute and result = "PUT" or
m.getAnAttribute() instanceof MicrosoftAspNetCoreMvcHttpDeleteAttribute and result = "DELETE"
}
/**
* The `Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper` class.
*/
class MicrosoftAspNetCoreMvcHtmlHelperClass extends Class {
MicrosoftAspNetCoreMvcHtmlHelperClass() {
getNamespace() = any(MicrosoftAspNetCoreMvcViewFeatures mvc) and
hasName("HtmlHelper")
}
/** Gets the `Raw` method. */
Method getRawMethod() {
result = getAMethod("Raw")
}
}
/**
* Class deriving from Microsoft.AspNetCore.Mvc.Razor.RazorPageBase, implements Razor page in ASPNET Core
*/
class MicrosoftAspNetCoreMvcRazorPageBase extends Class {
MicrosoftAspNetCoreMvcRazorPageBase () {
this.getABaseType*().hasQualifiedName("Microsoft.AspNetCore.Mvc.Razor", "RazorPageBase")
}
Method getWriteLiteralMethod() {
result = getAMethod("WriteLiteral")
}
}
/**
* Class deriving from Microsoft.AspNetCore.Http.HttpRequest, implements HttpRequest in ASPNET Core
*/
class MicrosoftAspNetCoreHttpHttpRequest extends Class {
MicrosoftAspNetCoreHttpHttpRequest() {
this.getABaseType*().hasQualifiedName("Microsoft.AspNetCore.Http", "HttpRequest")
}
}
/**
* Class deriving from Microsoft.AspNetCore.Http.HttpResponse, implements HttpResponse in ASPNET Core
*/
class MicrosoftAspNetCoreHttpHttpResponse extends Class {
MicrosoftAspNetCoreHttpHttpResponse() {
this.getABaseType*().hasQualifiedName("Microsoft.AspNetCore.Http", "HttpResponse")
}
Method getRedirectMethod() {
result = this.getAMethod("Redirect")
}
Property getHeadersProperty() {
result = this.getProperty("Headers")
}
}
/**
* Class is a wrapper around the collection of cookies in the response
*/
class MicrosoftAspNetCoreHttpResponseCookies extends Interface{
MicrosoftAspNetCoreHttpResponseCookies() {
this.hasQualifiedName("Microsoft.AspNetCore.Http.IResponseCookies")
}
Method getAppendMethod() {
result = this.getAMethod("Append")
}
}
/**
* Class Microsoft.AspNetCore.Http.QueryString, holds query string in ASP.NET Core
*/
class MicrosoftAspNetCoreHttpQueryString extends Struct {
MicrosoftAspNetCoreHttpQueryString() {
this.hasQualifiedName("Microsoft.AspNetCore.Http", "QueryString")
}
}
/**
* Class implementing IQueryCollection, holds parsed query string in ASP.NET Core
*/
class MicrosoftAspNetCoreHttpQueryCollection extends RefType {
MicrosoftAspNetCoreHttpQueryCollection() {
this.getABaseInterface().hasQualifiedName("Microsoft.AspNetCore.Http", "IQueryCollection")
}
}
/**
* Helper class for setting headers
*/
class MicrosoftAspNetCoreHttpResponseHeaders extends RefType {
MicrosoftAspNetCoreHttpResponseHeaders() {
this.hasQualifiedName("Microsoft.AspNetCore.Http.Headers","ResponseHeaders")
}
Property getLocationProperty() {
result = this.getProperty("Location")
}
}
class MicrosoftAspNetCoreHttpHeaderDictionaryExtensions extends RefType {
MicrosoftAspNetCoreHttpHeaderDictionaryExtensions () {
this.hasQualifiedName("Microsoft.AspNetCore.Http","HeaderDictionaryExtensions")
}
Method getAppendMethod() {
result = this.getAMethod("Append")
}
Method getAppendCommaSeparatedValuesMethod() {
result = this.getAMethod("AppendCommaSeparatedValues")
}
Method getSetCommaSeparatedValuesMethod() {
result = this.getAMethod("SetCommaSeparatedValues")
}
}
/**
* Class Microsoft.AspNetCore.Html.HtmlString, suppose to wrap HTML-encoded string in ASP.NET Core
* Untrusted and unsanitized data should never flow there.
*/
class MicrosoftAspNetCoreHttpHtmlString extends Class {
MicrosoftAspNetCoreHttpHtmlString() {
this.hasQualifiedName("Microsoft.AspNetCore.Html", "HtmlString")
}}

View File

@@ -0,0 +1,32 @@
/**
* Provides definitions for working with classes in Microsoft.Extensions.Primitives namespace
*/
import csharp
module ExtensionPrimitives {
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
/**
* StringValues class used in many .Net Core libraries. Requreres special LibraryTypeDataFlow flow.
*/
class StringValues extends Struct {
StringValues() {
this.hasQualifiedName("Microsoft.Extensions.Primitives", "StringValues")
}
}
/**
* Custom flow through StringValues.StringValues library class
*/
class StringValuesFlow extends LibraryTypeDataFlow, StringValues {
override predicate callableFlow(CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue) {
c = any(Callable ca | this = ca.getDeclaringType())
and
(source = any(CallableFlowSourceArg a) or
source = any(CallableFlowSourceQualifier q))
and
sink = any(CallableFlowSinkReturn r)
and preservesValue = false
}
}
}

View File

@@ -0,0 +1,37 @@
/** Definitions related to the namespace `System.Web.WebPages`, ASP.NET */
import csharp
private import semmle.code.csharp.frameworks.system.Web
/** The `System.Web.WebPages` namespace. */
class SystemWebWebPagesNamespace extends Namespace {
SystemWebWebPagesNamespace() {
getParentNamespace() instanceof SystemWebNamespace and
hasName("WebPages")
}
}
/** The `System.Web.WebPages.WebPageExecutingBase` class. */
class SystemWebWebPagesWebPageExecutingBaseClass extends Class {
SystemWebWebPagesWebPageExecutingBaseClass() {
getNamespace() instanceof SystemWebWebPagesNamespace and
hasName("WebPageExecutingBase")
}
}
/**
* This class describes any class that derives from System.Web.WebPages.WebPageExecutingBase
*/
class WebPageClass extends Class {
WebPageClass () {
this.getBaseClass*() instanceof SystemWebWebPagesWebPageExecutingBaseClass
}
Method getWriteLiteralMethod() {
result = getAMethod("WriteLiteral")
}
Method getWriteLiteralToMethod() {
result = getAMethod("WriteLiteralTo")
}
}

View File

@@ -9,6 +9,7 @@ module UrlRedirect {
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.system.web.Mvc
import semmle.code.csharp.security.Sanitizers
import semmle.code.csharp.frameworks.microsoft.AspNetCore
/**
* A data flow source for unvalidated URL redirect vulnerabilities.
@@ -149,4 +150,65 @@ module UrlRedirect {
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
/**
* A URL argument to a call to `HttpResponse.Redirect()` or `Controller.Redirect()`, that is a
* sink for URL redirects.
*/
class AspNetCoreRedirectSink extends Sink {
AspNetCoreRedirectSink() {
exists(MethodCall mc |
mc.getTarget() = any(MicrosoftAspNetCoreHttpHttpResponse response).getRedirectMethod() or
mc.getTarget() = any(MicrosoftAspNetCoreMvcController response).getARedirectMethod()
|
// Response.Redirect uses 'location' parameter
this.getExpr() = mc.getArgumentForName("location") or
// Redirect uses the parameter name 'url'
this.getExpr() = mc.getArgumentForName("url") or
// Controller.RedirectToAction*
this.getExpr() = mc.getArgumentForName("actionName") or
// Controller.RedirectToRoute*
this.getExpr() = mc.getArgumentForName("routeName") or
// Controller.RedirectToPage*
this.getExpr() = mc.getArgumentForName("pageName")
)
}
}
/**
* Anything that is setting "location" header in the response headers.
*/
class AspNetCoreLocationHeaderSink extends Sink {
AspNetCoreLocationHeaderSink () {
// ResponseHeaders.Location = <user-provided value>
exists(Assignment propAssign, PropertyAccess pa |
pa.getTarget() = any(MicrosoftAspNetCoreHttpResponseHeaders headers).getLocationProperty() and
pa = propAssign.getLValue() and
this.asExpr() = propAssign.getRValue())
or // HttpResponse.Headers.Append("location", <user-provided value>)
exists(MethodCall mc, MicrosoftAspNetCoreHttpHeaderDictionaryExtensions ext |
mc.getTarget() = ext.getAppendMethod() or
mc.getTarget() = ext.getAppendCommaSeparatedValuesMethod() or
mc.getTarget() = ext.getSetCommaSeparatedValuesMethod() |
mc.getArgumentForName("key").getValue().toLowerCase() = "location" and
this.getExpr() = mc.getArgument(2))
or // HttpResponse.Headers.Add("location", <user-provided value>)
exists(RefType cl, MicrosoftAspNetCoreHttpHttpResponse resp, PropertyAccess c, MethodCall add |
c.getTarget() = resp.getHeadersProperty() and
add.getTarget() = cl.getAMethod("Add") and
c = add.getQualifier() and
add.getArgument(0).getValue().toLowerCase() = "location" and
this.getExpr() = add.getArgument(1))
or // HttpResponse.Headers["location"] = <user-provided value>
exists(RefType cl, MicrosoftAspNetCoreHttpHttpResponse resp, IndexerAccess ci, Call cs, PropertyAccess pa |
pa.getTarget() = resp.getHeadersProperty() and
ci.getTarget() = cl.getAnIndexer() and
pa = ci.getQualifier() and
cs.getTarget() = cl.getAnIndexer().getSetter() and
cs.getArgument(0).getValue().toLowerCase() = "location" and
this.asExpr() = cs.getArgument(1))
}
}
}

View File

@@ -9,11 +9,13 @@ module XSS {
import semmle.code.csharp.frameworks.system.Net
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.system.web.Mvc
import semmle.code.csharp.frameworks.system.web.WebPages
import semmle.code.csharp.frameworks.system.web.UI
import semmle.code.csharp.frameworks.system.web.ui.WebControls
import semmle.code.csharp.frameworks.system.windows.Forms
import semmle.code.csharp.security.Sanitizers
import semmle.code.asp.AspNet
import semmle.code.csharp.frameworks.microsoft.AspNetCore
/**
* Holds if there is tainted flow from `source` to `sink` that may lead to a
@@ -514,6 +516,75 @@ module XSS {
this.getExpr() = any(ObjectCreation oc | oc.getTarget().getDeclaringType().hasQualifiedName("System.Net.Http", "StringContent")).getArgumentForName("content")
}
}
/**
* An expression that is used as an argument to `Page.WriteLiteral`, typically in
* a `.cshtml` file.
*/
class WebPageWriteLiteralSink extends Sink, HtmlSink {
WebPageWriteLiteralSink() {
this.getExpr() = any(WebPageClass h).getWriteLiteralMethod().getACall().getAnArgument()
}
override string explanation() {
result = "System.Web.WebPages.WebPage.WriteLiteral() method"
}
}
/**
* An expression that is used as an argument to `Page.WriteLiteralTo`, typically in
* a `.cshtml` file.
*/
class WebPageWriteLiteralToSink extends Sink, HtmlSink {
WebPageWriteLiteralToSink() {
this.getExpr() = any(WebPageClass h).getWriteLiteralToMethod().getACall().getAnArgument()
}
override string explanation() {
result = "System.Web.WebPages.WebPage.WriteLiteralTo() method"
}
}
abstract class AspNetCoreSink extends Sink, HtmlSink { }
/**
* An expression that is used as an argument to `HtmlHelper.Raw`, typically in
* a `.cshtml` file.
*/
class MicrosoftAspNetCoreMvcHtmlHelperRawSink extends AspNetCoreSink {
MicrosoftAspNetCoreMvcHtmlHelperRawSink() {
this.getExpr() = any(MicrosoftAspNetCoreMvcHtmlHelperClass h).getRawMethod().getACall().getAnArgument()
}
override string explanation() {
result = "Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.Raw() method"
}
}
/**
* An expression that is used as an argument to `Page.WriteLiteral` in ASP.NET 6.0 razor page, typically in
* a `.cshtml` file.
*/
class MicrosoftAspNetRazorPageWriteLiteralSink extends AspNetCoreSink {
MicrosoftAspNetRazorPageWriteLiteralSink() {
this.getExpr() = any(MicrosoftAspNetCoreMvcRazorPageBase h).getWriteLiteralMethod().getACall().getAnArgument()
}
override string explanation() {
result = "Microsoft.AspNetCore.Mvc.Razor.RazorPageBase.WriteLiteral() method"
}
}
/**
* HtmlString that may be rendered as is need to have sanitized value
*/
class MicrosoftAspNetHtmlStringSink extends AspNetCoreSink {
MicrosoftAspNetHtmlStringSink() {
exists (ObjectCreation c, MicrosoftAspNetCoreHttpHtmlString s |
c.getTarget() = s.getAConstructor() and
this.asExpr() = c.getAnArgument())
}
}
}
private Type getMemberType(Member m) {

View File

@@ -0,0 +1,50 @@
// semmle-extractor-options: /r:System.Web.dll /r:${testdir}/../../../../../packages/Microsoft.AspNet.WebPages.3.2.3/lib/net45/System.Web.WebPages.dll /r:${testdir}/../../../../../packages/Microsoft.AspNet.Mvc.5.2.3/lib/net45/System.Web.Mvc.dll
namespace ASP
{
using System;
using System.IO;
using System.Net;
using System.Web;
using System.Web.UI;
using System.Web.WebPages;
public class _Page_Views_Home_Contact_cshtml : System.Web.Mvc.WebViewPage<dynamic>
{
public _Page_Views_Home_Contact_cshtml()
{
}
public override void Execute()
{
Layout = "~/_SiteLayout.cshtml";
Page.Title = "Contact";
var sayHi = Request.QueryString["sayHi"];
if (sayHi.IsEmpty())
{
WriteLiteral("<script>alert(\"XSS via WriteLiteral\")</script>"); // GOOD: hard-coded, not user input
}
else
{
WriteLiteral(sayHi); // BAD: user input flows to HTML unencoded
WriteLiteral(HttpUtility.HtmlEncode(sayHi)); // Good: user input is encoded before it flows to HTML
}
if (sayHi.IsEmpty())
{
WriteLiteralTo(Output, "<script > alert(\"XSS via WriteLiteralTo\")</script>"); // GOOD: hard-coded, not user input
}
else
{
WriteLiteralTo(Output, sayHi); // BAD: user input flows to HTML unencoded
WriteLiteralTo(Output, Html.Encode(sayHi)); // Good: user input is encoded before it flows to HTML
}
BeginContext("~/Views/Home/Contact.cshtml", 288, 32, false);
Write(Html.Raw("<script>alert(\"XSS via Html.Raw()\")</script>")); // GOOD: hard-coded, not user input
Write(Html.Raw(Request.QueryString["sayHi"])); // BAD: user input flows to HTML unencoded
Write(Html.Raw(HttpContext.Current.Server.HtmlEncode(Request.QueryString["sayHi"]))); // Good: user input is encoded before it flows to HTML
EndContext("~/Views/Home/Contact.cshtml", 288, 32, false);
}
}
}

View File

@@ -0,0 +1,3 @@
| XSS.cs:28:30:28:34 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteral() method. | XSS.cs:21:25:21:43 | access to property QueryString | User-provided value |
| XSS.cs:38:40:38:44 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteralTo() method. | XSS.cs:21:25:21:43 | access to property QueryString | User-provided value |
| XSS.cs:45:28:45:55 | access to indexer | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:45:28:45:46 | access to property QueryString | User-provided value |

View File

@@ -0,0 +1 @@
Security Features/CWE-079/XSS.ql

View File

@@ -0,0 +1,79 @@
// semmle-extractor-options: /r:netstandard.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.Core.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.Core.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Antiforgery.1.1.2/lib/net451/Microsoft.AspNetCore.Antiforgery.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.ViewFeatures.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.ViewFeatures.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.Abstractions.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.Abstractions.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Http.Abstractions.1.1.2\lib\net451\Microsoft.AspNetCore.Http.Abstractions.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Html.Abstractions.1.1.2/lib/netstandard1.0/Microsoft.AspNetCore.Html.Abstractions.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Http.Features.1.1.2\lib\net451\Microsoft.AspNetCore.Http.Features.dll /r:${testdir}/../../../../../packages\Microsoft.Extensions.Primitives.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Html;
using Microsoft.Extensions.Primitives;
namespace Testing.Controllers
{
public class HomeViewModel
{
public string Message { get; set; }
public HtmlString Description { get; set; }
}
public class TestController : Controller
{
public IActionResult Index()
{
// BAD: flow of content type to
var v = new ViewResult();
v.ViewData["BadData"] = new HtmlString(Request.Query["Bad data"]);
StringValues vOut;
Request.Query.TryGetValue("Foo", out vOut);
// BAD: via Enumerable
v.ViewData["FooFirst"] = new HtmlString(vOut.First());
// BAD: via toArray
v.ViewData["FooArray0"] = new HtmlString(vOut.ToArray()[0]);
// BAD: via implicit conversion operator
v.ViewData["FooImplicit"] = new HtmlString(vOut);
return v;
}
[HttpPost("Test")]
[ValidateAntiForgeryToken]
public IActionResult Submit([FromQuery] string foo)
{
var view = new ViewResult();
//BAD: flow of submitted value to view in HtmlString.
view.ViewData["FOO"] = new HtmlString(foo);
return view;
}
public IActionResult IndexToModel()
{
//BAD: flow of submitted value to view in HtmlString.
HtmlString v = new HtmlString(Request.QueryString.Value);
return View(new HomeViewModel() { Message = "Message from Index", Description = v });
}
public IActionResult About()
{
//BAD: flow of submitted value to view in HtmlString.
HtmlString v = new HtmlString(Request.Query["Foo"].ToString());
//BAD: flow of submitted value to view in HtmlString.
HtmlString v1 = new HtmlString(Request.Query["Foo"][0]);
return View(new HomeViewModel() { Message = "Message from About", Description = v });
}
public IActionResult Contact()
{
//BAD: flow of user content type to view in HtmlString.
HtmlString v = new HtmlString(Request.ContentType);
//BAD: flow of headers to view in HtmlString.
HtmlString v1 = new HtmlString(value: Request.Headers["Foo"]);
return View(new HomeViewModel() { Message = "Message from Contact", Description = v });
}
}
}

View File

@@ -0,0 +1,7 @@
| XSS.cs:22:52:22:76 | call to operator implicit conversion | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:22:52:22:64 | access to property Query | User-provided value |
| XSS.cs:45:51:45:53 | access to parameter foo | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:41:56:41:58 | foo | User-provided value |
| XSS.cs:53:43:53:67 | access to property Value | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:53:43:53:67 | access to property Value | User-provided value |
| XSS.cs:60:43:60:73 | call to method ToString | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:60:43:60:55 | access to property Query | User-provided value |
| XSS.cs:63:44:63:66 | access to indexer | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:63:44:63:56 | access to property Query | User-provided value |
| XSS.cs:71:43:71:61 | access to property ContentType | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:71:43:71:61 | access to property ContentType | User-provided value |
| XSS.cs:74:51:74:72 | call to operator implicit conversion | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:74:51:74:65 | access to property Headers | User-provided value |

View File

@@ -0,0 +1 @@
Security Features/CWE-079/XSS.ql

View File

@@ -0,0 +1,65 @@
// semmle-extractor-options: /r:netstandard.dll /r:${testdir}/../../../../packages\Microsoft.AspNetCore.Mvc.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.dll /r:${testdir}/../../../../packages\Microsoft.AspNetCore.Mvc.Core.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Core.dll /r:${testdir}/../../../../packages\Microsoft.AspNetCore.Http.Extensions.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Extensions.dll /r:${testdir}/../../../../packages\Microsoft.AspNetCore.Http.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Abstractions.dll /r:${testdir}/../../../../packages\Microsoft.AspNetCore.Mvc.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Abstractions.dll /r:${testdir}/../../../../packages\Microsoft.AspNetCore.Http.Features.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Features.dll /r:${testdir}/../../../../packages\Microsoft.Extensions.Primitives.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Headers;
using Microsoft.AspNetCore.Mvc;
namespace Testing.Controllers
{
public class SomeController : ControllerBase
{
private static string SomeValue = "HeaderValue";
[HttpPost]
public void Post([FromBody] string value)
{
// BAD: straight up controller redirect
Redirect(value);
// BAD: Setting response headers collection, location = redirect
Response.Headers["location"] = value;
// GOOD: Setting response header to a constant value
Response.Headers["location"] = SomeValue;
// BAD: Setting response headers collection, location = redirect via add method
Response.Headers.Add("location", value);
// GOOD: Setting response header to a constant value
Response.Headers.Add("location", "foo");
// BAD: redirect via location
Response.Headers.SetCommaSeparatedValues("location", value);
// BAD = redirect via setting location value from tainted source
Response.Headers.Append("location", value);
// BAD: redirect via setting location header from comma-separated values
Response.Headers.AppendCommaSeparatedValues("location", value);
// BAD: tainted redirect to Action
RedirectToActionPermanent("Error" + value);
}
// PUT: api/Some/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
RedirectToPage(value);
var headers = new ResponseHeaders(Response.Headers);
// BAD: redirect via header helper class
headers.Location = new Uri(value);
// BAD: response redirect
Response.Redirect(value);
// GOOD: whitelisted redirect
if(Url.IsLocalUrl(value))
Redirect(value);
}
}
}

View File

@@ -0,0 +1,10 @@
| TestController.cs:18:22:18:26 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirect.cs:15:44:15:48 | value | user-provided value |
| UrlRedirect.cs:21:44:21:48 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirect.cs:15:44:15:48 | value | user-provided value |
| UrlRedirect.cs:27:46:27:50 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirect.cs:15:44:15:48 | value | user-provided value |
| UrlRedirect.cs:33:66:33:70 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirect.cs:15:44:15:48 | value | user-provided value |
| UrlRedirect.cs:36:49:36:53 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirect.cs:15:44:15:48 | value | user-provided value |
| UrlRedirect.cs:39:69:39:73 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirect.cs:15:44:15:48 | value | user-provided value |
| UrlRedirect.cs:42:39:42:53 | ... + ... | Untrusted URL redirection due to $@. | UrlRedirect.cs:15:44:15:48 | value | user-provided value |
| UrlRedirect.cs:50:28:50:32 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirect.cs:47:51:47:55 | value | user-provided value |
| UrlRedirect.cs:55:32:55:45 | object creation of type Uri | Untrusted URL redirection due to $@. | UrlRedirect.cs:47:51:47:55 | value | user-provided value |
| UrlRedirect.cs:58:31:58:35 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirect.cs:47:51:47:55 | value | user-provided value |

View File

@@ -1,4 +1,4 @@
// semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Web.cs /r:System.Collections.Specialized.dll
// semmle-extractor-options: ${testdir}/../../../../resources/stubs/System.Web.cs /r:System.Collections.Specialized.dll
using System;
using System.Web;

View File

@@ -0,0 +1 @@
Security Features/CWE-601/UrlRedirect.ql