From 1d36f3f9c640f3cc6eacc09b9fbb8ba9274d48bb Mon Sep 17 00:00:00 2001 From: Thomas Barber Date: Tue, 16 Jan 2024 17:28:02 +0000 Subject: [PATCH 1/3] Foxhound: use xpath to identify DOM elements --- dom/tainting/nsTaintingUtils.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dom/tainting/nsTaintingUtils.cpp b/dom/tainting/nsTaintingUtils.cpp index 4628e311e7077..5c50a542214a8 100644 --- a/dom/tainting/nsTaintingUtils.cpp +++ b/dom/tainting/nsTaintingUtils.cpp @@ -13,6 +13,7 @@ #include #include "jsfriendapi.h" #include "mozilla/dom/ToJSValue.h" +#include "XPathGenerator.h" #include "nsContentUtils.h" #include "nsString.h" #include "mozilla/Logging.h" @@ -83,13 +84,21 @@ static TaintOperation GetTaintOperation(JSContext *cx, const char* name, const n return TaintOperation(name); } +static void DescribeElement(const mozilla::dom::Element* element, nsAString& aInput) +{ + aInput.Truncate(); + if (element) { + XPathGenerator::Generate(element, aInput); + } +} + static TaintOperation GetTaintOperation(JSContext *cx, const char* name, const mozilla::dom::Element* element) { if (element) { nsTArray args; nsAutoString elementDesc; - element->Describe(elementDesc); + DescribeElement(element, elementDesc); args.AppendElement(elementDesc); return GetTaintOperation(cx, name, args); @@ -105,7 +114,7 @@ static TaintOperation GetTaintOperation(JSContext *cx, const char* name, const m nsTArray args; nsAutoString elementDesc; - element->Describe(elementDesc); + DescribeElement(element, elementDesc); args.AppendElement(elementDesc); nsAutoString attributeName; @@ -411,7 +420,7 @@ nsresult ReportTaintSink(const nsAString &str, const char* name, const mozilla:: { nsAutoString elementDesc; if (element) { - element->Describe(elementDesc); + DescribeElement(element, elementDesc); } return ReportTaintSink(str, name, elementDesc); } From b838cb11086751789e17378ddc60a1cf3259634b Mon Sep 17 00:00:00 2001 From: Thomas Barber Date: Tue, 16 Jan 2024 17:28:25 +0000 Subject: [PATCH 2/3] Foxhound: Add XHR headers to tainting arguments --- dom/xhr/XMLHttpRequestMainThread.cpp | 29 +++++++++++++++++++++++++++- dom/xhr/XMLHttpRequestMainThread.h | 4 ++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 42321d8613aae..305938fdd1702 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -565,7 +565,24 @@ void XMLHttpRequestMainThread::GetResponseText(DOMString& aResponseText, } // Taintfox: XMLHttpRequest.response source - MarkTaintSource(aResponseText, "XMLHttpRequest.response"); + nsTArray args; + + nsAutoString url; + if (mRequestURL) { + nsCString cUrl = mRequestURL->GetSpecOrDefault(); + url = NS_ConvertUTF8toUTF16(cUrl); + } + args.AppendElement(url); + + nsAutoCString requestHeaders; + mAuthorRequestHeaders.GetAll(requestHeaders); + args.AppendElement(NS_ConvertUTF8toUTF16(requestHeaders)); + + nsAutoCString responseHeaders; + GetAllResponseHeaders(responseHeaders, aRv); + args.AppendElement(NS_ConvertUTF8toUTF16(responseHeaders)); + + MarkTaintSource(aResponseText, "XMLHttpRequest.response", args); } void XMLHttpRequestMainThread::GetResponseText( @@ -4368,4 +4385,14 @@ bool RequestHeaders::CharsetIterator::Next() { return true; } +void RequestHeaders::GetAll(nsACString& aValue) const { + aValue.Truncate(); + for (const RequestHeaders::RequestHeader& header : mHeaders) { + aValue.Append(header.mName); + aValue.AppendLiteral(": "); + aValue.Append(header.mValue); + aValue.AppendLiteral("\r\n"); + } +} + } // namespace mozilla::dom diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h index c1622ea5cd97a..45da05d3ab984 100644 --- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -176,6 +176,10 @@ class RequestHeaders { void ApplyToChannel(nsIHttpChannel* aChannel, bool aStripRequestBodyHeader, bool aStripAuth) const; void GetCORSUnsafeHeaders(nsTArray& aArray) const; + + // Tainting Helper Function: + void GetAll(nsACString& aValue) const; + }; class nsXHRParseEndListener; From 6dd625795ec7c8b2e9d0ff1ab83838df733cbc2c Mon Sep 17 00:00:00 2001 From: Thomas Barber Date: Wed, 17 Jan 2024 11:10:15 +0000 Subject: [PATCH 3/3] Foxhound: prevent argument shortening for element xpath strings --- dom/tainting/nsTaintingUtils.cpp | 18 +++++++++++++++++- js/src/jsapi.cpp | 10 ++++++++-- js/src/jsapi.h | 4 ++++ js/src/jstaint.cpp | 12 ++++++------ js/src/jstaint.h | 8 ++++---- 5 files changed, 39 insertions(+), 13 deletions(-) diff --git a/dom/tainting/nsTaintingUtils.cpp b/dom/tainting/nsTaintingUtils.cpp index 5c50a542214a8..fdd935bb5dbdc 100644 --- a/dom/tainting/nsTaintingUtils.cpp +++ b/dom/tainting/nsTaintingUtils.cpp @@ -84,11 +84,27 @@ static TaintOperation GetTaintOperation(JSContext *cx, const char* name, const n return TaintOperation(name); } +static TaintOperation GetTaintOperationFullArgs(JSContext *cx, const char* name, const nsTArray &args) +{ + if (cx && JS::CurrentGlobalOrNull(cx)) { + JS::RootedValue argval(cx); + + if (mozilla::dom::ToJSValue(cx, args, &argval)) { + return JS_GetTaintOperationFullArgs(cx, name, argval); + } + } + + return TaintOperation(name); +} + static void DescribeElement(const mozilla::dom::Element* element, nsAString& aInput) { aInput.Truncate(); if (element) { XPathGenerator::Generate(element, aInput); + if (aInput.IsEmpty()) { + element->Describe(aInput); + } } } @@ -101,7 +117,7 @@ static TaintOperation GetTaintOperation(JSContext *cx, const char* name, const m DescribeElement(element, elementDesc); args.AppendElement(elementDesc); - return GetTaintOperation(cx, name, args); + return GetTaintOperationFullArgs(cx, name, args); } return TaintOperation(name); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 033d929449f0f..02c4fac231e0f 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4805,7 +4805,13 @@ JS_MarkTaintSource(JSContext* cx, JS::MutableHandleValue value, const TaintOpera JS_PUBLIC_API TaintOperation JS_GetTaintOperation(JSContext* cx, const char* sink, JS::HandleValue arg) { - return TaintOperationFromContext(cx, sink, false, arg); + return TaintOperationFromContext(cx, sink, false, arg, false); +} + +JS_PUBLIC_API TaintOperation +JS_GetTaintOperationFullArgs(JSContext* cx, const char* sink, JS::HandleValue arg) +{ + return TaintOperationFromContext(cx, sink, false, arg, true); } JS_PUBLIC_API TaintOperation @@ -4863,7 +4869,7 @@ JS_ReportTaintSink(JSContext* cx, JS::HandleString str, const char* sink, JS::Ha JS_ReportWarningUTF8(cx, "Tainted flow from %s into %s!", firstRange.flow().source().name(), sink); // Extend the taint flow to include the sink function - str->taint().extend(TaintOperationFromContext(cx, sink, true, arg)); + str->taint().extend(TaintOperationFromContext(cx, sink, true, arg, true)); // Trigger a custom event that can be caught by an extension. // To simplify things, this part is implemented in JavaScript. Since we don't want to recompile diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 12ef4d77aa176..a8f1da5bda88a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -986,6 +986,10 @@ JS_SetStringTaint(JSContext* cx, JSString* str, const StringTaint& taint); extern JS_PUBLIC_API TaintOperation JS_GetTaintOperation(JSContext* cx, const char* name, JS::HandleValue args); +// Taintfox: Get Taint Operation with no argument length restrictions +extern JS_PUBLIC_API TaintOperation +JS_GetTaintOperationFullArgs(JSContext* cx, const char* name, JS::HandleValue args); + // Taintfox: Create new String Taint Location from the context extern JS_PUBLIC_API TaintOperation JS_GetTaintOperation(JSContext* cx, const char* name); diff --git a/js/src/jstaint.cpp b/js/src/jstaint.cpp index 312900d426f5f..9c2c9476c9f12 100644 --- a/js/src/jstaint.cpp +++ b/js/src/jstaint.cpp @@ -142,12 +142,12 @@ std::u16string JS::taintarg(JSContext* cx, HandleObject obj) return taintarg(cx, str); } -std::u16string JS::taintarg(JSContext* cx, HandleValue val) +std::u16string JS::taintarg(JSContext* cx, HandleValue val, bool fullArgs) { RootedString str(cx, ToString(cx, val)); if (!str) return std::u16string(); - return taintarg(cx, str); + return fullArgs ? taintarg_full(cx, str) : taintarg(cx, str); } std::u16string JS::taintarg(JSContext* cx, int32_t num) @@ -156,7 +156,7 @@ std::u16string JS::taintarg(JSContext* cx, int32_t num) return taintarg(cx, val); } -std::vector JS::taintargs(JSContext* cx, HandleValue val) +std::vector JS::taintargs(JSContext* cx, HandleValue val, bool fullArgs) { std::vector args; bool isArray; @@ -176,7 +176,7 @@ std::vector JS::taintargs(JSContext* cx, HandleValue val) if (!JS_GetElement(cx, array, i, &v)) { continue; } - args.push_back(taintarg(cx, v)); + args.push_back(taintarg(cx, v, fullArgs)); } } else { args.push_back(taintarg(cx, val)); @@ -277,8 +277,8 @@ TaintLocation JS::TaintLocationFromContext(JSContext* cx) return TaintLocation(ascii2utf16(std::string(filename)), line, pos, scriptStartline, hash, taintarg(cx, function)); } -TaintOperation JS::TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleValue args) { - return TaintOperation(name, is_native, TaintLocationFromContext(cx), taintargs(cx, args)); +TaintOperation JS::TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleValue args, bool fullArgs) { + return TaintOperation(name, is_native, TaintLocationFromContext(cx), taintargs(cx, args, fullArgs)); } TaintOperation JS::TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleString arg ) { diff --git a/js/src/jstaint.h b/js/src/jstaint.h index 04aa36ad963f9..767f48c302850 100644 --- a/js/src/jstaint.h +++ b/js/src/jstaint.h @@ -41,13 +41,13 @@ std::u16string taintarg_jsstring_full(JSContext* cx, JSString* const& str); std::u16string taintarg(JSContext* cx, JS::HandleObject obj); // Converts a JS value into an argument string for a taint operation. -std::u16string taintarg(JSContext* cx, JS::HandleValue str); +std::u16string taintarg(JSContext* cx, JS::HandleValue val, bool fullArgs = false); // Converts an integer to a taint argument string. std::u16string taintarg(JSContext* cx, int32_t num); // Converts a JS Handle to a taint argument string. -std::vector taintargs(JSContext* cx, JS::HandleValue str); +std::vector taintargs(JSContext* cx, JS::HandleValue str, bool fullArgs); std::vector taintargs(JSContext* cx, JS::HandleString str); @@ -62,11 +62,11 @@ std::string convertDigestToHexString(const TaintMd5& digest); // Extracts the current filename, linenumber and function from the JSContext TaintLocation TaintLocationFromContext(JSContext* cx); -TaintOperation TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleValue args); +TaintOperation TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleValue args, bool fullArgs = false); TaintOperation TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleString arg); - TaintOperation TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleString arg1, JS::HandleString arg2); +TaintOperation TaintOperationFromContext(JSContext* cx, const char* name, bool is_native, JS::HandleString arg1, JS::HandleString arg2); TaintOperation TaintOperationFromContextJSString(JSContext* cx, const char* name, bool is_native, JSString* const& str);