C#: Remove Query.qll top-level modules

This commit is contained in:
Tom Hvitved
2021-07-02 12:16:53 +02:00
parent c812d4e4e8
commit 4de4753c67
55 changed files with 2185 additions and 2248 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -16,9 +16,9 @@ import semmle.code.csharp.frameworks.System
import semmle.code.csharp.frameworks.system.Net
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.system.web.UI
import semmle.code.csharp.security.dataflow.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

View File

@@ -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 }
}

View File

@@ -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 }
}

View File

@@ -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")
)
}
}

View File

@@ -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 { }

View File

@@ -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 }
}

View File

@@ -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 }
}

View File

@@ -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.

View File

@@ -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")) }
}

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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()))
)
)
}
}

View File

@@ -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)
)
}
}

View File

@@ -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 { }

View File

@@ -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 { }

View File

@@ -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
)
}
}

View File

@@ -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()) }
}

View File

@@ -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
}
}

View File

@@ -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"
}
}
}

View File

@@ -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