Skip to content

Commit 8ab171f

Browse files
committed
Allow percentEncode to exclude characters
This passes the test suite as-is, where the colon is provided unencoded in certain components and encoded in others.
1 parent 94379c9 commit 8ab171f

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
@@ -403,11 +403,11 @@ private String canonicalize(boolean coordinatesOnly) {
403403
}
404404
purl.append("/");
405405
if (namespace != null) {
406-
purl.append(encodePath(namespace));
406+
purl.append(encodePath(namespace, ":"));
407407
purl.append("/");
408408
}
409409
if (name != null) {
410-
purl.append(percentEncode(name));
410+
purl.append(percentEncode(name, ":"));
411411
}
412412
if (version != null) {
413413
purl.append("@").append(percentEncode(version));
@@ -418,36 +418,44 @@ private String canonicalize(boolean coordinatesOnly) {
418418
qualifiers.entrySet().stream().forEachOrdered((entry) -> {
419419
purl.append(entry.getKey().toLowerCase());
420420
purl.append("=");
421-
purl.append(percentEncode(entry.getValue()));
421+
purl.append(percentEncode(entry.getValue(), ":/"));
422422
purl.append("&");
423423
});
424424
purl.setLength(purl.length() - 1);
425425
}
426426
if (subpath != null) {
427-
purl.append("#").append(encodePath(subpath));
427+
purl.append("#").append(encodePath(subpath, "?#+&="));
428428
}
429429
}
430430
return purl.toString();
431431
}
432432

433+
private String percentEncode(String input, Charset charset, String charsToExclude) {
434+
return uriEncode(input, charset, charsToExclude);
435+
}
436+
437+
private String percentEncode(String input, String charsToExclude) {
438+
return percentEncode(input, StandardCharsets.UTF_8, charsToExclude);
439+
}
440+
433441
/**
434442
* Encodes the input in conformance with RFC 3986.
435443
*
436444
* @param input the String to encode
437445
* @return an encoded String
438446
*/
439447
private String percentEncode(final String input) {
440-
return uriEncode(input, StandardCharsets.UTF_8);
448+
return uriEncode(input, StandardCharsets.UTF_8, null);
441449
}
442450

443-
private static String uriEncode(String source, Charset charset) {
451+
private static String uriEncode(String source, Charset charset, String chars) {
444452
if (source == null || source.length() == 0) {
445453
return source;
446454
}
447455

448456
StringBuilder builder = new StringBuilder();
449457
for (byte b : source.getBytes(charset)) {
450-
if (isUnreserved(b)) {
458+
if (isUnreserved(b) || chars != null && chars.indexOf(b) != -1) {
451459
builder.append((char) b);
452460
}
453461
else {
@@ -460,7 +468,7 @@ private static String uriEncode(String source, Charset charset) {
460468
}
461469

462470
private static boolean isUnreserved(int c) {
463-
return (isAlpha(c) || isDigit(c) || '-' == c || '.' == c || '_' == c || '~' == c || ':' == c || '/' == c);
471+
return (isAlpha(c) || isDigit(c) || '-' == c || '.' == c || '_' == c || '~' == c);
464472
}
465473

466474
private static boolean isAlpha(int c) {
@@ -640,8 +648,8 @@ private String[] parsePath(final String value, final boolean isSubpath) throws M
640648
.toArray(String[]::new);
641649
}
642650

643-
private String encodePath(final String path) {
644-
return Arrays.stream(path.split("/")).map(segment -> percentEncode(segment)).collect(Collectors.joining("/"));
651+
private String encodePath(final String path, String chars) {
652+
return Arrays.stream(path.split("/")).map(segment -> percentEncode(segment, chars)).collect(Collectors.joining("/"));
645653
}
646654

647655
/**

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)