mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
607 lines
20 KiB
Plaintext
607 lines
20 KiB
Plaintext
/**
|
|
* Provides abstract classes representing generic concepts such as file system
|
|
* access or system command execution, for which individual framework libraries
|
|
* provide concrete subclasses.
|
|
*/
|
|
|
|
import go
|
|
import semmle.go.dataflow.FunctionInputsAndOutputs
|
|
import semmle.go.concepts.HTTP
|
|
import semmle.go.concepts.GeneratedFile
|
|
private import codeql.concepts.ConceptsShared
|
|
private import semmle.go.dataflow.internal.DataFlowImplSpecific
|
|
|
|
private module ConceptsShared = ConceptsMake<Location, GoDataFlow>;
|
|
|
|
/**
|
|
* A data-flow node that executes an operating system command,
|
|
* for instance by spawning a new process.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `SystemCommandExecution::Range` instead.
|
|
*/
|
|
class SystemCommandExecution extends DataFlow::Node instanceof SystemCommandExecution::Range {
|
|
/** Gets the argument that specifies the command to be executed. */
|
|
DataFlow::Node getCommandName() { result = super.getCommandName() }
|
|
|
|
/** Holds if this node is sanitized whenever it follows `--` in an argument list. */
|
|
predicate doubleDashIsSanitizing() { super.doubleDashIsSanitizing() }
|
|
}
|
|
|
|
/** Provides a class for modeling new system-command execution APIs. */
|
|
module SystemCommandExecution {
|
|
/**
|
|
* A data-flow node that executes an operating system command,
|
|
* for instance by spawning a new process.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `SystemCommandExecution` instead.
|
|
*/
|
|
abstract class Range extends DataFlow::Node {
|
|
/** Gets the argument that specifies the command to be executed. */
|
|
abstract DataFlow::Node getCommandName();
|
|
|
|
/** Holds if this node is sanitized whenever it follows `--` in an argument list. */
|
|
predicate doubleDashIsSanitizing() { none() }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An instantiation of a template; that is, a call which fills out a template with data.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `TemplateInstantiation::Range` instead.
|
|
*/
|
|
class TemplateInstantiation extends DataFlow::Node instanceof TemplateInstantiation::Range {
|
|
/**
|
|
* Gets the argument to this template instantiation that is the template being
|
|
* instantiated.
|
|
*/
|
|
DataFlow::Node getTemplateArgument() { result = super.getTemplateArgument() }
|
|
|
|
/**
|
|
* Gets an argument to this template instantiation that is data being inserted
|
|
* into the template.
|
|
*/
|
|
DataFlow::Node getADataArgument() { result = super.getADataArgument() }
|
|
}
|
|
|
|
/** Provides a class for modeling new template-instantiation APIs. */
|
|
module TemplateInstantiation {
|
|
/**
|
|
* An instantiation of a template; that is, a call which fills out a template with data.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `TemplateInstantiation` instead.
|
|
*/
|
|
abstract class Range extends DataFlow::Node {
|
|
/**
|
|
* Gets the argument to this template instantiation that is the template being
|
|
* instantiated.
|
|
*/
|
|
abstract DataFlow::Node getTemplateArgument();
|
|
|
|
/**
|
|
* Gets an argument to this template instantiation that is data being inserted
|
|
* into the template.
|
|
*/
|
|
abstract DataFlow::Node getADataArgument();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A data-flow node that performs a file system access, including reading and writing data,
|
|
* creating and deleting files and folders, checking and updating permissions, and so on.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `FileSystemAccess::Range` instead.
|
|
*/
|
|
class FileSystemAccess extends DataFlow::Node instanceof FileSystemAccess::Range {
|
|
/** Gets an argument to this file system access that is interpreted as a path. */
|
|
DataFlow::Node getAPathArgument() { result = super.getAPathArgument() }
|
|
}
|
|
|
|
/** Provides a class for modeling new file-system access APIs. */
|
|
module FileSystemAccess {
|
|
/**
|
|
* A data-flow node that performs a file system access, including reading and writing data,
|
|
* creating and deleting files and folders, checking and updating permissions, and so on.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `FileSystemAccess` instead.
|
|
*/
|
|
abstract class Range extends DataFlow::Node {
|
|
/** Gets an argument to this file system access that is interpreted as a path. */
|
|
abstract DataFlow::Node getAPathArgument();
|
|
}
|
|
}
|
|
|
|
private class DefaultFileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
|
|
DataFlow::ArgumentNode pathArgument;
|
|
|
|
DefaultFileSystemAccess() {
|
|
sinkNode(pathArgument, "path-injection") and
|
|
this = pathArgument.getCall()
|
|
}
|
|
|
|
override DataFlow::Node getAPathArgument() {
|
|
result = pathArgument.getACorrespondingSyntacticArgument()
|
|
}
|
|
}
|
|
|
|
/** A function that escapes meta-characters to prevent injection attacks. */
|
|
class EscapeFunction extends Function instanceof EscapeFunction::Range {
|
|
/**
|
|
* Gets the context that this function escapes for.
|
|
*
|
|
* Currently, this can be "js", "html", or "url".
|
|
*/
|
|
string kind() { result = super.kind() }
|
|
}
|
|
|
|
/** Provides a class for modeling new escape-function APIs. */
|
|
module EscapeFunction {
|
|
/**
|
|
* A function that escapes meta-characters to prevent injection attacks.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `EscapeFunction' instead.
|
|
*/
|
|
abstract class Range extends Function {
|
|
/**
|
|
* Gets the context that this function escapes for.
|
|
*
|
|
* Currently, this can be `js', `html', or `url'.
|
|
*/
|
|
abstract string kind();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function that escapes a string so it can be safely included in a
|
|
* JavaScript string literal.
|
|
*/
|
|
class JsEscapeFunction extends EscapeFunction {
|
|
JsEscapeFunction() { super.kind() = "js" }
|
|
}
|
|
|
|
/**
|
|
* A function that escapes a string so it can be safely included in an
|
|
* the body of an HTML element, for example, replacing `{}` in
|
|
* `<p>{}</p>`.
|
|
*/
|
|
class HtmlEscapeFunction extends EscapeFunction {
|
|
HtmlEscapeFunction() { super.kind() = "html" }
|
|
}
|
|
|
|
/**
|
|
* A function that escapes a string so it can be safely included as part
|
|
* of a URL.
|
|
*/
|
|
class UrlEscapeFunction extends EscapeFunction {
|
|
UrlEscapeFunction() { super.kind() = "url" }
|
|
}
|
|
|
|
/**
|
|
* A node whose value is interpreted as a part of a regular expression.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `RegexpPattern::Range` instead.
|
|
*/
|
|
class RegexpPattern extends DataFlow::Node instanceof RegexpPattern::Range {
|
|
/**
|
|
* Gets the node where this pattern is parsed as a part of a regular
|
|
* expression.
|
|
*/
|
|
DataFlow::Node getAParse() { result = super.getAParse() }
|
|
|
|
/**
|
|
* Gets this regexp pattern as a string.
|
|
*/
|
|
string getPattern() { result = super.getPattern() }
|
|
|
|
/**
|
|
* Gets a use of this pattern, either as itself in an argument to a function or as a compiled
|
|
* regexp object.
|
|
*/
|
|
DataFlow::Node getAUse() { result = super.getAUse() }
|
|
}
|
|
|
|
/** Provides a class for modeling new regular-expression APIs. */
|
|
module RegexpPattern {
|
|
/**
|
|
* A node whose value is interpreted as a part of a regular expression.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `RegexpPattern' instead.
|
|
*/
|
|
abstract class Range extends DataFlow::Node {
|
|
/**
|
|
* Gets a node where the pattern of this node is parsed as a part of
|
|
* a regular expression.
|
|
*/
|
|
abstract DataFlow::Node getAParse();
|
|
|
|
/**
|
|
* Gets this regexp pattern as a string.
|
|
*/
|
|
abstract string getPattern();
|
|
|
|
/**
|
|
* Gets a use of this pattern, either as itself in an argument to a function or as a compiled
|
|
* regexp object.
|
|
*/
|
|
abstract DataFlow::Node getAUse();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function that matches a regexp with a string or byte slice.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `RegexpMatchFunction::Range` instead.
|
|
*/
|
|
class RegexpMatchFunction extends Function instanceof RegexpMatchFunction::Range {
|
|
/**
|
|
* Gets the function input that is the regexp being matched.
|
|
*/
|
|
FunctionInput getRegexpArg() { result = super.getRegexpArg() }
|
|
|
|
/**
|
|
* Gets the regexp pattern that is used in the call to this function `call`.
|
|
*/
|
|
RegexpPattern getRegexp(DataFlow::CallNode call) {
|
|
result.getAUse() = this.getRegexpArg().getNode(call)
|
|
}
|
|
|
|
/**
|
|
* Gets the function input that is the string being matched against.
|
|
*/
|
|
FunctionInput getValue() { result = super.getValue() }
|
|
|
|
/**
|
|
* Gets the function output that is the Boolean result of the match function.
|
|
*/
|
|
FunctionOutput getResult() { result = super.getResult() }
|
|
}
|
|
|
|
/** Provides a class for modeling new regular-expression matcher APIs. */
|
|
module RegexpMatchFunction {
|
|
/**
|
|
* A function that matches a regexp with a string or byte slice.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `RegexpPattern' instead.
|
|
*/
|
|
abstract class Range extends Function {
|
|
/**
|
|
* Gets the function input that is the regexp being matched.
|
|
*/
|
|
abstract FunctionInput getRegexpArg();
|
|
|
|
/**
|
|
* Gets the function input that is the string being matched against.
|
|
*/
|
|
abstract FunctionInput getValue();
|
|
|
|
/**
|
|
* Gets the Boolean result of the match function.
|
|
*/
|
|
abstract FunctionOutput getResult();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function that uses a regexp to replace parts of a string or byte slice.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `RegexpReplaceFunction::Range` instead.
|
|
*/
|
|
class RegexpReplaceFunction extends Function instanceof RegexpReplaceFunction::Range {
|
|
/**
|
|
* Gets the function input that is the regexp that matches text to replace.
|
|
*/
|
|
FunctionInput getRegexpArg() { result = super.getRegexpArg() }
|
|
|
|
/**
|
|
* Gets the regexp pattern that is used to match patterns to replace in the call to this function
|
|
* `call`.
|
|
*/
|
|
RegexpPattern getRegexp(DataFlow::CallNode call) {
|
|
result.getAUse() = call.(DataFlow::MethodCallNode).getReceiver()
|
|
}
|
|
|
|
/**
|
|
* Gets the function input corresponding to the source value, that is, the value that is having
|
|
* its contents replaced.
|
|
*/
|
|
FunctionInput getSource() { result = super.getSource() }
|
|
|
|
/**
|
|
* Gets the function output corresponding to the result, that is, the value after replacement has
|
|
* occurred.
|
|
*/
|
|
FunctionOutput getResult() { result = super.getResult() }
|
|
}
|
|
|
|
/** Provides a class for modeling new regular-expression replacer APIs. */
|
|
module RegexpReplaceFunction {
|
|
/**
|
|
* A function that uses a regexp to replace parts of a string or byte slice.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `RegexpReplaceFunction' instead.
|
|
*/
|
|
abstract class Range extends Function {
|
|
/**
|
|
* Gets the function input that is the regexp that matches text to replace.
|
|
*/
|
|
abstract FunctionInput getRegexpArg();
|
|
|
|
/**
|
|
* Gets the function input corresponding to the source value, that is, the value that is having
|
|
* its contents replaced.
|
|
*/
|
|
abstract FunctionInput getSource();
|
|
|
|
/**
|
|
* Gets the function output corresponding to the result, that is, the value after replacement
|
|
* has occurred.
|
|
*/
|
|
abstract FunctionOutput getResult();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call to a logging mechanism.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `LoggerCall::Range` instead.
|
|
*/
|
|
class LoggerCall extends DataFlow::Node instanceof LoggerCall::Range {
|
|
/** Gets a node that is a part of the logged message. */
|
|
DataFlow::Node getAMessageComponent() { result = super.getAMessageComponent() }
|
|
|
|
/**
|
|
* Gets a node whose value is a part of the logged message.
|
|
*
|
|
* Components corresponding to the format specifier "%T" are excluded as
|
|
* their type is logged rather than their value.
|
|
*/
|
|
DataFlow::Node getAValueFormattedMessageComponent() {
|
|
result = this.getAMessageComponent() and
|
|
not exists(string formatSpecifier |
|
|
result = this.(StringOps::Formatting::StringFormatCall).getOperand(_, formatSpecifier) and
|
|
// We already know that `formatSpecifier` starts with `%`, so we check
|
|
// that it ends with `T` to confirm that it is `%T` or possibly some
|
|
// variation on it.
|
|
formatSpecifier.matches("%T")
|
|
)
|
|
}
|
|
}
|
|
|
|
/** Provides a class for modeling new logging APIs. */
|
|
module LoggerCall {
|
|
/**
|
|
* A call to a logging mechanism.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `LoggerCall` instead.
|
|
*/
|
|
abstract class Range extends DataFlow::Node {
|
|
/** Gets a node that is a part of the logged message. */
|
|
abstract DataFlow::Node getAMessageComponent();
|
|
}
|
|
}
|
|
|
|
private class DefaultLoggerCall extends LoggerCall::Range, DataFlow::CallNode {
|
|
DataFlow::ArgumentNode messageComponent;
|
|
|
|
DefaultLoggerCall() {
|
|
sinkNode(messageComponent, "log-injection") and
|
|
this = messageComponent.getCall()
|
|
}
|
|
|
|
override DataFlow::Node getAMessageComponent() {
|
|
not messageComponent instanceof DataFlow::ImplicitVarargsSlice and
|
|
result = messageComponent
|
|
or
|
|
messageComponent instanceof DataFlow::ImplicitVarargsSlice and
|
|
result = this.getAnImplicitVarargsArgument()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call to an interface that looks like a logger. It is common to use a
|
|
* locally-defined interface for logging to make it easy to changing logging
|
|
* library.
|
|
*/
|
|
private class HeuristicLoggerCall extends LoggerCall::Range, DataFlow::CallNode {
|
|
HeuristicLoggerCall() {
|
|
exists(Method m, string tp, string logFunctionPrefix, string name |
|
|
m = this.getTarget() and
|
|
m.hasQualifiedName(_, tp, name) and
|
|
m.getReceiverBaseType().getUnderlyingType() instanceof InterfaceType
|
|
|
|
|
tp.regexpMatch(".*[lL]ogger") and
|
|
logFunctionPrefix =
|
|
[
|
|
"Debug", "Error", "Fatal", "Info", "Log", "Output", "Panic", "Print", "Trace", "Warn",
|
|
"With"
|
|
] and
|
|
name.matches(logFunctionPrefix + "%")
|
|
)
|
|
}
|
|
|
|
override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
|
|
}
|
|
|
|
/**
|
|
* A function that encodes data into a binary or textual format.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `MarshalingFunction::Range` instead.
|
|
*/
|
|
class MarshalingFunction extends Function instanceof MarshalingFunction::Range {
|
|
/** Gets an input that is encoded by this function. */
|
|
FunctionInput getAnInput() { result = super.getAnInput() }
|
|
|
|
/** Gets the output that contains the encoded data produced by this function. */
|
|
FunctionOutput getOutput() { result = super.getOutput() }
|
|
|
|
/** Gets an identifier for the format this function encodes into, such as "JSON". */
|
|
string getFormat() { result = super.getFormat() }
|
|
}
|
|
|
|
/** Provides a class for modeling new marshaling APIs. */
|
|
module MarshalingFunction {
|
|
/**
|
|
* A function that encodes data into a binary or textual format.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `MarshalingFunction` instead.
|
|
*/
|
|
abstract class Range extends Function {
|
|
/** Gets an input that is encoded by this function. */
|
|
abstract FunctionInput getAnInput();
|
|
|
|
/** Gets the output that contains the encoded data produced by this function. */
|
|
abstract FunctionOutput getOutput();
|
|
|
|
/** Gets an identifier for the format this function encodes into, such as "JSON". */
|
|
abstract string getFormat();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function that decodes data from a binary or textual format.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `UnmarshalingFunction::Range` instead.
|
|
*/
|
|
class UnmarshalingFunction extends Function instanceof UnmarshalingFunction::Range {
|
|
/** Gets an input that is decoded by this function. */
|
|
FunctionInput getAnInput() { result = super.getAnInput() }
|
|
|
|
/** Gets the output that contains the decoded data produced by this function. */
|
|
FunctionOutput getOutput() { result = super.getOutput() }
|
|
|
|
/** Gets an identifier for the format this function decodes from, such as "JSON". */
|
|
string getFormat() { result = super.getFormat() }
|
|
}
|
|
|
|
/** Provides a class for modeling new unmarshaling APIs. */
|
|
module UnmarshalingFunction {
|
|
/**
|
|
* A function that decodes data from a binary or textual format.
|
|
*
|
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
|
* extend `UnmarshalingFunction` instead.
|
|
*/
|
|
abstract class Range extends Function {
|
|
/** Gets an input that is decoded by this function. */
|
|
abstract FunctionInput getAnInput();
|
|
|
|
/** Gets the output that contains the decoded data produced by this function. */
|
|
abstract FunctionOutput getOutput();
|
|
|
|
/** Gets an identifier for the format this function decodes from, such as "JSON". */
|
|
abstract string getFormat();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides models for cryptographic things.
|
|
*/
|
|
module Cryptography {
|
|
private import ConceptsShared::Cryptography as SC
|
|
|
|
/**
|
|
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
|
* encryption, decryption, signature-validation.
|
|
*
|
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
|
* extend `CryptographicOperation::Range` instead.
|
|
*/
|
|
class CryptographicOperation extends SC::CryptographicOperation { }
|
|
|
|
class EncryptionAlgorithm = SC::EncryptionAlgorithm;
|
|
|
|
class HashingAlgorithm = SC::HashingAlgorithm;
|
|
|
|
class PasswordHashingAlgorithm = SC::PasswordHashingAlgorithm;
|
|
|
|
module CryptographicOperation = SC::CryptographicOperation;
|
|
|
|
class BlockMode = SC::BlockMode;
|
|
|
|
class CryptographicAlgorithm = SC::CryptographicAlgorithm;
|
|
|
|
/** A data flow node that initializes a hash algorithm. */
|
|
abstract class HashAlgorithmInit extends DataFlow::Node {
|
|
/** Gets the hash algorithm being initialized. */
|
|
abstract HashingAlgorithm getAlgorithm();
|
|
}
|
|
|
|
/** A data flow node that is an application of a hash algorithm. */
|
|
abstract class HashOperation extends CryptographicOperation::Range {
|
|
override BlockMode getBlockMode() { none() }
|
|
}
|
|
|
|
/** A data flow node that initializes an encryption algorithm. */
|
|
abstract class EncryptionAlgorithmInit extends DataFlow::Node {
|
|
/** Gets the encryption algorithm being initialized. */
|
|
abstract EncryptionAlgorithm getAlgorithm();
|
|
}
|
|
|
|
/**
|
|
* A data flow node that initializes a block cipher mode of operation, and
|
|
* may also propagate taint for encryption algorithms.
|
|
*/
|
|
abstract class BlockModeInit extends DataFlow::CallNode {
|
|
/** Gets the block cipher mode of operation being initialized. */
|
|
abstract BlockMode getMode();
|
|
|
|
/** Gets a step propagating the encryption algorithm through this call. */
|
|
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
|
}
|
|
|
|
/**
|
|
* A data flow node that is an application of an encryption algorithm, where
|
|
* the encryption algorithm and the block cipher mode of operation (if there
|
|
* is one) have been initialized separately.
|
|
*/
|
|
abstract class EncryptionOperation extends CryptographicOperation::Range {
|
|
DataFlow::Node encryptionFlowTarget;
|
|
DataFlow::Node inputNode;
|
|
|
|
override DataFlow::Node getInitialization() {
|
|
EncryptionFlow::flow(result, encryptionFlowTarget)
|
|
}
|
|
|
|
override EncryptionAlgorithm getAlgorithm() {
|
|
result = this.getInitialization().(EncryptionAlgorithmInit).getAlgorithm()
|
|
}
|
|
|
|
override DataFlow::Node getAnInput() { result = inputNode }
|
|
|
|
override BlockMode getBlockMode() {
|
|
result = this.getInitialization().(BlockModeInit).getMode()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An `EncryptionOperation` which is a method call where the encryption
|
|
* algorithm and block cipher mode of operation (if there is one) flow to the
|
|
* receiver and the input is an argument.
|
|
*/
|
|
abstract class EncryptionMethodCall extends EncryptionOperation instanceof DataFlow::CallNode {
|
|
int inputArg;
|
|
|
|
EncryptionMethodCall() {
|
|
encryptionFlowTarget = super.getReceiver() and
|
|
inputNode = super.getArgument(inputArg)
|
|
}
|
|
}
|
|
}
|