mirror of
https://github.com/github/codeql.git
synced 2026-05-03 12:45:27 +02:00
Merge branch 'main' into jty/python/emailInjection
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
Sometimes we accidentally re-export too much from `DataFlow` such that for example we can access `Add` from `DataFlow::Add` :disappointed:
|
||||
|
||||
This test should always FAIL to compile!
|
||||
@@ -0,0 +1 @@
|
||||
ERROR: Could not resolve type DataFlow::Add (Test.ql:7,6-19)
|
||||
@@ -0,0 +1,8 @@
|
||||
import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
// Sometimes we accidentally re-export too much from `DataFlow` such that for example we can access `Add` from `DataFlow::Add` :(
|
||||
//
|
||||
// This test should always FAIL to compile!
|
||||
from DataFlow::Add this_should_not_work
|
||||
select this_should_not_work
|
||||
@@ -0,0 +1 @@
|
||||
1+1
|
||||
@@ -8,7 +8,7 @@
|
||||
* s = SOURCE
|
||||
* SINK(s)
|
||||
* ```
|
||||
* `SOURCE` will be a source and the second occurance of `s` will be a sink.
|
||||
* `SOURCE` will be a source and the second occurrence of `s` will be a sink.
|
||||
*
|
||||
* In order to test literals, alternative sources are defined for each type:
|
||||
*
|
||||
|
||||
@@ -49,6 +49,24 @@ def test_create_with_foo():
|
||||
x = create_with_foo() # $ tracked=foo
|
||||
print(x.foo) # $ tracked=foo tracked
|
||||
|
||||
def test_global_attribute_assignment():
|
||||
global global_var
|
||||
global_var.foo = tracked # $ tracked tracked=foo
|
||||
|
||||
def test_global_attribute_read():
|
||||
x = global_var.foo # $ tracked tracked=foo
|
||||
|
||||
def test_local_attribute_assignment():
|
||||
# Same as `test_global_attribute_assignment`, but the assigned variable is not global
|
||||
# In this case, we don't want flow going to the `ModuleVariableNode` for `local_var`
|
||||
# (which is referenced in `test_local_attribute_read`).
|
||||
local_var = object() # $ tracked=foo
|
||||
local_var.foo = tracked # $ tracked tracked=foo
|
||||
|
||||
def test_local_attribute_read():
|
||||
x = local_var.foo
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Attributes assigned statically to a class
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
@@ -5,7 +5,7 @@ import semmle.python.ApiGraphs
|
||||
|
||||
private DataFlow::TypeTrackingNode module_tracker(TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("module").getAUse()
|
||||
result = API::moduleImport("module").getAnImmediateUse()
|
||||
or
|
||||
exists(TypeTracker t2 | result = module_tracker(t2).track(t2, t))
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ class TrackedSelfTest extends InlineExpectationsTest {
|
||||
/** Gets a reference to `foo` (fictive module). */
|
||||
private DataFlow::TypeTrackingNode foo(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("foo").getAUse()
|
||||
result = API::moduleImport("foo").getAnImmediateUse()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = foo(t2).track(t2, t))
|
||||
}
|
||||
@@ -131,7 +131,7 @@ DataFlow::Node foo() { foo(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
/** Gets a reference to `foo.bar` (fictive module). */
|
||||
private DataFlow::TypeTrackingNode foo_bar(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("foo.bar").getAUse()
|
||||
result = API::moduleImport("foo").getMember("bar").getAnImmediateUse()
|
||||
or
|
||||
t.startInAttr("bar") and
|
||||
result = foo()
|
||||
@@ -145,7 +145,7 @@ DataFlow::Node foo_bar() { foo_bar(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
/** Gets a reference to `foo.bar.baz` (fictive attribute on `foo.bar` module). */
|
||||
private DataFlow::TypeTrackingNode foo_bar_baz(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("foo.bar.baz").getAUse()
|
||||
result = API::moduleImport("foo").getMember("bar").getMember("baz").getAnImmediateUse()
|
||||
or
|
||||
t.startInAttr("baz") and
|
||||
result = foo_bar()
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import python
|
||||
import experimental.semmle.python.Concepts
|
||||
import experimental.semmle.python.frameworks.Xml
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
class XmlParsingTest extends InlineExpectationsTest {
|
||||
XmlParsingTest() { this = "XmlParsingTest" }
|
||||
|
||||
override string getARelevantTag() { result in ["input", "vuln"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(XML::XMLParsing parsing |
|
||||
exists(DataFlow::Node input |
|
||||
input = parsing.getAnInput() and
|
||||
location = input.getLocation() and
|
||||
element = input.toString() and
|
||||
value = prettyNodeForInlineTest(input) and
|
||||
tag = "input"
|
||||
)
|
||||
or
|
||||
exists(XML::XMLVulnerabilityKind kind |
|
||||
parsing.vulnerableTo(kind) and
|
||||
location = parsing.getLocation() and
|
||||
element = parsing.toString() and
|
||||
value = "'" + kind + "'" and
|
||||
tag = "vuln"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
from io import StringIO
|
||||
import lxml.etree
|
||||
|
||||
x = "some xml"
|
||||
|
||||
# different parsing methods
|
||||
lxml.etree.fromstring(x) # $ input=x vuln='XXE'
|
||||
lxml.etree.fromstring(text=x) # $ input=x vuln='XXE'
|
||||
|
||||
lxml.etree.fromstringlist([x]) # $ input=List vuln='XXE'
|
||||
lxml.etree.fromstringlist(strings=[x]) # $ input=List vuln='XXE'
|
||||
|
||||
lxml.etree.XML(x) # $ input=x vuln='XXE'
|
||||
lxml.etree.XML(text=x) # $ input=x vuln='XXE'
|
||||
|
||||
lxml.etree.parse(StringIO(x)) # $ input=StringIO(..) vuln='XXE'
|
||||
lxml.etree.parse(source=StringIO(x)) # $ input=StringIO(..) vuln='XXE'
|
||||
|
||||
lxml.etree.parseid(StringIO(x)) # $ input=StringIO(..) vuln='XXE'
|
||||
lxml.etree.parseid(source=StringIO(x)) # $ input=StringIO(..) vuln='XXE'
|
||||
|
||||
# With default parsers (nothing changed)
|
||||
parser = lxml.etree.XMLParser()
|
||||
lxml.etree.fromstring(x, parser=parser) # $ input=x vuln='XXE'
|
||||
|
||||
parser = lxml.etree.get_default_parser()
|
||||
lxml.etree.fromstring(x, parser=parser) # $ input=x vuln='XXE'
|
||||
|
||||
# manual use of feed method
|
||||
parser = lxml.etree.XMLParser()
|
||||
parser.feed(x) # $ input=x vuln='XXE'
|
||||
parser.feed(data=x) # $ input=x vuln='XXE'
|
||||
parser.close()
|
||||
|
||||
# XXE-safe
|
||||
parser = lxml.etree.XMLParser(resolve_entities=False)
|
||||
lxml.etree.fromstring(x, parser) # $ input=x
|
||||
lxml.etree.fromstring(x, parser=parser) # $ input=x
|
||||
|
||||
# XXE-vuln
|
||||
parser = lxml.etree.XMLParser(resolve_entities=True)
|
||||
lxml.etree.fromstring(x, parser=parser) # $ input=x vuln='XXE'
|
||||
|
||||
# Billion laughs vuln (also XXE)
|
||||
parser = lxml.etree.XMLParser(huge_tree=True)
|
||||
lxml.etree.fromstring(x, parser=parser) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup' vuln='XXE'
|
||||
|
||||
# Safe for both Billion laughs and XXE
|
||||
parser = lxml.etree.XMLParser(resolve_entities=False, huge_tree=True)
|
||||
lxml.etree.fromstring(x, parser=parser) # $ input=x
|
||||
|
||||
# DTD retrival vuln (also XXE)
|
||||
parser = lxml.etree.XMLParser(load_dtd=True, no_network=False)
|
||||
lxml.etree.fromstring(x, parser=parser) # $ input=x vuln='DTD retrieval' vuln='XXE'
|
||||
@@ -1,677 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# this file doesn't have a .py extension so the extractor doesn't pick it up, so it
|
||||
# doesn't have to be annotated
|
||||
|
||||
# This file shows the ways to make exploit vulnerable XML parsing
|
||||
# see
|
||||
# https://pypi.org/project/defusedxml/#python-xml-libraries
|
||||
# https://docs.python.org/3.10/library/xml.html#xml-vulnerabilities
|
||||
|
||||
import pathlib
|
||||
from flask import Flask
|
||||
import threading
|
||||
import multiprocessing
|
||||
import time
|
||||
from io import StringIO
|
||||
import pytest
|
||||
|
||||
HOST = "localhost"
|
||||
PORT = 8080
|
||||
|
||||
|
||||
FLAG_PATH = pathlib.Path(__file__).with_name("flag")
|
||||
|
||||
# ==============================================================================
|
||||
# xml samples
|
||||
|
||||
ok_xml = f"""<?xml version="1.0"?>
|
||||
<test>hello world</test>
|
||||
"""
|
||||
|
||||
local_xxe = f"""<?xml version="1.0"?>
|
||||
<!DOCTYPE dt [
|
||||
<!ENTITY xxe SYSTEM "file://{FLAG_PATH}">
|
||||
]>
|
||||
<test>&xxe;</test>
|
||||
"""
|
||||
|
||||
remote_xxe = f"""<?xml version="1.0"?>
|
||||
<!DOCTYPE dt [
|
||||
<!ENTITY remote_xxe SYSTEM "http://{HOST}:{PORT}/xxe">
|
||||
]>
|
||||
<test>&remote_xxe;</test>
|
||||
"""
|
||||
|
||||
billion_laughs = """<?xml version="1.0"?>
|
||||
<!DOCTYPE lolz [
|
||||
<!ENTITY lol "lol">
|
||||
<!ELEMENT lolz (#PCDATA)>
|
||||
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
|
||||
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
|
||||
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
|
||||
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
|
||||
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
|
||||
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
|
||||
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
|
||||
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
|
||||
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
|
||||
]>
|
||||
<lolz>&lol9;</lolz>"""
|
||||
|
||||
quadratic_blowup = f"""<?xml version="1.0"?>
|
||||
<!DOCTYPE wolo [
|
||||
<!ENTITY oops "{"a" * 100000}">
|
||||
]>
|
||||
<foo>{"&oops;"*20000}</foo>"""
|
||||
|
||||
dtd_retrieval = f"""<?xml version="1.0"?>
|
||||
<!DOCTYPE dt PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://{HOST}:{PORT}/test.dtd">
|
||||
<foo>bar</foo>
|
||||
"""
|
||||
|
||||
# ==============================================================================
|
||||
# other setup
|
||||
|
||||
# we set up local Flask application so we can tests whether loading external resources
|
||||
# works (such as SSRF from DTD-retrival works)
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/alive")
|
||||
def alive():
|
||||
return "ok"
|
||||
|
||||
hit_dtd = False
|
||||
@app.route("/test.dtd")
|
||||
def test_dtd():
|
||||
global hit_dtd
|
||||
hit_dtd = True
|
||||
return """<?xml version="1.0" encoding="UTF-8"?>"""
|
||||
|
||||
hit_xxe = False
|
||||
@app.route("/xxe")
|
||||
def test_xxe():
|
||||
global hit_xxe
|
||||
hit_xxe = True
|
||||
return "ok"
|
||||
|
||||
def run_app():
|
||||
app.run(host=HOST, port=PORT)
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def flask_app_running():
|
||||
# run flask in other thread
|
||||
flask_thread = threading.Thread(target=run_app, daemon=True)
|
||||
flask_thread.start()
|
||||
|
||||
# give flask a bit of time to start
|
||||
time.sleep(0.1)
|
||||
|
||||
# ensure that the server works
|
||||
import requests
|
||||
requests.get(f"http://{HOST}:{PORT}/alive")
|
||||
|
||||
yield
|
||||
|
||||
def expects_timeout(func):
|
||||
def inner():
|
||||
proc = multiprocessing.Process(target=func)
|
||||
proc.start()
|
||||
time.sleep(0.1)
|
||||
assert proc.exitcode == None
|
||||
proc.kill()
|
||||
proc.join()
|
||||
return inner
|
||||
|
||||
|
||||
class TestExpectsTimeout:
|
||||
"test that expects_timeout works as expected"
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_slow():
|
||||
time.sleep(1000)
|
||||
|
||||
@staticmethod
|
||||
def test_fast():
|
||||
@expects_timeout
|
||||
def fast_func():
|
||||
return "done!"
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
fast_func()
|
||||
|
||||
# ==============================================================================
|
||||
import xml.sax
|
||||
import xml.sax.handler
|
||||
|
||||
class SimpleHandler(xml.sax.ContentHandler):
|
||||
def __init__(self):
|
||||
self.result = []
|
||||
|
||||
def characters(self, data):
|
||||
self.result.append(data)
|
||||
|
||||
class TestSax():
|
||||
# always vuln to billion laughs, quadratic
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_billion_laughs_allowed_by_default():
|
||||
parser = xml.sax.make_parser()
|
||||
parser.parse(StringIO(billion_laughs))
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_quardratic_blowup_allowed_by_default():
|
||||
parser = xml.sax.make_parser()
|
||||
parser.parse(StringIO(quadratic_blowup))
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml():
|
||||
handler = SimpleHandler()
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setContentHandler(handler)
|
||||
parser.parse(StringIO(ok_xml))
|
||||
assert handler.result == ["hello world"], handler.result
|
||||
|
||||
@staticmethod
|
||||
def test_xxe_disabled_by_default():
|
||||
handler = SimpleHandler()
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setContentHandler(handler)
|
||||
parser.parse(StringIO(local_xxe))
|
||||
assert handler.result == [], handler.result
|
||||
|
||||
@staticmethod
|
||||
def test_local_xxe_manually_enabled():
|
||||
handler = SimpleHandler()
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setContentHandler(handler)
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
parser.parse(StringIO(local_xxe))
|
||||
assert handler.result[0] == "SECRET_FLAG", handler.result
|
||||
|
||||
@staticmethod
|
||||
def test_remote_xxe_manually_enabled():
|
||||
global hit_xxe
|
||||
hit_xxe = False
|
||||
|
||||
handler = SimpleHandler()
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setContentHandler(handler)
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
parser.parse(StringIO(remote_xxe))
|
||||
assert handler.result == ["ok"], handler.result
|
||||
assert hit_xxe == True
|
||||
|
||||
@staticmethod
|
||||
def test_dtd_disabled_by_default():
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
parser = xml.sax.make_parser()
|
||||
parser.parse(StringIO(dtd_retrieval))
|
||||
assert hit_dtd == False
|
||||
|
||||
@staticmethod
|
||||
def test_dtd_manually_enabled():
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
parser.parse(StringIO(dtd_retrieval))
|
||||
assert hit_dtd == True
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
import xml.etree.ElementTree
|
||||
|
||||
class TestEtree:
|
||||
|
||||
# always vuln to billion laughs, quadratic
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_billion_laughs_allowed_by_default():
|
||||
parser = xml.etree.ElementTree.XMLParser()
|
||||
_root = xml.etree.ElementTree.fromstring(billion_laughs, parser=parser)
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_quardratic_blowup_allowed_by_default():
|
||||
parser = xml.etree.ElementTree.XMLParser()
|
||||
_root = xml.etree.ElementTree.fromstring(quadratic_blowup, parser=parser)
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml():
|
||||
parser = xml.etree.ElementTree.XMLParser()
|
||||
root = xml.etree.ElementTree.fromstring(ok_xml, parser=parser)
|
||||
assert root.tag == "test"
|
||||
assert root.text == "hello world"
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml_sax_parser():
|
||||
# you _can_ pass a SAX parser to xml.etree... but it doesn't give you the output :|
|
||||
parser = xml.sax.make_parser()
|
||||
root = xml.etree.ElementTree.fromstring(ok_xml, parser=parser)
|
||||
assert root == None
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml_lxml_parser():
|
||||
# this is technically possible, since parsers follow the same API, and the
|
||||
# `fromstring` function is just a thin wrapper... seems very unlikely that
|
||||
# anyone would do this though :|
|
||||
parser = lxml.etree.XMLParser()
|
||||
root = xml.etree.ElementTree.fromstring(ok_xml, parser=parser)
|
||||
assert root.tag == "test"
|
||||
assert root.text == "hello world"
|
||||
|
||||
@staticmethod
|
||||
def test_xxe_not_possible():
|
||||
parser = xml.etree.ElementTree.XMLParser()
|
||||
try:
|
||||
_root = xml.etree.ElementTree.fromstring(local_xxe, parser=parser)
|
||||
assert False
|
||||
except xml.etree.ElementTree.ParseError as e:
|
||||
assert "undefined entity &xxe" in str(e)
|
||||
|
||||
@staticmethod
|
||||
def test_dtd_not_possible():
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
parser = xml.etree.ElementTree.XMLParser()
|
||||
_root = xml.etree.ElementTree.fromstring(dtd_retrieval, parser=parser)
|
||||
assert hit_dtd == False
|
||||
|
||||
# ==============================================================================
|
||||
import lxml.etree
|
||||
|
||||
class TestLxml:
|
||||
# see https://lxml.de/apidoc/lxml.etree.html?highlight=xmlparser#lxml.etree.XMLParser
|
||||
@staticmethod
|
||||
def test_billion_laughs_disabled_by_default():
|
||||
parser = lxml.etree.XMLParser()
|
||||
try:
|
||||
_root = lxml.etree.fromstring(billion_laughs, parser=parser)
|
||||
assert False
|
||||
except lxml.etree.XMLSyntaxError as e:
|
||||
assert "Detected an entity reference loop" in str(e)
|
||||
|
||||
@staticmethod
|
||||
def test_quardratic_blowup_disabled_by_default():
|
||||
parser = lxml.etree.XMLParser()
|
||||
try:
|
||||
_root = lxml.etree.fromstring(quadratic_blowup, parser=parser)
|
||||
assert False
|
||||
except lxml.etree.XMLSyntaxError as e:
|
||||
assert "Detected an entity reference loop" in str(e)
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_billion_laughs_manually_enabled():
|
||||
parser = lxml.etree.XMLParser(huge_tree=True)
|
||||
root = lxml.etree.fromstring(billion_laughs, parser=parser)
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_quadratic_blowup_manually_enabled():
|
||||
parser = lxml.etree.XMLParser(huge_tree=True)
|
||||
root = lxml.etree.fromstring(quadratic_blowup, parser=parser)
|
||||
|
||||
@staticmethod
|
||||
def test_billion_laughs_huge_tree_not_enough():
|
||||
parser = lxml.etree.XMLParser(huge_tree=True, resolve_entities=False)
|
||||
root = lxml.etree.fromstring(billion_laughs, parser=parser)
|
||||
assert root.tag == "lolz"
|
||||
assert root.text == None
|
||||
|
||||
@staticmethod
|
||||
def test_quadratic_blowup_huge_tree_not_enough():
|
||||
parser = lxml.etree.XMLParser(huge_tree=True, resolve_entities=False)
|
||||
root = lxml.etree.fromstring(quadratic_blowup, parser=parser)
|
||||
assert root.tag == "foo"
|
||||
assert root.text == None
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml():
|
||||
parser = lxml.etree.XMLParser()
|
||||
root = lxml.etree.fromstring(ok_xml, parser=parser)
|
||||
assert root.tag == "test"
|
||||
assert root.text == "hello world"
|
||||
|
||||
@staticmethod
|
||||
def test_local_xxe_enabled_by_default():
|
||||
parser = lxml.etree.XMLParser()
|
||||
root = lxml.etree.fromstring(local_xxe, parser=parser)
|
||||
assert root.tag == "test"
|
||||
assert root.text == "SECRET_FLAG\n", root.text
|
||||
|
||||
@staticmethod
|
||||
def test_local_xxe_disabled():
|
||||
parser = lxml.etree.XMLParser(resolve_entities=False)
|
||||
root = lxml.etree.fromstring(local_xxe, parser=parser)
|
||||
assert root.tag == "test"
|
||||
assert root.text == None
|
||||
|
||||
@staticmethod
|
||||
def test_remote_xxe_disabled_by_default():
|
||||
global hit_xxe
|
||||
hit_xxe = False
|
||||
|
||||
parser = lxml.etree.XMLParser()
|
||||
try:
|
||||
root = lxml.etree.fromstring(remote_xxe, parser=parser)
|
||||
assert False
|
||||
except lxml.etree.XMLSyntaxError as e:
|
||||
assert "Failure to process entity remote_xxe" in str(e)
|
||||
assert hit_xxe == False
|
||||
|
||||
@staticmethod
|
||||
def test_remote_xxe_manually_enabled():
|
||||
global hit_xxe
|
||||
hit_xxe = False
|
||||
|
||||
parser = lxml.etree.XMLParser(no_network=False)
|
||||
root = lxml.etree.fromstring(remote_xxe, parser=parser)
|
||||
assert root.tag == "test"
|
||||
assert root.text == "ok"
|
||||
assert hit_xxe == True
|
||||
|
||||
@staticmethod
|
||||
def test_dtd_disabled_by_default():
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
parser = lxml.etree.XMLParser()
|
||||
root = lxml.etree.fromstring(dtd_retrieval, parser=parser)
|
||||
assert hit_dtd == False
|
||||
|
||||
@staticmethod
|
||||
def test_dtd_manually_enabled():
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
# Need to set BOTH load_dtd and no_network
|
||||
parser = lxml.etree.XMLParser(load_dtd=True)
|
||||
root = lxml.etree.fromstring(dtd_retrieval, parser=parser)
|
||||
assert hit_dtd == False
|
||||
|
||||
parser = lxml.etree.XMLParser(no_network=False)
|
||||
root = lxml.etree.fromstring(dtd_retrieval, parser=parser)
|
||||
assert hit_dtd == False
|
||||
|
||||
parser = lxml.etree.XMLParser(load_dtd=True, no_network=False)
|
||||
root = lxml.etree.fromstring(dtd_retrieval, parser=parser)
|
||||
assert hit_dtd == True
|
||||
|
||||
hit_dtd = False
|
||||
|
||||
# Setting dtd_validation also does not allow the remote access
|
||||
parser = lxml.etree.XMLParser(dtd_validation=True, load_dtd=True)
|
||||
try:
|
||||
root = lxml.etree.fromstring(dtd_retrieval, parser=parser)
|
||||
except lxml.etree.XMLSyntaxError:
|
||||
pass
|
||||
assert hit_dtd == False
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
import xmltodict
|
||||
|
||||
class TestXmltodict:
|
||||
@staticmethod
|
||||
def test_billion_laughs_disabled_by_default():
|
||||
d = xmltodict.parse(billion_laughs)
|
||||
assert d == {"lolz": None}, d
|
||||
|
||||
@staticmethod
|
||||
def test_quardratic_blowup_disabled_by_default():
|
||||
d = xmltodict.parse(quadratic_blowup)
|
||||
assert d == {"foo": None}, d
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_billion_laughs_manually_enabled():
|
||||
xmltodict.parse(billion_laughs, disable_entities=False)
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_quardratic_blowup_manually_enabled():
|
||||
xmltodict.parse(quadratic_blowup, disable_entities=False)
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml():
|
||||
d = xmltodict.parse(ok_xml)
|
||||
assert d == {"test": "hello world"}, d
|
||||
|
||||
@staticmethod
|
||||
def test_local_xxe_not_possible():
|
||||
d = xmltodict.parse(local_xxe)
|
||||
assert d == {"test": None}
|
||||
|
||||
d = xmltodict.parse(local_xxe, disable_entities=False)
|
||||
assert d == {"test": None}
|
||||
|
||||
@staticmethod
|
||||
def test_remote_xxe_not_possible():
|
||||
global hit_xxe
|
||||
hit_xxe = False
|
||||
|
||||
d = xmltodict.parse(remote_xxe)
|
||||
assert d == {"test": None}
|
||||
assert hit_xxe == False
|
||||
|
||||
d = xmltodict.parse(remote_xxe, disable_entities=False)
|
||||
assert d == {"test": None}
|
||||
assert hit_xxe == False
|
||||
|
||||
@staticmethod
|
||||
def test_dtd_not_possible():
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
d = xmltodict.parse(dtd_retrieval)
|
||||
assert hit_dtd == False
|
||||
|
||||
# ==============================================================================
|
||||
import xml.dom.minidom
|
||||
|
||||
class TestMinidom:
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_billion_laughs():
|
||||
xml.dom.minidom.parseString(billion_laughs)
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_quardratic_blowup():
|
||||
xml.dom.minidom.parseString(quadratic_blowup)
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml():
|
||||
doc = xml.dom.minidom.parseString(ok_xml)
|
||||
assert doc.documentElement.tagName == "test"
|
||||
assert doc.documentElement.childNodes[0].data == "hello world"
|
||||
|
||||
@staticmethod
|
||||
def test_xxe():
|
||||
# disabled by default
|
||||
doc = xml.dom.minidom.parseString(local_xxe)
|
||||
assert doc.documentElement.tagName == "test"
|
||||
assert doc.documentElement.childNodes == []
|
||||
|
||||
# but can be turned on
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
doc = xml.dom.minidom.parseString(local_xxe, parser=parser)
|
||||
assert doc.documentElement.tagName == "test"
|
||||
assert doc.documentElement.childNodes[0].data == "SECRET_FLAG"
|
||||
|
||||
# which also works remotely
|
||||
global hit_xxe
|
||||
hit_xxe = False
|
||||
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
_doc = xml.dom.minidom.parseString(remote_xxe, parser=parser)
|
||||
assert hit_xxe == True
|
||||
|
||||
@staticmethod
|
||||
def test_dtd():
|
||||
# not possible by default
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
_doc = xml.dom.minidom.parseString(dtd_retrieval)
|
||||
assert hit_dtd == False
|
||||
|
||||
# but can be turned on
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
_doc = xml.dom.minidom.parseString(dtd_retrieval, parser=parser)
|
||||
assert hit_dtd == True
|
||||
|
||||
# ==============================================================================
|
||||
import xml.dom.pulldom
|
||||
|
||||
class TestPulldom:
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_billion_laughs():
|
||||
doc = xml.dom.pulldom.parseString(billion_laughs)
|
||||
# you NEED to iterate over the items for it to take long
|
||||
for event, node in doc:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_quardratic_blowup():
|
||||
doc = xml.dom.pulldom.parseString(quadratic_blowup)
|
||||
for event, node in doc:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml():
|
||||
doc = xml.dom.pulldom.parseString(ok_xml)
|
||||
for event, node in doc:
|
||||
if event == xml.dom.pulldom.START_ELEMENT:
|
||||
assert node.tagName == "test"
|
||||
elif event == xml.dom.pulldom.CHARACTERS:
|
||||
assert node.data == "hello world"
|
||||
|
||||
@staticmethod
|
||||
def test_xxe():
|
||||
# disabled by default
|
||||
doc = xml.dom.pulldom.parseString(local_xxe)
|
||||
found_flag = False
|
||||
for event, node in doc:
|
||||
if event == xml.dom.pulldom.START_ELEMENT:
|
||||
assert node.tagName == "test"
|
||||
elif event == xml.dom.pulldom.CHARACTERS:
|
||||
if node.data == "SECRET_FLAG":
|
||||
found_flag = True
|
||||
assert found_flag == False
|
||||
|
||||
# but can be turned on
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
doc = xml.dom.pulldom.parseString(local_xxe, parser=parser)
|
||||
found_flag = False
|
||||
for event, node in doc:
|
||||
if event == xml.dom.pulldom.START_ELEMENT:
|
||||
assert node.tagName == "test"
|
||||
elif event == xml.dom.pulldom.CHARACTERS:
|
||||
if node.data == "SECRET_FLAG":
|
||||
found_flag = True
|
||||
assert found_flag == True
|
||||
|
||||
# which also works remotely
|
||||
global hit_xxe
|
||||
hit_xxe = False
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
doc = xml.dom.pulldom.parseString(remote_xxe, parser=parser)
|
||||
assert hit_xxe == False
|
||||
for event, node in doc:
|
||||
pass
|
||||
assert hit_xxe == True
|
||||
|
||||
@staticmethod
|
||||
def test_dtd():
|
||||
# not possible by default
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
doc = xml.dom.pulldom.parseString(dtd_retrieval)
|
||||
for event, node in doc:
|
||||
pass
|
||||
assert hit_dtd == False
|
||||
|
||||
# but can be turned on
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
doc = xml.dom.pulldom.parseString(dtd_retrieval, parser=parser)
|
||||
for event, node in doc:
|
||||
pass
|
||||
assert hit_dtd == True
|
||||
|
||||
# ==============================================================================
|
||||
import xml.parsers.expat
|
||||
|
||||
class TestExpat:
|
||||
# this is the underlying parser implementation used by the rest of the Python
|
||||
# standard library. But people are probably not using this directly.
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_billion_laughs():
|
||||
parser = xml.parsers.expat.ParserCreate()
|
||||
parser.Parse(billion_laughs, True)
|
||||
|
||||
@staticmethod
|
||||
@expects_timeout
|
||||
def test_quardratic_blowup():
|
||||
parser = xml.parsers.expat.ParserCreate()
|
||||
parser.Parse(quadratic_blowup, True)
|
||||
|
||||
@staticmethod
|
||||
def test_ok_xml():
|
||||
char_data_recv = []
|
||||
def char_data_handler(data):
|
||||
char_data_recv.append(data)
|
||||
|
||||
parser = xml.parsers.expat.ParserCreate()
|
||||
parser.CharacterDataHandler = char_data_handler
|
||||
parser.Parse(ok_xml, True)
|
||||
|
||||
assert char_data_recv == ["hello world"]
|
||||
|
||||
@staticmethod
|
||||
def test_xxe():
|
||||
# not vuln by default
|
||||
char_data_recv = []
|
||||
def char_data_handler(data):
|
||||
char_data_recv.append(data)
|
||||
|
||||
parser = xml.parsers.expat.ParserCreate()
|
||||
parser.CharacterDataHandler = char_data_handler
|
||||
parser.Parse(local_xxe, True)
|
||||
|
||||
assert char_data_recv == []
|
||||
|
||||
# there might be ways to make it vuln, but I did not investigate futher.
|
||||
|
||||
@staticmethod
|
||||
def test_dtd():
|
||||
# not vuln by default
|
||||
global hit_dtd
|
||||
hit_dtd = False
|
||||
|
||||
parser = xml.parsers.expat.ParserCreate()
|
||||
parser.Parse(dtd_retrieval, True)
|
||||
assert hit_dtd == False
|
||||
|
||||
# there might be ways to make it vuln, but I did not investigate futher.
|
||||
@@ -1 +0,0 @@
|
||||
SECRET_FLAG
|
||||
@@ -1 +0,0 @@
|
||||
just FYI
|
||||
@@ -1,31 +0,0 @@
|
||||
from io import StringIO
|
||||
import xml.dom.minidom
|
||||
import xml.dom.pulldom
|
||||
import xml.sax
|
||||
|
||||
x = "some xml"
|
||||
|
||||
# minidom
|
||||
xml.dom.minidom.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.dom.minidom.parse(file=StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.dom.minidom.parseString(x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.dom.minidom.parseString(string=x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
|
||||
# pulldom
|
||||
xml.dom.pulldom.parse(StringIO(x))['START_DOCUMENT'][1] # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.dom.pulldom.parse(stream_or_string=StringIO(x))['START_DOCUMENT'][1] # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.dom.pulldom.parseString(x)['START_DOCUMENT'][1] # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.dom.pulldom.parseString(string=x)['START_DOCUMENT'][1] # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
|
||||
# These are based on SAX parses, and you can specify your own, so you can expose yourself to XXE (yay/)
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
xml.dom.minidom.parse(StringIO(x), parser) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
xml.dom.minidom.parse(StringIO(x), parser=parser) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
|
||||
xml.dom.pulldom.parse(StringIO(x), parser) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
xml.dom.pulldom.parse(StringIO(x), parser=parser) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
@@ -1,45 +0,0 @@
|
||||
from io import StringIO
|
||||
import xml.etree.ElementTree
|
||||
|
||||
x = "some xml"
|
||||
|
||||
# Parsing in different ways
|
||||
xml.etree.ElementTree.fromstring(x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.etree.ElementTree.fromstring(text=x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.etree.ElementTree.fromstringlist([x]) # $ input=List vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.etree.ElementTree.fromstringlist(sequence=[x]) # $ input=List vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.etree.ElementTree.XML(x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.etree.ElementTree.XML(text=x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.etree.ElementTree.XMLID(x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.etree.ElementTree.XMLID(text=x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.etree.ElementTree.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.etree.ElementTree.parse(source=StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.etree.ElementTree.iterparse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.etree.ElementTree.iterparse(source=StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
|
||||
# With parsers (no options available to disable/enable security features)
|
||||
parser = xml.etree.ElementTree.XMLParser()
|
||||
xml.etree.ElementTree.fromstring(x, parser=parser) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
# manual use of feed method
|
||||
parser = xml.etree.ElementTree.XMLParser()
|
||||
parser.feed(x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
parser.feed(data=x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
parser.close()
|
||||
|
||||
# manual use of feed method on XMLPullParser
|
||||
parser = xml.etree.ElementTree.XMLPullParser()
|
||||
parser.feed(x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
parser.feed(data=x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
parser.close()
|
||||
|
||||
# note: it's technically possible to use the thing wrapper func `fromstring` with an
|
||||
# `lxml` parser, and thereby change what vulnerabilities you are exposed to.. but it
|
||||
# seems very unlikely that anyone would do this, so we have intentionally not added any
|
||||
# tests for this.
|
||||
@@ -1,64 +0,0 @@
|
||||
from io import StringIO
|
||||
import xml.sax
|
||||
|
||||
x = "some xml"
|
||||
|
||||
class MainHandler(xml.sax.ContentHandler):
|
||||
def __init__(self):
|
||||
self._result = []
|
||||
|
||||
def characters(self, data):
|
||||
self._result.append(data)
|
||||
|
||||
xml.sax.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.sax.parse(source=StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
xml.sax.parseString(x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
xml.sax.parseString(string=x) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
parser = xml.sax.make_parser()
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
parser.parse(source=StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
# You can make it vuln to both XXE and DTD retrieval by setting this flag
|
||||
# see https://docs.python.org/3/library/xml.sax.handler.html#xml.sax.handler.feature_external_ges
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, False)
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
# Forward Type Tracking test
|
||||
def func(cond):
|
||||
parser = xml.sax.make_parser()
|
||||
if cond:
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
else:
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
# make it vuln, then making it safe
|
||||
# a bit of an edge-case, but is nice to be able to handle.
|
||||
parser = xml.sax.make_parser()
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, False)
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
|
||||
def check_conditional_assignment(cond):
|
||||
parser = xml.sax.make_parser()
|
||||
if cond:
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, True)
|
||||
else:
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, False)
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
|
||||
def check_conditional_assignment2(cond):
|
||||
parser = xml.sax.make_parser()
|
||||
if cond:
|
||||
flag_value = True
|
||||
else:
|
||||
flag_value = False
|
||||
parser.setFeature(xml.sax.handler.feature_external_ges, flag_value)
|
||||
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
|
||||
@@ -1,8 +0,0 @@
|
||||
import xmltodict
|
||||
|
||||
x = "some xml"
|
||||
|
||||
xmltodict.parse(x) # $ input=x
|
||||
xmltodict.parse(xml_input=x) # $ input=x
|
||||
|
||||
xmltodict.parse(x, disable_entities=False) # $ input=x vuln='Billion Laughs' vuln='Quadratic Blowup'
|
||||
@@ -539,3 +539,54 @@ class HttpClientRequestTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CsrfProtectionSettingTest extends InlineExpectationsTest {
|
||||
CsrfProtectionSettingTest() { this = "CsrfProtectionSettingTest" }
|
||||
|
||||
override string getARelevantTag() { result = "CsrfProtectionSetting" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(HTTP::Server::CsrfProtectionSetting setting |
|
||||
location = setting.getLocation() and
|
||||
element = setting.toString() and
|
||||
value = setting.getVerificationSetting().toString() and
|
||||
tag = "CsrfProtectionSetting"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CsrfLocalProtectionSettingTest extends InlineExpectationsTest {
|
||||
CsrfLocalProtectionSettingTest() { this = "CsrfLocalProtectionSettingTest" }
|
||||
|
||||
override string getARelevantTag() { result = "CsrfLocalProtection" + ["Enabled", "Disabled"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(HTTP::Server::CsrfLocalProtectionSetting p |
|
||||
location = p.getLocation() and
|
||||
element = p.toString() and
|
||||
value = p.getRequestHandler().getName().toString() and
|
||||
if p.csrfEnabled()
|
||||
then tag = "CsrfLocalProtectionEnabled"
|
||||
else tag = "CsrfLocalProtectionDisabled"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class XmlParsingTest extends InlineExpectationsTest {
|
||||
XmlParsingTest() { this = "XmlParsingTest" }
|
||||
|
||||
override string getARelevantTag() { result = "xmlVuln" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(XML::XmlParsing parsing, XML::XmlParsingVulnerabilityKind kind |
|
||||
parsing.vulnerableTo(kind) and
|
||||
location = parsing.getLocation() and
|
||||
element = parsing.toString() and
|
||||
value = "'" + kind + "'" and
|
||||
tag = "xmlVuln"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ edges
|
||||
| flask_bad.py:35:18:35:24 | ControlFlowNode for request | flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute | flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header |
|
||||
| flask_bad.py:44:44:44:50 | ControlFlowNode for request | flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript |
|
||||
nodes
|
||||
| django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
|
||||
@@ -36,9 +34,6 @@ nodes
|
||||
| flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
|
||||
| flask_bad.py:44:44:44:50 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
subpaths
|
||||
#select
|
||||
| django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | This | django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | user-provided value |
|
||||
@@ -47,4 +42,3 @@ subpaths
|
||||
| flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | flask_bad.py:19:18:19:24 | ControlFlowNode for request | flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | This | flask_bad.py:19:18:19:24 | ControlFlowNode for request | user-provided value |
|
||||
| flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | flask_bad.py:27:18:27:24 | ControlFlowNode for request | flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | This | flask_bad.py:27:18:27:24 | ControlFlowNode for request | user-provided value |
|
||||
| flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | flask_bad.py:35:18:35:24 | ControlFlowNode for request | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | This | flask_bad.py:35:18:35:24 | ControlFlowNode for request | user-provided value |
|
||||
| flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | flask_bad.py:44:44:44:50 | ControlFlowNode for request | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | $@ HTTP header is constructed from a $@. | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | This | flask_bad.py:44:44:44:50 | ControlFlowNode for request | user-provided value |
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| xmlrpc_server.py:7:10:7:48 | ControlFlowNode for SimpleXMLRPCServer() | SimpleXMLRPCServer is vulnerable to XML bombs |
|
||||
@@ -1 +0,0 @@
|
||||
| xmlrpc_server.py:7:10:7:48 | ControlFlowNode for SimpleXMLRPCServer() | SimpleXMLRPCServer is vulnerable to: Billion Laughs, Quadratic Blowup. |
|
||||
@@ -1,27 +0,0 @@
|
||||
edges
|
||||
| test.py:8:19:8:25 | ControlFlowNode for request | test.py:8:19:8:30 | ControlFlowNode for Attribute |
|
||||
| test.py:8:19:8:30 | ControlFlowNode for Attribute | test.py:8:19:8:45 | ControlFlowNode for Subscript |
|
||||
| test.py:8:19:8:45 | ControlFlowNode for Subscript | test.py:9:34:9:44 | ControlFlowNode for xml_content |
|
||||
| test.py:13:19:13:25 | ControlFlowNode for request | test.py:13:19:13:30 | ControlFlowNode for Attribute |
|
||||
| test.py:13:19:13:30 | ControlFlowNode for Attribute | test.py:13:19:13:45 | ControlFlowNode for Subscript |
|
||||
| test.py:13:19:13:45 | ControlFlowNode for Subscript | test.py:15:34:15:44 | ControlFlowNode for xml_content |
|
||||
| test.py:19:19:19:25 | ControlFlowNode for request | test.py:19:19:19:30 | ControlFlowNode for Attribute |
|
||||
| test.py:19:19:19:30 | ControlFlowNode for Attribute | test.py:19:19:19:45 | ControlFlowNode for Subscript |
|
||||
| test.py:19:19:19:45 | ControlFlowNode for Subscript | test.py:30:34:30:44 | ControlFlowNode for xml_content |
|
||||
nodes
|
||||
| test.py:8:19:8:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| test.py:8:19:8:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:8:19:8:45 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:9:34:9:44 | ControlFlowNode for xml_content | semmle.label | ControlFlowNode for xml_content |
|
||||
| test.py:13:19:13:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| test.py:13:19:13:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:13:19:13:45 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:15:34:15:44 | ControlFlowNode for xml_content | semmle.label | ControlFlowNode for xml_content |
|
||||
| test.py:19:19:19:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| test.py:19:19:19:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:19:19:19:45 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:30:34:30:44 | ControlFlowNode for xml_content | semmle.label | ControlFlowNode for xml_content |
|
||||
subpaths
|
||||
#select
|
||||
| test.py:9:34:9:44 | ControlFlowNode for xml_content | test.py:8:19:8:25 | ControlFlowNode for request | test.py:9:34:9:44 | ControlFlowNode for xml_content | $@ XML input is constructed from a $@ and is vulnerable to: XXE. | test.py:9:34:9:44 | ControlFlowNode for xml_content | This | test.py:8:19:8:25 | ControlFlowNode for request | user-provided value |
|
||||
| test.py:30:34:30:44 | ControlFlowNode for xml_content | test.py:19:19:19:25 | ControlFlowNode for request | test.py:30:34:30:44 | ControlFlowNode for xml_content | $@ XML input is constructed from a $@ and is vulnerable to: Billion Laughs, DTD retrieval, Quadratic Blowup, XXE. | test.py:30:34:30:44 | ControlFlowNode for xml_content | This | test.py:19:19:19:25 | ControlFlowNode for request | user-provided value |
|
||||
@@ -1 +0,0 @@
|
||||
experimental/Security/CWE-611/XmlEntityInjection.ql
|
||||
@@ -1,30 +0,0 @@
|
||||
from flask import Flask, request
|
||||
import lxml.etree
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/vuln-handler")
|
||||
def vuln_handler():
|
||||
xml_content = request.args['xml_content']
|
||||
return lxml.etree.fromstring(xml_content).text
|
||||
|
||||
@app.route("/safe-handler")
|
||||
def safe_handler():
|
||||
xml_content = request.args['xml_content']
|
||||
parser = lxml.etree.XMLParser(resolve_entities=False)
|
||||
return lxml.etree.fromstring(xml_content, parser=parser).text
|
||||
|
||||
@app.route("/super-vuln-handler")
|
||||
def super_vuln_handler():
|
||||
xml_content = request.args['xml_content']
|
||||
parser = lxml.etree.XMLParser(
|
||||
# allows XXE
|
||||
resolve_entities=True,
|
||||
# allows remote XXE
|
||||
no_network=False,
|
||||
# together with `no_network=False`, allows DTD-retrival
|
||||
load_dtd=True,
|
||||
# allows DoS attacks
|
||||
huge_tree=True,
|
||||
)
|
||||
return lxml.etree.fromstring(xml_content, parser=parser).text
|
||||
@@ -0,0 +1,63 @@
|
||||
edges
|
||||
| django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring |
|
||||
| django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring |
|
||||
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:21:24:32 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:49:24:60 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:24:21:24:32 | ControlFlowNode for Attribute | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:24:49:24:55 | ControlFlowNode for request | flask_bad.py:24:49:24:60 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:24:49:24:60 | ControlFlowNode for Attribute | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:32:37:32:43 | ControlFlowNode for request | flask_bad.py:32:37:32:48 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:32:37:32:43 | ControlFlowNode for request | flask_bad.py:32:60:32:71 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:32:37:32:48 | ControlFlowNode for Attribute | flask_bad.py:32:37:32:56 | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:32:37:32:56 | ControlFlowNode for Subscript | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring |
|
||||
| flask_bad.py:32:60:32:66 | ControlFlowNode for request | flask_bad.py:32:60:32:71 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:32:60:32:71 | ControlFlowNode for Attribute | flask_bad.py:32:60:32:80 | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:32:60:32:80 | ControlFlowNode for Subscript | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring |
|
||||
nodes
|
||||
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
|
||||
| django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_bad.py:24:21:24:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:24:49:24:55 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_bad.py:24:49:24:60 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
|
||||
| flask_bad.py:32:37:32:43 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_bad.py:32:37:32:48 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:32:37:32:56 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:32:60:32:66 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_bad.py:32:60:32:71 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:32:60:32:80 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
subpaths
|
||||
#select
|
||||
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | user-supplied input |
|
||||
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:24:21:24:27 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:24:21:24:27 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:24:21:24:27 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:24:21:24:27 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:24:21:24:27 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:24:21:24:27 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:24:49:24:55 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:24:49:24:55 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:24:49:24:55 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:24:49:24:55 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:24:49:24:55 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:24:49:24:55 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:32:37:32:43 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:32:37:32:43 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:32:37:32:43 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:32:37:32:43 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:32:37:32:43 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:32:37:32:43 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:32:60:32:66 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:32:60:32:66 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:32:60:32:66 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:32:60:32:66 | ControlFlowNode for request | user-supplied input |
|
||||
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:32:60:32:66 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:32:60:32:66 | ControlFlowNode for request | user-supplied input |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-614/CookieInjection.ql
|
||||
@@ -0,0 +1,30 @@
|
||||
| django_bad.py:6:5:7:52 | ControlFlowNode for Attribute() | Cookie is added without the 'httponly' flag properly set. |
|
||||
| django_bad.py:6:5:7:52 | ControlFlowNode for Attribute() | Cookie is added without the 'samesite' flag properly set. |
|
||||
| django_bad.py:6:5:7:52 | ControlFlowNode for Attribute() | Cookie is added without the 'secure' flag properly set. |
|
||||
| django_bad.py:13:5:13:26 | ControlFlowNode for Subscript | Cookie is added without the 'httponly' flag properly set. |
|
||||
| django_bad.py:13:5:13:26 | ControlFlowNode for Subscript | Cookie is added without the 'samesite' flag properly set. |
|
||||
| django_bad.py:13:5:13:26 | ControlFlowNode for Subscript | Cookie is added without the 'secure' flag properly set. |
|
||||
| django_bad.py:19:5:21:66 | ControlFlowNode for Attribute() | Cookie is added without the 'httponly' flag properly set. |
|
||||
| django_bad.py:19:5:21:66 | ControlFlowNode for Attribute() | Cookie is added without the 'samesite' flag properly set. |
|
||||
| django_bad.py:19:5:21:66 | ControlFlowNode for Attribute() | Cookie is added without the 'secure' flag properly set. |
|
||||
| django_bad.py:27:5:27:26 | ControlFlowNode for Subscript | Cookie is added without the 'httponly' flag properly set. |
|
||||
| django_bad.py:27:5:27:26 | ControlFlowNode for Subscript | Cookie is added without the 'samesite' flag properly set. |
|
||||
| django_bad.py:27:5:27:26 | ControlFlowNode for Subscript | Cookie is added without the 'secure' flag properly set. |
|
||||
| django_good.py:19:5:19:44 | ControlFlowNode for Attribute() | Cookie is added without the 'httponly' flag properly set. |
|
||||
| django_good.py:19:5:19:44 | ControlFlowNode for Attribute() | Cookie is added without the 'samesite' flag properly set. |
|
||||
| django_good.py:19:5:19:44 | ControlFlowNode for Attribute() | Cookie is added without the 'secure' flag properly set. |
|
||||
| flask_bad.py:9:5:10:52 | ControlFlowNode for Attribute() | Cookie is added without the 'httponly' flag properly set. |
|
||||
| flask_bad.py:9:5:10:52 | ControlFlowNode for Attribute() | Cookie is added without the 'samesite' flag properly set. |
|
||||
| flask_bad.py:9:5:10:52 | ControlFlowNode for Attribute() | Cookie is added without the 'secure' flag properly set. |
|
||||
| flask_bad.py:17:5:17:30 | ControlFlowNode for Subscript | Cookie is added without the 'httponly' flag properly set. |
|
||||
| flask_bad.py:17:5:17:30 | ControlFlowNode for Subscript | Cookie is added without the 'samesite' flag properly set. |
|
||||
| flask_bad.py:17:5:17:30 | ControlFlowNode for Subscript | Cookie is added without the 'secure' flag properly set. |
|
||||
| flask_bad.py:24:5:25:52 | ControlFlowNode for Attribute() | Cookie is added without the 'httponly' flag properly set. |
|
||||
| flask_bad.py:24:5:25:52 | ControlFlowNode for Attribute() | Cookie is added without the 'samesite' flag properly set. |
|
||||
| flask_bad.py:24:5:25:52 | ControlFlowNode for Attribute() | Cookie is added without the 'secure' flag properly set. |
|
||||
| flask_bad.py:32:5:32:30 | ControlFlowNode for Subscript | Cookie is added without the 'httponly' flag properly set. |
|
||||
| flask_bad.py:32:5:32:30 | ControlFlowNode for Subscript | Cookie is added without the 'samesite' flag properly set. |
|
||||
| flask_bad.py:32:5:32:30 | ControlFlowNode for Subscript | Cookie is added without the 'secure' flag properly set. |
|
||||
| flask_good.py:23:5:23:57 | ControlFlowNode for Attribute() | Cookie is added without the 'httponly' flag properly set. |
|
||||
| flask_good.py:23:5:23:57 | ControlFlowNode for Attribute() | Cookie is added without the 'samesite' flag properly set. |
|
||||
| flask_good.py:23:5:23:57 | ControlFlowNode for Attribute() | Cookie is added without the 'secure' flag properly set. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-614/InsecureCookie.ql
|
||||
@@ -0,0 +1,28 @@
|
||||
import django.http
|
||||
|
||||
|
||||
def django_response(request):
|
||||
resp = django.http.HttpResponse()
|
||||
resp.set_cookie("name", "value", secure=False,
|
||||
httponly=False, samesite='None')
|
||||
return resp
|
||||
|
||||
|
||||
def django_response():
|
||||
response = django.http.HttpResponse()
|
||||
response['Set-Cookie'] = "name=value; SameSite=None;"
|
||||
return response
|
||||
|
||||
|
||||
def django_response(request):
|
||||
resp = django.http.HttpResponse()
|
||||
resp.set_cookie(django.http.request.GET.get("name"),
|
||||
django.http.request.GET.get("value"),
|
||||
secure=False, httponly=False, samesite='None')
|
||||
return resp
|
||||
|
||||
|
||||
def django_response():
|
||||
response = django.http.HttpResponse()
|
||||
response['Set-Cookie'] = f"{django.http.request.GET.get('name')}={django.http.request.GET.get('value')}; SameSite=None;"
|
||||
return response
|
||||
@@ -0,0 +1,20 @@
|
||||
import django.http
|
||||
|
||||
|
||||
def django_response(request):
|
||||
resp = django.http.HttpResponse()
|
||||
resp['Set-Cookie'] = "name=value; Secure; HttpOnly; SameSite=Lax;"
|
||||
return resp
|
||||
|
||||
|
||||
def django_response(request):
|
||||
resp = django.http.HttpResponse()
|
||||
resp.set_cookie("name", "value", secure=True,
|
||||
httponly=True, samesite='Lax')
|
||||
return resp
|
||||
|
||||
|
||||
def indeterminate(secure):
|
||||
resp = django.http.HttpResponse()
|
||||
resp.set_cookie("name", "value", secure)
|
||||
return resp
|
||||
@@ -0,0 +1,37 @@
|
||||
from flask import Flask, request, make_response, Response
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/1")
|
||||
def false():
|
||||
resp = make_response()
|
||||
resp.set_cookie("name", value="value", secure=False,
|
||||
httponly=False, samesite='None')
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/2")
|
||||
def flask_Response():
|
||||
resp = Response()
|
||||
resp.headers['Set-Cookie'] = "name=value; SameSite=None;"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/3")
|
||||
def false():
|
||||
resp = make_response()
|
||||
resp.set_cookie(request.args["name"], value=request.args["value"], secure=False,
|
||||
httponly=False, samesite='None')
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/4")
|
||||
def flask_Response():
|
||||
resp = Response()
|
||||
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['value']}; SameSite=None;"
|
||||
return resp
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,28 @@
|
||||
from flask import Flask, request, make_response, Response
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/1")
|
||||
def true():
|
||||
resp = make_response()
|
||||
resp.set_cookie("name", value="value", secure=True,
|
||||
httponly=True, samesite='Lax')
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/2")
|
||||
def flask_Response():
|
||||
resp = Response()
|
||||
resp.headers['Set-Cookie'] = "name=value; Secure; HttpOnly; SameSite=Lax;"
|
||||
return resp
|
||||
|
||||
|
||||
def indeterminate(secure):
|
||||
resp = make_response()
|
||||
resp.set_cookie("name", value="value", secure=secure)
|
||||
return resp
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -44,11 +44,19 @@ edges
|
||||
| mongoengine_bad.py:57:21:57:42 | ControlFlowNode for Subscript | mongoengine_bad.py:58:30:58:42 | ControlFlowNode for unsafe_search |
|
||||
| mongoengine_bad.py:58:19:58:43 | ControlFlowNode for Attribute() | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict |
|
||||
| mongoengine_bad.py:58:30:58:42 | ControlFlowNode for unsafe_search | mongoengine_bad.py:58:19:58:43 | ControlFlowNode for Attribute() |
|
||||
| pymongo_bad.py:11:21:11:27 | ControlFlowNode for request | pymongo_bad.py:11:21:11:32 | ControlFlowNode for Attribute |
|
||||
| pymongo_bad.py:11:21:11:32 | ControlFlowNode for Attribute | pymongo_bad.py:11:21:11:42 | ControlFlowNode for Subscript |
|
||||
| pymongo_bad.py:11:21:11:42 | ControlFlowNode for Subscript | pymongo_bad.py:12:30:12:42 | ControlFlowNode for unsafe_search |
|
||||
| pymongo_bad.py:12:19:12:43 | ControlFlowNode for Attribute() | pymongo_bad.py:14:42:14:62 | ControlFlowNode for Dict |
|
||||
| pymongo_bad.py:12:30:12:42 | ControlFlowNode for unsafe_search | pymongo_bad.py:12:19:12:43 | ControlFlowNode for Attribute() |
|
||||
| pymongo_test.py:12:21:12:27 | ControlFlowNode for request | pymongo_test.py:12:21:12:32 | ControlFlowNode for Attribute |
|
||||
| pymongo_test.py:12:21:12:32 | ControlFlowNode for Attribute | pymongo_test.py:12:21:12:42 | ControlFlowNode for Subscript |
|
||||
| pymongo_test.py:12:21:12:42 | ControlFlowNode for Subscript | pymongo_test.py:13:30:13:42 | ControlFlowNode for unsafe_search |
|
||||
| pymongo_test.py:13:19:13:43 | ControlFlowNode for Attribute() | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict |
|
||||
| pymongo_test.py:13:30:13:42 | ControlFlowNode for unsafe_search | pymongo_test.py:13:19:13:43 | ControlFlowNode for Attribute() |
|
||||
| pymongo_test.py:29:16:29:51 | ControlFlowNode for Attribute() | pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict |
|
||||
| pymongo_test.py:29:27:29:33 | ControlFlowNode for request | pymongo_test.py:29:27:29:38 | ControlFlowNode for Attribute |
|
||||
| pymongo_test.py:29:27:29:38 | ControlFlowNode for Attribute | pymongo_test.py:29:27:29:50 | ControlFlowNode for Subscript |
|
||||
| pymongo_test.py:29:27:29:50 | ControlFlowNode for Subscript | pymongo_test.py:29:16:29:51 | ControlFlowNode for Attribute() |
|
||||
| pymongo_test.py:39:16:39:51 | ControlFlowNode for Attribute() | pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict |
|
||||
| pymongo_test.py:39:27:39:33 | ControlFlowNode for request | pymongo_test.py:39:27:39:38 | ControlFlowNode for Attribute |
|
||||
| pymongo_test.py:39:27:39:38 | ControlFlowNode for Attribute | pymongo_test.py:39:27:39:50 | ControlFlowNode for Subscript |
|
||||
| pymongo_test.py:39:27:39:50 | ControlFlowNode for Subscript | pymongo_test.py:39:16:39:51 | ControlFlowNode for Attribute() |
|
||||
nodes
|
||||
| flask_mongoengine_bad.py:19:21:19:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_mongoengine_bad.py:19:21:19:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
@@ -104,12 +112,22 @@ nodes
|
||||
| mongoengine_bad.py:58:19:58:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| mongoengine_bad.py:58:30:58:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search |
|
||||
| mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
|
||||
| pymongo_bad.py:11:21:11:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| pymongo_bad.py:11:21:11:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| pymongo_bad.py:11:21:11:42 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| pymongo_bad.py:12:19:12:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| pymongo_bad.py:12:30:12:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search |
|
||||
| pymongo_bad.py:14:42:14:62 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
|
||||
| pymongo_test.py:12:21:12:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| pymongo_test.py:12:21:12:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| pymongo_test.py:12:21:12:42 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| pymongo_test.py:13:19:13:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| pymongo_test.py:13:30:13:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search |
|
||||
| pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
|
||||
| pymongo_test.py:29:16:29:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| pymongo_test.py:29:27:29:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| pymongo_test.py:29:27:29:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| pymongo_test.py:29:27:29:50 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
|
||||
| pymongo_test.py:39:16:39:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| pymongo_test.py:39:27:39:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| pymongo_test.py:39:27:39:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| pymongo_test.py:39:27:39:50 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
|
||||
subpaths
|
||||
#select
|
||||
| flask_mongoengine_bad.py:22:34:22:44 | ControlFlowNode for json_search | flask_mongoengine_bad.py:19:21:19:27 | ControlFlowNode for request | flask_mongoengine_bad.py:22:34:22:44 | ControlFlowNode for json_search | $@ NoSQL query contains an unsanitized $@ | flask_mongoengine_bad.py:22:34:22:44 | ControlFlowNode for json_search | This | flask_mongoengine_bad.py:19:21:19:27 | ControlFlowNode for request | user-provided value |
|
||||
@@ -121,4 +139,6 @@ subpaths
|
||||
| mongoengine_bad.py:46:26:46:46 | ControlFlowNode for Dict | mongoengine_bad.py:42:21:42:27 | ControlFlowNode for request | mongoengine_bad.py:46:26:46:46 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | mongoengine_bad.py:46:26:46:46 | ControlFlowNode for Dict | This | mongoengine_bad.py:42:21:42:27 | ControlFlowNode for request | user-provided value |
|
||||
| mongoengine_bad.py:53:34:53:44 | ControlFlowNode for json_search | mongoengine_bad.py:50:21:50:27 | ControlFlowNode for request | mongoengine_bad.py:53:34:53:44 | ControlFlowNode for json_search | $@ NoSQL query contains an unsanitized $@ | mongoengine_bad.py:53:34:53:44 | ControlFlowNode for json_search | This | mongoengine_bad.py:50:21:50:27 | ControlFlowNode for request | user-provided value |
|
||||
| mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | mongoengine_bad.py:57:21:57:27 | ControlFlowNode for request | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | This | mongoengine_bad.py:57:21:57:27 | ControlFlowNode for request | user-provided value |
|
||||
| pymongo_bad.py:14:42:14:62 | ControlFlowNode for Dict | pymongo_bad.py:11:21:11:27 | ControlFlowNode for request | pymongo_bad.py:14:42:14:62 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_bad.py:14:42:14:62 | ControlFlowNode for Dict | This | pymongo_bad.py:11:21:11:27 | ControlFlowNode for request | user-provided value |
|
||||
| pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | pymongo_test.py:12:21:12:27 | ControlFlowNode for request | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | This | pymongo_test.py:12:21:12:27 | ControlFlowNode for request | user-provided value |
|
||||
| pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | pymongo_test.py:29:27:29:33 | ControlFlowNode for request | pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | This | pymongo_test.py:29:27:29:33 | ControlFlowNode for request | user-provided value |
|
||||
| pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | pymongo_test.py:39:27:39:33 | ControlFlowNode for request | pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | This | pymongo_test.py:39:27:39:33 | ControlFlowNode for request | user-provided value |
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
from flask import Flask, request
|
||||
from pymongo import MongoClient
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
client = MongoClient()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def home_page():
|
||||
unsafe_search = request.args['search']
|
||||
json_search = json.loads(unsafe_search)
|
||||
|
||||
return client.db.collection.find_one({'data': json_search})
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -1,19 +0,0 @@
|
||||
from flask import Flask, request
|
||||
from pymongo import MongoClient
|
||||
from mongosanitizer.sanitizer import sanitize
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
client = MongoClient()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def home_page():
|
||||
unsafe_search = request.args['search']
|
||||
json_search = json.loads(unsafe_search)
|
||||
safe_search = sanitize(json_search)
|
||||
|
||||
return client.db.collection.find_one({'data': safe_search})
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,47 @@
|
||||
from flask import Flask, request
|
||||
from pymongo import MongoClient
|
||||
from mongosanitizer.sanitizer import sanitize
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
client = MongoClient()
|
||||
|
||||
|
||||
@app.route("/bad")
|
||||
def bad():
|
||||
unsafe_search = request.args['search']
|
||||
json_search = json.loads(unsafe_search)
|
||||
|
||||
return client.db.collection.find_one({'data': json_search})
|
||||
|
||||
|
||||
@app.route("/good")
|
||||
def good():
|
||||
unsafe_search = request.args['search']
|
||||
json_search = json.loads(unsafe_search)
|
||||
safe_search = sanitize(json_search)
|
||||
|
||||
return client.db.collection.find_one({'data': safe_search})
|
||||
|
||||
|
||||
@app.route("/bad2")
|
||||
def bad2():
|
||||
event_id = json.loads(request.args['event_id'])
|
||||
client = MongoClient("localhost", 27017, maxPoolSize=50)
|
||||
db = client.localhost
|
||||
collection = db['collection']
|
||||
cursor = collection.find_one({"$where": f"this._id == '${event_id}'"})
|
||||
|
||||
|
||||
@app.route("/bad3")
|
||||
def bad3():
|
||||
# using `get_` methods instead of subscript/attribute lookups
|
||||
event_id = json.loads(request.args['event_id'])
|
||||
client = MongoClient("localhost", 27017, maxPoolSize=50)
|
||||
db = client.get_database(name="localhost")
|
||||
collection = db.get_collection("collection")
|
||||
cursor = collection.find_one({"$where": f"this._id == '${event_id}'"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
Reference in New Issue
Block a user