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.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.util.List;
28 import java.util.zip.GZIPInputStream;
29 import javax.xml.parsers.ParserConfigurationException;
30 import javax.xml.parsers.SAXParser;
31 import javax.xml.parsers.SAXParserFactory;
32 import org.apache.commons.io.FileUtils;
33 import static org.owasp.dependencycheck.data.nvdcve.DatabaseProperties.LAST_CPE_UPDATE;
34 import org.owasp.dependencycheck.data.update.cpe.CPEHandler;
35 import org.owasp.dependencycheck.data.update.cpe.Cpe;
36 import org.owasp.dependencycheck.data.update.exception.UpdateException;
37 import org.owasp.dependencycheck.utils.DateUtil;
38 import org.owasp.dependencycheck.utils.DownloadFailedException;
39 import org.owasp.dependencycheck.utils.Downloader;
40 import org.owasp.dependencycheck.utils.Settings;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.xml.sax.SAXException;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
59
60
61
62
63 private static final Logger LOGGER = LoggerFactory.getLogger(CpeUpdater.class);
64
65 @Override
66 public void update() throws UpdateException {
67 try {
68 openDataStores();
69 if (updateNeeded()) {
70 LOGGER.info("Updating the Common Platform Enumeration (CPE)");
71 final File xml = downloadCpe();
72 final List<Cpe> cpes = processXML(xml);
73 getCveDB().deleteUnusedCpe();
74 for (Cpe cpe : cpes) {
75 getCveDB().addCpe(cpe.getValue(), cpe.getVendor(), cpe.getProduct());
76 }
77 final long now = System.currentTimeMillis();
78 getProperties().save(LAST_CPE_UPDATE, Long.toString(now));
79 LOGGER.info("CPE update complete");
80 }
81 } finally {
82 closeDataStores();
83 }
84 }
85
86
87
88
89
90
91
92
93 private File downloadCpe() throws UpdateException {
94 File xml;
95 final URL url;
96 try {
97 url = new URL(Settings.getString(Settings.KEYS.CPE_URL));
98 xml = File.createTempFile("cpe", ".xml", Settings.getTempDirectory());
99 Downloader.fetchFile(url, xml);
100 if (url.toExternalForm().endsWith(".xml.gz")) {
101 extractGzip(xml);
102 }
103
104 } catch (MalformedURLException ex) {
105 throw new UpdateException("Invalid CPE URL", ex);
106 } catch (DownloadFailedException ex) {
107 throw new UpdateException("Unable to download CPE XML file", ex);
108 } catch (IOException ex) {
109 throw new UpdateException("Unable to create temporary file to download CPE", ex);
110 }
111 return xml;
112 }
113
114
115
116
117
118
119
120
121
122 private List<Cpe> processXML(final File xml) throws UpdateException {
123 try {
124 final SAXParserFactory factory = SAXParserFactory.newInstance();
125 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
126 final SAXParser saxParser = factory.newSAXParser();
127 final CPEHandler handler = new CPEHandler();
128 saxParser.parse(xml, handler);
129 return handler.getData();
130 } catch (ParserConfigurationException ex) {
131 throw new UpdateException("Unable to parse CPE XML file due to SAX Parser Issue", ex);
132 } catch (SAXException ex) {
133 throw new UpdateException("Unable to parse CPE XML file due to SAX Parser Exception", ex);
134 } catch (IOException ex) {
135 throw new UpdateException("Unable to parse CPE XML file due to IO Failure", ex);
136 }
137 }
138
139
140
141
142
143
144
145 private boolean updateNeeded() {
146 final long now = System.currentTimeMillis();
147 final int days = Settings.getInt(Settings.KEYS.CPE_MODIFIED_VALID_FOR_DAYS, 30);
148 long timestamp = 0;
149 final String ts = getProperties().getProperty(LAST_CPE_UPDATE);
150 if (ts != null && ts.matches("^[0-9]+$")) {
151 timestamp = Long.parseLong(ts);
152 }
153 return !DateUtil.withinDateRange(timestamp, now, days);
154 }
155
156
157
158
159
160
161
162
163
164 private void extractGzip(File file) throws FileNotFoundException, IOException {
165
166 final String originalPath = file.getPath();
167 final File gzip = new File(originalPath + ".gz");
168 if (gzip.isFile() && !gzip.delete()) {
169 LOGGER.debug("Failed to delete intial temporary file {}", gzip.toString());
170 gzip.deleteOnExit();
171 }
172 if (!file.renameTo(gzip)) {
173 throw new IOException("Unable to rename '" + file.getPath() + "'");
174 }
175 final File newfile = new File(originalPath);
176
177 final byte[] buffer = new byte[4096];
178
179 GZIPInputStream cin = null;
180 FileOutputStream out = null;
181 try {
182 cin = new GZIPInputStream(new FileInputStream(gzip));
183 out = new FileOutputStream(newfile);
184
185 int len;
186 while ((len = cin.read(buffer)) > 0) {
187 out.write(buffer, 0, len);
188 }
189 } finally {
190 if (cin != null) {
191 try {
192 cin.close();
193 } catch (IOException ex) {
194 LOGGER.trace("ignore", ex);
195 }
196 }
197 if (out != null) {
198 try {
199 out.close();
200 } catch (IOException ex) {
201 LOGGER.trace("ignore", ex);
202 }
203 }
204 if (gzip.isFile() && !FileUtils.deleteQuietly(gzip)) {
205 LOGGER.debug("Failed to delete temporary file {}", gzip.toString());
206 gzip.deleteOnExit();
207 }
208 }
209 }
210 }