From 531f2a15a4414441ab19cdb7bc524175430529ff Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 30 Apr 2025 19:58:14 +0200 Subject: [PATCH 1/3] python: model `send_header` from `http.server` --- .../ql/lib/semmle/python/frameworks/Stdlib.qll | 16 ++++++++++++++++ .../frameworks/stdlib/http_server.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index 4ad671bb19aa..2d4bd83a55a1 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -1963,6 +1963,22 @@ module StdlibPrivate { /** Gets a reference to an instance of the `BaseHttpRequestHandler` class or any subclass. */ DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) } + /** A call to a method that writes to a response header. */ + private class HeaderWriteCall extends Http::Server::ResponseHeaderWrite::Range, + DataFlow::MethodCallNode + { + HeaderWriteCall() { this.calls(instance(), "send_header") } + + override DataFlow::Node getNameArg() { result = this.getArg(0) } + + override DataFlow::Node getValueArg() { result = this.getArg(1) } + + // TODO: These checks perhaps could be made more precise. + override predicate nameAllowsNewline() { any() } + + override predicate valueAllowsNewline() { any() } + } + private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { nodeFrom = instance() and diff --git a/python/ql/test/library-tests/frameworks/stdlib/http_server.py b/python/ql/test/library-tests/frameworks/stdlib/http_server.py index 9110aa6a26a9..8e9fd925c249 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/http_server.py +++ b/python/ql/test/library-tests/frameworks/stdlib/http_server.py @@ -83,7 +83,7 @@ def taint_sources(self): def do_GET(self): # $ requestHandler # send_response will log a line to stderr self.send_response(200) - self.send_header("Content-type", "text/plain; charset=utf-8") + self.send_header("Content-type", "text/plain; charset=utf-8") # $ headerWriteNameUnsanitized="Content-type" headerWriteValueUnsanitized="text/plain; charset=utf-8" self.end_headers() self.wfile.write(b"Hello BaseHTTPRequestHandler\n") self.wfile.writelines([b"1\n", b"2\n", b"3\n"]) From cf45e771f3a1bd2b4e91f4dcfef0bda6bcb0b022 Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 30 Apr 2025 20:01:43 +0200 Subject: [PATCH 2/3] python: remove copied comment --- python/ql/lib/semmle/python/frameworks/Stdlib.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index 2d4bd83a55a1..4a3c346fb016 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -1973,7 +1973,6 @@ module StdlibPrivate { override DataFlow::Node getValueArg() { result = this.getArg(1) } - // TODO: These checks perhaps could be made more precise. override predicate nameAllowsNewline() { any() } override predicate valueAllowsNewline() { any() } From e63b38c515db9730258956573ad2784934ef8b54 Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 30 Apr 2025 20:05:55 +0200 Subject: [PATCH 3/3] python: add change note --- python/ql/lib/change-notes/2025-04-30-model-send-header.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 python/ql/lib/change-notes/2025-04-30-model-send-header.md diff --git a/python/ql/lib/change-notes/2025-04-30-model-send-header.md b/python/ql/lib/change-notes/2025-04-30-model-send-header.md new file mode 100644 index 000000000000..032e984bdf3a --- /dev/null +++ b/python/ql/lib/change-notes/2025-04-30-model-send-header.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added header write model for `send_header` in `http.server`. \ No newline at end of file