Visitar URL original
feat(jsonrpc): validate jsonrpc version, method and null id by 0xbigapple · Pull Request #7032 · tronprotocol/java-tron · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class JsonRpcServlet extends RateLimiterServlet {

private static final ObjectMapper MAPPER = buildMapper();
private static final ObjectMapper OUTBOUND_MAPPER = new ObjectMapper();
private static final String INVALID_REQUEST_MESSAGE = "Invalid Request";

private static ObjectMapper buildMapper() {
JsonFactory factory = JsonFactory.builder()
Expand Down Expand Up @@ -125,21 +126,22 @@ private void doPostInternal(HttpServletRequest req, HttpServletResponse resp) th
}

if (!rootNode.isObject() && !rootNode.isArray()) {
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST, "Invalid Request", null, false);
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST, INVALID_REQUEST_MESSAGE, null, false);
return;
}

boolean isBatch = rootNode.isArray();
if (isBatch && rootNode.isEmpty()) {
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST, "Invalid Request", null, false);
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST, INVALID_REQUEST_MESSAGE, null, false);
return;
}
if (!isBatch && hasInvalidRequestId(rootNode)) {
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST, "Invalid Request", null, false);
if (!isBatch && hasInvalidVersion(rootNode)) {
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST,
INVALID_REQUEST_MESSAGE + ": jsonrpc must be \"2.0\"", rootNode.get("id"), false);
return;
}
if (!isBatch && hasScalarParams(rootNode)) {
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST, "Invalid Request",
if (!isBatch && isInvalidRequest(rootNode)) {
writeJsonRpcError(resp, JsonRpcError.INVALID_REQUEST, INVALID_REQUEST_MESSAGE,
rootNode.get("id"), false);
return;
}
Expand Down Expand Up @@ -202,9 +204,14 @@ private void handleBatch(HttpServletResponse resp, JsonNode rootNode, int maxRes
for (int i = 0; i < rootNode.size(); i++) {
JsonNode subRequest = rootNode.get(i);

if (!subRequest.isObject() || hasInvalidRequestId(subRequest)
|| hasScalarParams(subRequest)) {
ObjectNode errNode = buildErrorNode(JsonRpcError.INVALID_REQUEST, "Invalid Request",
String invalidRequest = null;
if (subRequest.isObject() && hasInvalidVersion(subRequest)) {
invalidRequest = INVALID_REQUEST_MESSAGE + ": jsonrpc must be \"2.0\"";
} else if (isInvalidRequest(subRequest)) {
invalidRequest = INVALID_REQUEST_MESSAGE;
}
if (invalidRequest != null) {
Comment thread
0xbigapple marked this conversation as resolved.
Resolved
ObjectNode errNode = buildErrorNode(JsonRpcError.INVALID_REQUEST, invalidRequest,
subRequest.get("id"));
if (!overflow) {
byte[] errBytes = MAPPER.writeValueAsBytes(errNode);
Expand Down Expand Up @@ -369,6 +376,29 @@ private ObjectNode buildErrorNode(JsonRpcError error, String message, JsonNode i
return errorObj;
}

private boolean hasInvalidVersion(JsonNode request) {
JsonNode version = request.get("jsonrpc");
return version == null || !version.isTextual() || !"2.0".equals(version.asText());
}

private boolean isInvalidRequest(JsonNode request) {
return !request.isObject() || hasInvalidRequestId(request) || hasNullRequestId(request)
|| hasScalarParams(request) || hasInvalidMethod(request);
}

// Stricter than the spec and geth, which both answer a null id normally. jsonrpc4j invokes
// the method but treats a null id as a notification and discards a successful result, so the
// caller never sees the outcome of a call that took effect. Reject it before dispatch instead;
// a request without an id is still dispatched as a notification.
private boolean hasNullRequestId(JsonNode request) {
return request.has("id") && request.get("id").isNull();
}

private boolean hasInvalidMethod(JsonNode request) {
JsonNode method = request.get("method");
return method == null || !method.isTextual() || method.asText().isEmpty();
}

private static boolean hasInvalidRequestId(JsonNode request) {
return request.has("id") && !isValidRequestId(request.get("id"));
}
Expand Down
Loading
Loading