Skip to content

Commit dbecb1b

Browse files
authored
Merge pull request github#14070 from yoff/python/promote-nosql-query
Python: promote nosql query
2 parents efb49fc + 9b73bbf commit dbecb1b

34 files changed

+883
-311
lines changed

python/ql/lib/semmle/python/Concepts.qll

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,68 @@ module SqlExecution {
378378
}
379379
}
380380

381+
/** Provides a class for modeling NoSQL execution APIs. */
382+
module NoSqlExecution {
383+
/**
384+
* A data-flow node that executes NoSQL queries.
385+
*
386+
* Extend this class to model new APIs. If you want to refine existing API models,
387+
* extend `NoSqlExecution` instead.
388+
*/
389+
abstract class Range extends DataFlow::Node {
390+
/** Gets the argument that specifies the NoSQL query to be executed. */
391+
abstract DataFlow::Node getQuery();
392+
393+
/** Holds if this query will unpack/interpret a dictionary */
394+
abstract predicate interpretsDict();
395+
396+
/** Holds if this query can be dangerous when run on a user-controlled string */
397+
abstract predicate vulnerableToStrings();
398+
}
399+
}
400+
401+
/**
402+
* A data-flow node that executes NoSQL queries.
403+
*
404+
* Extend this class to refine existing API models. If you want to model new APIs,
405+
* extend `NoSqlExecution::Range` instead.
406+
*/
407+
class NoSqlExecution extends DataFlow::Node instanceof NoSqlExecution::Range {
408+
/** Gets the argument that specifies the NoSQL query to be executed. */
409+
DataFlow::Node getQuery() { result = super.getQuery() }
410+
411+
/** Holds if this query will unpack/interpret a dictionary */
412+
predicate interpretsDict() { super.interpretsDict() }
413+
414+
/** Holds if this query can be dangerous when run on a user-controlled string */
415+
predicate vulnerableToStrings() { super.vulnerableToStrings() }
416+
}
417+
418+
/** Provides classes for modeling NoSql sanitization-related APIs. */
419+
module NoSqlSanitizer {
420+
/**
421+
* A data-flow node that collects functions sanitizing NoSQL queries.
422+
*
423+
* Extend this class to model new APIs. If you want to refine existing API models,
424+
* extend `NoSQLSanitizer` instead.
425+
*/
426+
abstract class Range extends DataFlow::Node {
427+
/** Gets the argument that specifies the NoSql query to be sanitized. */
428+
abstract DataFlow::Node getAnInput();
429+
}
430+
}
431+
432+
/**
433+
* A data-flow node that collects functions sanitizing NoSQL queries.
434+
*
435+
* Extend this class to model new APIs. If you want to refine existing API models,
436+
* extend `NoSQLSanitizer::Range` instead.
437+
*/
438+
class NoSqlSanitizer extends DataFlow::Node instanceof NoSqlSanitizer::Range {
439+
/** Gets the argument that specifies the NoSql query to be sanitized. */
440+
DataFlow::Node getAnInput() { result = super.getAnInput() }
441+
}
442+
381443
/**
382444
* A data-flow node that executes a regular expression.
383445
*

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import semmle.python.frameworks.Aiomysql
1010
private import semmle.python.frameworks.Aiosqlite
1111
private import semmle.python.frameworks.Aiopg
1212
private import semmle.python.frameworks.Asyncpg
13+
private import semmle.python.frameworks.BSon
1314
private import semmle.python.frameworks.CassandraDriver
1415
private import semmle.python.frameworks.ClickhouseDriver
1516
private import semmle.python.frameworks.Cryptodome
@@ -42,6 +43,7 @@ private import semmle.python.frameworks.Phoenixdb
4243
private import semmle.python.frameworks.Psycopg2
4344
private import semmle.python.frameworks.Pycurl
4445
private import semmle.python.frameworks.Pydantic
46+
private import semmle.python.frameworks.PyMongo
4547
private import semmle.python.frameworks.Pymssql
4648
private import semmle.python.frameworks.PyMySQL
4749
private import semmle.python.frameworks.Pyodbc
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `bson` PyPI package.
3+
* See
4+
* - https://pypi.org/project/bson/
5+
* - https://github.com/py-bson/bson
6+
*/
7+
8+
private import python
9+
private import semmle.python.dataflow.new.DataFlow
10+
private import semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
12+
13+
/**
14+
* Provides models for the `bson` PyPI package.
15+
* See
16+
* - https://pypi.org/project/bson/
17+
* - https://github.com/py-bson/bson
18+
*/
19+
private module BSon {
20+
/**
21+
* ObjectId returns a string representing an id.
22+
* If at any time ObjectId can't parse it's input (like when a tainted dict in passed in),
23+
* then ObjectId will throw an error preventing the query from running.
24+
*/
25+
private class BsonObjectIdCall extends DataFlow::CallCfgNode, NoSqlSanitizer::Range {
26+
BsonObjectIdCall() {
27+
exists(API::Node mod |
28+
mod = API::moduleImport("bson")
29+
or
30+
mod = API::moduleImport("bson").getMember(["objectid", "json_util"])
31+
|
32+
this = mod.getMember("ObjectId").getACall()
33+
)
34+
}
35+
36+
override DataFlow::Node getAnInput() { result = this.getArg(0) }
37+
}
38+
}

0 commit comments

Comments
 (0)