WIP: Flask View class modeling for restplus

Based on some DBs I had that contained dependencies
This commit is contained in:
Rasmus Wriedt Larsen
2023-06-19 10:47:56 +02:00
parent 6a48e6ed5e
commit 2f17d2f3ac
4 changed files with 62 additions and 15 deletions

View File

@@ -14,6 +14,7 @@ private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
private import semmle.python.security.dataflow.PathInjectionCustomizations
private import semmle.python.dataflow.new.FlowSummary
private import semmle.python.frameworks.data.ModelsAsData
/**
* Provides models for the `flask` PyPI package.
@@ -39,6 +40,10 @@ module Flask {
"MethodView"
])
.getASubclass*()
or
result = ModelOutput::getATypeNode("flask.View~Subclass").getASubclass*()
or
result = ModelOutput::getATypeNode("flask.MethodView~Subclass").getASubclass*()
}
}
@@ -52,6 +57,8 @@ module Flask {
API::Node subclassRef() {
result =
API::moduleImport("flask").getMember("views").getMember("MethodView").getASubclass*()
or
result = ModelOutput::getATypeNode("flask.MethodView~Subclass").getASubclass*()
}
}
}

View File

@@ -23,7 +23,12 @@ extensions:
- addsTo:
pack: codeql/python-all
extensible: typeModel
data: []
data:
- ["flask.MethodView~Subclass","flask_restplus","Member[api].Member[SwaggerView]"]
- ["flask.MethodView~Subclass","flask_restplus","Member[resource].Member[Resource]"]
- ["flask.MethodView~Subclass","flask_restplus","Member[api].Member[Resource]"]
- ["flask.MethodView~Subclass","flask_restplus","Member[resource].Member[MethodView]"]
- ["flask.MethodView~Subclass","flask_restplus","Member[Resource]"]
- addsTo:
pack: codeql/python-all

View File

@@ -11,28 +11,17 @@ private import semmle.python.ApiGraphs
private import semmle.python.filters.Tests
// very much inspired by the draft at https://github.com/github/codeql/pull/5632
private module NotExposed {
module NotExposed {
// Instructions:
// This needs to be automated better, but for this prototype, here are some rough instructions:
// 0) get a database of the library you are about to model
// 1) fill out the `getAlreadyModeledClass` body below
// 2) quick-eval the `quickEvalMe` predicate below, and copy the output to your modeling predicate
class MySpec extends FindSubclassesSpec {
MySpec() { this = "MySpec" }
override API::Node getAlreadyModeledClass() {
// FILL ME OUT ! (but don't commit with any changes)
none()
// for example
// result = API::moduleImport("rest_framework").getMember("views").getMember("APIView")
}
}
predicate quickEvalMe(string newImport) {
newImport =
"// imports generated by python/frameworks/internal/SubclassFinder.qll\n" + "this = API::" +
concat(string newModelFullyQualified |
newModel(any(MySpec spec), newModelFullyQualified, _, _, _)
newModel(any(FindSubclassesSpec spec), newModelFullyQualified, _, _, _)
|
fullyQualifiedToApiGraphPath(newModelFullyQualified), " or this = API::"
)
@@ -76,6 +65,8 @@ private module NotExposed {
bindingset[this]
abstract class FindSubclassesSpec extends string {
abstract API::Node getAlreadyModeledClass();
FindSubclassesSpec getSuperClass() { none() }
}
/**
@@ -111,7 +102,7 @@ private module NotExposed {
predicate isNonTestProjectCode(AstNode ast) {
not ast.getScope*() instanceof TestScope and
not ast.getLocation().getFile().getRelativePath().matches("tests/%") and
exists(ast.getLocation().getFile().getRelativePath())
not exists(ast.getLocation().getFile().getRelativePath())
}
predicate hasAllStatement(Module mod) {

View File

@@ -0,0 +1,44 @@
import python
import semmle.python.dataflow.new.DataFlow
private import semmle.python.ApiGraphs
import semmle.python.frameworks.internal.SubclassFinder::NotExposed
private import semmle.python.frameworks.Flask
private import semmle.python.frameworks.FastApi
private import semmle.python.frameworks.Django
import semmle.python.frameworks.data.internal.ApiGraphModelsExtensions as Extensions
class FlaskViewClasses extends FindSubclassesSpec {
FlaskViewClasses() { this = "flask.View~Subclass" }
override API::Node getAlreadyModeledClass() { result = Flask::Views::View::subclassRef() }
}
class FlaskMethodViewClasses extends FindSubclassesSpec {
FlaskMethodViewClasses() { this = "flask.MethodView~Subclass" }
override API::Node getAlreadyModeledClass() { result = Flask::Views::MethodView::subclassRef() }
override FlaskViewClasses getSuperClass() { any() }
}
bindingset[fullyQualified]
predicate fullyQualifiedToYamlFormat(string fullyQualified, string type2, string path) {
exists(int firstDot | firstDot = fullyQualified.indexOf(".", 0, 0) |
type2 = fullyQualified.prefix(firstDot) and
path =
("Member[" + fullyQualified.suffix(firstDot + 1).replaceAll(".", "].Member[") + "]")
.replaceAll(".Member[__init__].", "")
.replaceAll("Member[__init__].", "")
)
}
from FindSubclassesSpec spec, string newModelFullyQualified, string type2, string path, Module mod
where
newModel(spec, newModelFullyQualified, _, mod, _) and
not exists(FindSubclassesSpec subclass | subclass.getSuperClass() = spec |
newModel(subclass, newModelFullyQualified, _, mod, _)
) and
not exists(mod.getLocation().getFile().getRelativePath()) and
fullyQualifiedToYamlFormat(newModelFullyQualified, type2, path) and
not Extensions::typeModel(spec, type2, path)
select spec.(string), type2, path