esp-idf/tools/test_sbom/test_submodules.py
Frantisek Hrbata dbff74bee4 tools: add sbom information for submodules
This adds SBOM information for submodules, which are not managed
by Espressif. Meaning there is no fork for them in the espressif
namespace. Other submodules should add sbom.yml manifest file to
the root of their git repository.

The SBOM information for submodules is stored in the .gitmodules file.
Each SBOM related variable has the "sbom-" prefix and the following
variables may be used:

sbom-version:
   submodule version

sbom-cpe:
   CPE record if available in NVD. This will be used by the SBOM
   tool to check for possible submodule vulnerabilities. The
   version in the CPE can be replaced with the "{}" placeholder,
   which will be replaced by the "sbom-version" value from above.

sbom-supplier:
   Person or organization who is providing the submodule.
   It has to start with "Person:" or "Organization:" prefix
   as required by the SPDX-2.2 standard.

sbom-url:
   URL to the project if exists, e.g. github.

sbom-description:
   Project description.

sbom-hash:
   Submodule SHA as recorded in the git-tree. This field is used by
   CI to check that the submodule checkout hash and info in .gitmodules
   are in sync. IOW if submodule is updated and it has SBOM info in
   .gitmodules, the .gitmodules has to be updated too. The test is
   part of this commit. The checkout has of the submodule can be found
   by using "git submodule status".

Example for micro-ecc submodule
---8<---
[submodule "components/bootloader/subproject/components/micro-ecc/micro-ecc"]
	path = components/bootloader/subproject/components/micro-ecc/micro-ecc
	url = ../../kmackay/micro-ecc.git
	sbom-version = 1.0
	sbom-cpe = cpe:2.3🅰️micro-ecc_project:micro-ecc:{}:*:*:*:*:*:*:*
	sbom-supplier = Person: Ken MacKay
	sbom-url = https://github.com/kmackay/micro-ecc
	sbom-description = A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors
	sbom-hash = d037ec89546fad14b5c4d5456c2e23a71e554966
---8<---

Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
2023-06-14 12:38:12 +02:00

70 lines
2.6 KiB
Python

# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import os
from subprocess import run
from typing import Dict, List
def run_cmd(cmd: List[str]) -> str:
"""Simple helper to run command and return it's stdout."""
proc = run(cmd, capture_output=True, check=True, text=True)
return proc.stdout.strip()
def get_gitwdir() -> str:
"""Return absolute path to the current git working tree."""
return run_cmd(['git', 'rev-parse', '--show-toplevel'])
def get_submodules_info() -> List[Dict[str,str]]:
"""Return list of submodules, where each submodule is represented
as dictionary with name, path and hash keys."""
cmd = ['git', 'submodule', '--quiet', 'foreach','echo "$name,$sm_path,$sha1"']
out = run_cmd(cmd)
submodules = []
for line in out.splitlines():
name, sm_path, sha1 = line.split(',')
submodules += [{'name': name, 'path': sm_path, 'hash': sha1}]
return submodules
def get_submodules_config() -> Dict[str,str]:
"""Return dictionary, where key is variable name and value
is variable value in git's --list(dot) format. Only variables
starting with "submodule." are returned and this prefix is removed
to make it simple to match against the submodule info dictionary."""
gitmodules_fn = os.path.join(get_gitwdir(), '.gitmodules')
gitmodules_data = run_cmd(['git', 'config', '--list', '--file', gitmodules_fn])
prefix = 'submodule.'
config = {}
for line in gitmodules_data.splitlines():
var, val = line.split('=', maxsplit=1)
if not var.startswith(prefix):
continue
# remove "submodule." prefix
var = var[len(prefix):]
config[var] = val
return config
def test_sha() -> None:
""" Check that submodule SHA1 in git-tree and .gitmodules match
if sbom-hash variable is available in the .gitmodules file.
"""
submodules = get_submodules_info()
config = get_submodules_config()
for submodule in submodules:
sbom_hash = config.get(submodule['name'] + '.sbom-hash')
if not sbom_hash:
continue
msg = (f'Submodule \"{submodule["name"]}\" SHA \"{submodule["hash"]}\" in git '
f'tree does not match SHA \"{sbom_hash}\" recorded in .gitmodules. '
f'Please update \"sbom-hash\" in .gitmodules for \"{submodule["name"]}\" '
f'and also please do not forget to update version and other submodule '
f'information if necessary. It is important to keep this information '
f'up-to-date for SBOM generation.')
assert submodule['hash'] == sbom_hash, msg