diff --git a/ql/src/experimental/Security/CWE-643/XPathInjection.go b/ql/src/Security/CWE-643/XPathInjection.go similarity index 100% rename from ql/src/experimental/Security/CWE-643/XPathInjection.go rename to ql/src/Security/CWE-643/XPathInjection.go diff --git a/ql/src/experimental/Security/CWE-643/XPathInjection.qhelp b/ql/src/Security/CWE-643/XPathInjection.qhelp similarity index 100% rename from ql/src/experimental/Security/CWE-643/XPathInjection.qhelp rename to ql/src/Security/CWE-643/XPathInjection.qhelp diff --git a/ql/src/Security/CWE-643/XPathInjection.ql b/ql/src/Security/CWE-643/XPathInjection.ql new file mode 100644 index 00000000000..141004ee60f --- /dev/null +++ b/ql/src/Security/CWE-643/XPathInjection.ql @@ -0,0 +1,19 @@ +/** + * @name XPath injection + * @description Building an XPath expression from user-controlled sources is vulnerable to insertion of + * malicious code by the user. + * @kind path-problem + * @problem.severity error + * @id go/xml/xpath-injection + * @tags security + * external/cwe/cwe-643 + */ + +import go +import semmle.go.security.XPathInjection::XPathInjection +import DataFlow::PathGraph + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ flows here and is used in an XPath expression.", + source.getNode(), "A user-provided value" diff --git a/ql/src/experimental/Security/CWE-643/XPathInjection.ql b/ql/src/experimental/Security/CWE-643/XPathInjection.ql deleted file mode 100644 index 453710d71e0..00000000000 --- a/ql/src/experimental/Security/CWE-643/XPathInjection.ql +++ /dev/null @@ -1,188 +0,0 @@ -/** - * @name XPath injection - * @description Building an XPath expression from user-controlled sources is vulnerable to insertion of - * malicious code by the user. - * @kind path-problem - * @problem.severity error - * @id go/xml/xpath-injection - * @tags security - * external/cwe/cwe-643 - */ - -import go -import DataFlow::PathGraph - -class ByteSliceType extends SliceType { - ByteSliceType() { this.getElementType() instanceof Uint8Type } -} - -class XPathInjectionConfiguration extends TaintTracking::Configuration { - XPathInjectionConfiguration() { this = "XPathInjectionConfiguration" } - - override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof XPathInjectionSink } - - override predicate isSanitizer(DataFlow::Node node) { - exists(Type t | t = node.getType().getUnderlyingType() | - not t instanceof StringType or not t instanceof ByteSliceType - ) - } -} - -abstract class XPathInjectionSink extends DataFlow::Node { } - -// https://github.com/antchfx/xpath -class XPathSink extends XPathInjectionSink { - XPathSink() { - exists(Function f, string name | name.matches("Compile%") | - f.hasQualifiedName("github.com/antchfx/xpath", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f, string name | name.matches("MustCompile%") | - f.hasQualifiedName("github.com/antchfx/xpath", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f, string name | name.matches("Select%") | - f.hasQualifiedName("github.com/antchfx/xpath", name) and - this = f.getACall().getArgument(1) - ) - } -} - -// https://github.com/antchfx/htmlquery -class HtmlQuerySink extends XPathInjectionSink { - HtmlQuerySink() { - exists(Function f, string name | name.matches("Find%") | - f.hasQualifiedName("github.com/antchfx/htmlquery", name) and - this = f.getACall().getArgument(1) - ) - or - exists(Function f, string name | name.matches("Query%") | - f.hasQualifiedName("github.com/antchfx/htmlquery", name) and - this = f.getACall().getArgument(1) - ) - or - exists(Function f | - f.hasQualifiedName("github.com/antchfx/htmlquery", "getQuery") and - this = f.getACall().getArgument(0) - ) - } -} - -// https://github.com/antchfx/xmlquery -class XmlQuerySink extends XPathInjectionSink { - XmlQuerySink() { - exists(Function f, string name | name.matches("Find%") | - f.hasQualifiedName("github.com/antchfx/xmlquery", name) and - this = f.getACall().getArgument(1) - ) - or - exists(Function f, string name | name.matches("Query%") | - f.hasQualifiedName("github.com/antchfx/xmlquery", name) and - this = f.getACall().getArgument(1) - ) - or - exists(Function f, string name | name.matches("Select%") | - f.hasQualifiedName("github.com/antchfx/xmlquery", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f | - f.hasQualifiedName("github.com/antchfx/xmlquery", "getQuery") and - this = f.getACall().getArgument(0) - ) - } -} - -// https://github.com/antchfx/jsonquery -class JsonQuerySink extends XPathInjectionSink { - JsonQuerySink() { - exists(Function f, string name | name.matches("Find%") | - f.hasQualifiedName("github.com/antchfx/jsonquery", name) and - this = f.getACall().getArgument(1) - ) - or - exists(Function f, string name | name.matches("Query%") | - f.hasQualifiedName("github.com/antchfx/jsonquery", name) and - this = f.getACall().getArgument(1) - ) - or - exists(Function f | - f.hasQualifiedName("github.com/antchfx/jsonquery", "getQuery") and - this = f.getACall().getArgument(0) - ) - } -} - -// https://github.com/go-xmlpath/xmlpath -class XmlPathSink extends XPathInjectionSink { - XmlPathSink() { - exists(Function f, string name | name.matches("Compile%") | - f.hasQualifiedName("github.com/go-xmlpath/xmlpath", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f, string name | name.matches("MustCompile%") | - f.hasQualifiedName("github.com/go-xmlpath/xmlpath", name) and - this = f.getACall().getArgument(0) - ) - } -} - -// https://github.com/ChrisTrenkamp/goxpath -class GoXPathSink extends XPathInjectionSink { - GoXPathSink() { - exists(Function f, string name | name.matches("Parse%") | - f.hasQualifiedName("github.com/ChrisTrenkamp/goxpath", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f, string name | name.matches("MustParse%") | - f.hasQualifiedName("github.com/ChrisTrenkamp/goxpath", name) and - this = f.getACall().getArgument(0) - ) - } -} - -// https://github.com/santhosh-tekuri/xpathparser -class XPathParserSink extends XPathInjectionSink { - XPathParserSink() { - exists(Function f, string name | name.matches("Parse%") | - f.hasQualifiedName("github.com/santhosh-tekuri/xpathparser", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f, string name | name.matches("MustParse%") | - f.hasQualifiedName("github.com/santhosh-tekuri/xpathparser", name) and - this = f.getACall().getArgument(0) - ) - } -} - -// https://github.com/moovweb/gokogiri -class GokogiriSink extends XPathInjectionSink { - GokogiriSink() { - exists(Function f, string name | name.matches("Compile%") | - f.hasQualifiedName("github.com/moovweb/gokogiri/xpath", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f, string name | name.matches("Search%") | - f.hasQualifiedName("github.com/moovweb/gokogiri/xml", name) and - this = f.getACall().getArgument(0) - ) - or - exists(Function f, string name | name.matches("EvalXPath%") | - f.hasQualifiedName("github.com/moovweb/gokogiri/xml", name) and - this = f.getACall().getArgument(0) - ) - } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, XPathInjectionConfiguration c -where c.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ flows here and is used in an XPath expression.", - source.getNode(), "A user-provided value"