WellFormedName.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;
import java.util.EnumMap;
import java.util.Map;
/**
* The WellFormedName class represents a Well Formed Name, as defined in the CPE Specification
* version 2.3.
*
* @see <a href= "https://doi.org/10.6028/NIST.IR.7695">NISTIR 7695 Section 5</a>
*
* @author <a href="mailto:jkraunelis@mitre.org">Joshua Kraunelis</a>
* @author <a href="mailto:david.waltermire@nist.gov">David Waltermire</a>
*/
public class WellFormedName {
public enum Attribute {
PART,
VENDOR,
PRODUCT,
VERSION,
UPDATE,
EDITION,
LANGUAGE,
SW_EDITION,
TARGET_SW,
TARGET_HW,
OTHER;
}
// Underlying wfn representation.
// String -> String.
private Map<Attribute, Object> wfn = new EnumMap<Attribute, Object>(Attribute.class);
/**
* Constructs a new WellFormedName object, with all components set to the default value "ANY".
*
* @throws RuntimeException
* if this implementation is incorrect
*/
public WellFormedName() {
for (Attribute a : Attribute.values()) {
// don't set part to ANY
if (!Attribute.PART.equals(a)) {
try {
set(a, LogicalValue.ANY);
} catch (ParseException ex) {
// the assignment of ANY is a valid value, so this should never happen
throw new RuntimeException(ex);
}
}
}
}
/**
* Constructs a new WellFormedName object, setting each component to the given parameter value. If a
* parameter is null, the component is set to the default value "ANY".
*
* @param part
* string representing the part component
* @param vendor
* string representing the vendor component
* @param product
* string representing the product component
* @param version
* string representing the version component
* @param update
* string representing the update component
* @param edition
* string representing the edition component
* @param language
* string representing the language component
* @param swEdition
* string representing the sw_edition component
* @param targetSoftware
* string representing the target_sw component
* @param targetHardware
* string representing the target_hw component
* @param other
* string representing the other component
* @throws ParseException
* if any of the provided values are invalid
*/
public WellFormedName(Object part, Object vendor, Object product, Object version, Object update, Object edition,
Object language, Object swEdition, Object targetSoftware, Object targetHardware, Object other)
throws ParseException {
set(Attribute.PART, part);
set(Attribute.VENDOR, vendor);
set(Attribute.PRODUCT, product);
set(Attribute.VERSION, version);
set(Attribute.UPDATE, update);
set(Attribute.EDITION, edition);
set(Attribute.LANGUAGE, language);
set(Attribute.SW_EDITION, swEdition);
set(Attribute.TARGET_SW, targetSoftware);
set(Attribute.TARGET_HW, targetHardware);
set(Attribute.OTHER, other);
}
/**
* get the value of the provided attribute.
*
* @param attribute
* String representing the component value to get
* @return the String value of the given component, or default value {@link LogicalValue#ANY} if the
* component does not exist
*/
public Object get(Attribute attribute) {
if (this.wfn.containsKey(attribute)) {
return this.wfn.get(attribute);
} else {
return LogicalValue.ANY;
}
}
/**
* Sets the given attribute to value, if the attribute is in the list of permissible components.
*
* @param attribute
* enumerated value representing the component to set
* @param value
* Object representing the value of the given component
* @throws ParseException
* if the provided value is invalid
*/
public final void set(Attribute attribute, Object value) throws ParseException {
// check to see if we're setting a LogicalValue ANY or NA
if (value instanceof LogicalValue) {
// don't allow logical values in part component
if (Attribute.PART.equals(attribute)) {
throw new ParseException("Error! part component cannot be a logical value", 0);
}
} else if (value == null || ((String) value).equals("")) {
// if value is null or blank, set attribute to default logical ANY
value = LogicalValue.ANY;
} else {
String svalue = (String) value;
// Reg exs
// check for printable characters - no control characters
if (!svalue.matches("\\p{Print}*")) {
throw new ParseException("Error! encountered non printable character in: " + svalue, 0);
}
// svalue has whitespace
if (svalue.matches(".*\\s+.*")) {
throw new ParseException("Error! component cannot contain whitespace: " + svalue, 0);
}
// svalue has more than one unquoted star
if (svalue.matches("\\*{2,}.*") || svalue.matches(".*\\*{2,}")) {
throw new ParseException("Error! component cannot contain more than one * in sequence: " + svalue, 0);
}
// svalue has unquoted punctuation embedded
if (svalue.matches(
".*(?<!\\\\)[\\!\\\"\\#\\$\\%\\&\\\'\\(\\)\\+\\,\\.\\/\\:\\;\\<\\=\\>\\@\\[\\]\\^\\`\\{\\|\\}\\~\\-].*")) {
throw new ParseException("Error! component cannot contain unquoted punctuation: " + svalue, 0);
}
// svalue has an unquoted *
if (svalue.matches(".+(?<!\\\\)[\\*].+")) {
throw new ParseException("Error! component cannot contain embedded *: " + svalue, 0);
}
// svalue has embedded unquoted ?
// this will catch a single unquoted ?, so make sure we deal with that
// if
// (svalue.matches("\\?*[\\p{Graph}&&[^\\?]]*(?<!\\\\)[\\?][\\p{Graph}&&[^\\?]]*\\?*"))
// {
if (svalue.contains("?")) {
if (svalue.equals("?")) {
// single ? is valid
value = svalue;
} else {
// remove leading and trailing ?s
StringBuffer buf = new StringBuffer(svalue);
while (buf.indexOf("?") == 0) {
// remove all leading ?'s
buf.deleteCharAt(0);
}
buf = buf.reverse();
while (buf.indexOf("?") == 0) {
// remove all trailing ?'s (string has been reversed)
buf.deleteCharAt(0);
}
// back to normal
buf = buf.reverse();
// after leading and trailing ?s are removed, check if value
// contains unquoted ?s
if (buf.toString().matches(".+(?<!\\\\)[\\?].+")) {
throw new ParseException("Error! component cannot contain embedded ?: " + svalue, 0);
}
}
}
// single asterisk is not allowed
if (svalue.equals("*")) {
throw new ParseException("Error! component cannot be a single *: " + svalue, 0);
}
// quoted hyphen not allowed by itself
if (svalue.equals("-")) {
throw new ParseException("Error! component cannot be quoted hyphen: " + svalue, 0);
}
// part must be a, o, or h
if (Attribute.PART.equals(attribute) && !svalue.equals("a") && !svalue.equals("o") && !svalue.equals("h")) {
throw new ParseException("Error! part component must be one of the following: 'a', 'o', 'h': " + svalue, 0);
}
value = svalue;
}
// should be good to go
this.wfn.put(attribute, value);
}
/**
* Get the string representation of this {@link WellFormedName}.
*
* @return the string representation of the WellFormedName
*/
@Override
public String toString() {
StringBuffer sb = new StringBuffer("wfn:[");
for (Attribute attr : Attribute.values()) {
sb.append(attr.name().toLowerCase());
sb.append("=");
Object obj = wfn.get(attr);
if (obj instanceof LogicalValue) {
sb.append(obj);
sb.append(", ");
} else {
sb.append("\"");
sb.append(obj);
sb.append("\", ");
}
}
sb.deleteCharAt(sb.length() - 1);
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
}
}