Updates to 3rd party libraries
Add Dockerfile and specific docker-php.ini
This commit is contained in:
5
inc/vendor/pacificsec/cpe/.gitignore
vendored
Normal file
5
inc/vendor/pacificsec/cpe/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/.settings/
|
||||
/.buildpath
|
||||
/.project
|
||||
/vendor/
|
||||
/composer.lock
|
42
inc/vendor/pacificsec/cpe/README.md
vendored
42
inc/vendor/pacificsec/cpe/README.md
vendored
@ -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
26
inc/vendor/pacificsec/cpe/composer.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
44
inc/vendor/pacificsec/cpe/src/Common/LogicalValue.php
vendored
Normal file
44
inc/vendor/pacificsec/cpe/src/Common/LogicalValue.php
vendored
Normal 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";
|
||||
}
|
||||
}
|
166
inc/vendor/pacificsec/cpe/src/Common/Utilities.php
vendored
Normal file
166
inc/vendor/pacificsec/cpe/src/Common/Utilities.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
210
inc/vendor/pacificsec/cpe/src/Common/WellFormedName.php
vendored
Normal file
210
inc/vendor/pacificsec/cpe/src/Common/WellFormedName.php
vendored
Normal 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;
|
||||
}
|
||||
|
||||
}
|
287
inc/vendor/pacificsec/cpe/src/Matching/CPENameMatcher.php
vendored
Normal file
287
inc/vendor/pacificsec/cpe/src/Matching/CPENameMatcher.php
vendored
Normal 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
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
466
inc/vendor/pacificsec/cpe/src/Naming/CPENameUnbinder.php
vendored
Normal file
466
inc/vendor/pacificsec/cpe/src/Naming/CPENameUnbinder.php
vendored
Normal 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";
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user