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

Contents of /bin/tracker_service.py

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:mime-type text/script

  ViewVC Help
Powered by ViewVC 1.1.5