Skip to content

Commit 4ab2c72

Browse files
committed
Fix percent encoding and pass original tests
1 parent 832a736 commit 4ab2c72

File tree

2 files changed

+20
-12
lines changed

2 files changed

+20
-12
lines changed

src/main/java/com/github/packageurl/PackageURL.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -434,11 +434,11 @@ private String canonicalize(boolean coordinatesOnly) {
434434
}
435435
purl.append("/");
436436
if (namespace != null) {
437-
purl.append(encodePath(namespace));
437+
purl.append(encodePath(namespace, ":"));
438438
purl.append("/");
439439
}
440440
if (name != null) {
441-
purl.append(percentEncode(name));
441+
purl.append(percentEncode(name, ":"));
442442
}
443443
if (version != null) {
444444
purl.append("@").append(percentEncode(version));
@@ -449,36 +449,44 @@ private String canonicalize(boolean coordinatesOnly) {
449449
qualifiers.entrySet().stream().forEachOrdered((entry) -> {
450450
purl.append(entry.getKey().toLowerCase());
451451
purl.append("=");
452-
purl.append(percentEncode(entry.getValue()));
452+
purl.append(percentEncode(entry.getValue(), ":/"));
453453
purl.append("&");
454454
});
455455
purl.setLength(purl.length() - 1);
456456
}
457457
if (subpath != null) {
458-
purl.append("#").append(encodePath(subpath));
458+
purl.append("#").append(encodePath(subpath, "?#+&="));
459459
}
460460
}
461461
return purl.toString();
462462
}
463463

464+
private String percentEncode(final String input, final Charset charset, final String charsToExclude) {
465+
return uriEncode(input, charset, charsToExclude);
466+
}
467+
468+
private String percentEncode(final String input, final String charsToExclude) {
469+
return percentEncode(input, StandardCharsets.UTF_8, charsToExclude);
470+
}
471+
464472
/**
465473
* Encodes the input in conformance with RFC 3986.
466474
*
467475
* @param input the String to encode
468476
* @return an encoded String
469477
*/
470478
private String percentEncode(final String input) {
471-
return uriEncode(input, StandardCharsets.UTF_8);
479+
return percentEncode(input, StandardCharsets.UTF_8, null);
472480
}
473481

474-
private static String uriEncode(String source, Charset charset) {
482+
private static String uriEncode(String source, Charset charset, String chars) {
475483
if (source == null || source.length() == 0) {
476484
return source;
477485
}
478486

479487
StringBuilder builder = new StringBuilder();
480488
for (byte b : source.getBytes(charset)) {
481-
if (isUnreserved(b)) {
489+
if (isUnreserved(b) || chars != null && chars.indexOf(b) != -1) {
482490
builder.append((char) b);
483491
}
484492
else {
@@ -491,7 +499,7 @@ private static String uriEncode(String source, Charset charset) {
491499
}
492500

493501
private static boolean isUnreserved(int c) {
494-
return (isAlpha(c) || isDigit(c) || '-' == c || '.' == c || '_' == c || '~' == c || ':' == c || '/' == c);
502+
return (isAlpha(c) || isDigit(c) || '-' == c || '.' == c || '_' == c || '~' == c);
495503
}
496504

497505
private static boolean isAlpha(int c) {
@@ -718,8 +726,8 @@ private String[] parsePath(final String value, final boolean isSubpath) throws M
718726
.toArray(String[]::new);
719727
}
720728

721-
private String encodePath(final String path) {
722-
return Arrays.stream(path.split("/")).map(segment -> percentEncode(segment)).collect(Collectors.joining("/"));
729+
private String encodePath(final String path, String chars) {
730+
return Arrays.stream(path.split("/")).map(segment -> percentEncode(segment, chars)).collect(Collectors.joining("/"));
723731
}
724732

725733
/**

src/test/resources/test-suite-data.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
},
8686
{
8787
"description": "docker uses qualifiers and hash image id as versions",
88-
"purl": "pkg:docker/customer/dockerimage@sha256:244fd47e07d1004f0aed9c?repository_url=gcr.io",
89-
"canonical_purl": "pkg:docker/customer/dockerimage@sha256:244fd47e07d1004f0aed9c?repository_url=gcr.io",
88+
"purl": "pkg:docker/customer/dockerimage@sha256%3A244fd47e07d1004f0aed9c?repository_url=gcr.io",
89+
"canonical_purl": "pkg:docker/customer/dockerimage@sha256%3A244fd47e07d1004f0aed9c?repository_url=gcr.io",
9090
"type": "docker",
9191
"namespace": "customer",
9292
"name": "dockerimage",

0 commit comments

Comments
 (0)