Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 66 additions & 21 deletions src/main/java/com/github/packageurl/PackageURL.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public PackageURL(final String type, final String namespace, final String name,
throws MalformedPackageURLException {

this.scheme = validateScheme("pkg");
this.type = validateType(type);
this.type = toLowerCase(validateType(type));
this.namespace = validateNamespace(namespace);
this.name = validateName(name);
this.version = validateVersion(version);
Expand Down Expand Up @@ -244,16 +244,19 @@ private String validateType(final String value) throws MalformedPackageURLExcept
if (value == null || value.isEmpty()) {
throw new MalformedPackageURLException("The PackageURL type cannot be null or empty");
}
if (value.charAt(0) >= '0' && value.charAt(0) <= '9') {

if (isDigit(value.charAt(0))) {
throw new MalformedPackageURLException("The PackageURL type cannot start with a number");
}
final String retVal = value.toLowerCase();
if (retVal.chars().anyMatch(c -> !(c == '.' || c == '+' || c == '-'
|| (c >= 'a' && c <= 'z')
|| (c >= '0' && c <= '9')))) {

if (!value.chars().allMatch(c -> (c == '.' || c == '+' || c == '-'
|| isUpperCase(c)
|| isLowerCase(c)
|| isDigit(c)))) {
throw new MalformedPackageURLException("The PackageURL type contains invalid characters");
}
return retVal;

return value;
}

private String validateNamespace(final String value) throws MalformedPackageURLException {
Expand All @@ -268,15 +271,14 @@ private String validateNamespace(final String[] values) throws MalformedPackageU
return null;
}
final String tempNamespace = validatePath(values, false);

String retVal;
switch (type) {
case StandardTypes.BITBUCKET:
case StandardTypes.DEBIAN:
case StandardTypes.GITHUB:
case StandardTypes.GOLANG:
case StandardTypes.RPM:
retVal = tempNamespace.toLowerCase();
retVal = tempNamespace != null ? toLowerCase(tempNamespace) : null;
break;
default:
retVal = tempNamespace;
Expand All @@ -295,10 +297,10 @@ private String validateName(final String value) throws MalformedPackageURLExcept
case StandardTypes.DEBIAN:
case StandardTypes.GITHUB:
case StandardTypes.GOLANG:
temp = value.toLowerCase();
temp = toLowerCase(value);
break;
case StandardTypes.PYPI:
temp = value.replaceAll("_", "-").toLowerCase();
temp = toLowerCase(value).replace('_', '-');
break;
default:
temp = value;
Expand Down Expand Up @@ -328,16 +330,15 @@ private Map<String, String> validateQualifiers(final Map<String, String> values)
return values;
}

private String validateKey(final String value) throws MalformedPackageURLException {
private void validateKey(final String value) throws MalformedPackageURLException {
if (value == null || value.isEmpty()) {
throw new MalformedPackageURLException("Qualifier key is invalid: " + value);
}
final String retValue = value.toLowerCase();
if ((value.charAt(0) >= '0' && value.charAt(0) <= '9')
|| !value.chars().allMatch(c -> (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '_')) {

if (isDigit(value.charAt(0))
|| !value.chars().allMatch(c -> isLowerCase(c) || (isDigit(c)) || c == '.' || c == '-' || c == '_')) {
throw new MalformedPackageURLException("Qualifier key is invalid: " + value);
}
return retValue;
}

private String validatePath(final String value, final boolean isSubpath) throws MalformedPackageURLException {
Expand Down Expand Up @@ -416,7 +417,7 @@ private String canonicalize(boolean coordinatesOnly) {
if (qualifiers != null && qualifiers.size() > 0) {
purl.append("?");
qualifiers.entrySet().stream().forEachOrdered((entry) -> {
purl.append(entry.getKey().toLowerCase());
purl.append(toLowerCase(entry.getKey()));
purl.append("=");
purl.append(percentEncode(entry.getValue()));
purl.append("&");
Expand Down Expand Up @@ -464,13 +465,56 @@ private static boolean isUnreserved(int c) {
}

private static boolean isAlpha(int c) {
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
return (isLowerCase(c) || isUpperCase(c));
}

private static boolean isDigit(int c) {
return (c >= '0' && c <= '9');
}

private static boolean isUpperCase(int c) {
return (c >= 'A' && c <= 'Z');
}

private static int indexOfFirstUpperCaseChar(String s) {
int length = s.length();

for (int i = 0; i < length; i++) {
if (!isLowerCase(s.charAt(i))) {
return i;
}
}

return -1;
}

private static boolean isLowerCase(int c) {
return (c >= 'a' && c <= 'z');
}

private static int toLowerCase(int c) {
return (c ^ 0x20);
}

private static String toLowerCase(String s) {
int pos = indexOfFirstUpperCaseChar(s);

if (pos == -1) {
return s;
}

char[] chars = s.toCharArray();
int length = chars.length;

for (int i = pos; i < length; i++) {
if (isUpperCase(chars[i])) {
chars[i] = (char) toLowerCase(chars[i]);
}
}

return new String(chars);
}

/**
* Optionally decodes a String, if it's encoded. If String is not encoded,
* method will return the original input value.
Expand Down Expand Up @@ -568,7 +612,7 @@ private void parse(final String purl) throws MalformedPackageURLException {
if (index <= start) {
throw new MalformedPackageURLException("Invalid purl: does not contain both a type and name");
}
this.type = validateType(remainder.substring(start, index).toLowerCase());
this.type = toLowerCase(validateType(remainder.substring(start, index)));
//remainder.delete(0, index + 1);
start = index + 1;

Expand Down Expand Up @@ -617,8 +661,9 @@ private Map<String, String> parseQualifiers(final String encodedString) throws M
(map, value) -> {
final String[] entry = value.split("=", 2);
if (entry.length == 2 && !entry[1].isEmpty()) {
if (map.put(entry[0].toLowerCase(), percentDecode(entry[1])) != null) {
throw new ValidationException("Duplicate package qualifier encountere - more then one value was specified for " + entry[0].toLowerCase());
String key = toLowerCase(entry[0]);
if (map.put(key, percentDecode(entry[1])) != null) {
throw new ValidationException("Duplicate package qualifier encountered. More then one value was specified for " + key);
}
}
},
Expand Down