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

Contents of /bin/tracker_service.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 16214 - (show annotations) (download) (as text)
Mon Feb 21 04:09:38 2011 UTC (2 years, 3 months ago) by gilbert-guest
File MIME type: text/script
File size: 56050 byte(s)
show all DSAs on package pages (closes: #608994)
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 H2('Security Announcements'),
558 make_table(gen_bug_list(self.db.getDSAsForSourcePackage
559 (self.db.cursor(), pkg)),
560 caption=('DSA', 'Description'),
561 replacement='No known security announcements.')
562 ])
563
564 def page_status_release_stable_oldstable(self, release, params, url):
565 assert release in ('stable', 'oldstable')
566
567 bf = BugFilterNoDSA(params)
568
569 def gen():
570 old_pkg_name = ''
571 for (pkg_name, bug_name, archive, urgency, vulnerable, remote, no_dsa) in \
572 self.db.cursor().execute(
573 """SELECT package, bug, section, urgency, vulnerable, remote, no_dsa
574 FROM %s_status""" % release):
575 if bf.urgencyFiltered(urgency, vulnerable):
576 continue
577 if bf.remoteFiltered(remote):
578 continue
579 if bf.nodsaFiltered(no_dsa):
580 continue
581
582 if pkg_name == old_pkg_name:
583 pkg_name = ''
584 else:
585 old_pkg_name = pkg_name
586 if archive <> 'main':
587 pkg_name = "%s (%s)" % (pkg_name, archive)
588
589 if remote is None:
590 remote = '???'
591 elif remote:
592 remote = 'yes'
593 else:
594 remote = 'no'
595
596 if urgency.startswith('high'):
597 urgency = self.make_red(urgency)
598 elif vulnerable == 2:
599 urgency = self.make_purple(urgency)
600 else:
601 if no_dsa:
602 urgency = urgency + '*'
603
604 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
605
606 return self.create_page(
607 url, 'Vulnerable source packages in the %s suite' % release,
608 [bf.actions(url), BR(),
609 make_table(gen(), caption=("Package", "Bug", "Urgency", "Remote")),
610 P('''If a "*" is included in the urgency field, no DSA is planned
611 for this vulnerability.'''),
612 self.nvd_text])
613
614 def page_status_release_stable(self, path, params, url):
615 return self.page_status_release_stable_oldstable('stable', params, url)
616 def page_status_release_oldstable(self, path, params, url):
617 return self.page_status_release_stable_oldstable('oldstable',
618 params, url)
619
620 def page_status_release_testing(self, path, params, url):
621 bf = BugFilterNoDSA(params)
622
623 def gen():
624 old_pkg_name = ''
625 for (pkg_name, bug_name, archive, urgency, vulnerable,
626 sid_vulnerable, ts_fixed, remote, no_dsa) \
627 in self.db.cursor().execute(
628 """SELECT package, bug, section, urgency, vulnerable,
629 unstable_vulnerable, testing_security_fixed, remote, no_dsa
630 FROM testing_status"""):
631 if bf.urgencyFiltered(urgency, vulnerable):
632 continue
633 if bf.remoteFiltered(remote):
634 continue
635 if bf.nodsaFiltered(no_dsa):
636 continue
637
638 if pkg_name == old_pkg_name:
639 pkg_name = ''
640 else:
641 old_pkg_name = pkg_name
642 if archive <> 'main':
643 pkg_name = "%s (%s)" % (pkg_name, archive)
644
645 if remote is None:
646 remote = '???'
647 elif remote:
648 remote = 'yes'
649 else:
650 remote = 'no'
651
652 if ts_fixed:
653 status = 'fixed in testing-security'
654 else:
655 if sid_vulnerable:
656 status = self.make_red('unstable is vulnerable')
657 else:
658 status = self.make_dangerous('fixed in unstable')
659
660 if urgency.startswith('high'):
661 urgency = self.make_red(urgency)
662 elif vulnerable == 2:
663 urgency = self.make_purple(urgency)
664
665 yield (pkg_name, self.make_xref(url, bug_name),
666 urgency, remote, status)
667
668 return self.create_page(
669 url, 'Vulnerable source packages in the testing suite',
670 [make_menu(url.scriptRelative,
671 ("status/dtsa-candidates", "Candidates for DTSAs")),
672 bf.actions(url), BR(),
673 make_table(gen(), caption=("Package", "Bug", "Urgency", "Remote")),
674 self.nvd_text])
675
676 def page_status_release_unstable_like(self, path, params, url,
677 rel, title):
678 bf = BugFilter(params)
679
680 def gen():
681 old_pkg_name = ''
682 for (pkg_name, bug_name, section, urgency, vulnerable, remote) \
683 in self.db.cursor().execute(
684 """SELECT DISTINCT sp.name, st.bug_name,
685 sp.archive, st.urgency, st.vulnerable,
686 (SELECT range_remote FROM nvd_data
687 WHERE cve_name = st.bug_name)
688 FROM source_package_status AS st, source_packages AS sp
689 WHERE st.vulnerable AND sp.rowid = st.package
690 AND sp.release = ? AND sp.subrelease = ''
691 ORDER BY sp.name, st.bug_name""", (rel,)):
692 if bf.urgencyFiltered(urgency, vulnerable):
693 continue
694 if bf.remoteFiltered(remote):
695 continue
696
697 if pkg_name == old_pkg_name:
698 pkg_name = ''
699 else:
700 old_pkg_name = pkg_name
701 if section <> 'main':
702 pkg_name = "%s (%s)" % (pkg_name, section)
703 else:
704 pkg_name = self.make_xref(url, pkg_name)
705
706 if remote is None:
707 remote = '???'
708 elif remote:
709 remote = 'yes'
710 else:
711 remote = 'no'
712
713 if urgency.startswith('high'):
714 urgency = self.make_red(urgency)
715 elif vulnerable == 2:
716 urgency = self.make_purple(urgency)
717
718 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
719
720 return self.create_page(
721 url, title,
722 [P("""Note that the list below is based on source packages.
723 This means that packages are not listed here once a new,
724 fixed source version has been uploaded to the archive, even
725 if there are still some vulnerably binary packages present
726 in the archive."""),
727 bf.actions(url), BR(),
728 make_table(gen(), caption=('Package', 'Bug', 'Urgency', 'Remote')),
729 self.nvd_text])
730
731 def page_status_release_unstable(self, path, params, url):
732 return self.page_status_release_unstable_like(
733 path, params, url,
734 title='Vulnerable source packages in the unstable suite',
735 rel='sid')
736
737 def page_status_release_stable_backports(self, path, params, url):
738 return self.page_status_release_unstable_like(
739 path, params, url,
740 title='Vulnerable source packages among backports for stable',
741 rel='squeeze-backports')
742
743 def page_status_release_oldstable_backports(self, path, params, url):
744 return self.page_status_release_unstable_like(
745 path, params, url,
746 title='Vulnerable source packages among backports for oldstable',
747 rel='lenny-backports')
748
749 def page_status_dtsa_candidates(self, path, params, url):
750 bf = BugFilter(params)
751
752 def gen():
753 old_pkg_name = ''
754 for (pkg_name, bug_name, archive, urgency, vulnerable,
755 stable_later, remote) \
756 in self.db.cursor().execute(
757 """SELECT package, bug, section, urgency, vulnerable,
758 (SELECT testing.version_id < stable.version_id
759 FROM source_packages AS testing, source_packages AS stable
760 WHERE testing.name = testing_status.package
761 AND testing.release = 'wheezy'
762 AND testing.subrelease = ''
763 AND testing.archive = testing_status.section
764 AND stable.name = testing_status.package
765 AND stable.release = 'squeeze'
766 AND stable.subrelease = 'security'
767 AND stable.archive = testing_status.section),
768 (SELECT range_remote FROM nvd_data
769 WHERE cve_name = bug)
770 FROM testing_status
771 WHERE (NOT unstable_vulnerable)
772 AND (NOT testing_security_fixed)"""):
773 if bf.urgencyFiltered(urgency, vulnerable):
774 continue
775 if bf.remoteFiltered(remote):
776 continue
777
778 if pkg_name == old_pkg_name:
779 pkg_name = ''
780 migration = ''
781 else:
782 old_pkg_name = pkg_name
783 migration = A(self.url_testing_status(url, pkg_name),
784 "check")
785 if archive <> 'main':
786 pkg_name = "%s (%s)" % (pkg_name, archive)
787 else:
788 pkg_name = self.make_source_package_ref(url, pkg_name)
789
790 if remote is None:
791 remote = '???'
792 elif remote:
793 remote = 'yes'
794 else:
795 remote = 'no'
796
797 if urgency.startswith('high'):
798 urgency = self.make_red(urgency)
799 elif vulnerable == 2:
800 urgency = self.make_purple(urgency)
801
802 if stable_later:
803 notes = "(fixed in stable?)"
804 else:
805 notes = ''
806
807 yield (pkg_name, migration, self.make_xref(url, bug_name),
808 urgency, remote, notes)
809
810 return self.create_page(
811 url, "Candidates for DTSAs",
812 [P("""The table below lists packages which are fixed
813 in unstable, but unfixed in testing. Use the testing migration
814 checker to find out why they have not entered testing yet."""),
815 make_menu(url.scriptRelative,
816 ("status/release/testing",
817 "List of vulnerable packages in testing")),
818 bf.actions(url), BR(),
819 make_table(gen(),
820 caption=("Package", "Migration", "Bug", "Urgency",
821 "Remote"))])
822
823 def page_status_todo(self, path, params, url):
824 hide_check = params.get('hide_check', False)
825 if hide_check:
826 flags = A(url.updateParamsDict({'hide_check' : None}),
827 'Show "check" TODOs')
828 else:
829 flags = A(url.updateParamsDict({'hide_check' : '1'}),
830 'Hide "check" TODOs')
831
832 def gen():
833 for (bug, description, note) in self.db.getTODOs(hide_check=hide_check):
834 yield self.make_xref(url, bug), description, note
835 return self.create_page(
836 url, 'Bugs with TODO items',
837 [P(flags), make_table(gen(), caption=('Bug', 'Description', 'Note'))])
838
839 def page_status_undetermined(self, path, params, url):
840 def gen():
841 outrel = []
842 old_bug = ''
843 old_pkg = ''
844 old_dsc = ''
845 last_displayed = ''
846 releases = ('sid', 'wheezy', 'squeeze', 'lenny')
847 for (pkg_name, bug_name, release, desc) in self.db.cursor().execute(
848 """SELECT DISTINCT sp.name, st.bug_name, sp.release,
849 bugs.description
850 FROM source_package_status AS st, source_packages AS sp, bugs
851 WHERE st.vulnerable == 2 AND sp.rowid = st.package
852 AND ( sp.release = ? OR sp.release = ? OR sp.release = ?
853 OR sp.release = ? )
854 AND sp.subrelease = '' AND st.bug_name == bugs.name
855 ORDER BY sp.name, st.bug_name""", releases):
856
857 if old_bug == '':
858 old_bug = bug_name
859 old_pkg = pkg_name
860 old_dsc = desc
861 elif old_bug != bug_name:
862 if old_pkg == last_displayed:
863 to_display = ''
864 else:
865 to_display = old_pkg
866 yield to_display, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
867 last_displayed = old_pkg
868 old_bug = bug_name
869 old_pkg = pkg_name
870 old_dsc = desc
871 outrel = []
872 outrel.append( release )
873 yield old_pkg, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
874
875 return self.create_page(url, 'Packages that may be vulnerable but need to be checked (undetermined issues)',
876 [P("""This page lists packages that may or may not be affected
877 by known issues. This means that some additional work needs to
878 be done to determined whether the package is actually
879 vulnerable or not. This list is a good area for new
880 contributors to make quick and meaningful contributions."""),
881 make_table(gen(), caption=('Package', 'Bug', 'Description', 'Releases'))])
882
883 def page_status_unimportant(self, path, params, url):
884 def gen():
885 outrel = []
886 old_bug = ''
887 old_pkg = ''
888 old_dsc = ''
889 old_name = ''
890 last_displayed = ''
891 releases = ('sid', 'wheezy', 'squeeze', 'lenny')
892 for (pkg_name, bug_name, release, desc) in self.db.cursor().execute(
893 """SELECT DISTINCT sp.name, st.bug_name, sp.release,
894 bugs.description
895 FROM source_package_status AS st, source_packages AS sp, bugs
896 WHERE st.vulnerable > 0 AND sp.rowid = st.package
897 AND ( sp.release = ? OR sp.release = ? OR sp.release = ?
898 OR sp.release = ? ) AND st.urgency == 'unimportant'
899 AND sp.subrelease = '' AND st.bug_name == bugs.name
900 ORDER BY sp.name, st.bug_name""", releases):
901
902 if old_bug == '':
903 old_bug = bug_name
904 old_pkg = pkg_name
905 old_dsc = desc
906 elif old_bug != bug_name:
907 if old_pkg == last_displayed:
908 to_display = ''
909 else:
910 to_display = old_pkg
911 yield to_display, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
912 last_displayed = old_pkg
913 old_bug = bug_name
914 old_pkg = pkg_name
915 old_dsc = desc
916 outrel = []
917 outrel.append( release )
918 yield old_pkg, self.make_xref(url, old_bug), old_dsc, ', '.join(outrel)
919
920 return self.create_page(url, 'Packages that have open unimportant issues',
921 [P("""This page lists packages that are affected by issues
922 that are considered unimportant from a security perspective.
923 These issues are thought to be unexploitable or uneffective
924 in most situations (for example, browser denial-of-services)."""),
925 make_table(gen(), caption=('Package', 'Bug', 'Description', 'Releases'))])
926
927 def page_status_itp(self, path, params, url):
928 def gen():
929 old_pkg = ''
930 for pkg, bugs, debian_bugs in self.db.getITPs(self.db.cursor()):
931 if pkg == old_pkg:
932 pkg = ''
933 else:
934 old_pkg = pkg
935 yield (pkg, self.make_xref_list(url, bugs),
936 self.make_debian_bug_list(url, debian_bugs))
937 return self.create_page(
938 url, "ITPs with potential security issues",
939 [make_table(gen(), caption=("Package", "Issue", "Debian Bugs"),
940 replacement="No ITP bugs are currently known.")])
941
942 def page_data_unknown_packages(self, path, params, url):
943 def gen():
944 for name, bugs in self.db.getUnknownPackages(self.db.cursor()):
945 yield name, self.make_xref_list(url, bugs)
946 return self.create_page(
947 url, "Unknown packages",
948 [P("""Sometimes, a package referenced in a bug report
949 cannot be found in the database. This can be the result of a spelling
950 error, or a historic entry refers to a
951 package which is no longer in the archive."""),
952 make_table(gen(), caption=("Package", "Bugs"),
953 replacement="No unknown packages are referenced in the database.")])
954
955 def page_data_missing_epochs(self, path, params, url):
956 def gen():
957 old_bug = ''
958 old_pkg = ''
959 for bug, pkg, ver1, ver2 in self.db.cursor().execute(
960 """SELECT DISTINCT bug_name, n.package,
961 n.fixed_version, sp.version
962 FROM package_notes AS n, source_packages AS sp
963 WHERE n.package_kind = 'source'
964 AND n.fixed_version NOT LIKE '%:%'
965 AND n.fixed_version <> '0'
966 AND n.bug_origin = ''
967 AND sp.name = n.package
968 AND sp.version LIKE '%:%'
969 ORDER BY bug_name, package"""):
970 if bug == old_bug:
971 bug = ''
972 else:
973 old_bug = bug
974 old_pkg = ''
975 bug = self.make_xref(url, bug)
976 if pkg == old_pkg:
977 pkg = ''
978 else:
979 old_pkg = pkg
980 pkg = self.make_source_package_ref(url, pkg)
981 yield bug, pkg, ver1, ver2
982
983 return self.create_page(
984 url, "Missing epochs in package versions",
985 [make_table(gen(),
986 caption=("Bug", "Package", "Version 1", "Version 2"),
987 replacement="No source package version with missing epochs.")])
988
989 def page_data_latently_vulnerable(self, path, params, url):
990 def gen():
991 for pkg, bugs in self.db.cursor().execute(
992 """SELECT package, string_set(bug_name)
993 FROM package_notes AS p1
994 WHERE release <> ''
995 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
996 AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
997 WHERE p2.bug_name = p1.bug_name
998 AND p2.package = p1.package
999 AND release = '')
1000 AND EXISTS (SELECT 1 FROM source_packages
1001 WHERE name = p1.package AND release = 'sid')
1002 GROUP BY package
1003 ORDER BY package"""):
1004 pkg = self.make_source_package_ref(url, pkg)
1005 bugs = bugs.split(',')
1006 yield pkg, self.make_xref_list(url, bugs)
1007
1008 def gen_unimportant():
1009 for pkg, bugs in self.db.cursor().execute(
1010 """SELECT package, string_set(bug_name)
1011 FROM package_notes AS p1
1012 WHERE release <> ''
1013 AND urgency <> 'unimportant'
1014 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
1015 AND EXISTS (SELECT 1 FROM package_notes AS p2
1016 WHERE p2.bug_name = p1.bug_name
1017 AND p2.package = p1.package
1018 AND release = '')
1019 AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
1020 WHERE p2.bug_name = p1.bug_name
1021 AND p2.package = p1.package
1022 AND urgency <> 'unimportant'
1023 AND release = '')
1024 AND EXISTS (SELECT 1 FROM source_packages
1025 WHERE name = p1.package AND release = 'sid')
1026 GROUP BY package
1027 ORDER BY package"""):
1028 pkg = self.make_source_package_ref(url, pkg)
1029 bugs = bugs.split(',')
1030 yield pkg, self.make_xref_list(url, bugs)
1031
1032 return self.create_page(
1033 url, "Latently vulnerable packages in unstable",
1034 [P(
1035 """A package is latently vulnerable in unstable if it is vulnerable in
1036 any release, and there is no package note for the same vulnerability
1037 and package in unstable (and the package is still available in
1038 unstable, of course)."""),
1039 make_table(gen(),
1040 caption=("Package", "Bugs"),
1041 replacement="No latently vulnerable packages were found."),
1042 P(
1043 """The next table lists issues which are marked unimportant for
1044 unstable, but for which release-specific annotations exist which are
1045 not unimportant."""),
1046 make_table(gen_unimportant(),
1047 caption=("Package", "Bugs"),
1048 replacement=
1049 "No packages with unimportant latent vulnerabilities were found."),
1050 ])
1051
1052 def page_data_releases(self, path, params, url):
1053 def gen():
1054 for (rel, subrel, archive, sources, archs) \
1055 in self.db.availableReleases():
1056 if sources:
1057 sources = 'yes'
1058 else:
1059 sources = 'no'
1060 yield rel, subrel, archive, sources, make_list(archs)
1061 return self.create_page(
1062 url, "Available releases",
1063 [P("""The security issue database is checked against
1064 the Debian releases listed in the table below."""),
1065 make_table(gen(),
1066 caption=("Release", "Subrelease", "Archive",
1067 "Sources", "Architectures"))])
1068
1069 def page_data_funny_versions(self, path, params, url):
1070 def gen():
1071 for name, release, archive, version, source_version \
1072 in self.db.getFunnyPackageVersions():
1073 yield name, release, archive, source_version, version
1074
1075 return self.create_page(
1076 url, "Version conflicts between source/binary packages",
1077 [P("""The table below lists source packages
1078 which have a binary package of the same name, but with a different
1079 version. This means that extra care is necessary to determine
1080 the version of a package which has been fixed. (Note that
1081 the bug tracker prefers source versions to binary versions
1082 in this case.)"""),
1083 make_table(gen(),
1084 caption=("Package",
1085 "Release",
1086 "Archive",
1087 "Source Version",
1088 "Binary Version")),
1089 P("""Technically speaking, these version numbering is fine,
1090 but it makes version-based bug tracking quite difficult for these packages."""),
1091 P("""There are many binary packages which are built from source
1092 packages with different version numbering schemes. However, as
1093 long as none of the binary packages carries the same name as the
1094 source package, most confusion is avoided or can be easily
1095 explained.""")])
1096
1097 def page_data_fake_names(self, path, params, url):
1098 def gen():
1099 for (bug, description) in self.db.getFakeBugs():
1100 yield self.make_xref(url, bug), description
1101 return self.create_page(
1102 url, "Automatically generated issue names",
1103 [P("""Some issues have not been assigned CVE names, but are still
1104 tracked by this database. In this case, the system automatically assigns
1105 a unique name. These names are not stable and can change when the database
1106 is updated, so they should not be used in external references."""),
1107 P('''The automatically generated names come in two flavors:
1108 the first kind starts with the string "''', CODE("TEMP-000000-"),
1109 '''". This means that no Debian bug has been assigned to this
1110 issue (or a bug has been created and is not recorded in this database).
1111 In the second kind of names, there is a Debian bug for the issue, and the "''',
1112 CODE("000000"), '''"part of the name is replaced with the
1113 Debian bug number.'''),
1114 make_table(gen(),
1115 caption=("Bug", "Description"))])
1116
1117 def page_data_pts(self, path, params, url):
1118 data = []
1119 for pkg, bugs in self.db.cursor().execute(
1120 """SELECT package, COUNT(DISTINCT bug) FROM
1121 (SELECT package, bug, urgency FROM stable_status
1122 UNION ALL SELECT DISTINCT sp.name, st.bug_name, st.urgency
1123 FROM source_package_status AS st, source_packages AS sp
1124 WHERE st.vulnerable AND st.urgency <> 'unimportant'
1125 AND sp.rowid = st.package AND sp.release = 'sid'
1126 AND sp.subrelease = '') x WHERE urgency <> 'unimportant'
1127 GROUP BY package ORDER BY package"""):
1128 data.append(pkg)
1129 data.append(':')
1130 data.append(str(bugs))
1131 data.append('\n')
1132 return BinaryResult(''.join(data))
1133
1134 def page_debsecan(self, path, params, url):
1135 obj = '/'.join(path)
1136 data = self.db.getDebsecan(obj)
1137 if data:
1138 return BinaryResult(data)
1139 else:
1140 return self.create_page(
1141 url, "Object not found",
1142 [P("The requested debsecan object has not been found.")],
1143 status=404)
1144
1145 def create_page(self, url, title, body, search_in_page=False, status=200):
1146 append = body.append
1147 append(HR())
1148 if not search_in_page:
1149 append(self.make_search_button(url))
1150 append(P(A(url.scriptRelative(""), "Home"),
1151 " - ", A(url.absolute("http://secure-testing.debian.net/"),
1152 "Testing Security Team"),
1153 " - ", A(url.absolute("http://www.debian.org/security/"),
1154 "Debian Security"),
1155 " - ", A(url.absolute
1156 ("http://www.enyo.de/fw/impressum.html"),
1157 "Imprint")))
1158 if search_in_page:
1159 on_load = "selectSearch()"
1160 else:
1161 on_load = None
1162 return HTMLResult(self.add_title(title, body,
1163 head_contents=self.head_contents,
1164 body_attribs={'onload': on_load}),
1165 doctype=self.html_dtd(),
1166 status=status)
1167
1168 def make_search_button(self, url):
1169 return FORM("Search for package or bug name: ",
1170 INPUT(type='text', name='query',
1171 onkeyup="onSearch(this.value)",
1172 onmousemove="onSearch(this.value)"),
1173 INPUT(type='submit', value='Go'),
1174 ' ',
1175 A(url.scriptRelative("data/report"), "Reporting problems"),
1176 method='get',
1177 action=url.scriptRelative(''))
1178
1179 def url_cve(self, url, name):
1180 return url.absolute("http://cve.mitre.org/cgi-bin/cvename.cgi",
1181 name=name)
1182 def url_nvd(self, url, name):
1183 return url.absolute("http://web.nvd.nist.gov/view/vuln/detail",
1184 vulnId=name)
1185 def url_rhbug(self, url, name):
1186 return url.absolute("https://bugzilla.redhat.com/show_bug.cgi",
1187 id=name)
1188
1189 def url_dsa(self, url, dsa, re_dsa=re.compile(r'^DSA-(\d+)(?:-\d+)?$')):
1190 match = re_dsa.match(dsa)
1191 if match:
1192 # We must determine the year because there is no generic URL.
1193 (number,) = match.groups()
1194 for (date,) in self.db.cursor().execute(
1195 "SELECT release_date FROM bugs WHERE name = ?", (dsa,)):
1196 (y, m, d) = date.split('-')
1197 return url.absolute("http://www.debian.org/security/%d/dsa-%d"
1198 % (int(y), int(number)))
1199 return None
1200
1201 def url_debian_bug(self, url, debian):
1202 return url.absolute("http://bugs.debian.org/cgi-bin/bugreport.cgi",
1203 bug=str(debian))
1204 def url_debian_bug_pkg(self, url, debian):
1205 return url.absolute("http://bugs.debian.org/cgi-bin/pkgreport.cgi",
1206 pkg=debian)
1207 def url_pts(self, url, package):
1208 return url.absolute("http://packages.qa.debian.org/common/index.html",
1209 src=package)
1210 def url_testing_status(self, url, package):
1211 return url.absolute("http://release.debian.org/migration/testing.pl",
1212 package=package)
1213 def url_source_package(self, url, package, full=False):
1214 if full:
1215 return url.scriptRelativeFull("source-package/" + package)
1216 else:
1217 return url.scriptRelative("source-package/" + package)
1218
1219 def make_xref(self, url, name):
1220 return A(url.scriptRelative(name), name)
1221
1222 def make_xref_list(self, url, lst, separator=', '):
1223 return make_list(map(lambda x: self.make_xref(url, x), lst), separator)
1224
1225 def make_debian_bug(self, url, debian):
1226 return A(self.url_debian_bug(url, debian), str(debian))
1227 def make_debian_bug_list(self, url, lst):
1228 return make_list(map(lambda x: self.make_debian_bug(url, x), lst))
1229
1230 def make_cve_ref(self, url, cve, name=None):
1231 if name is None:
1232 name = cve
1233 return A(self.url_cve(url, cve), name)
1234
1235 def make_nvd_ref(self, url, cve, name=None):
1236 if name is None:
1237 name = cve
1238 return A(self.url_nvd(url, cve), name)
1239
1240 def make_rhbug_ref(self, url, cve, name=None):
1241 if name is None:
1242 name = cve
1243 return A(self.url_rhbug(url, cve), name)
1244
1245 def make_dsa_ref(self, url, dsa, name=None):
1246 if name is None:
1247 name = dsa
1248 u = self.url_dsa(url, dsa)
1249 if u:
1250 return A(u, name)
1251 else:
1252 return name
1253
1254 def make_pts_ref(self, url, pkg, name=None):
1255 if name is None:
1256 name = pkg
1257 return A(self.url_pts(url, pkg), name)
1258
1259 def make_source_package_ref(self, url, pkg, title=None):
1260 if title is None:
1261 title = pkg
1262 return A(self.url_source_package(url, pkg), title)
1263
1264 def make_red(self, contents):
1265 return SPAN(contents, _class="red")
1266
1267 def make_purple(self, contents):
1268 return SPAN(contents, _class="purple")
1269
1270 def make_dangerous(self, contents):
1271 return SPAN(contents, _class="dangerous")
1272
1273 def pre_dispatch(self):
1274 self.db.refresh()
1275
1276 TrackerService(socket_name, db_name).run()

Properties

Name Value
svn:mime-type text/script

  ViewVC Help
Powered by ViewVC 1.1.5