View Javadoc
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 }