Skip to content

Commit 00c46dd

Browse files
committed
Silently drop empty and dot segments for subpath
This change makes the two test pass that were introduced in package-url/purl-spec#368. See also package-url/purl-spec#404.
1 parent d23a0ee commit 00c46dd

File tree

2 files changed

+25
-22
lines changed

2 files changed

+25
-22
lines changed

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

+22-17
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.util.Map;
3232
import java.util.Objects;
3333
import java.util.TreeMap;
34-
import java.util.regex.Pattern;
3534
import java.util.stream.Collectors;
3635

3736
/**
@@ -51,9 +50,7 @@
5150
* @since 1.0.0
5251
*/
5352
public final class PackageURL implements Serializable {
54-
5553
private static final long serialVersionUID = 3243226021636427586L;
56-
private static final Pattern PATH_SPLITTER = Pattern.compile("/");
5754

5855
/**
5956
* Constructs a new PackageURL object by parsing the specified string.
@@ -347,22 +344,31 @@ private String validatePath(final String value, final boolean isSubpath) throws
347344
return validatePath(value.split("/"), isSubpath);
348345
}
349346

350-
private String validatePath(final String[] segments, final boolean isSubpath) throws MalformedPackageURLException {
347+
private static boolean isValidSegment(String segment, boolean isSubpath) {
348+
return (!isSubpath || (!segment.isEmpty() && !".".equals(segment) && !"..".equals(segment)));
349+
}
350+
351+
private static String validatePath(final String[] segments, final boolean isSubpath) throws MalformedPackageURLException {
351352
if (segments == null || segments.length == 0) {
352353
return null;
353354
}
355+
354356
try {
355357
return Arrays.stream(segments)
356358
.map(segment -> {
357-
if (isSubpath && ("..".equals(segment) || ".".equals(segment))) {
358-
throw new ValidationException("Segments in the subpath may not be a period ('.') or repeated period ('..')");
359-
} else if (segment.contains("/")) {
360-
throw new ValidationException("Segments in the namespace and subpath may not contain a forward slash ('/')");
361-
} else if (segment.isEmpty()) {
362-
throw new ValidationException("Segments in the namespace and subpath may not be empty");
359+
if (!isSubpath) {
360+
if ("..".equals(segment) || ".".equals(segment)) {
361+
throw new ValidationException("Segments in the namespace may not be a period ('.') or repeated period ('..')");
362+
} else if (segment.contains("/")) {
363+
throw new ValidationException("Segments in the namespace and subpath may not contain a forward slash ('/')");
364+
} else if (segment.isEmpty()) {
365+
throw new ValidationException("Segments in the namespace and subpath may not be empty");
366+
}
363367
}
364368
return segment;
365-
}).collect(Collectors.joining("/"));
369+
})
370+
.filter(segment1 -> isValidSegment(segment1, isSubpath))
371+
.collect(Collectors.joining("/"));
366372
} catch (ValidationException e) {
367373
throw new MalformedPackageURLException(e);
368374
}
@@ -478,7 +484,7 @@ private static boolean isDigit(int c) {
478484
* @param input the value String to decode
479485
* @return a decoded String
480486
*/
481-
private String percentDecode(final String input) {
487+
private static String percentDecode(final String input) {
482488
if (input == null) {
483489
return null;
484490
}
@@ -629,14 +635,13 @@ private Map<String, String> parseQualifiers(final String encodedString) throws M
629635
}
630636
}
631637

632-
@SuppressWarnings("StringSplitter")//reason: surprising behavior is okay in this case
633-
private String[] parsePath(final String value, final boolean isSubpath) throws MalformedPackageURLException {
638+
private static String[] parsePath(final String value, final boolean isSubpath) {
634639
if (value == null || value.isEmpty()) {
635640
return null;
636641
}
637-
return PATH_SPLITTER.splitAsStream(value)
638-
.filter(segment -> !segment.isEmpty() && !(isSubpath && (".".equals(segment) || "..".equals(segment))))
639-
.map(segment -> percentDecode(segment))
642+
643+
return Arrays.stream(percentDecode(value).split("/"))
644+
.filter(segment ->isValidSegment(segment, isSubpath))
640645
.toArray(String[]::new);
641646
}
642647

src/test/java/com/github/packageurl/PackageURLTest.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,9 @@ public void testConstructorWithInvalidNumberType() throws MalformedPackageURLExc
210210
}
211211

212212
@Test
213-
public void testConstructorWithInvalidSubpath() throws MalformedPackageURLException {
214-
exception.expect(MalformedPackageURLException.class);
215-
216-
PackageURL purl = new PackageURL("pkg:GOLANG/google.golang.org/genproto@abcdedf#invalid/%2F/subpath");
217-
Assert.fail("constructor with `invalid/%2F/subpath` should have thrown an error and this line should not be reached");
213+
public void testConstructorWithValidSubpathContainingSlashIsDropped() throws MalformedPackageURLException {
214+
PackageURL purl = new PackageURL("pkg:GOLANG/google.golang.org/genproto@abcdedf#valid/%2F/subpath");
215+
Assert.assertEquals("valid/subpath", purl.getSubpath());
218216
}
219217

220218

0 commit comments

Comments
 (0)