mirror of
https://github.com/github/codeql.git
synced 2025-12-18 01:33:15 +01:00
Python : Improve Xpath Injection Query
This commit is contained in:
36
python/ql/src/experimental/Security/CWE-643-new/Xpath.ql
Normal file
36
python/ql/src/experimental/Security/CWE-643-new/Xpath.ql
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @name XPath query built from user-controlled sources
|
||||
* @description Building a XPath query from user-controlled sources is vulnerable to insertion of
|
||||
* malicious Xpath code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id py/xpath-injection-new
|
||||
* @tags security
|
||||
* external/cwe/cwe-643
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
import XpathInjection::XpathInjection
|
||||
|
||||
class XpathInjectionConfiguration extends TaintTracking::Configuration {
|
||||
XpathInjectionConfiguration() { this = "PathNotNormalizedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
// override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// exists(AdditionalFlowStep af | af.isAdditionalTaintStep(node1, node2))
|
||||
// }
|
||||
}
|
||||
|
||||
from XpathInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink, "This Xpath query depends on $@.", source,
|
||||
"a user-provided value"
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting "Xpath Injection" vulnerabilities.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `XpathInjection::Configuration` is needed, otherwise
|
||||
* `XpathInjectionCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting "Xpath Injection" vulnerabilities.
|
||||
*/
|
||||
module XpathInjection {
|
||||
import XpathInjectionCustomizations::XpathInjection
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting "Xpath Injection" vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "Xpath Injection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Provides class and predicates to track external data that
|
||||
* may represent malicious xpath query objects.
|
||||
*
|
||||
* This module is intended to be imported into a taint-tracking query.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
/** Models Xpath Injection related classes and functions */
|
||||
module XpathInjection {
|
||||
/**
|
||||
* A data flow source for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
|
||||
|
||||
/** Returns an API node referring to `lxml.etree` */
|
||||
API::Node etree() { result = API::moduleImport("lxml").getMember("etree") }
|
||||
|
||||
/** Returns an API node referring to `lxml.etree` */
|
||||
API::Node etreeFromString() { result = etree().getMember("fromstring") }
|
||||
|
||||
/** Returns an API node referring to `lxml.etree.parse` */
|
||||
API::Node etreeParse() { result = etree().getMember("parse") }
|
||||
|
||||
/** Returns an API node referring to `lxml.etree.parse` */
|
||||
API::Node libxml2parseFile() { result = API::moduleImport("libxml2").getMember("parseFile") }
|
||||
|
||||
/**
|
||||
* A Sink representing an argument to `etree.XPath` or `etree.ETXpath` call.
|
||||
*
|
||||
* from lxml import etree
|
||||
* root = etree.XML("<xmlContent>")
|
||||
* find_text = etree.XPath("`sink`")
|
||||
* find_text = etree.ETXpath("`sink`")
|
||||
*/
|
||||
private class EtreeXpathArgument extends Sink {
|
||||
EtreeXpathArgument() { this = etree().getMember(["XPath", "ETXpath"]).getACall().getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Sink representing an argument to the `etree.XPath` call.
|
||||
*
|
||||
* from lxml import etree
|
||||
* root = etree.fromstring(file(XML_DB).read(), XMLParser())
|
||||
* find_text = root.xpath("`sink`")
|
||||
*/
|
||||
private class EtreeFromstringXpathArgument extends Sink {
|
||||
EtreeFromstringXpathArgument() {
|
||||
this = etreeFromString().getReturn().getMember("xpath").getACall().getArg(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Sink representing an argument to the `xpath` call to a parsed xml document.
|
||||
*
|
||||
* from lxml import etree
|
||||
* from io import StringIO
|
||||
* f = StringIO('<foo><bar></bar></foo>')
|
||||
* tree = etree.parse(f)
|
||||
* r = tree.xpath('`sink`')
|
||||
*/
|
||||
private class ParseXpathArgument extends Sink {
|
||||
ParseXpathArgument() { this = etreeParse().getReturn().getMember("xpath").getACall().getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Sink representing an argument to the `xpathEval` call to a parsed libxml2 document.
|
||||
*
|
||||
* import libxml2
|
||||
* tree = libxml2.parseFile("file.xml")
|
||||
* r = tree.xpathEval('`sink`')
|
||||
*/
|
||||
private class ParseFileXpathEvalArgument extends Sink {
|
||||
ParseFileXpathEvalArgument() {
|
||||
this = libxml2parseFile().getReturn().getMember("xpathEval").getACall().getArg(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user