mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
C#: Remove Query.qll top-level modules
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.TaintedPathQuery::TaintedPath
|
||||
import semmle.code.csharp.security.dataflow.TaintedPathQuery
|
||||
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.ZipSlipQuery::ZipSlip
|
||||
import semmle.code.csharp.security.dataflow.ZipSlipQuery
|
||||
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.CommandInjectionQuery::CommandInjection
|
||||
import semmle.code.csharp.security.dataflow.CommandInjectionQuery
|
||||
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.CommandInjectionQuery::CommandInjection
|
||||
import semmle.code.csharp.security.dataflow.CommandInjectionQuery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
|
||||
class StoredTaintTrackingConfiguration extends TaintTrackingConfiguration {
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Stored
|
||||
import semmle.code.csharp.security.dataflow.XSSQuery::XSS
|
||||
import semmle.code.csharp.security.dataflow.XSSQuery
|
||||
import semmle.code.csharp.security.dataflow.XSSSinks
|
||||
import semmle.code.csharp.dataflow.DataFlow2
|
||||
import DataFlow2::PathGraph
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.XSSQuery::XSS
|
||||
import semmle.code.csharp.security.dataflow.XSSQuery
|
||||
import PathGraph
|
||||
|
||||
from XssNode source, XssNode sink, string message
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery as SqlInjection
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Stored
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery::SqlInjection
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
|
||||
string getSourceType(DataFlow::Node node) {
|
||||
result = node.(RemoteFlowSource).getSourceType()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.LDAPInjectionQuery::LDAPInjection
|
||||
import semmle.code.csharp.security.dataflow.LDAPInjectionQuery
|
||||
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.LDAPInjectionQuery::LDAPInjection
|
||||
import semmle.code.csharp.security.dataflow.LDAPInjectionQuery
|
||||
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.CodeInjectionQuery::CodeInjection
|
||||
import semmle.code.csharp.security.dataflow.CodeInjectionQuery
|
||||
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.ResourceInjectionQuery::ResourceInjection
|
||||
import semmle.code.csharp.security.dataflow.ResourceInjectionQuery
|
||||
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.MissingXMLValidationQuery::MissingXMLValidation
|
||||
import semmle.code.csharp.security.dataflow.MissingXMLValidationQuery
|
||||
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.LogForgingQuery::LogForging
|
||||
import semmle.code.csharp.security.dataflow.LogForgingQuery
|
||||
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.CleartextStorageQuery::CleartextStorage
|
||||
import semmle.code.csharp.security.dataflow.CleartextStorageQuery
|
||||
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.EncryptionKeyDataFlowQuery::EncryptionKeyDataFlow
|
||||
import semmle.code.csharp.security.cryptography.EncryptionKeyDataFlowQuery
|
||||
|
||||
/**
|
||||
* The creation of a literal byte array.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.ExposureOfPrivateInformationQuery::ExposureOfPrivateInformation
|
||||
import semmle.code.csharp.security.dataflow.ExposureOfPrivateInformationQuery
|
||||
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.UnsafeDeserializationQuery::UnsafeDeserialization
|
||||
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery
|
||||
|
||||
from Call deserializeCall, Sink sink
|
||||
where deserializeCall.getAnArgument() = sink.asExpr()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery::UnsafeDeserialization
|
||||
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from TaintTrackingConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.UrlRedirectQuery::UrlRedirect
|
||||
import semmle.code.csharp.security.dataflow.UrlRedirectQuery
|
||||
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.XMLEntityInjectionQuery::XMLEntityInjection
|
||||
import semmle.code.csharp.security.dataflow.XMLEntityInjectionQuery
|
||||
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.InsecureXMLQuery::InsecureXML
|
||||
import semmle.code.csharp.security.xml.InsecureXMLQuery
|
||||
|
||||
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.XPathInjectionQuery
|
||||
import semmle.code.csharp.security.dataflow.XPathInjectionQuery as XPathInjection
|
||||
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.XPathInjectionQuery::XPathInjection
|
||||
import semmle.code.csharp.security.dataflow.XPathInjectionQuery
|
||||
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.ReDoSQuery::ReDoS
|
||||
import semmle.code.csharp.security.dataflow.ReDoSQuery
|
||||
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.RegexInjectionQuery::RegexInjection
|
||||
import semmle.code.csharp.security.dataflow.RegexInjectionQuery
|
||||
import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
/**
|
||||
* A string literal containing a username or password field.
|
||||
*/
|
||||
class ConnectionStringPasswordOrUsername extends HardcodedCredentials::NonEmptyStringLiteral {
|
||||
class ConnectionStringPasswordOrUsername extends NonEmptyStringLiteral {
|
||||
ConnectionStringPasswordOrUsername() {
|
||||
this.getExpr().getValue().regexpMatch("(?i).*(Password|PWD|User Id|UID)=.+")
|
||||
}
|
||||
@@ -41,9 +41,7 @@ class ConnectionStringTaintTrackingConfiguration extends TaintTracking::Configur
|
||||
any(SystemDataConnectionClass connection).getConnectionStringProperty().getAnAssignedValue()
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node instanceof HardcodedCredentials::StringFormatSanitizer
|
||||
}
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof StringFormatSanitizer }
|
||||
}
|
||||
|
||||
from
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.HardcodedCredentialsQuery::HardcodedCredentials
|
||||
import semmle.code.csharp.security.dataflow.HardcodedCredentialsQuery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.ConditionalBypassQuery::UserControlledBypassOfSensitiveMethod
|
||||
import semmle.code.csharp.security.dataflow.ConditionalBypassQuery
|
||||
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.SqlInjectionQuery
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery as SqlInjection
|
||||
import semmle.code.csharp.security.dataflow.flowsinks.Html
|
||||
import semmle.code.csharp.security.dataflow.UrlRedirectQuery
|
||||
import semmle.code.csharp.security.dataflow.UrlRedirectQuery as UrlRedirect
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2
|
||||
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2::PathGraph
|
||||
|
||||
@@ -3,81 +3,78 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm
|
||||
|
||||
module EncryptionKeyDataFlow {
|
||||
private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm
|
||||
|
||||
/** Array of type Byte */
|
||||
class ByteArray extends ArrayType {
|
||||
ByteArray() { getElementType() instanceof ByteType }
|
||||
}
|
||||
|
||||
/** Abstract class for all sources of keys */
|
||||
abstract class KeySource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A symmetric encryption sink is abstract base class for all ways to set a key for symmetric encryption.
|
||||
*/
|
||||
abstract class SymmetricEncryptionKeySink extends DataFlow::Node {
|
||||
/** override to create a meaningful description of the sink */
|
||||
abstract string getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for symmetric encryption key. If present, for example, key is properly constructed or retrieved from secret storage.
|
||||
*/
|
||||
abstract class KeySanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* Symmetric Algorithm, 'Key' property assigned a value
|
||||
*/
|
||||
class SymmetricEncryptionKeyPropertySink extends SymmetricEncryptionKeySink {
|
||||
SymmetricEncryptionKeyPropertySink() {
|
||||
exists(SymmetricAlgorithm ag | asExpr() = ag.getKeyProperty().getAnAssignedValue())
|
||||
}
|
||||
|
||||
override string getDescription() { result = "Key property assignment" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric Algorithm, CreateEncryptor method, rgbKey parameter
|
||||
*/
|
||||
class SymmetricEncryptionCreateEncryptorSink extends SymmetricEncryptionKeySink {
|
||||
SymmetricEncryptionCreateEncryptorSink() {
|
||||
exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricEncryptor() |
|
||||
asExpr() = mc.getArgumentForName("rgbKey")
|
||||
)
|
||||
}
|
||||
|
||||
override string getDescription() { result = "Encryptor(rgbKey, IV)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric Algorithm, CreateDecryptor method, rgbKey parameter
|
||||
*/
|
||||
class SymmetricEncryptionCreateDecryptorSink extends SymmetricEncryptionKeySink {
|
||||
SymmetricEncryptionCreateDecryptorSink() {
|
||||
exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricDecryptor() |
|
||||
asExpr() = mc.getArgumentForName("rgbKey")
|
||||
)
|
||||
}
|
||||
|
||||
override string getDescription() { result = "Decryptor(rgbKey, IV)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric Key Data Flow configuration.
|
||||
*/
|
||||
class SymmetricKeyTaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
SymmetricKeyTaintTrackingConfiguration() { this = "SymmetricKeyTaintTracking" }
|
||||
|
||||
/** Holds if the node is a key source. */
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof KeySource }
|
||||
|
||||
/** Holds if the node is a symmetric encryption key sink. */
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof SymmetricEncryptionKeySink }
|
||||
|
||||
/** Holds if the node is a key sanitizer. */
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof KeySanitizer }
|
||||
}
|
||||
/** Array of type Byte */
|
||||
class ByteArray extends ArrayType {
|
||||
ByteArray() { getElementType() instanceof ByteType }
|
||||
}
|
||||
|
||||
/** Abstract class for all sources of keys */
|
||||
abstract class KeySource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A symmetric encryption sink is abstract base class for all ways to set a key for symmetric encryption.
|
||||
*/
|
||||
abstract class SymmetricEncryptionKeySink extends DataFlow::Node {
|
||||
/** override to create a meaningful description of the sink */
|
||||
abstract string getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for symmetric encryption key. If present, for example, key is properly constructed or retrieved from secret storage.
|
||||
*/
|
||||
abstract class KeySanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* Symmetric Algorithm, 'Key' property assigned a value
|
||||
*/
|
||||
class SymmetricEncryptionKeyPropertySink extends SymmetricEncryptionKeySink {
|
||||
SymmetricEncryptionKeyPropertySink() {
|
||||
exists(SymmetricAlgorithm ag | asExpr() = ag.getKeyProperty().getAnAssignedValue())
|
||||
}
|
||||
|
||||
override string getDescription() { result = "Key property assignment" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric Algorithm, CreateEncryptor method, rgbKey parameter
|
||||
*/
|
||||
class SymmetricEncryptionCreateEncryptorSink extends SymmetricEncryptionKeySink {
|
||||
SymmetricEncryptionCreateEncryptorSink() {
|
||||
exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricEncryptor() |
|
||||
asExpr() = mc.getArgumentForName("rgbKey")
|
||||
)
|
||||
}
|
||||
|
||||
override string getDescription() { result = "Encryptor(rgbKey, IV)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric Algorithm, CreateDecryptor method, rgbKey parameter
|
||||
*/
|
||||
class SymmetricEncryptionCreateDecryptorSink extends SymmetricEncryptionKeySink {
|
||||
SymmetricEncryptionCreateDecryptorSink() {
|
||||
exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricDecryptor() |
|
||||
asExpr() = mc.getArgumentForName("rgbKey")
|
||||
)
|
||||
}
|
||||
|
||||
override string getDescription() { result = "Decryptor(rgbKey, IV)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric Key Data Flow configuration.
|
||||
*/
|
||||
class SymmetricKeyTaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
SymmetricKeyTaintTrackingConfiguration() { this = "SymmetricKeyTaintTracking" }
|
||||
|
||||
/** Holds if the node is a key source. */
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof KeySource }
|
||||
|
||||
/** Holds if the node is a symmetric encryption key sink. */
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof SymmetricEncryptionKeySink }
|
||||
|
||||
/** Holds if the node is a key sanitizer. */
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof KeySanitizer }
|
||||
}
|
||||
|
||||
@@ -3,62 +3,59 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.Web
|
||||
private import semmle.code.csharp.security.SensitiveActions
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
|
||||
|
||||
module CleartextStorage {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.Web
|
||||
import semmle.code.csharp.security.SensitiveActions
|
||||
import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
|
||||
/**
|
||||
* A data flow source for cleartext storage of sensitive information.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow source for cleartext storage of sensitive information.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A data flow sink for cleartext storage of sensitive information.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for cleartext storage of sensitive information.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for cleartext storage of sensitive information.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for cleartext storage of sensitive information.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for cleartext storage of sensitive information.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ClearTextStorage" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for cleartext storage of sensitive information.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ClearTextStorage" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
/** A source of sensitive data. */
|
||||
class SensitiveExprSource extends Source {
|
||||
SensitiveExprSource() { this.getExpr() instanceof SensitiveExpr }
|
||||
}
|
||||
|
||||
/** A source of sensitive data. */
|
||||
class SensitiveExprSource extends Source {
|
||||
SensitiveExprSource() { this.getExpr() instanceof SensitiveExpr }
|
||||
}
|
||||
|
||||
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
|
||||
class ProtectSanitizer extends Sanitizer {
|
||||
ProtectSanitizer() {
|
||||
exists(Method m, string s |
|
||||
this.getExpr().(MethodCall).getTarget() = m and
|
||||
m.getName().regexpMatch("(?i).*" + s + ".*")
|
||||
|
|
||||
s = "protect" or s = "encode" or s = "encrypt"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An external location sink.
|
||||
*/
|
||||
class ExternalSink extends Sink {
|
||||
ExternalSink() { this instanceof ExternalLocationSink }
|
||||
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
|
||||
class ProtectSanitizer extends Sanitizer {
|
||||
ProtectSanitizer() {
|
||||
exists(Method m, string s |
|
||||
this.getExpr().(MethodCall).getTarget() = m and
|
||||
m.getName().regexpMatch("(?i).*" + s + ".*")
|
||||
|
|
||||
s = "protect" or s = "encode" or s = "encrypt"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An external location sink.
|
||||
*/
|
||||
class ExternalSink extends Sink {
|
||||
ExternalSink() { this instanceof ExternalLocationSink }
|
||||
}
|
||||
|
||||
@@ -3,84 +3,79 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
private import semmle.code.csharp.frameworks.system.codedom.Compiler
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module CodeInjection {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
import semmle.code.csharp.frameworks.system.codedom.Compiler
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for user input treated as code vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "CodeInjection" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for user input treated as code vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "CodeInjection" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
/** A source of local user input. */
|
||||
class LocalSource extends Source {
|
||||
LocalSource() { this instanceof LocalFlowSource }
|
||||
}
|
||||
|
||||
/** A source of local user input. */
|
||||
class LocalSource extends Source {
|
||||
LocalSource() { this instanceof LocalFlowSource }
|
||||
}
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
/**
|
||||
* A `source` argument to a call to `ICodeCompiler.CompileAssemblyFromSource*` which is a sink for
|
||||
* code injection vulnerabilities.
|
||||
*/
|
||||
class CompileAssemblyFromSourceSink extends Sink {
|
||||
CompileAssemblyFromSourceSink() {
|
||||
exists(Method m, MethodCall mc |
|
||||
m.getName().matches("CompileAssemblyFromSource%") and
|
||||
m = any(SystemCodeDomCompilerICodeCompilerClass c).getAMethod() and
|
||||
mc = m.getAnOverrider*().getACall()
|
||||
|
|
||||
this.getExpr() = mc.getArgumentForName("source") or
|
||||
this.getExpr() = mc.getArgumentForName("sources")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `code` argument to a call to a method on `CSharpScript`.
|
||||
*
|
||||
* This class is provided by Roslyn, and allows dynamic evaluation of C#.
|
||||
*/
|
||||
class RoslynCSharpScriptSink extends Sink {
|
||||
RoslynCSharpScriptSink() {
|
||||
exists(Class c |
|
||||
c.hasQualifiedName("Microsoft.CodeAnalysis.CSharp.Scripting", "CSharpScript")
|
||||
|
|
||||
this.getExpr() = c.getAMethod().getACall().getArgumentForName("code")
|
||||
)
|
||||
}
|
||||
/**
|
||||
* A `source` argument to a call to `ICodeCompiler.CompileAssemblyFromSource*` which is a sink for
|
||||
* code injection vulnerabilities.
|
||||
*/
|
||||
class CompileAssemblyFromSourceSink extends Sink {
|
||||
CompileAssemblyFromSourceSink() {
|
||||
exists(Method m, MethodCall mc |
|
||||
m.getName().matches("CompileAssemblyFromSource%") and
|
||||
m = any(SystemCodeDomCompilerICodeCompilerClass c).getAMethod() and
|
||||
mc = m.getAnOverrider*().getACall()
|
||||
|
|
||||
this.getExpr() = mc.getArgumentForName("source") or
|
||||
this.getExpr() = mc.getArgumentForName("sources")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `code` argument to a call to a method on `CSharpScript`.
|
||||
*
|
||||
* This class is provided by Roslyn, and allows dynamic evaluation of C#.
|
||||
*/
|
||||
class RoslynCSharpScriptSink extends Sink {
|
||||
RoslynCSharpScriptSink() {
|
||||
exists(Class c | c.hasQualifiedName("Microsoft.CodeAnalysis.CSharp.Scripting", "CSharpScript") |
|
||||
this.getExpr() = c.getAMethod().getACall().getArgumentForName("code")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,71 +3,68 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.Diagnostics
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module CommandInjection {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.Diagnostics
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A source specific to command injection vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A source specific to command injection vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A sink for command injection vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sink for command injection vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for user input treated as code vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for command injection vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "CommandInjection" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for command injection vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "CommandInjection" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink in `System.Diagnostic.Process` or its related classes.
|
||||
*/
|
||||
class SystemProcessCommandInjectionSink extends Sink {
|
||||
SystemProcessCommandInjectionSink() {
|
||||
// Arguments passed directly to the `System.Diagnostics.Process.Start` method
|
||||
exists(SystemDiagnosticsProcessClass processClass |
|
||||
this.getExpr() = processClass.getAStartMethod().getAParameter().getAnAssignedArgument()
|
||||
)
|
||||
or
|
||||
// Values set on a `System.Diagnostics.ProcessStartInfo` class
|
||||
exists(SystemDiagnosticsProcessStartInfoClass startInfoClass |
|
||||
this.getExpr() = startInfoClass.getAConstructor().getACall().getAnArgument()
|
||||
or
|
||||
exists(Property p |
|
||||
p = startInfoClass.getArgumentsProperty() or
|
||||
p = startInfoClass.getFileNameProperty() or
|
||||
p = startInfoClass.getWorkingDirectoryProperty()
|
||||
|
|
||||
this.getExpr() = p.getSetter().getParameter(0).getAnAssignedArgument()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink in `System.Diagnostic.Process` or its related classes.
|
||||
*/
|
||||
class SystemProcessCommandInjectionSink extends Sink {
|
||||
SystemProcessCommandInjectionSink() {
|
||||
// Arguments passed directly to the `System.Diagnostics.Process.Start` method
|
||||
exists(SystemDiagnosticsProcessClass processClass |
|
||||
this.getExpr() = processClass.getAStartMethod().getAParameter().getAnAssignedArgument()
|
||||
)
|
||||
or
|
||||
// Values set on a `System.Diagnostics.ProcessStartInfo` class
|
||||
exists(SystemDiagnosticsProcessStartInfoClass startInfoClass |
|
||||
this.getExpr() = startInfoClass.getAConstructor().getACall().getAnArgument()
|
||||
or
|
||||
exists(Property p |
|
||||
p = startInfoClass.getArgumentsProperty() or
|
||||
p = startInfoClass.getFileNameProperty() or
|
||||
p = startInfoClass.getWorkingDirectoryProperty()
|
||||
|
|
||||
this.getExpr() = p.getSetter().getParameter(0).getAnAssignedArgument()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -4,114 +4,108 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
private import semmle.code.csharp.controlflow.BasicBlocks
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.system.Net
|
||||
private import semmle.code.csharp.security.SensitiveActions
|
||||
|
||||
module UserControlledBypassOfSensitiveMethod {
|
||||
import semmle.code.csharp.controlflow.Guards
|
||||
import semmle.code.csharp.controlflow.BasicBlocks
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.System
|
||||
import semmle.code.csharp.frameworks.system.Net
|
||||
import semmle.code.csharp.security.SensitiveActions
|
||||
/**
|
||||
* A data flow source for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/** Gets the 'MethodCall' which is considered sensitive. */
|
||||
abstract MethodCall getSensitiveMethodCall();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/** Gets the 'MethodCall' which is considered sensitive. */
|
||||
abstract MethodCall getSensitiveMethodCall();
|
||||
}
|
||||
/**
|
||||
* A sanitizer for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "UserControlledBypassOfSensitiveMethodConfiguration" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for user-controlled bypass of sensitive method.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "UserControlledBypassOfSensitiveMethodConfiguration" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** The result of a reverse dns may be user-controlled. */
|
||||
class ReverseDnsSource extends Source {
|
||||
ReverseDnsSource() {
|
||||
this.asExpr().(MethodCall).getTarget() =
|
||||
any(SystemNetDnsClass dns).getGetHostByAddressMethod()
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate conditionControlsCall0(
|
||||
SensitiveExecutionMethodCall call, Expr e, ControlFlow::SuccessorTypes::BooleanSuccessor s
|
||||
) {
|
||||
forex(BasicBlock bb | bb = call.getAControlFlowNode().getBasicBlock() |
|
||||
e.controlsBlock(bb, s, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate conditionControlsCall(
|
||||
SensitiveExecutionMethodCall call, SensitiveExecutionMethod def, Expr e, boolean cond
|
||||
) {
|
||||
exists(ControlFlow::SuccessorTypes::BooleanSuccessor s | cond = s.getValue() |
|
||||
conditionControlsCall0(call, e, s)
|
||||
) and
|
||||
def = call.getTarget()
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls to a sensitive method that are controlled by a condition
|
||||
* on the given expression.
|
||||
*/
|
||||
predicate conditionControlsMethod(SensitiveExecutionMethodCall call, Expr e) {
|
||||
exists(SensitiveExecutionMethod def, boolean cond |
|
||||
conditionControlsCall(call, def, e, cond) and
|
||||
// Exclude this condition if the other branch also contains a call to the same security
|
||||
// sensitive method.
|
||||
not conditionControlsCall(_, def, e, cond.booleanNot())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression which is a condition which controls access to a sensitive action.
|
||||
*/
|
||||
class ConditionControllingSensitiveAction extends Sink {
|
||||
private MethodCall sensitiveMethodCall;
|
||||
|
||||
ConditionControllingSensitiveAction() {
|
||||
// A condition used to guard a sensitive method call
|
||||
conditionControlsMethod(sensitiveMethodCall, this.getExpr())
|
||||
or
|
||||
// A condition used to guard a sensitive method call, where the condition is `EndsWith`,
|
||||
// `StartsWith` or `Contains` on a tainted value. Tracking from strings to booleans doesn't
|
||||
// make sense in all contexts, so this is restricted to this case.
|
||||
exists(MethodCall stringComparisonCall, string methodName |
|
||||
methodName = "EndsWith" or
|
||||
methodName = "StartsWith" or
|
||||
methodName = "Contains"
|
||||
|
|
||||
stringComparisonCall = any(SystemStringClass s).getAMethod(methodName).getACall() and
|
||||
conditionControlsMethod(sensitiveMethodCall, stringComparisonCall) and
|
||||
stringComparisonCall.getQualifier() = this.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override MethodCall getSensitiveMethodCall() { result = sensitiveMethodCall }
|
||||
/** The result of a reverse dns may be user-controlled. */
|
||||
class ReverseDnsSource extends Source {
|
||||
ReverseDnsSource() {
|
||||
this.asExpr().(MethodCall).getTarget() = any(SystemNetDnsClass dns).getGetHostByAddressMethod()
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate conditionControlsCall0(
|
||||
SensitiveExecutionMethodCall call, Expr e, ControlFlow::SuccessorTypes::BooleanSuccessor s
|
||||
) {
|
||||
forex(BasicBlock bb | bb = call.getAControlFlowNode().getBasicBlock() | e.controlsBlock(bb, s, _))
|
||||
}
|
||||
|
||||
private predicate conditionControlsCall(
|
||||
SensitiveExecutionMethodCall call, SensitiveExecutionMethod def, Expr e, boolean cond
|
||||
) {
|
||||
exists(ControlFlow::SuccessorTypes::BooleanSuccessor s | cond = s.getValue() |
|
||||
conditionControlsCall0(call, e, s)
|
||||
) and
|
||||
def = call.getTarget()
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls to a sensitive method that are controlled by a condition
|
||||
* on the given expression.
|
||||
*/
|
||||
predicate conditionControlsMethod(SensitiveExecutionMethodCall call, Expr e) {
|
||||
exists(SensitiveExecutionMethod def, boolean cond |
|
||||
conditionControlsCall(call, def, e, cond) and
|
||||
// Exclude this condition if the other branch also contains a call to the same security
|
||||
// sensitive method.
|
||||
not conditionControlsCall(_, def, e, cond.booleanNot())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression which is a condition which controls access to a sensitive action.
|
||||
*/
|
||||
class ConditionControllingSensitiveAction extends Sink {
|
||||
private MethodCall sensitiveMethodCall;
|
||||
|
||||
ConditionControllingSensitiveAction() {
|
||||
// A condition used to guard a sensitive method call
|
||||
conditionControlsMethod(sensitiveMethodCall, this.getExpr())
|
||||
or
|
||||
// A condition used to guard a sensitive method call, where the condition is `EndsWith`,
|
||||
// `StartsWith` or `Contains` on a tainted value. Tracking from strings to booleans doesn't
|
||||
// make sense in all contexts, so this is restricted to this case.
|
||||
exists(MethodCall stringComparisonCall, string methodName |
|
||||
methodName = "EndsWith" or
|
||||
methodName = "StartsWith" or
|
||||
methodName = "Contains"
|
||||
|
|
||||
stringComparisonCall = any(SystemStringClass s).getAMethod(methodName).getACall() and
|
||||
conditionControlsMethod(sensitiveMethodCall, stringComparisonCall) and
|
||||
stringComparisonCall.getQualifier() = this.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override MethodCall getSensitiveMethodCall() { result = sensitiveMethodCall }
|
||||
}
|
||||
|
||||
@@ -3,45 +3,42 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
|
||||
private import semmle.code.csharp.security.PrivateData
|
||||
|
||||
module ExposureOfPrivateInformation {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
|
||||
import semmle.code.csharp.security.PrivateData
|
||||
/**
|
||||
* A data flow source for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow source for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A data flow sink for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ExposureOfPrivateInformation" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ExposureOfPrivateInformation" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
class PrivateDataSource extends Source {
|
||||
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
|
||||
}
|
||||
|
||||
class ExternalLocation extends Sink {
|
||||
ExternalLocation() { this instanceof ExternalLocationSink }
|
||||
}
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
private class PrivateDataSource extends Source {
|
||||
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
|
||||
}
|
||||
|
||||
private class ExternalLocation extends Sink {
|
||||
ExternalLocation() { this instanceof ExternalLocationSink }
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.dataflow.TaintTracking
|
||||
import semmle.code.csharp.frameworks.System
|
||||
import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.dataflow.TaintTracking
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
|
||||
/**
|
||||
* A callable that is considered a "safe" external API from a security perspective.
|
||||
|
||||
@@ -3,259 +3,256 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.commons.ComparisonTest
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.Moq
|
||||
private import semmle.code.csharp.frameworks.system.web.Security
|
||||
private import semmle.code.csharp.frameworks.system.security.cryptography.X509Certificates
|
||||
private import semmle.code.csharp.frameworks.Test
|
||||
|
||||
module HardcodedCredentials {
|
||||
import semmle.code.csharp.commons.ComparisonTest
|
||||
import semmle.code.csharp.frameworks.System
|
||||
import semmle.code.csharp.frameworks.Moq
|
||||
import semmle.code.csharp.frameworks.system.web.Security
|
||||
import semmle.code.csharp.frameworks.system.security.cryptography.X509Certificates
|
||||
import semmle.code.csharp.frameworks.Test
|
||||
/**
|
||||
* A data flow source for hard coded credentials.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for hard coded credentials.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/**
|
||||
* A data flow source for hard coded credentials.
|
||||
* Gets a description of this sink, including a placeholder for the sink and a placeholder for
|
||||
* the supplementary element.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
abstract string getSinkDescription();
|
||||
|
||||
/**
|
||||
* A data flow sink for hard coded credentials.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/**
|
||||
* Gets a description of this sink, including a placeholder for the sink and a placeholder for
|
||||
* the supplementary element.
|
||||
*/
|
||||
abstract string getSinkDescription();
|
||||
/** Gets an element that is used as supplementary data in the description. */
|
||||
abstract Element getSupplementaryElement();
|
||||
|
||||
/** Gets an element that is used as supplementary data in the description. */
|
||||
abstract Element getSupplementaryElement();
|
||||
/** Gets the sink name to use when displaying the sink. */
|
||||
abstract string getSinkName();
|
||||
}
|
||||
|
||||
/** Gets the sink name to use when displaying the sink. */
|
||||
abstract string getSinkName();
|
||||
/**
|
||||
* A sanitizer for hard coded credentials.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for hard coded credentials.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "HardcodedCredentials" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink and
|
||||
// Ignore values that are ultimately returned by mocks, as they don't represent "real"
|
||||
// credentials.
|
||||
not any(ReturnedByMockObject mock).getAMemberInitializationValue() = sink.asExpr() and
|
||||
not any(ReturnedByMockObject mock).getAnArgument() = sink.asExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for hard coded credentials.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for hard coded credentials.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "HardcodedCredentials" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink and
|
||||
// Ignore values that are ultimately returned by mocks, as they don't represent "real"
|
||||
// credentials.
|
||||
not any(ReturnedByMockObject mock).getAMemberInitializationValue() = sink.asExpr() and
|
||||
not any(ReturnedByMockObject mock).getAnArgument() = sink.asExpr()
|
||||
}
|
||||
|
||||
override predicate hasFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
|
||||
super.hasFlowPath(source, sink) and
|
||||
// Exclude hard-coded credentials in tests if they only flow to calls to methods with a name
|
||||
// like "Add*" "Create*" or "Update*". The rationale is that hard-coded credentials within
|
||||
// tests that are only used for creating or setting values within tests are unlikely to
|
||||
// represent credentials to some accessible system.
|
||||
not (
|
||||
source.getNode().asExpr().getFile() instanceof TestFile and
|
||||
exists(MethodCall createOrAddCall, string createOrAddMethodName |
|
||||
createOrAddMethodName.matches("Update%") or
|
||||
createOrAddMethodName.matches("Create%") or
|
||||
createOrAddMethodName.matches("Add%")
|
||||
|
|
||||
createOrAddCall.getTarget().hasName(createOrAddMethodName) and
|
||||
createOrAddCall.getAnArgument() = sink.getNode().asExpr()
|
||||
)
|
||||
override predicate hasFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
|
||||
super.hasFlowPath(source, sink) and
|
||||
// Exclude hard-coded credentials in tests if they only flow to calls to methods with a name
|
||||
// like "Add*" "Create*" or "Update*". The rationale is that hard-coded credentials within
|
||||
// tests that are only used for creating or setting values within tests are unlikely to
|
||||
// represent credentials to some accessible system.
|
||||
not (
|
||||
source.getNode().asExpr().getFile() instanceof TestFile and
|
||||
exists(MethodCall createOrAddCall, string createOrAddMethodName |
|
||||
createOrAddMethodName.matches("Update%") or
|
||||
createOrAddMethodName.matches("Create%") or
|
||||
createOrAddMethodName.matches("Add%")
|
||||
|
|
||||
createOrAddCall.getTarget().hasName(createOrAddMethodName) and
|
||||
createOrAddCall.getAnArgument() = sink.getNode().asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A string literal that is not empty.
|
||||
*/
|
||||
class NonEmptyStringLiteral extends Source {
|
||||
NonEmptyStringLiteral() { this.getExpr().(StringLiteral).getValue().length() > 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The creation of a literal byte array.
|
||||
*/
|
||||
class ByteArrayLiteral extends Source {
|
||||
ByteArrayLiteral() {
|
||||
this.getExpr() =
|
||||
any(ArrayCreation ac |
|
||||
ac.getArrayType().getElementType() instanceof ByteType and
|
||||
ac.hasInitializer()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The creation of a literal char array.
|
||||
*/
|
||||
class CharArrayLiteral extends Source {
|
||||
CharArrayLiteral() {
|
||||
this.getExpr() =
|
||||
any(ArrayCreation ac |
|
||||
ac.getArrayType().getElementType() instanceof CharType and
|
||||
ac.hasInitializer()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignable whose name indicates that the value being held is a credential.
|
||||
*/
|
||||
private class CredentialVar extends Assignable {
|
||||
pragma[noinline]
|
||||
CredentialVar() {
|
||||
exists(string name | name = this.getName() |
|
||||
name.regexpMatch("(?i).*pass(wd|word|code|phrase)(?!.*question).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(puid|username|userid).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(cert)(?!.*(format|name)).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CredentialVariableAccess extends VariableAccess {
|
||||
pragma[noinline]
|
||||
CredentialVariableAccess() { this.getTarget() instanceof CredentialVar }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a credential sink, a display name, the operation it exists in, and a description of the sink.
|
||||
*/
|
||||
private predicate getCredentialSink(
|
||||
Expr sink, string sinkName, Element supplementaryElement, string description
|
||||
) {
|
||||
// An argument to a library call that looks like a credential
|
||||
// "...flows to the [Username] parameter in [call to method CreateUser]"
|
||||
exists(Call call, CredentialVar param |
|
||||
supplementaryElement = call and
|
||||
description = "the $@ parameter in $@" and
|
||||
sink = call.getArgumentForParameter(param) and
|
||||
sinkName = param.getName() and
|
||||
call.getTarget().fromLibrary()
|
||||
)
|
||||
or
|
||||
// An argument to a library setter call for a property that looks like a credential
|
||||
// "...flows to the [setter call argument] for the property [UserName]"
|
||||
exists(Property p, Call call |
|
||||
call = p.getSetter().getACall() and
|
||||
supplementaryElement = p and
|
||||
description = "the $@ in $@" and
|
||||
sink = call.getArgument(0) and
|
||||
sinkName = "setter call argument" and
|
||||
p instanceof CredentialVar and
|
||||
p.fromLibrary()
|
||||
)
|
||||
or
|
||||
// Sink compared to password variable
|
||||
// "...flows to [] which is compared against [access of UserName]"
|
||||
exists(ComparisonTest ct, CredentialVariableAccess credentialAccess |
|
||||
sinkName = sink.toString() and
|
||||
supplementaryElement = credentialAccess and
|
||||
description = "$@ which is compared against $@" and
|
||||
ct.getAnArgument() = credentialAccess and
|
||||
ct.getAnArgument() = sink and
|
||||
ct.getComparisonKind().isEquality() and
|
||||
not sink = credentialAccess
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is a sink for a specific type of credential.
|
||||
*/
|
||||
class HardcodedCredentialsSinkExpr extends Sink {
|
||||
private string description;
|
||||
private Element supplementaryElement;
|
||||
private string sinkName;
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
HardcodedCredentialsSinkExpr() {
|
||||
getCredentialSink(this.getExpr(), sinkName, supplementaryElement, description)
|
||||
}
|
||||
/**
|
||||
* A string literal that is not empty.
|
||||
*/
|
||||
class NonEmptyStringLiteral extends Source {
|
||||
NonEmptyStringLiteral() { this.getExpr().(StringLiteral).getValue().length() > 1 }
|
||||
}
|
||||
|
||||
override string getSinkDescription() { result = description }
|
||||
|
||||
override Element getSupplementaryElement() { result = supplementaryElement }
|
||||
|
||||
override string getSinkName() { result = sinkName }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "name" argument to a construction of "MembershipUser" or a subtype.
|
||||
*/
|
||||
class MembershipUserUserNameSink extends Sink {
|
||||
private Call call;
|
||||
|
||||
MembershipUserUserNameSink() {
|
||||
call.getTarget().getDeclaringType().getABaseType*() instanceof
|
||||
SystemWebSecurityMembershipUserClass and
|
||||
this.getExpr() = call.getArgumentForName("name")
|
||||
}
|
||||
|
||||
override string getSinkDescription() { result = "the $@ parameter in $@" }
|
||||
|
||||
override Element getSupplementaryElement() { result = call }
|
||||
|
||||
override string getSinkName() { result = "name" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "rawData" argument to a construction of "X509Certificate" or a subtype.
|
||||
*/
|
||||
class X509CertificateDataSink extends Sink {
|
||||
private ObjectCreation x509Creation;
|
||||
|
||||
X509CertificateDataSink() {
|
||||
x509Creation.getTarget().getDeclaringType() instanceof
|
||||
SystemSecurityCryptographyX509CertificatesX509CertificateClass and
|
||||
this.getExpr() = x509Creation.getArgumentForName("rawData")
|
||||
}
|
||||
|
||||
override string getSinkDescription() { result = "the $@ parameter in $@" }
|
||||
|
||||
override Element getSupplementaryElement() { result = x509Creation }
|
||||
|
||||
override string getSinkName() { result = "rawData" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A format argument to `Format`, that is considered not to be a source of hardcoded secret data.
|
||||
*/
|
||||
class StringFormatSanitizer extends Sanitizer {
|
||||
StringFormatSanitizer() {
|
||||
this.getExpr() =
|
||||
any(SystemStringClass s).getFormatMethod().getACall().getArgumentForName("format")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A replacement argument to `Replace`, that is considered not to be a source of hardcoded secret
|
||||
* data.
|
||||
*/
|
||||
class StringReplaceSanitizer extends Sanitizer {
|
||||
StringReplaceSanitizer() {
|
||||
exists(SystemStringClass s, Call c | c = s.getReplaceMethod().getACall() |
|
||||
this.getExpr() = c.getArgumentForName("newValue") or
|
||||
this.getExpr() = c.getArgumentForName("newChar")
|
||||
/**
|
||||
* The creation of a literal byte array.
|
||||
*/
|
||||
class ByteArrayLiteral extends Source {
|
||||
ByteArrayLiteral() {
|
||||
this.getExpr() =
|
||||
any(ArrayCreation ac |
|
||||
ac.getArrayType().getElementType() instanceof ByteType and
|
||||
ac.hasInitializer()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `ToString()` method, which is considered not to return hard-coded constants.
|
||||
*/
|
||||
class ToStringSanitizer extends Sanitizer {
|
||||
ToStringSanitizer() { this.getExpr() = any(Call c | c.getTarget().hasName("ToString")) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The creation of a literal char array.
|
||||
*/
|
||||
class CharArrayLiteral extends Source {
|
||||
CharArrayLiteral() {
|
||||
this.getExpr() =
|
||||
any(ArrayCreation ac |
|
||||
ac.getArrayType().getElementType() instanceof CharType and
|
||||
ac.hasInitializer()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignable whose name indicates that the value being held is a credential.
|
||||
*/
|
||||
private class CredentialVar extends Assignable {
|
||||
pragma[noinline]
|
||||
CredentialVar() {
|
||||
exists(string name | name = this.getName() |
|
||||
name.regexpMatch("(?i).*pass(wd|word|code|phrase)(?!.*question).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(puid|username|userid).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(cert)(?!.*(format|name)).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CredentialVariableAccess extends VariableAccess {
|
||||
pragma[noinline]
|
||||
CredentialVariableAccess() { this.getTarget() instanceof CredentialVar }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a credential sink, a display name, the operation it exists in, and a description of the sink.
|
||||
*/
|
||||
private predicate getCredentialSink(
|
||||
Expr sink, string sinkName, Element supplementaryElement, string description
|
||||
) {
|
||||
// An argument to a library call that looks like a credential
|
||||
// "...flows to the [Username] parameter in [call to method CreateUser]"
|
||||
exists(Call call, CredentialVar param |
|
||||
supplementaryElement = call and
|
||||
description = "the $@ parameter in $@" and
|
||||
sink = call.getArgumentForParameter(param) and
|
||||
sinkName = param.getName() and
|
||||
call.getTarget().fromLibrary()
|
||||
)
|
||||
or
|
||||
// An argument to a library setter call for a property that looks like a credential
|
||||
// "...flows to the [setter call argument] for the property [UserName]"
|
||||
exists(Property p, Call call |
|
||||
call = p.getSetter().getACall() and
|
||||
supplementaryElement = p and
|
||||
description = "the $@ in $@" and
|
||||
sink = call.getArgument(0) and
|
||||
sinkName = "setter call argument" and
|
||||
p instanceof CredentialVar and
|
||||
p.fromLibrary()
|
||||
)
|
||||
or
|
||||
// Sink compared to password variable
|
||||
// "...flows to [] which is compared against [access of UserName]"
|
||||
exists(ComparisonTest ct, CredentialVariableAccess credentialAccess |
|
||||
sinkName = sink.toString() and
|
||||
supplementaryElement = credentialAccess and
|
||||
description = "$@ which is compared against $@" and
|
||||
ct.getAnArgument() = credentialAccess and
|
||||
ct.getAnArgument() = sink and
|
||||
ct.getComparisonKind().isEquality() and
|
||||
not sink = credentialAccess
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is a sink for a specific type of credential.
|
||||
*/
|
||||
class HardcodedCredentialsSinkExpr extends Sink {
|
||||
private string description;
|
||||
private Element supplementaryElement;
|
||||
private string sinkName;
|
||||
|
||||
HardcodedCredentialsSinkExpr() {
|
||||
getCredentialSink(this.getExpr(), sinkName, supplementaryElement, description)
|
||||
}
|
||||
|
||||
override string getSinkDescription() { result = description }
|
||||
|
||||
override Element getSupplementaryElement() { result = supplementaryElement }
|
||||
|
||||
override string getSinkName() { result = sinkName }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "name" argument to a construction of "MembershipUser" or a subtype.
|
||||
*/
|
||||
class MembershipUserUserNameSink extends Sink {
|
||||
private Call call;
|
||||
|
||||
MembershipUserUserNameSink() {
|
||||
call.getTarget().getDeclaringType().getABaseType*() instanceof
|
||||
SystemWebSecurityMembershipUserClass and
|
||||
this.getExpr() = call.getArgumentForName("name")
|
||||
}
|
||||
|
||||
override string getSinkDescription() { result = "the $@ parameter in $@" }
|
||||
|
||||
override Element getSupplementaryElement() { result = call }
|
||||
|
||||
override string getSinkName() { result = "name" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "rawData" argument to a construction of "X509Certificate" or a subtype.
|
||||
*/
|
||||
class X509CertificateDataSink extends Sink {
|
||||
private ObjectCreation x509Creation;
|
||||
|
||||
X509CertificateDataSink() {
|
||||
x509Creation.getTarget().getDeclaringType() instanceof
|
||||
SystemSecurityCryptographyX509CertificatesX509CertificateClass and
|
||||
this.getExpr() = x509Creation.getArgumentForName("rawData")
|
||||
}
|
||||
|
||||
override string getSinkDescription() { result = "the $@ parameter in $@" }
|
||||
|
||||
override Element getSupplementaryElement() { result = x509Creation }
|
||||
|
||||
override string getSinkName() { result = "rawData" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A format argument to `Format`, that is considered not to be a source of hardcoded secret data.
|
||||
*/
|
||||
class StringFormatSanitizer extends Sanitizer {
|
||||
StringFormatSanitizer() {
|
||||
this.getExpr() =
|
||||
any(SystemStringClass s).getFormatMethod().getACall().getArgumentForName("format")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A replacement argument to `Replace`, that is considered not to be a source of hardcoded secret
|
||||
* data.
|
||||
*/
|
||||
class StringReplaceSanitizer extends Sanitizer {
|
||||
StringReplaceSanitizer() {
|
||||
exists(SystemStringClass s, Call c | c = s.getReplaceMethod().getACall() |
|
||||
this.getExpr() = c.getArgumentForName("newValue") or
|
||||
this.getExpr() = c.getArgumentForName("newChar")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `ToString()` method, which is considered not to return hard-coded constants.
|
||||
*/
|
||||
class ToStringSanitizer extends Sanitizer {
|
||||
ToStringSanitizer() { this.getExpr() = any(Call c | c.getTarget().hasName("ToString")) }
|
||||
}
|
||||
|
||||
@@ -4,128 +4,125 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.DirectoryServices
|
||||
private import semmle.code.csharp.frameworks.system.directoryservices.Protocols
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module LDAPInjection {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.DirectoryServices
|
||||
import semmle.code.csharp.frameworks.system.directoryservices.Protocols
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "LDAPInjection" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "LDAPInjection" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument that sets the `Path` property of a `DirectoryEntry` object that is a sink for LDAP
|
||||
* injection.
|
||||
*
|
||||
* This is either an argument to the constructor, or to the setter for the property.
|
||||
*/
|
||||
class DirectoryEntryPathSink extends Sink {
|
||||
DirectoryEntryPathSink() {
|
||||
exists(ObjectCreation create |
|
||||
create.getTarget() = any(SystemDirectoryServicesDirectoryEntryClass d).getAConstructor()
|
||||
|
|
||||
this.getExpr() = create.getArgumentForName("path")
|
||||
)
|
||||
or
|
||||
exists(Property path |
|
||||
path = any(SystemDirectoryServicesDirectoryEntryClass d).getAProperty() and
|
||||
path.hasName("Path")
|
||||
|
|
||||
this.getExpr() = path.getSetter().getACall().getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A argument that sets the `Filter` property of a `DirectorySearcher` object that is a sink for
|
||||
* LDAP injection.
|
||||
*
|
||||
* This is either an argument to the constructor, or to the setter for the property.
|
||||
*/
|
||||
class DirectorySearcherFilterSink extends Sink {
|
||||
DirectorySearcherFilterSink() {
|
||||
exists(ObjectCreation create |
|
||||
create.getTarget() = any(SystemDirectoryServicesDirectorySearcherClass d).getAConstructor()
|
||||
|
|
||||
this.getExpr() = create.getArgumentForName("filter")
|
||||
)
|
||||
or
|
||||
exists(Property filter |
|
||||
filter = any(SystemDirectoryServicesDirectorySearcherClass d).getAProperty() and
|
||||
filter.hasName("Filter")
|
||||
|
|
||||
this.getExpr() = filter.getSetter().getACall().getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A argument that sets the `Filter` property of a `SearchRequest` object that is a sink for
|
||||
* LDAP injection.
|
||||
*
|
||||
* This is either an argument to the constructor, or to the setter for the property.
|
||||
*/
|
||||
class SearchRequestFilterSink extends Sink {
|
||||
SearchRequestFilterSink() {
|
||||
exists(ObjectCreation create |
|
||||
create.getTarget() = any(SystemDirectoryServicesProtocolsSearchRequest d).getAConstructor()
|
||||
|
|
||||
this.getExpr() = create.getArgumentForName("ldapFilter") or
|
||||
this.getExpr() = create.getArgumentForName("filter")
|
||||
)
|
||||
or
|
||||
exists(Property filter |
|
||||
filter = any(SystemDirectoryServicesProtocolsSearchRequest d).getAProperty() and
|
||||
filter.hasName("Filter")
|
||||
|
|
||||
this.getExpr() = filter.getSetter().getACall().getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method which is named "LDAP*Encode", which is likely to be an LDAP sanitizer.
|
||||
*
|
||||
* This will match the encoding methods provided by the AntiXSS library.
|
||||
*/
|
||||
class LDAPEncodeSanitizer extends Sanitizer {
|
||||
LDAPEncodeSanitizer() {
|
||||
this.getExpr().(MethodCall).getTarget().getName().regexpMatch("(?i)LDAP.*Encode.*")
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument that sets the `Path` property of a `DirectoryEntry` object that is a sink for LDAP
|
||||
* injection.
|
||||
*
|
||||
* This is either an argument to the constructor, or to the setter for the property.
|
||||
*/
|
||||
class DirectoryEntryPathSink extends Sink {
|
||||
DirectoryEntryPathSink() {
|
||||
exists(ObjectCreation create |
|
||||
create.getTarget() = any(SystemDirectoryServicesDirectoryEntryClass d).getAConstructor()
|
||||
|
|
||||
this.getExpr() = create.getArgumentForName("path")
|
||||
)
|
||||
or
|
||||
exists(Property path |
|
||||
path = any(SystemDirectoryServicesDirectoryEntryClass d).getAProperty() and
|
||||
path.hasName("Path")
|
||||
|
|
||||
this.getExpr() = path.getSetter().getACall().getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A argument that sets the `Filter` property of a `DirectorySearcher` object that is a sink for
|
||||
* LDAP injection.
|
||||
*
|
||||
* This is either an argument to the constructor, or to the setter for the property.
|
||||
*/
|
||||
class DirectorySearcherFilterSink extends Sink {
|
||||
DirectorySearcherFilterSink() {
|
||||
exists(ObjectCreation create |
|
||||
create.getTarget() = any(SystemDirectoryServicesDirectorySearcherClass d).getAConstructor()
|
||||
|
|
||||
this.getExpr() = create.getArgumentForName("filter")
|
||||
)
|
||||
or
|
||||
exists(Property filter |
|
||||
filter = any(SystemDirectoryServicesDirectorySearcherClass d).getAProperty() and
|
||||
filter.hasName("Filter")
|
||||
|
|
||||
this.getExpr() = filter.getSetter().getACall().getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A argument that sets the `Filter` property of a `SearchRequest` object that is a sink for
|
||||
* LDAP injection.
|
||||
*
|
||||
* This is either an argument to the constructor, or to the setter for the property.
|
||||
*/
|
||||
class SearchRequestFilterSink extends Sink {
|
||||
SearchRequestFilterSink() {
|
||||
exists(ObjectCreation create |
|
||||
create.getTarget() = any(SystemDirectoryServicesProtocolsSearchRequest d).getAConstructor()
|
||||
|
|
||||
this.getExpr() = create.getArgumentForName("ldapFilter") or
|
||||
this.getExpr() = create.getArgumentForName("filter")
|
||||
)
|
||||
or
|
||||
exists(Property filter |
|
||||
filter = any(SystemDirectoryServicesProtocolsSearchRequest d).getAProperty() and
|
||||
filter.hasName("Filter")
|
||||
|
|
||||
this.getExpr() = filter.getSetter().getACall().getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method which is named "LDAP*Encode", which is likely to be an LDAP sanitizer.
|
||||
*
|
||||
* This will match the encoding methods provided by the AntiXSS library.
|
||||
*/
|
||||
class LDAPEncodeSanitizer extends Sanitizer {
|
||||
LDAPEncodeSanitizer() {
|
||||
this.getExpr().(MethodCall).getTarget().getName().regexpMatch("(?i)LDAP.*Encode.*")
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -3,77 +3,74 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
|
||||
|
||||
module LogForging {
|
||||
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.Sanitizers
|
||||
import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
|
||||
/**
|
||||
* A data flow source for untrusted user input used in log entries.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input used in log entries.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in log entries.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in log entries.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in log entries.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in log entries.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in log entries.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "LogForging" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in log entries.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "LogForging" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
class HtmlSanitizer extends Sanitizer {
|
||||
HtmlSanitizer() { this.asExpr() instanceof HtmlSanitizedExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a call to a method on a logger class.
|
||||
*/
|
||||
class LogForgingLogMessageSink extends Sink, LogMessageSink { }
|
||||
|
||||
/**
|
||||
* An argument to a call to a method on a trace class.
|
||||
*/
|
||||
class LogForgingTraceMessageSink extends Sink, TraceMessageSink { }
|
||||
|
||||
/**
|
||||
* A call to String replace or remove that is considered to sanitize replaced string.
|
||||
*/
|
||||
class StringReplaceSanitizer extends Sanitizer {
|
||||
StringReplaceSanitizer() {
|
||||
exists(Method m |
|
||||
exists(SystemStringClass s | m = s.getReplaceMethod() or m = s.getRemoveMethod())
|
||||
or
|
||||
m = any(SystemTextRegularExpressionsRegexClass r).getAReplaceMethod()
|
||||
|
|
||||
this.asExpr() = m.getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
private class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
private class HtmlSanitizer extends Sanitizer {
|
||||
HtmlSanitizer() { this.asExpr() instanceof HtmlSanitizedExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a call to a method on a logger class.
|
||||
*/
|
||||
private class LogForgingLogMessageSink extends Sink, LogMessageSink { }
|
||||
|
||||
/**
|
||||
* An argument to a call to a method on a trace class.
|
||||
*/
|
||||
private class LogForgingTraceMessageSink extends Sink, TraceMessageSink { }
|
||||
|
||||
/**
|
||||
* A call to String replace or remove that is considered to sanitize replaced string.
|
||||
*/
|
||||
private class StringReplaceSanitizer extends Sanitizer {
|
||||
StringReplaceSanitizer() {
|
||||
exists(Method m |
|
||||
exists(SystemStringClass s | m = s.getReplaceMethod() or m = s.getRemoveMethod())
|
||||
or
|
||||
m = any(SystemTextRegularExpressionsRegexClass r).getAReplaceMethod()
|
||||
|
|
||||
this.asExpr() = m.getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -4,97 +4,94 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.Xml
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module MissingXMLValidation {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.Xml
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for untrusted user input processed as XML without validation against a known
|
||||
* schema.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input processed as XML without validation against a known
|
||||
* schema.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input processed as XML without validation against a known
|
||||
* schema.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/** Gets a string describing the reason why this is a sink. */
|
||||
abstract string getReason();
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input processed as XML without validation against a known schema.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input processed as XML without validation against a
|
||||
* known schema.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "MissingXMLValidation" }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The input argument to a call to `XmlReader.Create` where the input will not be validated against
|
||||
* a schema.
|
||||
*/
|
||||
class XmlReaderCreateCallSink extends Sink {
|
||||
XmlReaderCreateCall createCall;
|
||||
|
||||
XmlReaderCreateCallSink() {
|
||||
// This is the XML that will be processed
|
||||
this.getExpr() = createCall.getArgumentForName("input")
|
||||
}
|
||||
|
||||
override string getReason() {
|
||||
// No settings = no Schema validation
|
||||
result = "there is no 'XmlReaderSettings' instance specifying schema validation." and
|
||||
not exists(createCall.getSettings())
|
||||
or
|
||||
// An XmlReaderSettings instance is passed where:
|
||||
// - The ValidationType is not set to Schema; or
|
||||
// - The ValidationType is set to Schema, but:
|
||||
// - The ProcessInlineSchema option is set (this allows the document to set a schema
|
||||
// internally); or
|
||||
// - The ProcessSchemaLocation option is set (this allows the document to reference a
|
||||
// schema by location that this document will validate against).
|
||||
result = "the 'XmlReaderSettings' instance does not specify the 'ValidationType' as 'Schema'." and
|
||||
exists(XmlReaderSettingsCreation settingsCreation |
|
||||
settingsCreation = createCall.getSettings().getASettingsCreation()
|
||||
|
|
||||
not settingsCreation.getValidationType().hasName("Schema")
|
||||
)
|
||||
or
|
||||
exists(string badValidationFlag |
|
||||
result = "the 'XmlReaderSettings' instance specifies '" + badValidationFlag + "'." and
|
||||
exists(XmlReaderSettingsCreation settingsCreation |
|
||||
settingsCreation = createCall.getSettings().getASettingsCreation() and
|
||||
settingsCreation.getValidationType().hasName("Schema") and
|
||||
settingsCreation.getAValidationFlag().hasName(badValidationFlag)
|
||||
|
|
||||
badValidationFlag = "ProcessInlineSchema" or
|
||||
badValidationFlag = "ProcessSchemaLocation"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
/**
|
||||
* A data flow sink for untrusted user input processed as XML without validation against a known
|
||||
* schema.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/** Gets a string describing the reason why this is a sink. */
|
||||
abstract string getReason();
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input processed as XML without validation against a known schema.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input processed as XML without validation against a
|
||||
* known schema.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "MissingXMLValidation" }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The input argument to a call to `XmlReader.Create` where the input will not be validated against
|
||||
* a schema.
|
||||
*/
|
||||
class XmlReaderCreateCallSink extends Sink {
|
||||
XmlReaderCreateCall createCall;
|
||||
|
||||
XmlReaderCreateCallSink() {
|
||||
// This is the XML that will be processed
|
||||
this.getExpr() = createCall.getArgumentForName("input")
|
||||
}
|
||||
|
||||
override string getReason() {
|
||||
// No settings = no Schema validation
|
||||
result = "there is no 'XmlReaderSettings' instance specifying schema validation." and
|
||||
not exists(createCall.getSettings())
|
||||
or
|
||||
// An XmlReaderSettings instance is passed where:
|
||||
// - The ValidationType is not set to Schema; or
|
||||
// - The ValidationType is set to Schema, but:
|
||||
// - The ProcessInlineSchema option is set (this allows the document to set a schema
|
||||
// internally); or
|
||||
// - The ProcessSchemaLocation option is set (this allows the document to reference a
|
||||
// schema by location that this document will validate against).
|
||||
result = "the 'XmlReaderSettings' instance does not specify the 'ValidationType' as 'Schema'." and
|
||||
exists(XmlReaderSettingsCreation settingsCreation |
|
||||
settingsCreation = createCall.getSettings().getASettingsCreation()
|
||||
|
|
||||
not settingsCreation.getValidationType().hasName("Schema")
|
||||
)
|
||||
or
|
||||
exists(string badValidationFlag |
|
||||
result = "the 'XmlReaderSettings' instance specifies '" + badValidationFlag + "'." and
|
||||
exists(XmlReaderSettingsCreation settingsCreation |
|
||||
settingsCreation = createCall.getSettings().getASettingsCreation() and
|
||||
settingsCreation.getValidationType().hasName("Schema") and
|
||||
settingsCreation.getAValidationFlag().hasName(badValidationFlag)
|
||||
|
|
||||
badValidationFlag = "ProcessInlineSchema" or
|
||||
badValidationFlag = "ProcessSchemaLocation"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -4,96 +4,93 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.dataflow.DataFlow2
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module ReDoS {
|
||||
private import semmle.code.csharp.dataflow.DataFlow2
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ReDoS" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ReDoS" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that represents a regular expression with potential exponential behavior.
|
||||
*/
|
||||
predicate isExponentialRegex(StringLiteral s) {
|
||||
/*
|
||||
* Detect three variants of a common pattern that leads to exponential blow-up.
|
||||
*/
|
||||
|
||||
// Example: ([a-z]+.)+
|
||||
s.getValue().regexpMatch(".*\\([^()*+\\]]+\\]?(\\*|\\+)\\.?\\)(\\*|\\+).*")
|
||||
or
|
||||
// Example: (([a-z])?([a-z]+.))+
|
||||
s.getValue()
|
||||
.regexpMatch(".*\\((\\([^()]+\\)\\?)?\\([^()*+\\]]+\\]?(\\*|\\+)\\.?\\)\\)(\\*|\\+).*")
|
||||
or
|
||||
// Example: (([a-z])+.)+
|
||||
s.getValue().regexpMatch(".*\\(\\([^()*+\\]]+\\]?\\)(\\*|\\+)\\.?\\)(\\*|\\+).*")
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow configuration for tracking exponential worst case time regular expression string
|
||||
* literals to the pattern argument of a regex.
|
||||
*/
|
||||
class ExponentialRegexDataflow extends DataFlow2::Configuration {
|
||||
ExponentialRegexDataflow() { this = "ExponentialRegex" }
|
||||
|
||||
override predicate isSource(DataFlow::Node s) { isExponentialRegex(s.asExpr()) }
|
||||
|
||||
override predicate isSink(DataFlow::Node s) { s.asExpr() = any(RegexOperation c).getPattern() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression passed as the `input` to a call to a `Regex` method, where the regex appears to
|
||||
* have exponential behaviour.
|
||||
*/
|
||||
class ExponentialRegexSink extends DataFlow::ExprNode, Sink {
|
||||
ExponentialRegexSink() {
|
||||
exists(ExponentialRegexDataflow regexDataflow, RegexOperation regexOperation |
|
||||
// Exponential regex flows to the pattern argument
|
||||
regexDataflow.hasFlow(_, DataFlow::exprNode(regexOperation.getPattern()))
|
||||
|
|
||||
// This is used as an input for this pattern
|
||||
this.getExpr() = regexOperation.getInput() and
|
||||
// No timeouts
|
||||
not regexOperation.hasTimeout()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that represents a regular expression with potential exponential behavior.
|
||||
*/
|
||||
predicate isExponentialRegex(StringLiteral s) {
|
||||
/*
|
||||
* Detect three variants of a common pattern that leads to exponential blow-up.
|
||||
*/
|
||||
|
||||
// Example: ([a-z]+.)+
|
||||
s.getValue().regexpMatch(".*\\([^()*+\\]]+\\]?(\\*|\\+)\\.?\\)(\\*|\\+).*")
|
||||
or
|
||||
// Example: (([a-z])?([a-z]+.))+
|
||||
s.getValue()
|
||||
.regexpMatch(".*\\((\\([^()]+\\)\\?)?\\([^()*+\\]]+\\]?(\\*|\\+)\\.?\\)\\)(\\*|\\+).*")
|
||||
or
|
||||
// Example: (([a-z])+.)+
|
||||
s.getValue().regexpMatch(".*\\(\\([^()*+\\]]+\\]?\\)(\\*|\\+)\\.?\\)(\\*|\\+).*")
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow configuration for tracking exponential worst case time regular expression string
|
||||
* literals to the pattern argument of a regex.
|
||||
*/
|
||||
class ExponentialRegexDataflow extends DataFlow2::Configuration {
|
||||
ExponentialRegexDataflow() { this = "ExponentialRegex" }
|
||||
|
||||
override predicate isSource(DataFlow::Node s) { isExponentialRegex(s.asExpr()) }
|
||||
|
||||
override predicate isSink(DataFlow::Node s) { s.asExpr() = any(RegexOperation c).getPattern() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression passed as the `input` to a call to a `Regex` method, where the regex appears to
|
||||
* have exponential behaviour.
|
||||
*/
|
||||
class ExponentialRegexSink extends DataFlow::ExprNode, Sink {
|
||||
ExponentialRegexSink() {
|
||||
exists(ExponentialRegexDataflow regexDataflow, RegexOperation regexOperation |
|
||||
// Exponential regex flows to the pattern argument
|
||||
regexDataflow.hasFlow(_, DataFlow::exprNode(regexOperation.getPattern()))
|
||||
|
|
||||
// This is used as an input for this pattern
|
||||
this.getExpr() = regexOperation.getInput() and
|
||||
// No timeouts
|
||||
not regexOperation.hasTimeout()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -4,66 +4,63 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module RegexInjection {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "RegexInjection" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "RegexInjection" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `pattern` argument to a construction of a `Regex`.
|
||||
*/
|
||||
class RegexObjectCreationSink extends Sink {
|
||||
RegexObjectCreationSink() {
|
||||
exists(RegexOperation operation |
|
||||
this.getExpr() = operation.getPattern() and
|
||||
not operation.hasTimeout()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `Regex.Escape` that sanitizes the user input for use in a regex. */
|
||||
class RegexEscapeSanitizer extends Sanitizer {
|
||||
RegexEscapeSanitizer() {
|
||||
this.getExpr().(MethodCall).getTarget() =
|
||||
any(SystemTextRegularExpressionsRegexClass r).getAMethod("Escape")
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `pattern` argument to a construction of a `Regex`.
|
||||
*/
|
||||
class RegexObjectCreationSink extends Sink {
|
||||
RegexObjectCreationSink() {
|
||||
exists(RegexOperation operation |
|
||||
this.getExpr() = operation.getPattern() and
|
||||
not operation.hasTimeout()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `Regex.Escape` that sanitizes the user input for use in a regex. */
|
||||
class RegexEscapeSanitizer extends Sanitizer {
|
||||
RegexEscapeSanitizer() {
|
||||
this.getExpr().(MethodCall).getTarget() =
|
||||
any(SystemTextRegularExpressionsRegexClass r).getAMethod("Escape")
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -3,60 +3,57 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
private import semmle.code.csharp.frameworks.system.Data
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module ResourceInjection {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
import semmle.code.csharp.frameworks.system.Data
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ResourceInjection" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ResourceInjection" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/** A source of local user input. */
|
||||
class LocalSource extends Source {
|
||||
LocalSource() { this instanceof LocalFlowSource }
|
||||
}
|
||||
|
||||
/** An argument to the `ConnectionString` property on a data connection class. */
|
||||
class SqlConnectionStringSink extends Sink {
|
||||
SqlConnectionStringSink() {
|
||||
this.getExpr() =
|
||||
any(SystemDataConnectionClass dataConn).getConnectionStringProperty().getAnAssignedValue()
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A source of local user input. */
|
||||
class LocalSource extends Source {
|
||||
LocalSource() { this instanceof LocalFlowSource }
|
||||
}
|
||||
|
||||
/** An argument to the `ConnectionString` property on a data connection class. */
|
||||
class SqlConnectionStringSink extends Sink {
|
||||
SqlConnectionStringSink() {
|
||||
this.getExpr() =
|
||||
any(SystemDataConnectionClass dataConn).getConnectionStringProperty().getAnAssignedValue()
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -3,57 +3,54 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
private import semmle.code.csharp.frameworks.Sql
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module SqlInjection {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
import semmle.code.csharp.frameworks.Sql
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A source specific to SQL injection vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A source specific to SQL injection vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A sink for SQL injection vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sink for SQL injection vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for SQL injection vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for SQL injection vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for SQL injection vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "SqlInjection" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for SQL injection vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "SqlInjection" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/** A source of local user input. */
|
||||
class LocalSource extends Source {
|
||||
LocalSource() { this instanceof LocalFlowSource }
|
||||
}
|
||||
|
||||
/** An SQL expression passed to an API call that executes SQL. */
|
||||
class SqlInjectionExprSink extends Sink {
|
||||
SqlInjectionExprSink() { exists(SqlExpr s | this.getExpr() = s.getSql()) }
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A source of local user input. */
|
||||
class LocalSource extends Source {
|
||||
LocalSource() { this instanceof LocalFlowSource }
|
||||
}
|
||||
|
||||
/** An SQL expression passed to an API call that executes SQL. */
|
||||
class SqlInjectionExprSink extends Sink {
|
||||
SqlInjectionExprSink() { exists(SqlExpr s | this.getExpr() = s.getSql()) }
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -4,147 +4,142 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.IO
|
||||
private import semmle.code.csharp.frameworks.system.Web
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module TaintedPath {
|
||||
import semmle.code.csharp.controlflow.Guards
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.IO
|
||||
import semmle.code.csharp.frameworks.system.Web
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "TaintedPath" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "TaintedPath" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `File` method call.
|
||||
*/
|
||||
class FileCreateSink extends Sink {
|
||||
FileCreateSink() {
|
||||
exists(Method create | create = any(SystemIOFileClass f).getAMethod() |
|
||||
this.getExpr() = create.getACall().getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `Directory` method call.
|
||||
*/
|
||||
class DirectorySink extends Sink {
|
||||
DirectorySink() {
|
||||
exists(Method create | create = any(SystemIODirectoryClass f).getAMethod() |
|
||||
this.getExpr() = create.getACall().getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `FileStream` constructor call.
|
||||
*/
|
||||
class FileStreamSink extends Sink {
|
||||
FileStreamSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType() = any(SystemIOFileStreamClass f)
|
||||
|
|
||||
this.getExpr() = oc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `StreamWriter` constructor call.
|
||||
*/
|
||||
class StreamWriterTaintedPathSink extends Sink {
|
||||
StreamWriterTaintedPathSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType() = any(SystemIOStreamWriterClass f)
|
||||
|
|
||||
this.getExpr() = oc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A weak guard that is insufficient to prevent path tampering.
|
||||
*/
|
||||
private class WeakGuard extends Guard {
|
||||
WeakGuard() {
|
||||
// None of these are sufficient to guarantee that a string is safe.
|
||||
exists(MethodCall mc, Method m | this = mc and mc.getTarget() = m |
|
||||
m.getName() = "StartsWith" or
|
||||
m.getName() = "EndsWith" or
|
||||
m.getName() = "IsNullOrEmpty" or
|
||||
m.getName() = "IsNullOrWhitespace" or
|
||||
m = any(SystemIOFileClass f).getAMethod("Exists") or
|
||||
m = any(SystemIODirectoryClass f).getAMethod("Exists")
|
||||
)
|
||||
or
|
||||
// Checking against `null` has no bearing on path traversal.
|
||||
this.controlsNode(_, _, any(AbstractValues::NullValue nv))
|
||||
or
|
||||
this.(LogicalOperation).getAnOperand() instanceof WeakGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A conditional involving the path, that is not considered to be a weak check.
|
||||
*
|
||||
* A weak check is one that is insufficient to prevent path tampering.
|
||||
*/
|
||||
class PathCheck extends Sanitizer {
|
||||
PathCheck() {
|
||||
// This expression is structurally replicated in a dominating guard which is not a "weak" check
|
||||
exists(Guard g, AbstractValues::BooleanValue v |
|
||||
g = this.(GuardedDataFlowNode).getAGuard(_, v) and
|
||||
not g instanceof WeakGuard
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `HttpRequest.MapPath` that is considered to sanitize the input.
|
||||
*/
|
||||
class RequestMapPathSanitizer extends Sanitizer {
|
||||
RequestMapPathSanitizer() {
|
||||
exists(Method m |
|
||||
m = any(SystemWebHttpRequestClass request).getAMethod() and
|
||||
m.hasName("MapPath")
|
||||
|
|
||||
this.getExpr() = m.getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `File` method call.
|
||||
*/
|
||||
class FileCreateSink extends Sink {
|
||||
FileCreateSink() {
|
||||
exists(Method create | create = any(SystemIOFileClass f).getAMethod() |
|
||||
this.getExpr() = create.getACall().getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `Directory` method call.
|
||||
*/
|
||||
class DirectorySink extends Sink {
|
||||
DirectorySink() {
|
||||
exists(Method create | create = any(SystemIODirectoryClass f).getAMethod() |
|
||||
this.getExpr() = create.getACall().getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `FileStream` constructor call.
|
||||
*/
|
||||
class FileStreamSink extends Sink {
|
||||
FileStreamSink() {
|
||||
exists(ObjectCreation oc | oc.getTarget().getDeclaringType() = any(SystemIOFileStreamClass f) |
|
||||
this.getExpr() = oc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a `StreamWriter` constructor call.
|
||||
*/
|
||||
class StreamWriterTaintedPathSink extends Sink {
|
||||
StreamWriterTaintedPathSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType() = any(SystemIOStreamWriterClass f)
|
||||
|
|
||||
this.getExpr() = oc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A weak guard that is insufficient to prevent path tampering.
|
||||
*/
|
||||
private class WeakGuard extends Guard {
|
||||
WeakGuard() {
|
||||
// None of these are sufficient to guarantee that a string is safe.
|
||||
exists(MethodCall mc, Method m | this = mc and mc.getTarget() = m |
|
||||
m.getName() = "StartsWith" or
|
||||
m.getName() = "EndsWith" or
|
||||
m.getName() = "IsNullOrEmpty" or
|
||||
m.getName() = "IsNullOrWhitespace" or
|
||||
m = any(SystemIOFileClass f).getAMethod("Exists") or
|
||||
m = any(SystemIODirectoryClass f).getAMethod("Exists")
|
||||
)
|
||||
or
|
||||
// Checking against `null` has no bearing on path traversal.
|
||||
this.controlsNode(_, _, any(AbstractValues::NullValue nv))
|
||||
or
|
||||
this.(LogicalOperation).getAnOperand() instanceof WeakGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A conditional involving the path, that is not considered to be a weak check.
|
||||
*
|
||||
* A weak check is one that is insufficient to prevent path tampering.
|
||||
*/
|
||||
class PathCheck extends Sanitizer {
|
||||
PathCheck() {
|
||||
// This expression is structurally replicated in a dominating guard which is not a "weak" check
|
||||
exists(Guard g, AbstractValues::BooleanValue v |
|
||||
g = this.(GuardedDataFlowNode).getAGuard(_, v) and
|
||||
not g instanceof WeakGuard
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `HttpRequest.MapPath` that is considered to sanitize the input.
|
||||
*/
|
||||
class RequestMapPathSanitizer extends Sanitizer {
|
||||
RequestMapPathSanitizer() {
|
||||
exists(Method m |
|
||||
m = any(SystemWebHttpRequestClass request).getAMethod() and
|
||||
m.hasName("MapPath")
|
||||
|
|
||||
this.getExpr() = m.getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -4,79 +4,76 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.serialization.Deserializers
|
||||
|
||||
module UnsafeDeserialization {
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.serialization.Deserializers
|
||||
/**
|
||||
* A data flow source for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
/**
|
||||
* A sanitizer for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about unsafe deserialization.
|
||||
*/
|
||||
class TaintTrackingConfig extends TaintTracking::Configuration {
|
||||
TaintTrackingConfig() { this = "UnsafeDeserialization" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about unsafe deserialization.
|
||||
*/
|
||||
class TaintTrackingConfig extends TaintTracking::Configuration {
|
||||
TaintTrackingConfig() { this = "UnsafeDeserialization" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
private class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A call to an unsafe deserializer. */
|
||||
class UnsafeDeserializerSink extends Sink {
|
||||
UnsafeDeserializerSink() {
|
||||
exists(Call c |
|
||||
this.asExpr() = c.getAnArgument() and
|
||||
c.getTarget() instanceof UnsafeDeserializer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaScriptSerializerClass extends Class {
|
||||
JavaScriptSerializerClass() {
|
||||
this.hasQualifiedName("System.Web.Script.Serialization.JavaScriptSerializer")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsafe use of a JavaScript deserializer. That is, a use with a custom type-resolver
|
||||
* (constructor parameter).
|
||||
*/
|
||||
class JavaScriptSerializerSink extends Sink {
|
||||
JavaScriptSerializerSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType() instanceof JavaScriptSerializerClass and
|
||||
oc.getTarget().getNumberOfParameters() > 0 and
|
||||
exists(MethodCall mc, Method m |
|
||||
m = mc.getTarget() and
|
||||
m.getDeclaringType() instanceof JavaScriptSerializerClass and
|
||||
(
|
||||
m.hasName("Deserialize") or
|
||||
m.hasName("DeserializeObject")
|
||||
) and
|
||||
this.asExpr() = mc.getAnArgument() and
|
||||
DataFlow::localFlow(DataFlow::exprNode(oc), DataFlow::exprNode(mc.getQualifier()))
|
||||
)
|
||||
)
|
||||
}
|
||||
/** A call to an unsafe deserializer. */
|
||||
private class UnsafeDeserializerSink extends Sink {
|
||||
UnsafeDeserializerSink() {
|
||||
exists(Call c |
|
||||
this.asExpr() = c.getAnArgument() and
|
||||
c.getTarget() instanceof UnsafeDeserializer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaScriptSerializerClass extends Class {
|
||||
JavaScriptSerializerClass() {
|
||||
this.hasQualifiedName("System.Web.Script.Serialization.JavaScriptSerializer")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsafe use of a JavaScript deserializer. That is, a use with a custom type-resolver
|
||||
* (constructor parameter).
|
||||
*/
|
||||
private class JavaScriptSerializerSink extends Sink {
|
||||
JavaScriptSerializerSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType() instanceof JavaScriptSerializerClass and
|
||||
oc.getTarget().getNumberOfParameters() > 0 and
|
||||
exists(MethodCall mc, Method m |
|
||||
m = mc.getTarget() and
|
||||
m.getDeclaringType() instanceof JavaScriptSerializerClass and
|
||||
(
|
||||
m.hasName("Deserialize") or
|
||||
m.hasName("DeserializeObject")
|
||||
) and
|
||||
this.asExpr() = mc.getAnArgument() and
|
||||
DataFlow::localFlow(DataFlow::exprNode(oc), DataFlow::exprNode(mc.getQualifier()))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,227 +3,223 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
private import semmle.code.csharp.frameworks.system.Web
|
||||
private import semmle.code.csharp.frameworks.system.web.Mvc
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
|
||||
|
||||
module UrlRedirect {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.controlflow.Guards
|
||||
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.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A guard for unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A guard for unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "UrlRedirect" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "UrlRedirect" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A URL argument to a call to `HttpResponse.Redirect()` or `Controller.Redirect()`, that is a
|
||||
* sink for URL redirects.
|
||||
*/
|
||||
class RedirectSink extends Sink {
|
||||
RedirectSink() {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget() = any(SystemWebHttpResponseClass response).getRedirectMethod() or
|
||||
mc.getTarget() = any(SystemWebMvcControllerClass response).getARedirectMethod()
|
||||
|
|
||||
// Redirect uses the parameter name url
|
||||
this.getExpr() = mc.getArgumentForName("url")
|
||||
or
|
||||
// RedirectToAction
|
||||
this.getExpr() = mc.getArgumentForName("actionName")
|
||||
or
|
||||
// RedirectToRoute
|
||||
this.getExpr() = mc.getArgumentForName("routeName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A value argument to a call to `AddHeader` or `AppendHeader` that adds the `Location`.
|
||||
*/
|
||||
class LocationHeaderSink extends Sink {
|
||||
LocationHeaderSink() {
|
||||
exists(MethodCall call |
|
||||
call.getTarget() = any(SystemWebHttpResponseClass r).getAppendHeaderMethod() or
|
||||
call.getTarget() = any(SystemWebHttpResponseClass r).getAddHeaderMethod()
|
||||
|
|
||||
call.getArgumentForName("name").getValue() = "Location" and
|
||||
this.getExpr() = call.getArgumentForName("value")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a call to `HttpServerUtility.Transfer`.
|
||||
*/
|
||||
class HttpServerTransferSink extends Sink {
|
||||
HttpServerTransferSink() {
|
||||
exists(MethodCall call |
|
||||
call.getTarget() = any(SystemWebHttpServerUtility s).getTransferMethod()
|
||||
|
|
||||
this.getExpr() = call.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A URL argument to a call to `UrlHelper.isLocalUrl()` that is a sanitizer for URL redirects.
|
||||
*/
|
||||
class IsLocalUrlSanitizer extends SanitizerGuard, MethodCall {
|
||||
IsLocalUrlSanitizer() { this.getTarget().hasName("IsLocalUrl") }
|
||||
|
||||
override predicate checks(Expr e, AbstractValue v) {
|
||||
e = this.getArgument(0) and
|
||||
v.(AbstractValues::BooleanValue).getValue() = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the getter of the RawUrl property, whose value is considered to be safe for URL
|
||||
* redirects.
|
||||
*/
|
||||
class RawUrlSanitizer extends Sanitizer {
|
||||
RawUrlSanitizer() {
|
||||
this.getExpr() = any(SystemWebHttpRequestClass r).getRawUrlProperty().getGetter().getACall()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A string concatenation expression, where the left hand side contains the character "?".
|
||||
*
|
||||
* This is considered as sanitizing the overall expression, because the attacker can then
|
||||
* only control the query string parameters, rather than the location itself. In the majority of
|
||||
* cases, this will only allow the attacker to redirect the user to a link they could have already
|
||||
* redirected them to.
|
||||
*/
|
||||
class ConcatenationSanitizer extends Sanitizer {
|
||||
ConcatenationSanitizer() {
|
||||
this.getType() instanceof StringType and
|
||||
this.getExpr().(AddExpr).getLeftOperand().getValue().matches("%?%")
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to an URL encoder. */
|
||||
class UrlEncodeSanitizer extends Sanitizer {
|
||||
UrlEncodeSanitizer() { this.getExpr() instanceof UrlSanitizedExpr }
|
||||
}
|
||||
|
||||
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(AssignableDefinition def |
|
||||
def.getTarget() = any(MicrosoftAspNetCoreHttpResponseHeaders headers).getLocationProperty()
|
||||
|
|
||||
this.asExpr() = def.getSource()
|
||||
)
|
||||
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 qualifier,
|
||||
MethodCall add
|
||||
|
|
||||
qualifier.getTarget() = resp.getHeadersProperty() and
|
||||
add.getTarget() = cl.getAMethod("Add") and
|
||||
qualifier = 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 qualifier
|
||||
|
|
||||
qualifier.getTarget() = resp.getHeadersProperty() and
|
||||
ci.getTarget() = cl.getAnIndexer() and
|
||||
qualifier = ci.getQualifier() and
|
||||
cs.getTarget() = cl.getAnIndexer().getSetter() and
|
||||
cs.getArgument(0).getValue().toLowerCase() = "location" and
|
||||
this.asExpr() = cs.getArgument(1)
|
||||
)
|
||||
}
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A URL argument to a call to `HttpResponse.Redirect()` or `Controller.Redirect()`, that is a
|
||||
* sink for URL redirects.
|
||||
*/
|
||||
class RedirectSink extends Sink {
|
||||
RedirectSink() {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget() = any(SystemWebHttpResponseClass response).getRedirectMethod() or
|
||||
mc.getTarget() = any(SystemWebMvcControllerClass response).getARedirectMethod()
|
||||
|
|
||||
// Redirect uses the parameter name url
|
||||
this.getExpr() = mc.getArgumentForName("url")
|
||||
or
|
||||
// RedirectToAction
|
||||
this.getExpr() = mc.getArgumentForName("actionName")
|
||||
or
|
||||
// RedirectToRoute
|
||||
this.getExpr() = mc.getArgumentForName("routeName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A value argument to a call to `AddHeader` or `AppendHeader` that adds the `Location`.
|
||||
*/
|
||||
class LocationHeaderSink extends Sink {
|
||||
LocationHeaderSink() {
|
||||
exists(MethodCall call |
|
||||
call.getTarget() = any(SystemWebHttpResponseClass r).getAppendHeaderMethod() or
|
||||
call.getTarget() = any(SystemWebHttpResponseClass r).getAddHeaderMethod()
|
||||
|
|
||||
call.getArgumentForName("name").getValue() = "Location" and
|
||||
this.getExpr() = call.getArgumentForName("value")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a call to `HttpServerUtility.Transfer`.
|
||||
*/
|
||||
class HttpServerTransferSink extends Sink {
|
||||
HttpServerTransferSink() {
|
||||
exists(MethodCall call |
|
||||
call.getTarget() = any(SystemWebHttpServerUtility s).getTransferMethod()
|
||||
|
|
||||
this.getExpr() = call.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A URL argument to a call to `UrlHelper.isLocalUrl()` that is a sanitizer for URL redirects.
|
||||
*/
|
||||
class IsLocalUrlSanitizer extends SanitizerGuard, MethodCall {
|
||||
IsLocalUrlSanitizer() { this.getTarget().hasName("IsLocalUrl") }
|
||||
|
||||
override predicate checks(Expr e, AbstractValue v) {
|
||||
e = this.getArgument(0) and
|
||||
v.(AbstractValues::BooleanValue).getValue() = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the getter of the RawUrl property, whose value is considered to be safe for URL
|
||||
* redirects.
|
||||
*/
|
||||
class RawUrlSanitizer extends Sanitizer {
|
||||
RawUrlSanitizer() {
|
||||
this.getExpr() = any(SystemWebHttpRequestClass r).getRawUrlProperty().getGetter().getACall()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A string concatenation expression, where the left hand side contains the character "?".
|
||||
*
|
||||
* This is considered as sanitizing the overall expression, because the attacker can then
|
||||
* only control the query string parameters, rather than the location itself. In the majority of
|
||||
* cases, this will only allow the attacker to redirect the user to a link they could have already
|
||||
* redirected them to.
|
||||
*/
|
||||
class ConcatenationSanitizer extends Sanitizer {
|
||||
ConcatenationSanitizer() {
|
||||
this.getType() instanceof StringType and
|
||||
this.getExpr().(AddExpr).getLeftOperand().getValue().matches("%?%")
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to an URL encoder. */
|
||||
class UrlEncodeSanitizer extends Sanitizer {
|
||||
UrlEncodeSanitizer() { this.getExpr() instanceof UrlSanitizedExpr }
|
||||
}
|
||||
|
||||
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(AssignableDefinition def |
|
||||
def.getTarget() = any(MicrosoftAspNetCoreHttpResponseHeaders headers).getLocationProperty()
|
||||
|
|
||||
this.asExpr() = def.getSource()
|
||||
)
|
||||
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 qualifier, MethodCall add
|
||||
|
|
||||
qualifier.getTarget() = resp.getHeadersProperty() and
|
||||
add.getTarget() = cl.getAMethod("Add") and
|
||||
qualifier = 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 qualifier
|
||||
|
|
||||
qualifier.getTarget() = resp.getHeadersProperty() and
|
||||
ci.getTarget() = cl.getAnIndexer() and
|
||||
qualifier = ci.getQualifier() and
|
||||
cs.getTarget() = cl.getAnIndexer().getSetter() and
|
||||
cs.getArgument(0).getValue().toLowerCase() = "location" and
|
||||
this.asExpr() = cs.getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,69 +3,66 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
private import semmle.code.csharp.security.xml.InsecureXMLQuery as InsecureXML
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
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.InsecureXMLQuery
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for untrusted user input used in XML processing.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input used in XML processing.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in XML processing.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/**
|
||||
* Gets the reason for the insecurity of this sink.
|
||||
*/
|
||||
abstract string getReason();
|
||||
}
|
||||
|
||||
class InsecureXMLSink extends Sink {
|
||||
private string reason;
|
||||
|
||||
InsecureXMLSink() {
|
||||
exists(InsecureXML::InsecureXmlProcessing r | r.isUnsafe(reason) |
|
||||
this.getExpr() = r.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override string getReason() { result = reason }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in XML processing.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in XML processing.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "XMLInjection" }
|
||||
|
||||
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 }
|
||||
|
||||
override predicate hasFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
|
||||
super.hasFlowPath(source, sink) and
|
||||
exists(sink.getNode().(Sink).getReason())
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
private class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in XML processing.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode {
|
||||
/**
|
||||
* Gets the reason for the insecurity of this sink.
|
||||
*/
|
||||
abstract string getReason();
|
||||
}
|
||||
|
||||
private class InsecureXMLSink extends Sink {
|
||||
private string reason;
|
||||
|
||||
InsecureXMLSink() {
|
||||
exists(InsecureXML::InsecureXmlProcessing r | r.isUnsafe(reason) |
|
||||
this.getExpr() = r.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override string getReason() { result = reason }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in XML processing.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in XML processing.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "XMLInjection" }
|
||||
|
||||
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 }
|
||||
|
||||
override predicate hasFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
|
||||
super.hasFlowPath(source, sink) and
|
||||
exists(sink.getNode().(Sink).getReason())
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -3,83 +3,80 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.xml.XPath
|
||||
private import semmle.code.csharp.frameworks.system.Xml
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
|
||||
module XPathInjection {
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.system.xml.XPath
|
||||
import semmle.code.csharp.frameworks.system.Xml
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
/**
|
||||
* A data flow source for untrusted user input used in XPath expression.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input used in XPath expression.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in XPath expression.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used in XPath expression.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in XPath expression.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted user input used in XPath expression.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in XPath expression.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "XPathInjection" }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in XPath expression.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "XPathInjection" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/** The `xpath` argument to an `XPathExpression.Compile(..)` call. */
|
||||
class XPathExpressionCompileSink extends Sink {
|
||||
XPathExpressionCompileSink() {
|
||||
this.getExpr() =
|
||||
any(SystemXmlXPath::XPathExpression xpathExpr)
|
||||
.getAMethod("Compile")
|
||||
.getACall()
|
||||
.getArgumentForName("xpath")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `xpath` argument to an `XmlNode.Select*Node*(..)` call. */
|
||||
class XmlNodeSink extends Sink {
|
||||
XmlNodeSink() {
|
||||
this.getExpr() =
|
||||
any(SystemXmlXmlNodeClass xmlNode)
|
||||
.getASelectNodeMethod()
|
||||
.getACall()
|
||||
.getArgumentForName("xpath")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `xpath` argument to an `XPathNavigator` call. */
|
||||
class XmlNavigatorSink extends Sink {
|
||||
XmlNavigatorSink() {
|
||||
exists(SystemXmlXPath::XPathNavigator xmlNav, Method m |
|
||||
this.getExpr() = m.getACall().getArgumentForName("xpath")
|
||||
|
|
||||
m = xmlNav.getASelectMethod() or
|
||||
m = xmlNav.getCompileMethod() or
|
||||
m = xmlNav.getAnEvaluateMethod() or
|
||||
m = xmlNav.getAMatchesMethod()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source {
|
||||
RemoteSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** The `xpath` argument to an `XPathExpression.Compile(..)` call. */
|
||||
class XPathExpressionCompileSink extends Sink {
|
||||
XPathExpressionCompileSink() {
|
||||
this.getExpr() =
|
||||
any(SystemXmlXPath::XPathExpression xpathExpr)
|
||||
.getAMethod("Compile")
|
||||
.getACall()
|
||||
.getArgumentForName("xpath")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `xpath` argument to an `XmlNode.Select*Node*(..)` call. */
|
||||
class XmlNodeSink extends Sink {
|
||||
XmlNodeSink() {
|
||||
this.getExpr() =
|
||||
any(SystemXmlXmlNodeClass xmlNode)
|
||||
.getASelectNodeMethod()
|
||||
.getACall()
|
||||
.getArgumentForName("xpath")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `xpath` argument to an `XPathNavigator` call. */
|
||||
class XmlNavigatorSink extends Sink {
|
||||
XmlNavigatorSink() {
|
||||
exists(SystemXmlXPath::XPathNavigator xmlNav, Method m |
|
||||
this.getExpr() = m.getACall().getArgumentForName("xpath")
|
||||
|
|
||||
m = xmlNav.getASelectMethod() or
|
||||
m = xmlNav.getCompileMethod() or
|
||||
m = xmlNav.getAnEvaluateMethod() or
|
||||
m = xmlNav.getAMatchesMethod()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -4,168 +4,169 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import XSSSinks
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.dataflow.DataFlow2
|
||||
private import semmle.code.csharp.dataflow.TaintTracking2
|
||||
|
||||
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."
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = "."
|
||||
)
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(pred,succ)` is an edge in the graph of data flow path explanations. */
|
||||
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
|
||||
// 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
|
||||
)
|
||||
}
|
||||
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. */
|
||||
private 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
* to the abstract class `RemoteFlowSink`.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode, RemoteFlowSink {
|
||||
/** Gets an explanation of this XSS sink. */
|
||||
string explanation() { none() }
|
||||
}
|
||||
|
||||
@@ -163,6 +164,7 @@ class AspInlineMember extends AspInlineCode {
|
||||
/** Gets the member that this inline code references. */
|
||||
Member getMember() { result = member }
|
||||
|
||||
/** Gets the type of this member. */
|
||||
Type getType() { result = getMemberType(getMember()) }
|
||||
}
|
||||
|
||||
|
||||
@@ -3,152 +3,149 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
|
||||
module ZipSlip {
|
||||
import semmle.code.csharp.controlflow.Guards
|
||||
/**
|
||||
* A data flow source for unsafe zip extraction.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow source for unsafe zip extraction.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* A data flow sink for unsafe zip extraction.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for unsafe zip extraction.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A sanitizer for unsafe zip extraction.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for unsafe zip extraction.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
/**
|
||||
* A guard for unsafe zip extraction.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A guard for unsafe zip extraction.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
/** A taint tracking configuration for Zip Slip */
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ZipSlipTaintTracking" }
|
||||
|
||||
/** A taint tracking configuration for Zip Slip */
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ZipSlipTaintTracking" }
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/** An access to the `FullName` property of a `ZipArchiveEntry`. */
|
||||
class ArchiveFullNameSource extends Source {
|
||||
ArchiveFullNameSource() {
|
||||
exists(PropertyAccess pa | this.asExpr() = pa |
|
||||
pa.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression.ZipArchiveEntry") and
|
||||
pa.getTarget().getName() = "FullName"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An argument to the `ExtractToFile` extension method. */
|
||||
class ExtractToFileArgSink extends Sink {
|
||||
ExtractToFileArgSink() {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget().hasQualifiedName("System.IO.Compression.ZipFileExtensions", "ExtractToFile") and
|
||||
this.asExpr() = mc.getArgumentForName("destinationFileName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A path argument to a `File.Open`, `File.OpenWrite`, or `File.Create` method call. */
|
||||
class FileOpenArgSink extends Sink {
|
||||
FileOpenArgSink() {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget().hasQualifiedName("System.IO.File", "Open") or
|
||||
mc.getTarget().hasQualifiedName("System.IO.File", "OpenWrite") or
|
||||
mc.getTarget().hasQualifiedName("System.IO.File", "Create")
|
||||
|
|
||||
this.asExpr() = mc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A path argument to a call to the `FileStream` constructor. */
|
||||
class FileStreamArgSink extends Sink {
|
||||
FileStreamArgSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileStream")
|
||||
|
|
||||
this.asExpr() = oc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a call to the `FileStream` constructor.
|
||||
*
|
||||
* This constructor can accept a tainted file name and subsequently be used to open a file stream.
|
||||
*/
|
||||
class FileInfoArgSink extends Sink {
|
||||
FileInfoArgSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileInfo")
|
||||
|
|
||||
this.asExpr() = oc.getArgumentForName("fileName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `GetFileName`.
|
||||
*
|
||||
* This is considered a sanitizer because it extracts just the file name, not the full path.
|
||||
*/
|
||||
class GetFileNameSanitizer extends Sanitizer {
|
||||
GetFileNameSanitizer() {
|
||||
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.IO.Path", "GetFileName") |
|
||||
this.asExpr() = mc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `Substring`.
|
||||
*
|
||||
* This is considered a sanitizer because `Substring` may be used to extract a single component
|
||||
* of a path to avoid ZipSlip.
|
||||
*/
|
||||
class SubstringSanitizer extends Sanitizer {
|
||||
SubstringSanitizer() {
|
||||
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.String", "Substring") |
|
||||
this.asExpr() = mc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `String.StartsWith()` that indicates that the tainted path value is being
|
||||
* validated to ensure that it occurs within a permitted output path.
|
||||
*/
|
||||
class StringCheckGuard extends SanitizerGuard, MethodCall {
|
||||
private Expr q;
|
||||
|
||||
StringCheckGuard() {
|
||||
this.getTarget().hasQualifiedName("System.String", "StartsWith") and
|
||||
this.getQualifier() = q and
|
||||
// A StartsWith check against Path.Combine is not sufficient, because the ".." elements have
|
||||
// not yet been resolved.
|
||||
not exists(MethodCall combineCall |
|
||||
combineCall.getTarget().hasQualifiedName("System.IO.Path", "Combine") and
|
||||
DataFlow::localExprFlow(combineCall, q)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate checks(Expr e, AbstractValue v) {
|
||||
e = q and
|
||||
v.(AbstractValues::BooleanValue).getValue() = true
|
||||
}
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/** An access to the `FullName` property of a `ZipArchiveEntry`. */
|
||||
class ArchiveFullNameSource extends Source {
|
||||
ArchiveFullNameSource() {
|
||||
exists(PropertyAccess pa | this.asExpr() = pa |
|
||||
pa.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression.ZipArchiveEntry") and
|
||||
pa.getTarget().getName() = "FullName"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An argument to the `ExtractToFile` extension method. */
|
||||
class ExtractToFileArgSink extends Sink {
|
||||
ExtractToFileArgSink() {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget().hasQualifiedName("System.IO.Compression.ZipFileExtensions", "ExtractToFile") and
|
||||
this.asExpr() = mc.getArgumentForName("destinationFileName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A path argument to a `File.Open`, `File.OpenWrite`, or `File.Create` method call. */
|
||||
class FileOpenArgSink extends Sink {
|
||||
FileOpenArgSink() {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget().hasQualifiedName("System.IO.File", "Open") or
|
||||
mc.getTarget().hasQualifiedName("System.IO.File", "OpenWrite") or
|
||||
mc.getTarget().hasQualifiedName("System.IO.File", "Create")
|
||||
|
|
||||
this.asExpr() = mc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A path argument to a call to the `FileStream` constructor. */
|
||||
class FileStreamArgSink extends Sink {
|
||||
FileStreamArgSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileStream")
|
||||
|
|
||||
this.asExpr() = oc.getArgumentForName("path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument to a call to the `FileStream` constructor.
|
||||
*
|
||||
* This constructor can accept a tainted file name and subsequently be used to open a file stream.
|
||||
*/
|
||||
class FileInfoArgSink extends Sink {
|
||||
FileInfoArgSink() {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileInfo")
|
||||
|
|
||||
this.asExpr() = oc.getArgumentForName("fileName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `GetFileName`.
|
||||
*
|
||||
* This is considered a sanitizer because it extracts just the file name, not the full path.
|
||||
*/
|
||||
class GetFileNameSanitizer extends Sanitizer {
|
||||
GetFileNameSanitizer() {
|
||||
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.IO.Path", "GetFileName") |
|
||||
this.asExpr() = mc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `Substring`.
|
||||
*
|
||||
* This is considered a sanitizer because `Substring` may be used to extract a single component
|
||||
* of a path to avoid ZipSlip.
|
||||
*/
|
||||
class SubstringSanitizer extends Sanitizer {
|
||||
SubstringSanitizer() {
|
||||
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.String", "Substring") |
|
||||
this.asExpr() = mc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `String.StartsWith()` that indicates that the tainted path value is being
|
||||
* validated to ensure that it occurs within a permitted output path.
|
||||
*/
|
||||
class StringCheckGuard extends SanitizerGuard, MethodCall {
|
||||
private Expr q;
|
||||
|
||||
StringCheckGuard() {
|
||||
this.getTarget().hasQualifiedName("System.String", "StartsWith") and
|
||||
this.getQualifier() = q and
|
||||
// A StartsWith check against Path.Combine is not sufficient, because the ".." elements have
|
||||
// not yet been resolved.
|
||||
not exists(MethodCall combineCall |
|
||||
combineCall.getTarget().hasQualifiedName("System.IO.Path", "Combine") and
|
||||
DataFlow::localExprFlow(combineCall, q)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate checks(Expr e, AbstractValue v) {
|
||||
e = q and
|
||||
v.(AbstractValues::BooleanValue).getValue() = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,261 +3,262 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.commons.TargetFramework
|
||||
|
||||
module InsecureXML {
|
||||
import semmle.code.csharp.commons.TargetFramework
|
||||
/**
|
||||
* Holds if the type `t` is in an assembly that has been compiled against a .NET framework version
|
||||
* before the given version.
|
||||
*/
|
||||
bindingset[version]
|
||||
private predicate isNetFrameworkBefore(Type t, string version) {
|
||||
// For assemblies compiled against framework versions before 4 the TargetFrameworkAttribute
|
||||
// will not be present. In this case, we can revert back to the assembly version, which may not
|
||||
// contain full minor version information.
|
||||
exists(string assemblyVersion |
|
||||
assemblyVersion =
|
||||
t.getALocation().(Assembly).getVersion().regexpCapture("([0-9]+\\.[0-9]+).*", 1)
|
||||
|
|
||||
assemblyVersion.toFloat() < version.toFloat() and
|
||||
// This method is only accurate when we're looking at versions before 4.0.
|
||||
assemblyVersion.toFloat() < 4.0
|
||||
)
|
||||
or
|
||||
// For 4.0 and above the TargetFrameworkAttribute should be present to provide detailed version
|
||||
// information.
|
||||
exists(TargetFrameworkAttribute tfa |
|
||||
tfa.hasElement(t) and
|
||||
tfa.isNetFramework() and
|
||||
tfa.getFrameworkVersion().isEarlierThan(version)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A call which may load an XML document insecurely.
|
||||
*/
|
||||
abstract class InsecureXmlProcessing extends Call {
|
||||
/**
|
||||
* Holds if the type `t` is in an assembly that has been compiled against a .NET framework version
|
||||
* before the given version.
|
||||
* Holds if this call is in fact unsafe, with the reason given.
|
||||
*/
|
||||
bindingset[version]
|
||||
private predicate isNetFrameworkBefore(Type t, string version) {
|
||||
// For assemblies compiled against framework versions before 4 the TargetFrameworkAttribute
|
||||
// will not be present. In this case, we can revert back to the assembly version, which may not
|
||||
// contain full minor version information.
|
||||
exists(string assemblyVersion |
|
||||
assemblyVersion =
|
||||
t.getALocation().(Assembly).getVersion().regexpCapture("([0-9]+\\.[0-9]+).*", 1)
|
||||
|
|
||||
assemblyVersion.toFloat() < version.toFloat() and
|
||||
// This method is only accurate when we're looking at versions before 4.0.
|
||||
assemblyVersion.toFloat() < 4.0
|
||||
)
|
||||
or
|
||||
// For 4.0 and above the TargetFrameworkAttribute should be present to provide detailed version
|
||||
// information.
|
||||
exists(TargetFrameworkAttribute tfa |
|
||||
tfa.hasElement(t) and
|
||||
tfa.isNetFramework() and
|
||||
tfa.getFrameworkVersion().isEarlierThan(version)
|
||||
abstract predicate isUnsafe(string reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression is a secure `XmlResolver`.
|
||||
*/
|
||||
private predicate isSafeXmlResolver(Expr e) {
|
||||
e instanceof NullLiteral or
|
||||
e.getType().(RefType).hasQualifiedName("System.Xml.XmlSecureResolver")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression is a safe DTD processing setting.
|
||||
*/
|
||||
private predicate isSafeDtdSetting(Expr e) {
|
||||
// new DtdProcessing setting
|
||||
exists(string name | e.(FieldAccess).getTarget().(EnumConstant).getName() = name |
|
||||
name = "Prohibit" or name = "Ignore"
|
||||
)
|
||||
or
|
||||
// old ProhibitDtd setting
|
||||
e.(BoolLiteral).getValue() = "true"
|
||||
}
|
||||
|
||||
/**
|
||||
* A simplistic points-to alternative: given an object creation and a property name, get the values that property can be assigned.
|
||||
*
|
||||
* Assumptions:
|
||||
* - we don't reassign the variable that the creation is stored in
|
||||
* - we always access the creation through the same variable it is initially assigned to
|
||||
*
|
||||
* This should cover most typical patterns...
|
||||
*/
|
||||
private Expr getAValueForProp(ObjectCreation create, string prop) {
|
||||
// values set in object init
|
||||
exists(MemberInitializer init |
|
||||
init = create.getInitializer().(ObjectInitializer).getAMemberInitializer() and
|
||||
init.getLValue().(PropertyAccess).getTarget().hasName(prop) and
|
||||
result = init.getRValue()
|
||||
)
|
||||
or
|
||||
// values set on var that create is assigned to
|
||||
exists(Assignment propAssign |
|
||||
DataFlow::localExprFlow(create, propAssign.getLValue().(PropertyAccess).getQualifier()) and
|
||||
propAssign.getLValue().(PropertyAccess).getTarget().hasName(prop) and
|
||||
result = propAssign.getRValue()
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides predicates related to `System.Xml.XmlReaderSettings`. */
|
||||
module XmlSettings {
|
||||
/**
|
||||
* Holds if the given object creation constructs `XmlReaderSettings` with an insecure resolver.
|
||||
*/
|
||||
predicate insecureResolverSettings(ObjectCreation creation, Expr evidence, string reason) {
|
||||
creation.getObjectType().getQualifiedName() = "System.Xml.XmlReaderSettings" and
|
||||
(
|
||||
// one unsafe assignment to XmlResolver
|
||||
exists(Expr xmlResolverVal | xmlResolverVal = getAValueForProp(creation, "XmlResolver") |
|
||||
not isSafeXmlResolver(xmlResolverVal) and evidence = xmlResolverVal
|
||||
) and
|
||||
reason = "insecure resolver set in settings"
|
||||
or
|
||||
// no assignments, and default is insecure before version 4.5
|
||||
isNetFrameworkBefore(creation.getObjectType(), "4.5") and
|
||||
not exists(getAValueForProp(creation, "XmlResolver")) and
|
||||
reason = "default settings resolver is insecure in versions before 4.5" and
|
||||
evidence = creation
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A call which may load an XML document insecurely.
|
||||
* Holds if the given object creation constructs `XmlReaderSettings` with DTD processing enabled.
|
||||
*/
|
||||
abstract class InsecureXmlProcessing extends Call {
|
||||
/**
|
||||
* Holds if this call is in fact unsafe, with the reason given.
|
||||
*/
|
||||
abstract predicate isUnsafe(string reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression is a secure `XmlResolver`.
|
||||
*/
|
||||
private predicate isSafeXmlResolver(Expr e) {
|
||||
e instanceof NullLiteral or
|
||||
e.getType().(RefType).hasQualifiedName("System.Xml.XmlSecureResolver")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression is a safe DTD processing setting.
|
||||
*/
|
||||
private predicate isSafeDtdSetting(Expr e) {
|
||||
// new DtdProcessing setting
|
||||
exists(string name | e.(FieldAccess).getTarget().(EnumConstant).getName() = name |
|
||||
name = "Prohibit" or name = "Ignore"
|
||||
)
|
||||
or
|
||||
// old ProhibitDtd setting
|
||||
e.(BoolLiteral).getValue() = "true"
|
||||
}
|
||||
|
||||
/**
|
||||
* A simplistic points-to alternative: given an object creation and a property name, get the values that property can be assigned.
|
||||
*
|
||||
* Assumptions:
|
||||
* - we don't reassign the variable that the creation is stored in
|
||||
* - we always access the creation through the same variable it is initially assigned to
|
||||
*
|
||||
* This should cover most typical patterns...
|
||||
*/
|
||||
private Expr getAValueForProp(ObjectCreation create, string prop) {
|
||||
// values set in object init
|
||||
exists(MemberInitializer init |
|
||||
init = create.getInitializer().(ObjectInitializer).getAMemberInitializer() and
|
||||
init.getLValue().(PropertyAccess).getTarget().hasName(prop) and
|
||||
result = init.getRValue()
|
||||
)
|
||||
or
|
||||
// values set on var that create is assigned to
|
||||
exists(Assignment propAssign |
|
||||
DataFlow::localExprFlow(create, propAssign.getLValue().(PropertyAccess).getQualifier()) and
|
||||
propAssign.getLValue().(PropertyAccess).getTarget().hasName(prop) and
|
||||
result = propAssign.getRValue()
|
||||
)
|
||||
}
|
||||
|
||||
module XmlSettings {
|
||||
/**
|
||||
* Holds if the given object creation constructs `XmlReaderSettings` with an insecure resolver.
|
||||
*/
|
||||
predicate insecureResolverSettings(ObjectCreation creation, Expr evidence, string reason) {
|
||||
creation.getObjectType().getQualifiedName() = "System.Xml.XmlReaderSettings" and
|
||||
predicate dtdEnabledSettings(ObjectCreation creation, Expr evidence, string reason) {
|
||||
creation.getObjectType().getQualifiedName() = "System.Xml.XmlReaderSettings" and
|
||||
(
|
||||
exists(Expr dtdVal | dtdVal = getAValueForProp(creation, "DtdProcessing") |
|
||||
not isSafeDtdSetting(dtdVal) and evidence = dtdVal
|
||||
) and
|
||||
reason = "DTD processing enabled in settings"
|
||||
or
|
||||
// default is secure in versions >= 4
|
||||
isNetFrameworkBefore(creation.getObjectType(), "4.0") and
|
||||
(
|
||||
// one unsafe assignment to XmlResolver
|
||||
exists(Expr xmlResolverVal | xmlResolverVal = getAValueForProp(creation, "XmlResolver") |
|
||||
not isSafeXmlResolver(xmlResolverVal) and evidence = xmlResolverVal
|
||||
) and
|
||||
reason = "insecure resolver set in settings"
|
||||
or
|
||||
// no assignments, and default is insecure before version 4.5
|
||||
isNetFrameworkBefore(creation.getObjectType(), "4.5") and
|
||||
not exists(getAValueForProp(creation, "XmlResolver")) and
|
||||
reason = "default settings resolver is insecure in versions before 4.5" and
|
||||
evidence = creation
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given object creation constructs `XmlReaderSettings` with DTD processing enabled.
|
||||
*/
|
||||
predicate dtdEnabledSettings(ObjectCreation creation, Expr evidence, string reason) {
|
||||
creation.getObjectType().getQualifiedName() = "System.Xml.XmlReaderSettings" and
|
||||
(
|
||||
exists(Expr dtdVal | dtdVal = getAValueForProp(creation, "DtdProcessing") |
|
||||
exists(Expr dtdVal |
|
||||
// different DTD setting before version 4
|
||||
dtdVal = getAValueForProp(creation, "ProhibitDtd")
|
||||
|
|
||||
not isSafeDtdSetting(dtdVal) and evidence = dtdVal
|
||||
) and
|
||||
reason = "DTD processing enabled in settings"
|
||||
reason = "DTD procesing enabled in settings"
|
||||
or
|
||||
// default is secure in versions >= 4
|
||||
isNetFrameworkBefore(creation.getObjectType(), "4.0") and
|
||||
(
|
||||
exists(Expr dtdVal |
|
||||
// different DTD setting before version 4
|
||||
dtdVal = getAValueForProp(creation, "ProhibitDtd")
|
||||
|
|
||||
not isSafeDtdSetting(dtdVal) and evidence = dtdVal
|
||||
) and
|
||||
reason = "DTD procesing enabled in settings"
|
||||
or
|
||||
not exists(getAValueForProp(creation, "ProhibitDtd")) and
|
||||
reason = "DTD processing is enabled by default in versions before 4.0" and
|
||||
evidence = creation
|
||||
)
|
||||
not exists(getAValueForProp(creation, "ProhibitDtd")) and
|
||||
reason = "DTD processing is enabled by default in versions before 4.0" and
|
||||
evidence = creation
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates related to `System.Xml.XmlReader`. */
|
||||
module XmlReader {
|
||||
private import semmle.code.csharp.dataflow.DataFlow2
|
||||
|
||||
private class InsecureXmlReaderCreate extends InsecureXmlProcessing, MethodCall {
|
||||
InsecureXmlReaderCreate() { this.getTarget().hasQualifiedName("System.Xml.XmlReader.Create") }
|
||||
|
||||
/**
|
||||
* Gets the `XmlReaderSettings` argument to to this call, if any.
|
||||
*/
|
||||
Expr getSettings() {
|
||||
result = this.getAnArgument() and
|
||||
result.getType().(RefType).getABaseType*().hasQualifiedName("System.Xml.XmlReaderSettings")
|
||||
}
|
||||
|
||||
override predicate isUnsafe(string reason) {
|
||||
exists(string dtdReason, string resolverReason |
|
||||
dtdEnabled(dtdReason, _) and
|
||||
insecureResolver(resolverReason, _) and
|
||||
reason = dtdReason + ", " + resolverReason
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module XmlReader {
|
||||
private import semmle.code.csharp.dataflow.DataFlow2
|
||||
|
||||
class InsecureXmlReaderCreate extends InsecureXmlProcessing, MethodCall {
|
||||
InsecureXmlReaderCreate() { this.getTarget().hasQualifiedName("System.Xml.XmlReader.Create") }
|
||||
|
||||
/**
|
||||
* Gets the `XmlReaderSettings` argument to to this call, if any.
|
||||
*/
|
||||
Expr getSettings() {
|
||||
result = this.getAnArgument() and
|
||||
result.getType().(RefType).getABaseType*().hasQualifiedName("System.Xml.XmlReaderSettings")
|
||||
}
|
||||
|
||||
override predicate isUnsafe(string reason) {
|
||||
exists(string dtdReason, string resolverReason |
|
||||
dtdEnabled(dtdReason, _) and
|
||||
insecureResolver(resolverReason, _) and
|
||||
reason = dtdReason + ", " + resolverReason
|
||||
)
|
||||
}
|
||||
|
||||
private predicate dtdEnabled(string reason, Expr evidence) {
|
||||
reason = "DTD processing is enabled by default in versions < 4.0" and
|
||||
evidence = this and
|
||||
not exists(this.getSettings()) and
|
||||
isNetFrameworkBefore(this.(MethodCall).getTarget().getDeclaringType(), "4.0")
|
||||
or
|
||||
// bad settings flow here
|
||||
exists(SettingsDataFlowConfig flow, ObjectCreation settings |
|
||||
flow.hasFlow(DataFlow::exprNode(settings), DataFlow::exprNode(this.getSettings())) and
|
||||
XmlSettings::dtdEnabledSettings(settings, evidence, reason)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate insecureResolver(string reason, Expr evidence) {
|
||||
// bad settings flow here
|
||||
exists(SettingsDataFlowConfig flow, ObjectCreation settings |
|
||||
flow.hasFlow(DataFlow::exprNode(settings), DataFlow::exprNode(this.getSettings())) and
|
||||
XmlSettings::insecureResolverSettings(settings, evidence, reason)
|
||||
)
|
||||
// default is secure
|
||||
}
|
||||
private predicate dtdEnabled(string reason, Expr evidence) {
|
||||
reason = "DTD processing is enabled by default in versions < 4.0" and
|
||||
evidence = this and
|
||||
not exists(this.getSettings()) and
|
||||
isNetFrameworkBefore(this.(MethodCall).getTarget().getDeclaringType(), "4.0")
|
||||
or
|
||||
// bad settings flow here
|
||||
exists(SettingsDataFlowConfig flow, ObjectCreation settings |
|
||||
flow.hasFlow(DataFlow::exprNode(settings), DataFlow::exprNode(this.getSettings())) and
|
||||
XmlSettings::dtdEnabledSettings(settings, evidence, reason)
|
||||
)
|
||||
}
|
||||
|
||||
private class SettingsDataFlowConfig extends DataFlow2::Configuration {
|
||||
SettingsDataFlowConfig() { this = "SettingsDataFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// flow from places where we construct an XmlReaderSettings
|
||||
source
|
||||
.asExpr()
|
||||
.(ObjectCreation)
|
||||
.getType()
|
||||
.(RefType)
|
||||
.getABaseType*()
|
||||
.hasQualifiedName("System.Xml.XmlReaderSettings")
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(InsecureXmlReaderCreate create).getSettings()
|
||||
}
|
||||
private predicate insecureResolver(string reason, Expr evidence) {
|
||||
// bad settings flow here
|
||||
exists(SettingsDataFlowConfig flow, ObjectCreation settings |
|
||||
flow.hasFlow(DataFlow::exprNode(settings), DataFlow::exprNode(this.getSettings())) and
|
||||
XmlSettings::insecureResolverSettings(settings, evidence, reason)
|
||||
)
|
||||
// default is secure
|
||||
}
|
||||
}
|
||||
|
||||
module XmlTextReader {
|
||||
class InsecureXmlTextReader extends InsecureXmlProcessing, ObjectCreation {
|
||||
InsecureXmlTextReader() {
|
||||
this.getObjectType().(ValueOrRefType).hasQualifiedName("System.Xml.XmlTextReader")
|
||||
}
|
||||
private class SettingsDataFlowConfig extends DataFlow2::Configuration {
|
||||
SettingsDataFlowConfig() { this = "SettingsDataFlowConfig" }
|
||||
|
||||
override predicate isUnsafe(string reason) {
|
||||
not exists(Expr xmlResolverVal |
|
||||
isSafeXmlResolver(xmlResolverVal) and
|
||||
xmlResolverVal = getAValueForProp(this, "XmlResolver")
|
||||
) and
|
||||
not exists(Expr dtdVal |
|
||||
isSafeDtdSetting(dtdVal) and
|
||||
dtdVal = getAValueForProp(this, "DtdProcessing")
|
||||
) and
|
||||
// This was made safe by default in 4.5.2, despite what the documentation says
|
||||
isNetFrameworkBefore(this.getObjectType(), "4.5.2") and
|
||||
reason = "DTD processing is enabled by default, and resolver is insecure by default"
|
||||
or
|
||||
exists(Expr xmlResolverVal |
|
||||
not isSafeXmlResolver(xmlResolverVal) and
|
||||
xmlResolverVal = getAValueForProp(this, "XmlResolver")
|
||||
) and
|
||||
exists(Expr dtdVal |
|
||||
not isSafeDtdSetting(dtdVal) and
|
||||
dtdVal = getAValueForProp(this, "DtdProcessing")
|
||||
) and
|
||||
reason = "DTD processing is enabled with an insecure resolver"
|
||||
}
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// flow from places where we construct an XmlReaderSettings
|
||||
source
|
||||
.asExpr()
|
||||
.(ObjectCreation)
|
||||
.getType()
|
||||
.(RefType)
|
||||
.getABaseType*()
|
||||
.hasQualifiedName("System.Xml.XmlReaderSettings")
|
||||
}
|
||||
}
|
||||
|
||||
module XmlDocument {
|
||||
/**
|
||||
* A call to `Load` or `LoadXml` on `XmlDocument`s that doesn't appear to have a safe `XmlResolver` set.
|
||||
*/
|
||||
class InsecureXmlDocument extends InsecureXmlProcessing, MethodCall {
|
||||
InsecureXmlDocument() {
|
||||
this.getTarget().hasQualifiedName("System.Xml.XmlDocument.Load") or
|
||||
this.getTarget().hasQualifiedName("System.Xml.XmlDocument.LoadXml")
|
||||
}
|
||||
|
||||
override predicate isUnsafe(string reason) {
|
||||
exists(ObjectCreation creation | DataFlow::localExprFlow(creation, this.getQualifier()) |
|
||||
not exists(Expr xmlResolverVal |
|
||||
isSafeXmlResolver(xmlResolverVal) and
|
||||
xmlResolverVal = getAValueForProp(creation, "XmlResolver")
|
||||
)
|
||||
) and
|
||||
isNetFrameworkBefore(this.getQualifier().getType(), "4.6") and
|
||||
reason = "resolver is insecure by default in versions before 4.6"
|
||||
}
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(InsecureXmlReaderCreate create).getSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates related to `System.Xml.XmlTextReader`. */
|
||||
module XmlTextReader {
|
||||
private class InsecureXmlTextReader extends InsecureXmlProcessing, ObjectCreation {
|
||||
InsecureXmlTextReader() {
|
||||
this.getObjectType().(ValueOrRefType).hasQualifiedName("System.Xml.XmlTextReader")
|
||||
}
|
||||
|
||||
override predicate isUnsafe(string reason) {
|
||||
not exists(Expr xmlResolverVal |
|
||||
isSafeXmlResolver(xmlResolverVal) and
|
||||
xmlResolverVal = getAValueForProp(this, "XmlResolver")
|
||||
) and
|
||||
not exists(Expr dtdVal |
|
||||
isSafeDtdSetting(dtdVal) and
|
||||
dtdVal = getAValueForProp(this, "DtdProcessing")
|
||||
) and
|
||||
// This was made safe by default in 4.5.2, despite what the documentation says
|
||||
isNetFrameworkBefore(this.getObjectType(), "4.5.2") and
|
||||
reason = "DTD processing is enabled by default, and resolver is insecure by default"
|
||||
or
|
||||
exists(Expr xmlResolverVal |
|
||||
not isSafeXmlResolver(xmlResolverVal) and
|
||||
xmlResolverVal = getAValueForProp(this, "XmlResolver")
|
||||
) and
|
||||
exists(Expr dtdVal |
|
||||
not isSafeDtdSetting(dtdVal) and
|
||||
dtdVal = getAValueForProp(this, "DtdProcessing")
|
||||
) and
|
||||
reason = "DTD processing is enabled with an insecure resolver"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates related to `System.Xml.XmlDocument`. */
|
||||
module XmlDocument {
|
||||
/**
|
||||
* A call to `Load` or `LoadXml` on `XmlDocument`s that doesn't appear to have a safe `XmlResolver` set.
|
||||
*/
|
||||
class InsecureXmlDocument extends InsecureXmlProcessing, MethodCall {
|
||||
InsecureXmlDocument() {
|
||||
this.getTarget().hasQualifiedName("System.Xml.XmlDocument.Load") or
|
||||
this.getTarget().hasQualifiedName("System.Xml.XmlDocument.LoadXml")
|
||||
}
|
||||
|
||||
override predicate isUnsafe(string reason) {
|
||||
exists(ObjectCreation creation | DataFlow::localExprFlow(creation, this.getQualifier()) |
|
||||
not exists(Expr xmlResolverVal |
|
||||
isSafeXmlResolver(xmlResolverVal) and
|
||||
xmlResolverVal = getAValueForProp(creation, "XmlResolver")
|
||||
)
|
||||
) and
|
||||
isNetFrameworkBefore(this.getQualifier().getType(), "4.6") and
|
||||
reason = "resolver is insecure by default in versions before 4.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.security.xml.InsecureXMLQuery::InsecureXML
|
||||
import semmle.code.csharp.security.xml.InsecureXMLQuery
|
||||
|
||||
from ObjectCreation creation, Expr evidence, string reason
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user