Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2f17d2f

Browse files
committed
WIP: Flask View class modeling for restplus
Based on some DBs I had that contained dependencies
1 parent 6a48e6e commit 2f17d2f

4 files changed

Lines changed: 62 additions & 15 deletions

File tree

python/ql/lib/semmle/python/frameworks/Flask.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private import semmle.python.ApiGraphs
1414
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
1515
private import semmle.python.security.dataflow.PathInjectionCustomizations
1616
private import semmle.python.dataflow.new.FlowSummary
17+
private import semmle.python.frameworks.data.ModelsAsData
1718

1819
/**
1920
* Provides models for the `flask` PyPI package.
@@ -39,6 +40,10 @@ module Flask {
3940
"MethodView"
4041
])
4142
.getASubclass*()
43+
or
44+
result = ModelOutput::getATypeNode("flask.View~Subclass").getASubclass*()
45+
or
46+
result = ModelOutput::getATypeNode("flask.MethodView~Subclass").getASubclass*()
4247
}
4348
}
4449

@@ -52,6 +57,8 @@ module Flask {
5257
API::Node subclassRef() {
5358
result =
5459
API::moduleImport("flask").getMember("views").getMember("MethodView").getASubclass*()
60+
or
61+
result = ModelOutput::getATypeNode("flask.MethodView~Subclass").getASubclass*()
5562
}
5663
}
5764
}

python/ql/lib/semmle/python/frameworks/data/internal/empty.model.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ extensions:
2323
- addsTo:
2424
pack: codeql/python-all
2525
extensible: typeModel
26-
data: []
26+
data:
27+
- ["flask.MethodView~Subclass","flask_restplus","Member[api].Member[SwaggerView]"]
28+
- ["flask.MethodView~Subclass","flask_restplus","Member[resource].Member[Resource]"]
29+
- ["flask.MethodView~Subclass","flask_restplus","Member[api].Member[Resource]"]
30+
- ["flask.MethodView~Subclass","flask_restplus","Member[resource].Member[MethodView]"]
31+
- ["flask.MethodView~Subclass","flask_restplus","Member[Resource]"]
2732

2833
- addsTo:
2934
pack: codeql/python-all

python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,17 @@ private import semmle.python.ApiGraphs
1111
private import semmle.python.filters.Tests
1212

1313
// very much inspired by the draft at https://github.com/github/codeql/pull/5632
14-
private module NotExposed {
14+
module NotExposed {
1515
// Instructions:
1616
// This needs to be automated better, but for this prototype, here are some rough instructions:
1717
// 0) get a database of the library you are about to model
1818
// 1) fill out the `getAlreadyModeledClass` body below
1919
// 2) quick-eval the `quickEvalMe` predicate below, and copy the output to your modeling predicate
20-
class MySpec extends FindSubclassesSpec {
21-
MySpec() { this = "MySpec" }
22-
23-
override API::Node getAlreadyModeledClass() {
24-
// FILL ME OUT ! (but don't commit with any changes)
25-
none()
26-
// for example
27-
// result = API::moduleImport("rest_framework").getMember("views").getMember("APIView")
28-
}
29-
}
30-
3120
predicate quickEvalMe(string newImport) {
3221
newImport =
3322
"// imports generated by python/frameworks/internal/SubclassFinder.qll\n" + "this = API::" +
3423
concat(string newModelFullyQualified |
35-
newModel(any(MySpec spec), newModelFullyQualified, _, _, _)
24+
newModel(any(FindSubclassesSpec spec), newModelFullyQualified, _, _, _)
3625
|
3726
fullyQualifiedToApiGraphPath(newModelFullyQualified), " or this = API::"
3827
)
@@ -76,6 +65,8 @@ private module NotExposed {
7665
bindingset[this]
7766
abstract class FindSubclassesSpec extends string {
7867
abstract API::Node getAlreadyModeledClass();
68+
69+
FindSubclassesSpec getSuperClass() { none() }
7970
}
8071

8172
/**
@@ -111,7 +102,7 @@ private module NotExposed {
111102
predicate isNonTestProjectCode(AstNode ast) {
112103
not ast.getScope*() instanceof TestScope and
113104
not ast.getLocation().getFile().getRelativePath().matches("tests/%") and
114-
exists(ast.getLocation().getFile().getRelativePath())
105+
not exists(ast.getLocation().getFile().getRelativePath())
115106
}
116107

117108
predicate hasAllStatement(Module mod) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import python
2+
import semmle.python.dataflow.new.DataFlow
3+
private import semmle.python.ApiGraphs
4+
import semmle.python.frameworks.internal.SubclassFinder::NotExposed
5+
private import semmle.python.frameworks.Flask
6+
private import semmle.python.frameworks.FastApi
7+
private import semmle.python.frameworks.Django
8+
import semmle.python.frameworks.data.internal.ApiGraphModelsExtensions as Extensions
9+
10+
class FlaskViewClasses extends FindSubclassesSpec {
11+
FlaskViewClasses() { this = "flask.View~Subclass" }
12+
13+
override API::Node getAlreadyModeledClass() { result = Flask::Views::View::subclassRef() }
14+
}
15+
16+
class FlaskMethodViewClasses extends FindSubclassesSpec {
17+
FlaskMethodViewClasses() { this = "flask.MethodView~Subclass" }
18+
19+
override API::Node getAlreadyModeledClass() { result = Flask::Views::MethodView::subclassRef() }
20+
21+
override FlaskViewClasses getSuperClass() { any() }
22+
}
23+
24+
bindingset[fullyQualified]
25+
predicate fullyQualifiedToYamlFormat(string fullyQualified, string type2, string path) {
26+
exists(int firstDot | firstDot = fullyQualified.indexOf(".", 0, 0) |
27+
type2 = fullyQualified.prefix(firstDot) and
28+
path =
29+
("Member[" + fullyQualified.suffix(firstDot + 1).replaceAll(".", "].Member[") + "]")
30+
.replaceAll(".Member[__init__].", "")
31+
.replaceAll("Member[__init__].", "")
32+
)
33+
}
34+
35+
from FindSubclassesSpec spec, string newModelFullyQualified, string type2, string path, Module mod
36+
where
37+
newModel(spec, newModelFullyQualified, _, mod, _) and
38+
not exists(FindSubclassesSpec subclass | subclass.getSuperClass() = spec |
39+
newModel(subclass, newModelFullyQualified, _, mod, _)
40+
) and
41+
not exists(mod.getLocation().getFile().getRelativePath()) and
42+
fullyQualifiedToYamlFormat(newModelFullyQualified, type2, path) and
43+
not Extensions::typeModel(spec, type2, path)
44+
select spec.(string), type2, path

0 commit comments

Comments
 (0)