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

Contents of /bin/tracker_service.py

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:mime-type text/script

  ViewVC Help
Powered by ViewVC 1.1.5