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) 2014 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.data.update;
19  
20  import java.io.IOException;
21  import java.net.HttpURLConnection;
22  import java.net.MalformedURLException;
23  import java.net.URL;
24  import org.apache.commons.io.IOUtils;
25  import org.owasp.dependencycheck.data.nvdcve.CveDB;
26  import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
27  import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
28  import org.owasp.dependencycheck.data.update.exception.UpdateException;
29  import org.owasp.dependencycheck.utils.DateUtil;
30  import org.owasp.dependencycheck.utils.DependencyVersion;
31  import org.owasp.dependencycheck.utils.InvalidSettingException;
32  import org.owasp.dependencycheck.utils.Settings;
33  import org.owasp.dependencycheck.utils.URLConnectionFactory;
34  import org.owasp.dependencycheck.utils.URLConnectionFailureException;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   *
40   * @author Jeremy Long
41   */
42  public class EngineVersionCheck implements CachedWebDataSource {
43  
44      /**
45       * Static logger.
46       */
47      private static final Logger LOGGER = LoggerFactory.getLogger(EngineVersionCheck.class);
48      /**
49       * The property key indicating when the last version check occurred.
50       */
51      public static final String ENGINE_VERSION_CHECKED_ON = "VersionCheckOn";
52      /**
53       * The property key indicating when the last version check occurred.
54       */
55      public static final String CURRENT_ENGINE_RELEASE = "CurrentEngineRelease";
56      /**
57       * Reference to the Cve Database.
58       */
59      private CveDB cveDB = null;
60  
61      /**
62       * The version retrieved from the database properties or web to check against.
63       */
64      private String updateToVersion;
65  
66      /**
67       * Getter for updateToVersion - only used for testing. Represents the version retrieved from the database.
68       *
69       * @return the version to test
70       */
71      protected String getUpdateToVersion() {
72          return updateToVersion;
73      }
74  
75      /**
76       * Setter for updateToVersion - only used for testing. Represents the version retrieved from the database.
77       *
78       * @param version the version to test
79       */
80      protected void setUpdateToVersion(String version) {
81          updateToVersion = version;
82      }
83  
84      @Override
85      public void update() throws UpdateException {
86  
87          try {
88              if (Settings.getBoolean(Settings.KEYS.AUTO_UPDATE)) {
89                  openDatabase();
90                  LOGGER.debug("Begin Engine Version Check");
91                  final DatabaseProperties properties = cveDB.getDatabaseProperties();
92                  final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0"));
93                  final long now = System.currentTimeMillis();
94                  updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, "");
95                  final String currentVersion = Settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0");
96                  LOGGER.debug("Last checked: {}", lastChecked);
97                  LOGGER.debug("Now: {}", now);
98                  LOGGER.debug("Current version: {}", currentVersion);
99                  final boolean updateNeeded = shouldUpdate(lastChecked, now, properties, currentVersion);
100                 if (updateNeeded) {
101                     LOGGER.warn("A new version of dependency-check is available. Consider updating to version {}.",
102                             updateToVersion);
103                 }
104             }
105         } catch (DatabaseException ex) {
106             LOGGER.debug("Database Exception opening databases to retrieve properties", ex);
107             throw new UpdateException("Error occured updating database properties.");
108         } catch (InvalidSettingException ex) {
109             LOGGER.debug("Unable to determine if autoupdate is enabled", ex);
110         } finally {
111             closeDatabase();
112 
113         }
114     }
115 
116     /**
117      * Determines if a new version of the dependency-check engine has been released.
118      *
119      * @param lastChecked the epoch time of the last version check
120      * @param now the current epoch time
121      * @param properties the database properties object
122      * @param currentVersion the current version of dependency-check
123      * @return <code>true</code> if a newer version of the database has been released; otherwise <code>false</code>
124      * @throws UpdateException thrown if there is an error connecting to the github documentation site or accessing the local
125      * database.
126      */
127     protected boolean shouldUpdate(final long lastChecked, final long now, final DatabaseProperties properties,
128             String currentVersion) throws UpdateException {
129         //check every 30 days if we know there is an update, otherwise check every 7 days
130         final int checkRange = 30;
131         if (!DateUtil.withinDateRange(lastChecked, now, checkRange)) {
132             LOGGER.debug("Checking web for new version.");
133             final String currentRelease = getCurrentReleaseVersion();
134             if (currentRelease != null) {
135                 final DependencyVersion v = new DependencyVersion(currentRelease);
136                 if (v.getVersionParts() != null && v.getVersionParts().size() >= 3) {
137                     updateToVersion = v.toString();
138                     if (!currentRelease.equals(updateToVersion)) {
139                         properties.save(CURRENT_ENGINE_RELEASE, updateToVersion);
140                     }
141                     properties.save(ENGINE_VERSION_CHECKED_ON, Long.toString(now));
142                 }
143             }
144             LOGGER.debug("Current Release: {}", updateToVersion);
145         }
146         if (updateToVersion == null) {
147             LOGGER.debug("Unable to obtain current release");
148             return false;
149         }
150         final DependencyVersion running = new DependencyVersion(currentVersion);
151         final DependencyVersion released = new DependencyVersion(updateToVersion);
152         if (running.compareTo(released) < 0) {
153             LOGGER.debug("Upgrade recommended");
154             return true;
155         }
156         LOGGER.debug("Upgrade not needed");
157         return false;
158     }
159 
160     /**
161      * Opens the CVE and CPE data stores.
162      *
163      * @throws DatabaseException thrown if a data store cannot be opened
164      */
165     protected final void openDatabase() throws DatabaseException {
166         if (cveDB != null) {
167             return;
168         }
169         cveDB = new CveDB();
170         cveDB.open();
171     }
172 
173     /**
174      * Closes the CVE and CPE data stores.
175      */
176     protected void closeDatabase() {
177         if (cveDB != null) {
178             try {
179                 cveDB.close();
180                 cveDB = null;
181             } catch (Throwable ignore) {
182                 LOGGER.trace("Error closing the cveDB", ignore);
183             }
184         }
185     }
186 
187     /**
188      * Retrieves the current released version number from the github documentation site.
189      *
190      * @return the current released version number
191      */
192     protected String getCurrentReleaseVersion() {
193         HttpURLConnection conn = null;
194         try {
195             final String str = Settings.getString(Settings.KEYS.ENGINE_VERSION_CHECK_URL, "http://jeremylong.github.io/DependencyCheck/current.txt");
196             final URL url = new URL(str);
197             conn = URLConnectionFactory.createHttpURLConnection(url);
198             conn.connect();
199             if (conn.getResponseCode() != 200) {
200                 return null;
201             }
202             final String releaseVersion = IOUtils.toString(conn.getInputStream(), "UTF-8");
203             if (releaseVersion != null) {
204                 return releaseVersion.trim();
205             }
206         } catch (MalformedURLException ex) {
207             LOGGER.debug("unable to retrieve current release version of dependency-check", ex);
208         } catch (URLConnectionFailureException ex) {
209             LOGGER.debug("unable to retrieve current release version of dependency-check", ex);
210         } catch (IOException ex) {
211             LOGGER.debug("unable to retrieve current release version of dependency-check", ex);
212         } finally {
213             if (conn != null) {
214                 conn.disconnect();
215             }
216         }
217         return null;
218     }
219 }