Skip to content

Commit

Permalink
Foxhound: adding JSON path string to JSON parse operations
Browse files Browse the repository at this point in the history
  • Loading branch information
tmbrbr committed Feb 13, 2024
1 parent 3887f79 commit 2d2b8e3
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 16 deletions.
44 changes: 44 additions & 0 deletions js/src/tests/non262/taint/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,50 @@ function JSONTaintTest() {
assertEq(hello, "hello");
assertFullTainted(hello);
assertLastTaintOperationEquals(hello, 'JSON.parse');

// From here: https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html
var jsonString = `
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
`;
t = String.tainted(jsonString);
var o = JSON.parse(t);
cat = o.store.book[1].category;
assertEq(cat.taint[0].flow[0].arguments[0], "$.store.book[1].category");
assertEq(cat, "fiction");
assertFullTainted(cat);
assertLastTaintOperationEquals(cat, 'JSON.parse');


}

runTaintTest(JSONTaintTest);
Expand Down
68 changes: 60 additions & 8 deletions js/src/vm/JSONParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ template <typename CharT, typename ParserT, typename StringBuilderT>
template <JSONStringType ST>
JSONToken JSONTokenizer<CharT, ParserT, StringBuilderT>::stringToken(
const CharPtr start, size_t length, const StringTaint& taint) {
if (!parser->handler.template setStringValue<ST>(start, length, taint)) {
if (!parser->handler.template setStringValue<ST, ParserT>(start, length, taint, parser)) {
return JSONToken::OOM;
}
return JSONToken::String;
Expand All @@ -71,7 +71,7 @@ template <typename CharT, typename ParserT, typename StringBuilderT>
template <JSONStringType ST>
JSONToken JSONTokenizer<CharT, ParserT, StringBuilderT>::stringToken(
StringBuilderT& builder) {
if (!parser->handler.template setStringValue<ST>(builder)) {
if (!parser->handler.template setStringValue<ST, ParserT>(builder, parser)) {
return JSONToken::OOM;
}
return JSONToken::String;
Expand Down Expand Up @@ -648,15 +648,61 @@ void JSONParser<CharT>::trace(JSTracer* trc) {
}
}

JSString* JSONFullParseHandlerAnyChar::CurrentJsonPath(const Vector<StackEntry, 10>& stack) const {
// https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html

JSStringBuilder builder(cx);

if (!builder.append(u'$')) {
return nullptr;
}
for (auto& elem : stack) {
if (elem.state == JSONParserState::FinishArrayElement) {
if (!builder.append(u'[')) {
return nullptr;
}
size_t l = elem.elements().length(); // JS::GCVector<JS::Value, 20>;
JSString* str = js::IndexToString(cx, l);
if (!builder.append(str)) {
return nullptr;
}
if (!builder.append(u']')) {
return nullptr;
}
} else {
if (!builder.append(u'.')) {
return nullptr;
}
jsid id = elem.properties().back().id;
if (id.isString()) {
JSString* str = id.toString();
if (str) {
if (!builder.append(str)) {
return nullptr;
}
} else {
return nullptr;
}
} else {
if (!builder.append(u'!')) {
return nullptr;
}
}
}
}
return builder.finishString();
}

inline void JSONFullParseHandlerAnyChar::setNumberValue(double d) {
v = JS::NumberValue(d);
}

template <typename CharT>
template <JSONStringType ST>
template <JSONStringType ST, typename ParserT>
inline bool JSONFullParseHandler<CharT>::setStringValue(CharPtr start,
size_t length,
const StringTaint& taint) {
const StringTaint& taint,
const ParserT* parser) {
JSString* str;
if constexpr (ST == JSONStringType::PropertyName) {
str = AtomizeChars(cx, start.get(), length);
Expand All @@ -671,17 +717,20 @@ inline bool JSONFullParseHandler<CharT>::setStringValue(CharPtr start,
// TaintFox: propagate taint.
if (ST != JSONStringType::PropertyName && taint.hasTaint()) {
str->setTaint(cx, taint);
str->taint().extend(TaintOperationFromContext(cx, "JSON.parse", true));
TaintOperation op = parser ?
TaintOperationFromContextJSString(cx, "JSON.parse", true, parser->CurrentJsonPath()) :
TaintOperationFromContext(cx, "JSON.parse", true);
str->taint().extend(op);
}

v = JS::StringValue(str);
return true;
}

template <typename CharT>
template <JSONStringType ST>
template <JSONStringType ST, typename ParserT>
inline bool JSONFullParseHandler<CharT>::setStringValue(
StringBuilder& builder) {
StringBuilder& builder, const ParserT* parser) {
JSString* str;
if constexpr (ST == JSONStringType::PropertyName) {
str = builder.buffer.finishAtom();
Expand All @@ -695,7 +744,10 @@ inline bool JSONFullParseHandler<CharT>::setStringValue(

// TaintFox: Add taint operation.
if (str->taint().hasTaint()) {
str->taint().extend(TaintOperationFromContext(cx, "JSON.parse", true));
TaintOperation op = parser ?
TaintOperationFromContextJSString(cx, "JSON.parse", true, parser->CurrentJsonPath()) :
TaintOperationFromContext(cx, "JSON.parse", true);
str->taint().extend(op);
}

v = JS::StringValue(str);
Expand Down
32 changes: 24 additions & 8 deletions js/src/vm/JSONParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ class MOZ_STACK_CLASS JSONFullParseHandlerAnyChar {
return *static_cast<PropertyVector*>(vector);
}

const ElementVector& elements() const {
MOZ_ASSERT(state == JSONParserState::FinishArrayElement);
return *static_cast<const ElementVector*>(vector);
}

const PropertyVector& properties() const {
MOZ_ASSERT(state == JSONParserState::FinishObjectMember);
return *static_cast<const PropertyVector*>(vector);
}

explicit StackEntry(ElementVector* elements)
: state(JSONParserState::FinishArrayElement), vector(elements) {}

Expand Down Expand Up @@ -276,6 +286,8 @@ class MOZ_STACK_CLASS JSONFullParseHandlerAnyChar {
inline void freeStackEntry(StackEntry& entry);

void trace(JSTracer* trc);

JSString* CurrentJsonPath(const Vector<StackEntry, 10>& stack) const;
};

template <typename CharT>
Expand Down Expand Up @@ -305,10 +317,10 @@ class MOZ_STACK_CLASS JSONFullParseHandler
JSONFullParseHandler(const JSONFullParseHandler& other) = delete;
void operator=(const JSONFullParseHandler& other) = delete;

template <JSONStringType ST>
inline bool setStringValue(CharPtr start, size_t length, const StringTaint& taint);
template <JSONStringType ST>
inline bool setStringValue(StringBuilder& builder);
template <JSONStringType ST, typename ParserT>
inline bool setStringValue(CharPtr start, size_t length, const StringTaint& taint, const ParserT* parser);
template <JSONStringType ST, typename ParserT>
inline bool setStringValue(StringBuilder& builder, const ParserT* parser);

void reportError(const char* msg, const char* lineString,
const char* columnString);
Expand Down Expand Up @@ -357,13 +369,13 @@ class MOZ_STACK_CLASS JSONSyntaxParseHandler {

FrontendContext* context() { return fc; }

template <JSONStringType ST>
inline bool setStringValue(CharPtr start, size_t length, const StringTaint& taint) {
template <JSONStringType ST, typename ParserT>
inline bool setStringValue(CharPtr start, size_t length, const StringTaint& taint, const ParserT* parser) {
return true;
}

template <JSONStringType ST>
inline bool setStringValue(StringBuilder& builder) {
template <JSONStringType ST, typename ParserT>
inline bool setStringValue(StringBuilder& builder, const ParserT* parser) {
return true;
}

Expand Down Expand Up @@ -406,6 +418,8 @@ class MOZ_STACK_CLASS JSONSyntaxParseHandler {

void reportError(const char* msg, const char* lineString,
const char* columnString);

JSString* CurrentJsonPath(const Vector<StackEntry, 10>& stack) const { return nullptr; }
};

template <typename CharT, typename HandlerT>
Expand Down Expand Up @@ -449,6 +463,8 @@ class MOZ_STACK_CLASS JSONPerHandlerParser {
void outOfMemory();

void error(const char* msg);

JSString* CurrentJsonPath() const { return handler.CurrentJsonPath(stack); }
};

template <typename CharT>
Expand Down

0 comments on commit 2d2b8e3

Please sign in to comment.