Version in base suite: 1.4.1-1 Base version: libjettison-java_1.4.1-1 Target version: libjettison-java_1.5.3-1~deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libj/libjettison-java/libjettison-java_1.4.1-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libj/libjettison-java/libjettison-java_1.5.3-1~deb11u1.dsc .github/workflows/codeql-analysis.yml | 74 +++ debian/changelog | 30 + debian/control | 4 debian/maven.ignoreRules | 1 debian/patches/01-maven-bundle-plugin-compatibility.patch | 13 debian/patches/series | 1 debian/watch | 6 pom.xml | 65 ++- src/main/java/org/codehaus/jettison/json/JSONArray.java | 9 src/main/java/org/codehaus/jettison/json/JSONObject.java | 68 ++- src/main/java/org/codehaus/jettison/json/JSONTokener.java | 45 +- src/main/java/org/codehaus/jettison/mapped/DefaultConverter.java | 211 ++++++++-- src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamWriter.java | 4 src/test/java/org/codehaus/jettison/json/JSONArrayTest.java | 28 + src/test/java/org/codehaus/jettison/json/JSONObjectTest.java | 122 +++++ src/test/java/org/codehaus/jettison/json/JSONTokenerTest.java | 27 + 16 files changed, 621 insertions(+), 87 deletions(-) diff -Nru libjettison-java-1.4.1/.github/workflows/codeql-analysis.yml libjettison-java-1.5.3/.github/workflows/codeql-analysis.yml --- libjettison-java-1.4.1/.github/workflows/codeql-analysis.yml 1970-01-01 00:00:00.000000000 +0000 +++ libjettison-java-1.5.3/.github/workflows/codeql-analysis.yml 2022-12-06 23:20:44.000000000 +0000 @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '38 10 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff -Nru libjettison-java-1.4.1/debian/changelog libjettison-java-1.5.3/debian/changelog --- libjettison-java-1.4.1/debian/changelog 2021-01-17 23:14:42.000000000 +0000 +++ libjettison-java-1.5.3/debian/changelog 2023-01-10 21:18:24.000000000 +0000 @@ -1,3 +1,33 @@ +libjettison-java (1.5.3-1~deb11u1) bullseye-security; urgency=high + + * Team upload. + * Fix CVE-2022-40150, CVE-2022-45685, CVE-2022-45693: + denial of service via stack overflow / out of memory + + -- Markus Koschany Tue, 10 Jan 2023 22:18:24 +0100 + +libjettison-java (1.5.3-1) unstable; urgency=high + + * Team upload. + * New upstream version 1.5.3. + - Fix CVE-2022-40150, CVE-2022-45685, CVE-2022-45693: + denial of service via stack overflow / out of memory + (Closes: #1022553) + * Declare compliance with Debian Policy 4.6.2. + + -- Markus Koschany Sat, 31 Dec 2022 11:18:53 +0100 + +libjettison-java (1.5.1-1) unstable; urgency=medium + + * Team upload. + * New upstream version 1.5.1. + * Fix CVE-2022-40149: + It was discovered that libjettison-java, a collection of StAX parsers and + writers for JSON, was vulnerable to a denial-of-service attack, if the + attacker provided untrusted XML or JSON data. (Closes: #1022554) + + -- Markus Koschany Thu, 10 Nov 2022 01:09:07 +0100 + libjettison-java (1.4.1-1) unstable; urgency=medium * Team upload. diff -Nru libjettison-java-1.4.1/debian/control libjettison-java-1.5.3/debian/control --- libjettison-java-1.4.1/debian/control 2021-01-17 23:10:10.000000000 +0000 +++ libjettison-java-1.5.3/debian/control 2023-01-10 21:18:24.000000000 +0000 @@ -4,13 +4,13 @@ Maintainer: Debian Java Maintainers Uploaders: Torsten Werner Build-Depends: - debhelper-compat (= 13), + debhelper-compat (= 12), default-jdk, junit4, libmaven-bundle-plugin-java, libwoodstox-java, maven-debian-helper -Standards-Version: 4.5.1 +Standards-Version: 4.6.2 Vcs-Git: https://salsa.debian.org/java-team/libjettison-java.git Vcs-Browser: https://salsa.debian.org/java-team/libjettison-java Homepage: https://github.com/jettison-json/jettison diff -Nru libjettison-java-1.4.1/debian/maven.ignoreRules libjettison-java-1.5.3/debian/maven.ignoreRules --- libjettison-java-1.4.1/debian/maven.ignoreRules 2021-01-17 23:10:10.000000000 +0000 +++ libjettison-java-1.5.3/debian/maven.ignoreRules 2023-01-10 21:18:24.000000000 +0000 @@ -1 +1,2 @@ org.sonatype.plugins nexus-staging-maven-plugin +org.apache.maven.plugins maven-enforcer-plugin * * * * diff -Nru libjettison-java-1.4.1/debian/patches/01-maven-bundle-plugin-compatibility.patch libjettison-java-1.5.3/debian/patches/01-maven-bundle-plugin-compatibility.patch --- libjettison-java-1.4.1/debian/patches/01-maven-bundle-plugin-compatibility.patch 2021-01-17 23:10:10.000000000 +0000 +++ libjettison-java-1.5.3/debian/patches/01-maven-bundle-plugin-compatibility.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -Description: Adjusts the bundle plugin configuration to avoid generating an empty jar -Author: Emmanuel Bourg -Forwarded: no ---- a/pom.xml -+++ b/pom.xml -@@ -70,7 +70,6 @@ - ${project.groupId}.${project.artifactId} - org.codehaus.jettison*;version=${project.version} - * -- !* - ${project.name} - ${project.version} - diff -Nru libjettison-java-1.4.1/debian/patches/series libjettison-java-1.5.3/debian/patches/series --- libjettison-java-1.4.1/debian/patches/series 2021-01-17 23:10:10.000000000 +0000 +++ libjettison-java-1.5.3/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01-maven-bundle-plugin-compatibility.patch diff -Nru libjettison-java-1.4.1/debian/watch libjettison-java-1.5.3/debian/watch --- libjettison-java-1.4.1/debian/watch 2021-01-17 23:10:10.000000000 +0000 +++ libjettison-java-1.5.3/debian/watch 2023-01-10 21:18:24.000000000 +0000 @@ -1,3 +1,3 @@ -version=3 -opts="repack,compression=xz" \ -https://github.com/jettison-json/jettison/tags .*/archive/jettison-([\d\.]+).tar.gz +version=4 +opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/jettison-json-$1\.tar\.gz/ \ + https://github.com/jettison-json/jettison/tags .*/jettison-?(\d\S+)\.tar\.gz diff -Nru libjettison-java-1.4.1/pom.xml libjettison-java-1.5.3/pom.xml --- libjettison-java-1.4.1/pom.xml 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/pom.xml 2022-12-06 23:20:44.000000000 +0000 @@ -2,7 +2,7 @@ 4.0.0 org.codehaus.jettison jettison - 1.4.1 + 1.5.3 bundle Jettison A StAX implementation for JSON. @@ -17,13 +17,13 @@ junit junit - 4.12 + 4.13.2 test com.fasterxml.woodstox woodstox-core - 5.0.3 + 6.4.0 test @@ -31,7 +31,7 @@ scm:git:http://github.com/jettison-json/jettison.git scm:git:https://github.com/jettison-json/jettison.git https://github.com/jettison-json/jettison - jettison-1.4.1 + jettison-1.5.3 @@ -48,8 +48,28 @@ org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0 + + + enforce-maven + + enforce + + + + + 3.2.5 + + + + + + + + org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.10.1 1.8 1.8 @@ -60,40 +80,41 @@ + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + true + + ossrh + https://oss.sonatype.org/ + true + + + org.apache.felix maven-bundle-plugin true - 1.0.0 + 5.1.6 ${project.artifactId} ${project.groupId}.${project.artifactId} org.codehaus.jettison*;version=${project.version} - * - !* + javax.xml,* ${project.name} ${project.version} + <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@))) + <_nouses>true - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.8 - true - - ossrh - https://oss.sonatype.org/ - true - - org.apache.maven.plugins maven-release-plugin - 2.5.2 + 2.5.3 false clean install @@ -114,7 +135,7 @@ true maven-deploy-plugin - 2.8.1 + 2.8.2 ${deploy.altRepository} true @@ -123,7 +144,7 @@ maven-gpg-plugin - 1.6 + 3.0.1 diff -Nru libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/json/JSONArray.java libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/json/JSONArray.java --- libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/json/JSONArray.java 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/json/JSONArray.java 2022-12-06 23:20:44.000000000 +0000 @@ -179,8 +179,9 @@ /** * Construct a JSONArray from a Collection. * @param collection A Collection. + * @throws JSONException If there is a syntax error. */ - public JSONArray(Collection collection) { + public JSONArray(Collection collection) throws JSONException { this.myArrayList = (collection == null) ? new ArrayList() : new ArrayList(collection); @@ -580,8 +581,9 @@ * JSONArray which is produced from a Collection. * @param value A Collection value. * @return this. + * @throws JSONException If there is a syntax error. */ - public JSONArray put(Collection value) { + public JSONArray put(Collection value) throws JSONException { put(new JSONArray(value)); return this; } @@ -631,8 +633,9 @@ * JSONObject which is produced from a Map. * @param value A Map value. * @return this. + * @throws JSONException If there is a syntax error. */ - public JSONArray put(Map value) { + public JSONArray put(Map value) throws JSONException { put(new JSONObject(value)); return this; } diff -Nru libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/json/JSONObject.java libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/json/JSONObject.java --- libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/json/JSONObject.java 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/json/JSONObject.java 2022-12-06 23:20:44.000000000 +0000 @@ -85,6 +85,13 @@ public class JSONObject implements Serializable { /** + * The default recursion depth limit to prevent stack overflow issues on deeply nested structures. + */ + final static int DEFAULT_RECURSION_DEPTH_LIMIT = 500; + + static int RECURSION_DEPTH_LIMIT = DEFAULT_RECURSION_DEPTH_LIMIT; + + /** * JSONObject.NULL is equivalent to the value that JavaScript calls null, * whilst Java's null is equivalent to the value that JavaScript calls * undefined. @@ -213,6 +220,8 @@ throw x.syntaxError("A JSONObject text must end with '}'"); case '}': return; + case '{': + throw x.syntaxError("Expected a key"); default: x.back(); key = x.nextValue().toString(); @@ -257,8 +266,17 @@ * Construct a JSONObject from a Map. * @param map A map object that can be used to initialize the contents of * the JSONObject. + * @throws JSONException If there is a syntax error. */ - public JSONObject(Map map) { + public JSONObject(Map map) throws JSONException { + this(map, 0); + } + + private JSONObject(Map map, int recursionDepth) throws JSONException { + + if (recursionDepth > RECURSION_DEPTH_LIMIT) { + throw new JSONException("JSONObject has reached recursion depth limit of " + RECURSION_DEPTH_LIMIT); + } this.myHashMap = (map == null) ? new LinkedHashMap() : new LinkedHashMap(map); @@ -268,8 +286,8 @@ if (v instanceof Collection) { myHashMap.put(entry.getKey(), new JSONArray((Collection) v)); } - if (v instanceof Map) { - myHashMap.put(entry.getKey(), new JSONObject((Map) v)); + if (v instanceof Map && v != map) { + myHashMap.put(entry.getKey(), new JSONObject((Map) v, recursionDepth + 1)); } } } @@ -1025,9 +1043,10 @@ c = string.charAt(i); switch (c) { case '\\': + sb.append("\\\\"); + break; case '"': - sb.append('\\'); - sb.append(c); + sb.append("\\\""); break; case '/': if (escapeForwardSlashAlways || i > 0 && string.charAt(i - 1) == '<') { @@ -1319,6 +1338,43 @@ return quote(value.toString(), escapeForwardSlash); } + /** + * Set the new recursion depth limit to prevent stack overflow issues on deeply nested structures. The default + * value is 500 + * @param newRecursionDepthLimit the new recursion depth limit to set + */ + public static void setGlobalRecursionDepthLimit(int newRecursionDepthLimit) { + RECURSION_DEPTH_LIMIT = newRecursionDepthLimit; + } + + /** + * Set the new recursion depth limit to prevent stack overflow issues on deeply nested structures. The default + * value is 500 + * @param newRecursionDepthLimit the new recursion depth limit to set + */ + @Deprecated + public void setRecursionDepthLimit(int newRecursionDepthLimit) { + RECURSION_DEPTH_LIMIT = newRecursionDepthLimit; + } + + /** + * Get the new recursion depth limit to prevent stack overflow issues on deeply nested structures. The default + * value is 500 + * @return the recursion depth limit + */ + public static int getGlobalRecursionDepthLimit() { + return RECURSION_DEPTH_LIMIT; + } + + /** + * Get the new recursion depth limit to prevent stack overflow issues on deeply nested structures. The default + * value is 500 + * @return the recursion depth limit + */ + @Deprecated + public int getRecursionDepthLimit() { + return RECURSION_DEPTH_LIMIT; + } /** * Write the contents of the JSONObject as JSON text to a writer. @@ -1350,6 +1406,7 @@ while (keys.hasNext()) { if (b) { writer.write(','); + b = false; } String k = keys.next().toString(); Object v = this.myHashMap.get(k); @@ -1395,4 +1452,5 @@ public Map toMap() { return Collections.unmodifiableMap(myHashMap); } + } diff -Nru libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/json/JSONTokener.java libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/json/JSONTokener.java --- libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/json/JSONTokener.java 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/json/JSONTokener.java 2022-12-06 23:20:44.000000000 +0000 @@ -15,6 +15,8 @@ */ package org.codehaus.jettison.json; +import java.math.BigDecimal; + /** * A JSONTokener takes a source string and extracts characters and tokens from * it. It is used by the JSONObject and JSONArray constructors to parse @@ -24,6 +26,11 @@ */ public class JSONTokener { + private static final String USE_BIGDECIMAL_JSONTOKENER_KEY = "jettison.json.jsontokener.use_bigdecimal"; + public static final boolean USE_BIGDECIMAL_JSONTOKENER = Boolean.getBoolean( USE_BIGDECIMAL_JSONTOKENER_KEY ); + protected boolean useBigDecimal = USE_BIGDECIMAL_JSONTOKENER; + + /** * The index of the next character. */ @@ -37,7 +44,9 @@ private int threshold = -1; - + + private int recursionDepth; + /** * Construct a JSONTokener from a string. * @@ -47,7 +56,7 @@ this.myIndex = 0; this.mySource = s.trim(); } - + /** * Construct a JSONTokener from a string. * @@ -185,11 +194,13 @@ if (next() == '/') { break; } - back(); } } break; default: + if (!more()) { + throw syntaxError("The JSON text is malformed"); + } back(); return '/'; } @@ -308,7 +319,8 @@ /** - * Get the next value. The value can be a Boolean, Double, Integer, + * Get the next value. The value can be a Boolean, Double/BigDecimal + * (depending on -Djettison.json.jsontokener.use_bigdecimal), Integer, * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. * @throws JSONException If syntax error. * @@ -398,7 +410,11 @@ return Long.valueOf(s); } catch (Exception f) { try { - return new Double(s); + if (useBigDecimal) { + return new BigDecimal(s); + } else { + return new Double(s); + } } catch (Exception g) { return s; } @@ -409,11 +425,24 @@ } protected JSONObject newJSONObject() throws JSONException { - return new JSONObject(this); + checkRecursionDepth(); + JSONObject object = new JSONObject(this); + recursionDepth--; + return object; } - + protected JSONArray newJSONArray() throws JSONException { - return new JSONArray(this); + checkRecursionDepth(); + JSONArray array = new JSONArray(this); + recursionDepth--; + return array; + } + + private void checkRecursionDepth() throws JSONException { + recursionDepth++; + if (recursionDepth > JSONObject.RECURSION_DEPTH_LIMIT) { + throw new JSONException("JSONTokener has reached recursion depth limit of " + JSONObject.RECURSION_DEPTH_LIMIT); + } } /** diff -Nru libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/mapped/DefaultConverter.java libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/mapped/DefaultConverter.java --- libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/mapped/DefaultConverter.java 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/mapped/DefaultConverter.java 2022-12-06 23:20:44.000000000 +0000 @@ -33,35 +33,194 @@ public Object convertToJSONPrimitive(String text) { if(text == null) return text; - Object primitive = null; - // Attempt to convert to Integer - try { - primitive = enforce32BitInt ? Integer.valueOf(text) : Long.valueOf(text); - } catch (Exception e) {/**/} - // Attempt to convert to double - if (primitive == null) { - try { - Double v = Double.valueOf(text); - if( !v.isInfinite() && !v.isNaN() ) { - primitive = v; + + // If there's at least one character + if (text.length() >= 1) { + // find the first character + char first = text.charAt(0); + + // Is it incompatible with a number? + if ((first < '0' || first > '9') && first != '-') { + // Yes it is, so special case check for Boolean values + if (first == 't') { + if (text.equals("true")) { + return Boolean.TRUE; + } + } else if (first == 'f') { + if (text.equals("false")) { + return Boolean.FALSE; + } } - else { + + // Definitely not a Boolean or a number, so return the original value + return text; + } + } + + Object primitive = null; + + if (enforce32BitInt) { + primitive = getInteger(text); + } else { + primitive = getLong(text); + } + + if (primitive == null) { + Double dbl = getDouble(text); + + if (dbl != null) { + if (dbl.isInfinite() || dbl.isNaN()) { primitive = text; } - } catch (Exception e) {/**/} - } - // Attempt to convert to boolean - if (primitive == null) { - if(text.trim().equalsIgnoreCase("true") || text.trim().equalsIgnoreCase("false")) { - primitive = Boolean.valueOf(text); - } - } - - if (primitive == null || !primitive.toString().equals(text)) { - // Default String - primitive = text; - } + else { + primitive = dbl; + } + } + } + + if (primitive == null || !primitive.toString().equals(text)) { + // Default String + primitive = text; + } + + return primitive; + } + + private static final int MAX_LENGTH_LONG = String.valueOf(Long.MAX_VALUE).length(); + private static final int MAX_LENGTH_LONG_NEGATIVE = String.valueOf(Long.MAX_VALUE).length() + 1; + + /** + * Try to get a Long value efficiently, avoiding Exceptions + */ + private static Long getLong(String text) + { + // handle an empty string + if (text.isEmpty()) return null; + + // if the text is too long it can't be a Long + if (text.charAt(0) == '-') { + if (text.length() > MAX_LENGTH_LONG_NEGATIVE) { + return null; + } + } else if (text.length() > MAX_LENGTH_LONG) { + return null; + } + + // Handle a leading minus sign + int i = 0; + if (text.charAt(0) == '-') { + if (text.length() > 1) { + i++; + } else { + return null; + } + } + + // Check each character is a digit + for (; i < text.length(); i++) { + if (!Character.isDigit(text.charAt(i))) { + return null; + } + } + + // It looks like it might be a Long, so give it a go + try { + return Long.parseLong(text); + } catch (Exception e) { + // It isn't a Long + return null; + } + } + + private static final int MAX_LENGTH_INTEGER = String.valueOf(Integer.MAX_VALUE).length(); + private static final int MAX_LENGTH_INTEGER_NEGATIVE = String.valueOf(Integer.MAX_VALUE).length() + 1; + + /** + * Try to get an Integer value efficiently, avoiding Exceptions + */ + private static Integer getInteger(String text) { + // handle an empty string + if (text.isEmpty()) return null; + + // if the text is too long it can't be an Integer + if (text.charAt(0) == '-') { + if (text.length() > MAX_LENGTH_INTEGER_NEGATIVE) { + return null; + } + } + else if (text.length() > MAX_LENGTH_INTEGER) { + return null; + } + + // Handle a leading minus sign + int i = 0; + if (text.charAt(0) == '-') { + if (text.length() > 1) { + i++; + } else { + return null; + } + } + + // Check each character is a digit + for (; i < text.length(); i++) { + if (!Character.isDigit(text.charAt(i))) { + return null; + } + } + + // It looks like it might be an Integer, so give it a go + try { + return Integer.parseInt(text); + } catch (Exception e) { + // It isn't an Integer + return null; + } + } + + /** + * Try to get a Double value efficiently, avoiding Exceptions + */ + private static Double getDouble(String text) { + boolean foundDP = false; + boolean foundExp = false; + + // handle an empty string + if (text.isEmpty()) + return null; + + // Handle a leading minus sign + int i = 0; + if (text.charAt(0) == '-') { + if (text.length() > 1) + i++; + else + return null; + } + + // Check each character is a digit + for (; i < text.length(); i++) { + char next = text.charAt(i); + if (!Character.isDigit(next)) { + if (next == '.') { + if (foundDP) + return null; + foundDP = true; + } else if (next == 'E' || next == 'e') { + if (foundExp) + return null; + foundExp = true; + } else + return null; + } + } - return primitive; + // It looks like it might be a Double, so give it a go + try { + return Double.parseDouble(text); + } catch (Exception e) { + // It isn't a Double + return null; + } } } diff -Nru libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamWriter.java libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamWriter.java --- libjettison-java-1.4.1/src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamWriter.java 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamWriter.java 2022-12-06 23:20:44.000000000 +0000 @@ -243,6 +243,10 @@ } public void writeStartElement(String prefix, String local, String ns) throws XMLStreamException { + if (current == null) { + this.writeStartDocument(); + } + String parentKey = current.getTreeKey(); stack.push(current); String key = convention.createKey(prefix, ns, local); diff -Nru libjettison-java-1.4.1/src/test/java/org/codehaus/jettison/json/JSONArrayTest.java libjettison-java-1.5.3/src/test/java/org/codehaus/jettison/json/JSONArrayTest.java --- libjettison-java-1.4.1/src/test/java/org/codehaus/jettison/json/JSONArrayTest.java 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/src/test/java/org/codehaus/jettison/json/JSONArrayTest.java 2022-12-06 23:20:44.000000000 +0000 @@ -43,6 +43,30 @@ String expectedValue = "[\"a string with / character\",{\"key\":\"http://example.com/foo\"}]"; assertEquals(expectedValue, array.toString()); } - - + + public void testInfiniteLoop() { + String str = "[*/*A25] **"; + try { + new JSONArray(str); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + public void testInfiniteLoop2() { + String str = "[/"; + try { + new JSONArray(str); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + public void testIssue52() throws JSONException { + JSONObject.setGlobalRecursionDepthLimit(10); + new JSONArray("[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {a:10}]"); + } + } diff -Nru libjettison-java-1.4.1/src/test/java/org/codehaus/jettison/json/JSONObjectTest.java libjettison-java-1.5.3/src/test/java/org/codehaus/jettison/json/JSONObjectTest.java --- libjettison-java-1.4.1/src/test/java/org/codehaus/jettison/json/JSONObjectTest.java 2020-03-18 16:42:02.000000000 +0000 +++ libjettison-java-1.5.3/src/test/java/org/codehaus/jettison/json/JSONObjectTest.java 2022-12-06 23:20:44.000000000 +0000 @@ -2,7 +2,13 @@ import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class JSONObjectTest extends TestCase { + public void testEquals() throws Exception { JSONObject aJsonObj = new JSONObject("{\"x\":\"y\"}"); JSONObject bJsonObj = new JSONObject("{\"x\":\"y\"}"); @@ -81,7 +87,11 @@ public void testSlashEscapingTurnedOnByDefault() throws Exception { JSONObject obj = new JSONObject(); obj.put("key", "http://example.com/foo"); - assertEquals(obj.toString(), "{\"key\":\"http:\\/\\/example.com\\/foo\"}"); + assertEquals("{\"key\":\"http:\\/\\/example.com\\/foo\"}", obj.toString()); + + obj = new JSONObject(); + obj.put("key", "\\\\"); + assertEquals("{\"key\":\"\\\\\\\\\"}", obj.toString()); } public void testForwardSlashEscapingModifiedfBySetter() throws Exception { @@ -92,5 +102,113 @@ assertEquals(obj.toString(), "{\"key\":\"http://example.com/foo\"}"); obj.setEscapeForwardSlashAlways(true); assertEquals(obj.toString(), "{\"key\":\"http:\\/\\/example.com\\/foo\"}"); - } + } + + public void testMalformedObject() throws Exception { + try { + new JSONObject("{/"); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + public void testMalformedObject2() throws Exception { + try { + new JSONObject("{x"); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + public void testMalformedObject3() throws Exception { + try { + new JSONObject("{/x"); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + public void testMalformedObject4() throws Exception { + try { + new JSONObject("{/*"); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + public void testMalformedObject5() throws Exception { + try { + new JSONObject("{//"); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + public void testMalformedArray() throws Exception { + try { + new JSONObject("{[/"); + fail("Failure expected on malformed JSON"); + } catch (JSONException ex) { + // expected + } + } + + // https://github.com/jettison-json/jettison/issues/52 + public void testIssue52() throws Exception { + Map map = new HashMap<>(); + map.put("t",map); + new JSONObject(map); + } + + // https://github.com/jettison-json/jettison/issues/52 + public void testIssue52Recursive() throws Exception { + try { + Map map = new HashMap<>(); + Map map2 = new HashMap<>(); + map.put("t", map2); + map2.put("t", map); + new JSONObject(map); + fail("Failure expected"); + } catch (JSONException e) { + assertTrue(e.getMessage().contains("JSONObject has reached recursion depth limit")); + // expected + } + } + + // https://github.com/jettison-json/jettison/issues/45 + public void testFuzzerTestCase() throws Exception, JSONException { + try { + new JSONObject("{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{\"G\":[30018084,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,38,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,0]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,340282366920938463463374607431768211458,6,1,1]}:[32768,1,1,6,1,0]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,340282366920938463463374607431768211458,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,9 68,1,127,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,6,32768,1,1,6,1,9223372036854775807]}:[3,1,6,32768,1,1,6,1,1]}:[3,1,10,32768,1,1,6,1,1]}"); + fail("Failure expected"); + } catch (JSONException ex) { + // expected + assertTrue(ex.getMessage().contains("Expected a key")); + } + } + + public void testFuzzerTestCase2() throws Exception { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 100000; i++) { + sb.append("{\"key\":"); + } + try { + new JSONObject(sb.toString()); + fail("Failure expected"); + } catch (JSONException e) { + assertTrue(e.getMessage().contains("JSONTokener has reached recursion depth limit")); + // expected + } + } + + public void testIssue58() throws JSONException { + Map map = new HashMap<>(); + map.put("request", "{\"exclude\":[\".\",\"?\",\"+\",\"*\",\"|\",\"{\",\"}\",\"[\",\"]\",\"(\",\")\",\"\\\"\",\"\\\\\",\"#\",\"@\",\"&\",\"<\",\">\",\"~\"]}"); + JSONObject jsonObject = new JSONObject(map); + JSONObject jsonObject1 = new JSONObject(jsonObject.toString()); + } } diff -Nru libjettison-java-1.4.1/src/test/java/org/codehaus/jettison/json/JSONTokenerTest.java libjettison-java-1.5.3/src/test/java/org/codehaus/jettison/json/JSONTokenerTest.java --- libjettison-java-1.4.1/src/test/java/org/codehaus/jettison/json/JSONTokenerTest.java 1970-01-01 00:00:00.000000000 +0000 +++ libjettison-java-1.5.3/src/test/java/org/codehaus/jettison/json/JSONTokenerTest.java 2022-12-06 23:20:44.000000000 +0000 @@ -0,0 +1,27 @@ +package org.codehaus.jettison.json; + +import java.math.BigDecimal; + +import junit.framework.TestCase; + +public class JSONTokenerTest extends TestCase { + + public void testDoublePrecision() throws Exception { + JSONTokener doubleTokener = new JSONTokener("9999999999999.9999"); + Object nextValue = doubleTokener.nextValue(); + assertEquals(Double.class, nextValue.getClass()); + assertEquals(Double.valueOf("1.0E13"), nextValue); + } + + public void testBigDecimalPrecision() throws Exception { + JSONTokener bigDecimalTokener = new JSONTokener("9999999999999.9999") { + { + this.useBigDecimal = true; + } + }; + Object nextValue = bigDecimalTokener.nextValue(); + assertEquals(BigDecimal.class, nextValue.getClass()); + assertEquals(new BigDecimal("9999999999999.9999"), nextValue); + } + +}