def _parse_component(self, component):

        # get priority
        md = Component()
        md.priority = int(component.get('priority', '0'))

        # check type
        if component.get('type') != 'firmware':
            raise MetadataInvalid('<component type="firmware"> required')

        # get <id>
        try:
            md.appstream_id = _node_validate_text(component.xpath('id')[0],
                                                  minlen=10, maxlen=256)
            if not md.appstream_id:
                raise MetadataInvalid('<id> value invalid')
            for char in md.appstream_id:
                if char.isspace():
                    raise MetadataInvalid('<id> Cannot contain spaces')
                if char in ['/', '\\']:
                    raise MetadataInvalid('<id> Cannot contain slashes')
                if char not in ['-', '_', '.'] and not char.isalnum():
                    raise MetadataInvalid('<id> Cannot contain {}'.format(char))
            if len(md.appstream_id.split('.')) < 4:
                raise MetadataInvalid('<id> Should contain at least 4 sections to identify the model')
        except IndexError as e:
            raise MetadataInvalid('<id> tag missing') from e

        # get <developer_name>
        try:
            md.developer_name = _node_validate_text(component.xpath('developer_name')[0],
                                                    minlen=3, maxlen=50, nourl=True)
            if md.developer_name == 'LenovoLtd.':
                md.developer_name = 'Lenovo Ltd.'
            md.add_keywords_from_string(md.developer_name, priority=10)
        except IndexError as e:
            raise MetadataInvalid('<developer_name> tag missing') from e
        if md.developer_name.find('@') != -1 or md.developer_name.find('_at_') != -1:
            raise MetadataInvalid('<developer_name> cannot contain an email address')

        # get <name>
        try:
            md.name = _node_validate_text(component.xpath('name')[0],
                                          minlen=3, maxlen=500)
            md.add_keywords_from_string(md.name, priority=3)

            # use categories instead
            if self.is_strict:
                category = {
                    'system' : 'X-System',
                    'device' : 'X-Device',
                    'bios' : 'X-System',
                    'me' : 'X-ManagementEngine',
                    'embedded' : 'X-EmbeddedController',
                    'controller' : 'X-EmbeddedController',
                    'microcode' : 'X-CpuMicrocode',
                }
                words = [word.lower() for word in md.name.split(' ')]
                for search in category:
                    if search in words:
                        raise MetadataInvalid('<name> tag should not contain {}, use '
                                              '<categories><category>{}'
                                              '</category></categories> instead'.\
                                              format(search, category[search]))

                # tokens banned outright
                for search in ['firmware', 'update', '(r)', '(c)']:
                    if search in words:
                        raise MetadataInvalid('<name> tag should not contain '
                                              'the word "{}"'.format(search))

                # should not include the vendor in the name
                if md.developer_name_display:
                    if md.developer_name_display.lower() in words:
                        raise MetadataInvalid('<name> tag should not contain '
                                              'the vendor name "{}"'.format(md.developer_name_display))
        except IndexError as e:
            raise MetadataInvalid('<name> tag missing') from e

        # get <summary>
        try:
            md.summary = _node_validate_text(component.xpath('summary')[0],
                                             minlen=10, maxlen=500)
            md.add_keywords_from_string(md.summary, priority=1)
        except IndexError as e:
            raise MetadataInvalid('<summary> tag missing') from e

        # get optional <name_variant_suffix>
        try:
            md.name_variant_suffix = _node_validate_text(component.xpath('name_variant_suffix')[0],
                                                         minlen=2, maxlen=500)
        except IndexError as _:
            pass

        # get optional <description}
        try:
            md.description = _node_validate_text(component.xpath('description')[0],
                                                 minlen=25, maxlen=1000, nourl=True)
        except IndexError as _:
            pass

        # get <metadata_license>
        if self.is_strict:
            try:
                md.metadata_license = _node_validate_text(component.xpath('metadata_license')[0])
                if md.metadata_license not in ['CC0-1.0', 'FSFAP',
                                               'CC-BY-3.0', 'CC-BY-SA-3.0', 'CC-BY-4.0', 'CC-BY-SA-4.0',
                                               'GFDL-1.1', 'GFDL-1.2', 'GFDL-1.3']:
                    raise MetadataInvalid('Invalid <metadata_license> tag of {}'.format(md.metadata_license))
            except AttributeError as e:
                raise MetadataInvalid('<metadata_license> tag') from e
            except IndexError as e:
                raise MetadataInvalid('<metadata_license> tag missing') from e
        else:
            try:
                md.metadata_license = _node_validate_text(component.xpath('metadata_license')[0])
            except (AttributeError, IndexError) as _:
                md.metadata_license = 'CC0-1.0'

        # get <project_license>
        try:
            md.project_license = _node_validate_text(component.xpath('project_license')[0],
                                                     minlen=3, maxlen=50, nourl=True)
        except IndexError as e:
            raise MetadataInvalid('<project_license> tag missing') from e
        if not md.project_license:
            raise MetadataInvalid('<project_license> value invalid')

        # get <url type="homepage">
        try:
            md.url_homepage = _node_validate_text(component.xpath('url[@type="homepage"]')[0],
                                                  minlen=7, maxlen=1000)
        except IndexError as e:
            raise MetadataInvalid('<url type="homepage"> tag missing') from e
        if not md.url_homepage:
            raise MetadataInvalid('<url type="homepage"> value invalid')

        # add manually added keywords
        for keyword in component.xpath('keywords/keyword'):
            text = _node_validate_text(keyword, minlen=3, maxlen=50, nourl=True)
            if text.find(' ') != -1:
                raise MetadataInvalid('<keywords> cannot contain spaces')
            md.add_keywords_from_string(text, priority=5)

        # add provides
        for prov in component.xpath('provides/firmware[@type="flashed"]'):
            text = _node_validate_text(prov, minlen=5, maxlen=1000)
            if not _validate_guid(text):
                raise MetadataInvalid('The GUID {} was invalid.'.format(text))
            if text in ['230c8b18-8d9b-53ec-838b-6cfc0383493a',     # main-system-firmware
                        'f15aa55c-9cd5-5942-85ae-a6bf8740b96c',     # MST-panamera
                        'd6072785-6fc0-5f83-9d49-11376e7f48b1',     # MST-leaf
                        '49ec4eb4-c02b-58fc-8935-b1ee182405c7']:    # MST-tesla
                raise MetadataInvalid('The GUID {} is too generic'.format(text))
            md.guids.append(Guid(value=text))
        if not md.guids:
            raise MetadataInvalid('The metadata file did not provide any GUID.')

        # check the file didn't try to add it's own <require> on vendor-id
        # to work around the vendor-id security checks in fwupd
        if component.xpath('requires/firmware[text()="vendor-id"]'):
            raise MetadataInvalid('Firmware cannot specify vendor-id')

        # check only recognised requirements are added
        for req in component.xpath('requires/*'):
            if req.tag == 'firmware':
                text = _node_validate_text(req, minlen=3, maxlen=1000, allow_none=True)
                rq = Requirement(kind=req.tag,
                                 value=text,
                                 compare=req.get('compare'),
                                 version=req.get('version'),
                                 depth=req.get('depth', None))
                md.requirements.append(rq)
            elif req.tag == 'id':
                text = _node_validate_text(req, minlen=3, maxlen=1000)
                rq = Requirement(kind=req.tag,
                                 value=text,
                                 compare=req.get('compare'),
                                 version=req.get('version'))
                md.requirements.append(rq)
                if text == 'org.freedesktop.fwupd':
                    self.fwupd_min_version = req.get('version')
            elif req.tag == 'hardware':
                text = _node_validate_text(req, minlen=3, maxlen=1000)
                for req_value in text.split('|'):
                    rq = Requirement(kind=req.tag,
                                     value=req_value,
                                     compare=req.get('compare'),
                                     version=req.get('version'))
                    md.requirements.append(rq)
            else:
                raise MetadataInvalid('<{}> requirement was invalid'.format(req.tag))

        # from the first screenshot
        try:
            md.screenshot_caption = _node_validate_text(component.xpath('screenshots/screenshot/caption')[0],
                                                        minlen=8, maxlen=1000, nourl=True)
        except IndexError as _:
            pass
        try:
            md.screenshot_url = _node_validate_text(component.xpath('screenshots/screenshot/image')[0],
                                                    minlen=8, maxlen=1000)
        except IndexError as _:
            pass

        # allows OEM to hide the direct download link on the LVFS
        if component.xpath('custom/value[@key="LVFS::InhibitDownload"]'):
            md.inhibit_download = True

        # allows OEM to disable ignore all kinds of statistics on this firmware
        if component.xpath('custom/value[@key="LVFS::DoNotTrack"]'):
            md.fw.do_not_track = True

        # allows OEM to change the triplet (AA.BB.CCDD) to quad (AA.BB.CC.DD)
        try:
            version_format = _node_validate_text(component.xpath('custom/value[@key="LVFS::VersionFormat"]')[-1])
            if not self.version_formats:
                md.verfmt = Verfmt(value=version_format)
            elif version_format not in self.version_formats:
                raise MetadataInvalid('LVFS::VersionFormat can only be {}'.\
                                      format(','.join(self.version_formats.keys())))
            else:
                md.verfmt = self.version_formats[version_format]
        except IndexError as _:
            pass

        # enforce the VersionFormat if the version is an integer
        if self.is_strict and md.version:
            if md.version.isdigit() and not md.version_format:
                raise MetadataInvalid('LVFS::VersionFormat is required for integer version')

        # allows OEM to specify protocol
        try:
            text = _node_validate_text(component.xpath('custom/value[@key="LVFS::UpdateProtocol"]')[0])
            if not self.version_formats:
                md.protocol = Protocol(value=text)
            elif text not in self.protocol_map:
                raise MetadataInvalid('No valid UpdateProtocol {} found'.format(text))
            else:
                md.protocol_id = self.protocol_map[text]
        except IndexError as _:
            pass

        # allows OEM to set banned country codes
        try:
            text = _node_validate_text(component.xpath('custom/value[@key="LVFS::BannedCountryCodes"]')[0],
                                       minlen=2, maxlen=1000, nourl=True)
            self.fw.banned_country_codes = text
        except IndexError as _:
            pass

        # allows OEM to set the update message
        try:
            text = _node_validate_text(component.xpath('custom/value[@key="LVFS::UpdateMessage"]')[0],
                                       minlen=8, maxlen=1000, nourl=True)
            md.release_message = text
        except IndexError as _:
            pass

        # allows OEM to set the update image
        try:
            text = _node_validate_text(component.xpath('custom/value[@key="LVFS::UpdateImage"]')[0],
                                       minlen=8, maxlen=1000)
            md.release_image = text
        except IndexError as _:
            pass

        # should we parse the .inf file?
        try:
            text = _node_validate_text(component.xpath('custom/value[@key="LVFS::EnableInfParsing"]')[0],
                                       minlen=2, maxlen=10, nourl=True)
            if text == 'true':
                self.enable_inf_parsing = True
            elif text == 'false':
                self.enable_inf_parsing = False
            else:
                raise MetadataInvalid('LVFS::EnableInfParsing only allowed true or false, got {}'.format(text))
        except IndexError as _:
            pass

        # allows OEM to specify category
        for category in component.xpath('categories/category'):
            text = _node_validate_text(category, minlen=8, maxlen=50, nourl=True)
            if not self.category_map:
                md.category = Category(value=text)
                break
            if text in self.category_map:
                md.category_id = self.category_map[text]
                break

        # parse the default (first) release
        try:
            default_release = component.xpath('releases/release')[0]
        except IndexError as e:
            raise MetadataInvalid('The metadata file did not provide any releases') from e
        self._parse_release(md, default_release)

        # ensure the update description does not refer to a file in the archive
        if md.release_description:
            for word in md.release_description.split(' '):
                if word.find('.') == -1: # any word without a dot is not a fn
                    continue
                if word in self.cabarchive_upload:
                    raise MetadataInvalid('The release description should not reference other files.')

        # check the inf file matches up with the .xml file
        if self._version_inf and self._version_inf != md.version:
            raise MetadataInvalid('The inf Firmware_AddReg[HKR->FirmwareVersion] '
                                  '%s did not match the metainfo.xml value %s.'
                                  % (self._version_inf, md.version))

        # success
        return md
示例#2
0
        for md in fw.mds:
            if not md.blob:
                continue
            if self._require_test_for_md(md):
                _run_intelme_on_blob(self, test, md)
        db.session.commit()


# run with PYTHONPATH=. ./.env3/bin/python3 plugins/intelme/__init__.py ./firmware.bin
if __name__ == '__main__':
    import sys
    from lvfs.models import Firmware, Component, Protocol, Category

    plugin = Plugin()
    _test = Test('intelme')
    _fw = Firmware()
    _md = Component()
    _md.protocol = Protocol('org.uefi.capsule')
    _md.category = Category('X-ManagementEngine')
    _fw.mds.append(_md)

    with open(sys.argv[1], 'rb') as f:
        _md.blob = f.read()
    plugin.run_test_on_fw(_test, _fw)
    for attribute in _test.attributes:
        print(attribute)
    for _shard in _md.shards:
        if not _shard.checksums:
            continue
        print(_shard.info.guid, _shard.checksums[0])
#
# pylint: disable=protected-access,wrong-import-position

import os
import sys

# allows us to run this from the project root
sys.path.append(os.path.realpath('.'))

from lvfs.models import Test, Firmware, Component, Protocol, Category
from plugins.intelme import Plugin

if __name__ == '__main__':
    for _argv in sys.argv[1:]:
        print('Processing', _argv)
        plugin = Plugin('intelme')
        _test = Test(plugin_id=plugin.id)
        _fw = Firmware()
        _md = Component()
        _md.component_id = 999999
        _md.category = Category(value='X-ManagementEngine')
        _md.filename_contents = 'filename.bin'
        _md.protocol = Protocol(value='org.uefi.capsule')
        with open(_argv, 'rb') as _f:
            _md.blob = _f.read()
        plugin.run_test_on_md(_test, _md)
        for attribute in _test.attributes:
            print(attribute)
        for shard in _md.shards:
            print(shard.guid, shard.name, shard.checksum)