mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Ruby: Model Editor support
Add experimental support for the CodeQL Model Editor.
This commit is contained in:
@@ -8,6 +8,7 @@ private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.BarrierGuards
|
||||
private import codeql.ruby.dataflow.RemoteFlowSources
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting SQL injection
|
||||
@@ -56,4 +57,8 @@ module SqlInjection {
|
||||
{ }
|
||||
|
||||
private class SqlSanitizationAsSanitizer extends Sanitizer, SqlSanitization { }
|
||||
|
||||
private class ExternalSqlInjectionSink extends Sink {
|
||||
ExternalSqlInjectionSink() { this = ModelOutput::getASinkNode("sql-injection").asSink() }
|
||||
}
|
||||
}
|
||||
|
||||
12
ruby/ql/src/utils/modeleditor/ApplicationModeEndpoints.ql
Normal file
12
ruby/ql/src/utils/modeleditor/ApplicationModeEndpoints.ql
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
// This query is empty as Application Mode is not yet supported for Ruby.
|
||||
from int n
|
||||
where none()
|
||||
select n
|
||||
15
ruby/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
Normal file
15
ruby/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @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 ModelEditor
|
||||
|
||||
from PublicEndpointFromSource endpoint
|
||||
select endpoint, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
|
||||
endpoint.getParameterTypes(), endpoint.getSupportedStatus(), endpoint.getFile().getBaseName(),
|
||||
endpoint.getSupportedType()
|
||||
145
ruby/ql/src/utils/modeleditor/ModelEditor.qll
Normal file
145
ruby/ql/src/utils/modeleditor/ModelEditor.qll
Normal file
@@ -0,0 +1,145 @@
|
||||
/** 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.dataflow.internal.FlowSummaryImplSpecific
|
||||
private import codeql.ruby.frameworks.core.Gem
|
||||
private import codeql.ruby.frameworks.data.ModelsAsData
|
||||
private import codeql.ruby.frameworks.data.internal.ApiGraphModelsExtensions
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(DataFlow::MethodNode c) {
|
||||
c.getLocation().getFile() instanceof TestFile
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable method or accessor from either the Ruby Standard Library, a 3rd party library, or from the source.
|
||||
*/
|
||||
class Endpoint extends DataFlow::MethodNode {
|
||||
Endpoint() { this.isPublic() and not isUninteresting(this) }
|
||||
|
||||
File getFile() { result = this.getLocation().getFile() }
|
||||
|
||||
string getName() { result = this.getMethodName() }
|
||||
|
||||
/**
|
||||
* Gets the namespace of this endpoint.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getNamespace() {
|
||||
// Return the name of any gemspec file in the database.
|
||||
// TODO: make this work for projects with multiple gems (and hence multiple gemspec files)
|
||||
result = any(Gem::GemSpec g).getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unbound type name of this endpoint.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getTypeName() {
|
||||
result =
|
||||
any(DataFlow::ModuleNode m | m.getOwnInstanceMethod(this.getMethodName()) = this)
|
||||
.getQualifiedName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter types of this endpoint.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getParameterTypes() {
|
||||
// For now, return the names of postional parameters. We don't always have type information, so we can't return type names.
|
||||
// We don't yet handle keyword params, splat params or block params.
|
||||
result =
|
||||
"(" +
|
||||
concat(DataFlow::ParameterNode p, int i |
|
||||
p = this.asCallable().getParameter(i)
|
||||
|
|
||||
p.getName(), "," order by i
|
||||
) + ")"
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() { none() }
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
abstract predicate isSource();
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
abstract predicate isSink();
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
predicate isNeutral() {
|
||||
none()
|
||||
// this instanceof FlowSummaryImpl::Public::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()
|
||||
}
|
||||
|
||||
boolean getSupportedStatus() { if this.isSupported() then result = true else result = false }
|
||||
|
||||
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).+") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable where there exists a MaD sink model that applies to it.
|
||||
*/
|
||||
class SinkCallable extends DataFlow::MethodNode {
|
||||
SinkCallable() {
|
||||
this = ModelOutput::getASinkNode(_).asCallable() and
|
||||
exists(string type, string path, string kind, string method |
|
||||
sinkModel(type, path, kind) and
|
||||
path = "Method[" + method + "]" and
|
||||
method = this.getMethodName()
|
||||
// TODO: (type, path) corresponds to this method
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable where there exists a MaD source model that applies to it.
|
||||
*/
|
||||
class SourceCallable extends DataFlow::CallableNode {
|
||||
SourceCallable() { sourceElement(this.asExpr().getExpr(), _, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of effectively public callables from source code.
|
||||
*/
|
||||
class PublicEndpointFromSource extends Endpoint {
|
||||
override predicate isSource() { this instanceof SourceCallable }
|
||||
|
||||
override predicate isSink() { this instanceof SinkCallable }
|
||||
}
|
||||
Reference in New Issue
Block a user