From 86c6d7011c50c365b3ea7e6dca9620296605741b Mon Sep 17 00:00:00 2001 From: Remco Vermeulen Date: Wed, 6 Dec 2023 11:40:24 -0800 Subject: [PATCH 1/3] Associate resource root with core script --- .../javascript/frameworks/ui5/UI5.qll | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll index c0686c0f6..d635e9208 100644 --- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll +++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll @@ -15,13 +15,12 @@ module UI5 { } private newtype TResourceRoot = - MkResourceRoot(string name, string root, string source) { + MkResourceRoot(string name, string root, SapUiCoreScriptElement coreScript) { exists( JsonParser::JsonObject config, - JsonParser::JsonMember configEntry, SapUiCoreScriptElement coreScript + JsonParser::JsonMember configEntry | - source = coreScript.getAttributeByName("data-sap-ui-resourceroots").getValue() and - source = config.getSource() and + config.getSource() = coreScript.getAttributeByName("data-sap-ui-resourceroots").getValue() and config.getAMember() = configEntry | name = configEntry.getKey() and @@ -34,7 +33,7 @@ module UI5 { string getRoot() { this = MkResourceRoot(_, result, _) } - string getSource() { this = MkResourceRoot(_, _, result) } + SapUiCoreScriptElement getCoreScript() { this = MkResourceRoot(_, _, result) } string toString() { result = this.getName() + ": " + this.getRoot() } } @@ -50,8 +49,8 @@ module UI5 { result = unresolvedRoot.getName() } - string getSource() { - result = unresolvedRoot.getSource() + SapUiCoreScriptElement getCoreScript() { + result = unresolvedRoot.getCoreScript() } predicate contains(File file) { @@ -69,11 +68,11 @@ module UI5 { } ResourceRoot getAResourceRoot() { - result.getSource() = this.getAttributeByName("data-sap-ui-resourceroots").getValue() + result.getCoreScript() = this } ResolvedResourceRoot getAResolvedResourceRoot() { - result.getSource() = this.getAttributeByName("data-sap-ui-resourceroots").getValue() + result.getCoreScript() = this } } From 5e04a740c8e49b6b6e26364933c18f2a66112f59 Mon Sep 17 00:00:00 2001 From: Remco Vermeulen Date: Wed, 6 Dec 2023 16:00:30 -0800 Subject: [PATCH 2/3] Rewrite interface to have more precise source information --- .../javascript/frameworks/ui5/JsonParser.qll | 1010 ++++++++--------- .../ui5/test/lib/JsonParser/JsonParser.ql | 16 +- 2 files changed, 515 insertions(+), 511 deletions(-) diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/JsonParser.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/JsonParser.qll index 622ea6bdb..26c6565c5 100644 --- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/JsonParser.qll +++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/JsonParser.qll @@ -1,573 +1,573 @@ -signature string getJsonSig(); - -/** - * A naive Json parser without error recovery. - */ -module JsonParser { - private newtype TJsonToken = - MkLeftBracketToken(int begin, int end, string value, string source) { - source = getJson() and - begin = source.indexOf("{") and - begin = end and - value = "{" - } or - MkRightBracketToken(int begin, int end, string value, string source) { - source = getJson() and - begin = source.indexOf("}") and - begin = end and - value = "}" - } or - MkLeftSquareBracketToken(int begin, int end, string value, string source) { - source = getJson() and - begin = source.indexOf("[") and - begin = end and - value = "[" - } or - MkRightSquareBracketToken(int begin, int end, string value, string source) { - source = getJson() and - begin = source.indexOf("]") and - begin = end and - value = "]" - } or - MkWhiteSpaceToken(int begin, int end, string value, string source) { - source = getJson() and - value = source.regexpFind("[\\s\\v\\h]", _, begin) and - begin + value.length() - 1 = end - } or - MkCommaToken(int begin, int end, string value, string source) { - source = getJson() and - begin = source.indexOf(",") and - begin = end and - value = "," - } or - MkColonToken(int begin, int end, string value, string source) { - source = getJson() and - begin = source.indexOf(":") and - begin = end and - value = ":" - } or - MkNumberToken(int begin, int end, string value, string source) { - source = getJson() and - value = source.regexpFind("-?[1-9]\\d*(\\.\\d+)?((e|E)?(\\+|-)?\\d+)?", _, begin) and - begin + value.length() - 1 = end - } or - MkStringToken(int begin, int end, string value, string source) { - source = getJson() and - exists(string literal | - literal = source.regexpFind("(?s)\".*?(? { + class JsonReader extends Source { + string getJson(); } + } - string getValue() { - this = MkLeftBracketToken(_, _, result, _) - or - this = MkRightBracketToken(_, _, result, _) - or - this = MkLeftSquareBracketToken(_, _, result, _) - or - this = MkRightSquareBracketToken(_, _, result, _) - or - this = MkWhiteSpaceToken(_, _, result, _) - or - this = MkCommaToken(_, _, result, _) - or - this = MkColonToken(_, _, result, _) - or - this = MkNumberToken(_, _, result, _) - or - this = MkStringToken(_, _, result, _) - or - this = MkTrueToken(_, _, result, _) - or - this = MkFalseToken(_, _, result, _) - or - this = MkNullToken(_, _, result, _) - } + /** + * A naive Json parser without error recovery. + */ + module Make MakeJsonReader> { + private newtype TJsonToken = + MkLeftBracketToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + begin = reader.getJson().indexOf("{") and + begin = end and + value = "{" + } or + MkRightBracketToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + begin = reader.getJson().indexOf("}") and + begin = end and + value = "}" + } or + MkLeftSquareBracketToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + begin = reader.getJson().indexOf("[") and + begin = end and + value = "[" + } or + MkRightSquareBracketToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + begin = reader.getJson().indexOf("]") and + begin = end and + value = "]" + } or + MkWhiteSpaceToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + value = reader.getJson().regexpFind("[\\s\\v\\h]", _, begin) and + begin + value.length() - 1 = end + } or + MkCommaToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + begin = reader.getJson().indexOf(",") and + begin = end and + value = "," + } or + MkColonToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + begin = reader.getJson().indexOf(":") and + begin = end and + value = ":" + } or + MkNumberToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + value = reader.getJson().regexpFind("-?[1-9]\\d*(\\.\\d+)?((e|E)?(\\+|-)?\\d+)?", _, begin) and + begin + value.length() - 1 = end + } or + MkStringToken(int begin, int end, string value, MakeJsonReader::JsonReader reader) { + exists(string literal | + literal = reader.getJson().regexpFind("(?s)\".*?(? t.getEnd() + private class StringToken extends JsonToken, MkStringToken { + predicate contains(JsonToken t) { + this.getReader() = t.getReader() and + this.getBegin() < t.getBegin() and + this.getEnd() > t.getEnd() + } } - } - private class NumberToken extends JsonToken, MkNumberToken { } + private class NumberToken extends JsonToken, MkNumberToken { } - private class TrueToken extends JsonToken, MkTrueToken { } + private class TrueToken extends JsonToken, MkTrueToken { } - private class FalseToken extends JsonToken, MkFalseToken { } + private class FalseToken extends JsonToken, MkFalseToken { } - private class NullToken extends JsonToken, MkNullToken { } + private class NullToken extends JsonToken, MkNullToken { } - private JsonToken getNextSkippingWhitespace(JsonToken t) { - result = t.getNext() and - not result instanceof WhiteSpaceToken - or - exists(WhiteSpaceToken ws | ws = t.getNext() | result = getNextSkippingWhitespace(ws)) - } - - private newtype TJsonMemberList = - EmptyMemberList() or - ConsMemberList(JsonMember head, JsonMemberList tail) { - exists(JsonToken first, JsonToken last | - mkJsonMember(first, head, last) and - if getNextSkippingWhitespace(last) instanceof CommaToken - then mkJsonMembers(getNextSkippingWhitespace(getNextSkippingWhitespace(last)), tail, _) - else tail = EmptyMemberList() - ) - } - - private JsonMember nthMember(JsonMemberList list, int index) { - exists(JsonMember h | list = ConsMemberList(h, _) | index = 0 and result = h) - or - exists(JsonMemberList t | list = ConsMemberList(_, t) | - index > 0 and result = nthMember(t, index - 1) - ) - } - - class JsonMemberList extends TJsonMemberList { - string toString() { - this = EmptyMemberList() and result = "{}" + private JsonToken getNextSkippingWhitespace(JsonToken t) { + result = t.getNext() and + not result instanceof WhiteSpaceToken or - exists(JsonMember head, JsonMemberList tail, string tailStr | - this = ConsMemberList(head, tail) and - "{" + tailStr + "}" = tail.toString() and - if tailStr != "" - then result = "{" + head.toString() + ", " + tailStr + "}" - else result = "{" + head.toString() + "}" - ) + exists(WhiteSpaceToken ws | ws = t.getNext() | result = getNextSkippingWhitespace(ws)) } - JsonMember getMember(int index) { result = nthMember(this, index) } - - JsonMember getAMember() { result = getMember(_) } - } - - private newtype TJsonMember = - MkJsonMember(string key, JsonValue value) { - exists(StringToken keyToken, ColonToken colonToken, JsonToken firstValueToken | - colonToken = getNextSkippingWhitespace(keyToken) and - firstValueToken = getNextSkippingWhitespace(colonToken) and - key = keyToken.(JsonToken).getValue() and - mkJsonValue(firstValueToken, value, _) + private newtype TJsonMemberList = + EmptyMemberList() or + ConsMemberList(JsonMember head, JsonMemberList tail) { + exists(JsonToken first, JsonToken last | + mkJsonMember(first, head, last) and + if getNextSkippingWhitespace(last) instanceof CommaToken + then mkJsonMembers(getNextSkippingWhitespace(getNextSkippingWhitespace(last)), tail, _) + else tail = EmptyMemberList() + ) + } + + private JsonMember nthMember(JsonMemberList list, int index) { + exists(JsonMember h | list = ConsMemberList(h, _) | index = 0 and result = h) + or + exists(JsonMemberList t | list = ConsMemberList(_, t) | + index > 0 and result = nthMember(t, index - 1) ) } - class JsonMember extends TJsonMember { - string toString() { - exists(string key, JsonValue value | - this = MkJsonMember(key, value) and - result = key + " : " + value.toString() - ) + class JsonMemberList extends TJsonMemberList { + string toString() { + this = EmptyMemberList() and result = "{}" + or + exists(JsonMember head, JsonMemberList tail, string tailStr | + this = ConsMemberList(head, tail) and + "{" + tailStr + "}" = tail.toString() and + if tailStr != "" + then result = "{" + head.toString() + ", " + tailStr + "}" + else result = "{" + head.toString() + "}" + ) + } + + JsonMember getMember(int index) { result = nthMember(this, index) } + + JsonMember getAMember() { result = getMember(_) } } - string getKey() { this = MkJsonMember(result, _) } - - JsonValue getValue() { this = MkJsonMember(_, result) } - } - - private predicate mkJsonMember(StringToken first, JsonMember member, JsonToken last) { - getNextSkippingWhitespace(first) instanceof ColonToken and - exists(JsonValue value | - mkJsonValue(getNextSkippingWhitespace(getNextSkippingWhitespace(first)), value, last) and - member = MkJsonMember(first.getValue(), value) - ) - } - - private predicate mkJsonMembers(JsonToken first, JsonMemberList members, JsonToken last) { - exists(JsonMember h, JsonToken memberLast | mkJsonMember(first, h, memberLast) | - not getNextSkippingWhitespace(memberLast) instanceof CommaToken and - members = ConsMemberList(h, EmptyMemberList()) and - last = memberLast - ) - or - exists(JsonMember h, JsonToken memberLast, JsonMemberList tail | - mkJsonMember(first, h, memberLast) - | - getNextSkippingWhitespace(memberLast) instanceof CommaToken and - mkJsonMembers(getNextSkippingWhitespace(getNextSkippingWhitespace(memberLast)), tail, last) and - members = ConsMemberList(h, tail) - ) - } - - private newtype TJsonValueList = - EmptyJsonValueList() or - ConsJsonValueList(JsonValue head, JsonValueList tail) { - exists(JsonToken first, JsonToken last | - mkJsonValue(first, head, last) and - if getNextSkippingWhitespace(last) instanceof CommaToken - then mkJsonValues(getNextSkippingWhitespace(getNextSkippingWhitespace(last)), tail, _) - else tail = EmptyJsonValueList() - ) + private newtype TJsonMember = + MkJsonMember(string key, JsonValue value) { + exists(StringToken keyToken, ColonToken colonToken, JsonToken firstValueToken | + colonToken = getNextSkippingWhitespace(keyToken) and + firstValueToken = getNextSkippingWhitespace(colonToken) and + key = keyToken.(JsonToken).getValue() and + mkJsonValue(firstValueToken, value, _) + ) + } + + class JsonMember extends TJsonMember { + string toString() { + exists(string key, JsonValue value | + this = MkJsonMember(key, value) and + result = key + " : " + value.toString() + ) + } + + string getKey() { this = MkJsonMember(result, _) } + + JsonValue getValue() { this = MkJsonMember(_, result) } } - private predicate mkJsonValues(JsonToken first, JsonValueList values, JsonToken last) { - exists(JsonValue h, JsonToken valueLast | mkJsonValue(first, h, valueLast) | - not getNextSkippingWhitespace(valueLast) instanceof CommaToken and - values = ConsJsonValueList(h, EmptyJsonValueList()) and - last = valueLast - ) - or - exists(JsonValue h, JsonToken valueLast, JsonValueList tail | mkJsonValue(first, h, valueLast) | - getNextSkippingWhitespace(valueLast) instanceof CommaToken and - mkJsonValues(getNextSkippingWhitespace(getNextSkippingWhitespace(valueLast)), tail, last) and - values = ConsJsonValueList(h, tail) - ) - } - - private JsonValue getNthValue(JsonValueList list, int index) { - exists(JsonValue h | list = ConsJsonValueList(h, _) | index = 0 and result = h) - or - exists(JsonValueList t | list = ConsJsonValueList(_, t) | - index > 0 and result = getNthValue(t, index - 1) - ) - } - - class JsonValueList extends TJsonValueList { - string toString() { - this = EmptyJsonValueList() and result = "[]" - or - exists(JsonValue head, JsonValueList tail, string tailStr | - this = ConsJsonValueList(head, tail) and - "[" + tailStr + "]" = tail.toString() and - if tailStr != "" - then result = "[" + head.toString() + ", " + tailStr + "]" - else result = "[" + head.toString() + "]" + private predicate mkJsonMember(StringToken first, JsonMember member, JsonToken last) { + getNextSkippingWhitespace(first) instanceof ColonToken and + exists(JsonValue value | + mkJsonValue(getNextSkippingWhitespace(getNextSkippingWhitespace(first)), value, last) and + member = MkJsonMember(first.getValue(), value) ) } - JsonValue getValue(int index) { result = getNthValue(this, index) } - - JsonValue getAValue() { result = getValue(_) } - } - - private newtype TJsonValue = - MkJsonNumber(float n, JsonToken source) { - exists(NumberToken t | t.getValue().toFloat() = n and source = t | - not any(StringToken str).contains(t) - ) - } or - MkJsonString(string s, JsonToken source) { - exists(StringToken t | t.(JsonToken).getValue() = s and t = source) - } or - MkJsonObject(JsonMemberList members, JsonToken source) { - exists(LeftBracketToken l, RightBracketToken r, JsonToken last | - mkJsonMembers(getNextSkippingWhitespace(l), members, last) and - getNextSkippingWhitespace(last) = r and - source = l + private predicate mkJsonMembers(JsonToken first, JsonMemberList members, JsonToken last) { + exists(JsonMember h, JsonToken memberLast | mkJsonMember(first, h, memberLast) | + not getNextSkippingWhitespace(memberLast) instanceof CommaToken and + members = ConsMemberList(h, EmptyMemberList()) and + last = memberLast ) or - exists(LeftBracketToken l, RightBracketToken r | getNextSkippingWhitespace(l) = r | - members = EmptyMemberList() and source = l + exists(JsonMember h, JsonToken memberLast, JsonMemberList tail | + mkJsonMember(first, h, memberLast) + | + getNextSkippingWhitespace(memberLast) instanceof CommaToken and + mkJsonMembers(getNextSkippingWhitespace(getNextSkippingWhitespace(memberLast)), tail, last) and + members = ConsMemberList(h, tail) ) - } or - MkJsonArray(JsonValueList values, JsonToken source) { - exists(LeftSquareBracketToken l, RightSquareBracketToken r, JsonToken last | - mkJsonValues(getNextSkippingWhitespace(l), values, last) and - getNextSkippingWhitespace(last) = r and - source = l + } + + private newtype TJsonValueList = + EmptyJsonValueList() or + ConsJsonValueList(JsonValue head, JsonValueList tail) { + exists(JsonToken first, JsonToken last | + mkJsonValue(first, head, last) and + if getNextSkippingWhitespace(last) instanceof CommaToken + then mkJsonValues(getNextSkippingWhitespace(getNextSkippingWhitespace(last)), tail, _) + else tail = EmptyJsonValueList() + ) + } + + private predicate mkJsonValues(JsonToken first, JsonValueList values, JsonToken last) { + exists(JsonValue h, JsonToken valueLast | mkJsonValue(first, h, valueLast) | + not getNextSkippingWhitespace(valueLast) instanceof CommaToken and + values = ConsJsonValueList(h, EmptyJsonValueList()) and + last = valueLast ) or - exists(LeftSquareBracketToken l, RightSquareBracketToken r | - getNextSkippingWhitespace(l) = r + exists(JsonValue h, JsonToken valueLast, JsonValueList tail | + mkJsonValue(first, h, valueLast) | - values = EmptyJsonValueList() and source = l + getNextSkippingWhitespace(valueLast) instanceof CommaToken and + mkJsonValues(getNextSkippingWhitespace(getNextSkippingWhitespace(valueLast)), tail, last) and + values = ConsJsonValueList(h, tail) ) - } or - MkJsonTrue(JsonToken source) { - exists(TrueToken t | not any(StringToken str).contains(t) and source = t) - } or - MkJsonFalse(JsonToken source) { - exists(FalseToken t | not any(StringToken str).contains(t) and source = t) - } or - MkJsonNull(JsonToken source) { - exists(NullToken t | not any(StringToken str).contains(t) and source = t) } - private predicate mkJsonValue(JsonToken first, JsonValue value, JsonToken last) { - first instanceof StringToken and - first = last and - value = MkJsonString(first.getValue(), first) - or - first instanceof NumberToken and - first = last and - value = MkJsonNumber(first.getValue().toFloat(), first) - or - first instanceof LeftBracketToken and - ( - exists(JsonMemberList members, JsonToken membersLast | - mkJsonMembers(getNextSkippingWhitespace(first), members, membersLast) and - value = MkJsonObject(members, first) and - last = getNextSkippingWhitespace(membersLast) - ) + private JsonValue getNthValue(JsonValueList list, int index) { + exists(JsonValue h | list = ConsJsonValueList(h, _) | index = 0 and result = h) or - last = getNextSkippingWhitespace(first) and - value = MkJsonObject(EmptyMemberList(), first) - ) and - last instanceof RightBracketToken - or - first instanceof LeftSquareBracketToken and - ( - exists(JsonValueList values, JsonToken valuesLast | - mkJsonValues(getNextSkippingWhitespace(first), values, valuesLast) and - value = MkJsonArray(values, first) and - last = getNextSkippingWhitespace(valuesLast) + exists(JsonValueList t | list = ConsJsonValueList(_, t) | + index > 0 and result = getNthValue(t, index - 1) ) - or - last = getNextSkippingWhitespace(first) and - value = MkJsonArray(EmptyJsonValueList(), first) - ) and - last instanceof RightSquareBracketToken - or - first instanceof TrueToken and - first = last and - value = MkJsonTrue(first) - or - first instanceof FalseToken and - first = last and - value = MkJsonFalse(first) - or - first instanceof NullToken and - first = last and - value = MkJsonNull(first) - } + } - class JsonValue extends TJsonValue { - string toString() { - exists(string value | - this = MkJsonString(value, _) and - result = "\"" + value + "\"" - ) - or - exists(float number | this = MkJsonNumber(number, _) | result = number.toString()) - or - exists(JsonMemberList members | this = MkJsonObject(members, _) | result = members.toString()) - or - exists(JsonValueList values | this = MkJsonArray(values, _) | result = values.toString()) - or - this = MkJsonTrue(_) and result = "true" - or - this = MkJsonFalse(_) and result = "false" - or - this = MkJsonNull(_) and result = "null" + class JsonValueList extends TJsonValueList { + string toString() { + this = EmptyJsonValueList() and result = "[]" + or + exists(JsonValue head, JsonValueList tail, string tailStr | + this = ConsJsonValueList(head, tail) and + "[" + tailStr + "]" = tail.toString() and + if tailStr != "" + then result = "[" + head.toString() + ", " + tailStr + "]" + else result = "[" + head.toString() + "]" + ) + } + + JsonValue getValue(int index) { result = getNthValue(this, index) } + + JsonValue getAValue() { result = getValue(_) } } - string getSource() { - exists(JsonToken token | - this = MkJsonString(_, token) + private newtype TJsonValue = + MkJsonNumber(float n, JsonToken source) { + exists(NumberToken t | t.getValue().toFloat() = n and source = t | + not any(StringToken str).contains(t) + ) + } or + MkJsonString(string s, JsonToken source) { + exists(StringToken t | t.(JsonToken).getValue() = s and t = source) + } or + MkJsonObject(JsonMemberList members, JsonToken source) { + exists(LeftBracketToken l, RightBracketToken r, JsonToken last | + mkJsonMembers(getNextSkippingWhitespace(l), members, last) and + getNextSkippingWhitespace(last) = r and + source = l + ) or - this = MkJsonNumber(_, token) + exists(LeftBracketToken l, RightBracketToken r | getNextSkippingWhitespace(l) = r | + members = EmptyMemberList() and source = l + ) + } or + MkJsonArray(JsonValueList values, JsonToken source) { + exists(LeftSquareBracketToken l, RightSquareBracketToken r, JsonToken last | + mkJsonValues(getNextSkippingWhitespace(l), values, last) and + getNextSkippingWhitespace(last) = r and + source = l + ) or - this = MkJsonObject(_, token) + exists(LeftSquareBracketToken l, RightSquareBracketToken r | + getNextSkippingWhitespace(l) = r + | + values = EmptyJsonValueList() and source = l + ) + } or + MkJsonTrue(JsonToken source) { + exists(TrueToken t | not any(StringToken str).contains(t) and source = t) + } or + MkJsonFalse(JsonToken source) { + exists(FalseToken t | not any(StringToken str).contains(t) and source = t) + } or + MkJsonNull(JsonToken source) { + exists(NullToken t | not any(StringToken str).contains(t) and source = t) + } + + private predicate mkJsonValue(JsonToken first, JsonValue value, JsonToken last) { + first instanceof StringToken and + first = last and + value = MkJsonString(first.getValue(), first) + or + first instanceof NumberToken and + first = last and + value = MkJsonNumber(first.getValue().toFloat(), first) + or + first instanceof LeftBracketToken and + ( + exists(JsonMemberList members, JsonToken membersLast | + mkJsonMembers(getNextSkippingWhitespace(first), members, membersLast) and + value = MkJsonObject(members, first) and + last = getNextSkippingWhitespace(membersLast) + ) or - this = MkJsonArray(_, token) + last = getNextSkippingWhitespace(first) and + value = MkJsonObject(EmptyMemberList(), first) + ) and + last instanceof RightBracketToken + or + first instanceof LeftSquareBracketToken and + ( + exists(JsonValueList values, JsonToken valuesLast | + mkJsonValues(getNextSkippingWhitespace(first), values, valuesLast) and + value = MkJsonArray(values, first) and + last = getNextSkippingWhitespace(valuesLast) + ) or - this = MkJsonTrue(token) + last = getNextSkippingWhitespace(first) and + value = MkJsonArray(EmptyJsonValueList(), first) + ) and + last instanceof RightSquareBracketToken + or + first instanceof TrueToken and + first = last and + value = MkJsonTrue(first) + or + first instanceof FalseToken and + first = last and + value = MkJsonFalse(first) + or + first instanceof NullToken and + first = last and + value = MkJsonNull(first) + } + + class JsonValue extends TJsonValue { + string toString() { + exists(string value | + this = MkJsonString(value, _) and + result = "\"" + value + "\"" + ) or - this = MkJsonFalse(token) + exists(float number | this = MkJsonNumber(number, _) | result = number.toString()) or - this = MkJsonNull(token) - | - result = token.getSource() - ) - } + exists(JsonMemberList members | this = MkJsonObject(members, _) | + result = members.toString() + ) + or + exists(JsonValueList values | this = MkJsonArray(values, _) | result = values.toString()) + or + this = MkJsonTrue(_) and result = "true" + or + this = MkJsonFalse(_) and result = "false" + or + this = MkJsonNull(_) and result = "null" + } + + MakeJsonReader::JsonReader getReader() { + exists(JsonToken token | + this = MkJsonString(_, token) + or + this = MkJsonNumber(_, token) + or + this = MkJsonObject(_, token) + or + this = MkJsonArray(_, token) + or + this = MkJsonTrue(token) + or + this = MkJsonFalse(token) + or + this = MkJsonNull(token) + | + result = token.getReader() + ) + } + + string getType() { + this = MkJsonString(_, _) and result = "string" + or + this = MkJsonNumber(_, _) and result = "number" + or + this = MkJsonObject(_, _) and result = "object" + or + this = MkJsonArray(_, _) and result = "array" + or + this = MkJsonTrue(_) and result = "true" + or + this = MkJsonFalse(_) and result = "false" + or + this = MkJsonNull(_) and result = "null" + } - string getType() { - this = MkJsonString(_, _) and result = "string" - or - this = MkJsonNumber(_, _) and result = "number" - or - this = MkJsonObject(_, _) and result = "object" - or - this = MkJsonArray(_, _) and result = "array" - or - this = MkJsonTrue(_) and result = "true" - or - this = MkJsonFalse(_) and result = "false" - or - this = MkJsonNull(_) and result = "null" + string asString() { this = MkJsonString(result, _) } } - string asString() { this = MkJsonString(result, _) } - } - - class JsonObject extends JsonValue, MkJsonObject { - JsonMemberList getMembers() { this = MkJsonObject(result, _) } + class JsonObject extends JsonValue, MkJsonObject { + JsonMemberList getMembers() { this = MkJsonObject(result, _) } - JsonMember getMember(int index) { result = getMembers().getMember(index) } + JsonMember getMember(int index) { result = getMembers().getMember(index) } - JsonMember getAMember() { result = getMember(_) } - } + JsonMember getAMember() { result = getMember(_) } + } - class JsonString extends JsonValue, MkJsonString { } + class JsonString extends JsonValue, MkJsonString { } - class JsonNumber extends JsonValue, MkJsonNumber { } + class JsonNumber extends JsonValue, MkJsonNumber { } - class JsonArray extends JsonValue, MkJsonArray { - JsonValueList getValues() { this = MkJsonArray(result, _) } + class JsonArray extends JsonValue, MkJsonArray { + JsonValueList getValues() { this = MkJsonArray(result, _) } - JsonValue getValue(int index) { result = getValues().getValue(index) } + JsonValue getValue(int index) { result = getValues().getValue(index) } - JsonValue getAValue() { result = getValue(_) } - } + JsonValue getAValue() { result = getValue(_) } + } - JsonValue parse(string json) { - result.getSource() = json and - exists(JsonToken firstToken | - firstToken.isFirst() and - if firstToken instanceof WhiteSpaceToken - then mkJsonValue(getNextSkippingWhitespace(firstToken), result, _) - else mkJsonValue(firstToken, result, _) - ) + JsonValue parse(MakeJsonReader::JsonReader reader) { + result.getReader() = reader and + exists(JsonToken firstToken | + firstToken.isFirst() and + if firstToken instanceof WhiteSpaceToken + then mkJsonValue(getNextSkippingWhitespace(firstToken), result, _) + else mkJsonValue(firstToken, result, _) + ) + } } } diff --git a/javascript/frameworks/ui5/test/lib/JsonParser/JsonParser.ql b/javascript/frameworks/ui5/test/lib/JsonParser/JsonParser.ql index ac0067221..5eb054727 100644 --- a/javascript/frameworks/ui5/test/lib/JsonParser/JsonParser.ql +++ b/javascript/frameworks/ui5/test/lib/JsonParser/JsonParser.ql @@ -1,12 +1,16 @@ import javascript import advanced_security.javascript.frameworks.ui5.JsonParser -string getStringLiteral() { - result = any(StringLiteral l).getValue() +module MakeStringLiteralJsonReader implements JsonParser::MakeJsonReaderSig { + class JsonReader extends StringLiteral { + string getJson() { + result = this.getValue() + } + } } -module StringLiteralJsonParser = JsonParser; +module StringLiteralJsonParser = JsonParser::Make; -from StringLiteralJsonParser::JsonValue val, StringLiteral lit -where val = StringLiteralJsonParser::parse(lit.getValue()) -select lit, val, val.getType() \ No newline at end of file +from StringLiteralJsonParser::JsonValue val, MakeStringLiteralJsonReader::JsonReader reader +where val = StringLiteralJsonParser::parse(reader) +select reader, val, val.getType() \ No newline at end of file From 33902e2ad5811a5869850796d0334167a4c5c528 Mon Sep 17 00:00:00 2001 From: Remco Vermeulen Date: Wed, 6 Dec 2023 16:01:45 -0800 Subject: [PATCH 3/3] Rewrite resource root resolving This rewrite associates resource roots to web apps and prevents a cross product between containers and resource root path strings. --- .../javascript/frameworks/ui5/UI5.qll | 103 +++++++++--------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll index d635e9208..be20af23f 100644 --- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll +++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll @@ -6,74 +6,67 @@ private import advanced_security.javascript.frameworks.ui5.UI5View private import advanced_security.javascript.frameworks.ui5.UI5HTML module UI5 { - private class ResourceRootPathString extends PathString { - SapUiCoreScriptElement coreScript; + private module WebAppResourceRootJsonReader implements JsonParser::MakeJsonReaderSig { + class JsonReader extends WebApp{ + string getJson() { + // We match on the lowercase to cover all the possible variants of wrtiting the attribute name. + exists(string resourceRootAttributeName | resourceRootAttributeName.toLowerCase() = "data-sap-ui-resourceroots" | + result = this.getCoreScript().getAttributeByName(resourceRootAttributeName).getValue() + ) + } + } + } - ResourceRootPathString() { this = coreScript.getAResourceRoot().getRoot() } + private module WebAppResourceRootJsonParser = JsonParser::Make; - override Folder getARootFolder() { result = coreScript.getFile().getParentContainer() } + private predicate isAnUnResolvedResourceRoot(WebApp webApp, string name, string path) { + exists( + WebAppResourceRootJsonParser::JsonObject config, + WebAppResourceRootJsonParser::JsonMember configEntry + | + config.getReader() = webApp and + config.getAMember() = configEntry and + name = configEntry.getKey() and + path = configEntry.getValue().asString() + ) } - private newtype TResourceRoot = - MkResourceRoot(string name, string root, SapUiCoreScriptElement coreScript) { - exists( - JsonParser::JsonObject config, - JsonParser::JsonMember configEntry - | - config.getSource() = coreScript.getAttributeByName("data-sap-ui-resourceroots").getValue() and - config.getAMember() = configEntry - | - name = configEntry.getKey() and - root = configEntry.getValue().asString() - ) + class ResourceRootPathString extends PathString { + WebApp webApp; + ResourceRootPathString() { + isAnUnResolvedResourceRoot(webApp, _, this) } - class ResourceRoot extends TResourceRoot, MkResourceRoot { - string getName() { this = MkResourceRoot(result, _, _) } - - string getRoot() { this = MkResourceRoot(_, result, _) } - - SapUiCoreScriptElement getCoreScript() { this = MkResourceRoot(_, _, result) } - - string toString() { result = this.getName() + ": " + this.getRoot() } + override Folder getARootFolder() { + result = webApp.getWebAppFolder() + } } - class ResolvedResourceRoot extends Container { - ResourceRoot unresolvedRoot; - ResolvedResourceRoot() { - exists(ResourceRootPathString resourceRootPathString | unresolvedRoot.getRoot() = resourceRootPathString | - this = resourceRootPathString.resolve(resourceRootPathString.getARootFolder()).getContainer()) + class ResourceRoot extends Container { + string name; + string path; + WebApp webApp; + ResourceRoot() { + isAnUnResolvedResourceRoot(webApp, name, path) + and + path.(PathString).resolve(webApp.getWebAppFolder()).getContainer() = this } - string getName() { - result = unresolvedRoot.getName() - } + string getName() { result = name } - SapUiCoreScriptElement getCoreScript() { - result = unresolvedRoot.getCoreScript() - } + WebApp getWebApp() { result = webApp} predicate contains(File file) { - file.getParentContainer+() = this + this.getAChildContainer+().getAFile() = file } } - private string getAResourceRootConfig() { - result = any(SapUiCoreScriptElement script).getAttributeByName("data-sap-ui-resourceroots").getValue() - } - class SapUiCoreScriptElement extends HTML::ScriptElement { SapUiCoreScriptElement() { this.getSourcePath().matches(["%sap-ui-core.js", "%sap-ui-core-nojQuery.js"]) } - ResourceRoot getAResourceRoot() { - result.getCoreScript() = this - } - - ResolvedResourceRoot getAResolvedResourceRoot() { - result.getCoreScript() = this - } + WebApp getWebApp() { result = this.getFile() } } /** A UI5 web application manifest associated with a bootstrapped UI5 web application. */ @@ -94,10 +87,16 @@ module UI5 { WebApp() { coreScript.getFile() = this } - File getAResource() { coreScript.getAResolvedResourceRoot().contains(result) } + SapUiCoreScriptElement getCoreScript() { result = coreScript } + + ResourceRoot getAResourceRoot() { + result.getWebApp() = this + } + + File getAResource() { getAResourceRoot().contains(result)} - File getResource(string path) { - getWebAppFolder().getAbsolutePath() + "/" + path = result.getAbsolutePath() + File getResource(string relativePath) { + result.getAbsolutePath() = getAResourceRoot().getAbsolutePath() + "/" + relativePath } Folder getWebAppFolder() { result = this.getParentContainer() } @@ -110,10 +109,10 @@ module UI5 { File getInitialModule() { exists( string initialModuleResourcePath, string resolvedModulePath, - ResolvedResourceRoot resourceRoot + ResourceRoot resourceRoot | initialModuleResourcePath = coreScript.getAttributeByName("data-sap-ui-onInit").getValue() and - coreScript.getAResolvedResourceRoot() = resourceRoot and + resourceRoot.getWebApp() = this and resolvedModulePath = initialModuleResourcePath .regexpReplaceAll("^module\\s*:\\s*", "")