Skip to content

Commit 7d8f909

Browse files
committed
Manually escape brackets in Jar URIs to work around ZipPath bug.
1 parent c5ead60 commit 7d8f909

3 files changed

Lines changed: 60 additions & 1 deletion

File tree

sm-test/src/test/java/net/minecraftforge/securemodules/test/TestSecureJarLoading.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.io.UncheckedIOException;
1616
import java.nio.charset.StandardCharsets;
17+
import java.nio.file.FileSystems;
1718
import java.nio.file.Files;
1819
import java.nio.file.Path;
1920
import java.nio.file.Paths;
@@ -103,6 +104,17 @@ void testEmptyJar() throws Exception {
103104
assertEquals(List.of(), seen, "Unexpected contents in empty jar");
104105
}
105106

107+
@Test // Jar has invalid URL characters, Java doesn't escape []'s properly so we need to force it to
108+
void testInvalidUri() throws Exception {
109+
var path = Paths.get("src/test/resources/invalid url[]%20.jar");
110+
var fileUri = path.toUri();
111+
var jar = (Jar)SecureJar.from(path);
112+
var uri = jar.getURI();
113+
assertEquals("jar:" + fileUri.toString() + "!/", uri.toString());
114+
var fs = FileSystems.getFileSystem(uri);
115+
assertEquals(jar.getRootPath().getFileSystem(), fs, "Non-matching file systems after URI cycle");
116+
}
117+
106118
@Test // Test opening the same file multiple times.
107119
void testSameJar() throws Exception {
108120
var path = Paths.get("src/test/resources/empty.jar");
261 Bytes
Binary file not shown.

src/main/java/cpw/mods/jarhandling/impl/Jar.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class Jar implements SecureJar {
5656
private final JarModuleDataProvider moduleDataProvider;
5757
private final Set<String> packages;
5858
private final List<Provider> providers;
59+
private final URI filesystemRootUri;
5960

6061
@Override
6162
public ModuleDataProvider moduleDataProvider() {
@@ -85,6 +86,7 @@ public Jar(Supplier<Manifest> defaultManifest, Function<SecureJar, JarMetadata>
8586
this.moduleDataProvider = new JarModuleDataProvider(this);
8687

8788
this.filesystemRoot = newFileSystem(pathfilter, validPaths);
89+
this.filesystemRootUri = fixUri(this.filesystemRoot);
8890
this.filesystemPrimary = validPaths[validPaths.length - 1];
8991
this.manifest = findManifest(validPaths, defaultManifest);
9092
this.nameOverrides = gatherVersionedFiles();
@@ -264,7 +266,7 @@ public Manifest getManifest() {
264266
}
265267

266268
public URI getURI() {
267-
return this.filesystemRoot.toUri();
269+
return this.filesystemRootUri;
268270
}
269271

270272
public ModuleDescriptor computeDescriptor() {
@@ -410,6 +412,51 @@ private Manifest findManifest(Path[] paths, Supplier<Manifest> defaultManifest)
410412
}
411413
}
412414

415+
// ZipPath has an issue where it decodes the URI to prevent double encoding, but then
416+
// uses a constructor of URI that doesn't encode brackets.
417+
// This causes an invalid URI when doing the Path.of(zipPath.toUri()).
418+
// So lets see if there are []s and then encode them.
419+
private static URI fixUri(Path path) {
420+
var uri = path.toUri();
421+
// If we're not jar (ZipPath) then trust it isn't broken
422+
// this could come back to bite me, but I don't want to go down the road later.
423+
if (!"jar".equals(uri.getScheme()))
424+
return uri;
425+
426+
var ssp = uri.getRawSchemeSpecificPart();
427+
var hasBrackets = ssp.indexOf('[') != -1 || ssp.indexOf(']') != -1;
428+
if (!hasBrackets)
429+
return uri;
430+
431+
int len = uri.getScheme().length() + 1;
432+
len += ssp.length() + 2; // At least one bracket, so 2 extra
433+
if (uri.getRawFragment() != null)
434+
len += uri.getRawFragment().length() + 1;
435+
436+
var buf = new StringBuilder(len);
437+
buf.append(uri.getScheme()).append(':');
438+
439+
for (int x = 0; x < ssp.length(); x++) {
440+
var c = ssp.charAt(x);
441+
if (c == '[')
442+
buf.append("%5B");
443+
else if (c == ']')
444+
buf.append("%5D");
445+
else
446+
buf.append(c);
447+
}
448+
449+
if (uri.getRawFragment() != null)
450+
buf.append('#').append(uri.getRawFragment());
451+
452+
try {
453+
// we have to use the full string because all other constructors try to do escaping, but don't escape [] correctly.
454+
return new URI(buf.toString());
455+
} catch (Exception ex) {
456+
throw new AssertionError(ex);
457+
}
458+
}
459+
413460
private record JarModuleDataProvider(Jar jar) implements ModuleDataProvider {
414461
@Override
415462
public String name() {

0 commit comments

Comments
 (0)