root/pkgcore-checks/pkgcore_checks/glsa_scan.py @ ferringb%2540gmail.com-20080624174036-q45fk4fxguj9kjlv

Revision ferringb%2540gmail.com-20080624174036-q45fk4fxguj9kjlv, 4.6 kB (checked in by Brian Harring <ferringb@…>, 7 months ago)

punt trailing whitespace

Line 
1# Copyright: 2006 Brian Harring <ferringb@gmail.com>
2# License: GPL2
3
4import os, optparse
5
6from pkgcore_checks import base
7from snakeoil.demandload import demandload
8demandload(globals(),
9    'pkgcore.pkgsets.glsa:GlsaDirSet',
10    'pkgcore.restrictions:packages,values',
11    'snakeoil:xml,osutils',
12    'pkgcore.restrictions.util:collect_package_restrictions',
13    'warnings'
14)
15
16
17class VulnerablePackage(base.Result):
18
19    """Packages marked as vulnerable by GLSAs"""
20
21    __slots__ = ("category", "package", "version", "arch", "glsa")
22    threshold = base.versioned_feed
23
24    def __init__(self, pkg, glsa):
25        base.Result.__init__(self)
26        self._store_cpv(pkg)
27        arches = set()
28        for v in collect_package_restrictions(glsa, ["keywords"]):
29            if isinstance(v.restriction, values.ContainmentMatch):
30                arches.update(x.lstrip("~") for x in v.restriction.vals)
31            else:
32                raise Exception("unexpected restriction sequence- %s in %s" %
33                    (v.restriction, glsa))
34        keys = set(x.lstrip("~") for x in pkg.keywords if not x.startswith("-"))
35        if arches:
36            self.arch = tuple(sorted(arches.intersection(keys)))
37            assert self.arch
38        else:
39            self.arch = tuple(sorted(keys))
40        self.glsa = str(glsa)
41
42    @property
43    def short_desc(self):
44        return "vulnerable via %s, keywords %s" % (self.glsa, self.arch)
45
46
47class TreeVulnerabilitiesReport(base.Template):
48    """
49    Scan for vulnerabile ebuilds in the tree
50
51    requires a GLSA directory for vuln. info
52    """
53
54    feed_type = base.versioned_feed
55    known_results = (VulnerablePackage,)
56
57    @staticmethod
58    def mangle_option_parser(parser):
59        parser.add_option(
60            "--glsa-dir", action='store', type='string', dest='glsa_location',
61            help="source directoy for glsas; tries to autodetermine it, may "
62            "be required if no glsa dirs are known")
63
64    @staticmethod
65    def check_values(values):
66        values.glsa_enabled = True
67        glsa_loc = values.glsa_location
68        if glsa_loc is not None:
69            if not os.path.isdir(glsa_loc):
70                raise optparse.OptionValueError("--glsa-dir '%r' doesn't "
71                    "exist" % glsa_loc)
72        else:
73            if not values.repo_bases:
74                raise optparse.OptionValueError(
75                    'Need a target repo or --overlayed-repo that is a single '
76                    'UnconfiguredTree for license checks')
77            for repo_base in values.repo_bases:
78                candidate = os.path.join(repo_base, "metadata", "glsa")
79                if os.path.isdir(candidate):
80                    if glsa_loc is None:
81                        glsa_loc = candidate
82                    else:
83                        raise optparse.OptionValueError(
84                            'multiple glsa sources is unsupported (detected '
85                            '%s and %s). Pick one with --glsa-dir.' % (
86                                glsa_loc, candidate))
87            if glsa_loc is None:
88                # form of 'optional' limiting; if they are using -c, force the
89                # error, else disable
90                if values.checks_to_run:
91                    raise optparse.OptionValueError("--glsa-dir must be "
92                        "specified, couldn't identify glsa src from %r" %
93                            values.src_repo)
94                values.glsa_enabled = False
95                warnings.warn("disabling GLSA checks due to no glsa source "
96                    "being found, and the check not being explicitly enabled; "
97                    "this behaviour may change")
98                return
99
100        values.glsa_location = osutils.abspath(glsa_loc)
101
102    def __init__(self, options):
103        base.Template.__init__(self, options)
104        self.options = options
105        self.glsa_dir = options.glsa_location
106        self.enabled = False
107        self.vulns = {}
108
109    def start(self):
110        if not self.options.glsa_enabled:
111            return
112        # this is a bit brittle
113        for r in GlsaDirSet(self.glsa_dir):
114            if len(r) > 2:
115                self.vulns.setdefault(r[0].key,
116                    []).append(packages.AndRestriction(*r[1:]))
117            else:
118                self.vulns.setdefault(r[0].key, []).append(r[1])
119
120    def finish(self, reporter):
121        self.vulns.clear()
122
123    def feed(self, pkg, reporter):
124        if not self.options.glsa_enabled:
125            return
126        for vuln in self.vulns.get(pkg.key, []):
127            if vuln.match(pkg):
128                reporter.add_report(VulnerablePackage(pkg, vuln))
Note: See TracBrowser for help on using the browser.