Skip to content

Commit 88e3227

Browse files
Add pyramid models
1 parent 75b1e14 commit 88e3227

File tree

5 files changed

+115
-0
lines changed

5 files changed

+115
-0
lines changed

python/ql/lib/semmle/python/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ private import semmle.python.frameworks.PyMongo
5656
private import semmle.python.frameworks.Pymssql
5757
private import semmle.python.frameworks.PyMySQL
5858
private import semmle.python.frameworks.Pyodbc
59+
private import semmle.python.frameworks.Pyramid
5960
private import semmle.python.frameworks.Requests
6061
private import semmle.python.frameworks.RestFramework
6162
private import semmle.python.frameworks.Rsa
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `pyramid` PyPI package.
3+
* See https://docs.pylonsproject.org/projects/pyramid/.
4+
*/
5+
6+
private import python
7+
private import semmle.python.dataflow.new.DataFlow
8+
private import semmle.python.dataflow.new.RemoteFlowSources
9+
private import semmle.python.dataflow.new.TaintTracking
10+
private import semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
12+
private import semmle.python.dataflow.new.FlowSummary
13+
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
14+
private import semmle.python.frameworks.data.ModelsAsData
15+
16+
/**
17+
* Provides models for the `pyramid` PyPI package.
18+
* See https://docs.pylonsproject.org/projects/pyramid/.
19+
*/
20+
module Pyramid {
21+
// TODO: qldoc
22+
module View {
23+
/**
24+
* A callable that could be used as a pyramid view callable.
25+
*/
26+
private class PotentialViewCallable extends Function {
27+
PotentialViewCallable() {
28+
this.getPositionalParameterCount() = 1 and
29+
this.getArgName(0) = "request"
30+
or
31+
this.getPositionalParameterCount() = 2 and
32+
this.getArgName(0) = "context" and
33+
this.getArgName(1) = "request"
34+
}
35+
36+
/** Gets the `request` parameter of this view callable. */
37+
Parameter getRequestParameter() { result = this.getArgByName("request") }
38+
}
39+
40+
abstract class ViewCallable extends PotentialViewCallable, Http::Server::RequestHandler::Range {
41+
override Parameter getARoutedParameter() { result = this.getRequestParameter() }
42+
43+
override string getFramework() { result = "Pyramid" }
44+
}
45+
46+
private class ViewCallableFromDecorator extends ViewCallable {
47+
ViewCallableFromDecorator() {
48+
this.getADecorator() =
49+
API::moduleImport("pyramid")
50+
.getMember("view")
51+
.getMember("view_config")
52+
.getACall()
53+
.asExpr()
54+
}
55+
}
56+
57+
private class ViewCallableFromConfigurator extends ViewCallable {
58+
ViewCallableFromConfigurator() {
59+
any(Configurator::AddViewCall c).getViewArg() = poorMansFunctionTracker(this)
60+
}
61+
}
62+
}
63+
64+
module Configurator {
65+
/** Gets a reference to the class `pyramid.config.Configurator`. */
66+
API::Node classRef() {
67+
result = API::moduleImport("pyramid").getMember("config").getMember("Configurator")
68+
}
69+
70+
/** Gets a reference to an instance of `pyramid.config.Configurator`. */
71+
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
72+
t.start() and
73+
result = classRef().getACall()
74+
or
75+
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
76+
}
77+
78+
/** Gets a reference to an instance of `pyramid.config.Configurator`. */
79+
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
80+
81+
class AddViewCall extends DataFlow::MethodCallNode {
82+
AddViewCall() { this.calls(instance(), "add_view") }
83+
84+
DataFlow::Node getViewArg() { result = [this.getArg(0), this.getArgByName("view")] }
85+
}
86+
}
87+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
argumentToEnsureNotTaintedNotMarkedAsSpurious
2+
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
3+
testFailures
4+
failures
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import experimental.meta.InlineTaintTest
2+
import MakeInlineTaintTest<TestTaintTrackingConfig>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from pyramid.view import view_config
2+
from pyramid.config import Configurator
3+
4+
@view_config(route_name="test1")
5+
def test1(request):
6+
ensure_tainted(
7+
request, # $ tainted
8+
request.body, # $ MISSING:tainted
9+
request.GET['a'] # $ MISSING:tainted
10+
)
11+
12+
def test2(request):
13+
ensure_tainted(request) # $ tainted
14+
15+
@view_config(route_name="test1")
16+
def test3(context, request):
17+
ensure_tainted(request) # $ tainted
18+
19+
if __name__ == "__main__":
20+
with Configurator() as config:
21+
config.add_view(test2, route_name="test2")

0 commit comments

Comments
 (0)