Coverage Report - org.owasp.dependencycheck.dependency.VulnerableSoftware
 
Classes in this File Line Coverage Branch Coverage Complexity
VulnerableSoftware
75%
63/84
72%
52/72
3.263
 
 1  
 /*
 2  
  * This file is part of dependency-check-core.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  *
 16  
  * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
 17  
  */
 18  
 package org.owasp.dependencycheck.dependency;
 19  
 
 20  
 import java.io.Serializable;
 21  
 import java.io.UnsupportedEncodingException;
 22  
 import java.net.URLDecoder;
 23  
 import java.util.logging.Level;
 24  
 import java.util.logging.Logger;
 25  
 import org.owasp.dependencycheck.data.cpe.IndexEntry;
 26  
 
 27  
 /**
 28  
  * A record containing information about vulnerable software. This is referenced from a vulnerability.
 29  
  *
 30  
  * @author Jeremy Long <jeremy.long@owasp.org>
 31  
  */
 32  10004
 public class VulnerableSoftware extends IndexEntry implements Serializable, Comparable<VulnerableSoftware> {
 33  
 
 34  
     /**
 35  
      * The logger.
 36  
      */
 37  1
     private static final Logger LOGGER = Logger.getLogger(VulnerableSoftware.class.getName());
 38  
     /**
 39  
      * The serial version UID.
 40  
      */
 41  
     private static final long serialVersionUID = 307319490326651052L;
 42  
 
 43  
     /**
 44  
      * Parse a CPE entry from the cpe string representation.
 45  
      *
 46  
      * @param cpe a cpe entry (e.g. cpe:/a:vendor:software:version)
 47  
      */
 48  
     public void setCpe(String cpe) {
 49  
         try {
 50  1014
             parseName(cpe);
 51  0
         } catch (UnsupportedEncodingException ex) {
 52  0
             final String msg = String.format("Character encoding is unsupported for CPE '%s'.", cpe);
 53  0
             LOGGER.log(Level.WARNING, msg);
 54  0
             LOGGER.log(Level.FINE, null, ex);
 55  0
             setName(cpe);
 56  1014
         }
 57  1014
     }
 58  
 
 59  
     /**
 60  
      * <p>
 61  
      * Parses a name attribute value, from the cpe.xml, into its corresponding parts: vendor, product, version,
 62  
      * revision.</p>
 63  
      * <p>
 64  
      * Example:</p>
 65  
      * <code>&nbsp;&nbsp;&nbsp;cpe:/a:apache:struts:1.1:rc2</code>
 66  
      *
 67  
      * <p>
 68  
      * Results in:</p> <ul> <li>Vendor: apache</li> <li>Product: struts</li>
 69  
      * <li>Version: 1.1</li> <li>Revision: rc2</li> </ul>
 70  
      *
 71  
      * @param cpeName the cpe name
 72  
      * @throws UnsupportedEncodingException should never be thrown...
 73  
      */
 74  
     @Override
 75  
     public void parseName(String cpeName) throws UnsupportedEncodingException {
 76  1051
         this.name = cpeName;
 77  1051
         if (cpeName != null && cpeName.length() > 7) {
 78  1051
             final String[] data = cpeName.substring(7).split(":");
 79  1051
             if (data.length >= 1) {
 80  1051
                 this.setVendor(urlDecode(data[0]));
 81  
             }
 82  1051
             if (data.length >= 2) {
 83  1051
                 this.setProduct(urlDecode(data[1]));
 84  
             }
 85  1051
             if (data.length >= 3) {
 86  1051
                 version = urlDecode(data[2]);
 87  
             }
 88  1051
             if (data.length >= 4) {
 89  151
                 revision = urlDecode(data[3]);
 90  
             }
 91  1051
             if (data.length >= 5) {
 92  0
                 edition = urlDecode(data[4]);
 93  
             }
 94  
         }
 95  1051
     }
 96  
     /**
 97  
      * If present, indicates that previous version are vulnerable.
 98  
      */
 99  
     private String previousVersion;
 100  
 
 101  
     /**
 102  
      * Indicates if previous versions of this software are vulnerable.
 103  
      *
 104  
      * @return if previous versions of this software are vulnerable
 105  
      */
 106  
     public boolean hasPreviousVersion() {
 107  0
         return previousVersion != null;
 108  
     }
 109  
 
 110  
     /**
 111  
      * Get the value of previousVersion.
 112  
      *
 113  
      * @return the value of previousVersion
 114  
      */
 115  
     public String getPreviousVersion() {
 116  
         return previousVersion;
 117  
     }
 118  
 
 119  
     /**
 120  
      * Set the value of previousVersion.
 121  
      *
 122  
      * @param previousVersion new value of previousVersion
 123  
      */
 124  
     public void setPreviousVersion(String previousVersion) {
 125  
         this.previousVersion = previousVersion;
 126  
     }
 127  
 
 128  
     /**
 129  
      * Standard equals implementation to compare this VulnerableSoftware to another object.
 130  
      *
 131  
      * @param obj the object to compare
 132  
      * @return whether or not the objects are equal
 133  
      */
 134  
     @Override
 135  
     public boolean equals(Object obj) {
 136  1
         if (obj == null) {
 137  0
             return false;
 138  
         }
 139  1
         if (getClass() != obj.getClass()) {
 140  0
             return false;
 141  
         }
 142  1
         final VulnerableSoftware other = (VulnerableSoftware) obj;
 143  1
         if ((this.getName() == null) ? (other.getName() != null) : !this.getName().equals(other.getName())) {
 144  1
             return false;
 145  
         }
 146  0
         return true;
 147  
     }
 148  
 
 149  
     /**
 150  
      * Standard implementation of hashCode.
 151  
      *
 152  
      * @return the hashCode for the object
 153  
      */
 154  
     @Override
 155  
     public int hashCode() {
 156  163
         int hash = 7;
 157  163
         hash = 83 * hash + (this.getName() != null ? this.getName().hashCode() : 0);
 158  163
         return hash;
 159  
     }
 160  
 
 161  
     /**
 162  
      * Standard toString() implementation display the name and whether or not previous versions are also affected.
 163  
      *
 164  
      * @return a string representation of the object
 165  
      */
 166  
     @Override
 167  
     public String toString() {
 168  0
         return "VulnerableSoftware{ name=" + name + ", previousVersion=" + previousVersion + '}';
 169  
     }
 170  
 
 171  
     /**
 172  
      * Implementation of the comparable interface.
 173  
      *
 174  
      * @param vs the VulnerableSoftware to compare
 175  
      * @return an integer indicating the ordering of the two objects
 176  
      */
 177  
     @Override
 178  
     public int compareTo(VulnerableSoftware vs) {
 179  10006
         int result = 0;
 180  10006
         final String[] left = this.getName().split(":");
 181  10006
         final String[] right = vs.getName().split(":");
 182  10006
         final int max = (left.length <= right.length) ? left.length : right.length;
 183  10006
         if (max > 0) {
 184  60251
             for (int i = 0; result == 0 && i < max; i++) {
 185  50245
                 final String[] subLeft = left[i].split("\\.");
 186  50245
                 final String[] subRight = right[i].split("\\.");
 187  50245
                 final int subMax = (subLeft.length <= subRight.length) ? subLeft.length : subRight.length;
 188  50245
                 if (subMax > 0) {
 189  114648
                     for (int x = 0; result == 0 && x < subMax; x++) {
 190  64403
                         if (isPositiveInteger(subLeft[x]) && isPositiveInteger(subRight[x])) {
 191  
                             try {
 192  23452
                                 result = Long.valueOf(subLeft[x]).compareTo(Long.valueOf(subRight[x]));
 193  
 //                                final long iLeft = Long.parseLong(subLeft[x]);
 194  
 //                                final long iRight = Long.parseLong(subRight[x]);
 195  
 //                                if (iLeft != iRight) {
 196  
 //                                    if (iLeft > iRight) {
 197  
 //                                        result = 2;
 198  
 //                                    } else {
 199  
 //                                        result = -2;
 200  
 //                                    }
 201  
 //                                }
 202  0
                             } catch (NumberFormatException ex) {
 203  
                                 //ignore the exception - they obviously aren't numbers
 204  0
                                 if (!subLeft[x].equalsIgnoreCase(subRight[x])) {
 205  0
                                     result = subLeft[x].compareToIgnoreCase(subRight[x]);
 206  
                                 }
 207  23452
                             }
 208  
                         } else {
 209  40951
                             result = subLeft[x].compareToIgnoreCase(subRight[x]);
 210  
                         }
 211  
                     }
 212  50245
                     if (result == 0) {
 213  40464
                         if (subLeft.length > subRight.length) {
 214  114
                             result = 2;
 215  
                         }
 216  40464
                         if (subRight.length > subLeft.length) {
 217  7
                             result = -2;
 218  
                         }
 219  
                     }
 220  
                 } else {
 221  0
                     result = left[i].compareToIgnoreCase(right[i]);
 222  
                 }
 223  
             }
 224  10006
             if (result == 0) {
 225  104
                 if (left.length > right.length) {
 226  68
                     result = 2;
 227  
                 }
 228  104
                 if (right.length > left.length) {
 229  10
                     result = -2;
 230  
                 }
 231  
             }
 232  
         } else {
 233  0
             result = this.getName().compareToIgnoreCase(vs.getName());
 234  
         }
 235  10006
         return result;
 236  
     }
 237  
 
 238  
     /**
 239  
      * Determines if the string passed in is a positive integer.
 240  
      *
 241  
      * @param str the string to test
 242  
      * @return true if the string only contains 0-9, otherwise false.
 243  
      */
 244  
     private static boolean isPositiveInteger(final String str) {
 245  87927
         if (str == null || str.isEmpty()) {
 246  14
             return false;
 247  
         }
 248  142898
         for (int i = 0; i < str.length(); i++) {
 249  95922
             final char c = str.charAt(i);
 250  95922
             if (c < '0' || c > '9') {
 251  40937
                 return false;
 252  
             }
 253  
         }
 254  46976
         return true;
 255  
     }
 256  
     /**
 257  
      * The name of the cpe.
 258  
      */
 259  
     private String name;
 260  
 
 261  
     /**
 262  
      * Get the value of name.
 263  
      *
 264  
      * @return the value of name
 265  
      */
 266  
     public String getName() {
 267  
         return name;
 268  
     }
 269  
 
 270  
     /**
 271  
      * Set the value of name.
 272  
      *
 273  
      * @param name new value of name
 274  
      */
 275  
     public void setName(String name) {
 276  
         this.name = name;
 277  
     }
 278  
     /**
 279  
      * The product version number.
 280  
      */
 281  
     private String version;
 282  
 
 283  
     /**
 284  
      * Get the value of version.
 285  
      *
 286  
      * @return the value of version
 287  
      */
 288  
     public String getVersion() {
 289  
         return version;
 290  
     }
 291  
 
 292  
     /**
 293  
      * Set the value of version.
 294  
      *
 295  
      * @param version new value of version
 296  
      */
 297  
     public void setVersion(String version) {
 298  
         this.version = version;
 299  
     }
 300  
     /**
 301  
      * The product revision version.
 302  
      */
 303  
     private String revision;
 304  
 
 305  
     /**
 306  
      * Get the value of revision.
 307  
      *
 308  
      * @return the value of revision
 309  
      */
 310  
     public String getRevision() {
 311  
         return revision;
 312  
     }
 313  
 
 314  
     /**
 315  
      * Set the value of revision.
 316  
      *
 317  
      * @param revision new value of revision
 318  
      */
 319  
     public void setRevision(String revision) {
 320  
         this.revision = revision;
 321  
     }
 322  
     /**
 323  
      * The product edition.
 324  
      */
 325  
     private String edition;
 326  
 
 327  
     /**
 328  
      * Get the value of edition.
 329  
      *
 330  
      * @return the value of edition
 331  
      */
 332  
     public String getEdition() {
 333  
         return edition;
 334  
     }
 335  
 
 336  
     /**
 337  
      * Set the value of edition.
 338  
      *
 339  
      * @param edition new value of edition
 340  
      */
 341  
     public void setEdition(String edition) {
 342  
         this.edition = edition;
 343  
     }
 344  
 
 345  
     /**
 346  
      * Replaces '+' with '%2B' and then URL Decodes the string attempting first UTF-8, then ASCII, then default.
 347  
      *
 348  
      * @param string the string to URL Decode
 349  
      * @return the URL Decoded string
 350  
      */
 351  
     private String urlDecode(String string) {
 352  3304
         final String text = string.replace("+", "%2B");
 353  
         String result;
 354  
         try {
 355  3304
             result = URLDecoder.decode(text, "UTF-8");
 356  0
         } catch (UnsupportedEncodingException ex) {
 357  
             try {
 358  0
                 result = URLDecoder.decode(text, "ASCII");
 359  0
             } catch (UnsupportedEncodingException ex1) {
 360  0
                 result = URLDecoder.decode(text);
 361  0
             }
 362  3304
         }
 363  3304
         return result;
 364  
     }
 365  
 }