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 import java.util.EnumMap; 56 import java.util.Map; 57 58 /** 59 * The WellFormedName class represents a Well Formed Name, as defined in the CPE Specification 60 * version 2.3. 61 * 62 * @see <a href= "https://doi.org/10.6028/NIST.IR.7695">NISTIR 7695 Section 5</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 WellFormedName { 68 public enum Attribute { 69 70 PART, 71 VENDOR, 72 PRODUCT, 73 VERSION, 74 UPDATE, 75 EDITION, 76 LANGUAGE, 77 SW_EDITION, 78 TARGET_SW, 79 TARGET_HW, 80 OTHER; 81 } 82 83 // Underlying wfn representation. 84 // String -> String. 85 private Map<Attribute, Object> wfn = new EnumMap<Attribute, Object>(Attribute.class); 86 87 /** 88 * Constructs a new WellFormedName object, with all components set to the default value "ANY". 89 * 90 * @throws RuntimeException 91 * if this implementation is incorrect 92 */ 93 public WellFormedName() { 94 for (Attribute a : Attribute.values()) { 95 // don't set part to ANY 96 if (!Attribute.PART.equals(a)) { 97 try { 98 set(a, LogicalValue.ANY); 99 } catch (ParseException ex) { 100 // the assignment of ANY is a valid value, so this should never happen 101 throw new RuntimeException(ex); 102 } 103 } 104 } 105 } 106 107 /** 108 * Constructs a new WellFormedName object, setting each component to the given parameter value. If a 109 * parameter is null, the component is set to the default value "ANY". 110 * 111 * @param part 112 * string representing the part component 113 * @param vendor 114 * string representing the vendor component 115 * @param product 116 * string representing the product component 117 * @param version 118 * string representing the version component 119 * @param update 120 * string representing the update component 121 * @param edition 122 * string representing the edition component 123 * @param language 124 * string representing the language component 125 * @param swEdition 126 * string representing the sw_edition component 127 * @param targetSoftware 128 * string representing the target_sw component 129 * @param targetHardware 130 * string representing the target_hw component 131 * @param other 132 * string representing the other component 133 * @throws ParseException 134 * if any of the provided values are invalid 135 */ 136 public WellFormedName(Object part, Object vendor, Object product, Object version, Object update, Object edition, 137 Object language, Object swEdition, Object targetSoftware, Object targetHardware, Object other) 138 throws ParseException { 139 set(Attribute.PART, part); 140 set(Attribute.VENDOR, vendor); 141 set(Attribute.PRODUCT, product); 142 set(Attribute.VERSION, version); 143 set(Attribute.UPDATE, update); 144 set(Attribute.EDITION, edition); 145 set(Attribute.LANGUAGE, language); 146 set(Attribute.SW_EDITION, swEdition); 147 set(Attribute.TARGET_SW, targetSoftware); 148 set(Attribute.TARGET_HW, targetHardware); 149 set(Attribute.OTHER, other); 150 } 151 152 /** 153 * get the value of the provided attribute. 154 * 155 * @param attribute 156 * String representing the component value to get 157 * @return the String value of the given component, or default value {@link LogicalValue#ANY} if the 158 * component does not exist 159 */ 160 public Object get(Attribute attribute) { 161 if (this.wfn.containsKey(attribute)) { 162 return this.wfn.get(attribute); 163 } else { 164 return LogicalValue.ANY; 165 } 166 } 167 168 /** 169 * Sets the given attribute to value, if the attribute is in the list of permissible components. 170 * 171 * @param attribute 172 * enumerated value representing the component to set 173 * @param value 174 * Object representing the value of the given component 175 * @throws ParseException 176 * if the provided value is invalid 177 */ 178 public final void set(Attribute attribute, Object value) throws ParseException { 179 // check to see if we're setting a LogicalValue ANY or NA 180 if (value instanceof LogicalValue) { 181 // don't allow logical values in part component 182 if (Attribute.PART.equals(attribute)) { 183 throw new ParseException("Error! part component cannot be a logical value", 0); 184 } 185 } else if (value == null || ((String) value).equals("")) { 186 // if value is null or blank, set attribute to default logical ANY 187 value = LogicalValue.ANY; 188 } else { 189 String svalue = (String) value; 190 // Reg exs 191 // check for printable characters - no control characters 192 if (!svalue.matches("\\p{Print}*")) { 193 throw new ParseException("Error! encountered non printable character in: " + svalue, 0); 194 } 195 // svalue has whitespace 196 if (svalue.matches(".*\\s+.*")) { 197 throw new ParseException("Error! component cannot contain whitespace: " + svalue, 0); 198 } 199 // svalue has more than one unquoted star 200 if (svalue.matches("\\*{2,}.*") || svalue.matches(".*\\*{2,}")) { 201 throw new ParseException("Error! component cannot contain more than one * in sequence: " + svalue, 0); 202 } 203 // svalue has unquoted punctuation embedded 204 if (svalue.matches( 205 ".*(?<!\\\\)[\\!\\\"\\#\\$\\%\\&\\\'\\(\\)\\+\\,\\.\\/\\:\\;\\<\\=\\>\\@\\[\\]\\^\\`\\{\\|\\}\\~\\-].*")) { 206 throw new ParseException("Error! component cannot contain unquoted punctuation: " + svalue, 0); 207 } 208 // svalue has an unquoted * 209 if (svalue.matches(".+(?<!\\\\)[\\*].+")) { 210 throw new ParseException("Error! component cannot contain embedded *: " + svalue, 0); 211 } 212 // svalue has embedded unquoted ? 213 // this will catch a single unquoted ?, so make sure we deal with that 214 // if 215 // (svalue.matches("\\?*[\\p{Graph}&&[^\\?]]*(?<!\\\\)[\\?][\\p{Graph}&&[^\\?]]*\\?*")) 216 // { 217 if (svalue.contains("?")) { 218 if (svalue.equals("?")) { 219 // single ? is valid 220 value = svalue; 221 } else { 222 // remove leading and trailing ?s 223 StringBuffer buf = new StringBuffer(svalue); 224 while (buf.indexOf("?") == 0) { 225 // remove all leading ?'s 226 buf.deleteCharAt(0); 227 } 228 buf = buf.reverse(); 229 while (buf.indexOf("?") == 0) { 230 // remove all trailing ?'s (string has been reversed) 231 buf.deleteCharAt(0); 232 } 233 // back to normal 234 buf = buf.reverse(); 235 // after leading and trailing ?s are removed, check if value 236 // contains unquoted ?s 237 if (buf.toString().matches(".+(?<!\\\\)[\\?].+")) { 238 throw new ParseException("Error! component cannot contain embedded ?: " + svalue, 0); 239 } 240 } 241 } 242 // single asterisk is not allowed 243 if (svalue.equals("*")) { 244 throw new ParseException("Error! component cannot be a single *: " + svalue, 0); 245 } 246 // quoted hyphen not allowed by itself 247 if (svalue.equals("-")) { 248 throw new ParseException("Error! component cannot be quoted hyphen: " + svalue, 0); 249 } 250 // part must be a, o, or h 251 if (Attribute.PART.equals(attribute) && !svalue.equals("a") && !svalue.equals("o") && !svalue.equals("h")) { 252 throw new ParseException("Error! part component must be one of the following: 'a', 'o', 'h': " + svalue, 0); 253 } 254 value = svalue; 255 } 256 // should be good to go 257 this.wfn.put(attribute, value); 258 } 259 260 /** 261 * Get the string representation of this {@link WellFormedName}. 262 * 263 * @return the string representation of the WellFormedName 264 */ 265 @Override 266 public String toString() { 267 StringBuffer sb = new StringBuffer("wfn:["); 268 for (Attribute attr : Attribute.values()) { 269 sb.append(attr.name().toLowerCase()); 270 sb.append("="); 271 Object obj = wfn.get(attr); 272 if (obj instanceof LogicalValue) { 273 sb.append(obj); 274 sb.append(", "); 275 } else { 276 sb.append("\""); 277 sb.append(obj); 278 sb.append("\", "); 279 } 280 } 281 sb.deleteCharAt(sb.length() - 1); 282 sb.deleteCharAt(sb.length() - 1); 283 sb.append("]"); 284 285 return sb.toString(); 286 } 287 }