Skip to content

Commit

Permalink
scripts: zephyr_module: Add modules dependencies spdx
Browse files Browse the repository at this point in the history
To track vulnerabilities from modules dependencies, a new SBOM,
`modules-deps.spdx` was created. It contains the `external-references`
provided by the modules. It allows to easily track vulnerabilities from
these external dependencies.

Signed-off-by: Thomas Gagneret <[email protected]>
  • Loading branch information
tgagneret-embedded committed Jan 30, 2024
1 parent 82c3046 commit 391ab83
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 1 deletion.
30 changes: 30 additions & 0 deletions doc/develop/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,36 @@ Build files located in a ``MODULE_EXT_ROOT`` can be described as:
This allows control of the build inclusion to be described externally to the
Zephyr module.

.. _modules-vulnerability-monitoring:

Vulnerability monitoring
========================

The module description file :file:`zephyr/module.yml` can be used to improve vulnerability monitoring.

If your module needs to track vulnerabilities using an external reference
(e.g your module is forked from an other repository), you can use the ``vulnerabilities`` section.
It contains the field ``external-references`` that contains a list of references that needs to
be monitored for your module. The supported format are:

- CPE (Common Platform Enumeration)
- PURL (Package URL)

.. code-block:: yaml
vulnerabilities:
external-references:
- <module-related-cpe>
- <an-other-module-related-cpe>
- <module-related-purl>
.. note::
CPE field must follow the CPE 2.3 schema provided by `NVD
<https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/cpe>`_.
PURL field must follow the PURL specification provided by `Github
<https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst>`_.


Build system integration
========================

Expand Down
2 changes: 2 additions & 0 deletions doc/develop/west/zephyr-cmds.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ This generates the following SPDX bill-of-materials (BOM) documents in
- :file:`app.spdx`: BOM for the application source files used for the build
- :file:`zephyr.spdx`: BOM for the specific Zephyr source code files used for the build
- :file:`build.spdx`: BOM for the built output files
- :file:`modules-deps.spdx`: BOM for modules dependencies. Check
:ref:`modules <modules-vulnerability-monitoring>` for more details.

Each file in the bill-of-materials is scanned, so that its hashes (SHA256 and
SHA1) can be recorded, along with any detected licenses if an
Expand Down
6 changes: 6 additions & 0 deletions scripts/west_commands/zspdx/sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,10 @@ def makeSPDX(cfg):
log.err("SPDX writer failed for build document; bailing")
return False

# write modules document
writeSPDX(os.path.join(cfg.spdxDir, "modules-deps.spdx"), w.docModulesExtRefs)
if not retval:
log.err("SPDX writer failed for build document; bailing")
return False

return True
41 changes: 40 additions & 1 deletion scripts/west_commands/zspdx/walker.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(self, cfg):
self.docZephyr = None
self.docApp = None
self.docSDK = None
self.docModulesExtRefs = None

# dict of absolute file path => the Document that owns that file
self.allFileLinks = {}
Expand Down Expand Up @@ -294,7 +295,7 @@ def setupZephyrDocument(self, zephyr, modules):

# set up zephyr sources package
cfgPackageZephyrModule = PackageConfig()
cfgPackageZephyrModule.name = module_name
cfgPackageZephyrModule.name = module_name + "-sources"
cfgPackageZephyrModule.spdxID = "SPDXRef-" + module_name + "-sources"
cfgPackageZephyrModule.relativeBaseDir = module_path

Expand Down Expand Up @@ -339,6 +340,42 @@ def setupSDKDocument(self):
# add it to pending relationships queue
self.pendingRelationships.append(rd)

def setupModulesDocument(self, modules):
# set up zephyr document
cfgModuleExtRef = DocumentConfig()
cfgModuleExtRef.name = "modules-deps"
cfgModuleExtRef.namespace = self.cfg.namespacePrefix + "/modules-deps"
cfgModuleExtRef.docRefID = "DocumentRef-modules-deps"
self.docModulesExtRefs = Document(cfgModuleExtRef)

for module in modules:
module_name = module.get("name", None)
module_vul = module.get("vulnerabilities", None)

if not module_name:
log.err(f"cannot find module name in meta file; bailing")
return False

module_name = self._normalize_module_name(module_name)

module_ext_ref = []
if module_vul:
module_ext_ref = module_vul.get("external-references")

# set up zephyr sources package
cfgPackageModuleExtRef = PackageConfig()
cfgPackageModuleExtRef.name = module_name + "-deps"
cfgPackageModuleExtRef.spdxID = "SPDXRef-" + module_name + "-deps"

for ref in module_ext_ref:
cfgPackageModuleExtRef.externalReferences.append(ref)

pkgModule = Package(cfgPackageModuleExtRef, self.docModulesExtRefs)
self.docModulesExtRefs.pkgs[pkgModule.cfg.spdxID] = pkgModule

self._add_describe_relationship(self.docModulesExtRefs, cfgPackageModuleExtRef)


# set up Documents before beginning
def setupDocuments(self):
log.dbg("setting up placeholder documents")
Expand All @@ -359,6 +396,8 @@ def setupDocuments(self):
if self.cfg.includeSDK:
self.setupSDKDocument()

self.setupModulesDocument(content["modules"])

return True

# walk through targets and gather information
Expand Down
11 changes: 11 additions & 0 deletions scripts/zephyr_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@
doc-url:
required: false
type: str
vulnerabilities:
required: false
type: map
mapping:
external-references:
required: false
type: seq
sequence:
- type: str
'''

MODULE_YML_PATH = PurePath('zephyr/module.yml')
Expand Down Expand Up @@ -548,6 +557,8 @@ def process_meta(zephyr_base, west_projs, modules, extra_modules=None,
for module in modules:
meta_module, dirty = _create_meta_project(module.project)
meta_module['name'] = module.meta.get('name')
if module.meta.get('vulnerabilities'):
meta_module['vulnerabilities'] = module.meta.get('vulnerabilities')
meta_projects.append(meta_module)

meta['modules'] = meta_projects
Expand Down

0 comments on commit 391ab83

Please sign in to comment.