When returning HTML (the default response type in Flask), any user-provided values rendered in the output must be escaped to protect from injection attacks. HTML templates rendered with Jinja, introduced later, will do this automatically.
/**
* @name SQL Injection
* @kind problem
* ...
*/import python
import DataFlow::PathGraph
// Predicates and Classesclass Sources extends DataFlow::Node { /* class */ }
// Query Output / Resultsfrom Call call
select call, "Calls in the code"
Let's Answer Some Questions...
Question 1
How do we find the Source?
Python Code
from flask import request
request.args.get("search")
# ^ Source!
CodeQL Query
import python
import semmle.python.Concepts
import semmle.python.ApiGraphs
/*
* How do we find the source?
*/from DataFlow::Node request, Attribute attr
where
request = API::moduleImport("flask").getMember("request").getAValueReachableFromSource() and
attr.getObject() = request.asExpr()
select attr, "Source"
import python
import semmle.python.Concepts
import semmle.python.ApiGraphs
/*
* What is the sink?
*/from CallNode call, DataFlow::Node sink
where// Find all functions called "execute"
call.getFunction().(AttrNode).getName() in ["execute"] and// The first argument is what we are interested in
sink.asCfgNode() = call.getArg(0)
select sink, "Sink"
Note: being lazy and looking for execute(...)
Question 3
Can we find a path from Source to Sink?
CodeQL Full - Complete
class SqlInjectionConfig extends TaintTracking::Configuration {
SqlInjectionConfig() { this = "SqlInjectionConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof Sources }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sinks }
}
from SqlInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "This SQL query depends on $@.", source.getNode(),
"a user-provided value"