mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Merge branch 'main' into p--oj-ox-unsafe-deser
This commit is contained in:
@@ -1,3 +1,34 @@
|
||||
## 0.8.7
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new experimental query, `rb/jwt-empty-secret-or-algorithm`, to detect when application uses an empty secret or weak algorithm.
|
||||
* Added a new experimental query, `rb/jwt-missing-verification`, to detect when the application does not verify a JWT payload.
|
||||
|
||||
## 0.8.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
## 0.8.1
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new experimental query, `rb/jwt-empty-secret-or-algorithm`, to detect when application uses an empty secret or weak algorithm.
|
||||
* Added a new experimental query, `rb/jwt-missing-verification`, to detect when the application does not verify a JWT payload.
|
||||
* Added a new experimental query, `rb/jwt-missing-verification`, to detect when the application does not verify a JWT payload.
|
||||
3
ruby/ql/src/change-notes/released/0.8.2.md
Normal file
3
ruby/ql/src/change-notes/released/0.8.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.2
|
||||
|
||||
No user-facing changes.
|
||||
3
ruby/ql/src/change-notes/released/0.8.3.md
Normal file
3
ruby/ql/src/change-notes/released/0.8.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.3
|
||||
|
||||
No user-facing changes.
|
||||
3
ruby/ql/src/change-notes/released/0.8.4.md
Normal file
3
ruby/ql/src/change-notes/released/0.8.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.4
|
||||
|
||||
No user-facing changes.
|
||||
3
ruby/ql/src/change-notes/released/0.8.5.md
Normal file
3
ruby/ql/src/change-notes/released/0.8.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.5
|
||||
|
||||
No user-facing changes.
|
||||
3
ruby/ql/src/change-notes/released/0.8.6.md
Normal file
3
ruby/ql/src/change-notes/released/0.8.6.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.6
|
||||
|
||||
No user-facing changes.
|
||||
3
ruby/ql/src/change-notes/released/0.8.7.md
Normal file
3
ruby/ql/src/change-notes/released/0.8.7.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.7
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.8.0
|
||||
lastReleaseVersion: 0.8.7
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/ruby-queries
|
||||
version: 0.8.1-dev
|
||||
version: 0.8.8-dev
|
||||
groups:
|
||||
- ruby
|
||||
- queries
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @name Successfully extracted files
|
||||
* @description Lists all files in the source code directory that were extracted
|
||||
* without encountering an error in the file.
|
||||
* @name Extracted files
|
||||
* @description Lists all files in the source code directory that were extracted.
|
||||
* @kind diagnostic
|
||||
* @id rb/diagnostics/successfully-extracted-files
|
||||
* @tags successfully-extracted-files
|
||||
@@ -11,7 +10,5 @@ import codeql.ruby.AST
|
||||
import codeql.ruby.Diagnostics
|
||||
|
||||
from File f
|
||||
where
|
||||
not exists(ExtractionError e | e.getLocation().getFile() = f) and
|
||||
exists(f.getRelativePath())
|
||||
where exists(f.getRelativePath())
|
||||
select f, ""
|
||||
|
||||
43
ruby/ql/src/queries/modeling/GenerateModel.ql
Normal file
43
ruby/ql/src/queries/modeling/GenerateModel.ql
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @name Generate flow models
|
||||
* @description Queries to generate source, sink, summary and type models.
|
||||
* @kind table
|
||||
* @id rb/utils/modeleditor/generate-model
|
||||
* @tags modeleditor generate-model framework-mode
|
||||
*/
|
||||
|
||||
private import internal.Types
|
||||
private import internal.Summaries
|
||||
|
||||
/**
|
||||
* Holds if `(type2, path)` should be seen as an instance of `type1`.
|
||||
*/
|
||||
query predicate typeModel = Types::typeModel/3;
|
||||
|
||||
/**
|
||||
* Holds if the value at `(type, path)` should be seen as a flow
|
||||
* source of the given `kind`.
|
||||
*
|
||||
* The kind `remote` represents a general remote flow source.
|
||||
*/
|
||||
query predicate sourceModel(string type, string path, string kind) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the value at `(type, path)` should be seen as a sink
|
||||
* of the given `kind`.
|
||||
*/
|
||||
query predicate sinkModel(string type, string path, string kind) { none() }
|
||||
|
||||
/**
|
||||
* Holds if calls to `(type, path)`, the value referred to by `input`
|
||||
* can flow to the value referred to by `output`.
|
||||
*
|
||||
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
|
||||
* respectively.
|
||||
*/
|
||||
query predicate summaryModel = Summaries::summaryModel/5;
|
||||
|
||||
/**
|
||||
* Holds if `path` can be substituted for a token `TypeVar[name]`.
|
||||
*/
|
||||
query predicate typeVariableModel(string name, string path) { none() }
|
||||
71
ruby/ql/src/queries/modeling/internal/Summaries.qll
Normal file
71
ruby/ql/src/queries/modeling/internal/Summaries.qll
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Contains predicates for generating `summaryModel`s to summarize flow through methods.
|
||||
*/
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.TaintTracking
|
||||
private import Util as Util
|
||||
|
||||
/**
|
||||
* Contains predicates for generating `summaryModel`s to summarize flow through methods.
|
||||
*/
|
||||
module Summaries {
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(DataFlow::MethodNode methodNode | methodNode.isPublic() |
|
||||
Util::getAnyParameter(methodNode) = source
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(DataFlow::MethodNode m).getAReturnNode() }
|
||||
|
||||
DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
private module ValueFlow {
|
||||
import DataFlow::Global<Config>
|
||||
|
||||
predicate summaryModel(string type, string path, string input, string output) {
|
||||
exists(DataFlow::MethodNode methodNode, DataFlow::ParameterNode paramNode |
|
||||
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
|
||||
flow(paramNode, methodNode.getAReturnNode())
|
||||
|
|
||||
Util::pathToMethod(methodNode, type, path) and
|
||||
input = Util::getArgumentPath(paramNode) and
|
||||
output = "ReturnValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module TaintFlow {
|
||||
import TaintTracking::Global<Config>
|
||||
|
||||
predicate summaryModel(string type, string path, string input, string output) {
|
||||
not ValueFlow::summaryModel(type, path, input, output) and
|
||||
exists(DataFlow::MethodNode methodNode, DataFlow::ParameterNode paramNode |
|
||||
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
|
||||
flow(paramNode, methodNode.getAReturnNode())
|
||||
|
|
||||
Util::pathToMethod(methodNode, type, path) and
|
||||
input = Util::getArgumentPath(paramNode) and
|
||||
output = "ReturnValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if in calls to `(type, path)`, the value referred to by `input`
|
||||
* can flow to the value referred to by `output`.
|
||||
*
|
||||
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
|
||||
* respectively.
|
||||
*/
|
||||
predicate summaryModel(string type, string path, string input, string output, string kind) {
|
||||
ValueFlow::summaryModel(type, path, input, output) and kind = "value"
|
||||
or
|
||||
TaintFlow::summaryModel(type, path, input, output) and kind = "taint"
|
||||
}
|
||||
}
|
||||
56
ruby/ql/src/queries/modeling/internal/Types.qll
Normal file
56
ruby/ql/src/queries/modeling/internal/Types.qll
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Contains predicates for generating `typeModel`s that contain typing
|
||||
* information for API nodes.
|
||||
*/
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import Util as Util
|
||||
private import codeql.ruby.ast.Module
|
||||
private import codeql.ruby.ast.internal.Module
|
||||
|
||||
/**
|
||||
* Contains predicates for generating `typeModel`s that contain typing
|
||||
* information for API nodes.
|
||||
*/
|
||||
module Types {
|
||||
/**
|
||||
* Holds if `node` should be seen as having the given `type`.
|
||||
*/
|
||||
private predicate valueHasTypeName(DataFlow::LocalSourceNode node, string type) {
|
||||
node.getLocation().getFile() instanceof Util::RelevantFile and
|
||||
exists(DataFlow::ModuleNode mod |
|
||||
(
|
||||
node = mod.getAnImmediateReference().getAMethodCall("new")
|
||||
or
|
||||
node = mod.getAnOwnInstanceSelf()
|
||||
) and
|
||||
type = mod.getQualifiedName()
|
||||
or
|
||||
(
|
||||
node = mod.getAnImmediateReference()
|
||||
or
|
||||
node = mod.getAnOwnModuleSelf()
|
||||
) and
|
||||
type = mod.getQualifiedName() + "!"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(type2, path)` should be seen as an instance of `type1`.
|
||||
*/
|
||||
predicate typeModel(string type1, string type2, string path) {
|
||||
exists(API::Node node |
|
||||
valueHasTypeName(node.getAValueReachingSink(), type1) and
|
||||
Util::pathToNode(node, type2, path, true)
|
||||
)
|
||||
or
|
||||
// class Type2 < Type1
|
||||
// class Type2; include Type1
|
||||
exists(Module m1, Module m2 |
|
||||
m2.getAnImmediateAncestor() = m1 and not m2.isBuiltin() and not m1.isBuiltin()
|
||||
|
|
||||
m1.getQualifiedName() = type1 and m2.getQualifiedName() = type2 and path = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
137
ruby/ql/src/queries/modeling/internal/Util.qll
Normal file
137
ruby/ql/src/queries/modeling/internal/Util.qll
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Contains utility methods and classes to assist with generating data extensions models.
|
||||
*/
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
/**
|
||||
* A file that is relevant in the context of library modeling.
|
||||
*
|
||||
* In practice, this means a file that is not part of test code.
|
||||
*/
|
||||
class RelevantFile extends File {
|
||||
RelevantFile() { not this.getRelativePath().regexpMatch(".*/?test(case)?s?/.*") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access path of an argument corresponding to the given `paramNode`.
|
||||
*/
|
||||
string getArgumentPath(DataFlow::ParameterNode paramNode) {
|
||||
paramNode.getLocation().getFile() instanceof RelevantFile and
|
||||
exists(string paramSpecifier |
|
||||
exists(Ast::Parameter param |
|
||||
param = paramNode.asParameter() and
|
||||
(
|
||||
paramSpecifier = param.getPosition().toString()
|
||||
or
|
||||
paramSpecifier = param.(Ast::KeywordParameter).getName() + ":"
|
||||
or
|
||||
param instanceof Ast::BlockParameter and
|
||||
paramSpecifier = "block"
|
||||
)
|
||||
)
|
||||
or
|
||||
paramNode instanceof DataFlow::SelfParameterNode and paramSpecifier = "self"
|
||||
|
|
||||
result = "Argument[" + paramSpecifier + "]"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(type,path)` evaluates to the given method, when evalauted from a client of the current library.
|
||||
*/
|
||||
predicate pathToMethod(DataFlow::MethodNode method, string type, string path) {
|
||||
method.getLocation().getFile() instanceof RelevantFile and
|
||||
exists(DataFlow::ModuleNode mod, string methodName |
|
||||
method = mod.getOwnInstanceMethod(methodName) and
|
||||
if methodName = "initialize"
|
||||
then (
|
||||
type = mod.getQualifiedName() + "!" and
|
||||
path = "Method[new]"
|
||||
) else (
|
||||
type = mod.getQualifiedName() and
|
||||
path = "Method[" + methodName + "]"
|
||||
)
|
||||
or
|
||||
method = mod.getOwnSingletonMethod(methodName) and
|
||||
type = mod.getQualifiedName() + "!" and
|
||||
path = "Method[" + methodName + "]"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any parameter to `methodNode`. This may be a positional, keyword,
|
||||
* block, or self parameter.
|
||||
*/
|
||||
DataFlow::ParameterNode getAnyParameter(DataFlow::MethodNode methodNode) {
|
||||
result =
|
||||
[
|
||||
methodNode.getParameter(_), methodNode.getKeywordParameter(_), methodNode.getBlockParameter(),
|
||||
methodNode.getSelfParameter()
|
||||
]
|
||||
}
|
||||
|
||||
private predicate pathToNodeBase(API::Node node, string type, string path, boolean isOutput) {
|
||||
exists(DataFlow::MethodNode method, string prevPath | pathToMethod(method, type, prevPath) |
|
||||
isOutput = true and
|
||||
node = method.getAReturnNode().backtrack() and
|
||||
path = prevPath + ".ReturnValue" and
|
||||
not method.getMethodName() = "initialize" // ignore return value of initialize method
|
||||
or
|
||||
isOutput = false and
|
||||
exists(DataFlow::ParameterNode paramNode |
|
||||
paramNode = getAnyParameter(method) and
|
||||
node = paramNode.track()
|
||||
|
|
||||
path = prevPath + "." + getArgumentPath(paramNode)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate pathToNodeRec(
|
||||
API::Node node, string type, string path, boolean isOutput, int pathLength
|
||||
) {
|
||||
pathLength < 8 and
|
||||
(
|
||||
pathToNodeBase(node, type, path, isOutput) and
|
||||
pathLength = 1
|
||||
or
|
||||
exists(API::Node prevNode, string prevPath, boolean prevIsOutput, int prevPathLength |
|
||||
pathToNodeRec(prevNode, type, prevPath, prevIsOutput, prevPathLength) and
|
||||
pathLength = prevPathLength + 1
|
||||
|
|
||||
node = prevNode.getAnElement() and
|
||||
path = prevPath + ".Element" and
|
||||
isOutput = prevIsOutput
|
||||
or
|
||||
node = prevNode.getReturn() and
|
||||
path = prevPath + ".ReturnValue" and
|
||||
isOutput = prevIsOutput
|
||||
or
|
||||
prevIsOutput = false and
|
||||
isOutput = true and
|
||||
(
|
||||
exists(int n |
|
||||
node = prevNode.getParameter(n) and
|
||||
path = prevPath + ".Parameter[" + n + "]"
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
node = prevNode.getKeywordParameter(name) and
|
||||
path = prevPath + ".Parameter[" + name + ":]"
|
||||
)
|
||||
or
|
||||
node = prevNode.getBlock() and
|
||||
path = prevPath + ".Parameter[block]"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(type,path)` evaluates to a value corresponding to `node`, when evaluated from a client of the current library.
|
||||
*/
|
||||
predicate pathToNode(API::Node node, string type, string path, boolean isOutput) {
|
||||
pathToNodeRec(node, type, path, isOutput, _)
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.</li>
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Time_complexity">Time complexity</a>.</li>
|
||||
<li>James Kirrage, Asiri Rathnayake, Hayo Thielecke:
|
||||
<a href="http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
|
||||
<a href="https://arxiv.org/abs/1301.0849">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -23,4 +23,5 @@ where
|
||||
)
|
||||
or
|
||||
operation.getBlockMode().isWeak() and msgPrefix = "The block mode " + operation.getBlockMode()
|
||||
select operation, msgPrefix + " is broken or weak, and should not be used."
|
||||
select operation, "$@ is broken or weak, and should not be used.", operation.getInitialization(),
|
||||
msgPrefix
|
||||
|
||||
17
ruby/ql/src/utils/modeleditor/ApplicationModeEndpoints.ql
Normal file
17
ruby/ql/src/utils/modeleditor/ApplicationModeEndpoints.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Fetch endpoints for use in the model editor (application mode)
|
||||
* @description A list of 3rd party endpoints (methods and attributes) used in the codebase. Excludes test and generated code.
|
||||
* @kind table
|
||||
* @id rb/utils/modeleditor/application-mode-endpoints
|
||||
* @tags modeleditor endpoints application-mode
|
||||
*/
|
||||
|
||||
import codeql.ruby.AST
|
||||
|
||||
// This query is empty as Application Mode is not yet supported for Ruby.
|
||||
from
|
||||
Call usage, string package, string type, string name, string parameters, boolean supported,
|
||||
string namespace, string version, string supportedType, string classification
|
||||
where none()
|
||||
select usage, package, namespace, type, name, parameters, supported, namespace, version,
|
||||
supportedType, classification
|
||||
16
ruby/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
Normal file
16
ruby/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Fetch endpoints for use in the model editor (framework mode)
|
||||
* @description A list of endpoints accessible (methods and attributes) for consumers of the library. Excludes test and generated code.
|
||||
* @kind table
|
||||
* @id rb/utils/modeleditor/framework-mode-endpoints
|
||||
* @tags modeleditor endpoints framework-mode
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql.ruby.AST
|
||||
import ModelEditor
|
||||
|
||||
from Endpoint endpoint
|
||||
select endpoint, endpoint.getNamespace(), endpoint.getType(), endpoint.getName(),
|
||||
endpoint.getParameters(), endpoint.getSupportedStatus(), endpoint.getFileName(),
|
||||
endpoint.getSupportedType()
|
||||
215
ruby/ql/src/utils/modeleditor/ModelEditor.qll
Normal file
215
ruby/ql/src/utils/modeleditor/ModelEditor.qll
Normal file
@@ -0,0 +1,215 @@
|
||||
/** Provides classes and predicates related to handling APIs for the VS Code extension. */
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate
|
||||
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import codeql.ruby.frameworks.core.Gem
|
||||
private import codeql.ruby.frameworks.data.ModelsAsData
|
||||
private import codeql.ruby.frameworks.data.internal.ApiGraphModelsExtensions
|
||||
private import queries.modeling.internal.Util as Util
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(DataFlow::MethodNode c) {
|
||||
c.getLocation().getFile() instanceof TestFile
|
||||
}
|
||||
|
||||
private predicate gemFileStep(Gem::GemSpec gem, Folder folder, int n) {
|
||||
n = 0 and folder.getAFile() = gem.(File)
|
||||
or
|
||||
exists(Folder parent, int m |
|
||||
gemFileStep(gem, parent, m) and
|
||||
parent.getAFolder() = folder and
|
||||
n = m + 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace of an endpoint in `file`.
|
||||
*/
|
||||
string getNamespace(File file) {
|
||||
exists(Folder folder | folder = file.getParentContainer() |
|
||||
// The nearest gemspec to this endpoint, if one exists
|
||||
result = min(Gem::GemSpec g, int n | gemFileStep(g, folder, n) | g order by n).getName()
|
||||
or
|
||||
not gemFileStep(_, folder, _) and
|
||||
result = ""
|
||||
)
|
||||
}
|
||||
|
||||
abstract class Endpoint instanceof DataFlow::Node {
|
||||
string getNamespace() { result = getNamespace(super.getLocation().getFile()) }
|
||||
|
||||
string getFileName() { result = super.getLocation().getFile().getBaseName() }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
Location getLocation() { result = super.getLocation() }
|
||||
|
||||
abstract string getType();
|
||||
|
||||
abstract string getName();
|
||||
|
||||
abstract string getParameters();
|
||||
|
||||
abstract boolean getSupportedStatus();
|
||||
|
||||
abstract string getSupportedType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable method or accessor from source code.
|
||||
*/
|
||||
class MethodEndpoint extends Endpoint instanceof DataFlow::MethodNode {
|
||||
MethodEndpoint() {
|
||||
this.isPublic() and
|
||||
not isUninteresting(this)
|
||||
}
|
||||
|
||||
DataFlow::MethodNode getNode() { result = this }
|
||||
|
||||
override string getName() { result = super.getMethodName() }
|
||||
|
||||
/**
|
||||
* Gets the unbound type name of this endpoint.
|
||||
*/
|
||||
override string getType() {
|
||||
result =
|
||||
any(DataFlow::ModuleNode m | m.getOwnInstanceMethod(this.getName()) = this).getQualifiedName() or
|
||||
result =
|
||||
any(DataFlow::ModuleNode m | m.getOwnSingletonMethod(this.getName()) = this)
|
||||
.getQualifiedName() + "!"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter types of this endpoint.
|
||||
*/
|
||||
override string getParameters() {
|
||||
// For now, return the names of positional and keyword parameters. We don't always have type information, so we can't return type names.
|
||||
// We don't yet handle splat params or block params.
|
||||
result =
|
||||
"(" +
|
||||
concat(string key, string value |
|
||||
value = any(int i | i.toString() = key | super.asCallable().getParameter(i)).getName()
|
||||
or
|
||||
exists(DataFlow::ParameterNode param |
|
||||
param = super.asCallable().getKeywordParameter(key)
|
||||
|
|
||||
value = key + ":"
|
||||
)
|
||||
|
|
||||
value, "," order by key
|
||||
) + ")"
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() { none() }
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
predicate isSource() { this.getNode() instanceof SourceCallable }
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
predicate isSink() { this.getNode() instanceof SinkCallable }
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
predicate isNeutral() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
|
||||
* recognized source, sink or neutral or it has a flow summary.
|
||||
*/
|
||||
predicate isSupported() {
|
||||
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
|
||||
}
|
||||
|
||||
override boolean getSupportedStatus() {
|
||||
if this.isSupported() then result = true else result = false
|
||||
}
|
||||
|
||||
override string getSupportedType() {
|
||||
this.isSink() and result = "sink"
|
||||
or
|
||||
this.isSource() and result = "source"
|
||||
or
|
||||
this.hasSummary() and result = "summary"
|
||||
or
|
||||
this.isNeutral() and result = "neutral"
|
||||
or
|
||||
not this.isSupported() and result = ""
|
||||
}
|
||||
}
|
||||
|
||||
string methodClassification(Call method) {
|
||||
method.getFile() instanceof TestFile and result = "test"
|
||||
or
|
||||
not method.getFile() instanceof TestFile and
|
||||
result = "source"
|
||||
}
|
||||
|
||||
class TestFile extends File {
|
||||
TestFile() {
|
||||
this.getRelativePath().regexpMatch(".*(test|spec|examples).+") and
|
||||
not this.getAbsolutePath().matches("%/ql/test/%") // allows our test cases to work
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable where there exists a MaD sink model that applies to it.
|
||||
*/
|
||||
class SinkCallable extends DataFlow::MethodNode {
|
||||
SinkCallable() {
|
||||
exists(string type, string path, string method |
|
||||
method = path.regexpCapture("(Method\\[[^\\]]+\\]).*", 1) and
|
||||
Util::pathToMethod(this, type, method) and
|
||||
sinkModel(type, path, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable where there exists a MaD source model that applies to it.
|
||||
*/
|
||||
class SourceCallable extends DataFlow::CallableNode {
|
||||
SourceCallable() {
|
||||
exists(string type, string path, string method |
|
||||
method = path.regexpCapture("(Method\\[[^\\]]+\\]).*", 1) and
|
||||
Util::pathToMethod(this, type, method) and
|
||||
sourceModel(type, path, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module defined in source code
|
||||
*/
|
||||
class ModuleEndpoint extends Endpoint {
|
||||
private DataFlow::ModuleNode moduleNode;
|
||||
|
||||
ModuleEndpoint() {
|
||||
this =
|
||||
min(DataFlow::Node n, Location loc |
|
||||
n.asExpr().getExpr() = moduleNode.getADeclaration() and
|
||||
loc = n.getLocation()
|
||||
|
|
||||
n order by loc.getFile().getAbsolutePath(), loc.getStartLine(), loc.getStartColumn()
|
||||
) and
|
||||
not moduleNode.(Module).isBuiltin() and
|
||||
not moduleNode.getLocation().getFile() instanceof TestFile
|
||||
}
|
||||
|
||||
DataFlow::ModuleNode getNode() { result = moduleNode }
|
||||
|
||||
override string getType() { result = this.getNode().getQualifiedName() }
|
||||
|
||||
override string getName() { result = "" }
|
||||
|
||||
override string getParameters() { result = "" }
|
||||
|
||||
override boolean getSupportedStatus() { result = false }
|
||||
|
||||
override string getSupportedType() { result = "" }
|
||||
}
|
||||
Reference in New Issue
Block a user