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

Contents of /bin/tracker_service.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 16134 - (show annotations) (download) (as text)
Sun Feb 13 22:46:09 2011 UTC (2 years, 3 months ago) by gilbert-guest
File MIME type: text/script
File size: 55718 byte(s)
display notes on todo page
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 if bug.description:
331 yield B("Description"), bug.description
332
333 xref = list(self.db.getBugXrefs(cursor, bug.name))
334 if xref:
335 yield B("References"), self.make_xref_list(url, xref)
336
337 nvd = self.db.getNVD(cursor, bug.name)
338 if nvd:
339 nvd_range = nvd.rangeString()
340 if nvd.severity:
341 nvd_severity = nvd.severity.lower()
342 if nvd_range:
343 nvd_severity = "%s (attack range: %s)" \
344 % (nvd_severity, nvd_range)
345 yield B("NVD severity"), nvd_severity
346
347 debian_bugs = bug.getDebianBugs(cursor)
348 if debian_bugs:
349 yield (B("Debian Bugs"),
350 self.make_debian_bug_list(url, debian_bugs))
351
352 if not bug.not_for_us:
353 for (release, status, reason) in bug.getStatus(cursor):
354 if status == 'undetermined':
355 reason = self.make_purple(reason)
356 elif status <> 'fixed':
357 reason = self.make_red(reason)
358 yield B('Debian/%s' % release), reason
359
360 page.append(make_table(gen_header()))
361
362 if bug.notes:
363 page.append(H2("Vulnerable and fixed packages"))
364
365 def gen_source():
366 old_pkg = ''
367 for (package, release, version, vulnerable) \
368 in self.db.getSourcePackages(cursor, bug.name):
369 if package == old_pkg:
370 package = ''
371 else:
372 old_pkg = package
373 package = compose(
374 self.make_source_package_ref(url, package),
375 " (", self.make_pts_ref(url, package, 'PTS'), ")")
376 if vulnerable == 1:
377 vuln = self.make_red('vulnerable')
378 version = self.make_red(version)
379 elif vulnerable == 2:
380 vuln = self.make_purple('undetermined')
381 version = self.make_purple(version)
382 else:
383 vuln = 'fixed'
384
385 yield package, ', '.join(release), version, vuln
386
387 page.append(make_table(gen_source(),
388 caption=("Source Package", "Release", "Version", "Status"),
389 introduction=P('The table below lists information on source packages.')))
390
391 def gen_data():
392 notes_sorted = bug.notes[:]
393 notes_sorted.sort(lambda a, b: cmp(a.package, b.package))
394 for n in notes_sorted:
395 if n.release:
396 rel = str(n.release)
397 else:
398 rel = '(unstable)'
399 urgency = str(n.urgency)
400 if n.fixed_version:
401 ver = str(n.fixed_version)
402 if ver == '0':
403 ver = '(not affected)'
404 urgency = ''
405 else:
406 ver = self.make_red('(unfixed)')
407 if urgency == 'not yet assigned':
408 urgency = ''
409
410 pkg = n.package
411 pkg_kind = n.package_kind
412 if pkg_kind == 'source':
413 pkg = self.make_source_package_ref(url, pkg)
414 elif pkg_kind == 'itp':
415 pkg_kind = 'ITP'
416 rel = ''
417 ver = ''
418 urgency = ''
419
420 bugs = n.bugs
421 bugs.sort()
422 bugs = make_list(
423 map(lambda x: self.make_debian_bug(url, x), bugs))
424 if n.bug_origin:
425 origin = self.make_xref(url, n.bug_origin)
426 else:
427 origin = ''
428 yield (pkg, pkg_kind, rel, ver, urgency, origin, bugs)
429
430 page.append(
431 make_table(gen_data(),
432 caption=("Package", "Type", "Release", "Fixed Version",
433 "Urgency", "Origin", "Debian Bugs"),
434 introduction=P("The information above is based on the following data on fixed versions.")))
435
436 if bug.comments:
437 page.append(H2("Notes"))
438 def gen_comments():
439 for (t, c) in bug.comments:
440 yield c
441 page.append(make_pre(gen_comments()))
442
443 return self.create_page(url, bug.name, page)
444
445 def page_debian_bug(self, url, bugnumber, buglist, fake_bug):
446 if fake_bug:
447 new_buglist = []
448 for b in buglist:
449 (bug_name, urgency, description) = b
450 if bug_name[0:5] == 'FAKE-' or bug_name[0:5] == 'TEMP-':
451 new_buglist.append(b)
452 if len(new_buglist) > 0:
453 # Only replace the bug list if there are still fake
454 # bug reports.
455 buglist = new_buglist
456
457 if len(buglist) == 1:
458 # Single issue, redirect.
459 return RedirectResult(url.scriptRelativeFull(buglist[0][0]))
460
461 def gen():
462 for (name, urgency, description) in buglist:
463 if urgency == "unknown":
464 urgency = ""
465 yield self.make_xref(url, name), urgency, description
466
467 if fake_bug:
468 intro = """The URL you used contained a non-stable name
469 based on a Debian bug number. This name cannot be mapped to a specific
470 issue. """
471 else:
472 intro = ""
473
474 return self.create_page(
475 url, "Information related to Debian bug #%d" % bugnumber,
476 [P(intro + "The following issues reference to Debian bug ",
477 self.make_debian_bug(url, bugnumber), ":"),
478 make_table(gen(),
479 caption=("Name", "Urgency", "Description"))])
480
481 def page_not_found(self, url, query):
482 return self.create_page(url, 'Not found',
483 [P('Your query ',
484 CODE(query),
485 ' matched no results.')],
486 status=404)
487
488 def page_report(self, path, params, url):
489 return self.create_page(
490 url, 'Reporting discrepancies in the data',
491 [P("""The data in this tracker is always in flux, as bugs are fixed and new
492 issues disclosed, the data contained herein is updated. We strive to
493 maintain complete and accurate state information, and appreciate any
494 updates in status, information or new issues."""),
495 P("There are three ways that you can report updates to this information:"),
496 make_numbered_list(
497 [P("""IRC: We can be found at """,
498 CODE("irc.oftc.net"),
499 ", ",
500 CODE("#debian-security"),
501 """. If you have information to report, please go ahead and join
502 the channel and tell us. Please feel free to state the issue,
503 regardless if there is someone who has acknowledged you. Many of us
504 idle on this channel and may not be around when you join, but we read
505 the backlog and will see what you have said. If you require a
506 response, do not forget to let us know how to get a hold of you."""),
507 P("Mailing list: Our mailing list is: ",
508 A("mailto:debian-security-tracker@lists.debian.org",
509 "debian-security-tracker@lists.debian.org")),
510 P("""Helping out: We welcome people who wish to join us in tracking
511 issues. The process is designed to be easy to learn and participate,
512 please read our """,
513 A("http://svn.debian.org/wsvn/secure-testing/doc/narrative_introduction?op=file&rev=0&sc=0",
514 "Introduction"),
515 """ to get familiar with how things work. Join us on
516 our mailing list, and on IRC and request to be added to the Alioth """,
517 A("http://alioth.debian.org/projects/secure-testing/", "project"),
518 """. We are really quite friendly. If you have a
519 question about how things work, don't be afraid to ask, we would like
520 to improve our documentation and procedures, so feedback is welcome.""")])])
521
522 def page_source_package(self, path, params, url):
523 pkg = path[0]
524
525 def gen_versions():
526 for (releases, version) in self.db.getSourcePackageVersions(
527 self.db.cursor(), pkg):
528 yield ', '.join(releases), version
529 def gen_bug_list(lst):
530 for (bug, description) in lst:
531 yield self.make_xref(url, bug), description
532
533 return self.create_page(
534 url, "Information on source package " + pkg,
535 [make_menu(lambda x: x,
536 (self.url_pts(url, pkg),
537 pkg + ' in the Package Tracking System'),
538 (self.url_debian_bug_pkg(url, pkg),
539 pkg + ' in the Bug Tracking System'),
540 (self.url_testing_status(url, pkg),
541 pkg + ' in the testing migration checker')),
542 H2("Available versions"),
543 make_table(gen_versions(), caption=("Release", "Version")),
544
545 H2("Open issues"),
546 make_table(gen_bug_list(self.db.getBugsForSourcePackage
547 (self.db.cursor(), pkg, True)),
548 caption=('Bug', 'Description'),
549 replacement='No known open issues.'),
550
551 H2("Resolved issues"),
552 make_table(gen_bug_list(self.db.getBugsForSourcePackage
553 (self.db.cursor(), pkg, False)),
554 caption=('Bug', 'Description'),
555 replacement='No known resolved issues.')])
556
557 def page_status_release_stable_oldstable(self, release, params, url):
558 assert release in ('stable', 'oldstable')
559
560 bf = BugFilterNoDSA(params)
561
562 def gen():
563 old_pkg_name = ''
564 for (pkg_name, bug_name, archive, urgency, vulnerable, remote, no_dsa) in \
565 self.db.cursor().execute(
566 """SELECT package, bug, section, urgency, vulnerable, remote, no_dsa
567 FROM %s_status""" % release):
568 if bf.urgencyFiltered(urgency, vulnerable):
569 continue
570 if bf.remoteFiltered(remote):
571 continue
572 if bf.nodsaFiltered(no_dsa):
573 continue
574
575 if pkg_name == old_pkg_name:
576 pkg_name = ''
577 else:
578 old_pkg_name = pkg_name
579 if archive <> 'main':
580 pkg_name = "%s (%s)" % (pkg_name, archive)
581
582 if remote is None:
583 remote = '???'
584 elif remote:
585 remote = 'yes'
586 else:
587 remote = 'no'
588
589 if urgency.startswith('high'):
590 urgency = self.make_red(urgency)
591 elif vulnerable == 2:
592 urgency = self.make_purple(urgency)
593 else:
594 if no_dsa:
595 urgency = urgency + '*'
596
597 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
598
599 return self.create_page(
600 url, 'Vulnerable source packages in the %s suite' % release,
601 [bf.actions(url), BR(),
602 make_table(gen(), caption=("Package", "Bug", "Urgency", "Remote")),
603 P('''If a "*" is included in the urgency field, no DSA is planned
604 for this vulnerability.'''),
605 self.nvd_text])
606
607 def page_status_release_stable(self, path, params, url):
608 return self.page_status_release_stable_oldstable('stable', params, url)
609 def page_status_release_oldstable(self, path, params, url):
610 return self.page_status_release_stable_oldstable('oldstable',
611 params, url)
612
613 def page_status_release_testing(self, path, params, url):
614 bf = BugFilterNoDSA(params)
615
616 def gen():
617 old_pkg_name = ''
618 for (pkg_name, bug_name, archive, urgency, vulnerable,
619 sid_vulnerable, ts_fixed, remote, no_dsa) \
620 in self.db.cursor().execute(
621 """SELECT package, bug, section, urgency, vulnerable,
622 unstable_vulnerable, testing_security_fixed, remote, no_dsa
623 FROM testing_status"""):
624 if bf.urgencyFiltered(urgency, vulnerable):
625 continue
626 if bf.remoteFiltered(remote):
627 continue
628 if bf.nodsaFiltered(no_dsa):
629 continue
630
631 if pkg_name == old_pkg_name:
632 pkg_name = ''
633 else:
634 old_pkg_name = pkg_name
635 if archive <> 'main':
636 pkg_name = "%s (%s)" % (pkg_name, archive)
637
638 if remote is None:
639 remote = '???'
640 elif remote:
641 remote = 'yes'
642 else:
643 remote = 'no'
644
645 if ts_fixed:
646 status = 'fixed in testing-security'
647 else:
648 if sid_vulnerable:
649 status = self.make_red('unstable is vulnerable')
650 else:
651 status = self.make_dangerous('fixed in unstable')
652
653 if urgency.startswith('high'):
654 urgency = self.make_red(urgency)
655 elif vulnerable == 2:
656 urgency = self.make_purple(urgency)
657
658 yield (pkg_name, self.make_xref(url, bug_name),
659 urgency, remote, status)
660
661 return self.create_page(
662 url, 'Vulnerable source packages in the testing suite',
663 [make_menu(url.scriptRelative,
664 ("status/dtsa-candidates", "Candidates for DTSAs")),
665 bf.actions(url), BR(),
666 make_table(gen(), caption=("Package", "Bug", "Urgency", "Remote")),
667 self.nvd_text])
668
669 def page_status_release_unstable_like(self, path, params, url,
670 rel, title):
671 bf = BugFilter(params)
672
673 def gen():
674 old_pkg_name = ''
675 for (pkg_name, bug_name, section, urgency, vulnerable, remote) \
676 in self.db.cursor().execute(
677 """SELECT DISTINCT sp.name, st.bug_name,
678 sp.archive, st.urgency, st.vulnerable,
679 (SELECT range_remote FROM nvd_data
680 WHERE cve_name = st.bug_name)
681 FROM source_package_status AS st, source_packages AS sp
682 WHERE st.vulnerable AND sp.rowid = st.package
683 AND sp.release = ? AND sp.subrelease = ''
684 ORDER BY sp.name, st.bug_name""", (rel,)):
685 if bf.urgencyFiltered(urgency, vulnerable):
686 continue
687 if bf.remoteFiltered(remote):
688 continue
689
690 if pkg_name == old_pkg_name:
691 pkg_name = ''
692 else:
693 old_pkg_name = pkg_name
694 if section <> 'main':
695 pkg_name = "%s (%s)" % (pkg_name, section)
696 else:
697 pkg_name = self.make_xref(url, pkg_name)
698
699 if remote is None:
700 remote = '???'
701 elif remote:
702 remote = 'yes'
703 else:
704 remote = 'no'
705
706 if urgency.startswith('high'):
707 urgency = self.make_red(urgency)
708 elif vulnerable == 2:
709 urgency = self.make_purple(urgency)
710
711 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
712
713 return self.create_page(
714 url, title,
715 [P("""Note that the list below is based on source packages.
716 This means that packages are not listed here once a new,
717 fixed source version has been uploaded to the archive, even
718 if there are still some vulnerably binary packages present
719 in the archive."""),
720 bf.actions(url), BR(),
721 make_table(gen(), caption=('Package', 'Bug', 'Urgency', 'Remote')),
722 self.nvd_text])
723
724 def page_status_release_unstable(self, path, params, url):
725 return self.page_status_release_unstable_like(
726 path, params, url,
727 title='Vulnerable source packages in the unstable suite',
728 rel='sid')
729
730 def page_status_release_stable_backports(self, path, params, url):
731 return self.page_status_release_unstable_like(
732 path, params, url,
733 title='Vulnerable source packages among backports for stable',
734 rel='squeeze-backports')
735
736 def page_status_release_oldstable_backports(self, path, params, url):
737 return self.page_status_release_unstable_like(
738 path, params, url,
739 title='Vulnerable source packages among backports for oldstable',
740 rel='lenny-backports')
741
742 def page_status_dtsa_candidates(self, path, params, url):
743 bf = BugFilter(params)
744
745 def gen():
746 old_pkg_name = ''
747 for (pkg_name, bug_name, archive, urgency, vulnerable,
748 stable_later, remote) \
749 in self.db.cursor().execute(
750 """SELECT package, bug, section, urgency, vulnerable,
751 (SELECT testing.version_id < stable.version_id
752 FROM source_packages AS testing, source_packages AS stable
753 WHERE testing.name = testing_status.package
754 AND testing.release = 'wheezy'
755 AND testing.subrelease = ''
756 AND testing.archive = testing_status.section
757 AND stable.name = testing_status.package
758 AND stable.release = 'squeeze'
759 AND stable.subrelease = 'security'
760 AND stable.archive = testing_status.section),
761 (SELECT range_remote FROM nvd_data
762 WHERE cve_name = bug)
763 FROM testing_status
764 WHERE (NOT unstable_vulnerable)
765 AND (NOT testing_security_fixed)"""):
766 if bf.urgencyFiltered(urgency, vulnerable):
767 continue
768 if bf.remoteFiltered(remote):
769 continue
770
771 if pkg_name == old_pkg_name:
772 pkg_name = ''
773 migration = ''
774 else:
775 old_pkg_name = pkg_name
776 migration = A(self.url_testing_status(url, pkg_name),
777 "check")
778 if archive <> 'main':
779 pkg_name = "%s (%s)" % (pkg_name, archive)
780 else:
781 pkg_name = self.make_source_package_ref(url, pkg_name)
782
783 if remote is None:
784 remote = '???'
785 elif remote:
786 remote = 'yes'
787 else:
788 remote = 'no'
789
790 if urgency.startswith('high'):
791 urgency = self.make_red(urgency)
792 elif vulnerable == 2:
793 urgency = self.make_purple(urgency)
794
795 if stable_later:
796 notes = "(fixed in stable?)"
797 else:
798 notes = ''
799
800 yield (pkg_name, migration, self.make_xref(url, bug_name),
801 urgency, remote, notes)
802
803 return self.create_page(
804 url, "Candidates for DTSAs",
805 [P("""The table below lists packages which are fixed
806 in unstable, but unfixed in testing. Use the testing migration
807 checker to find out why they have not entered testing yet."""),
808 make_menu(url.scriptRelative,
809 ("status/release/testing",
810 "List of vulnerable packages in testing")),
811 bf.actions(url), BR(),
812 make_table(gen(),
813 caption=("Package", "Migration", "Bug", "Urgency",
814 "Remote"))])
815
816 def page_status_todo(self, path, params, url):
817 hide_check = params.get('hide_check', False)
818 if hide_check:
819 flags = A(url.updateParamsDict({'hide_check' : None}),
820 'Show "check" TODOs')
821 else:
822 flags = A(url.updateParamsDict({'hide_check' : '1'}),
823 'Hide "check" TODOs')
824
825 def gen():
826 for (bug, description, note) in self.db.getTODOs(hide_check=hide_check):
827 yield self.make_xref(url, bug), description, note
828 return self.create_page(
829 url, 'Bugs with TODO items',
830 [P(flags), make_table(gen(), caption=('Bug', 'Description', 'Note'))])
831
832 def page_status_undetermined(self, path, params, url):
833 def gen():
834 outrel = []
835 old_bug = ''
836 old_pkg = ''
837 old_dsc = ''
838 last_displayed = ''
839 releases = ('sid', 'wheezy', 'squeeze', 'lenny')
840 for (pkg_name, bug_name, release, desc) in self.db.cursor().execute(
841 """SELECT DISTINCT sp.name, st.bug_name, sp.release,
842 bugs.description
843 FROM source_package_status AS st, source_packages AS sp, bugs
844 WHERE st.vulnerable == 2 AND sp.rowid = st.package
845 AND ( sp.release = ? OR sp.release = ? OR sp.release = ?
846 OR sp.release = ? )
847 AND sp.subrelease = '' AND st.bug_name == bugs.name
848 ORDER BY sp.name, st.bug_name""", releases):
849
850 if old_bug == '':
851 old_bug = bug_name
852 old_pkg = pkg_name
853 old_dsc = desc
854 elif old_bug != bug_name:
855 if old_pkg == last_displayed:
856 to_display = ''
857 else:
858 to_display = old_pkg
859 yield to_display, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
860 last_displayed = old_pkg
861 old_bug = bug_name
862 old_pkg = pkg_name
863 old_dsc = desc
864 outrel = []
865 outrel.append( release )
866 yield old_pkg, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
867
868 return self.create_page(url, 'Packages that may be vulnerable but need to be checked (undetermined issues)',
869 [P("""This page lists packages that may or may not be affected
870 by known issues. This means that some additional work needs to
871 be done to determined whether the package is actually
872 vulnerable or not. This list is a good area for new
873 contributors to make quick and meaningful contributions."""),
874 make_table(gen(), caption=('Package', 'Bug', 'Description', 'Releases'))])
875
876 def page_status_unimportant(self, path, params, url):
877 def gen():
878 outrel = []
879 old_bug = ''
880 old_pkg = ''
881 old_dsc = ''
882 old_name = ''
883 last_displayed = ''
884 releases = ('sid', 'wheezy', 'squeeze', 'lenny')
885 for (pkg_name, bug_name, release, desc) in self.db.cursor().execute(
886 """SELECT DISTINCT sp.name, st.bug_name, sp.release,
887 bugs.description
888 FROM source_package_status AS st, source_packages AS sp, bugs
889 WHERE st.vulnerable > 0 AND sp.rowid = st.package
890 AND ( sp.release = ? OR sp.release = ? OR sp.release = ?
891 OR sp.release = ? ) AND st.urgency == 'unimportant'
892 AND sp.subrelease = '' AND st.bug_name == bugs.name
893 ORDER BY sp.name, st.bug_name""", releases):
894
895 if old_bug == '':
896 old_bug = bug_name
897 old_pkg = pkg_name
898 old_dsc = desc
899 elif old_bug != bug_name:
900 if old_pkg == last_displayed:
901 to_display = ''
902 else:
903 to_display = old_pkg
904 yield to_display, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
905 last_displayed = old_pkg
906 old_bug = bug_name
907 old_pkg = pkg_name
908 old_dsc = desc
909 outrel = []
910 outrel.append( release )
911 yield old_pkg, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
912
913 return self.create_page(url, 'Packages that have open unimportant issues',
914 [P("""This page lists packages that are affected by issues
915 that are considered unimportant from a security perspective.
916 These issues are thought to be unexploitable or uneffective
917 in most situations (for example, browser denial-of-services)."""),
918 make_table(gen(), caption=('Package', 'Bug', 'Description', 'Releases'))])
919
920 def page_status_itp(self, path, params, url):
921 def gen():
922 old_pkg = ''
923 for pkg, bugs, debian_bugs in self.db.getITPs(self.db.cursor()):
924 if pkg == old_pkg:
925 pkg = ''
926 else:
927 old_pkg = pkg
928 yield (pkg, self.make_xref_list(url, bugs),
929 self.make_debian_bug_list(url, debian_bugs))
930 return self.create_page(
931 url, "ITPs with potential security issues",
932 [make_table(gen(), caption=("Package", "Issue", "Debian Bugs"),
933 replacement="No ITP bugs are currently known.")])
934
935 def page_data_unknown_packages(self, path, params, url):
936 def gen():
937 for name, bugs in self.db.getUnknownPackages(self.db.cursor()):
938 yield name, self.make_xref_list(url, bugs)
939 return self.create_page(
940 url, "Unknown packages",
941 [P("""Sometimes, a package referenced in a bug report
942 cannot be found in the database. This can be the result of a spelling
943 error, or a historic entry refers to a
944 package which is no longer in the archive."""),
945 make_table(gen(), caption=("Package", "Bugs"),
946 replacement="No unknown packages are referenced in the database.")])
947
948 def page_data_missing_epochs(self, path, params, url):
949 def gen():
950 old_bug = ''
951 old_pkg = ''
952 for bug, pkg, ver1, ver2 in self.db.cursor().execute(
953 """SELECT DISTINCT bug_name, n.package,
954 n.fixed_version, sp.version
955 FROM package_notes AS n, source_packages AS sp
956 WHERE n.package_kind = 'source'
957 AND n.fixed_version NOT LIKE '%:%'
958 AND n.fixed_version <> '0'
959 AND n.bug_origin = ''
960 AND sp.name = n.package
961 AND sp.version LIKE '%:%'
962 ORDER BY bug_name, package"""):
963 if bug == old_bug:
964 bug = ''
965 else:
966 old_bug = bug
967 old_pkg = ''
968 bug = self.make_xref(url, bug)
969 if pkg == old_pkg:
970 pkg = ''
971 else:
972 old_pkg = pkg
973 pkg = self.make_source_package_ref(url, pkg)
974 yield bug, pkg, ver1, ver2
975
976 return self.create_page(
977 url, "Missing epochs in package versions",
978 [make_table(gen(),
979 caption=("Bug", "Package", "Version 1", "Version 2"),
980 replacement="No source package version with missing epochs.")])
981
982 def page_data_latently_vulnerable(self, path, params, url):
983 def gen():
984 for pkg, bugs in self.db.cursor().execute(
985 """SELECT package, string_set(bug_name)
986 FROM package_notes AS p1
987 WHERE release <> ''
988 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
989 AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
990 WHERE p2.bug_name = p1.bug_name
991 AND p2.package = p1.package
992 AND release = '')
993 AND EXISTS (SELECT 1 FROM source_packages
994 WHERE name = p1.package AND release = 'sid')
995 GROUP BY package
996 ORDER BY package"""):
997 pkg = self.make_source_package_ref(url, pkg)
998 bugs = bugs.split(',')
999 yield pkg, self.make_xref_list(url, bugs)
1000
1001 def gen_unimportant():
1002 for pkg, bugs in self.db.cursor().execute(
1003 """SELECT package, string_set(bug_name)
1004 FROM package_notes AS p1
1005 WHERE release <> ''
1006 AND urgency <> 'unimportant'
1007 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
1008 AND EXISTS (SELECT 1 FROM package_notes AS p2
1009 WHERE p2.bug_name = p1.bug_name
1010 AND p2.package = p1.package
1011 AND release = '')
1012 AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
1013 WHERE p2.bug_name = p1.bug_name
1014 AND p2.package = p1.package
1015 AND urgency <> 'unimportant'
1016 AND release = '')
1017 AND EXISTS (SELECT 1 FROM source_packages
1018 WHERE name = p1.package AND release = 'sid')
1019 GROUP BY package
1020 ORDER BY package"""):
1021 pkg = self.make_source_package_ref(url, pkg)
1022 bugs = bugs.split(',')
1023 yield pkg, self.make_xref_list(url, bugs)
1024
1025 return self.create_page(
1026 url, "Latently vulnerable packages in unstable",
1027 [P(
1028 """A package is latently vulnerable in unstable if it is vulnerable in
1029 any release, and there is no package note for the same vulnerability
1030 and package in unstable (and the package is still available in
1031 unstable, of course)."""),
1032 make_table(gen(),
1033 caption=("Package", "Bugs"),
1034 replacement="No latently vulnerable packages were found."),
1035 P(
1036 """The next table lists issues which are marked unimportant for
1037 unstable, but for which release-specific annotations exist which are
1038 not unimportant."""),
1039 make_table(gen_unimportant(),
1040 caption=("Package", "Bugs"),
1041 replacement=
1042 "No packages with unimportant latent vulnerabilities were found."),
1043 ])
1044
1045 def page_data_releases(self, path, params, url):
1046 def gen():
1047 for (rel, subrel, archive, sources, archs) \
1048 in self.db.availableReleases():
1049 if sources:
1050 sources = 'yes'
1051 else:
1052 sources = 'no'
1053 yield rel, subrel, archive, sources, make_list(archs)
1054 return self.create_page(
1055 url, "Available releases",
1056 [P("""The security issue database is checked against
1057 the Debian releases listed in the table below."""),
1058 make_table(gen(),
1059 caption=("Release", "Subrelease", "Archive",
1060 "Sources", "Architectures"))])
1061
1062 def page_data_funny_versions(self, path, params, url):
1063 def gen():
1064 for name, release, archive, version, source_version \
1065 in self.db.getFunnyPackageVersions():
1066 yield name, release, archive, source_version, version
1067
1068 return self.create_page(
1069 url, "Version conflicts between source/binary packages",
1070 [P("""The table below lists source packages
1071 which have a binary package of the same name, but with a different
1072 version. This means that extra care is necessary to determine
1073 the version of a package which has been fixed. (Note that
1074 the bug tracker prefers source versions to binary versions
1075 in this case.)"""),
1076 make_table(gen(),
1077 caption=("Package",
1078 "Release",
1079 "Archive",
1080 "Source Version",
1081 "Binary Version")),
1082 P("""Technically speaking, these version numbering is fine,
1083 but it makes version-based bug tracking quite difficult for these packages."""),
1084 P("""There are many binary packages which are built from source
1085 packages with different version numbering schemes. However, as
1086 long as none of the binary packages carries the same name as the
1087 source package, most confusion is avoided or can be easily
1088 explained.""")])
1089
1090 def page_data_fake_names(self, path, params, url):
1091 def gen():
1092 for (bug, description) in self.db.getFakeBugs():
1093 yield self.make_xref(url, bug), description
1094 return self.create_page(
1095 url, "Automatically generated issue names",
1096 [P("""Some issues have not been assigned CVE names, but are still
1097 tracked by this database. In this case, the system automatically assigns
1098 a unique name. These names are not stable and can change when the database
1099 is updated, so they should not be used in external references."""),
1100 P('''The automatically generated names come in two flavors:
1101 the first kind starts with the string "''', CODE("TEMP-000000-"),
1102 '''". This means that no Debian bug has been assigned to this
1103 issue (or a bug has been created and is not recorded in this database).
1104 In the second kind of names, there is a Debian bug for the issue, and the "''',
1105 CODE("000000"), '''"part of the name is replaced with the
1106 Debian bug number.'''),
1107 make_table(gen(),
1108 caption=("Bug", "Description"))])
1109
1110 def page_data_pts(self, path, params, url):
1111 data = []
1112 for pkg, bugs in self.db.cursor().execute(
1113 """SELECT package, COUNT(DISTINCT bug) FROM
1114 (SELECT package, bug, urgency FROM stable_status
1115 UNION ALL SELECT DISTINCT sp.name, st.bug_name, st.urgency
1116 FROM source_package_status AS st, source_packages AS sp
1117 WHERE st.vulnerable AND st.urgency <> 'unimportant'
1118 AND sp.rowid = st.package AND sp.release = 'sid'
1119 AND sp.subrelease = '') x WHERE urgency <> 'unimportant'
1120 GROUP BY package ORDER BY package"""):
1121 data.append(pkg)
1122 data.append(':')
1123 data.append(str(bugs))
1124 data.append('\n')
1125 return BinaryResult(''.join(data))
1126
1127 def page_debsecan(self, path, params, url):
1128 obj = '/'.join(path)
1129 data = self.db.getDebsecan(obj)
1130 if data:
1131 return BinaryResult(data)
1132 else:
1133 return self.create_page(
1134 url, "Object not found",
1135 [P("The requested debsecan object has not been found.")],
1136 status=404)
1137
1138 def create_page(self, url, title, body, search_in_page=False, status=200):
1139 append = body.append
1140 append(HR())
1141 if not search_in_page:
1142 append(self.make_search_button(url))
1143 append(P(A(url.scriptRelative(""), "Home"),
1144 " - ", A(url.absolute("http://secure-testing.debian.net/"),
1145 "Testing Security Team"),
1146 " - ", A(url.absolute("http://www.debian.org/security/"),
1147 "Debian Security"),
1148 " - ", A(url.absolute
1149 ("http://www.enyo.de/fw/impressum.html"),
1150 "Imprint")))
1151 if search_in_page:
1152 on_load = "selectSearch()"
1153 else:
1154 on_load = None
1155 return HTMLResult(self.add_title(title, body,
1156 head_contents=self.head_contents,
1157 body_attribs={'onload': on_load}),
1158 doctype=self.html_dtd(),
1159 status=status)
1160
1161 def make_search_button(self, url):
1162 return FORM("Search for package or bug name: ",
1163 INPUT(type='text', name='query',
1164 onkeyup="onSearch(this.value)",
1165 onmousemove="onSearch(this.value)"),
1166 INPUT(type='submit', value='Go'),
1167 ' ',
1168 A(url.scriptRelative("data/report"), "Reporting problems"),
1169 method='get',
1170 action=url.scriptRelative(''))
1171
1172 def url_cve(self, url, name):
1173 return url.absolute("http://cve.mitre.org/cgi-bin/cvename.cgi",
1174 name=name)
1175 def url_nvd(self, url, name):
1176 return url.absolute("http://web.nvd.nist.gov/view/vuln/detail",
1177 vulnId=name)
1178 def url_rhbug(self, url, name):
1179 return url.absolute("https://bugzilla.redhat.com/show_bug.cgi",
1180 id=name)
1181
1182 def url_dsa(self, url, dsa, re_dsa=re.compile(r'^DSA-(\d+)(?:-\d+)?$')):
1183 match = re_dsa.match(dsa)
1184 if match:
1185 # We must determine the year because there is no generic URL.
1186 (number,) = match.groups()
1187 for (date,) in self.db.cursor().execute(
1188 "SELECT release_date FROM bugs WHERE name = ?", (dsa,)):
1189 (y, m, d) = date.split('-')
1190 return url.absolute("http://www.debian.org/security/%d/dsa-%d"
1191 % (int(y), int(number)))
1192 return None
1193
1194 def url_debian_bug(self, url, debian):
1195 return url.absolute("http://bugs.debian.org/cgi-bin/bugreport.cgi",
1196 bug=str(debian))
1197 def url_debian_bug_pkg(self, url, debian):
1198 return url.absolute("http://bugs.debian.org/cgi-bin/pkgreport.cgi",
1199 pkg=debian)
1200 def url_pts(self, url, package):
1201 return url.absolute("http://packages.qa.debian.org/common/index.html",
1202 src=package)
1203 def url_testing_status(self, url, package):
1204 return url.absolute("http://release.debian.org/migration/testing.pl",
1205 package=package)
1206 def url_source_package(self, url, package, full=False):
1207 if full:
1208 return url.scriptRelativeFull("source-package/" + package)
1209 else:
1210 return url.scriptRelative("source-package/" + package)
1211
1212 def make_xref(self, url, name):
1213 return A(url.scriptRelative(name), name)
1214
1215 def make_xref_list(self, url, lst, separator=', '):
1216 return make_list(map(lambda x: self.make_xref(url, x), lst), separator)
1217
1218 def make_debian_bug(self, url, debian):
1219 return A(self.url_debian_bug(url, debian), str(debian))
1220 def make_debian_bug_list(self, url, lst):
1221 return make_list(map(lambda x: self.make_debian_bug(url, x), lst))
1222
1223 def make_cve_ref(self, url, cve, name=None):
1224 if name is None:
1225 name = cve
1226 return A(self.url_cve(url, cve), name)
1227
1228 def make_nvd_ref(self, url, cve, name=None):
1229 if name is None:
1230 name = cve
1231 return A(self.url_nvd(url, cve), name)
1232
1233 def make_rhbug_ref(self, url, cve, name=None):
1234 if name is None:
1235 name = cve
1236 return A(self.url_rhbug(url, cve), name)
1237
1238 def make_dsa_ref(self, url, dsa, name=None):
1239 if name is None:
1240 name = dsa
1241 u = self.url_dsa(url, dsa)
1242 if u:
1243 return A(u, name)
1244 else:
1245 return name
1246
1247 def make_pts_ref(self, url, pkg, name=None):
1248 if name is None:
1249 name = pkg
1250 return A(self.url_pts(url, pkg), name)
1251
1252 def make_source_package_ref(self, url, pkg, title=None):
1253 if title is None:
1254 title = pkg
1255 return A(self.url_source_package(url, pkg), title)
1256
1257 def make_red(self, contents):
1258 return SPAN(contents, _class="red")
1259
1260 def make_purple(self, contents):
1261 return SPAN(contents, _class="purple")
1262
1263 def make_dangerous(self, contents):
1264 return SPAN(contents, _class="dangerous")
1265
1266 def pre_dispatch(self):
1267 self.db.refresh()
1268
1269 TrackerService(socket_name, db_name).run()

Properties

Name Value
svn:mime-type text/script

  ViewVC Help
Powered by ViewVC 1.1.5