Utilities.java
/**
* Portions of this software was developed by employees of the National Institute
* of Standards and Technology (NIST), an agency of the Federal Government and is
* being made available as a public service. Pursuant to title 17 United States
* Code Section 105, works of NIST employees are not subject to copyright
* protection in the United States. This software may be subject to foreign
* copyright. Permission in the United States and in foreign countries, to the
* extent that NIST may hold copyright, to use, copy, modify, create derivative
* works, and distribute this software and its documentation without fee is hereby
* granted on a non-exclusive basis, provided that this notice and disclaimer
* of warranty appears in all copies.
*
* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
* EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
* THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
* INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
* SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
* SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
* INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
* OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
* CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
* PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
* OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
*/
// Copyright (c) 2011, The MITRE Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list
// of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice, this
// list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
// * Neither the name of The MITRE Corporation nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gov.nist.secauto.cpe.common;
import java.text.ParseException;
/**
* A collection of utility functions for use with the gov.nist.secauto.cpe.matching and
* gov.nist.secauto.cpe.naming packages.
*
* @see <a href=
* "https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/cpe/">CPE
* Specifications</a>
*
* @author <a href="mailto:jkraunelis@mitre.org">Joshua Kraunelis</a>
* @author <a href="mailto:david.waltermire@nist.gov">David Waltermire</a>
*/
public class Utilities {
/**
* Concatenates an arbitrary number of strings, in the given order.
*
* @param strings
* strings to be concatenated
* @return a single string representing all the arguments, concatenated
*/
public static String strcat(String... strings) {
StringBuilder retval = new StringBuilder();
for (String s : strings) {
retval.append(s);
}
return retval.toString();
}
/**
* Extracts the characters between b and e, from the string s.
*
* @param str
* the string which the substring should be extracted from
* @param begin
* beginning index, inclusive
* @param end
* ending index, exclusive
* @return the characters between index begin and index end
*/
public static String substr(String str, int begin, int end) {
return str.substring(begin, end);
}
/**
* Returns the number of characters in the given string.
*
* @param str
* the string
* @return the number of characters in the string
*/
public static int strlen(String str) {
return str.length();
}
/**
* Searches a string for a character starting at a given offset. Returns the offset of the character
* if found, -1 if not found.
*
* @param str
* string to be searched
* @param chr
* character to search for
* @param offset
* offset to start at
* @return the integer offset of the character, or -1
*/
public static int strchr(String str, int chr, int offset) {
return str.indexOf(chr, offset);
}
/**
* Converts all alphabetic characters in a String to lowercase.
*
* @param str
* string to convert to lowercase
* @return lowercase version of str
*/
public static String toLowercase(String str) {
return str.toLowerCase();
}
/**
* Searches a string for the first occurrence of another string, starting at a given offset.
*
* @param str1
* String to search.
* @param str2
* String to search for.
* @param offset
* Integer offset or -1 if not found.
* @return the position of the first occurrence of str2, or -1 if the string was not found
*/
public static int indexOf(String str1, String str2, int offset) {
return str1.indexOf(str2, offset);
}
/**
* Searches string for special characters * and ?.
*
* @param str
* String to be searched
* @return {@code true} if string contains a wildcard, or {@code false} otherwise
*/
public static boolean containsWildcards(String str) {
if (str.contains("*") || str.contains("?")) {
if (!(str.contains("\\"))) {
return true;
}
return false;
}
return false;
}
/**
* Checks if given number is even or not.
*
* @param num
* number to check
* @return {@code true} if number is even, {@code false} if not
*/
public static boolean isEvenNumber(int num) {
if (num % 2 == 0) {
return true;
} else {
return false;
}
}
/**
* Counts the number of escape characters in the string beginning and ending at the given indices.
*
* @param str
* string to search
* @param start
* beginning index, inclusive
* @param end
* ending index, exclusive
* @return number of escape characters in string
*/
public static int countEscapeCharacters(String str, int start, int end) {
int result = 0;
boolean active = false;
int pos = 0;
while (pos < end) {
active = !active && str.charAt(pos) == '\\';
if (active && (pos >= start)) {
result = result + 1;
}
pos = pos + 1;
}
return result;
}
/**
* Searches a string for the first unescaped colon and returns the index of that colon.
*
* @param str
* string to search
* @return index of first unescaped colon, or 0 if not found
*/
public static int getUnescapedColonIndex(String str) {
boolean found = false;
int colonIndex = 0;
int startIndex = 0;
// Find the first non-escaped colon.
while (!found) {
colonIndex = str.indexOf(':', startIndex + 1);
// If no colon is found, return 0.
if (colonIndex == -1) {
return 0;
}
// Peek at character before colon.
if ((str.substring(colonIndex - 1, colonIndex)).equals("\\")) {
// If colon is escaped, keep looking.
startIndex = colonIndex;
} else {
found = true;
}
}
return colonIndex;
}
/**
* Determines if the string contains only alphanumeric characters or the underscore character.
*
* @param str
* the string in question
* @return {@code true} if c is alphanumeric or underscore, or {@code false} otherwise
*/
public static boolean isAlphanum(String str) {
if (str.matches("[a-zA-Z0-9]") || str.equals("_")) {
return true;
}
return false;
}
/**
* Returns a copy of the given string in reverse order.
*
* @param str
* the string to be reversed
* @return a reversed copy of s
*/
public static String reverse(String str) {
return new StringBuffer(str).reverse().toString();
}
/**
* This function is not part of the reference implementation pseudo code found in the CPE 2.3
* specification. It enforces two rules in the specification: 1) a CPE URI must start with the
* characters "cpe:/". 2) A URI may not contain more than 7 components. If either rule is violated,
* a ParseException is thrown.
*
* @param str
* the potential CPE formatted string to validate
* @throws ParseException
* if one of the rules for a CPE URI is violated
*/
public static void validateURI(String str) throws ParseException {
// make sure uri starts with cpe:/
if (!str.toLowerCase().startsWith("cpe:/")) {
throw new ParseException("Error: URI must start with 'cpe:/'. Given: " + str, 0);
}
// make sure uri doesn't contain more than 7 colons
int count = 0;
for (int i = 0; i != str.length(); i++) {
if (str.charAt(i) == ':') {
count++;
}
}
if (count > 7) {
throw new ParseException("Error parsing URI. Found " + (count - 7) + " extra components in: " + str, 0);
}
}
/**
* This function is not part of the reference implementation pseudo code found in the CPE 2.3
* specification. It enforces three rules found in the specification: 1) A CPE formatted string must
* start with the characters "cpe:2.3:". 2) A formatted string must contain 11 components. 3) A
* formatted string must not contain empty components. If any rule is violated, a ParseException is
* thrown.
*
* @param str
* the potential CPE formatted string to validate
* @throws ParseException
* if one of the rules for a CPE formatted string is violated
*/
public static void validateFS(String str) throws ParseException {
if (!str.toLowerCase().startsWith("cpe:2.3:")) {
throw new ParseException("Error: Formatted String must start with \"cpe:2.3\". Given: " + str, 0);
}
// make sure fs contains exactly 12 unquoted colons
int count = 0;
for (int i = 0; i != str.length(); i++) {
if (str.charAt(i) == ':') {
if (str.charAt(i - 1) != '\\') {
count++;
}
if ((i + 1) < str.length() && str.charAt(i + 1) == ':') {
throw new ParseException("Error parsing formatted string. Found empty component", 0);
}
}
}
if (count > 12) {
int extra = (count - 12);
StringBuffer msg = new StringBuffer("Error parsing formatted string. Found " + extra + " extra component");
if (extra > 1) {
msg.append("s");
}
msg.append(" in: " + str);
throw new ParseException(msg.toString(), 0);
}
if (count < 12) {
int missing = (12 - count);
StringBuffer msg = new StringBuffer("Error parsing formatted string. Missing " + missing + " component");
if (missing > 1) {
msg.append("s");
}
throw new ParseException(msg.toString(), 0);
}
}
}