1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.data.update;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.net.MalformedURLException;
23 import java.net.URL;
24 import javax.annotation.concurrent.ThreadSafe;
25
26 import org.owasp.dependencycheck.Engine;
27 import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
28 import org.owasp.dependencycheck.data.update.exception.UpdateException;
29 import org.owasp.dependencycheck.exception.WriteLockException;
30 import org.owasp.dependencycheck.utils.Downloader;
31 import org.owasp.dependencycheck.utils.ResourceNotFoundException;
32 import org.owasp.dependencycheck.utils.Settings;
33 import org.owasp.dependencycheck.utils.TooManyRequestsException;
34 import org.owasp.dependencycheck.utils.WriteLock;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38
39
40
41
42
43 @ThreadSafe
44 public class RetireJSDataSource implements CachedWebDataSource {
45
46
47
48
49 private static final Logger LOGGER = LoggerFactory.getLogger(RetireJSDataSource.class);
50
51
52
53 public static final String RETIREJS_UPDATED_ON = "RetireJSUpdatedOn";
54
55
56
57 private Settings settings;
58
59
60
61 private DatabaseProperties dbProperties = null;
62
63
64
65 public static final String DEFAULT_JS_URL = "https://raw.githubusercontent.com/Retirejs/retire.js/master/repository/jsrepository.json";
66
67
68
69
70 public RetireJSDataSource() {
71 }
72
73
74
75
76
77
78
79
80 @Override
81 public boolean update(Engine engine) throws UpdateException {
82 this.settings = engine.getSettings();
83 this.dbProperties = engine.getDatabase().getDatabaseProperties();
84 final String configuredUrl = settings.getString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, DEFAULT_JS_URL);
85 final boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true);
86 final boolean forceupdate = settings.getBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false);
87 final boolean enabled = settings.getBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true);
88 try {
89 final URL url = new URL(configuredUrl);
90 final File filepath = new File(url.getPath());
91 final File repoFile = new File(settings.getDataDirectory(), filepath.getName());
92 final boolean proceed = enabled && (forceupdate || (autoupdate && shouldUpdate(repoFile)));
93 if (proceed) {
94 LOGGER.debug("Begin RetireJS Update");
95 initializeRetireJsRepo(settings, url, repoFile);
96 dbProperties.save(DatabaseProperties.RETIRE_LAST_CHECKED, Long.toString(System.currentTimeMillis() / 1000));
97 }
98 } catch (MalformedURLException ex) {
99 throw new UpdateException(String.format("Invalid URL for RetireJS repository (%s)", configuredUrl), ex);
100 } catch (IOException ex) {
101 throw new UpdateException("Unable to get the data directory", ex);
102 }
103 return false;
104 }
105
106
107
108
109
110
111
112
113
114
115 protected boolean shouldUpdate(File repo) throws NumberFormatException {
116 boolean proceed = true;
117 if (repo != null && repo.isFile()) {
118 final int validForHours = settings.getInt(Settings.KEYS.ANALYZER_RETIREJS_REPO_VALID_FOR_HOURS, 0);
119 long lastUpdatedOn = dbProperties.getPropertyInSeconds(DatabaseProperties.RETIRE_LAST_CHECKED);
120 if (lastUpdatedOn <= 0) {
121
122 lastUpdatedOn = repo.lastModified();
123 }
124 final long now = System.currentTimeMillis();
125 LOGGER.debug("Last updated: {}", lastUpdatedOn);
126 LOGGER.debug("Now: {}", now);
127 final long msValid = validForHours * 60L * 60L * 1000L;
128 proceed = (now - lastUpdatedOn) > msValid;
129 if (!proceed) {
130 LOGGER.info("Skipping RetireJS update since last update was within {} hours.", validForHours);
131 }
132 }
133 return proceed;
134 }
135
136
137
138
139
140
141
142
143
144
145 @SuppressWarnings("try")
146 private void initializeRetireJsRepo(Settings settings, URL repoUrl, File repoFile) throws UpdateException {
147 try (WriteLock lock = new WriteLock(settings, true, repoFile.getName() + ".lock")) {
148 LOGGER.debug("RetireJS Repo URL: {}", repoUrl.toExternalForm());
149 final Downloader downloader = new Downloader(settings);
150 downloader.fetchFile(repoUrl, repoFile, Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_USER, Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_PASSWORD);
151 } catch (IOException | TooManyRequestsException | ResourceNotFoundException | WriteLockException ex) {
152 throw new UpdateException("Failed to initialize the RetireJS repo", ex);
153 }
154 }
155
156 @Override
157 @SuppressWarnings("try")
158 public boolean purge(Engine engine) {
159 this.settings = engine.getSettings();
160 boolean result = true;
161 try {
162 final File dataDir = engine.getSettings().getDataDirectory();
163 final URL repoUrl = new URL(engine.getSettings().getString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, DEFAULT_JS_URL));
164 final String filename = repoUrl.getFile().substring(repoUrl.getFile().lastIndexOf("/") + 1);
165 final File repo = new File(dataDir, filename);
166 if (repo.exists()) {
167 try (WriteLock lock = new WriteLock(settings, true, filename + ".lock")) {
168 if (repo.delete()) {
169 LOGGER.info("RetireJS repo removed successfully");
170 } else {
171 LOGGER.error("Unable to delete '{}'; please delete the file manually", repo.getAbsolutePath());
172 result = false;
173 }
174 }
175 }
176 } catch (WriteLockException | IOException ex) {
177 LOGGER.error("Unable to delete the RetireJS repo - invalid configuration");
178 result = false;
179 }
180 return result;
181 }
182 }