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

Skip to content

Commit df6fd53

Browse files
committed
Python: Add HttpResponse concept
We might need to rework this a bit when we also start to handle redirects. I could see a world where we simply allow http redirects to be subclasses of http responses, and need to manually exclude them from queries (or create HttpContentResponse to model the HttpResponses that will contain a body). Let us see where the wind will take us. I looked through JS and Go libraries, but I didn't feel their modeling would map very well to Python.
1 parent 0d61658 commit df6fd53

2 files changed

Lines changed: 101 additions & 0 deletions

File tree

python/ql/src/experimental/semmle/python/Concepts.qll

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,5 +219,68 @@ module HTTP {
219219

220220
override string getSourceType() { result = "RoutedParameter" }
221221
}
222+
223+
/**
224+
* A data-flow node that creates a HTTP response on a server.
225+
*
226+
* Note: we don't require that this response must be sent to a client (a kind of
227+
* "if a tree falls in a forest and nobody hears it" situation).
228+
*
229+
* Extend this class to refine existing API models. If you want to model new APIs,
230+
* extend `HttpResponse::Range` instead.
231+
*/
232+
class HttpResponse extends DataFlow::Node {
233+
HttpResponse::Range range;
234+
235+
HttpResponse() { this = range }
236+
237+
/** Gets the data-flow node that specifies the body of this HTTP response. */
238+
DataFlow::Node getBody() { result = range.getBody() }
239+
240+
/** Gets the content-type of this HTTP response, if it can be statically determined. */
241+
string getContentType() { result = range.getContentType() }
242+
243+
/** Gets the status code of this HTTP response, if it can be statically determined. */
244+
int getStatusCode() { result = range.getStatusCode() }
245+
}
246+
247+
/** Provides a class for modeling new HTTP response APIs. */
248+
module HttpResponse {
249+
/**
250+
* A data-flow node that creates a HTTP response on a server.
251+
*
252+
* Note: we don't require that this response must be sent to a client (a kind of
253+
* "if a tree falls in a forest and nobody hears it" situation).
254+
*
255+
* Extend this class to model new APIs. If you want to refine existing API models,
256+
* extend `HttpResponse` instead.
257+
*/
258+
abstract class Range extends DataFlow::Node {
259+
/** Gets the data-flow node that specifies the body of this HTTP response. */
260+
abstract DataFlow::Node getBody();
261+
262+
/** Gets the data-flow node that specifies the content-type of this HTTP response, if any. */
263+
abstract DataFlow::Node getContentTypeArg();
264+
265+
/** Gets the content-type of this HTTP response, if it can be statically determined. */
266+
string getContentType() {
267+
exists(StrConst str |
268+
DataFlow::localFlow(DataFlow::exprNode(str), this.getContentTypeArg()) and
269+
result = str.getText()
270+
)
271+
}
272+
273+
/** Gets the data-flow node that specifies the status code of this HTTP response, if any. */
274+
abstract DataFlow::Node getStatusCodeArg();
275+
276+
/** Gets the status code of this HTTP response, if it can be statically determined. */
277+
int getStatusCode() {
278+
exists(IntegerLiteral i |
279+
DataFlow::localFlow(DataFlow::exprNode(i), this.getStatusCodeArg()) and
280+
result = i.getValue()
281+
)
282+
}
283+
}
284+
}
222285
}
223286
}

python/ql/test/experimental/meta/ConceptsTest.qll

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,41 @@ class HttpServerRouteSetupTest extends InlineExpectationsTest {
142142
)
143143
}
144144
}
145+
146+
class HttpServerHttpResponseTest extends InlineExpectationsTest {
147+
HttpServerHttpResponseTest() { this = "HttpServerHttpResponseTest" }
148+
149+
override string getARelevantTag() {
150+
result in ["HttpResponse", "responseBody", "contentType", "statusCode"]
151+
}
152+
153+
override predicate hasActualResult(Location location, string element, string tag, string value) {
154+
exists(HTTP::Server::HttpResponse response |
155+
location = response.getLocation() and
156+
element = response.toString() and
157+
value = "" and
158+
tag = "HttpResponse"
159+
)
160+
or
161+
exists(HTTP::Server::HttpResponse response |
162+
location = response.getLocation() and
163+
element = response.toString() and
164+
value = value_from_expr(response.getBody().asExpr()) and
165+
tag = "responseBody"
166+
)
167+
or
168+
exists(HTTP::Server::HttpResponse response |
169+
location = response.getLocation() and
170+
element = response.toString() and
171+
value = response.getContentType() and
172+
tag = "contentType"
173+
)
174+
or
175+
exists(HTTP::Server::HttpResponse response |
176+
location = response.getLocation() and
177+
element = response.toString() and
178+
value = response.getStatusCode().toString() and
179+
tag = "statusCode"
180+
)
181+
}
182+
}

0 commit comments

Comments
 (0)