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