C#: Add Query suffix to libraries that should only be imported by queries

This commit is contained in:
Tom Hvitved
2021-07-02 10:47:41 +02:00
parent 1d56748eed
commit c812d4e4e8
65 changed files with 473 additions and 468 deletions

View File

@@ -9,7 +9,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.ExternalAPIs
import semmle.code.csharp.security.dataflow.ExternalAPIsQuery
from ExternalAPIUsedWithUntrustedData externalAPI
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,

View File

@@ -11,7 +11,7 @@
import csharp
import semmle.code.csharp.dataflow.TaintTracking
import semmle.code.csharp.security.dataflow.ExternalAPIs
import semmle.code.csharp.security.dataflow.ExternalAPIsQuery
import DataFlow::PathGraph
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -15,7 +15,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.TaintedPath::TaintedPath
import semmle.code.csharp.security.dataflow.TaintedPathQuery::TaintedPath
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -13,7 +13,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.ZipSlip::ZipSlip
import semmle.code.csharp.security.dataflow.ZipSlipQuery::ZipSlip
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration zipTaintTracking, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -14,7 +14,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.CommandInjection::CommandInjection
import semmle.code.csharp.security.dataflow.CommandInjectionQuery::CommandInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -15,7 +15,7 @@
import csharp
import semmle.code.csharp.security.dataflow.flowsources.Stored
import semmle.code.csharp.security.dataflow.CommandInjection::CommandInjection
import semmle.code.csharp.security.dataflow.CommandInjectionQuery::CommandInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
class StoredTaintTrackingConfiguration extends TaintTrackingConfiguration {

View File

@@ -14,7 +14,7 @@
import csharp
import semmle.code.csharp.security.dataflow.flowsources.Stored
import semmle.code.csharp.security.dataflow.XSS::XSS
import semmle.code.csharp.security.dataflow.XSSQuery::XSS
import semmle.code.csharp.dataflow.DataFlow2
import DataFlow2::PathGraph

View File

@@ -13,7 +13,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.XSS::XSS
import semmle.code.csharp.security.dataflow.XSSQuery::XSS
import PathGraph
from XssNode source, XssNode sink, string message

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.SqlInjection
import semmle.code.csharp.security.dataflow.SqlInjectionQuery
import semmle.code.csharp.security.dataflow.flowsources.Stored
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.SqlInjection::SqlInjection
import semmle.code.csharp.security.dataflow.SqlInjectionQuery::SqlInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
string getSourceType(DataFlow::Node node) {

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.LDAPInjection::LDAPInjection
import semmle.code.csharp.security.dataflow.LDAPInjectionQuery::LDAPInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.LDAPInjection::LDAPInjection
import semmle.code.csharp.security.dataflow.LDAPInjectionQuery::LDAPInjection
import semmle.code.csharp.security.dataflow.flowsources.Stored
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph

View File

@@ -14,7 +14,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.CodeInjection::CodeInjection
import semmle.code.csharp.security.dataflow.CodeInjectionQuery::CodeInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.ResourceInjection::ResourceInjection
import semmle.code.csharp.security.dataflow.ResourceInjectionQuery::ResourceInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.MissingXMLValidation::MissingXMLValidation
import semmle.code.csharp.security.dataflow.MissingXMLValidationQuery::MissingXMLValidation
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.LogForging::LogForging
import semmle.code.csharp.security.dataflow.LogForgingQuery::LogForging
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -14,7 +14,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.CleartextStorage::CleartextStorage
import semmle.code.csharp.security.dataflow.CleartextStorageQuery::CleartextStorage
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -14,7 +14,7 @@
*/
import csharp
import semmle.code.csharp.security.cryptography.EncryptionKeyDataFlow::EncryptionKeyDataFlow
import semmle.code.csharp.security.cryptography.EncryptionKeyDataFlowQuery::EncryptionKeyDataFlow
/**
* The creation of a literal byte array.

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.ExposureOfPrivateInformation::ExposureOfPrivateInformation
import semmle.code.csharp.security.dataflow.ExposureOfPrivateInformationQuery::ExposureOfPrivateInformation
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.UnsafeDeserialization::UnsafeDeserialization
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery::UnsafeDeserialization
from Call deserializeCall, Sink sink
where deserializeCall.getAnArgument() = sink.asExpr()

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.UnsafeDeserialization::UnsafeDeserialization
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery::UnsafeDeserialization
import DataFlow::PathGraph
from TaintTrackingConfig config, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.UrlRedirect::UrlRedirect
import semmle.code.csharp.security.dataflow.UrlRedirectQuery::UrlRedirect
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -13,7 +13,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.XMLEntityInjection::XMLEntityInjection
import semmle.code.csharp.security.dataflow.XMLEntityInjectionQuery::XMLEntityInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -14,7 +14,7 @@
*/
import csharp
import semmle.code.csharp.security.xml.InsecureXML::InsecureXML
import semmle.code.csharp.security.xml.InsecureXMLQuery::InsecureXML
from InsecureXmlProcessing xmlProcessing, string reason
where xmlProcessing.isUnsafe(reason)

View File

@@ -13,7 +13,7 @@
import csharp
import semmle.code.csharp.security.dataflow.flowsources.Stored
import semmle.code.csharp.security.dataflow.XPathInjection
import semmle.code.csharp.security.dataflow.XPathInjectionQuery
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
class StoredTaintTrackingConfiguration extends XPathInjection::TaintTrackingConfiguration {

View File

@@ -12,7 +12,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.XPathInjection::XPathInjection
import semmle.code.csharp.security.dataflow.XPathInjectionQuery::XPathInjection
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -13,7 +13,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.ReDoS::ReDoS
import semmle.code.csharp.security.dataflow.ReDoSQuery::ReDoS
import semmle.code.csharp.frameworks.system.text.RegularExpressions
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph

View File

@@ -14,7 +14,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.RegexInjection::RegexInjection
import semmle.code.csharp.security.dataflow.RegexInjectionQuery::RegexInjection
import semmle.code.csharp.frameworks.system.text.RegularExpressions
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph

View File

@@ -14,7 +14,7 @@
import csharp
import semmle.code.csharp.frameworks.system.Data
import semmle.code.csharp.security.dataflow.HardcodedCredentials
import semmle.code.csharp.security.dataflow.HardcodedCredentialsQuery
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
/**

View File

@@ -13,7 +13,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.HardcodedCredentials::HardcodedCredentials
import semmle.code.csharp.security.dataflow.HardcodedCredentialsQuery::HardcodedCredentials
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from

View File

@@ -14,7 +14,7 @@
*/
import csharp
import semmle.code.csharp.security.dataflow.ConditionalBypass::UserControlledBypassOfSensitiveMethod
import semmle.code.csharp.security.dataflow.ConditionalBypassQuery::UserControlledBypassOfSensitiveMethod
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -16,9 +16,9 @@ import semmle.code.csharp.frameworks.System
import semmle.code.csharp.frameworks.system.Net
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.system.web.UI
import semmle.code.csharp.security.dataflow.SqlInjection
import semmle.code.csharp.security.dataflow.SqlInjectionQuery
import semmle.code.csharp.security.dataflow.flowsinks.Html
import semmle.code.csharp.security.dataflow.UrlRedirect
import semmle.code.csharp.security.dataflow.UrlRedirectQuery
import semmle.code.csharp.security.Sanitizers
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2::PathGraph

View File

@@ -10,4 +10,4 @@
* maintainability
*/
import DefaultToString
import DefaultToStringQuery

View File

@@ -87,7 +87,7 @@ private module Frameworks {
private import semmle.code.csharp.security.dataflow.flowsources.Local
private import semmle.code.csharp.security.dataflow.flowsinks.Html
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.security.dataflow.XSS
private import semmle.code.csharp.security.dataflow.XSSSinks
}
/**

View File

@@ -8,7 +8,7 @@ module XMLEntityInjection {
import semmle.code.csharp.security.dataflow.flowsources.Remote
import semmle.code.csharp.frameworks.System
import semmle.code.csharp.frameworks.system.text.RegularExpressions
import semmle.code.csharp.security.xml.InsecureXML
import semmle.code.csharp.security.xml.InsecureXMLQuery
import semmle.code.csharp.security.Sanitizers
/**

View File

@@ -1,425 +0,0 @@
/**
* Provides a taint-tracking configuration for reasoning about cross-site scripting
* (XSS) vulnerabilities.
*/
import csharp
module XSS {
import semmle.code.asp.AspNet
import semmle.code.csharp.frameworks.system.Net
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.system.web.UI
import semmle.code.csharp.security.Sanitizers
import semmle.code.csharp.security.dataflow.flowsinks.Html
import semmle.code.csharp.security.dataflow.flowsinks.Remote
import semmle.code.csharp.security.dataflow.flowsources.Remote
private import semmle.code.csharp.dataflow.DataFlow2
private import semmle.code.csharp.dataflow.TaintTracking2
private import semmle.code.csharp.dataflow.ExternalFlow
/**
* Holds if there is tainted flow from `source` to `sink` that may lead to a
* cross-site scripting (XSS) vulnerability, with `message`
* providing a description of the source.
* This is the main predicate to use in XSS queries.
*/
predicate xssFlow(XssNode source, XssNode sink, string message) {
// standard taint-tracking
exists(
TaintTrackingConfiguration c, DataFlow2::PathNode sourceNode, DataFlow2::PathNode sinkNode
|
sourceNode = source.asDataFlowNode() and
sinkNode = sink.asDataFlowNode() and
c.hasFlowPath(sourceNode, sinkNode) and
message =
"is written to HTML or JavaScript" +
any(string explanation |
if exists(sinkNode.getNode().(Sink).explanation())
then explanation = ": " + sinkNode.getNode().(Sink).explanation() + "."
else explanation = "."
)
)
or
// flow entirely within ASP inline code
source = sink and
source.asAspInlineMember().getMember() instanceof AspNetQueryStringMember and
message = "is a remote source accessed inline in an ASPX page."
}
module PathGraph {
query predicate edges(XssNode pred, XssNode succ) {
exists(DataFlow2::PathNode a, DataFlow2::PathNode b | DataFlow2::PathGraph::edges(a, b) |
pred.asDataFlowNode() = a and
succ.asDataFlowNode() = b
)
or
xssFlow(pred, succ, _) and
pred instanceof XssAspNode
}
}
private newtype TXssNode =
TXssDataFlowNode(DataFlow2::PathNode node) or
TXssAspNode(AspInlineMember m)
/**
* A flow node for tracking cross-site scripting (XSS) vulnerabilities.
* Can be a standard data flow node (`XssDataFlowNode`)
* or an ASP inline code element (`XssAspNode`).
*/
class XssNode extends TXssNode {
/** Gets a textual representation of this node. */
string toString() { none() }
/** Gets the location of this node. */
Location getLocation() { none() }
/** Gets the data flow node corresponding to this node, if any. */
DataFlow2::PathNode asDataFlowNode() { result = this.(XssDataFlowNode).getDataFlowNode() }
/** Gets the ASP inline code element corresponding to this node, if any. */
AspInlineMember asAspInlineMember() { result = this.(XssAspNode).getAspInlineMember() }
}
/** A data flow node, viewed as an XSS flow node. */
class XssDataFlowNode extends TXssDataFlowNode, XssNode {
DataFlow2::PathNode node;
XssDataFlowNode() { this = TXssDataFlowNode(node) }
/** Gets the data flow node corresponding to this node. */
DataFlow2::PathNode getDataFlowNode() { result = node }
override string toString() { result = node.toString() }
override Location getLocation() { result = node.getNode().getLocation() }
}
/** An ASP inline code element, viewed as an XSS flow node. */
class XssAspNode extends TXssAspNode, XssNode {
AspInlineMember member;
XssAspNode() { this = TXssAspNode(member) }
/** Gets the ASP inline code element corresponding to this node. */
AspInlineMember getAspInlineMember() { result = member }
override string toString() { result = member.toString() }
override Location getLocation() { result = member.getLocation() }
}
/**
* A data flow sink for cross-site scripting (XSS) vulnerabilities.
*
* Any XSS sink is also a remote flow sink, so this class contributes
* to the abstract class `RemoteFlowSink`.
*/
abstract class Sink extends DataFlow::ExprNode, RemoteFlowSink {
string explanation() { none() }
}
private class ExternalXssSink extends Sink {
ExternalXssSink() { sinkNode(this, "xss") }
}
/**
* A data flow source for cross-site scripting (XSS) vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A sanitizer for cross-site scripting (XSS) vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::ExprNode { }
/**
* A taint-tracking configuration for cross-site scripting (XSS) vulnerabilities.
*/
class TaintTrackingConfiguration extends TaintTracking2::Configuration {
TaintTrackingConfiguration() { this = "XSSDataFlowConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}
/** A source of remote user input. */
class RemoteSource extends Source {
RemoteSource() { this instanceof RemoteFlowSource }
}
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
/** A call to an HTML encoder. */
private class HtmlEncodeSanitizer extends Sanitizer {
HtmlEncodeSanitizer() { this.getExpr() instanceof HtmlSanitizedExpr }
}
/**
* A call to a URL encoder.
*
* Url encoding is sufficient to sanitize for XSS because it ensures <, >, " and ' are escaped.
* Furthermore, URL encoding is the only valid way to sanitize URLs that get inserted into HTML
* attributes. Other uses of URL encoding may or may not produce the desired visual result, but
* should be safe from XSS.
*/
private class UrlEncodeSanitizer extends Sanitizer {
UrlEncodeSanitizer() { this.getExpr() instanceof UrlSanitizedExpr }
}
private class HtmlSinkSink extends Sink {
HtmlSinkSink() { this instanceof HtmlSink }
override string explanation() {
this instanceof WebPageWriteLiteralSink and
result = "System.Web.WebPages.WebPage.WriteLiteral() method"
or
this instanceof WebPageWriteLiteralToSink and
result = "System.Web.WebPages.WebPage.WriteLiteralTo() method"
or
this instanceof MicrosoftAspNetCoreMvcHtmlHelperRawSink and
result = "Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.Raw() method"
or
this instanceof MicrosoftAspNetRazorPageWriteLiteralSink and
result = "Microsoft.AspNetCore.Mvc.Razor.RazorPageBase.WriteLiteral() method"
}
}
/**
* An expression that is used as an argument to an XSS sink method on
* `System.Web.UI.Page`.
*/
private class PageSink extends Sink {
PageSink() {
exists(Property p, SystemWebUIPageClass page |
p = page.getIDProperty() or
p = page.getMetaDescriptionProperty() or
p = page.getMetaKeywordsProperty() or
p = page.getTitleProperty()
|
this.getExpr() = p.getSetter().getParameter(0).getAnAssignedArgument()
)
or
exists(Method m, SystemWebUIPageClass page |
m = page.getRegisterStartupScriptMethod() or
m = page.getRegisterClientScriptBlockMethod()
|
this.getExpr() = m.getAParameter().getAnAssignedArgument()
)
}
}
/**
* An expression that is used as an argument to an XSS sink method on
* `ClientScriptManager`.
*/
private class ClientScriptManagerSink extends Sink {
ClientScriptManagerSink() {
exists(Method m, SystemWebUIClientScriptManagerClass clientScriptManager, int paramNumber |
this.getExpr() = m.getParameter(paramNumber).getAnAssignedArgument() and
(
paramNumber = 2 and m.getNumberOfParameters() in [3 .. 4]
or
paramNumber = 3 and m.getNumberOfParameters() = 5
)
|
m = clientScriptManager.getRegisterClientScriptBlockMethod() or
m = clientScriptManager.getRegisterStartupScriptMethod()
)
}
}
/**
* An expression that is used as an argument to an XSS sink setter, on
* a class within the `System.Web.UI` namespace.
*/
private class SystemWebSetterNonHtmlSink extends Sink {
SystemWebSetterNonHtmlSink() {
exists(Property p, string name |
any(SystemWebUINamespace n).getAChildNamespace*() = p.getDeclaringType().getNamespace() and
this.getExpr() = p.getSetter().getParameter(0).getAnAssignedArgument() and
p.hasName(name)
|
name = "GroupingTest" or
name = "GroupName" or
name = "Style" or
name.matches("%URL")
)
}
}
/**
* A call to `Parse` for a numeric type, that causes the data to be considered
* sanitized.
*/
private class NumericTypeParse extends Sanitizer {
NumericTypeParse() {
exists(Method m |
m.getDeclaringType() instanceof IntegralType or
m.getDeclaringType() instanceof FloatingPointType
|
m.hasName("Parse") and
this.getExpr().(Call).getTarget() = m
)
}
}
/**
* Gets a member which is accessed by the given `AspInlineCode`.
* The code body must consist only of an access to the member, possibly with qualified
* field accesses or array indexing.
*/
private Member aspxInlineAccess(AspInlineCode code) {
result = max(int i, Member m | m = getMemberAccessByIndex(code, i) | m order by i)
}
/**
* Gets the `i`th member accessed by `code`, where the string in `code`
* must be of the form `f1.f2...fn`, `f1.f2...fn[...]`, `f1.f2...fn()`, or
* `f1.f2...fn[...]()`. The `i`th member is `fi` in all cases.
*/
private Member getMemberAccessByIndex(AspInlineCode code, int i) {
exists(ValueOrRefType t |
result.getName() = getMemberAccessNameByIndex(code, i) and
t.hasMember(result)
|
// Base case: a member on the code-behind class
i = 0 and
t = code.getLocation().getFile().(CodeBehindFile).getInheritedType()
or
// Recursive case: a nested member
exists(Member mid |
mid = getMemberAccessByIndex(code, i - 1) and
t = getMemberType(mid)
)
)
}
/**
* Gets the name of the `i`th member accessed by `code`, where the string in `code`
* must be of the form `f1.f2...fn`, `f1.f2...fn[...]`, `f1.f2...fn()`, or
* `f1.f2...fn[...]()`. The `i`th member is `fi` in all cases.
*/
private string getMemberAccessNameByIndex(AspInlineCode code, int i) {
// Strip:
// - leading and trailing whitespace, which apparently you're allowed to have
// - trailing parens, so we can recognize nullary method calls
// - trailing square brackets with some contents, to recognize indexing into arrays
result = code.getBody().splitAt(".", i).regexpCapture("\\s*(.*?)(\\[.*\\])?(\\(\\))?\\s*", 1)
}
/**
* An `AspInlineCode` which is an access to a member inherited from the
* corresponding 'CodeBehind' class. This includes direct accesses as well as
* qualified accesses or array indexing on the member.
*/
class AspInlineMember extends AspInlineCode {
Member member;
AspInlineMember() { member = aspxInlineAccess(this) }
/** Gets the member that this inline code references. */
Member getMember() { result = member }
Type getType() { result = getMemberType(getMember()) }
}
/** Gets a value that is written to the member accessed by the given `AspInlineMember`. */
Expr aspWrittenValue(AspInlineMember m) {
exists(Property p | p = m.getMember() |
// a directly assigned property
result = p.getAnAssignedValue()
or
// one step of flow through a variable returned by the getter
// this is mainly to handle trivial forwarding properties
exists(VariableAccess access |
p.getGetter().canReturn(access) and
result = access.getTarget().getAnAssignedValue()
)
)
or
result = m.getMember().(Field).getAnAssignedValue()
or
m.getMember().(Callable).canReturn(result)
}
private string makeUrl(Location l) {
exists(string path, int sl, int sc, int el, int ec |
l.hasLocationInfo(path, sl, sc, el, ec) and
result = "file://" + path + ":" + sl + ":" + sc + ":" + el + ":" + ec
)
}
/**
* A sink for writes to properties that are accessed in ASP pages.
*
* Currently we only support inline code tags that directly reference a member
* on the corresponding 'CodeBehind' class.
* This may include qualified accesses to fields or array indexing on the member.
* The sink is any assigned value of such a
* member, since we don't track the flow all the way to the ASP element.
*/
private class AspxCodeSink extends Sink {
/** The ASP inline code element that references a member of the backing class. */
AspInlineMember inline;
AspxCodeSink() { this.getExpr() = aspWrittenValue(inline) }
override string explanation() {
result =
"member is [[\"accessed inline\"|\"" + makeUrl(inline.getLocation()) +
"\"]] in an ASPX page"
}
}
/** A sink for the output stream associated with a `HttpListenerResponse`. */
private class HttpListenerResponseSink extends Sink {
HttpListenerResponseSink() {
exists(PropertyAccess responseOutputStream |
responseOutputStream.getProperty() =
any(SystemNetHttpListenerResponseClass h).getOutputStreamProperty()
|
DataFlow::localFlow(DataFlow::exprNode(responseOutputStream), this)
)
}
}
/**
* An expression that is used as an argument to an XSS sink method on
* `HttpResponseBase`.
*/
private class HttpResponseBaseSink extends Sink {
HttpResponseBaseSink() {
exists(Method m, SystemWebHttpResponseBaseClass responseClass |
m = responseClass.getAWriteMethod() or
m = responseClass.getAWriteFileMethod() or
m = responseClass.getATransmitFileMethod() or
m = responseClass.getABinaryWriteMethod()
|
// Calls to these methods, or overrides of them
this.getExpr() = m.getAnOverrider*().getParameter(0).getAnAssignedArgument()
)
}
}
/**
* An expression passed as the `content` argument to the constructor of `StringContent`.
*/
private class StringContentSinkModelCsv extends SinkModelCsv {
override predicate row(string row) {
row = ["System.Net.Http;StringContent;false;StringContent;;;Argument[0];xss"]
}
}
}
private Type getMemberType(Member m) {
result = m.(Property).getType() or
result = m.(Field).getType() or
result = m.(Callable).getReturnType()
}

View File

@@ -0,0 +1,171 @@
/**
* Provides a taint-tracking configuration for reasoning about cross-site scripting
* (XSS) vulnerabilities.
*/
import csharp
module XSS {
import XSSSinks
import semmle.code.csharp.security.Sanitizers
import semmle.code.csharp.security.dataflow.flowsources.Remote
private import semmle.code.csharp.dataflow.DataFlow2
private import semmle.code.csharp.dataflow.TaintTracking2
/**
* Holds if there is tainted flow from `source` to `sink` that may lead to a
* cross-site scripting (XSS) vulnerability, with `message`
* providing a description of the source.
* This is the main predicate to use in XSS queries.
*/
predicate xssFlow(XssNode source, XssNode sink, string message) {
// standard taint-tracking
exists(
TaintTrackingConfiguration c, DataFlow2::PathNode sourceNode, DataFlow2::PathNode sinkNode
|
sourceNode = source.asDataFlowNode() and
sinkNode = sink.asDataFlowNode() and
c.hasFlowPath(sourceNode, sinkNode) and
message =
"is written to HTML or JavaScript" +
any(string explanation |
if exists(sinkNode.getNode().(Sink).explanation())
then explanation = ": " + sinkNode.getNode().(Sink).explanation() + "."
else explanation = "."
)
)
or
// flow entirely within ASP inline code
source = sink and
source.asAspInlineMember().getMember() instanceof AspNetQueryStringMember and
message = "is a remote source accessed inline in an ASPX page."
}
module PathGraph {
query predicate edges(XssNode pred, XssNode succ) {
exists(DataFlow2::PathNode a, DataFlow2::PathNode b | DataFlow2::PathGraph::edges(a, b) |
pred.asDataFlowNode() = a and
succ.asDataFlowNode() = b
)
or
xssFlow(pred, succ, _) and
pred instanceof XssAspNode
}
}
private newtype TXssNode =
TXssDataFlowNode(DataFlow2::PathNode node) or
TXssAspNode(AspInlineMember m)
/**
* A flow node for tracking cross-site scripting (XSS) vulnerabilities.
* Can be a standard data flow node (`XssDataFlowNode`)
* or an ASP inline code element (`XssAspNode`).
*/
class XssNode extends TXssNode {
/** Gets a textual representation of this node. */
string toString() { none() }
/** Gets the location of this node. */
Location getLocation() { none() }
/** Gets the data flow node corresponding to this node, if any. */
DataFlow2::PathNode asDataFlowNode() { result = this.(XssDataFlowNode).getDataFlowNode() }
/** Gets the ASP inline code element corresponding to this node, if any. */
AspInlineMember asAspInlineMember() { result = this.(XssAspNode).getAspInlineMember() }
}
/** A data flow node, viewed as an XSS flow node. */
class XssDataFlowNode extends TXssDataFlowNode, XssNode {
DataFlow2::PathNode node;
XssDataFlowNode() { this = TXssDataFlowNode(node) }
/** Gets the data flow node corresponding to this node. */
DataFlow2::PathNode getDataFlowNode() { result = node }
override string toString() { result = node.toString() }
override Location getLocation() { result = node.getNode().getLocation() }
}
/** An ASP inline code element, viewed as an XSS flow node. */
class XssAspNode extends TXssAspNode, XssNode {
AspInlineMember member;
XssAspNode() { this = TXssAspNode(member) }
/** Gets the ASP inline code element corresponding to this node. */
AspInlineMember getAspInlineMember() { result = member }
override string toString() { result = member.toString() }
override Location getLocation() { result = member.getLocation() }
}
/**
* A data flow source for cross-site scripting (XSS) vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A sanitizer for cross-site scripting (XSS) vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::ExprNode { }
/**
* A taint-tracking configuration for cross-site scripting (XSS) vulnerabilities.
*/
class TaintTrackingConfiguration extends TaintTracking2::Configuration {
TaintTrackingConfiguration() { this = "XSSDataFlowConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}
/** A source of remote user input. */
class RemoteSource extends Source {
RemoteSource() { this instanceof RemoteFlowSource }
}
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
/** A call to an HTML encoder. */
private class HtmlEncodeSanitizer extends Sanitizer {
HtmlEncodeSanitizer() { this.getExpr() instanceof HtmlSanitizedExpr }
}
/**
* A call to a URL encoder.
*
* Url encoding is sufficient to sanitize for XSS because it ensures <, >, " and ' are escaped.
* Furthermore, URL encoding is the only valid way to sanitize URLs that get inserted into HTML
* attributes. Other uses of URL encoding may or may not produce the desired visual result, but
* should be safe from XSS.
*/
private class UrlEncodeSanitizer extends Sanitizer {
UrlEncodeSanitizer() { this.getExpr() instanceof UrlSanitizedExpr }
}
/**
* A call to `Parse` for a numeric type, that causes the data to be considered
* sanitized.
*/
private class NumericTypeParse extends Sanitizer {
NumericTypeParse() {
exists(Method m |
m.getDeclaringType() instanceof IntegralType or
m.getDeclaringType() instanceof FloatingPointType
|
m.hasName("Parse") and
this.getExpr().(Call).getTarget() = m
)
}
}
}

View File

@@ -0,0 +1,259 @@
/**
* Provides sink definitions for cross-site scripting (XSS) vulnerabilities.
*/
import csharp
private import semmle.code.asp.AspNet
private import semmle.code.csharp.frameworks.system.Net
private import semmle.code.csharp.frameworks.system.Web
private import semmle.code.csharp.frameworks.system.web.UI
private import semmle.code.csharp.security.dataflow.flowsinks.Html
private import semmle.code.csharp.security.dataflow.flowsinks.Remote
private import semmle.code.csharp.dataflow.ExternalFlow
/**
* A data flow sink for cross-site scripting (XSS) vulnerabilities.
*
* Any XSS sink is also a remote flow sink, so this class contributes
* to the abstract class `RemoteFlowSink`.
*/
abstract class Sink extends DataFlow::ExprNode, RemoteFlowSink {
string explanation() { none() }
}
private class ExternalXssSink extends Sink {
ExternalXssSink() { sinkNode(this, "xss") }
}
private class HtmlSinkSink extends Sink {
HtmlSinkSink() { this instanceof HtmlSink }
override string explanation() {
this instanceof WebPageWriteLiteralSink and
result = "System.Web.WebPages.WebPage.WriteLiteral() method"
or
this instanceof WebPageWriteLiteralToSink and
result = "System.Web.WebPages.WebPage.WriteLiteralTo() method"
or
this instanceof MicrosoftAspNetCoreMvcHtmlHelperRawSink and
result = "Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.Raw() method"
or
this instanceof MicrosoftAspNetRazorPageWriteLiteralSink and
result = "Microsoft.AspNetCore.Mvc.Razor.RazorPageBase.WriteLiteral() method"
}
}
/**
* An expression that is used as an argument to an XSS sink method on
* `System.Web.UI.Page`.
*/
private class PageSink extends Sink {
PageSink() {
exists(Property p, SystemWebUIPageClass page |
p = page.getIDProperty() or
p = page.getMetaDescriptionProperty() or
p = page.getMetaKeywordsProperty() or
p = page.getTitleProperty()
|
this.getExpr() = p.getSetter().getParameter(0).getAnAssignedArgument()
)
or
exists(Method m, SystemWebUIPageClass page |
m = page.getRegisterStartupScriptMethod() or
m = page.getRegisterClientScriptBlockMethod()
|
this.getExpr() = m.getAParameter().getAnAssignedArgument()
)
}
}
/**
* An expression that is used as an argument to an XSS sink method on
* `ClientScriptManager`.
*/
private class ClientScriptManagerSink extends Sink {
ClientScriptManagerSink() {
exists(Method m, SystemWebUIClientScriptManagerClass clientScriptManager, int paramNumber |
this.getExpr() = m.getParameter(paramNumber).getAnAssignedArgument() and
(
paramNumber = 2 and m.getNumberOfParameters() in [3 .. 4]
or
paramNumber = 3 and m.getNumberOfParameters() = 5
)
|
m = clientScriptManager.getRegisterClientScriptBlockMethod() or
m = clientScriptManager.getRegisterStartupScriptMethod()
)
}
}
/**
* An expression that is used as an argument to an XSS sink setter, on
* a class within the `System.Web.UI` namespace.
*/
private class SystemWebSetterNonHtmlSink extends Sink {
SystemWebSetterNonHtmlSink() {
exists(Property p, string name |
any(SystemWebUINamespace n).getAChildNamespace*() = p.getDeclaringType().getNamespace() and
this.getExpr() = p.getSetter().getParameter(0).getAnAssignedArgument() and
p.hasName(name)
|
name = "GroupingTest" or
name = "GroupName" or
name = "Style" or
name.matches("%URL")
)
}
}
/**
* Gets a member which is accessed by the given `AspInlineCode`.
* The code body must consist only of an access to the member, possibly with qualified
* field accesses or array indexing.
*/
private Member aspxInlineAccess(AspInlineCode code) {
result = max(int i, Member m | m = getMemberAccessByIndex(code, i) | m order by i)
}
/**
* Gets the `i`th member accessed by `code`, where the string in `code`
* must be of the form `f1.f2...fn`, `f1.f2...fn[...]`, `f1.f2...fn()`, or
* `f1.f2...fn[...]()`. The `i`th member is `fi` in all cases.
*/
private Member getMemberAccessByIndex(AspInlineCode code, int i) {
exists(ValueOrRefType t |
result.getName() = getMemberAccessNameByIndex(code, i) and
t.hasMember(result)
|
// Base case: a member on the code-behind class
i = 0 and
t = code.getLocation().getFile().(CodeBehindFile).getInheritedType()
or
// Recursive case: a nested member
exists(Member mid |
mid = getMemberAccessByIndex(code, i - 1) and
t = getMemberType(mid)
)
)
}
/**
* Gets the name of the `i`th member accessed by `code`, where the string in `code`
* must be of the form `f1.f2...fn`, `f1.f2...fn[...]`, `f1.f2...fn()`, or
* `f1.f2...fn[...]()`. The `i`th member is `fi` in all cases.
*/
private string getMemberAccessNameByIndex(AspInlineCode code, int i) {
// Strip:
// - leading and trailing whitespace, which apparently you're allowed to have
// - trailing parens, so we can recognize nullary method calls
// - trailing square brackets with some contents, to recognize indexing into arrays
result = code.getBody().splitAt(".", i).regexpCapture("\\s*(.*?)(\\[.*\\])?(\\(\\))?\\s*", 1)
}
/**
* An `AspInlineCode` which is an access to a member inherited from the
* corresponding 'CodeBehind' class. This includes direct accesses as well as
* qualified accesses or array indexing on the member.
*/
class AspInlineMember extends AspInlineCode {
Member member;
AspInlineMember() { member = aspxInlineAccess(this) }
/** Gets the member that this inline code references. */
Member getMember() { result = member }
Type getType() { result = getMemberType(getMember()) }
}
/** Gets a value that is written to the member accessed by the given `AspInlineMember`. */
private Expr aspWrittenValue(AspInlineMember m) {
exists(Property p | p = m.getMember() |
// a directly assigned property
result = p.getAnAssignedValue()
or
// one step of flow through a variable returned by the getter
// this is mainly to handle trivial forwarding properties
exists(VariableAccess access |
p.getGetter().canReturn(access) and
result = access.getTarget().getAnAssignedValue()
)
)
or
result = m.getMember().(Field).getAnAssignedValue()
or
m.getMember().(Callable).canReturn(result)
}
private string makeUrl(Location l) {
exists(string path, int sl, int sc, int el, int ec |
l.hasLocationInfo(path, sl, sc, el, ec) and
result = "file://" + path + ":" + sl + ":" + sc + ":" + el + ":" + ec
)
}
/**
* A sink for writes to properties that are accessed in ASP pages.
*
* Currently we only support inline code tags that directly reference a member
* on the corresponding 'CodeBehind' class.
* This may include qualified accesses to fields or array indexing on the member.
* The sink is any assigned value of such a
* member, since we don't track the flow all the way to the ASP element.
*/
private class AspxCodeSink extends Sink {
/** The ASP inline code element that references a member of the backing class. */
AspInlineMember inline;
AspxCodeSink() { this.getExpr() = aspWrittenValue(inline) }
override string explanation() {
result =
"member is [[\"accessed inline\"|\"" + makeUrl(inline.getLocation()) + "\"]] in an ASPX page"
}
}
/** A sink for the output stream associated with a `HttpListenerResponse`. */
private class HttpListenerResponseSink extends Sink {
HttpListenerResponseSink() {
exists(PropertyAccess responseOutputStream |
responseOutputStream.getProperty() =
any(SystemNetHttpListenerResponseClass h).getOutputStreamProperty()
|
DataFlow::localFlow(DataFlow::exprNode(responseOutputStream), this)
)
}
}
/**
* An expression that is used as an argument to an XSS sink method on
* `HttpResponseBase`.
*/
private class HttpResponseBaseSink extends Sink {
HttpResponseBaseSink() {
exists(Method m, SystemWebHttpResponseBaseClass responseClass |
m = responseClass.getAWriteMethod() or
m = responseClass.getAWriteFileMethod() or
m = responseClass.getATransmitFileMethod() or
m = responseClass.getABinaryWriteMethod()
|
// Calls to these methods, or overrides of them
this.getExpr() = m.getAnOverrider*().getParameter(0).getAnAssignedArgument()
)
}
}
/**
* An expression passed as the `content` argument to the constructor of `StringContent`.
*/
private class StringContentSinkModelCsv extends SinkModelCsv {
override predicate row(string row) {
row = ["System.Net.Http;StringContent;false;StringContent;;;Argument[0];xss"]
}
}
private Type getMemberType(Member m) {
result = m.(Property).getType() or
result = m.(Field).getType() or
result = m.(Callable).getReturnType()
}

View File

@@ -6,7 +6,7 @@ import csharp
private import Email::Email
private import ExternalLocationSink
private import Html
private import semmle.code.csharp.security.dataflow.XSS
private import semmle.code.csharp.security.dataflow.XSSSinks as XSSSinks
private import semmle.code.csharp.frameworks.system.web.UI
/** A data flow sink of remote user output. */

View File

@@ -1,5 +1,5 @@
import csharp
import semmle.code.csharp.security.dataflow.XSS
import semmle.code.csharp.security.dataflow.XSSSinks
string tweakMemberLocation(Member member) {
exists(Location loc |
@@ -10,7 +10,7 @@ string tweakMemberLocation(Member member) {
)
}
from XSS::AspInlineMember inline, Member member
from AspInlineMember inline, Member member
where member = inline.getMember()
// some members, such as ASP members inherited from DLLs, are outside the test directory,
// so we select them specially using a modified location and the normal toString

View File

@@ -1,5 +1,5 @@
import csharp
import semmle.code.csharp.security.xml.InsecureXML::InsecureXML
import semmle.code.csharp.security.xml.InsecureXMLQuery::InsecureXML
from ObjectCreation creation, Expr evidence, string reason
where

View File

@@ -1,4 +1,4 @@
import csharp
import semmle.code.csharp.security.dataflow.ReDoS
import semmle.code.csharp.security.dataflow.ReDoSQuery
select any(StringLiteral e | ReDoS::isExponentialRegex(e))
select any(StringLiteral e | isExponentialRegex(e))

View File

@@ -1,5 +1,5 @@
import csharp
import Useless_code.DefaultToString
import Useless_code.DefaultToStringQuery
class MyDefaultToStringType extends DefaultToStringType {
// A workaround for generating empty URLs for non-source locations, because qltest