/[secure-testing]/bin/tracker_service.py
ViewVC logotype

Contents of /bin/tracker_service.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 16962 - (show annotations) (download) (as text)
Wed Jul 20 22:29:50 2011 UTC (23 months ago) by nion
File MIME type: text/script
File size: 56451 byte(s)
fix narrative introduction link on tracker website
1 #!/usr/bin/python
2
3 import sys
4 sys.path.insert(0,'../lib/python')
5 import bugs
6 import re
7 import security_db
8 from web_support import *
9
10 if len(sys.argv) not in (3, 5):
11 print "usage: python tracker_service.py SOCKET-PATH DATABASE-PATH"
12 print " python tracker_service.py URL HOST PORT DATABASE-PATH"
13 sys.exit(1)
14 if len(sys.argv) == 3:
15 socket_name = sys.argv[1]
16 db_name = sys.argv[2]
17 webservice_base_class = WebService
18 else:
19 server_base_url = sys.argv[1]
20 server_address = sys.argv[2]
21 server_port = int(sys.argv[3])
22 socket_name = (server_base_url, server_address, server_port)
23 db_name = sys.argv[4]
24 webservice_base_class = WebServiceHTTP
25
26 class BugFilter:
27 default_action_list = [('show_high_urgency', 'only high urgencies'),
28 ('show_medium_urgency', 'only medium and high urgencies'),
29 ('show_undetermined_urgency', 'issues that may be vulnerable but need to be checked (shown in purple)'),
30 ('show_unimportant_urgency', 'unimportant issues'),
31 ('show_remote_only', 'only remote vulnerabilities')]
32 def __init__(self, params, action_list=None):
33 if action_list is None:
34 self.action_list = self.default_action_list
35 else:
36 self.action_list = action_list
37 self.params = {}
38 for (prop, desc) in self.action_list:
39 self.params[prop] = int(params.get(prop, (0,))[0])
40
41 def actions(self, url):
42 """Returns a HTML snippet which can be used to change the filter."""
43
44 l = []
45 for (prop, desc) in self.action_list:
46 if self.params[prop]:
47 if self.params['show_medium_urgency'] and prop == 'show_medium_urgency':
48 note = 'Restore lower than medium urgencies'
49 elif self.params['show_high_urgency'] and prop == 'show_high_urgency':
50 note = 'Restore lower than high urgencies'
51 elif self.params['show_remote_only'] and prop == 'show_remote_only':
52 note = 'Restore local vulnerabilities'
53 else:
54 note = 'Hide ' + desc
55 l.append(TR(TD(A(url.updateParamsDict({prop : None}), note))))
56 else:
57 note = 'Show ' + desc
58 l.append(TR(TD(A(url.updateParamsDict({prop : '1'}), note))))
59
60 return TABLE(l)
61
62 def urgencyFiltered(self, urg, vuln):
63 """Returns True for urgencies that should be filtered."""
64 filterlow = self.params['show_medium_urgency'] and \
65 urg in ('low', 'low**', 'unimportant',
66 'undetermined', 'not yet assigned')
67 filtermed = self.params['show_high_urgency'] and \
68 urg in ('medium', 'medium**', 'low', 'low**',
69 'unimportant', 'undetermined', 'not yet assigned')
70 filterund = not self.params['show_undetermined_urgency'] and vuln == 2
71 filteruni = not self.params['show_unimportant_urgency'] \
72 and urg == 'unimportant'
73 return filterlow or filtermed or filterund or filteruni
74
75 def remoteFiltered(self, remote):
76 """Returns True for only remote flaws if filtered."""
77 return self.params['show_remote_only'] and not remote and not remote is None
78
79 class BugFilterNoDSA(BugFilter):
80 def __init__(self, params):
81 BugFilter.__init__(self, params, self.default_action_list
82 + [('show_nodsa', 'issues that are not severe enough to warrant a DSA')])
83
84 def nodsaFiltered(self, nodsa):
85 """Returns True for no DSA issues if filtered."""
86 return nodsa and not self.params['show_nodsa']
87
88 class TrackerService(webservice_base_class):
89 head_contents = compose(STYLE(
90 """h1 { font-size : 144%; }
91 h2 { font-size : 120%; }
92 h3 { font-size : 100%; }
93
94 table { padding-left : 1.5em }
95 td, th { text-align : left;
96 padding-left : 0.25em;
97 padding-right : 0.25em; }
98 td { vertical-align: baseline }
99 span.red { color: red; }
100 span.purple { color: purple; }
101 span.dangerous { color: rgb(191,127,0); }
102 """), SCRIPT('''var old_query_value = "";
103
104 function selectSearch() {
105 document.searchForm.query.focus();
106 }
107
108 function onSearch(query) {
109 if (old_query_value == "") {
110 if (query.length > 5) {
111 old_query_value = query;
112 document.searchForm.submit();
113 } else {
114 old_query_value = query;
115 }
116 }
117 }
118 ''')).toHTML()
119
120 nvd_text = P('''If a "**" is included, the urgency field was automatically
121 assigned by the NVD (National Vulnerability Database). Note that this
122 rating is automatically derived from a set of known factors about the
123 issue (such as access complexity, confidentiality impact, exploitability,
124 remediation level, and others). Human intervention is involved in
125 determining the values of these factors, but the rating itself comes
126 from a fully automated formula.''')
127
128 def __init__(self, socket_name, db_name):
129 webservice_base_class.__init__(self, socket_name)
130 self.db = security_db.DB(db_name)
131 self.register('', self.page_home)
132 self.register('*', self.page_object)
133 self.register('redirect/*', self.page_redirect)
134 self.register('source-package/*', self.page_source_package)
135 self.register('status/release/oldstable',
136 self.page_status_release_oldstable)
137 self.register('status/release/stable', self.page_status_release_stable)
138 self.register('status/release/stable-backports',
139 self.page_status_release_stable_backports)
140 self.register('status/release/oldstable-backports',
141 self.page_status_release_oldstable_backports)
142 self.register('status/release/testing',
143 self.page_status_release_testing)
144 self.register('status/release/unstable',
145 self.page_status_release_unstable)
146 self.register('status/dtsa-candidates',
147 self.page_status_dtsa_candidates)
148 self.register('status/todo', self.page_status_todo)
149 self.register('status/undetermined', self.page_status_undetermined)
150 self.register('status/unimportant', self.page_status_unimportant)
151 self.register('status/itp', self.page_status_itp)
152 self.register('data/unknown-packages', self.page_data_unknown_packages)
153 self.register('data/missing-epochs', self.page_data_missing_epochs)
154 self.register('data/latently-vulnerable',
155 self.page_data_latently_vulnerable)
156 self.register('data/releases', self.page_data_releases)
157 self.register('data/funny-versions', self.page_data_funny_versions)
158 self.register('data/fake-names', self.page_data_fake_names)
159 self.register('data/pts/1', self.page_data_pts)
160 self.register('debsecan/**', self.page_debsecan)
161 self.register('data/report', self.page_report)
162
163 def page_home(self, path, params, url):
164 query = params.get('query', ('',))[0]
165 if query:
166 if '/' in query:
167 return self.page_not_found(url, query)
168 else:
169 return RedirectResult(url.scriptRelativeFull(query))
170
171 return self.create_page(
172 url, 'Security Bug Tracker',
173 [P(
174 """The data in this tracker comes solely from the bug database maintained
175 by Debian's security team located in the testing-security Subversion """,
176 A("http://svn.debian.org/wsvn/secure-testing/data/", "repository"),
177 """. The data represented here is derived from: """,
178 A("http://www.debian.org/security/#DSAS", "DSAs"),
179 """ issued by the Security Team; issues tracked in the """,
180 A("http://cve.mitre.org/cve/", "CVE database"),
181 """, issues tracked in the """,
182 A("http://nvd.nist.gov/", "National Vulnerability Database"),
183 """ (NVD), maintained by NIST; and security issues
184 discovered in Debian packages as reported in the BTS."""),
185 P("""All external data (including Debian bug reports and official Debian
186 security advisories) must be added to this database before it appears
187 here. Please help us keep this information up-to-date by """,
188 A(url.scriptRelative("data/report"), "reporting"),
189 """ any discrepancies or change of states that you are
190 aware of and/or help us improve the quality of this information by """,
191 A(url.scriptRelative("data/report"), "participating"),
192 "."),
193 make_menu(
194 url.scriptRelative,
195 ('status/release/unstable',
196 'Vulnerable packages in the unstable suite'),
197 ('status/release/testing',
198 'Vulnerable packages in the testing suite'),
199 ('status/release/stable',
200 'Vulnerable packages in the stable suite'),
201 ('status/release/oldstable',
202 'Vulnerable packages in the old stable suite'),
203 ('status/release/stable-backports',
204 'Vulnerable packages in backports for stable'),
205 ('status/release/oldstable-backports',
206 'Vulnerable packages in backports for oldstable'),
207 ('status/dtsa-candidates', "Candidates for DTSAs"),
208 ('status/todo', 'TODO items'),
209 ('status/undetermined', 'Packages that may be vulnerable but need to be checked (undetermined issues)'),
210 ('status/unimportant', 'Packages that have open unimportant issues'),
211 ('status/itp', 'ITPs with potential security issues'),
212 ('data/unknown-packages',
213 'Packages names not found in the archive'),
214 ('data/fake-names', 'Tracked issues without a CVE name'),
215 ('data/missing-epochs',
216 'Package versions which might lack an epoch'),
217 ('data/latently-vulnerable',
218 'Packages which are latently vulnerable in unstable'),
219 ('data/funny-versions',
220 'Packages with strange version numbers'),
221 ('data/releases',
222 'Covered Debian releases and architectures (slow)'),
223 self.make_search_button(url)),
224 P("""(You can enter CVE names, Debian bug numbers and package
225 names in the search forms.)"""),
226
227 H2("External interfaces"),
228 P("""If you want to automatically open a relevant web page for
229 some object, use the """,
230 CODE(str(url.scriptRelative("redirect/")), EM("object")),
231 """ URL. If no information is contained in this database,
232 the browser is automatically redirected to the corresponding external
233 data source.""")],
234 search_in_page=True)
235
236 def page_object(self, path, params, url):
237 obj = path[0]
238 return self.page_object_or_redirect(url, obj, False)
239
240 def page_redirect(self, path, params, url):
241 if path == ():
242 obj = ''
243 else:
244 obj = path[0]
245 return self.page_object_or_redirect(url, obj, True)
246
247 def page_object_or_redirect(self, url, obj, redirect):
248 c = self.db.cursor()
249
250 if not obj:
251 # Redirect to start page.
252 return RedirectResult(url.scriptRelativeFull(""))
253
254 # Attempt to decode a bug number. TEMP-nnn bugs (but not
255 # TEMP-nnn-mmm bugs) are treated as bug references, too.
256 bugnumber = 0
257 fake_bug = False
258 try:
259 if obj[0:5] == 'FAKE-' or obj[0:5] == 'TEMP-':
260 bugnumber = int(obj[5:])
261 fake_bug = True
262 else:
263 bugnumber = int(obj)
264 except ValueError:
265 pass
266 if bugnumber:
267 buglist = list(self.db.getBugsFromDebianBug(c, bugnumber))
268 if buglist:
269 return self.page_debian_bug(url, bugnumber, buglist, fake_bug)
270 if redirect:
271 return RedirectResult(self.url_debian_bug(url, str(bugnumber)),
272 permanent=False)
273
274 if 'A' <= obj[0] <= 'Z':
275 # Bug names start with a capital letter.
276 return self.page_bug(url, obj, redirect)
277
278 if self.db.isSourcePackage(c, obj):
279 return RedirectResult(self.url_source_package(url, obj, full=True))
280
281 return self.page_not_found(url, obj)
282
283 def page_bug(self, url, name, redirect):
284 # FIXME: Normalize CAN-* to CVE-* when redirecting. Too many
285 # people still use CAN.
286 if redirect and name[0:4] == 'CAN-':
287 name = 'CVE-' + name[4:]
288
289 cursor = self.db.cursor()
290 try:
291 bug = bugs.BugFromDB(cursor, name)
292 except ValueError:
293 if redirect:
294 if name[0:4] == 'CVE-':
295 return RedirectResult(self.url_cve(url, name),
296 permanent=False)
297 return self.page_not_found(url, name)
298 if bug.name <> name or redirect:
299 # Show the normalized bug name in the browser address bar.
300 return RedirectResult(url.scriptRelativeFull(bug.name))
301
302 page = []
303
304 def gen_header():
305 yield B("Name"), bug.name
306
307 source = bug.name.split('-')[0]
308 if source == 'CVE':
309 source_xref = compose(self.make_cve_ref(url, bug.name, 'CVE'),
310 " (at ",
311 self.make_nvd_ref(url, bug.name,
312 'NVD'),
313 "; ",
314 self.make_rhbug_ref(url, bug.name,
315 'RH'),
316 ")")
317 elif source == 'DSA':
318 source_xref = self.make_dsa_ref(url, bug.name, 'Debian')
319 elif source == 'DTSA':
320 source_xref = 'Debian Testing Security Team'
321 elif source == 'TEMP':
322 source_xref = (
323 'Automatically generated temporary name. Not for external reference.')
324 else:
325 source_xref = None
326
327 if source_xref:
328 yield B("Source"), source_xref
329
330 nvd = self.db.getNVD(cursor, bug.name)
331
332 if nvd and nvd.cve_desc:
333 yield B("Description"), nvd.cve_desc
334 elif bug.description:
335 yield B("Description"), bug.description
336
337 xref = list(self.db.getBugXrefs(cursor, bug.name))
338 if xref:
339 yield B("References"), self.make_xref_list(url, xref)
340
341 if nvd:
342 nvd_range = nvd.rangeString()
343 if nvd.severity:
344 nvd_severity = nvd.severity.lower()
345 if nvd_range:
346 nvd_severity = "%s (attack range: %s)" \
347 % (nvd_severity, nvd_range)
348 yield B("NVD severity"), nvd_severity
349
350 debian_bugs = bug.getDebianBugs(cursor)
351 if debian_bugs:
352 yield (B("Debian Bugs"),
353 self.make_debian_bug_list(url, debian_bugs))
354
355 if not bug.not_for_us:
356 for (release, status, reason) in bug.getStatus(cursor):
357 if status == 'undetermined':
358 reason = self.make_purple(reason)
359 elif status <> 'fixed':
360 reason = self.make_red(reason)
361 yield B('Debian/%s' % release), reason
362
363 page.append(make_table(gen_header()))
364
365 if bug.notes:
366 page.append(H2("Vulnerable and fixed packages"))
367
368 def gen_source():
369 old_pkg = ''
370 for (package, release, version, vulnerable) \
371 in self.db.getSourcePackages(cursor, bug.name):
372 if package == old_pkg:
373 package = ''
374 else:
375 old_pkg = package
376 package = compose(
377 self.make_source_package_ref(url, package),
378 " (", self.make_pts_ref(url, package, 'PTS'), ")")
379 if vulnerable == 1:
380 vuln = self.make_red('vulnerable')
381 version = self.make_red(version)
382 elif vulnerable == 2:
383 vuln = self.make_purple('undetermined')
384 version = self.make_purple(version)
385 else:
386 vuln = 'fixed'
387
388 yield package, ', '.join(release), version, vuln
389
390 page.append(make_table(gen_source(),
391 caption=("Source Package", "Release", "Version", "Status"),
392 introduction=P('The table below lists information on source packages.')))
393
394 def gen_data():
395 notes_sorted = bug.notes[:]
396 notes_sorted.sort(lambda a, b: cmp(a.package, b.package))
397 for n in notes_sorted:
398 if n.release:
399 rel = str(n.release)
400 else:
401 rel = '(unstable)'
402 urgency = str(n.urgency)
403 if n.fixed_version:
404 ver = str(n.fixed_version)
405 if ver == '0':
406 ver = '(not affected)'
407 urgency = ''
408 else:
409 ver = self.make_red('(unfixed)')
410 if urgency == 'not yet assigned':
411 urgency = ''
412
413 pkg = n.package
414 pkg_kind = n.package_kind
415 if pkg_kind == 'source':
416 pkg = self.make_source_package_ref(url, pkg)
417 elif pkg_kind == 'itp':
418 pkg_kind = 'ITP'
419 rel = ''
420 ver = ''
421 urgency = ''
422
423 bugs = n.bugs
424 bugs.sort()
425 bugs = make_list(
426 map(lambda x: self.make_debian_bug(url, x), bugs))
427 if n.bug_origin:
428 origin = self.make_xref(url, n.bug_origin)
429 else:
430 origin = ''
431 yield (pkg, pkg_kind, rel, ver, urgency, origin, bugs)
432
433 page.append(
434 make_table(gen_data(),
435 caption=("Package", "Type", "Release", "Fixed Version",
436 "Urgency", "Origin", "Debian Bugs"),
437 introduction=P("The information above is based on the following data on fixed versions.")))
438
439 if bug.comments:
440 page.append(H2("Notes"))
441 def gen_comments():
442 for (t, c) in bug.comments:
443 yield c
444 page.append(make_pre(gen_comments()))
445
446 return self.create_page(url, bug.name, page)
447
448 def page_debian_bug(self, url, bugnumber, buglist, fake_bug):
449 if fake_bug:
450 new_buglist = []
451 for b in buglist:
452 (bug_name, urgency, description) = b
453 if bug_name[0:5] == 'FAKE-' or bug_name[0:5] == 'TEMP-':
454 new_buglist.append(b)
455 if len(new_buglist) > 0:
456 # Only replace the bug list if there are still fake
457 # bug reports.
458 buglist = new_buglist
459
460 if len(buglist) == 1:
461 # Single issue, redirect.
462 return RedirectResult(url.scriptRelativeFull(buglist[0][0]))
463
464 def gen():
465 for (name, urgency, description) in buglist:
466 if urgency == "unknown":
467 urgency = ""
468 yield self.make_xref(url, name), urgency, description
469
470 if fake_bug:
471 intro = """The URL you used contained a non-stable name
472 based on a Debian bug number. This name cannot be mapped to a specific
473 issue. """
474 else:
475 intro = ""
476
477 return self.create_page(
478 url, "Information related to Debian bug #%d" % bugnumber,
479 [P(intro + "The following issues reference to Debian bug ",
480 self.make_debian_bug(url, bugnumber), ":"),
481 make_table(gen(),
482 caption=("Name", "Urgency", "Description"))])
483
484 def page_not_found(self, url, query):
485 return self.create_page(url, 'Not found',
486 [P('Your query ',
487 CODE(query),
488 ' matched no results.')],
489 status=404)
490
491 def page_report(self, path, params, url):
492 return self.create_page(
493 url, 'Reporting discrepancies in the data',
494 [P("""The data in this tracker is always in flux, as bugs are fixed and new
495 issues disclosed, the data contained herein is updated. We strive to
496 maintain complete and accurate state information, and appreciate any
497 updates in status, information or new issues."""),
498 P("There are three ways that you can report updates to this information:"),
499 make_numbered_list(
500 [P("""IRC: We can be found at """,
501 CODE("irc.oftc.net"),
502 ", ",
503 CODE("#debian-security"),
504 """. If you have information to report, please go ahead and join
505 the channel and tell us. Please feel free to state the issue,
506 regardless if there is someone who has acknowledged you. Many of us
507 idle on this channel and may not be around when you join, but we read
508 the backlog and will see what you have said. If you require a
509 response, do not forget to let us know how to get a hold of you."""),
510 P("Mailing list: Our mailing list is: ",
511 A("mailto:debian-security-tracker@lists.debian.org",
512 "debian-security-tracker@lists.debian.org")),
513 P("""Helping out: We welcome people who wish to join us in tracking
514 issues. The process is designed to be easy to learn and participate,
515 please read our """,
516 A("http://svn.debian.org/viewvc/secure-testing/doc/narrative_introduction?view=markup",
517 "Introduction"),
518 """ to get familiar with how things work. Join us on
519 our mailing list, and on IRC and request to be added to the Alioth """,
520 A("http://alioth.debian.org/projects/secure-testing/", "project"),
521 """. We are really quite friendly. If you have a
522 question about how things work, don't be afraid to ask, we would like
523 to improve our documentation and procedures, so feedback is welcome.""")])])
524
525 def page_source_package(self, path, params, url):
526 pkg = path[0]
527
528 def gen_versions():
529 for (releases, version) in self.db.getSourcePackageVersions(
530 self.db.cursor(), pkg):
531 yield ', '.join(releases), version
532 def gen_bug_list(lst):
533 for (bug, description) in lst:
534 yield self.make_xref(url, bug), description
535
536 return self.create_page(
537 url, 'Information on source package ' + pkg,
538 [make_menu(lambda x: x,
539 (self.url_pts(url, pkg),
540 pkg + ' in the Package Tracking System'),
541 (self.url_debian_bug_pkg(url, pkg),
542 pkg + ' in the Bug Tracking System'),
543 (self.url_testing_status(url, pkg),
544 pkg + ' in the testing migration checker')),
545 H2('Available versions'),
546 make_table(gen_versions(), caption=('Release', 'Version')),
547
548 H2('Open issues'),
549 make_table(gen_bug_list(self.db.getBugsForSourcePackage
550 (self.db.cursor(), pkg, True, False)),
551 caption=('Bug', 'Description'),
552 replacement='No known open issues.'),
553
554 H2('Open unimportant issues'),
555 make_table(gen_bug_list(self.db.getBugsForSourcePackage
556 (self.db.cursor(), pkg, True, True)),
557 caption=('Bug', 'Description'),
558 replacement='No known unimportant issues.'),
559
560 H2('Resolved issues'),
561 make_table(gen_bug_list(self.db.getBugsForSourcePackage
562 (self.db.cursor(), pkg, False, True)),
563 caption=('Bug', 'Description'),
564 replacement='No known resolved issues.'),
565
566 H2('Security announcements'),
567 make_table(gen_bug_list(self.db.getDSAsForSourcePackage
568 (self.db.cursor(), pkg)),
569 caption=('DSA', 'Description'),
570 replacement='No known security announcements.')
571 ])
572
573 def page_status_release_stable_oldstable(self, release, params, url):
574 assert release in ('stable', 'oldstable')
575
576 bf = BugFilterNoDSA(params)
577
578 def gen():
579 old_pkg_name = ''
580 for (pkg_name, bug_name, archive, urgency, vulnerable, remote, no_dsa) in \
581 self.db.cursor().execute(
582 """SELECT package, bug, section, urgency, vulnerable, remote, no_dsa
583 FROM %s_status""" % release):
584 if bf.urgencyFiltered(urgency, vulnerable):
585 continue
586 if bf.remoteFiltered(remote):
587 continue
588 if bf.nodsaFiltered(no_dsa):
589 continue
590
591 if pkg_name == old_pkg_name:
592 pkg_name = ''
593 else:
594 old_pkg_name = pkg_name
595 if archive <> 'main':
596 pkg_name = "%s (%s)" % (pkg_name, archive)
597
598 if remote is None:
599 remote = '???'
600 elif remote:
601 remote = 'yes'
602 else:
603 remote = 'no'
604
605 if urgency.startswith('high'):
606 urgency = self.make_red(urgency)
607 elif vulnerable == 2:
608 urgency = self.make_purple(urgency)
609 else:
610 if no_dsa:
611 urgency = urgency + '*'
612
613 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
614
615 return self.create_page(
616 url, 'Vulnerable source packages in the %s suite' % release,
617 [bf.actions(url), BR(),
618 make_table(gen(), caption=("Package", "Bug", "Urgency", "Remote")),
619 P('''If a "*" is included in the urgency field, no DSA is planned
620 for this vulnerability.'''),
621 self.nvd_text])
622
623 def page_status_release_stable(self, path, params, url):
624 return self.page_status_release_stable_oldstable('stable', params, url)
625 def page_status_release_oldstable(self, path, params, url):
626 return self.page_status_release_stable_oldstable('oldstable',
627 params, url)
628
629 def page_status_release_testing(self, path, params, url):
630 bf = BugFilterNoDSA(params)
631
632 def gen():
633 old_pkg_name = ''
634 for (pkg_name, bug_name, archive, urgency, vulnerable,
635 sid_vulnerable, ts_fixed, remote, no_dsa) \
636 in self.db.cursor().execute(
637 """SELECT package, bug, section, urgency, vulnerable,
638 unstable_vulnerable, testing_security_fixed, remote, no_dsa
639 FROM testing_status"""):
640 if bf.urgencyFiltered(urgency, vulnerable):
641 continue
642 if bf.remoteFiltered(remote):
643 continue
644 if bf.nodsaFiltered(no_dsa):
645 continue
646
647 if pkg_name == old_pkg_name:
648 pkg_name = ''
649 else:
650 old_pkg_name = pkg_name
651 if archive <> 'main':
652 pkg_name = "%s (%s)" % (pkg_name, archive)
653
654 if remote is None:
655 remote = '???'
656 elif remote:
657 remote = 'yes'
658 else:
659 remote = 'no'
660
661 if ts_fixed:
662 status = 'fixed in testing-security'
663 else:
664 if sid_vulnerable:
665 status = self.make_red('unstable is vulnerable')
666 else:
667 status = self.make_dangerous('fixed in unstable')
668
669 if urgency.startswith('high'):
670 urgency = self.make_red(urgency)
671 elif vulnerable == 2:
672 urgency = self.make_purple(urgency)
673
674 yield (pkg_name, self.make_xref(url, bug_name),
675 urgency, remote, status)
676
677 return self.create_page(
678 url, 'Vulnerable source packages in the testing suite',
679 [make_menu(url.scriptRelative,
680 ("status/dtsa-candidates", "Candidates for DTSAs")),
681 bf.actions(url), BR(),
682 make_table(gen(), caption=("Package", "Bug", "Urgency", "Remote")),
683 self.nvd_text])
684
685 def page_status_release_unstable_like(self, path, params, url,
686 rel, title):
687 bf = BugFilter(params)
688
689 def gen():
690 old_pkg_name = ''
691 for (pkg_name, bug_name, section, urgency, vulnerable, remote) \
692 in self.db.cursor().execute(
693 """SELECT DISTINCT sp.name, st.bug_name,
694 sp.archive, st.urgency, st.vulnerable,
695 (SELECT range_remote FROM nvd_data
696 WHERE cve_name = st.bug_name)
697 FROM source_package_status AS st, source_packages AS sp
698 WHERE st.vulnerable AND sp.rowid = st.package
699 AND sp.release = ? AND sp.subrelease = ''
700 ORDER BY sp.name, st.bug_name""", (rel,)):
701 if bf.urgencyFiltered(urgency, vulnerable):
702 continue
703 if bf.remoteFiltered(remote):
704 continue
705
706 if pkg_name == old_pkg_name:
707 pkg_name = ''
708 else:
709 old_pkg_name = pkg_name
710 if section <> 'main':
711 pkg_name = "%s (%s)" % (pkg_name, section)
712 else:
713 pkg_name = self.make_xref(url, pkg_name)
714
715 if remote is None:
716 remote = '???'
717 elif remote:
718 remote = 'yes'
719 else:
720 remote = 'no'
721
722 if urgency.startswith('high'):
723 urgency = self.make_red(urgency)
724 elif vulnerable == 2:
725 urgency = self.make_purple(urgency)
726
727 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
728
729 return self.create_page(
730 url, title,
731 [P("""Note that the list below is based on source packages.
732 This means that packages are not listed here once a new,
733 fixed source version has been uploaded to the archive, even
734 if there are still some vulnerably binary packages present
735 in the archive."""),
736 bf.actions(url), BR(),
737 make_table(gen(), caption=('Package', 'Bug', 'Urgency', 'Remote')),
738 self.nvd_text])
739
740 def page_status_release_unstable(self, path, params, url):
741 return self.page_status_release_unstable_like(
742 path, params, url,
743 title='Vulnerable source packages in the unstable suite',
744 rel='sid')
745
746 def page_status_release_stable_backports(self, path, params, url):
747 return self.page_status_release_unstable_like(
748 path, params, url,
749 title='Vulnerable source packages among backports for stable',
750 rel='squeeze-backports')
751
752 def page_status_release_oldstable_backports(self, path, params, url):
753 return self.page_status_release_unstable_like(
754 path, params, url,
755 title='Vulnerable source packages among backports for oldstable',
756 rel='lenny-backports')
757
758 def page_status_dtsa_candidates(self, path, params, url):
759 bf = BugFilter(params)
760
761 def gen():
762 old_pkg_name = ''
763 for (pkg_name, bug_name, archive, urgency, vulnerable,
764 stable_later, remote) \
765 in self.db.cursor().execute(
766 """SELECT package, bug, section, urgency, vulnerable,
767 (SELECT testing.version_id < stable.version_id
768 FROM source_packages AS testing, source_packages AS stable
769 WHERE testing.name = testing_status.package
770 AND testing.release = 'wheezy'
771 AND testing.subrelease = ''
772 AND testing.archive = testing_status.section
773 AND stable.name = testing_status.package
774 AND stable.release = 'squeeze'
775 AND stable.subrelease = 'security'
776 AND stable.archive = testing_status.section),
777 (SELECT range_remote FROM nvd_data
778 WHERE cve_name = bug)
779 FROM testing_status
780 WHERE (NOT unstable_vulnerable)
781 AND (NOT testing_security_fixed)"""):
782 if bf.urgencyFiltered(urgency, vulnerable):
783 continue
784 if bf.remoteFiltered(remote):
785 continue
786
787 if pkg_name == old_pkg_name:
788 pkg_name = ''
789 migration = ''
790 else:
791 old_pkg_name = pkg_name
792 migration = A(self.url_testing_status(url, pkg_name),
793 "check")
794 if archive <> 'main':
795 pkg_name = "%s (%s)" % (pkg_name, archive)
796 else:
797 pkg_name = self.make_source_package_ref(url, pkg_name)
798
799 if remote is None:
800 remote = '???'
801 elif remote:
802 remote = 'yes'
803 else:
804 remote = 'no'
805
806 if urgency.startswith('high'):
807 urgency = self.make_red(urgency)
808 elif vulnerable == 2:
809 urgency = self.make_purple(urgency)
810
811 if stable_later:
812 notes = "(fixed in stable?)"
813 else:
814 notes = ''
815
816 yield (pkg_name, migration, self.make_xref(url, bug_name),
817 urgency, remote, notes)
818
819 return self.create_page(
820 url, "Candidates for DTSAs",
821 [P("""The table below lists packages which are fixed
822 in unstable, but unfixed in testing. Use the testing migration
823 checker to find out why they have not entered testing yet."""),
824 make_menu(url.scriptRelative,
825 ("status/release/testing",
826 "List of vulnerable packages in testing")),
827 bf.actions(url), BR(),
828 make_table(gen(),
829 caption=("Package", "Migration", "Bug", "Urgency",
830 "Remote"))])
831
832 def page_status_todo(self, path, params, url):
833 hide_check = params.get('hide_check', False)
834 if hide_check:
835 flags = A(url.updateParamsDict({'hide_check' : None}),
836 'Show "check" TODOs')
837 else:
838 flags = A(url.updateParamsDict({'hide_check' : '1'}),
839 'Hide "check" TODOs')
840
841 def gen():
842 for (bug, description, note) in self.db.getTODOs(hide_check=hide_check):
843 yield self.make_xref(url, bug), description, note
844 return self.create_page(
845 url, 'Bugs with TODO items',
846 [P(flags), make_table(gen(), caption=('Bug', 'Description', 'Note'))])
847
848 def page_status_undetermined(self, path, params, url):
849 def gen():
850 outrel = []
851 old_bug = ''
852 old_pkg = ''
853 old_dsc = ''
854 last_displayed = ''
855 releases = ('sid', 'wheezy', 'squeeze', 'lenny')
856 for (pkg_name, bug_name, release, desc) in self.db.cursor().execute(
857 """SELECT DISTINCT sp.name, st.bug_name, sp.release,
858 bugs.description
859 FROM source_package_status AS st, source_packages AS sp, bugs
860 WHERE st.vulnerable == 2 AND sp.rowid = st.package
861 AND ( sp.release = ? OR sp.release = ? OR sp.release = ?
862 OR sp.release = ? )
863 AND sp.subrelease = '' AND st.bug_name == bugs.name
864 ORDER BY sp.name, st.bug_name""", releases):
865
866 if old_bug == '':
867 old_bug = bug_name
868 old_pkg = pkg_name
869 old_dsc = desc
870 elif old_bug != bug_name:
871 if old_pkg == last_displayed:
872 to_display = ''
873 else:
874 to_display = old_pkg
875 yield to_display, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
876 last_displayed = old_pkg
877 old_bug = bug_name
878 old_pkg = pkg_name
879 old_dsc = desc
880 outrel = []
881 outrel.append( release )
882 yield old_pkg, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
883
884 return self.create_page(url, 'Packages that may be vulnerable but need to be checked (undetermined issues)',
885 [P("""This page lists packages that may or may not be affected
886 by known issues. This means that some additional work needs to
887 be done to determined whether the package is actually
888 vulnerable or not. This list is a good area for new
889 contributors to make quick and meaningful contributions."""),
890 make_table(gen(), caption=('Package', 'Bug', 'Description', 'Releases'))])
891
892 def page_status_unimportant(self, path, params, url):
893 def gen():
894 outrel = []
895 old_bug = ''
896 old_pkg = ''
897 old_dsc = ''
898 old_name = ''
899 last_displayed = ''
900 releases = ('sid', 'wheezy', 'squeeze', 'lenny')
901 for (pkg_name, bug_name, release, desc) in self.db.cursor().execute(
902 """SELECT DISTINCT sp.name, st.bug_name, sp.release,
903 bugs.description
904 FROM source_package_status AS st, source_packages AS sp, bugs
905 WHERE st.vulnerable > 0 AND sp.rowid = st.package
906 AND ( sp.release = ? OR sp.release = ? OR sp.release = ?
907 OR sp.release = ? ) AND st.urgency == 'unimportant'
908 AND sp.subrelease = '' AND st.bug_name == bugs.name
909 ORDER BY sp.name, st.bug_name""", releases):
910
911 if old_bug == '':
912 old_bug = bug_name
913 old_pkg = pkg_name
914 old_dsc = desc
915 elif old_bug != bug_name:
916 if old_pkg == last_displayed:
917 to_display = ''
918 else:
919 to_display = old_pkg
920 yield to_display, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
921 last_displayed = old_pkg
922 old_bug = bug_name
923 old_pkg = pkg_name
924 old_dsc = desc
925 outrel = []
926 outrel.append( release )
927 yield old_pkg, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
928
929 return self.create_page(url, 'Packages that have open unimportant issues',
930 [P("""This page lists packages that are affected by issues
931 that are considered unimportant from a security perspective.
932 These issues are thought to be unexploitable or uneffective
933 in most situations (for example, browser denial-of-services)."""),
934 make_table(gen(), caption=('Package', 'Bug', 'Description', 'Releases'))])
935
936 def page_status_itp(self, path, params, url):
937 def gen():
938 old_pkg = ''
939 for pkg, bugs, debian_bugs in self.db.getITPs(self.db.cursor()):
940 if pkg == old_pkg:
941 pkg = ''
942 else:
943 old_pkg = pkg
944 yield (pkg, self.make_xref_list(url, bugs),
945 self.make_debian_bug_list(url, debian_bugs))
946 return self.create_page(
947 url, "ITPs with potential security issues",
948 [make_table(gen(), caption=("Package", "Issue", "Debian Bugs"),
949 replacement="No ITP bugs are currently known.")])
950
951 def page_data_unknown_packages(self, path, params, url):
952 def gen():
953 for name, bugs in self.db.getUnknownPackages(self.db.cursor()):
954 yield name, self.make_xref_list(url, bugs)
955 return self.create_page(
956 url, "Unknown packages",
957 [P("""Sometimes, a package referenced in a bug report
958 cannot be found in the database. This can be the result of a spelling
959 error, or a historic entry refers to a
960 package which is no longer in the archive."""),
961 make_table(gen(), caption=("Package", "Bugs"),
962 replacement="No unknown packages are referenced in the database.")])
963
964 def page_data_missing_epochs(self, path, params, url):
965 def gen():
966 old_bug = ''
967 old_pkg = ''
968 for bug, pkg, ver1, ver2 in self.db.cursor().execute(
969 """SELECT DISTINCT bug_name, n.package,
970 n.fixed_version, sp.version
971 FROM package_notes AS n, source_packages AS sp
972 WHERE n.package_kind = 'source'
973 AND n.fixed_version NOT LIKE '%:%'
974 AND n.fixed_version <> '0'
975 AND n.bug_origin = ''
976 AND sp.name = n.package
977 AND sp.version LIKE '%:%'
978 ORDER BY bug_name, package"""):
979 if bug == old_bug:
980 bug = ''
981 else:
982 old_bug = bug
983 old_pkg = ''
984 bug = self.make_xref(url, bug)
985 if pkg == old_pkg:
986 pkg = ''
987 else:
988 old_pkg = pkg
989 pkg = self.make_source_package_ref(url, pkg)
990 yield bug, pkg, ver1, ver2
991
992 return self.create_page(
993 url, "Missing epochs in package versions",
994 [make_table(gen(),
995 caption=("Bug", "Package", "Version 1", "Version 2"),
996 replacement="No source package version with missing epochs.")])
997
998 def page_data_latently_vulnerable(self, path, params, url):
999 def gen():
1000 for pkg, bugs in self.db.cursor().execute(
1001 """SELECT package, string_set(bug_name)
1002 FROM package_notes AS p1
1003 WHERE release <> ''
1004 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
1005 AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
1006 WHERE p2.bug_name = p1.bug_name
1007 AND p2.package = p1.package
1008 AND release = '')
1009 AND EXISTS (SELECT 1 FROM source_packages
1010 WHERE name = p1.package AND release = 'sid')
1011 GROUP BY package
1012 ORDER BY package"""):
1013 pkg = self.make_source_package_ref(url, pkg)
1014 bugs = bugs.split(',')
1015 yield pkg, self.make_xref_list(url, bugs)
1016
1017 def gen_unimportant():
1018 for pkg, bugs in self.db.cursor().execute(
1019 """SELECT package, string_set(bug_name)
1020 FROM package_notes AS p1
1021 WHERE release <> ''
1022 AND urgency <> 'unimportant'
1023 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
1024 AND EXISTS (SELECT 1 FROM package_notes AS p2
1025 WHERE p2.bug_name = p1.bug_name
1026 AND p2.package = p1.package
1027 AND release = '')
1028 AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
1029 WHERE p2.bug_name = p1.bug_name
1030 AND p2.package = p1.package
1031 AND urgency <> 'unimportant'
1032 AND release = '')
1033 AND EXISTS (SELECT 1 FROM source_packages
1034 WHERE name = p1.package AND release = 'sid')
1035 GROUP BY package
1036 ORDER BY package"""):
1037 pkg = self.make_source_package_ref(url, pkg)
1038 bugs = bugs.split(',')
1039 yield pkg, self.make_xref_list(url, bugs)
1040
1041 return self.create_page(
1042 url, "Latently vulnerable packages in unstable",
1043 [P(
1044 """A package is latently vulnerable in unstable if it is vulnerable in
1045 any release, and there is no package note for the same vulnerability
1046 and package in unstable (and the package is still available in
1047 unstable, of course)."""),
1048 make_table(gen(),
1049 caption=("Package", "Bugs"),
1050 replacement="No latently vulnerable packages were found."),
1051 P(
1052 """The next table lists issues which are marked unimportant for
1053 unstable, but for which release-specific annotations exist which are
1054 not unimportant."""),
1055 make_table(gen_unimportant(),
1056 caption=("Package", "Bugs"),
1057 replacement=
1058 "No packages with unimportant latent vulnerabilities were found."),
1059 ])
1060
1061 def page_data_releases(self, path, params, url):
1062 def gen():
1063 for (rel, subrel, archive, sources, archs) \
1064 in self.db.availableReleases():
1065 if sources:
1066 sources = 'yes'
1067 else:
1068 sources = 'no'
1069 yield rel, subrel, archive, sources, make_list(archs)
1070 return self.create_page(
1071 url, "Available releases",
1072 [P("""The security issue database is checked against
1073 the Debian releases listed in the table below."""),
1074 make_table(gen(),
1075 caption=("Release", "Subrelease", "Archive",
1076 "Sources", "Architectures"))])
1077
1078 def page_data_funny_versions(self, path, params, url):
1079 def gen():
1080 for name, release, archive, version, source_version \
1081 in self.db.getFunnyPackageVersions():
1082 yield name, release, archive, source_version, version
1083
1084 return self.create_page(
1085 url, "Version conflicts between source/binary packages",
1086 [P("""The table below lists source packages
1087 which have a binary package of the same name, but with a different
1088 version. This means that extra care is necessary to determine
1089 the version of a package which has been fixed. (Note that
1090 the bug tracker prefers source versions to binary versions
1091 in this case.)"""),
1092 make_table(gen(),
1093 caption=("Package",
1094 "Release",
1095 "Archive",
1096 "Source Version",
1097 "Binary Version")),
1098 P("""Technically speaking, these version numbering is fine,
1099 but it makes version-based bug tracking quite difficult for these packages."""),
1100 P("""There are many binary packages which are built from source
1101 packages with different version numbering schemes. However, as
1102 long as none of the binary packages carries the same name as the
1103 source package, most confusion is avoided or can be easily
1104 explained.""")])
1105
1106 def page_data_fake_names(self, path, params, url):
1107 def gen():
1108 for (bug, description) in self.db.getFakeBugs():
1109 yield self.make_xref(url, bug), description
1110 return self.create_page(
1111 url, "Automatically generated issue names",
1112 [P("""Some issues have not been assigned CVE names, but are still
1113 tracked by this database. In this case, the system automatically assigns
1114 a unique name. These names are not stable and can change when the database
1115 is updated, so they should not be used in external references."""),
1116 P('''The automatically generated names come in two flavors:
1117 the first kind starts with the string "''', CODE("TEMP-000000-"),
1118 '''". This means that no Debian bug has been assigned to this
1119 issue (or a bug has been created and is not recorded in this database).
1120 In the second kind of names, there is a Debian bug for the issue, and the "''',
1121 CODE("000000"), '''"part of the name is replaced with the
1122 Debian bug number.'''),
1123 make_table(gen(),
1124 caption=("Bug", "Description"))])
1125
1126 def page_data_pts(self, path, params, url):
1127 data = []
1128 for pkg, bugs in self.db.cursor().execute(
1129 """SELECT package, COUNT(DISTINCT bug) FROM
1130 (SELECT package, bug, urgency FROM stable_status
1131 UNION ALL SELECT DISTINCT sp.name, st.bug_name, st.urgency
1132 FROM source_package_status AS st, source_packages AS sp
1133 WHERE st.vulnerable AND st.urgency <> 'unimportant'
1134 AND sp.rowid = st.package AND sp.release = 'sid'
1135 AND sp.subrelease = '') x WHERE urgency <> 'unimportant'
1136 GROUP BY package ORDER BY package"""):
1137 data.append(pkg)
1138 data.append(':')
1139 data.append(str(bugs))
1140 data.append('\n')
1141 return BinaryResult(''.join(data))
1142
1143 def page_debsecan(self, path, params, url):
1144 obj = '/'.join(path)
1145 data = self.db.getDebsecan(obj)
1146 if data:
1147 return BinaryResult(data)
1148 else:
1149 return self.create_page(
1150 url, "Object not found",
1151 [P("The requested debsecan object has not been found.")],
1152 status=404)
1153
1154 def create_page(self, url, title, body, search_in_page=False, status=200):
1155 append = body.append
1156 append(HR())
1157 if not search_in_page:
1158 append(self.make_search_button(url))
1159 append(P(A(url.scriptRelative(""), "Home"),
1160 " - ", A(url.absolute("http://secure-testing.debian.net/"),
1161 "Testing Security Team"),
1162 " - ", A(url.absolute("http://www.debian.org/security/"),
1163 "Debian Security"),
1164 " - ", A(url.absolute
1165 ("http://www.enyo.de/fw/impressum.html"),
1166 "Imprint")))
1167 if search_in_page:
1168 on_load = "selectSearch()"
1169 else:
1170 on_load = None
1171 return HTMLResult(self.add_title(title, body,
1172 head_contents=self.head_contents,
1173 body_attribs={'onload': on_load}),
1174 doctype=self.html_dtd(),
1175 status=status)
1176
1177 def make_search_button(self, url):
1178 return FORM("Search for package or bug name: ",
1179 INPUT(type='text', name='query',
1180 onkeyup="onSearch(this.value)",
1181 onmousemove="onSearch(this.value)"),
1182 INPUT(type='submit', value='Go'),
1183 ' ',
1184 A(url.scriptRelative("data/report"), "Reporting problems"),
1185 method='get',
1186 action=url.scriptRelative(''))
1187
1188 def url_cve(self, url, name):
1189 return url.absolute("http://cve.mitre.org/cgi-bin/cvename.cgi",
1190 name=name)
1191 def url_nvd(self, url, name):
1192 return url.absolute("http://web.nvd.nist.gov/view/vuln/detail",
1193 vulnId=name)
1194 def url_rhbug(self, url, name):
1195 return url.absolute("https://bugzilla.redhat.com/show_bug.cgi",
1196 id=name)
1197
1198 def url_dsa(self, url, dsa, re_dsa=re.compile(r'^DSA-(\d+)(?:-\d+)?$')):
1199 match = re_dsa.match(dsa)
1200 if match:
1201 # We must determine the year because there is no generic URL.
1202 (number,) = match.groups()
1203 for (date,) in self.db.cursor().execute(
1204 "SELECT release_date FROM bugs WHERE name = ?", (dsa,)):
1205 (y, m, d) = date.split('-')
1206 return url.absolute("http://www.debian.org/security/%d/dsa-%d"
1207 % (int(y), int(number)))
1208 return None
1209
1210 def url_debian_bug(self, url, debian):
1211 return url.absolute("http://bugs.debian.org/cgi-bin/bugreport.cgi",
1212 bug=str(debian))
1213 def url_debian_bug_pkg(self, url, debian):
1214 return url.absolute("http://bugs.debian.org/cgi-bin/pkgreport.cgi",
1215 pkg=debian)
1216 def url_pts(self, url, package):
1217 return url.absolute("http://packages.qa.debian.org/common/index.html",
1218 src=package)
1219 def url_testing_status(self, url, package):
1220 return url.absolute("http://release.debian.org/migration/testing.pl",
1221 package=package)
1222 def url_source_package(self, url, package, full=False):
1223 if full:
1224 return url.scriptRelativeFull("source-package/" + package)
1225 else:
1226 return url.scriptRelative("source-package/" + package)
1227
1228 def make_xref(self, url, name):
1229 return A(url.scriptRelative(name), name)
1230
1231 def make_xref_list(self, url, lst, separator=', '):
1232 return make_list(map(lambda x: self.make_xref(url, x), lst), separator)
1233
1234 def make_debian_bug(self, url, debian):
1235 return A(self.url_debian_bug(url, debian), str(debian))
1236 def make_debian_bug_list(self, url, lst):
1237 return make_list(map(lambda x: self.make_debian_bug(url, x), lst))
1238
1239 def make_cve_ref(self, url, cve, name=None):
1240 if name is None:
1241 name = cve
1242 return A(self.url_cve(url, cve), name)
1243
1244 def make_nvd_ref(self, url, cve, name=None):
1245 if name is None:
1246 name = cve
1247 return A(self.url_nvd(url, cve), name)
1248
1249 def make_rhbug_ref(self, url, cve, name=None):
1250 if name is None:
1251 name = cve
1252 return A(self.url_rhbug(url, cve), name)
1253
1254 def make_dsa_ref(self, url, dsa, name=None):
1255 if name is None:
1256 name = dsa
1257 u = self.url_dsa(url, dsa)
1258 if u:
1259 return A(u, name)
1260 else:
1261 return name
1262
1263 def make_pts_ref(self, url, pkg, name=None):
1264 if name is None:
1265 name = pkg
1266 return A(self.url_pts(url, pkg), name)
1267
1268 def make_source_package_ref(self, url, pkg, title=None):
1269 if title is None:
1270 title = pkg
1271 return A(self.url_source_package(url, pkg), title)
1272
1273 def make_red(self, contents):
1274 return SPAN(contents, _class="red")
1275
1276 def make_purple(self, contents):
1277 return SPAN(contents, _class="purple")
1278
1279 def make_dangerous(self, contents):
1280 return SPAN(contents, _class="dangerous")
1281
1282 def pre_dispatch(self):
1283 pass
1284
1285 TrackerService(socket_name, db_name).run()

Properties

Name Value
svn:mime-type text/script

  ViewVC Help
Powered by ViewVC 1.1.5