mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
C#: Add Query suffix to libraries that should only be imported by queries
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
* maintainability
|
||||
*/
|
||||
|
||||
import DefaultToString
|
||||
import DefaultToStringQuery
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
@@ -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()
|
||||
}
|
||||
171
csharp/ql/src/semmle/code/csharp/security/dataflow/XSSQuery.qll
Normal file
171
csharp/ql/src/semmle/code/csharp/security/dataflow/XSSQuery.qll
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
259
csharp/ql/src/semmle/code/csharp/security/dataflow/XSSSinks.qll
Normal file
259
csharp/ql/src/semmle/code/csharp/security/dataflow/XSSSinks.qll
Normal 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()
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user