mirror of
https://github.com/github/codeql.git
synced 2026-03-10 09:36:46 +01:00
254 lines
7.3 KiB
Plaintext
254 lines
7.3 KiB
Plaintext
/** 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
|
|
|
|
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 = ""
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this method is a constructor for a module.
|
|
*/
|
|
predicate isConstructor(DataFlow::MethodNode method) {
|
|
method.getMethodName() = "initialize" and
|
|
exists(DataFlow::ModuleNode m | m.getOwnInstanceMethod(method.getMethodName()) = method)
|
|
}
|
|
|
|
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
|
|
this.(DataFlow::MethodNode).getLocation().getFile() instanceof Util::RelevantFile
|
|
}
|
|
|
|
DataFlow::MethodNode getNode() { result = this }
|
|
|
|
override string getName() {
|
|
result = super.getMethodName() and not this.isConstructor()
|
|
or
|
|
// Constructors are modeled as Type!#new rather than Type#initialize
|
|
result = "new" and this.isConstructor()
|
|
}
|
|
|
|
/**
|
|
* Gets the unbound type name of this endpoint.
|
|
*/
|
|
override string getType() {
|
|
result =
|
|
any(DataFlow::ModuleNode m | m.getOwnInstanceMethod(this.getName()) = this).getQualifiedName() and
|
|
not this.isConstructor()
|
|
or
|
|
// Constructors are modeled on `Type!`, not on `Type`
|
|
result =
|
|
any(DataFlow::ModuleNode m | m.getOwnInstanceMethod(super.getMethodName()) = this)
|
|
.getQualifiedName() + "!" and
|
|
this.isConstructor()
|
|
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() { this.getNode() instanceof SummaryCallable }
|
|
|
|
/** 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() { this.getNode() instanceof NeutralCallable }
|
|
|
|
/**
|
|
* 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 = ""
|
|
}
|
|
|
|
/**
|
|
* Holds if this method is a constructor for a module.
|
|
*/
|
|
private predicate isConstructor() { isConstructor(this) }
|
|
}
|
|
|
|
string methodClassification(Call method) {
|
|
method.getFile() instanceof Util::TestFile and result = "test"
|
|
or
|
|
not method.getFile() instanceof Util::TestFile and
|
|
result = "source"
|
|
}
|
|
|
|
/**
|
|
* 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 callable where there exists a MaD summary model that applies to it.
|
|
*/
|
|
class SummaryCallable extends DataFlow::CallableNode {
|
|
SummaryCallable() {
|
|
exists(string type, string path |
|
|
Util::pathToMethod(this, type, path) and
|
|
summaryModel(type, path, _, _, _, _)
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A callable where there exists a MaD neutral model that applies to it.
|
|
*/
|
|
class NeutralCallable extends DataFlow::CallableNode {
|
|
NeutralCallable() {
|
|
exists(string type, string path |
|
|
Util::pathToMethod(this, type, path) and
|
|
neutralModel(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
|
|
moduleNode.getLocation().getFile() instanceof Util::RelevantFile
|
|
}
|
|
|
|
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 = "" }
|
|
}
|