View Javadoc
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) 2020 OWASP Foundation. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.data.nvdcve;
19  
20  import io.github.jeremylong.openvulnerability.client.nvd.Config;
21  
22  import java.util.Objects;
23  import java.util.stream.Collectors;
24  import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem;
25  
26  import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem;
27  import io.github.jeremylong.openvulnerability.client.nvd.LangString;
28  import io.github.jeremylong.openvulnerability.client.nvd.Node;
29  import java.util.List;
30  import org.owasp.dependencycheck.dependency.VulnerableSoftware;
31  
32  /**
33   *
34   * Utility for processing {@linkplain DefCveItem} in order to extract key values
35   * like textual description and ecosystem type.
36   *
37   * @author skjolber
38   */
39  public class CveItemOperator {
40  
41      /**
42       * The filter for 2.3 CPEs in the CVEs - we don't import unless we get a
43       * match.
44       */
45      private final String cpeStartsWithFilter;
46  
47      /**
48       * Constructs a new CVE Item Operator utility.
49       *
50       * @param cpeStartsWithFilter the filter to use for CPE entries
51       */
52      public CveItemOperator(String cpeStartsWithFilter) {
53          this.cpeStartsWithFilter = cpeStartsWithFilter;
54      }
55  
56      /**
57       * Extracts the english description from the CVE object.
58       *
59       * @param cve the CVE data
60       * @return the English descriptions from the CVE object
61       */
62      public String extractDescription(DefCveItem cve) {
63          return cve.getCve().getDescriptions().stream().filter((desc)
64                  -> "en".equals(desc.getLang())).map(LangString::getValue).collect(Collectors.joining(" "));
65      }
66  
67      //CSOFF: MissingSwitchDefault
68      /**
69       * Attempts to determine the ecosystem based on the vendor, product and
70       * targetSw.
71       *
72       * @param baseEcosystem the base ecosystem
73       * @param vendor the vendor
74       * @param product the product
75       * @param targetSw the target software
76       * @return the ecosystem if one is identified
77       */
78      private String extractEcosystem(String baseEcosystem, String vendor, String product, String targetSw) {
79          //TODO the following was added to reduce the need for the slow UPDATE_ECOSYSTEM2 query
80          // the following should be analyzed to determine if an ecosystem should be returned.
81          // Note that these all have 'bindings' in the description of a vulnerability in more than
82          // one case these were related to language bindings; as such the list need to be reviewed and refined.
83          if (("mysql".equals(vendor) && "mysql".equals(product))
84                  || ("postgresql".equals(vendor) && "postgresql".equals(product))
85                  || ("picketlink".equals(vendor) && "picketlink".equals(product))
86                  || ("libxl_project".equals(vendor) && "libxl".equals(product))
87                  || ("ocaml".equals(vendor) && "postgresql-ocaml".equals(product))
88                  || ("curses_project".equals(vendor) && "curses".equals(product))
89                  || ("dalekjs".equals(vendor) && "dalekjs".equals(product))
90                  || ("microsoft".equals(vendor) && "internet_explorer".equals(product))
91                  || ("jenkins".equals(vendor) && "ssh_credentials".equals(product))
92                  || ("kubernetes".equals(vendor) && "kubernetes".equals(product))
93                  || ("gnome".equals(vendor) && "nautilus-python".equals(product))
94                  || ("apache".equals(vendor) && "qpid_proton".equals(product))
95                  || ("mysql-ocaml".equals(vendor) && "mysql-ocaml".equals(product))
96                  || ("google".equals(vendor) && "chrome".equals(product))
97                  || ("canonical".equals(vendor) && "ltsp_display_manager".equals(product))
98                  || ("gnome".equals(vendor) && "vala".equals(product))
99                  || ("apple".equals(vendor) && "safari".equals(product))
100                 || ("mapbox".equals(vendor) && "npm-test-sqlite3-trunk".equals(product))
101                 || ("apple".equals(vendor) && "webkit".equals(product))
102                 || ("mozilla".equals(vendor) && "firefox".equals(product))
103                 || ("apache".equals(vendor) && "thrift".equals(product))
104                 || ("apache".equals(vendor) && "qpid".equals(product))
105                 || ("mozilla".equals(vendor) && "thunderbird".equals(product))
106                 || ("mozilla".equals(vendor) && "firefox_esr".equals(product))
107                 || ("redhat".equals(vendor) && "jboss_amq_clients_2".equals(product))
108                 || ("node-opencv_project".equals(vendor) && "node-opencv".equals(product))
109                 || ("mozilla".equals(vendor) && "seamonkey".equals(product))
110                 || ("mozilla".equals(vendor) && "thunderbird_esr".equals(product))
111                 || ("mnet_soft_factory".equals(vendor) && "nodemanager_professional".equals(product))
112                 || ("mozilla".equals(vendor) && "mozilla_suite".equals(product))
113                 || ("theforeman".equals(vendor) && "hammer_cli".equals(product))
114                 || ("ibm".equals(vendor) && "websphere_application_server".equals(product))
115                 || ("sap".equals(vendor) && "hana_extend_application_services".equals(product))
116                 || ("apache".equals(vendor) && "zookeeper".equals(product))) {
117             return null;
118         }
119 
120         if ("ibm".equals(vendor)
121                 && "java".equals(product)) {
122             return Ecosystem.NATIVE;
123         }
124 
125         if ("oracle".equals(vendor)
126                 && "vm".equals(product)) {
127             return Ecosystem.NATIVE;
128         }
129         switch (targetSw) {
130             case "asp.net"://.net
131             case "c#"://.net
132             case ".net"://.net
133             case "dotnetnuke"://.net
134                 return Ecosystem.DOTNET;
135             case "android"://android
136             case "java"://java
137                 return Ecosystem.JAVA;
138             case "c/c++"://c++
139             case "borland_c++"://c++
140             case "visual_c++"://c++
141             case "gnu_c++"://c++
142             case "linux_kernel"://native
143             case "linux"://native
144             case "unix"://native
145             case "suse_linux"://native
146             case "redhat_enterprise_linux"://native
147             case "debian"://native
148                 return Ecosystem.NATIVE;
149             case "coldfusion"://coldfusion
150                 return Ecosystem.COLDFUSION;
151             case "ios"://ios
152             case "iphone"://ios
153             case "ipad"://ios
154             case "iphone_os"://ios
155                 return Ecosystem.IOS;
156             case "jquery"://javascript
157                 return Ecosystem.JAVASCRIPT;
158             case "node.js"://node.js
159             case "nodejs"://node.js
160                 return Ecosystem.NODEJS;
161             case "perl"://perl
162                 return Ecosystem.PERL;
163             case "joomla!"://php
164             case "joomla"://php
165             case "mybb"://php
166             case "simplesamlphp"://php
167             case "craft_cms"://php
168             case "moodle"://php
169             case "phpcms"://php
170             case "buddypress"://php
171             case "typo3"://php
172             case "php"://php
173             case "wordpress"://php
174             case "drupal"://php
175             case "mediawiki"://php
176             case "symfony"://php
177             case "openpne"://php
178             case "vbulletin3"://php
179             case "vbulletin4"://php
180                 return Ecosystem.PHP;
181             case "python"://python
182                 return Ecosystem.PYTHON;
183             case "ruby"://ruby
184                 return Ecosystem.RUBY;
185         }
186         return baseEcosystem;
187     }
188     //CSON: MissingSwitchDefault
189 
190     /**
191      * Attempts to determine the ecosystem based on the vendor, product and
192      * targetSw.
193      *
194      * @param baseEcosystem the base ecosystem
195      * @param parsedCpe the CPE identifier
196      * @return the ecosystem if one is identified
197      */
198     public String extractEcosystem(String baseEcosystem, VulnerableSoftware parsedCpe) {
199         return extractEcosystem(baseEcosystem, parsedCpe.getVendor(), parsedCpe.getProduct(), parsedCpe.getTargetSw());
200     }
201 
202     /**
203      * Determines if the CVE entry is rejected.
204      *
205      * @param description the CVE description
206      * @return <code>true</code> if the CVE was rejected; otherwise
207      * <code>false</code>
208      */
209     public boolean isRejected(String description) {
210         return description.startsWith("** REJECT **") || description.startsWith("DO NOT USE THIS CANDIDATE NUMBER");
211     }
212 
213     /**
214      * Tests the CVE's CPE entries against the starts with filter. In general
215      * this limits the CVEs imported to just application level vulnerabilities.
216      *
217      * @param cve the CVE entry to examine
218      * @return <code>true</code> if the CVE affects CPEs identified by the
219      * configured CPE Starts with filter
220      */
221     boolean testCveCpeStartWithFilter(final DefCveItem cve) {
222         if (cve.getCve().getConfigurations() != null) {
223             //cycle through to see if this is a CPE we care about (use the CPE filters
224             return cve.getCve().getConfigurations().stream()
225                     .map(Config::getNodes)
226                     .flatMap(List::stream)
227                     .filter(Objects::nonNull)
228                     .map(Node::getCpeMatch)
229                     .filter(Objects::nonNull)
230                     .flatMap(List::stream)
231                     .filter(cpe -> cpe != null && cpe.getCriteria() != null)
232                     .anyMatch(cpe -> cpe.getCriteria().startsWith(cpeStartsWithFilter));
233         }
234         return false;
235     }
236 }