Updates to 3rd party libraries

Add Dockerfile and specific docker-php.ini
This commit is contained in:
2018-08-28 21:27:13 -04:00
parent 9edd6c1c35
commit d52454d1bb
511 changed files with 45960 additions and 2739 deletions

5
inc/vendor/pacificsec/cpe/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/.settings/
/.buildpath
/.project
/vendor/
/composer.lock

View File

@ -22,3 +22,45 @@ Features
- CPE rich comparison.
- CPE Language parsing and evaluation.
- MIT Licensed.
Getting Started
--------
- Clone repository
```bash
$ git clone https://github.com/pacificsec/cpe.git
$ cd cpe
```
- Create a new PHP file to run tests
```php
<?php
require('autoload.php');
use PacificSec\CPE\Matching\CPENameMatcher;
use PacificSec\CPE\Naming\CPENameUnbinder;
use PacificSec\CPE\Naming\CPENameBinder;
CPENameMatcher::test();
CPENameUnbinder::test();
CPENameBinder::test();
```
```php
<?php
require('autoload.php');
use PacificSec\CPE\Naming\CPENameUnbinder;
$cpenu = new CPENameUnbinder();
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer%01%01%01%01:?:beta");
var_dump($wfn);
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer:8.%2a:sp%3f");
var_dump($wfn);
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer:8.%02:sp%01");
var_dump($wfn);
$wfn = $cpenu->unbindURI("cpe:/a:hp:insight_diagnostics:7.4.0.1570::~~online~win2003~x64~");
var_dump($wfn);
$wfn = $cpenu->unbindFS("cpe:2.3:a:micr\\?osoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*");
var_dump($wfn);
```

26
inc/vendor/pacificsec/cpe/composer.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"name" : "pacificsec/cpe",
"type" : "library",
"description" : "CPE: Common Platform Enumeration for PHP",
"keywords" : [
"cpe",
"cve",
"security",
"pacificsec"
],
"homepage" : "https://github.com/pacificsec/cpe",
"license" : "MIT",
"authors" : [{
"name" : "Antonio Franco",
"email" : "antonio.franco@pacificsec.com"
}
],
"require" : {
"php" : ">=5.3.0"
},
"autoload" : {
"psr-4" : {
"PacificSec\\CPE\\" : "src"
}
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace PacificSec\CPE\Common;
use \Exception;
/**
* This class represents a Logical Value. It is based on Java version
* implemented by JKRAUNELIS <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class LogicalValue {
private $any = false;
private $na = false;
// Object must be constructed with the string "ANY" or "NA".
public function __construct($type) {
if ($type == "ANY") {
$this->any = true;
} else if ($type == "NA") {
$this->na = true;
} else {
throw new Exception("LogicalValue must be ANY or NA");
}
}
public function isANY(){
return $this->any;
}
public function isNA(){
return $this->na;
}
public function __toString(){
if ($this->any){
return "ANY";
}
return "NA";
}
}

View File

@ -0,0 +1,166 @@
<?php
namespace PacificSec\CPE\Common;
use \Exception;
/**
* A collection of utility functions for use with the matching and
* naming namespaces. It is based on Java version implemented by
* Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class Utilities {
/**
* Searches string for special characters * and ?
* @param string $string to be searched
* @return bool true if string contains wildcard, false otherwise
*/
public static function containsWildcards($string) {
if (strpos($string, "*") !== false || strpos($string, "?") !== false) {
if (!(strpos($string, "\\") !== false)) {
return true;
}
}
return false;
}
/**
* Checks if given number is even or not
* @param int $num number to check
* @return bool true if number is even, false if not
*/
public static function isEvenNumber($num) {
return (is_int($num) && $num % 2 == 0);
}
/**
* Counts the number of escape characters in the string beginning and ending
* at the given indices
* @param string $str string to search
* @param int $start beginning index
* @param int $end ending index
* @return number of escape characters in string
* @todo fix the use of $str. The Java version is also not using this variable.
*/
public static function countEscapeCharacters($str, $start, $end) {
$result = 0;
$active = false;
$i = 0;
while ($i < $end) {
if ($active && ($i >= $start)) {
$result = $result + 1;
}
$i = $i + 1;
}
return $result;
}
/**
* Searches a string for the first unescaped colon and returns the index of
* that colon
* @param string $str string to search
* @return int index of first unescaped colon, or 0 if not found
*/
public static function getUnescapedColonIndex($str) {
$found = false;
$colon_idx = 0;
$start_idx = 0;
// Find the first non-escaped colon.
while (!$found) {
$colon_idx = strpos($str, ":", $start_idx + 1);
// If no colon is found, return 0.
if ($colon_idx === false) {
return 0;
}
// Peek at character before colon.
if (substr($str, $colon_idx-1, 1) == "\\") {
// If colon is escaped, keep looking.
$start_idx = $colon_idx;
} else {
$found = true;
}
}
return $colon_idx;
}
/**
* Returns true if the string contains only
* alphanumeric characters or the underscore character,
* false otherwise.
* @param string $c the string in question
* @return bool true if $c is alphanumeric or underscore, false if not
*/
public static function isAlphanum($c) {
return (preg_match("/^[a-zA-Z0-9\_]+$/", $c) ? true : false);
}
/**
* 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:
* URI must start with the characters "cpe:/"
* A URI may not contain more than 7 components
* If either rule is violated, a Exception is thrown.
* @param $in string with URI to be validated
*/
public static function validateURI($in) {
// make sure uri starts with cpe:/
if (strpos(strtolower($in), "cpe:/") !== 0) {
throw new Exception("Error: URI must start with 'cpe:/'. Given: " . $in, 0);
}
// make sure uri doesn't contain more than 7 colons
$count = sizeof(explode(":", $in));
if ($count > 8) {
throw new Exception("Error parsing URI. Found " . ($count - 8) . " extra components in: " . $in, 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:
* Formatted string must start with the characters "cpe:2.3:"
* A formatted string must contain 11 components
* A formatted string must not contain empty components
* If any rule is violated, a ParseException is thrown.
* @param $in string with FS to be validated
*/
public static function validateFS($in) {
if (strpos(strtolower($in), "cpe:2.3:") !== 0) {
throw new Exception("Error: Formatted String must start with \"cpe:2.3\". Given: " . $in, 0);
}
$count = 0;
for ($i = 0; $i != strlen($in); $i++){
if (substr($in, $i, 1) == ":"){
if (substr($in, $i - 1, 1) != "\\"){
$count++;
}
if (($i+1) < strlen($in) && substr($in, $i+1, 1) == ":"){
throw new Exception("Error parsing formatted string. Found empty component", 0);
}
}
}
if ($count > 12){
$extra = $count - 12;
$s = "Error parsing formatted string. Found " . $extra . " extra component";
if ($extra > 1){
$s = $s . "s";
}
$s = $s . " in: " . $in;
throw new Exception($s, 0);
}
if ($count < 12){
$missing = 12 - $count;
$s = "Error parsing formatted string. Missing " . $missing . " component";
if ($missing > 1){
$s = $s . "s";
}
throw new Exception($s, 0);
}
}
}

View File

@ -0,0 +1,210 @@
<?php
namespace PacificSec\CPE\Common;
use \Exception;
/**
* The WellFormedName class represents a Well Formed Name, as defined
* in the CPE Specification version 2.3. It is based on Java version
* implemented by jkraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for details.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class WellFormedName {
// Underlying wfn representation.
private $wfn = null;
// All permissible WFN attributes as defined by specification.
private $attributes = array("part", "vendor", "product", "version",
"update", "edition", "language", "sw_edition", "target_sw",
"target_hw", "other");
/**
* 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 $sw_edition string representing the sw_edition component
* @param $target_sw string representing the target_sw component
* @param $target_hw string representing the target_hw component
* @param $other string representing the other component
*/
public function __construct($part = null, $vendor = null, $product = null, $version = null,
$update = null, $edition = null, $language = null, $sw_edition = null, $target_sw = null,
$target_hw = null, $other = null) {
$this->wfn = array();
// Constructs a new WellFormedName object, with all components set to the default value "ANY".
if ($part === null && $vendor === null && $product === null && $version === null &&
$update === null && $edition === null && $language === null && $sw_edition === null && $target_sw === null &&
$target_hw === null && $other === null){
foreach ($this->attributes as $a){
if ($a != "part"){
$this->set($a, new LogicalValue("ANY"));
}
}
return;
}
$this->set("part", $part);
$this->set("vendor", $vendor);
$this->set("product", $product);
$this->set("version", $version);
$this->set("update", $update);
$this->set("edition", $edition);
$this->set("language", $language);
$this->set("sw_edition", $sw_edition);
$this->set("target_sw", $target_sw);
$this->set("target_hw", $target_hw);
$this->set("other", $other);
}
/**
* @param $attribute string representing the component value to get
* @return string the string value of the given component, or default value "ANY"
* if the component does not exist
*/
public function get($attribute){
if (array_key_exists($attribute, $this->wfn))
return $this->wfn[$attribute];
else
return new LogicalValue("ANY");
}
/**
* Sets the given attribute to value, if the attribute is in the list of
* permissible components
* @param $attribute string representing the component to set
* @param $value object or string representing the value of the given component
*/
public final function set($attribute, $value){
// Iterate over permissible attributes.
foreach ($this->attributes as $a){
// If the argument is a valid attribute, set that attribute's value.
if ($attribute == $a) {
// 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"){
var_dump($value); echo "<br>\n";
var_dump($a); echo "<br>\n";
var_dump($attribute); echo "<br>\n";
throw new Exception("Error! part component cannot be a logical value");
}
// put the Object in the ht and break
$this->wfn[$attribute] = $value;
break;
}
if ($value == null || $value == ""){
// if value is null or blank, set attribute to default logical ANY
$this->wfn[$attribute] = new LogicalValue("ANY");
break;
}
$svalue = $value;
// Reg exs
// check for printable characters - no control characters
if (!preg_match("/^[[:print:]]*$/", $svalue)){
throw new Exception("Error! encountered non printable character in: " . $svalue, 0);
}
// svalue has whitespace
if (preg_match("/^.*\\s+.*$/", $svalue)){
throw new Exception("Error! component cannot contain whitespace: " . $svalue, 0);
}
// svalue has more than one unquoted star
if (preg_match("/^\\*{2,}.*$/", $svalue) || preg_match("/^.*\\*{2,}$/", $svalue)){
throw new Exception("Error! component cannot contain more than one * in sequence: " . $svalue, 0);
}
// svalue has unquoted punctuation embedded
if (preg_match("/^.*(?<!\\\\)[\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\+\\,\\.\\/\\:\\;\\<\\=\\>\\@\\[\\]\\^\\`\\{\\|\\}\\~\\-].*$/", $svalue)) {
throw new Exception("Error! component cannot contain unquoted punctuation: " . $svalue, 0);
}
// svalue has an unquoted *
if (preg_match("/^.+(?<!\\\\)[\\*].+$/", $svalue)) {
throw new Exception("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 (strpos($svalue, "?") !== false) {
if ($svalue == "?") {
// single ? is valid
$this->wfn[$attribute] = $svalue;
break;
}
// remove leading and trailing ?s
$v = $svalue;
while (strpos($v, "?") === 0) {
// remove all leading ?'s
$v = substr($v, 1);
}
$v = strrev($v);
while (strpos($v, "?") === 0) {
// remove all trailing ?'s (string has been reversed)
$v = substr($v, 1);
}
// back to normal
$v = strrev($v);
// after leading and trailing ?s are removed, check if value
// contains unquoted ?s
if (preg_match("/^.+(?<!\\\\)[\\?].+$/", $v)) {
throw new Exception("Error! component cannot contain embedded ?: " . $svalue, 0);
}
}
// single asterisk is not allowed
if ($svalue == "*") {
throw new Exception("Error! component cannot be a single *: " . $svalue, 0);
}
// quoted hyphen not allowed by itself
if ($svalue == "-") {
throw new Exception("Error! component cannot be quoted hyphen: " . $svalue, 0);
}
// part must be a, o, or h
if ($attribute == "part") {
if ($svalue != "a" && $svalue != "o" && $svalue != "h") {
throw new Exception("Error! part component must be one of the following: 'a', 'o', 'h': " . $svalue, 0);
}
}
// should be good to go
$this->wfn[$attribute] = $svalue;
break;
}
}
}
/**
*
* @return string representation of the WellFormedName
*/
public function __toString() {
$str = "wfn:[";
foreach ($this->attributes as $attr) {
$str = $str . $attr;
$str = $str . "=";
$o = $this->wfn[$attr];
if ($o instanceof LogicalValue) {
$str = $str . $o;
$str = $str . ", ";
} else {
$str = $str . "\"";
$str = $str . $o;
$str = $str . "\", ";
}
}
$str = substr($str, 0, strlen($str)-1);
$str = substr($str, 0, strlen($str)-1);
$str = $str . "]";
return $str;
}
}

View File

@ -0,0 +1,287 @@
<?php
namespace PacificSec\CPE\Matching;
use PacificSec\CPE\Common\WellFormedName;
use PacificSec\CPE\Common\Utilities;
use PacificSec\CPE\Common\LogicalValue;
use PacificSec\CPE\Naming\CPENameBinder;
use PacificSec\CPE\Naming\CPENameUnbinder;
/**
* The CPENameMatcher is an implementation of the CPE Matching algorithm,
* as specified in the CPE Matching Standard version 2.3. It is based on
* Java version implemented by Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class CPENameMatcher {
/**
* Tests two Well Formed Names for disjointness.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the names are disjoint, false otherwise
*/
public function isDisjoint(WellFormedName $source, WellFormedName $target) {
// if any pairwise comparison is disjoint, the names are disjoint.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result == Relation::DISJOINT)
return true;
}
return false;
}
/**
* Tests two Well Formed Names for equality.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the names are equal, false otherwise
*/
public function isEqual(WellFormedName $source, WellFormedName $target) {
// if every pairwise comparison is equal, the names are equal.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result != Relation::EQUAL){
return false;
}
}
return true;
}
/**
* Tests if the target Well Formed Name is a subset of the source Well Formed
* Name.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the target is a subset of the source, false otherwise
*/
public function isSubset(WellFormedName $source, WellFormedName $target) {
// if any comparison is anything other than subset or equal, then target is
// not a subset of source.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result != Relation::SUBSET && $result != Relation::EQUAL) {
return false;
}
}
return true;
}
/**
* Tests if the target Well Formed name is a superset of the source Well Formed
* Name.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the target is a superset of the source, false otherwise
*/
public function isSuperset(WellFormedName $source, WellFormedName $target) {
// if any comparison is anything other than superset or equal, then target is not
// a superset of source.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result != Relation::SUPERSET && $result != Relation::EQUAL) {
return false;
}
}
return true;
}
/**
* Compares each attribute value pair in two Well Formed Names.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return array A array mapping attribute string to attribute value Relation
*/
public function compareWFNs(WellFormedName $source, WellFormedName $target) {
$result = array();
$result["part"] = $this->compare($source->get("part"), $target->get("part"));
$result["vendor"] = $this->compare($source->get("vendor"), $target->get("vendor"));
$result["product"] = $this->compare($source->get("product"), $target->get("product"));
$result["version"] = $this->compare($source->get("version"), $target->get("version"));
$result["update"] = $this->compare($source->get("update"), $target->get("update"));
$result["edition"] = $this->compare($source->get("edition"), $target->get("edition"));
$result["language"] = $this->compare($source->get("language"), $target->get("language"));
$result["sw_edition"] = $this->compare($source->get("sw_edition"), $target->get("sw_edition"));
$result["target_sw"] = $this->compare($source->get("target_sw"), $target->get("target_sw"));
$result["target_hw"] = $this->compare($source->get("target_hw"), $target->get("target_hw"));
$result["other"] = $this->compare($source->get("other"), $target->get("other"));
return $result;
}
/**
* Compares an attribute value pair.
* @param string $source Source attribute value.
* @param string $target Target attribute value.
* @return int The relation between the two attribute values.
*/
private function compare($source, $target) {
// matching is case insensitive, convert strings to lowercase.
if ($this->isString($source)) {
$source = strtolower($source);
}
if ($this->isString($target)) {
$target = strtolower($target);
}
// Unquoted wildcard characters yield an undefined result.
if ($this->isString($target) && Utilities::containsWildcards($target)) {
return Relation::UNDEFINED;
}
// If source and target values are equal, then result is equal.
if ($source == $target) {
return Relation::EQUAL;
}
// Check to see if source or target are Logical Values.
$lvSource = null;
$lvTarget = null;
if ($source instanceof LogicalValue) {
$lvSource = $source;
}
if ($target instanceof LogicalValue) {
$lvTarget = $target;
}
if ($lvSource != null && $lvTarget != null) {
// If Logical Values are equal, result is equal.
if ($lvSource->isANY() == $lvTarget->isANY() || $lvSource->isNA() == $lvTarget->isNA()) {
return Relation::EQUAL;
}
}
// If source value is ANY, result is a superset.
if ($lvSource != null) {
if ($lvSource->isANY()) {
return Relation::SUPERSET;
}
}
// If target value is ANY, result is a subset.
if ($lvTarget != null) {
if ($lvTarget->isANY()) {
return Relation::SUBSET;
}
}
// If source or target is NA, result is disjoint.
if ($lvSource != null) {
if ($lvSource->isNA()) {
return Relation::DISJOINT;
}
}
if ($lvTarget != null) {
if ($lvTarget->isNA()) {
return Relation::DISJOINT;
}
}
// only Strings will get to this point, not LogicalValues
return $this->compareStrings($source, $target);
}
/**
* Compares a source string to a target string, and addresses the condition
* in which the source string includes unquoted special characters. It
* performs a simple regular expression match, with the assumption that
* (as required) unquoted special characters appear only at the beginning
* and/or the end of the source string. It also properly differentiates
* between unquoted and quoted special characters.
*
* @param $source string Source attribute value.
* @param $target string Target attribute value.
* @return Relation between source and target Strings.
*/
private function compareStrings($source, $target) {
$start = 0;
$end = strlen($source);
$begins = 0;
$ends = 0;
$index = 0; $leftover = 0; $escapes = 0;
if (substr($source, 0, 1) == "*") {
$start = 1;
$begins = -1;
} else {
while (($start < strlen($source)) && (substr($source, $start, 1) == "?")) {
$start = $start + 1;
$begins = $begins + 1;
}
}
if ((substr($source, $end - 1, 1) == "*") && ($this->isEvenWildcards($source, $end - 1))) { //TODO
$end = $end - 1;
$ends = -1;
} else {
while (($end > 0) && substr($source, $end - 1, 1) == "?" && ($this->isEvenWildcards($source, $end - 1))) { //TODO
$end = $end - 1;
$ends = $ends + 1;
}
}
$source = substr($source, $start, $end-$start);
$index = -1;
$leftover = strlen($target);
while ($leftover > 0) {
$index = strpos($target, $source, $index + 1);
if ($index === false) {
break;
}
$escapes = Utilities::countEscapeCharacters($target, 0, $index);
if (($index > 0) && ($begins != -1) && ($begins < ($index - $escapes))) {
break;
}
$escapes = Utilities::countEscapeCharacters($target, $index + 1, strlen($target));
$leftover = strlen($target) - $index - $escapes - strlen($source);
if (($leftover > 0) && (($ends != -1) && ($leftover > $ends))) {
continue;
}
return Relation::SUPERSET;
}
return Relation::DISJOINT;
}
/**
* Searches a string for the backslash character
* @param $str string to search in
* @param int $idx end index
* @return true if the number of backslash characters is even, false if odd
*/
private function isEvenWildcards($str, $idx) {
$result = 0;
while (($idx > 0) && (strpos($str, "\\", $idx - 1)) !== false) {
$idx = $idx - 1;
$result = $result + 1;
}
return Utilities::isEvenNumber($result);
}
/**
* Tests if an Object is an instance of the String class
* @param mixed $arg the var to test
* @return bool true if arg is a string, false if not
*/
private function isString($arg) {
return is_string($arg);
}
/*
* Static method to demonstrate this class.
*/
public static function test() {
// Examples.
$wfn = new WellFormedName("a", "microsoft", "internet_explorer", "8\\.0\\.6001", "beta", new LogicalValue("ANY"), "sp2", null, null, null, null);
$wfn2 = new WellFormedName("a", "microsoft", "internet_explorer", new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"));
$cpenm = new CPENameMatcher();
$cpenu = new CPENameUnbinder();
$cpenb = new CPENameBinder();
$wfn = $cpenu->unbindURI($cpenb->bindToURI($wfn));
$wfn2 = $cpenu->unbindFS($cpenb->bindToFS($wfn2));
var_dump($cpenm->isDisjoint($wfn, $wfn2)); // false
var_dump($cpenm->isEqual($wfn, $wfn2)); // false
var_dump($cpenm->isSubset($wfn, $wfn2)); // true, $wfn2 is a subset of wfn
var_dump($cpenm->isSuperset($wfn, $wfn2)); // false
$wfn = $cpenu->unbindFS("cpe:2.3:a:adobe:*:9.*:*:PalmOS:*:*:*:*:*");
$wfn2 = $cpenu->unbindURI("cpe:/a::Reader:9.3.2:-:-");
var_dump($cpenm->isDisjoint($wfn, $wfn2)); // true, $wfn2 and wfn are disjoint
var_dump($cpenm->isEqual($wfn, $wfn2)); // false
var_dump($cpenm->isSubset($wfn, $wfn2)); // false
var_dump($cpenm->isSuperset($wfn, $wfn2)); // false
}
}

View File

@ -7,28 +7,41 @@ use PacificSec\CPE\Common\LogicalValue;
/**
* The CPENameBinder class is a simple implementation
* of the CPE Name binding algorithm, as specified in the
* CPE Naming Standard version 2.3. It is based on Java version
* implemented by Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* of the CPE Name binding algorithm, as specified in the
* CPE Naming Standard version 2.3.
* It is based on Java version
* implemented by Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class CPENameBinder {
class CPENameBinder
{
/**
* Binds a {@link WellFormedName} object to a URI.
* @param $w WellFormedName to be bound to URI
* @return URI binding of WFN
*
* @param $w WellFormedName
* to be bound to URI
* @return string URI binding of WFN
*/
public function bindToURI(WellFormedName $w) {
public function bindToURI(WellFormedName $w)
{
// Initialize the output with the CPE v2.2 URI prefix.
$uri = "cpe:/";
// Define the attributes that correspond to the seven components in a v2.2. CPE.
$attributes = array("part", "vendor", "product", "version", "update", "edition", "language");
$attributes = array(
"part",
"vendor",
"product",
"version",
"update",
"edition",
"language"
);
// Iterate over the well formed name
foreach ($attributes as $a) {
@ -56,15 +69,28 @@ class CPENameBinder {
/**
* Top-level function used to bind WFN w to formatted string.
* @param $w WellFormedName to bind
* @return Formatted String
*
* @param $w WellFormedName
* to bind
* @return string Formatted String
*/
public function bindToFS(WellFormedName $w) {
public function bindToFS(WellFormedName $w)
{
// Initialize the output with the CPE v2.3 string prefix.
$fs = "cpe:2.3:";
foreach (array("part", "vendor", "product", "version",
"update", "edition", "language", "sw_edition", "target_sw",
"target_hw", "other") as $a) {
foreach (array(
"part",
"vendor",
"product",
"version",
"update",
"edition",
"language",
"sw_edition",
"target_sw",
"target_hw",
"other"
) as $a) {
$v = $this->bindValueForFS($w->get($a));
$fs = $fs . $v;
// add a colon except at the very end
@ -78,10 +104,13 @@ class CPENameBinder {
/**
* Convert the value v to its proper string representation for insertion to
* formatted string.
* @param $v value to convert
* @return Formatted value
*
* @param mixed $v
* value to convert
* @return mixed Formatted value
*/
private function bindValueForFS($v) {
private function bindValueForFS($v)
{
if ($v instanceof LogicalValue) {
$l = $v;
// The value NA binds to a blank.
@ -97,30 +126,34 @@ class CPENameBinder {
}
/**
* Inspect each character in string s. Certain nonalpha characters pass
* Inspect each character in string s.
* Certain nonalpha characters pass
* thru without escaping into the result, but most retain escaping.
* @param $s
* @return
*
* @param
* $s
* @return
*/
private function processQuotedChars($s) {
private function processQuotedChars($s)
{
$result = "";
$idx = 0;
while ($idx < strlen($s)) {
$c = substr($s, $idx, 1);
if ($c != "\\") {
// unquoted characters pass thru unharmed.
$result = $result . $c;
$result .= $c;
} else {
// escaped characters are examined.
$nextchr = substr($s, $idx + 1, 1);
// the period, hyphen and underscore pass unharmed.
if ($nextchr == "." || $nextchr == "-" || $nextchr == "_") {
$result = $result . $nextchr;
$result .= $nextchr;
$idx = $idx + 2;
continue;
} else {
// all others retain escaping.
$result = $result . "\\" . $nextchr;
$result .= "\\" . $nextchr;
$idx = $idx + 2;
continue;
}
@ -132,12 +165,16 @@ class CPENameBinder {
/**
* Converts a string to the proper string for including in
* a CPE v2.2-conformant URI. The logical value ANY binds
* a CPE v2.2-conformant URI.
* The logical value ANY binds
* to the blank in the 2.2-conformant URI.
* @param $s string to be converted
* @return converted string
*
* @param $s string
* to be converted
* @return string converted string
*/
private function bindValueForURI($s) {
private function bindValueForURI($s)
{
if ($s instanceof LogicalValue) {
$l = $s;
// The value NA binds to a blank.
@ -159,10 +196,13 @@ class CPENameBinder {
* - Pass alphanumeric characters thru untouched
* - Percent-encode quoted non-alphanumerics as needed
* - Unquoted special characters are mapped to their special forms
* @param $s string to be transformed
* @return transformed string
*
* @param $s string
* to be transformed
* @return string transformed string
*/
private function transformForURI($s) {
private function transformForURI($s)
{
$result = "";
$idx = 0;
@ -171,7 +211,7 @@ class CPENameBinder {
$thischar = substr($s, $idx, 1);
// Alphanumerics (incl. underscore) pass untouched.
if (Utilities::isAlphanum($thischar)) {
$result = $result . $thischar;
$result .= $thischar;
$idx = $idx + 1;
continue;
}
@ -179,17 +219,17 @@ class CPENameBinder {
if ($thischar == "\\") {
$idx = $idx + 1;
$nxtchar = substr($s, $idx, 1);
$result = $result . $this->pctEncode($nxtchar);
$result .= $this->pctEncode($nxtchar);
$idx = $idx + 1;
continue;
}
// Bind the unquoted '?' special character to "%01".
if ($thischar == "?") {
$result = $result . "%01";
$result .= "%01";
}
// Bind the unquoted '*' special character to "%02".
if ($thischar == "*") {
$result = $result . "%02";
$result .= "%02";
}
$idx = $idx + 1;
}
@ -199,122 +239,98 @@ class CPENameBinder {
/**
* Returns the appropriate percent-encoding of character c.
* Certain characters are returned without encoding.
* @param $c the single character string to be encoded
* @return the percent encoded string
*
* @param string $c the
* single character string to be encoded
* @return string the percent encoded string
*/
private function pctEncode($c) {
if ($c == "!") {
return "%21";
private function pctEncode($c)
{
switch ($c) {
case '!':
return "%21";
case "\"":
return "%22";
case "#":
return "%23";
case "$":
return "%24";
case "%":
return "%25";
case "&":
return "%26";
case "'":
return "%27";
case "(":
return "%28";
case ")":
return "%29";
case "*":
return "%2a";
case "+":
return "%2b";
case ",":
return "%2c";
case "/":
return "%2f";
case ":":
return "%3a";
case ";":
return "%3b";
case "<":
return "%3c";
case "=":
return "%3d";
case ">":
return "%3e";
case "?":
return "%3f";
case "@":
return "%40";
case "[":
return "%5b";
case "\\":
return "%5c";
case "]":
return "%5d";
case "^":
return "%5e";
case "`":
return "%60";
case "{":
return "%7b";
case "|":
return "%7c";
case "}":
return "%7d";
case "~":
return "%7e";
default:
return $c;
}
if ($c == "\"") {
return "%22";
}
if ($c == "#") {
return "%23";
}
if ($c == "$") {
return "%24";
}
if ($c == "%") {
return "%25";
}
if ($c == "&") {
return "%26";
}
if ($c == "'") {
return "%27";
}
if ($c == "(") {
return "%28";
}
if ($c == ")") {
return "%29";
}
if ($c == "*") {
return "%2a";
}
if ($c == "+") {
return "%2b";
}
if ($c == ",") {
return "%2c";
}
// bound without encoding.
if ($c == "-") {
return $c;
}
// bound without encoding.
if ($c == ".") {
return $c;
}
if ($c == "/") {
return "%2f";
}
if ($c == ":") {
return "%3a";
}
if ($c == ";") {
return "%3b";
}
if ($c == "<") {
return "%3c";
}
if ($c == "=") {
return "%3d";
}
if ($c == ">") {
return "%3e";
}
if ($c == "?") {
return "%3f";
}
if ($c == "@") {
return "%40";
}
if ($c == "[") {
return "%5b";
}
if ($c == "\\") {
return "%5c";
}
if ($c == "]") {
return "%5d";
}
if ($c == "^") {
return "%5e";
}
if ($c == "`") {
return "%60";
}
if ($c == "{") {
return "%7b";
}
if ($c == "|") {
return "%7c";
}
if ($c == "}") {
return "%7d";
}
if ($c == "~") {
return "%7d";
}
// Shouldn't reach here, return original character
return $c;
}
/**
* Packs the values of the five arguments into the single
* edition component. If all the values are blank, the
* Packs the values of the five arguments into the single
* edition component.
* If all the values are blank, the
* function returns a blank.
* @param $ed edition string
* @param $sw_ed software edition string
* @param $t_sw target software string
* @param $t_hw target hardware string
* @param $oth other edition information string
* @return the packed string, or blank
*
* @param string $ed edition
* string
* @param string $sw_ed software
* edition string
* @param string $t_sw target
* software string
* @param string $t_hw target
* hardware string
* @param string $oth other
* edition information string
* @return string the packed string, or blank
*/
private function pack($ed, $sw_ed, $t_sw, $t_hw, $oth) {
if ($sw_ed == "" && $t_sw == "" && $t_hw == "" && $oth == "") {
private function pack($ed, $sw_ed, $t_sw, $t_hw, $oth)
{
if ($sw_ed == "" && $t_sw == "" && $t_hw == "" && $oth == "") {
// All the extended attributes are blank, so don't do
// any packing, just return ed.
return $ed;
@ -326,13 +342,16 @@ class CPENameBinder {
/**
* Removes trailing colons from the URI.
* @param $s the string to be trimmed
* @return the trimmed string
*
* @param string $s the
* string to be trimmed
* @return string the trimmed string
*/
private function trim($s) {
private function trim($s)
{
$s1 = strrev($s);
$idx = 0;
for ($i = 0; $i != strlen($s1); $i++) {
for ($i = 0; $i != strlen($s1); $i ++) {
if (substr($s1, $i, 1) == ":") {
$idx = $idx + 1;
} else {
@ -341,30 +360,30 @@ class CPENameBinder {
}
// Return the substring after all trailing colons,
// reversed back to its original character order.
return strrev(substr($s1, $idx, strlen($s1)-$idx));
return strrev(substr($s1, $idx, strlen($s1) - $idx));
}
/*
* Static method to demonstrate this class.
*/
public static function test() {
public static function test()
{
// A few examples.
echo "Testing CPENamingBind...<br>\n";
$wfn = new WellFormedName("a", "microsoft", "internet_explorer", "8\\.0\\.6001",
"beta", new LogicalValue("ANY"), "sp2", null, null, null, null);
$wfn2 = new WellFormedName();
$wfn2->set("part", "a");
$wfn2->set("vendor", "foo\\\$bar");
$wfn2->set("product", "insight");
$wfn2->set("version", "7\\.4\\.0\\.1570");
$wfn2->set("target_sw", "win2003");
$wfn2->set("update", new LogicalValue("NA"));
$wfn2->set("sw_edition", "online");
$wfn2->set("target_hw", "x64");
$cpenb = new CPENameBinder();
echo $cpenb->bindToURI($wfn) . "<br>\n";
echo $cpenb->bindToFS($wfn2) . "<br>\n";
echo "Testing CPENamingBind...<br>\n";
$wfn = new WellFormedName("a", "microsoft", "internet_explorer", "8\\.0\\.6001", "beta", new LogicalValue("ANY"), "sp2", null, null, null, null);
$wfn2 = new WellFormedName();
$wfn2->set("part", "a");
$wfn2->set("vendor", "foo\\\$bar");
$wfn2->set("product", "insight");
$wfn2->set("version", "7\\.4\\.0\\.1570");
$wfn2->set("target_sw", "win2003");
$wfn2->set("update", new LogicalValue("NA"));
$wfn2->set("sw_edition", "online");
$wfn2->set("target_hw", "x64");
$cpenb = new CPENameBinder();
echo $cpenb->bindToURI($wfn) . "<br>\n";
echo $cpenb->bindToFS($wfn2) . "<br>\n";
}
}

View File

@ -0,0 +1,466 @@
<?php
namespace PacificSec\CPE\Naming;
use PacificSec\CPE\Common\WellFormedName;
use PacificSec\CPE\Common\Utilities;
use PacificSec\CPE\Common\LogicalValue;
use \Exception;
/**
* The CPENameUnBinder class is a simple implementation
* of the CPE Name unbinding algorithm, as specified in the
* CPE Naming Standard version 2.3. It is based on Java version
* implemented by Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class CPENameUnbinder {
/**
* Top level function used to unbind a URI to a WFN.
* @param $uri string representing the URI to be unbound.
* @return WellFormedName representing the unbound URI.
* @throws Exception representing parsing errors.
*/
public function unbindURI($uri) {
// Validate the URI
Utilities::validateURI($uri);
// Initialize the empty WFN.
$result = new WellFormedName();
for ($i = 0; $i != 8; $i++) {
// get the i'th component of uri
$v = $this->getCompURI($uri, $i);
switch ($i) {
case 1:
$result->set("part", $this->decode($v));
break;
case 2:
$result->set("vendor", $this->decode($v));
break;
case 3:
$result->set("product", $this->decode($v));
break;
case 4:
$result->set("version", $this->decode($v));
break;
case 5:
$result->set("update", $this->decode($v));
break;
case 6:
// Special handling for edition component.
// Unpack edition if needed.
if ($v == "" || $v == "-"
|| substr($v, 0, 1) != "~") {
// Just a logical value or a non-packed value.
// So unbind to legacy edition, leaving other
// extended attributes unspecified.
$result->set("edition", $this->decode($v));
} else {
// We have five values packed together here.
$this->unpack($v, $result);
}
break;
case 7:
$result->set("language", $this->decode($v));
break;
}
}
return $result;
}
/**
* Top level function to unbind a formatted string to WFN.
* @param string $fs Formatted string to unbind
* @return WellFormedName
* @throws Exception representing parsing error
*/
public function unbindFS($fs) {
// Validate the formatted string
Utilities::validateFS($fs);
// Initialize empty WFN
$result = new WellFormedName();
// The cpe scheme is the 0th component, the cpe version is the 1st.
// So we start parsing at the 2nd component.
for ($a = 2; $a != 13; $a++) {
// Get the a'th string field.
$v = $this->getCompFS($fs, $a);
// Unbind the string.
$v = $this->unbindValueFS($v);
// Set the value of the corresponding attribute.
switch ($a) {
case 2:
$result->set("part", $v);
break;
case 3:
$result->set("vendor", $v);
break;
case 4:
$result->set("product", $v);
break;
case 5:
$result->set("version", $v);
break;
case 6:
$result->set("update", $v);
break;
case 7:
$result->set("edition", $v);
break;
case 8:
$result->set("language", $v);
break;
case 9:
$result->set("sw_edition", $v);
break;
case 10:
$result->set("target_sw", $v);
break;
case 11:
$result->set("target_hw", $v);
break;
case 12:
$result->set("other", $v);
break;
}
}
return $result;
}
/**
* Returns the i'th field of the formatted string. The colon is the field
* delimiter unless prefixed by a backslash.
* @param string $fs formatted string to retrieve from
* @param int $i index of field to retrieve from fs.
* @return int value of index of formatted string
*/
private function getCompFS($fs, $i) {
if ($i == 0) {
// return the substring from index 0 to the first occurence of an
// unescaped colon
$colon_idx = Utilities::getUnescapedColonIndex($fs);
// If no colon is found, we are at the end of the formatted string,
// so just return what's left.
if ($colon_idx == 0) {
return $fs;
}
return substr($fs, 0, $colon_idx);
} else {
$substrStart = Utilities::getUnescapedColonIndex($fs) + 1;
$substrLength = strlen($fs) - $substrStart;
return $this->getCompFS(substr($fs, $substrStart, $substrLength), $i - 1);
}
}
/**
* Takes a string value and returns the appropriate logical value if string
* is the bound form of a logical value. If string is some general value
* string, add quoting of non-alphanumerics as needed.
* @param string $s value to be unbound
* @return string logical value or quoted string
* @throws Exception representing parsing errors
*/
private function unbindValueFS($s) {
if ($s == "*") {
return new LogicalValue("ANY");
}
if ($s == "-") {
return new LogicalValue("NA");
}
return $this->addQuoting($s);
}
/**
* Inspect each character in a string, copying quoted characters, with
* their escaping, into the result. Look for unquoted non alphanumerics
* and if not "*" or "?", add escaping.
* @param $s
* @return
* @throws Exception representing parsing errors.
*/
private function addQuoting($s) {
$result = "";
$idx = 0;
$embedded = false;
while ($idx < strlen($s)) {
$c = substr($s, $idx, 1);
if (Utilities::isAlphanum($c)) {
// Alphanumeric characters pass untouched.
$result .= $c;
$idx = $idx + 1;
$embedded = true;
continue;
}
if ($c == "\\") {
// Anything quoted in the bound string stays quoted in the
// unbound string.
$result .= substr($s, $idx, 2);
$idx = $idx + 2;
$embedded = true;
continue;
}
if ($c == "*") {
// An unquoted asterisk must appear at the beginning or the end
// of the string.
if ($idx == 0 || $idx == strlen($s) - 1) {
$result .= $c;
$idx = $idx + 1;
$embedded = true;
continue;
} else {
throw new Exception("Error! cannot have unquoted * embedded in formatted string.", 0);
}
}
if ($c == "?") {
// An unquoted question mark must appear at the beginning or
// end of the string, or in a leading or trailing sequence.
if ( // ? legal at beginning or end
(($idx == 0) || ($idx == (strlen($s) - 1)))
// embedded is false, so must be preceded by ?
|| (!$embedded && (substr($s, $idx - 1, 1) == "?"))
// embedded is true, so must be followed by ?
|| ($embedded && (substr($s, $idx + 1, 1) == "?"))) {
$result .= $c;
$idx = $idx + 1;
$embedded = false;
continue;
} else {
throw new Exception("Error! cannot have unquoted ? embedded in formatted string.", 0);
}
}
// All other characters must be quoted.
$result .= "\\" . $c;
$idx = $idx + 1;
$embedded = true;
}
return $result;
}
/**
* Return the i'th component of the URI.
* @param $uri string representation of URI to retrieve components from.
* @param int $i Index of component to return.
* @return mixed If i = 0, returns the URI scheme. Otherwise, returns the i'th
* component of uri.
*/
private function getCompURI($uri, $i) {
if ($i == 0) {
return substr($uri, $i, strpos($uri, "/"));
}
$sa = explode(":", $uri);
// If requested component exceeds the number
// of components in URI, return blank
if ($i >= sizeof($sa)) {
return "";
}
if ($i === 1) {
return substr($sa[$i], 1, strlen($sa[$i])-1);
}
return $sa[$i];
}
/**
* Scans a string and returns a copy with all percent-encoded characters
* decoded. This function is the inverse of pctEncode() defined in the
* CPENameBinder class. Only legal percent-encoded forms are decoded.
* Others raise a ParseException.
* @param $s string to be decoded
* @return string decoded string
* @throws Exception representing parsing errors
* @see CPENameBinder#pctEncode
*/
private function decode($s) {
if ($s == "") {
return new LogicalValue("ANY");
}
if ($s == "-") {
return new LogicalValue("NA");
}
// Start the scanning loop.
// Normalize: convert all uppercase letters to lowercase first.
$s = strtolower($s);
$result = "";
$idx = 0;
$embedded = false;
while ($idx < strlen($s)) {
// Get the idx'th character of s.
$c = substr($s, $idx, 1);
// Deal with dot, hyphen, and tilde: decode with quoting.
if ($c == "." || $c == "-" || $c == "~") {
$result .= "\\" . $c;
$idx = $idx + 1;
// a non-%01 encountered.
$embedded = true;
continue;
}
if ($c != "%") {
$result .= $c;
$idx = $idx + 1;
// a non-%01 encountered.
$embedded = true;
continue;
}
// We get here if we have a substring starting w/ '%'.
$form = substr($s, $idx, 3);
if ($form == "%01") {
if (($idx == 0)
|| ($idx == strlen($s) - 3)
|| (!$embedded && substr($s, $idx - 3, 2) == "%01")
|| ($embedded && (strlen($s) >= $idx + 6))
&& (substr($s, $idx + 3, 3) == "%01")) {
$result .= "?";
$idx = $idx + 3;
continue;
} else {
throw new Exception("Error decoding string", 0);
}
} else if ($form == "%02") {
if (($idx == 0) || ($idx == (strlen($s) - 3))) {
$result .= "*";
} else {
throw new Exception("Error decoding string", 0);
}
} else if ($form == "%21") {
$result .= "\\!";
} else if ($form == "%22") {
$result .= "\\\"";
} else if ($form == "%23") {
$result .= "\\#";
} else if ($form == "%24") {
$result .= "\\$";
} else if ($form == "%25") {
$result .= "\\%";
} else if ($form == "%26") {
$result .= "\\&";
} else if ($form == "%27") {
$result .= "\\'";
} else if ($form == "%28") {
$result .= "\\(";
} else if ($form == "%29") {
$result .= "\\)";
} else if ($form == "%2a") {
$result .= "\\*";
} else if ($form == "%2b") {
$result .= "\\+";
} else if ($form == "%2c") {
$result .= "\\,";
} else if ($form == "%2f") {
$result .= "\\/";
} else if ($form == "%3a") {
$result .= "\\:";
} else if ($form == "%3b") {
$result .= "\\;";
} else if ($form == "%3c") {
$result .= "\\<";
} else if ($form == "%3d") {
$result .= "\\=";
} else if ($form == "%3e") {
$result .= "\\>";
} else if ($form == "%3f") {
$result .= "\\?";
} else if ($form == "%40") {
$result .= "\\@";
} else if ($form == "%5b") {
$result .= "\\[";
} else if ($form == "%5c") {
$result .= "\\\\";
} else if ($form == "%5d") {
$result .= "\\]";
} else if ($form == "%5e") {
$result .= "\\^";
} else if ($form == "%60") {
$result .= "\\`";
} else if ($form == "%7b") {
$result .= "\\{";
} else if ($form == "%7c") {
$result .= "\\|";
} else if ($form == "%7d") {
$result .= "\\}";
} else if ($form == "%7e") {
$result .= "\\~";
} else {
throw new Exception("Unknown form: " . $form, 0);
}
$idx = $idx + 3;
$embedded = true;
}
return $result;
}
/**
* Unpacks the elements in s and sets the attributes in the given
* WellFormedName accordingly.
* @param string $s packed string
* @param $wfn WellFormedName
* @return WellFormedName The augmented WellFormedName.
*/
private function unpack($s, WellFormedName $wfn) {
// Parse out the five elements.
$start = 1;
$ed = ""; $sw_edition = ""; $t_sw = ""; $t_hw = ""; $oth = "";
$end = strpos($s, "~", $start);
if ($start == $end) {
$ed = "";
} else {
$ed = substr($s, $start, $end-$start);
}
$start = $end + 1;
$end = strpos($s, "~", $start);
if ($start == $end) {
$sw_edition = "";
} else {
$sw_edition = substr($s, $start, $end-$start);
}
$start = $end + 1;
$end = strpos($s, "~", $start);
if ($start == $end) {
$t_sw = "";
} else {
$t_sw = substr($s, $start, $end-$start);
}
$start = $end + 1;
$end = strpos($s, "~", $start);
if ($start == $end) {
$t_hw = "";
} else {
$t_hw = substr($s, $start, $end-$start);
}
$start = $end + 1;
if ($start >= strlen($s)) {
$oth = "";
} else {
$oth = substr($s, $start, strlen($s) - 1 - $start);
}
// Set each component in the WFN.
try {
$wfn->set("edition", $this->decode($ed));
$wfn->set("sw_edition", $this->decode($sw_edition));
$wfn->set("target_sw", $this->decode($t_sw));
$wfn->set("target_hw", $this->decode($t_hw));
$wfn->set("other", $this->decode($oth));
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
return $wfn;
}
/*
* Static method to demonstrate this class.
*/
public static function test() {
// A few examples.
echo "Testing CPENamingUnbind...<br>\n";
$cpenu = new CPENameUnbinder();
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer%01%01%01%01:?:beta");
echo $wfn . "<br>\n";
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer:8.%2a:sp%3f");
echo $wfn . "<br>\n";
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer:8.%02:sp%01");
echo $wfn . "<br>\n";
$wfn = $cpenu->unbindURI("cpe:/a:hp:insight_diagnostics:7.4.0.1570::~~online~win2003~x64~");
echo $wfn . "<br>\n";
echo $cpenu->unbindFS("cpe:2.3:a:micr\\?osoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*") . "<br>\n";
}
}

View File

@ -1,42 +0,0 @@
<?php
namespace PacificSec\CPE\Common;
/**
* This class represents a Logical Value. It is based on Java version
* implemented by JKRAUNELIS <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class LogicalValue {
private $any = false;
private $na = false;
// Object must be constructed with the string "ANY" or "NA".
public function __construct($type) {
if ($type == "ANY") {
$this->any = true;
} else if ($type == "NA") {
$this->na = true;
} else {
throw new Exception("LogicalValue must be ANY or NA");
}
}
public function isANY(){
return $this->any;
}
public function isNA(){
return $this->na;
}
public function __toString(){
if ($this->any){
return "ANY";
}
return "NA";
}
}

View File

@ -1,167 +0,0 @@
<?php
namespace PacificSec\CPE\Common;
use \Exception;
/**
* A collection of utility functions for use with the matching and
* naming namespaces. It is based on Java version implemented by
* Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class Utilities {
/**
* Searches string for special characters * and ?
* @param $string to be searched
* @return true if string contains wildcard, false otherwise
*/
public static function containsWildcards($string) {
if (strpos($string, "*") !== false || strpos($string, "?") !== false) {
if (!(strpos($string, "\\") !== false)) {
return true;
}
return false;
}
return false;
}
/**
* Checks if given number is even or not
* @param $num number to check
* @return true if number is even, false if not
*/
public static function isEvenNumber($num) {
return (is_int($num) && $num % 2 == 0);
}
/**
* 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
* @param $end ending index
* @return number of escape characters in string
* @todo fix the use of $str. The Java version is also not using this variable.
*/
public static function countEscapeCharacters($str, $start, $end) {
$result = 0;
$active = false;
$i = 0;
while ($i < $end) {
if ($active && ($i >= $start)) {
$result = $result + 1;
}
$i = $i + 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 function getUnescapedColonIndex($str) {
$found = false;
$colon_idx = 0;
$start_idx = 0;
// Find the first non-escaped colon.
while (!$found) {
$colon_idx = strpos($str, ":", $start_idx + 1);
// If no colon is found, return 0.
if ($colon_idx === false) {
return 0;
}
// Peek at character before colon.
if (substr($str, $colon_idx-1, 1) == "\\") {
// If colon is escaped, keep looking.
$start_idx = $colon_idx;
} else {
$found = true;
}
}
return $colon_idx;
}
/**
* Returns true if the string contains only
* alphanumeric characters or the underscore character,
* false otherwise.
* @param $c the string in question
* @return true if $c is alphanumeric or underscore, false if not
*/
public static function isAlphanum($c) {
return (preg_match("/^[a-zA-Z0-9]$/", $c) || $c == "_");
}
/**
* 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:
* URI must start with the characters "cpe:/"
* A URI may not contain more than 7 components
* If either rule is violated, a Exception is thrown.
* @param $in string with URI to be validated
*/
public static function validateURI($in) {
// make sure uri starts with cpe:/
if (strpos(strtolower($in), "cpe:/") !== 0) {
throw new Exception("Error: URI must start with 'cpe:/'. Given: " . $in, 0);
}
// make sure uri doesn't contain more than 7 colons
$count = sizeof(explode(":", $in));
if ($count > 8) {
throw new Exception("Error parsing URI. Found " . ($count - 8) . " extra components in: " . $in, 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:
* Formatted string must start with the characters "cpe:2.3:"
* A formatted string must contain 11 components
* A formatted string must not contain empty components
* If any rule is violated, a ParseException is thrown.
* @param $in string with FS to be validated
*/
public static function validateFS($in) {
if (strpos(strtolower($in), "cpe:2.3:") !== 0) {
throw new Exception("Error: Formatted String must start with \"cpe:2.3\". Given: " . $in, 0);
}
$count = 0;
for ($i = 0; $i != strlen($in); $i++){
if (substr($in, $i, 1) == ":"){
if (substr($in, $i - 1, 1) != "\\"){
$count++;
}
if (($i+1) < strlen($in) && substr($in, $i+1, 1) == ":"){
throw new Exception("Error parsing formatted string. Found empty component", 0);
}
}
}
if ($count > 12){
$extra = $count - 12;
$s = "Error parsing formatted string. Found " . $extra . " extra component";
if ($extra > 1){
$s = $s . "s";
}
$s = $s . " in: " . $in;
throw new Exception($s, 0);
}
if ($count < 12){
$missing = 12 - $count;
$s = "Error parsing formatted string. Missing " . $missing . " component";
if ($missing > 1){
$s = $s . "s";
}
throw new Exception($s, 0);
}
}
}

View File

@ -1,210 +0,0 @@
<?php
namespace PacificSec\CPE\Common;
use \Exception;
/**
* The WellFormedName class represents a Well Formed Name, as defined
* in the CPE Specification version 2.3. It is based on Java version
* implemented by jkraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for details.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class WellFormedName {
// Underlying wfn representation.
private $wfn = null;
// All permissible WFN attributes as defined by specification.
const ATTRIBUTES = array("part", "vendor", "product", "version",
"update", "edition", "language", "sw_edition", "target_sw",
"target_hw", "other");
/**
* 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 $sw_edition string representing the sw_edition component
* @param $target_sw string representing the target_sw component
* @param $target_hw string representing the target_hw component
* @param $other string representing the other component
*/
public function __construct($part = null, $vendor = null, $product = null, $version = null,
$update = null, $edition = null, $language = null, $sw_edition = null, $target_sw = null,
$target_hw = null, $other = null) {
$this->wfn = array();
// Constructs a new WellFormedName object, with all components set to the default value "ANY".
if ($part === null && $vendor === null && $product === null && $version === null &&
$update === null && $edition === null && $language === null && $sw_edition === null && $target_sw === null &&
$target_hw === null && $other === null){
foreach (WellFormedName::ATTRIBUTES as $a){
if ($a != "part"){
$this->set($a, new LogicalValue("ANY"));
}
}
return;
}
$this->set("part", $part);
$this->set("vendor", $vendor);
$this->set("product", $product);
$this->set("version", $version);
$this->set("update", $update);
$this->set("edition", $edition);
$this->set("language", $language);
$this->set("sw_edition", $sw_edition);
$this->set("target_sw", $target_sw);
$this->set("target_hw", $target_hw);
$this->set("other", $other);
}
/**
* @param $attribute string representing the component value to get
* @return the string value of the given component, or default value "ANY"
* if the component does not exist
*/
public function get($attribute){
if (array_key_exists($attribute, $this->wfn))
return $this->wfn[$attribute];
else
return new LogicalValue("ANY");
}
/**
* Sets the given attribute to value, if the attribute is in the list of
* permissible components
* @param $attribute string representing the component to set
* @param $value object or string representing the value of the given component
*/
public final function set($attribute, $value){
// Iterate over permissible attributes.
foreach (WellFormedName::ATTRIBUTES as $a){
// If the argument is a valid attribute, set that attribute's value.
if ($attribute == $a) {
// 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"){
var_dump($value); echo "<br>\n";
var_dump($a); echo "<br>\n";
var_dump($attribute); echo "<br>\n";
throw new Exception("Error! part component cannot be a logical value");
}
// put the Object in the ht and break
$this->wfn[$attribute] = $value;
break;
}
if ($value == null || $value == ""){
// if value is null or blank, set attribute to default logical ANY
$this->wfn[$attribute] = new LogicalValue("ANY");
break;
}
$svalue = $value;
// Reg exs
// check for printable characters - no control characters
if (!preg_match("/^[[:print:]]*$/", $svalue)){
throw new Exception("Error! encountered non printable character in: " . $svalue, 0);
}
// svalue has whitespace
if (preg_match("/^.*\\s+.*$/", $svalue)){
throw new Exception("Error! component cannot contain whitespace: " . $svalue, 0);
}
// svalue has more than one unquoted star
if (preg_match("/^\\*{2,}.*$/", $svalue) || preg_match("/^.*\\*{2,}$/", $svalue)){
throw new Exception("Error! component cannot contain more than one * in sequence: " . $svalue, 0);
}
// svalue has unquoted punctuation embedded
if (preg_match("/^.*(?<!\\\\)[\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\+\\,\\.\\/\\:\\;\\<\\=\\>\\@\\[\\]\\^\\`\\{\\|\\}\\~\\-].*$/", $svalue)) {
throw new Exception("Error! component cannot contain unquoted punctuation: " . $svalue, 0);
}
// svalue has an unquoted *
if (preg_match("/^.+(?<!\\\\)[\\*].+$/", $svalue)) {
throw new Exception("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 (strpos($svalue, "?") !== false) {
if ($svalue == "?") {
// single ? is valid
$this->wfn[$attribute] = $svalue;
break;
}
// remove leading and trailing ?s
$v = $svalue;
while (strpos($v, "?") === 0) {
// remove all leading ?'s
$v = substr($v, 1);
}
$v = strrev($v);
while (strpos($v, "?") === 0) {
// remove all trailing ?'s (string has been reversed)
$v = substr($v, 1);
}
// back to normal
$v = strrev($v);
// after leading and trailing ?s are removed, check if value
// contains unquoted ?s
if (preg_match("/^.+(?<!\\\\)[\\?].+$/", $v)) {
throw new Exception("Error! component cannot contain embedded ?: " . $svalue, 0);
}
}
// single asterisk is not allowed
if ($svalue == "*") {
throw new Exception("Error! component cannot be a single *: " . $svalue, 0);
}
// quoted hyphen not allowed by itself
if ($svalue == "-") {
throw new Exception("Error! component cannot be quoted hyphen: " . $svalue, 0);
}
// part must be a, o, or h
if ($attribute == "part") {
if ($svalue != "a" && $svalue != "o" && $svalue != "h") {
throw new Exception("Error! part component must be one of the following: 'a', 'o', 'h': " . $svalue, 0);
}
}
// should be good to go
$this->wfn[$attribute] = $svalue;
break;
}
}
}
/**
*
* @return string representation of the WellFormedName
*/
public function __toString() {
$str = "wfn:[";
foreach (WellFormedName::ATTRIBUTES as $attr) {
$str = $str . $attr;
$str = $str . "=";
$o = $this->wfn[$attr];
if ($o instanceof LogicalValue) {
$str = $str . $o;
$str = $str . ", ";
} else {
$str = $str . "\"";
$str = $str . $o;
$str = $str . "\", ";
}
}
$str = substr($str, 0, strlen($str)-1);
$str = substr($str, 0, strlen($str)-1);
$str = $str . "]";
return $str;
}
}

View File

@ -1,287 +0,0 @@
<?php
namespace PacificSec\CPE\Matching;
use PacificSec\CPE\Common\WellFormedName;
use PacificSec\CPE\Common\Utilities;
use PacificSec\CPE\Common\LogicalValue;
use PacificSec\CPE\Naming\CPENameBinder;
use PacificSec\CPE\Naming\CPENameUnbinder;
/**
* The CPENameMatcher is an implementation of the CPE Matching algorithm,
* as specified in the CPE Matching Standard version 2.3. It is based on
* Java version implemented by Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class CPENameMatcher {
/**
* Tests two Well Formed Names for disjointness.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the names are disjoint, false otherwise
*/
public function isDisjoint(WellFormedName $source, WellFormedName $target) {
// if any pairwise comparison is disjoint, the names are disjoint.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result == Relation::DISJOINT)
return true;
}
return false;
}
/**
* Tests two Well Formed Names for equality.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the names are equal, false otherwise
*/
public function isEqual(WellFormedName $source, WellFormedName $target) {
// if every pairwise comparison is equal, the names are equal.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result != Relation::EQUAL){
return false;
}
}
return true;
}
/**
* Tests if the target Well Formed Name is a subset of the source Well Formed
* Name.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the target is a subset of the source, false otherwise
*/
public function isSubset(WellFormedName $source, WellFormedName $target) {
// if any comparison is anything other than subset or equal, then target is
// not a subset of source.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result != Relation::SUBSET && $result != Relation::EQUAL) {
return false;
}
}
return true;
}
/**
* Tests if the target Well Formed name is a superset of the source Well Formed
* Name.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return true if the target is a superset of the source, false otherwise
*/
public function isSuperset(WellFormedName $source, WellFormedName $target) {
// if any comparison is anything other than superset or equal, then target is not
// a superset of source.
$resultList = $this->compareWFNs($source, $target);
foreach ($resultList as $result){
if ($result != Relation::SUPERSET && $result != Relation::EQUAL) {
return false;
}
}
return true;
}
/**
* Compares each attribute value pair in two Well Formed Names.
* @param $source WellFormedName Source WFN
* @param $target WellFormedName Target WFN
* @return A array mapping attribute string to attribute value Relation
*/
public function compareWFNs(WellFormedName $source, WellFormedName $target) {
$result = array();
$result["part"] = $this->compare($source->get("part"), $target->get("part"));
$result["vendor"] = $this->compare($source->get("vendor"), $target->get("vendor"));
$result["product"] = $this->compare($source->get("product"), $target->get("product"));
$result["version"] = $this->compare($source->get("version"), $target->get("version"));
$result["update"] = $this->compare($source->get("update"), $target->get("update"));
$result["edition"] = $this->compare($source->get("edition"), $target->get("edition"));
$result["language"] = $this->compare($source->get("language"), $target->get("language"));
$result["sw_edition"] = $this->compare($source->get("sw_edition"), $target->get("sw_edition"));
$result["target_sw"] = $this->compare($source->get("target_sw"), $target->get("target_sw"));
$result["target_hw"] = $this->compare($source->get("target_hw"), $target->get("target_hw"));
$result["other"] = $this->compare($source->get("other"), $target->get("other"));
return $result;
}
/**
* Compares an attribute value pair.
* @param $source Source attribute value.
* @param $target Target attribute value.
* @return The relation between the two attribute values.
*/
private function compare($source, $target) {
// matching is case insensitive, convert strings to lowercase.
if ($this->isString($source)) {
$source = strtolower($source);
}
if ($this->isString($target)) {
$target = strtolower($target);
}
// Unquoted wildcard characters yield an undefined result.
if ($this->isString($target) && Utilities::containsWildcards($target)) {
return Relation::UNDEFINED;
}
// If source and target values are equal, then result is equal.
if ($source == $target) {
return Relation::EQUAL;
}
// Check to see if source or target are Logical Values.
$lvSource = null;
$lvTarget = null;
if ($source instanceof LogicalValue) {
$lvSource = $source;
}
if ($target instanceof LogicalValue) {
$lvTarget = $target;
}
if ($lvSource != null && $lvTarget != null) {
// If Logical Values are equal, result is equal.
if ($lvSource->isANY() == $lvTarget->isANY() || $lvSource->isNA() == $lvTarget->isNA()) {
return Relation::EQUAL;
}
}
// If source value is ANY, result is a superset.
if ($lvSource != null) {
if ($lvSource->isANY()) {
return Relation::SUPERSET;
}
}
// If target value is ANY, result is a subset.
if ($lvTarget != null) {
if ($lvTarget->isANY()) {
return Relation::SUBSET;
}
}
// If source or target is NA, result is disjoint.
if ($lvSource != null) {
if ($lvSource->isNA()) {
return Relation::DISJOINT;
}
}
if ($lvTarget != null) {
if ($lvTarget->isNA()) {
return Relation::DISJOINT;
}
}
// only Strings will get to this point, not LogicalValues
return $this->compareStrings($source, $target);
}
/**
* Compares a source string to a target string, and addresses the condition
* in which the source string includes unquoted special characters. It
* performs a simple regular expression match, with the assumption that
* (as required) unquoted special characters appear only at the beginning
* and/or the end of the source string. It also properly differentiates
* between unquoted and quoted special characters.
*
* @param $source string Source attribute value.
* @param $target string Target attribute value.
* @return Relation between source and target Strings.
*/
private function compareStrings($source, $target) {
$start = 0;
$end = strlen($source);
$begins = 0;
$ends = 0;
$index = 0; $leftover = 0; $escapes = 0;
if (substr($source, 0, 1) == "*") {
$start = 1;
$begins = -1;
} else {
while (($start < strlen($source)) && (substr($source, $start, 1) == "?")) {
$start = $start + 1;
$begins = $begins + 1;
}
}
if ((substr($source, $end - 1, 1) == "*") && ($this->isEvenWildcards($source, $end - 1))) { //TODO
$end = $end - 1;
$ends = -1;
} else {
while (($end > 0) && substr($source, $end - 1, 1) == "?" && ($this->isEvenWildcards($source, $end - 1))) { //TODO
$end = $end - 1;
$ends = $ends + 1;
}
}
$source = substr($source, $start, $end-$start);
$index = -1;
$leftover = strlen($target);
while ($leftover > 0) {
$index = strpos($target, $source, $index + 1);
if ($index === false) {
break;
}
$escapes = Utilities::countEscapeCharacters($target, 0, $index);
if (($index > 0) && ($begins != -1) && ($begins < ($index - $escapes))) {
break;
}
$escapes = Utilities::countEscapeCharacters($target, $index + 1, strlen($target));
$leftover = strlen($target) - $index - $escapes - strlen($source);
if (($leftover > 0) && (($ends != -1) && ($leftover > $ends))) {
continue;
}
return Relation::SUPERSET;
}
return Relation::DISJOINT;
}
/**
* Searches a string for the backslash character
* @param $str string to search in
* @param $idx end index
* @return true if the number of backslash characters is even, false if odd
*/
private function isEvenWildcards($str, $idx) {
$result = 0;
while (($idx > 0) && (strpos($str, "\\", $idx - 1)) !== false) {
$idx = $idx - 1;
$result = $result + 1;
}
return Utilities::isEvenNumber($result);
}
/**
* Tests if an Object is an instance of the String class
* @param $arg the var to test
* @return true if arg is a string, false if not
*/
private function isString($arg) {
is_string($arg);
}
/*
* Static method to demonstrate this class.
*/
public static function test() {
// Examples.
$wfn = new WellFormedName("a", "microsoft", "internet_explorer", "8\\.0\\.6001", "beta", new LogicalValue("ANY"), "sp2", null, null, null, null);
$wfn2 = new WellFormedName("a", "microsoft", "internet_explorer", new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"), new LogicalValue("ANY"));
$cpenm = new CPENameMatcher();
$cpenu = new CPENameUnbinder();
$cpenb = new CPENameBinder();
$wfn = $cpenu->unbindURI($cpenb->bindToURI($wfn));
$wfn2 = $cpenu->unbindFS($cpenb->bindToFS($wfn2));
var_dump($cpenm->isDisjoint($wfn, $wfn2)); // false
var_dump($cpenm->isEqual($wfn, $wfn2)); // false
var_dump($cpenm->isSubset($wfn, $wfn2)); // true, $wfn2 is a subset of wfn
var_dump($cpenm->isSuperset($wfn, $wfn2)); // false
$wfn = $cpenu->unbindFS("cpe:2.3:a:adobe:*:9.*:*:PalmOS:*:*:*:*:*");
$wfn2 = $cpenu->unbindURI("cpe:/a::Reader:9.3.2:-:-");
var_dump($cpenm->isDisjoint($wfn, $wfn2)); // true, $wfn2 and wfn are disjoint
var_dump($cpenm->isEqual($wfn, $wfn2)); // false
var_dump($cpenm->isSubset($wfn, $wfn2)); // false
var_dump($cpenm->isSuperset($wfn, $wfn2)); // false
}
}

View File

@ -1,466 +0,0 @@
<?php
namespace PacificSec\CPE\Naming;
use PacificSec\CPE\Common\WellFormedName;
use PacificSec\CPE\Common\Utilities;
use PacificSec\CPE\Common\LogicalValue;
use \Exception;
/**
* The CPENameUnBinder class is a simple implementation
* of the CPE Name unbinding algorithm, as specified in the
* CPE Naming Standard version 2.3. It is based on Java version
* implemented by Joshua Kraunelis <jkraunelis@mitre.org>.
*
* @see <a href="http://cpe.mitre.org">cpe.mitre.org</a> for more information.
* @author Antonio Franco
* @email antonio.franco@pacificsec.com
*/
class CPENameUnbinder {
/**
* Top level function used to unbind a URI to a WFN.
* @param $uri string representing the URI to be unbound.
* @return WellFormedName representing the unbound URI.
* @throws Exception representing parsing errors.
*/
public function unbindURI($uri) {
// Validate the URI
Utilities::validateURI($uri);
// Initialize the empty WFN.
$result = new WellFormedName();
for ($i = 0; $i != 8; $i++) {
// get the i'th component of uri
$v = $this->getCompURI($uri, $i);
switch ($i) {
case 1:
$result->set("part", $this->decode($v));
break;
case 2:
$result->set("vendor", $this->decode($v));
break;
case 3:
$result->set("product", $this->decode($v));
break;
case 4:
$result->set("version", $this->decode($v));
break;
case 5:
$result->set("update", $this->decode($v));
break;
case 6:
// Special handling for edition component.
// Unpack edition if needed.
if ($v == "" || $v == "-"
|| substr($v, 0, 1) != "~") {
// Just a logical value or a non-packed value.
// So unbind to legacy edition, leaving other
// extended attributes unspecified.
$result->set("edition", $this->decode($v));
} else {
// We have five values packed together here.
$this->unpack($v, $result);
}
break;
case 7:
$result->set("language", $this->decode($v));
break;
}
}
return $result;
}
/**
* Top level function to unbind a formatted string to WFN.
* @param $fs Formatted string to unbind
* @return WellFormedName
* @throws Exception representing parsing error
*/
public function unbindFS($fs) {
// Validate the formatted string
Utilities::validateFS($fs);
// Initialize empty WFN
$result = new WellFormedName();
// The cpe scheme is the 0th component, the cpe version is the 1st.
// So we start parsing at the 2nd component.
for ($a = 2; $a != 13; $a++) {
// Get the a'th string field.
$v = $this->getCompFS($fs, $a);
// Unbind the string.
$v = $this->unbindValueFS($v);
// Set the value of the corresponding attribute.
switch ($a) {
case 2:
$result->set("part", $v);
break;
case 3:
$result->set("vendor", $v);
break;
case 4:
$result->set("product", $v);
break;
case 5:
$result->set("version", $v);
break;
case 6:
$result->set("update", $v);
break;
case 7:
$result->set("edition", $v);
break;
case 8:
$result->set("language", $v);
break;
case 9:
$result->set("sw_edition", $v);
break;
case 10:
$result->set("target_sw", $v);
break;
case 11:
$result->set("target_hw", $v);
break;
case 12:
$result->set("other", $v);
break;
}
}
return $result;
}
/**
* Returns the i'th field of the formatted string. The colon is the field
* delimiter unless prefixed by a backslash.
* @param $fs formatted string to retrieve from
* @param $i index of field to retrieve from fs.
* @return value of index of formatted string
*/
private function getCompFS($fs, $i) {
if ($i == 0) {
// return the substring from index 0 to the first occurence of an
// unescaped colon
$colon_idx = Utilities::getUnescapedColonIndex($fs);
// If no colon is found, we are at the end of the formatted string,
// so just return what's left.
if ($colon_idx == 0) {
return $fs;
}
return substr($fs, 0, $colon_idx);
} else {
$substrStart = Utilities::getUnescapedColonIndex($fs) + 1;
$substrLength = strlen($fs) - $substrStart;
return $this->getCompFS(substr($fs, $substrStart, $substrLength), $i - 1);
}
}
/**
* Takes a string value and returns the appropriate logical value if string
* is the bound form of a logical value. If string is some general value
* string, add quoting of non-alphanumerics as needed.
* @param $s value to be unbound
* @return logical value or quoted string
* @throws Exception representing parsing errors
*/
private function unbindValueFS($s) {
if ($s == "*") {
return new LogicalValue("ANY");
}
if ($s == "-") {
return new LogicalValue("NA");
}
return $this->addQuoting($s);
}
/**
* Inspect each character in a string, copying quoted characters, with
* their escaping, into the result. Look for unquoted non alphanumerics
* and if not "*" or "?", add escaping.
* @param $s
* @return
* @throws Exception representing parsing errors.
*/
private function addQuoting($s) {
$result = "";
$idx = 0;
$embedded = false;
while ($idx < strlen($s)) {
$c = substr($s, $idx, 1);
if (Utilities::isAlphanum($c) || $c == "_") {
// Alphanumeric characters pass untouched.
$result = $result . $c;
$idx = $idx + 1;
$embedded = true;
continue;
}
if ($c == "\\") {
// Anything quoted in the bound string stays quoted in the
// unbound string.
$result = $result . substr($s, $idx, 2);
$idx = $idx + 2;
$embedded = true;
continue;
}
if ($c == "*") {
// An unquoted asterisk must appear at the beginning or the end
// of the string.
if ($idx == 0 || $idx == strlen($s) - 1) {
$result = $result . $c;
$idx = $idx + 1;
$embedded = true;
continue;
} else {
throw new Exception("Error! cannot have unquoted * embedded in formatted string.", 0);
}
}
if ($c == "?") {
// An unquoted question mark must appear at the beginning or
// end of the string, or in a leading or trailing sequence.
if ( // ? legal at beginning or end
(($idx == 0) || ($idx == (strlen($s) - 1)))
// embedded is false, so must be preceded by ?
|| (!$embedded && (substr($s, $idx - 1, 1) == "?"))
// embedded is true, so must be followed by ?
|| ($embedded && (substr($s, $idx + 1, 1) == "?"))) {
$result = $result . $c;
$idx = $idx + 1;
$embedded = false;
continue;
} else {
throw new Exception("Error! cannot have unquoted ? embedded in formatted string.", 0);
}
}
// All other characters must be quoted.
$result = $result . "\\" . $c;
$idx = $idx + 1;
$embedded = true;
}
return $result;
}
/**
* Return the i'th component of the URI.
* @param $uri string representation of URI to retrieve components from.
* @param $i Index of component to return.
* @return If i = 0, returns the URI scheme. Otherwise, returns the i'th
* component of uri.
*/
private function getCompURI($uri, $i) {
if ($i == 0) {
return substr($uri, $i, strpos($uri, "/"));
}
$sa = explode(":", $uri);
// If requested component exceeds the number
// of components in URI, return blank
if ($i >= sizeof($sa)) {
return "";
}
if ($i === 1) {
return substr($sa[$i], 1, strlen($sa[$i])-1);
}
return $sa[$i];
}
/**
* Scans a string and returns a copy with all percent-encoded characters
* decoded. This function is the inverse of pctEncode() defined in the
* CPENameBinder class. Only legal percent-encoded forms are decoded.
* Others raise a ParseException.
* @param $s string to be decoded
* @return decoded string
* @throws Exception representing parsing errors
* @see CPENameBinder#pctEncode
*/
private function decode($s) {
if ($s == "") {
return new LogicalValue("ANY");
}
if ($s == "-") {
return new LogicalValue("NA");
}
// Start the scanning loop.
// Normalize: convert all uppercase letters to lowercase first.
$s = strtolower($s);
$result = "";
$idx = 0;
$embedded = false;
while ($idx < strlen($s)) {
// Get the idx'th character of s.
$c = substr($s, $idx, 1);
// Deal with dot, hyphen, and tilde: decode with quoting.
if ($c == "." || $c == "-" || $c == "~") {
$result = $result . "\\" . $c;
$idx = $idx + 1;
// a non-%01 encountered.
$embedded = true;
continue;
}
if ($c != "%") {
$result = $result . $c;
$idx = $idx + 1;
// a non-%01 encountered.
$embedded = true;
continue;
}
// We get here if we have a substring starting w/ '%'.
$form = substr($s, $idx, 3);
if ($form == "%01") {
if (($idx == 0)
|| ($idx == strlen($s) - 3)
|| (!$embedded && substr($s, $idx - 3, 2) == "%01")
|| ($embedded && (strlen($s) >= $idx + 6))
&& (substr($s, $idx + 3, 3) == "%01")) {
$result = $result . "?";
$idx = $idx + 3;
continue;
} else {
throw new Exception("Error decoding string", 0);
}
} else if ($form == "%02") {
if (($idx == 0) || ($idx == (strlen($s) - 3))) {
$result = $result . "*";
} else {
throw new Exception("Error decoding string", 0);
}
} else if ($form == "%21") {
$result = $result . "\\!";
} else if ($form == "%22") {
$result = $result . "\\\"";
} else if ($form == "%23") {
$result = $result . "\\#";
} else if ($form == "%24") {
$result = $result . "\\$";
} else if ($form == "%25") {
$result = $result . "\\%";
} else if ($form == "%26") {
$result = $result . "\\&";
} else if ($form == "%27") {
$result = $result . "\\'";
} else if ($form == "%28") {
$result = $result . "\\(";
} else if ($form == "%29") {
$result = $result . "\\)";
} else if ($form == "%2a") {
$result = $result . "\\*";
} else if ($form == "%2b") {
$result = $result . "\\+";
} else if ($form == "%2c") {
$result = $result . "\\,";
} else if ($form == "%2f") {
$result = $result . "\\/";
} else if ($form == "%3a") {
$result = $result . "\\))";
} else if ($form == "%3b") {
$result = $result . "\\;";
} else if ($form == "%3c") {
$result = $result . "\\<";
} else if ($form == "%3d") {
$result = $result . "\\=";
} else if ($form == "%3e") {
$result = $result . "\\>";
} else if ($form == "%3f") {
$result = $result . "\\?";
} else if ($form == "%40") {
$result = $result . "\\@";
} else if ($form == "%5b") {
$result = $result . "\\[";
} else if ($form == "%5c") {
$result = $result . "\\\\";
} else if ($form == "%5d") {
$result = $result . "\\]";
} else if ($form == "%5e") {
$result = $result . "\\^";
} else if ($form == "%60") {
$result = $result . "\\`";
} else if ($form == "%7b") {
$result = $result . "\\{";
} else if ($form == "%7c") {
$result = $result . "\\|";
} else if ($form == "%7d") {
$result = $result . "\\}";
} else if ($form == "%7e") {
$result = $result . "\\~";
} else {
throw new Exception("Unknown form: " . $form, 0);
}
$idx = $idx + 3;
$embedded = true;
}
return $result;
}
/**
* Unpacks the elements in s and sets the attributes in the given
* WellFormedName accordingly.
* @param $s packed string
* @param $wfn WellFormedName
* @return The augmented WellFormedName.
*/
private function unpack($s, WellFormedName $wfn) {
// Parse out the five elements.
$start = 1;
$ed = ""; $sw_edition = ""; $t_sw = ""; $t_hw = ""; $oth = "";
$end = strpos($s, "~", $start);
if ($start == $end) {
$ed = "";
} else {
$ed = substr($s, $start, $end-$start);
}
$start = $end + 1;
$end = strpos($s, "~", $start);
if ($start == $end) {
$sw_edition = "";
} else {
$sw_edition = substr($s, $start, $end-$start);
}
$start = $end + 1;
$end = strpos($s, "~", $start);
if ($start == $end) {
$t_sw = "";
} else {
$t_sw = substr($s, $start, $end-$start);
}
$start = $end + 1;
$end = strpos($s, "~", $start);
if ($start == $end) {
$t_hw = "";
} else {
$t_hw = substr($s, $start, $end-$start);
}
$start = $end + 1;
if ($start >= strlen($s)) {
$oth = "";
} else {
$oth = substr($s, $start, strlen($s) - 1 - $start);
}
// Set each component in the WFN.
try {
$wfn->set("edition", $this->decode($ed));
$wfn->set("sw_edition", $this->decode($sw_edition));
$wfn->set("target_sw", $this->decode($t_sw));
$wfn->set("target_hw", $this->decode($t_hw));
$wfn->set("other", $this->decode($oth));
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
return $wfn;
}
/*
* Static method to demonstrate this class.
*/
public static function test() {
// A few examples.
echo "Testing CPENamingUnbind...<br>\n";
$cpenu = new CPENameUnbinder();
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer%01%01%01%01:?:beta");
echo $wfn . "<br>\n";
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer:8.%2a:sp%3f");
echo $wfn . "<br>\n";
$wfn = $cpenu->unbindURI("cpe:/a:microsoft:internet_explorer:8.%02:sp%01");
echo $wfn . "<br>\n";
$wfn = $cpenu->unbindURI("cpe:/a:hp:insight_diagnostics:7.4.0.1570::~~online~win2003~x64~");
echo $wfn . "<br>\n";
echo $cpenu->unbindFS("cpe:2.3:a:micr\\?osoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*") . "<br>\n";
}
}