/** * 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; /** * 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 ExternalFileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode { DataFlow::ArgumentNode pathArgument; ExternalFileSystemAccess() { 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 * `

{}

`. */ 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 ExternalLoggerCall extends LoggerCall::Range, DataFlow::CallNode { DataFlow::ArgumentNode messageComponent; ExternalLoggerCall() { 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) } } }