Skip to content

Commit

Permalink
Merge pull request #4808 from thc202/graphql/use-jackson
Browse files Browse the repository at this point in the history
graphql: use Jackson to parse JSON
  • Loading branch information
psiinon authored Aug 12, 2023
2 parents 7e36440 + 737a254 commit de2e3b2
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 68 deletions.
3 changes: 1 addition & 2 deletions addOns/graphql/graphql.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ zapAddOn {
dependencies {
addOns {
register("commonlib") {
version.set(">= 1.14.0 & < 2.0.0")
version.set(">= 1.16.0 & < 2.0.0")
}
}
}
Expand Down Expand Up @@ -76,7 +76,6 @@ dependencies {
zapAddOn("formhandler")
zapAddOn("spider")

implementation("com.google.code.gson:gson:2.10.1")
implementation("com.graphql-java:graphql-java:21.0")

testImplementation(project(":testutils"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
*/
package org.zaproxy.addon.graphql;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
Expand All @@ -43,7 +43,7 @@ public class GraphQlFingerprinter {
private static final Map<String, String> FINGERPRINTING_ALERT_TAGS =
CommonAlertTag.toMap(CommonAlertTag.WSTG_V42_INFO_02_FINGERPRINT_WEB_SERVER);
private static final Logger LOGGER = LogManager.getLogger(GraphQlFingerprinter.class);
private static final Gson GSON = new Gson();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

private final Requestor requestor;
private final Map<String, HttpMessage> queryCache;
Expand Down Expand Up @@ -122,20 +122,19 @@ boolean errorContains(String substring, String errorField) {
}
try {
String response = lastQueryMsg.getResponseBody().toString();
JsonElement errors = GSON.fromJson(response, JsonObject.class).get("errors");
if (errors == null || !errors.isJsonArray()) {
JsonNode errors = OBJECT_MAPPER.readValue(response, JsonNode.class).get("errors");
if (errors == null || !errors.isArray()) {
return false;
}
var errorsArray = errors.getAsJsonArray();
for (var error : errorsArray) {
if (!error.isJsonObject()) {
for (var error : errors) {
if (!error.isObject()) {
continue;
}
var errorFieldValue = error.getAsJsonObject().get(errorField);
var errorFieldValue = error.get(errorField);
if (errorFieldValue == null) {
continue;
}
if (errorFieldValue.getAsString().contains(substring)) {
if (errorFieldValue.asText().contains(substring)) {
matchedString = substring;
return true;
}
Expand Down Expand Up @@ -196,7 +195,7 @@ private boolean checkAriadneEngine() {
if (errorContains("Unknown directive '@abc'.")) {
try {
String response = lastQueryMsg.getResponseBody().toString();
JsonElement data = GSON.fromJson(response, JsonObject.class).get("data");
JsonNode data = OBJECT_MAPPER.readValue(response, JsonNode.class).get("data");
if (data == null) {
matchedString = null;
return true;
Expand All @@ -223,11 +222,9 @@ private boolean checkDgraphEngine() {
if (lastQueryMsg != null && lastQueryMsg.getResponseHeader().isJson()) {
try {
String response = lastQueryMsg.getResponseBody().toString();
JsonElement data = GSON.fromJson(response, JsonObject.class).get("data");
if (data != null && data.isJsonObject()) {
var dataObject = data.getAsJsonObject();
if (dataObject.has("__typename")
&& "Query".equals(dataObject.get("__typename").getAsString())) {
JsonNode data = OBJECT_MAPPER.readValue(response, JsonNode.class).get("data");
if (data != null && data.isObject()) {
if (data.has("__typename") && "Query".equals(data.get("__typename").asText())) {
matchedString = "Query";
return true;
}
Expand All @@ -252,25 +249,23 @@ private boolean checkDirectusEngine() {
return false;
}
String response = lastQueryMsg.getResponseBody().toString();
JsonElement errors = GSON.fromJson(response, JsonObject.class).get("errors");
if (errors == null || !errors.isJsonArray()) {
JsonNode errors = OBJECT_MAPPER.readValue(response, JsonNode.class).get("errors");
if (errors == null || !errors.isArray()) {
return false;
}
var errorsArray = errors.getAsJsonArray();
if (errorsArray.size() == 0) {
if (errors.size() == 0) {
return false;
}
var error = errorsArray.get(0);
if (error == null || !error.isJsonObject()) {
var error = errors.get(0);
if (error == null || !error.isObject()) {
return false;
}
JsonElement extensions = error.getAsJsonObject().get("extensions");
if (extensions == null || !extensions.isJsonObject()) {
JsonNode extensions = error.get("extensions");
if (extensions == null || !extensions.isObject()) {
return false;
}
var extensionsObject = extensions.getAsJsonObject();
if (extensionsObject.has("code")
&& "INVALID_PAYLOAD".equals(extensionsObject.get("code").getAsString())) {
if (extensions.has("code")
&& "INVALID_PAYLOAD".equals(extensions.get("code").asText())) {
matchedString = "INVALID_PAYLOAD";
return true;
}
Expand Down Expand Up @@ -303,11 +298,9 @@ private boolean checkGraphQlByPopEngine() {
if (lastQueryMsg != null && lastQueryMsg.getResponseHeader().isJson()) {
try {
String response = lastQueryMsg.getResponseBody().toString();
JsonElement data = GSON.fromJson(response, JsonObject.class).get("data");
if (data != null && data.isJsonObject()) {
var dataObject = data.getAsJsonObject();
if (dataObject.has("alias1$1")
&& "QueryRoot".equals(dataObject.get("alias1$1").getAsString())) {
JsonNode data = OBJECT_MAPPER.readValue(response, JsonNode.class).get("data");
if (data != null && data.isObject()) {
if (data.has("alias1$1") && "QueryRoot".equals(data.get("alias1$1").asText())) {
matchedString = "QueryRoot";
return true;
}
Expand Down Expand Up @@ -343,11 +336,9 @@ private boolean checkGraphQlGoEngine() {
sendQuery("{__typename}");
try {
String response = lastQueryMsg.getResponseBody().toString();
JsonElement data = GSON.fromJson(response, JsonObject.class).get("data");
if (data != null && data.isJsonObject()) {
var dataObject = data.getAsJsonObject();
if (dataObject.has("__typename")
&& "RootQuery".equals(dataObject.get("__typename").getAsString())) {
JsonNode data = OBJECT_MAPPER.readValue(response, JsonNode.class).get("data");
if (data != null && data.isObject()) {
if (data.has("__typename") && "RootQuery".equals(data.get("__typename").asText())) {
matchedString = "RootQuery";
return true;
}
Expand Down Expand Up @@ -406,11 +397,10 @@ private boolean checkHasuraEngine() {
if (lastQueryMsg != null && lastQueryMsg.getResponseHeader().isJson()) {
try {
String response = lastQueryMsg.getResponseBody().toString();
JsonElement data = GSON.fromJson(response, JsonObject.class).get("data");
if (data != null && data.isJsonObject()) {
var dataObject = data.getAsJsonObject();
if (dataObject.has("__typename")
&& "query_root".equals(dataObject.get("__typename").getAsString())) {
JsonNode data = OBJECT_MAPPER.readValue(response, JsonNode.class).get("data");
if (data != null && data.isObject()) {
if (data.has("__typename")
&& "query_root".equals(data.get("__typename").asText())) {
matchedString = "query_root";
return true;
}
Expand Down Expand Up @@ -481,13 +471,14 @@ private boolean checkSangriaEngine() {
return false;
}
String response = lastQueryMsg.getResponseBody().toString();
JsonElement syntaxError = GSON.fromJson(response, JsonObject.class).get("syntaxError");
if (syntaxError == null || !syntaxError.isJsonPrimitive()) {
JsonNode syntaxError =
OBJECT_MAPPER.readValue(response, JsonNode.class).get("syntaxError");
if (syntaxError == null || !syntaxError.isValueNode()) {
return false;
}
String expectedError =
"Syntax error while parsing GraphQL query. Invalid input \"queryy\", expected ExecutableDefinition or TypeSystemDefinition";
if (syntaxError.getAsString().contains(expectedError)) {
if (syntaxError.asText().contains(expectedError)) {
matchedString = expectedError;
return true;
}
Expand All @@ -499,8 +490,12 @@ private boolean checkSangriaEngine() {
private boolean checkStrawberryEngine() {
sendQuery("query @deprecated {__typename}");
String response = lastQueryMsg.getResponseBody().toString();
return errorContains("Directive '@deprecated' may not be used on query.")
&& GSON.fromJson(response, JsonObject.class).has("data");
try {
return errorContains("Directive '@deprecated' may not be used on query.")
&& OBJECT_MAPPER.readValue(response, JsonNode.class).has("data");
} catch (JsonProcessingException ignore) {
}
return false;
}

private boolean checkTartifletteEngine() {
Expand Down Expand Up @@ -537,26 +532,24 @@ private boolean checkWpGraphQlEngine() {
}
try {
String response = lastQueryMsg.getResponseBody().toString();
JsonElement extensions = GSON.fromJson(response, JsonObject.class).get("extensions");
if (extensions != null && extensions.isJsonObject()) {
var extensionsObject = extensions.getAsJsonObject();
JsonElement debug = extensionsObject.get("debug");
if (debug != null && debug.isJsonArray()) {
var debugArray = debug.getAsJsonArray();
if (!debugArray.isEmpty()) {
var debugObject = debugArray.get(0).getAsJsonObject();
JsonNode extensions =
OBJECT_MAPPER.readValue(response, JsonNode.class).get("extensions");
if (extensions != null && extensions.isObject()) {
JsonNode debug = extensions.get("debug");
if (debug != null && debug.isArray()) {
if (!debug.isEmpty()) {
var debugObject = debug.get(0);
String expectedDebugType = "DEBUG_LOGS_INACTIVE";
if (debugObject.has("type")
&& expectedDebugType.equals(
debugObject.get("type").getAsString())) {
&& expectedDebugType.equals(debugObject.get("type").asText())) {
matchedString = expectedDebugType;
return true;
}
String expectedDebugMessage =
"GraphQL Debug logging is not active. To see debug logs, GRAPHQL_DEBUG must be enabled.";
if (debugObject.has("message")
&& expectedDebugMessage.equals(
debugObject.get("message").getAsString())) {
debugObject.get("message").asText())) {
matchedString = expectedDebugMessage;
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
*/
package org.zaproxy.addon.graphql;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import graphql.introspection.IntrospectionQueryBuilder;
import graphql.introspection.IntrospectionResultToSchema;
import graphql.language.Document;
Expand Down Expand Up @@ -102,10 +102,10 @@ public void introspect(boolean raiseAlert) throws IOException {
}
try {
Map<String, Object> result =
new Gson()
.fromJson(
new ObjectMapper()
.readValue(
importMessage.getResponseBody().toString(),
new TypeToken<Map<String, Object>>() {}.getType());
new TypeReference<Map<String, Object>>() {});
if (result == null) {
throw new IOException("The response was empty.");
}
Expand All @@ -121,7 +121,7 @@ public void introspect(boolean raiseAlert) throws IOException {
}
String schemaSdl = new SchemaPrinter().print(schema);
parse(schemaSdl);
} catch (JsonSyntaxException e) {
} catch (JacksonException e) {
throw new IOException("The response was not valid JSON.");
}
}
Expand Down

0 comments on commit de2e3b2

Please sign in to comment.