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