1 /** 2 * Portions of this software was developed by employees of the National Institute 3 * of Standards and Technology (NIST), an agency of the Federal Government and is 4 * being made available as a public service. Pursuant to title 17 United States 5 * Code Section 105, works of NIST employees are not subject to copyright 6 * protection in the United States. This software may be subject to foreign 7 * copyright. Permission in the United States and in foreign countries, to the 8 * extent that NIST may hold copyright, to use, copy, modify, create derivative 9 * works, and distribute this software and its documentation without fee is hereby 10 * granted on a non-exclusive basis, provided that this notice and disclaimer 11 * of warranty appears in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 14 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 15 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 17 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 18 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 19 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 20 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 21 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 22 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 23 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 24 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 25 */ 26 // Copyright (c) 2011, The MITRE Corporation 27 28 // All rights reserved. 29 // 30 // Redistribution and use in source and binary forms, with or without modification, are 31 // permitted provided that the following conditions are met: 32 // 33 // * Redistributions of source code must retain the above copyright notice, this list 34 // of conditions and the following disclaimer. 35 // * Redistributions in binary form must reproduce the above copyright notice, this 36 // list of conditions and the following disclaimer in the documentation and/or other 37 // materials provided with the distribution. 38 // * Neither the name of The MITRE Corporation nor the names of its contributors may be 39 // used to endorse or promote products derived from this software without specific 40 // prior written permission. 41 // 42 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 43 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 44 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 45 // SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 47 // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 49 // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 50 // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 52 package gov.nist.secauto.cpe.common; 53 54 import java.text.ParseException; 55 56 /** 57 * A collection of utility functions for use with the gov.nist.secauto.cpe.matching and 58 * gov.nist.secauto.cpe.naming packages. 59 * 60 * @see <a href= 61 * "https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/cpe/">CPE 62 * Specifications</a> 63 * 64 * @author <a href="mailto:jkraunelis@mitre.org">Joshua Kraunelis</a> 65 * @author <a href="mailto:david.waltermire@nist.gov">David Waltermire</a> 66 */ 67 public class Utilities { 68 69 /** 70 * Concatenates an arbitrary number of strings, in the given order. 71 * 72 * @param strings 73 * strings to be concatenated 74 * @return a single string representing all the arguments, concatenated 75 */ 76 public static String strcat(String... strings) { 77 StringBuilder retval = new StringBuilder(); 78 for (String s : strings) { 79 retval.append(s); 80 } 81 return retval.toString(); 82 } 83 84 /** 85 * Extracts the characters between b and e, from the string s. 86 * 87 * @param str 88 * the string which the substring should be extracted from 89 * @param begin 90 * beginning index, inclusive 91 * @param end 92 * ending index, exclusive 93 * @return the characters between index begin and index end 94 */ 95 public static String substr(String str, int begin, int end) { 96 return str.substring(begin, end); 97 } 98 99 /** 100 * Returns the number of characters in the given string. 101 * 102 * @param str 103 * the string 104 * @return the number of characters in the string 105 */ 106 public static int strlen(String str) { 107 return str.length(); 108 } 109 110 /** 111 * Searches a string for a character starting at a given offset. Returns the offset of the character 112 * if found, -1 if not found. 113 * 114 * @param str 115 * string to be searched 116 * @param chr 117 * character to search for 118 * @param offset 119 * offset to start at 120 * @return the integer offset of the character, or -1 121 */ 122 public static int strchr(String str, int chr, int offset) { 123 return str.indexOf(chr, offset); 124 } 125 126 /** 127 * Converts all alphabetic characters in a String to lowercase. 128 * 129 * @param str 130 * string to convert to lowercase 131 * @return lowercase version of str 132 */ 133 public static String toLowercase(String str) { 134 return str.toLowerCase(); 135 } 136 137 /** 138 * Searches a string for the first occurrence of another string, starting at a given offset. 139 * 140 * @param str1 141 * String to search. 142 * @param str2 143 * String to search for. 144 * @param offset 145 * Integer offset or -1 if not found. 146 * @return the position of the first occurrence of str2, or -1 if the string was not found 147 */ 148 public static int indexOf(String str1, String str2, int offset) { 149 return str1.indexOf(str2, offset); 150 } 151 152 /** 153 * Searches string for special characters * and ?. 154 * 155 * @param str 156 * String to be searched 157 * @return {@code true} if string contains a wildcard, or {@code false} otherwise 158 */ 159 public static boolean containsWildcards(String str) { 160 if (str.contains("*") || str.contains("?")) { 161 if (!(str.contains("\\"))) { 162 return true; 163 } 164 return false; 165 } 166 return false; 167 } 168 169 /** 170 * Checks if given number is even or not. 171 * 172 * @param num 173 * number to check 174 * @return {@code true} if number is even, {@code false} if not 175 */ 176 public static boolean isEvenNumber(int num) { 177 if (num % 2 == 0) { 178 return true; 179 } else { 180 return false; 181 } 182 } 183 184 /** 185 * Counts the number of escape characters in the string beginning and ending at the given indices. 186 * 187 * @param str 188 * string to search 189 * @param start 190 * beginning index, inclusive 191 * @param end 192 * ending index, exclusive 193 * @return number of escape characters in string 194 */ 195 public static int countEscapeCharacters(String str, int start, int end) { 196 int result = 0; 197 boolean active = false; 198 int pos = 0; 199 while (pos < end) { 200 active = !active && str.charAt(pos) == '\\'; 201 if (active && (pos >= start)) { 202 result = result + 1; 203 } 204 pos = pos + 1; 205 } 206 return result; 207 } 208 209 /** 210 * Searches a string for the first unescaped colon and returns the index of that colon. 211 * 212 * @param str 213 * string to search 214 * @return index of first unescaped colon, or 0 if not found 215 */ 216 public static int getUnescapedColonIndex(String str) { 217 boolean found = false; 218 int colonIndex = 0; 219 int startIndex = 0; 220 // Find the first non-escaped colon. 221 while (!found) { 222 colonIndex = str.indexOf(':', startIndex + 1); 223 // If no colon is found, return 0. 224 if (colonIndex == -1) { 225 return 0; 226 } 227 // Peek at character before colon. 228 if ((str.substring(colonIndex - 1, colonIndex)).equals("\\")) { 229 // If colon is escaped, keep looking. 230 startIndex = colonIndex; 231 } else { 232 found = true; 233 } 234 } 235 return colonIndex; 236 } 237 238 /** 239 * Determines if the string contains only alphanumeric characters or the underscore character. 240 * 241 * @param str 242 * the string in question 243 * @return {@code true} if c is alphanumeric or underscore, or {@code false} otherwise 244 */ 245 public static boolean isAlphanum(String str) { 246 if (str.matches("[a-zA-Z0-9]") || str.equals("_")) { 247 return true; 248 } 249 return false; 250 } 251 252 /** 253 * Returns a copy of the given string in reverse order. 254 * 255 * @param str 256 * the string to be reversed 257 * @return a reversed copy of s 258 */ 259 public static String reverse(String str) { 260 return new StringBuffer(str).reverse().toString(); 261 } 262 263 /** 264 * This function is not part of the reference implementation pseudo code found in the CPE 2.3 265 * specification. It enforces two rules in the specification: 1) a CPE URI must start with the 266 * characters "cpe:/". 2) A URI may not contain more than 7 components. If either rule is violated, 267 * a ParseException is thrown. 268 * 269 * @param str 270 * the potential CPE formatted string to validate 271 * @throws ParseException 272 * if one of the rules for a CPE URI is violated 273 */ 274 public static void validateURI(String str) throws ParseException { 275 // make sure uri starts with cpe:/ 276 if (!str.toLowerCase().startsWith("cpe:/")) { 277 throw new ParseException("Error: URI must start with 'cpe:/'. Given: " + str, 0); 278 } 279 // make sure uri doesn't contain more than 7 colons 280 int count = 0; 281 for (int i = 0; i != str.length(); i++) { 282 if (str.charAt(i) == ':') { 283 count++; 284 } 285 } 286 if (count > 7) { 287 throw new ParseException("Error parsing URI. Found " + (count - 7) + " extra components in: " + str, 0); 288 } 289 } 290 291 /** 292 * This function is not part of the reference implementation pseudo code found in the CPE 2.3 293 * specification. It enforces three rules found in the specification: 1) A CPE formatted string must 294 * start with the characters "cpe:2.3:". 2) A formatted string must contain 11 components. 3) A 295 * formatted string must not contain empty components. If any rule is violated, a ParseException is 296 * thrown. 297 * 298 * @param str 299 * the potential CPE formatted string to validate 300 * @throws ParseException 301 * if one of the rules for a CPE formatted string is violated 302 */ 303 public static void validateFS(String str) throws ParseException { 304 if (!str.toLowerCase().startsWith("cpe:2.3:")) { 305 throw new ParseException("Error: Formatted String must start with \"cpe:2.3\". Given: " + str, 0); 306 } 307 // make sure fs contains exactly 12 unquoted colons 308 int count = 0; 309 for (int i = 0; i != str.length(); i++) { 310 if (str.charAt(i) == ':') { 311 if (str.charAt(i - 1) != '\\') { 312 count++; 313 } 314 if ((i + 1) < str.length() && str.charAt(i + 1) == ':') { 315 throw new ParseException("Error parsing formatted string. Found empty component", 0); 316 } 317 } 318 } 319 if (count > 12) { 320 int extra = (count - 12); 321 StringBuffer msg = new StringBuffer("Error parsing formatted string. Found " + extra + " extra component"); 322 if (extra > 1) { 323 msg.append("s"); 324 } 325 msg.append(" in: " + str); 326 throw new ParseException(msg.toString(), 0); 327 } 328 if (count < 12) { 329 int missing = (12 - count); 330 StringBuffer msg = new StringBuffer("Error parsing formatted string. Missing " + missing + " component"); 331 if (missing > 1) { 332 msg.append("s"); 333 } 334 throw new ParseException(msg.toString(), 0); 335 } 336 } 337 }