1 /*-
2  * Copyright (c) 2004 Jacques A. Vidrine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 #include <sys/param.h>
28 
29 #include <algorithm>
30 #include <iostream>
31 #include <fstream>
32 #include <map>
33 #include <string>
34 #include <utility>
35 #include <vector>
36 
37 #include <vuxml/vuxml.hh>
38 #include <vuxml/constants.hh>
39 #include <vuxml/processors.hh>
40 
41 
42 using namespace vuxml;
43 
44 
45 //
46 // AllMatcher
47 //
AllMatcher(EntryProcessor & proc)48 AllMatcher::AllMatcher(EntryProcessor &proc) : proc_(proc) {}
49 
50 
51 bool
operator ()(const Entry & entry)52 AllMatcher::operator()(const Entry &entry)
53 {
54 	return proc_(entry);
55 }
56 
57 
start()58 void AllMatcher::start() { proc_.start(); }
end()59 void AllMatcher::end() { proc_.end(); }
60 
61 
62 //
63 // VersionMatcher
64 //
VersionMatcher(std::vector<std::string> & args,EntryProcessor & proc)65 VersionMatcher::VersionMatcher(std::vector<std::string> &args, EntryProcessor &proc) :
66     args_(args), proc_(proc)
67 {
68 }
69 
70 
71 bool
operator ()(const Entry & entry)72 VersionMatcher::operator()(const Entry &entry)
73 {
74 	std::vector<std::string>::const_iterator arg, arg_end;
75 	std::vector<std::string>::const_iterator nam, nam_end;
76 	std::vector<AffectedSet>::const_iterator aff, aff_end;
77 	std::vector<VersionRange>::const_iterator rng, rng_end;
78 	std::string name, version;
79 	std::string::size_type i, npos = std::string::npos;
80 
81 	for (arg = args_.begin(), arg_end = args_.end();
82 	     arg != arg_end; ++arg) {
83 		i = arg->rfind('-');
84 		if (i == npos) {
85 			name = *arg;
86 			version = VersionRange::zero;
87 		} else {
88 			name = arg->substr(0, i);
89 			version = arg->substr(i+1, npos);
90 		}
91 		for (aff = entry.affected().begin(),
92 		    aff_end = entry.affected().end(); aff != aff_end; ++aff) {
93 			for (nam = aff->names.begin(),
94 			    nam_end = aff->names.end();
95 			    nam != nam_end; ++nam) {
96 				if (*nam == name)
97 					break;
98 			}
99 			if (nam == nam_end)
100 				continue;
101 			for (rng = aff->ranges.begin(),
102 			    rng_end = aff->ranges.end(); rng != rng_end; ++rng)
103 				if (rng->contains(version))
104 					return proc_(entry);
105 		}
106 	}
107 	return true;
108 }
109 
110 
start()111 void VersionMatcher::start() { proc_.start(); }
end()112 void VersionMatcher::end() { proc_.end(); }
113 
114 
115 //
116 // TextWriter
117 //
TextWriter(std::ostream & os)118 TextWriter::TextWriter(std::ostream &os) : os_(os)
119 {
120 }
121 
122 
123 namespace {
124 void
writeTopic(std::ostream & os,const Entry & entry)125 writeTopic(std::ostream &os, const Entry &entry)
126 {
127 	os << "Topic: " << entry.topic() << '\n';
128 }
129 
130 
131 void
writeRangeExpression(std::ostream & os,const std::string & nam,const VersionRange & rng)132 writeRangeExpression(std::ostream &os, const std::string &nam,
133     const VersionRange &rng)
134 {
135 	os << "    ";
136 	if (rng.lo == rng.hi)
137 		os << nam << " == " << rng.lo;
138 	else if (rng.lo != VersionRange::zero && rng.hi != VersionRange::infinity)
139 		os << rng.lo << " <" << (rng.lo_closed ? "=" : "") << ' '
140 		   << nam << " <" << (rng.hi_closed ? "=" : "") << ' '
141 		   << rng.hi;
142 	else if (rng.hi != VersionRange::infinity)
143 		os << nam << " <" << (rng.hi_closed ? "=" : "") << ' '
144 		   << rng.hi;
145 	else
146 		os << rng.lo << " <" << (rng.lo_closed ? "=" : "") << ' '
147 		   << nam;
148 	os << '\n';
149 }
150 
151 
152 void
writeAffected(std::ostream & os,const Entry & entry)153 writeAffected(std::ostream &os, const Entry &entry)
154 {
155 	std::vector<AffectedSet>::const_iterator aff, aff_end;
156 	std::vector<VersionRange>::const_iterator rng, rng_end;
157 	std::vector<std::string>::const_iterator nam, nam_end;
158 
159 	os << "Affects:\n";
160 	aff_end = entry.affected().end();
161 	for (aff = entry.affected().begin(); aff != aff_end; ++aff) {
162 		nam_end = aff->names.end();
163 		for (nam = aff->names.begin(); nam != nam_end; ++nam) {
164 			rng_end = aff->ranges.end();
165 			for (rng = aff->ranges.begin(); rng != rng_end;
166 			     ++rng)
167 				writeRangeExpression(os, *nam, *rng);
168 		}
169 	}
170 }
171 
172 
173 void
writeReferences(std::ostream & os,const Entry & entry)174 writeReferences(std::ostream &os, const Entry &entry)
175 {
176 	std::vector<Reference>::const_iterator ref, ref_end;
177 
178 	os << "References:\n";
179 	ref_end = entry.references().end();
180 	for (ref = entry.references().begin(); ref != ref_end; ++ref)
181 		os << "    " << ref->type << ':' << ref->text << '\n';
182 }
183 }
184 
185 
186 
187 bool
operator ()(const Entry & entry)188 TextWriter::operator()(const Entry &entry)
189 {
190 	writeTopic(os_, entry);
191 	writeAffected(os_, entry);
192 	writeReferences(os_, entry);
193 	os_ << "<URL:http://vuxml.freebsd.org/"
194 	    << entry.vid() << ".html>\n\n";
195 	return true;
196 }
197 
198 
199 //
200 // VuXMLWriter
201 //
VuXMLWriter(std::ostream & os)202 VuXMLWriter::VuXMLWriter(std::ostream &os) : os_(os) {}
203 
204 
205 void
start()206 VuXMLWriter::start()
207 {
208 	os_ << "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
209 	    << "<!DOCTYPE vuxml PUBLIC \"-//vuxml.org//DTD VuXML 1.0//EN\"\n"
210 	    << "  \"http://www.vuxml.org/dtd/vuxml-1/vuxml-10.dtd\">\n"
211 	    << "<vuxml xmlns=\"http://www.vuxml.org/apps/vuxml-1\">\n";
212 }
213 
214 
215 void
end()216 VuXMLWriter::end()
217 {
218 	os_ << "</vuxml>\n";
219 }
220 
221 
222 namespace vuxml { namespace vuxmlwriter {
223 
224 struct WriteAffected : public std::unary_function<void, const AffectedSet &> {
225   std::ostream &os_;
WriteAffectedvuxml::vuxmlwriter::WriteAffected226   WriteAffected(std::ostream &os) : os_(os) {}
operator ()vuxml::vuxmlwriter::WriteAffected227   void operator()(const AffectedSet &as) {
228 	  os_ << "      <package>\n";
229 
230 	  std::vector<std::string>::const_iterator nam, nam_end;
231 	  for (nam = as.names.begin(), nam_end = as.names.end();
232 	       nam != nam_end; ++nam)
233 		  os_ << "        <name>" << xmlescape(*nam) << "</name>\n";
234 
235 	  std::vector<VersionRange>::const_iterator rng, rng_end;
236 	  std::string hi, lo;
237 	  for (rng = as.ranges.begin(), rng_end = as.ranges.end();
238 	       rng != rng_end; ++rng) {
239 		  hi = xmlescape(rng->hi);
240 		  lo = xmlescape(rng->lo);
241 		  os_ << "        <range>";
242 		  if (rng->lo == rng->hi) {
243 			  os_ << "<eq>" << rng->lo << "</eq>";
244 			  continue;
245 		  }
246 		  if (rng->lo != VersionRange::zero)
247 			  os_ << (rng->lo_closed? "<ge>": "<gt>") << lo
248 			      << (rng->lo_closed?"</ge>":"</gt>");
249 		  if (rng->hi != VersionRange::infinity)
250 			  os_ << (rng->hi_closed? "<le>": "<lt>") << hi
251 			      << (rng->hi_closed?"</le>":"</lt>");
252 		  os_ << "</range>\n";
253 	  }
254 
255 	  os_ << "      </package>\n";
256   }
257 };
258 
259 
260 struct WriteReferences : public std::unary_function<void, const Reference &> {
261   std::ostream &os_;
WriteReferencesvuxml::vuxmlwriter::WriteReferences262   WriteReferences(std::ostream &os) : os_(os) {}
operator ()vuxml::vuxmlwriter::WriteReferences263   void operator()(const Reference &ref) {
264 	  os_ << "      <" << ref.type << ">"
265 	      << xmlescape(ref.text) << "</" << ref.type << ">\n";
266   }
267 };
268 }}
269 
270 
271 bool
operator ()(const Entry & entry)272 VuXMLWriter::operator()(const Entry &entry)
273 {
274 	using namespace vuxml::vuxmlwriter;
275 	os_ << "  <vuln vid=\"" << xmlescape(entry.vid()) << "\">\n"
276 	    << "    <topic>" << xmlescape(entry.topic()) << "</topic>\n"
277 	    << "    <affects>\n";
278 	std::for_each(entry.affected().begin(), entry.affected().end(),
279 	    WriteAffected(os_));
280 	os_ << "    </affects>\n"
281 	    << "    <description>\n"
282 	    << "      <body xmlns=\"" << XHTML_NAMESPACE << "\">\n"
283 	    << entry.description()
284 	    << "      </body>\n"
285 	    << "    </description>\n"
286 	    << "    <references>\n";
287 	std::for_each(entry.references().begin(), entry.references().end(),
288 	    WriteReferences(os_));
289 	os_ << "    </references>\n"
290 	    << "    <dates>\n"
291 	    << "      <discovery>" << xmlescape(entry.discoveryDate())
292 	    << "</discovery>\n"
293 	    << "      <entry>" << xmlescape(entry.entryDate())
294 	    << "</entry>\n";
295 	if (entry.modifiedDate().size() != 0)
296 		os_ << "      <modified>" << xmlescape(entry.modifiedDate())
297 		    << "</modified>\n";
298 	os_ << "    </dates>\n"
299 	    << "  </vuln>\n";
300 	return true;
301 }
302 
303 
304 //
305 // XHTMLWriter
306 //
XHTMLWriter(std::ostream & os,unsigned int h1)307 XHTMLWriter::XHTMLWriter(std::ostream &os, unsigned int h1) :
308     os_(os), h1_(h1) {}
309 
310 
311 void
start()312 XHTMLWriter::start()
313 {
314 	os_ << "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
315 	    << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML Basic 1.0//EN\"\n"
316 	    << "  \"http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd\">\n"
317 	    << "<html>\n"
318 	    << "  <head><title>vxquery report</title></head>\n"
319 	    << "  <body><h1>vxquery report</h1>\n";
320 }
321 
322 
323 void
end()324 XHTMLWriter::end()
325 {
326 	os_ << "</body></html>\n";
327 }
328 
329 
330 namespace vuxml { namespace xhtmlwriter {
331 struct WriteRangeExpression : public std::binary_function<void,
332     const std::string &, const VersionRange &> {
333   std::ostream &os_;
WriteRangeExpressionvuxml::xhtmlwriter::WriteRangeExpression334   WriteRangeExpression(std::ostream &os) : os_(os) {}
operator ()vuxml::xhtmlwriter::WriteRangeExpression335   void operator()(const std::string &nam, const VersionRange &rng) {
336 	std::string s;
337 	if (rng.lo == rng.hi) {
338 		s += nam; s += " == "; s += rng.lo;
339 	} else if (rng.hi != VersionRange::infinity &&
340 	    rng.lo == VersionRange::zero) {
341 		s += nam; s += " <";
342 		if (rng.hi_closed) s += "=";
343 		s += " "; s += rng.hi;
344 	} else {
345 		s += rng.lo; s += " <";
346 		if (rng.lo_closed) s += "=";
347 		s += " "; s += nam;
348 		if (rng.hi != VersionRange::infinity) {
349 			s += " <";
350 			if (rng.hi_closed) s += "=";
351 			s += " "; s += rng.hi;
352 		}
353 	}
354 	os_ << xmlescape(s) << "<br/>";
355   }
356 };
357 
358 
359 struct WriteAffected : public std::unary_function<void, const AffectedSet &> {
360   std::ostream &os_;
WriteAffectedvuxml::xhtmlwriter::WriteAffected361   WriteAffected(std::ostream &os) : os_(os) {}
operator ()vuxml::xhtmlwriter::WriteAffected362   void operator()(const AffectedSet &as) {
363 	std::vector<VersionRange>::const_iterator rng, rng_end;
364 	std::vector<std::string>::const_iterator nam, nam_end;
365 	WriteRangeExpression fn(os_);
366 
367 	os_ << "<p>";
368 	for (nam = as.names.begin(), nam_end = as.names.end() ; nam != nam_end;
369 	    ++nam)
370 	    	for (rng = as.ranges.begin(), rng_end = as.ranges.end();
371 		    rng != rng_end; ++rng)
372 			fn(*nam, *rng);
373 	os_ << "</p>\n";
374   }
375 };
376 
377 
378 struct WriteReferences : public std::unary_function<void, const Reference &> {
379   std::ostream &os_;
WriteReferencesvuxml::xhtmlwriter::WriteReferences380   WriteReferences(std::ostream &os) : os_(os) {}
operator ()vuxml::xhtmlwriter::WriteReferences381   void operator()(const Reference &ref) {
382 	  std::string link, tmp = xmlescape(ref.text);
383 	  if (ref.type == "url") {
384 		os_ << "<tr><td>URL</td><td><a href=\""
385 		    << tmp << "\">" << tmp << "</a></td></tr>\n";
386 	  } else if (ref.type == "cvename") {
387 		link = "http://cve.mitre.org/cgi-bin/cvename.cgi?name=";
388 		link += tmp;
389 		os_ << "<tr><td>CVE Name</td><td><a href=\""
390 		    << link << "\">" << tmp << "</a></td></tr>\n";
391 	  } else if (ref.type == "bid") {
392 		link = "http://www.securityfocus.com/bid/";
393 		link += tmp;
394 		os_ << "<tr><td>Bugtraq ID</td><td><a href=\""
395 		    << link << "\">" << tmp << "</a></td></tr>\n";
396 	  } else if (ref.type == "certsa") {
397 		link = "http://www.cert.org/advisories/";
398 		link += tmp;
399 		link += ".html";
400 		os_ << "<tr><td>CERT/CC Advisory</td><td><a href=\""
401 		    << link << "\">" << tmp << "</a></td></tr>\n";
402 	  } else if (ref.type == "certvu") {
403 		link = "http://www.kb.cert.org/vuls/id/";
404 		link += tmp;
405 		os_ << "<tr><td>CERT/CC Vulnerability Note</td><td><a href=\""
406 		    << link << "\">" << tmp << "</a></td></tr>\n";
407 	  } else if (ref.type == "freebsdsa") {
408 		link = "ftp://ftp.freebsd.org/pub/CERT/advisories/FreeBSD-";
409 		link += tmp;
410 		link += ".asc";
411 		os_ << "<tr><td>FreeBSD Advisory</td><td><a href=\""
412 		    << link << "\">" << tmp << "</a></td></tr>\n";
413 	  } else {
414 		os_ << "<tr><td>" << xmlescape(ref.type)
415 		    << "</td><td>" << xmlescape(ref.text) << "</td></tr>\n";
416 	  }
417   }
418 };
419 }}
420 
421 
422 bool
operator ()(const Entry & entry)423 XHTMLWriter::operator()(const Entry &entry)
424 {
425 	using namespace vuxml::xhtmlwriter;
426 	os_ << "<h" << h1_+1 << " id=\"_" << xmlescape(entry.vid()) << "\">"
427 	    << xmlescape(entry.topic()) << "</h" << h1_+1 << ">\n"
428 	    << "<h" << h1_+2 << ">Affects</h" << h1_+2 << ">\n";
429 	std::for_each(entry.affected().begin(), entry.affected().end(),
430 	    WriteAffected(os_));
431 	os_ << "<h" << h1_+2 << ">Description</h" << h1_+2 << ">\n"
432 	    << entry.description() << '\n'
433 	    << "<h" << h1_+2 << ">References</h" << h1_+2 << ">\n"
434 	    << "<table>\n";
435 	std::for_each(entry.references().begin(), entry.references().end(),
436 	    WriteReferences(os_));
437 
438 	os_ << "</table>\n";
439 	return true;
440 }
441 
442 
443 //
444 // XHTMLFilesWriter
445 //
446 
447 
XHTMLFilesWriter(const std::string & dir)448 XHTMLFilesWriter::XHTMLFilesWriter(const std::string &dir) : dir_(dir), fails_(0) {}
449 
450 
451 bool
operator ()(const Entry & entry)452 XHTMLFilesWriter::operator()(const Entry &entry)
453 {
454 	std::string path = dir_; path += "/"; path += entry.vid(); path += ".html";
455 
456 	std::ofstream ofs;
457 	ofs.open(path.c_str(), std::ofstream::binary |
458 	    std::ofstream::out | std::ofstream::trunc);
459 
460 	if (ofs.fail()) {
461 		++fails_;
462 		if (fails_ > 10)
463 			throw except("Too many failures.");
464 		LOG << "Warning: cannot open file `" << path << "'\n"
465 		    << "for output.\n";
466 		return false;
467 	}
468 
469 
470 	ofs << "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
471 	    << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML Basic 1.0//EN\"\n"
472 	    << "  \"http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd\">\n"
473 	    << "<html>\n"
474 	    << "  <head><title>" << xmlescape(entry.topic()) << "</title></head>\n"
475 	    << "  <body>\n";
476 	XHTMLWriter w(ofs, 0);
477 	w(entry);
478 	ofs << "</body></html>\n";
479 	ofs.close();
480 	return true;
481 }
482