Skip to content

Commit 1c995f7

Browse files
fix: replace java-json-tools to fix security issues (#2416)
1 parent 1afdcdd commit 1c995f7

23 files changed

Lines changed: 792 additions & 159 deletions

‎CLAUDE.md‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Branch context
6+
7+
This is the **v1.x line** (Swagger 2.0 / OpenAPI 2, `io.swagger:*`, swagger-core 1.6.x, Java 8 source/target).
8+
9+
## Commands
10+
11+
```bash
12+
mvn -B verify # full build + tests (what CI runs; see .github/workflows/maven-v1.yml)
13+
mvn -B verify -Dsurefire.forkCount=4 -DargLine="-XX:-OmitStackTraceInFastThrow" # CI flags
14+
mvn -pl modules/swagger-parser -am test # one module (+ upstream deps)
15+
mvn -pl modules/swagger-compat-spec-parser test -Dtest=JsonPatchTest # single test class
16+
mvn -pl modules/swagger-parser test -Dtest=SwaggerParserTest#methodName # single test method
17+
```
18+
19+
Tests mix TestNG (6.9.6), JUnit 4, JMockit and WireMock. CI builds on JDK 8 (Temurin) only.
20+
21+
## Modules (root `pom.xml`)
22+
23+
- `swagger-parser-safe-url-resolver` — SSRF-protection layer for fetching remote `$ref`s/specs (allow/deny host matchers). Built first; `swagger-parser` depends on it.
24+
- `swagger-parser` — reads Swagger 2.0 JSON/YAML into swagger-core `Swagger` POJOs and resolves `$ref`s.
25+
- `swagger-compat-spec-parser` — reads legacy Swagger 1.1/1.2 (resource listing + API declarations) and migrates them to 2.0.
26+
27+
## Architecture
28+
29+
**Extension-based entry point.** `SwaggerParser` (`io.swagger.parser`) is a facade: it discovers `SwaggerParserExtension` implementations via `ServiceLoader` (`META-INF/services/io.swagger.parser.SwaggerParserExtension`) and tries each in order until one returns a non-null result. `Swagger20Parser` is the built-in 2.0 implementation; `SwaggerCompatConverter` (compat module, registered in its own `META-INF/services` file) plugs in the same way, which is why adding the compat jar enables 1.x input with no code change.
30+
31+
**Parse → resolve pipeline (swagger-parser).** `Swagger20Parser` deserializes via `SwaggerDeserializer` (hand-written tree walker that collects validation messages into `SwaggerDeserializationResult` rather than throwing). If `ParseOptions.resolve` is set, `SwaggerResolver` runs the `processors/*` (Definitions/Paths/Operation/Parameter/Response/Model/Property/ExternalRefProcessor) over the model, using `ResolverCache` to load and cache external refs (file, classpath, remote via `RemoteUrl`, with auth headers from `AuthorizationValue`). `InlineModelResolver` optionally flattens inline schemas into `definitions`.
32+
33+
**Legacy migration (compat module).** `SwaggerCompatConverter` downloads/reads 1.x docs (`APIFileDownload`), then `transform/migrate/*` migrators (`V11*`, `ApiObjectMigrator`, …) rewrite the JSON tree to 1.2 shape using JSON patches in `src/main/resources/patches`, validated against JSON schemas in `resources/schemas`, before mapping to `Swagger`.
34+
35+
**JSON patch/schema support** lives in `io.swagger.transform.util`: `JsonPatch` (own RFC-6902 impl), `JacksonSupport`, `MutableJsonTree`, `SwaggerJsonSchemaFactory`. Don't use `com.github.fge.*` (java-json-tools) imports. When comparing `JsonNode`s in TestNG use `equals()`/`assertTrue`, not `assertEquals` (it picks the `Iterable` overload and ignores object member names).
36+
37+
## Release / CI
38+
39+
Releases are scripted via GitHub workflows (`prepare-release-v1.yml`, `release-v1.yml`, `next-snapshot-v1.yml`) calling `CI/*.sh|py`; see `CI/CI.md`. Don't hand-edit versions for releases.
40+
41+
`TRUST_ALL=true` env var disables SSL cert validation for remote fetches (documented in README).

‎modules/swagger-compat-spec-parser/pom.xml‎

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
<relativePath>../..</relativePath>
99
</parent>
1010
<modelVersion>4.0.0</modelVersion>
11-
<groupId>io.swagger</groupId>
1211
<artifactId>swagger-compat-spec-parser</artifactId>
1312
<packaging>jar</packaging>
1413
<name>swagger-compat-spec-parser</name>
@@ -25,26 +24,21 @@
2524
<version>${project.parent.version}</version>
2625
</dependency>
2726
<dependency>
28-
<groupId>com.github.java-json-tools</groupId>
27+
<groupId>com.networknt</groupId>
2928
<artifactId>json-schema-validator</artifactId>
30-
<version>2.2.14</version>
29+
<version>2.0.8</version>
30+
<!-- use the Jackson version provided by swagger-core -->
3131
<exclusions>
3232
<exclusion>
33-
<groupId>org.mozilla</groupId>
34-
<artifactId>rhino</artifactId>
33+
<groupId>com.fasterxml.jackson.core</groupId>
34+
<artifactId>jackson-databind</artifactId>
35+
</exclusion>
36+
<exclusion>
37+
<groupId>com.fasterxml.jackson.dataformat</groupId>
38+
<artifactId>jackson-dataformat-yaml</artifactId>
3539
</exclusion>
3640
</exclusions>
3741
</dependency>
38-
<dependency>
39-
<groupId>org.mozilla</groupId>
40-
<artifactId>rhino</artifactId>
41-
<version>1.7.15.1</version>
42-
</dependency>
43-
<dependency>
44-
<groupId>com.github.java-json-tools</groupId>
45-
<artifactId>json-patch</artifactId>
46-
<version>1.13</version>
47-
</dependency>
4842
<dependency>
4943
<groupId>junit</groupId>
5044
<artifactId>junit</artifactId>

‎modules/swagger-compat-spec-parser/src/main/java/io/swagger/transform/migrate/ApiObjectMigrator.java‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package io.swagger.transform.migrate;
22

3+
import com.fasterxml.jackson.core.JsonPointer;
34
import com.fasterxml.jackson.databind.JsonNode;
45
import com.fasterxml.jackson.databind.node.ArrayNode;
56
import com.fasterxml.jackson.databind.node.ObjectNode;
6-
import com.github.fge.jackson.jsonpointer.JsonPointer;
77
import io.swagger.transform.util.MutableJsonTree;
88
import io.swagger.transform.util.SwaggerMigrationException;
99
import io.swagger.util.Json;
@@ -69,17 +69,17 @@ public JsonNode migrate(@Nonnull final JsonNode input)
6969
/*
7070
* Migrate response messages, if any
7171
*/
72-
JsonPointer ptr = JsonPointer.of("responseMessages");
72+
JsonPointer ptr = JsonPointer.compile("/responseMessages");
7373

74-
if (!ptr.path(tree.getBaseNode()).isMissingNode()) {
74+
if (!tree.getBaseNode().at(ptr).isMissingNode()) {
7575
tree.setPointer(ptr);
7676
tree.applyMigratorToElements(renameMember("reason", "message"));
7777
}
7878

7979
/*
8080
* Migrate parameters
8181
*/
82-
ptr = JsonPointer.of("parameters");
82+
ptr = JsonPointer.compile("/parameters");
8383
tree.setPointer(ptr);
8484
tree.applyMigratorToElements(parametersMigrator);
8585

‎modules/swagger-compat-spec-parser/src/main/java/io/swagger/transform/migrate/ApiOperationMigrator.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.swagger.transform.migrate;
22

3+
import com.fasterxml.jackson.core.JsonPointer;
34
import com.fasterxml.jackson.databind.JsonNode;
4-
import com.github.fge.jackson.jsonpointer.JsonPointer;
55
import io.swagger.transform.util.MutableJsonTree;
66
import io.swagger.transform.util.SwaggerMigrationException;
77

@@ -19,7 +19,7 @@ public final class ApiOperationMigrator
1919
public JsonNode migrate(@Nonnull final JsonNode input)
2020
throws SwaggerMigrationException {
2121
final MutableJsonTree tree = new MutableJsonTree(input);
22-
tree.setPointer(JsonPointer.of("operations"));
22+
tree.setPointer(JsonPointer.compile("/operations"));
2323
tree.applyMigratorToElements(migrator);
2424
return tree.getBaseNode();
2525
}

‎modules/swagger-compat-spec-parser/src/main/java/io/swagger/transform/migrate/V11AllowableValuesMigrator.java‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.ObjectMapper;
5-
import com.github.fge.jackson.JacksonUtils;
6-
import com.github.fge.jackson.jsonpointer.JsonPointer;
7-
import com.github.fge.jsonpatch.JsonPatch;
5+
import com.fasterxml.jackson.core.JsonPointer;
6+
import io.swagger.transform.util.JacksonSupport;
7+
import io.swagger.transform.util.JsonPatch;
88
import io.swagger.transform.util.SwaggerMigrationException;
99
import io.swagger.transform.util.SwaggerMigrators;
1010

@@ -41,7 +41,8 @@
4141
*/
4242
public final class V11AllowableValuesMigrator
4343
implements SwaggerMigrator {
44-
private static final ObjectMapper MAPPER = JacksonUtils.newMapper();
44+
private static final ObjectMapper MAPPER = JacksonSupport.newMapper();
45+
private static final JsonPointer VALUE_TYPE = JsonPointer.compile("/allowableValues/valueType");
4546
private static final JsonPatch PATCH;
4647

4748
@Nonnull
@@ -52,8 +53,7 @@ public JsonNode migrate(@Nonnull final JsonNode input)
5253
if (!input.has("allowableValues")) {
5354
return input;
5455
}
55-
final String valueType = JsonPointer.of("allowableValues", "valueType")
56-
.get(input).textValue();
56+
final String valueType = input.at(VALUE_TYPE).textValue();
5757
if ("LIST".equals(valueType)) {
5858
return SwaggerMigrators.fromPatch(PATCH).migrate(input);
5959
}

‎modules/swagger-compat-spec-parser/src/main/java/io/swagger/transform/migrate/V11ApiDeclarationMigrator.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.swagger.transform.migrate;
22

3+
import com.fasterxml.jackson.core.JsonPointer;
34
import com.fasterxml.jackson.databind.JsonNode;
4-
import com.github.fge.jackson.jsonpointer.JsonPointer;
55
import io.swagger.transform.util.MutableJsonTree;
66
import io.swagger.transform.util.SwaggerMigrationException;
77

@@ -27,7 +27,7 @@ public JsonNode migrate(@Nonnull final JsonNode input)
2727
tree.applyMigrator(patchFromResource("/patches/v1.1/versionChange" +
2828
".json"));
2929

30-
tree.setPointer(JsonPointer.of("apis"));
30+
tree.setPointer(JsonPointer.compile("/apis"));
3131
tree.applyMigratorToElements(migrator);
3232

3333
return tree.getBaseNode();

‎modules/swagger-compat-spec-parser/src/main/java/io/swagger/transform/migrate/V11ResourceListingMigrator.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.swagger.transform.migrate;
22

3+
import com.fasterxml.jackson.core.JsonPointer;
34
import com.fasterxml.jackson.databind.JsonNode;
45
import com.fasterxml.jackson.databind.node.ObjectNode;
5-
import com.github.fge.jackson.jsonpointer.JsonPointer;
66
import com.google.common.base.Preconditions;
77
import com.google.common.base.Strings;
88
import com.google.common.net.InternetDomainName;
@@ -107,7 +107,7 @@ public JsonNode migrate(@Nonnull final JsonNode input)
107107
}
108108

109109
migrator = new PathAppenderMigrator(basePath);
110-
tree.setPointer(JsonPointer.of("apis"));
110+
tree.setPointer(JsonPointer.compile("/apis"));
111111
tree.applyMigratorToElements(migrator);
112112
}
113113

‎modules/swagger-compat-spec-parser/src/main/java/io/swagger/transform/migrate/V11TypeMigrator.java‎

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
import com.fasterxml.jackson.core.type.TypeReference;
44
import com.fasterxml.jackson.databind.JsonNode;
55
import com.fasterxml.jackson.databind.ObjectMapper;
6-
import com.github.fge.jackson.JacksonUtils;
7-
import com.github.fge.jackson.JsonLoader;
8-
import com.github.fge.jsonpatch.JsonPatch;
9-
import com.github.fge.jsonpatch.JsonPatchException;
106
import com.google.common.base.Optional;
7+
import io.swagger.transform.util.JacksonSupport;
8+
import io.swagger.transform.util.JsonPatch;
119
import io.swagger.transform.util.SwaggerMigrationException;
1210
import io.swagger.transform.util.SwaggerMigrators;
1311

@@ -32,7 +30,7 @@
3230
*/
3331
public final class V11TypeMigrator
3432
implements SwaggerMigrator {
35-
private static final ObjectMapper MAPPER = JacksonUtils.newMapper();
33+
private static final ObjectMapper MAPPER = JacksonSupport.newMapper();
3634
private static final TypeReference<Map<String, JsonPatch>> TYPE_REF
3735
= new TypeReference<Map<String, JsonPatch>>() {
3836
};
@@ -43,10 +41,10 @@ public V11TypeMigrator() {
4341
try {
4442
JsonNode node;
4543

46-
node = JsonLoader.fromResource("/patches/v1.1/dataType.json");
44+
node = JacksonSupport.fromResource("/patches/v1.1/dataType.json");
4745
patches = MAPPER.readValue(node.traverse(), TYPE_REF);
4846

49-
node = JsonLoader.fromResource("/patches/other/fileTypePatch.json");
47+
node = JacksonSupport.fromResource("/patches/other/fileTypePatch.json");
5048
fileTypePatch = MAPPER.readValue(node.traverse(), JsonPatch.class);
5149
} catch (IOException e) {
5250
throw new RuntimeException("failed to load the necessary file", e);
@@ -83,11 +81,7 @@ private JsonNode postMigrate(final JsonNode input)
8381
if (!"File".equals(typeNode.textValue())) {
8482
return input;
8583
}
86-
try {
87-
return fileTypePatch.apply(input);
88-
} catch (JsonPatchException e) {
89-
throw new SwaggerMigrationException(e);
90-
}
84+
return fileTypePatch.apply(input);
9185
}
9286

9387
static {

‎modules/swagger-compat-spec-parser/src/main/java/io/swagger/transform/util/APIFileDownload.java‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.ObjectMapper;
55
import com.fasterxml.jackson.databind.ObjectWriter;
6-
import com.github.fge.jackson.JacksonUtils;
76

87
import java.io.BufferedWriter;
98
import java.io.IOException;
@@ -17,7 +16,7 @@
1716

1817
public final class APIFileDownload {
1918
private static final Path RESOURCE_PATH;
20-
private static final ObjectMapper MAPPER = JacksonUtils.newMapper();
19+
private static final ObjectMapper MAPPER = JacksonSupport.newMapper();
2120
private static final ObjectWriter JSONWRITER
2221
= MAPPER.writerWithDefaultPrettyPrinter();
2322

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package io.swagger.transform.util;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.DeserializationFeature;
6+
import com.fasterxml.jackson.databind.JsonNode;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import com.fasterxml.jackson.databind.ObjectWriter;
9+
import com.fasterxml.jackson.databind.SerializationFeature;
10+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
11+
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.net.URL;
15+
import java.util.Objects;
16+
17+
/**
18+
* Jackson utilities used by the migration and validation code
19+
*
20+
* <p>The mapper configuration matches the one previously provided by
21+
* jackson-coreutils, so that migrated documents are serialized the same way
22+
* (in particular, floating point numbers are read as {@link
23+
* java.math.BigDecimal}s and written in plain notation).</p>
24+
*/
25+
public final class JacksonSupport {
26+
private static final ObjectMapper MAPPER = newMapper();
27+
private static final ObjectWriter WRITER = MAPPER.writerWithDefaultPrettyPrinter();
28+
29+
private JacksonSupport() {
30+
}
31+
32+
/**
33+
* Return a newly configured {@link ObjectMapper}
34+
*
35+
* @return a new mapper
36+
*/
37+
public static ObjectMapper newMapper() {
38+
return new ObjectMapper()
39+
.setNodeFactory(JsonNodeFactory.instance)
40+
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
41+
.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)
42+
.enable(SerializationFeature.INDENT_OUTPUT);
43+
}
44+
45+
/**
46+
* Return the node factory used by mappers created by this class
47+
*
48+
* @return the node factory
49+
*/
50+
public static JsonNodeFactory nodeFactory() {
51+
return JsonNodeFactory.instance;
52+
}
53+
54+
/**
55+
* Pretty print a JSON value
56+
*
57+
* @param node the value
58+
* @return the pretty printed value
59+
*/
60+
public static String prettyPrint(final JsonNode node) {
61+
Objects.requireNonNull(node, "node must not be null");
62+
try {
63+
return WRITER.writeValueAsString(node);
64+
} catch (JsonProcessingException e) {
65+
throw new IllegalStateException("cannot serialize JSON value", e);
66+
}
67+
}
68+
69+
/**
70+
* Read a JSON value from a classpath resource
71+
*
72+
* @param resource the resource path; must start with {@code /}
73+
* @return the JSON value
74+
* @throws IOException resource not found, or invalid JSON
75+
*/
76+
public static JsonNode fromResource(final String resource)
77+
throws IOException {
78+
Objects.requireNonNull(resource, "resource must not be null");
79+
if (!resource.startsWith("/")) {
80+
throw new IllegalArgumentException("resource path does not start with a '/'");
81+
}
82+
URL url = JacksonSupport.class.getResource(resource);
83+
if (url == null) {
84+
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
85+
if (loader != null) {
86+
url = loader.getResource(resource.substring(1));
87+
}
88+
}
89+
if (url == null) {
90+
throw new IOException("resource " + resource + " not found");
91+
}
92+
try (InputStream in = url.openStream()) {
93+
return MAPPER.readTree(in);
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)