summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorPetr Štetiar <ynezz@true.cz>2023-10-24 08:27:13 +0000
committerPetr Štetiar <ynezz@true.cz>2023-11-01 11:14:41 +0000
commitd604a07225c5c82b942cd3374cc113ad676a2519 (patch)
tree716b236d86df66ca729fd12bba72b3f2a462d336 /scripts
parent649655f427932fe79b96a41f883c8054b1806191 (diff)
downloadopenwrt-d604a07225c5c82b942cd3374cc113ad676a2519.tar.gz
openwrt-d604a07225c5c82b942cd3374cc113ad676a2519.tar.bz2
openwrt-d604a07225c5c82b942cd3374cc113ad676a2519.zip
build: add CycloneDX SBOM JSON support
CycloneDX is an open source standard developed by the OWASP foundation. It supports a wide range of development ecosystems, a comprehensive set of use cases, and focuses on automation, ease of adoption, and progressive enhancement of SBOMs (Software Bill Of Materials) throughout build pipelines. So lets add support for CycloneDX SBOM for packages and images manifests. Signed-off-by: Petr Štetiar <ynezz@true.cz>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/metadata.pm40
-rwxr-xr-xscripts/package-metadata.pl187
2 files changed, 219 insertions, 8 deletions
diff --git a/scripts/metadata.pm b/scripts/metadata.pm
index a00d19f185..587ce7207d 100644
--- a/scripts/metadata.pm
+++ b/scripts/metadata.pm
@@ -2,7 +2,7 @@ package metadata;
use base 'Exporter';
use strict;
use warnings;
-our @EXPORT = qw(%package %vpackage %srcpackage %category %overrides clear_packages parse_package_metadata parse_target_metadata get_multiline @ignore %usernames %groupnames);
+our @EXPORT = qw(%package %vpackage %srcpackage %category %overrides clear_packages parse_package_metadata parse_package_manifest_metadata parse_target_metadata get_multiline @ignore %usernames %groupnames);
our %package;
our %vpackage;
@@ -317,4 +317,42 @@ sub parse_package_metadata($) {
return 1;
}
+sub parse_package_manifest_metadata($) {
+ my $file = shift;
+ my $pkg;
+ my %pkgs;
+
+ open FILE, "<$file" or do {
+ warn "Cannot open '$file': $!\n";
+ return undef;
+ };
+
+ while (<FILE>) {
+ chomp;
+ /^Package:\s*(.+?)\s*$/ and do {
+ $pkg = {};
+ $pkg->{name} = $1;
+ $pkg->{depends} = [];
+ $pkgs{$1} = $pkg;
+ };
+ /^Version:\s*(.+)\s*$/ and $pkg->{version} = $1;
+ /^Depends:\s*(.+)\s*$/ and $pkg->{depends} = [ split /\s+/, $1 ];
+ /^Source:\s*(.+)\s*$/ and $pkg->{source} = $1;
+ /^SourceName:\s*(.+)\s*$/ and $pkg->{sourcename} = $1;
+ /^License:\s*(.+)\s*$/ and $pkg->{license} = $1;
+ /^LicenseFiles:\s*(.+)\s*$/ and $pkg->{licensefiles} = $1;
+ /^Section:\s*(.+)\s*$/ and $pkg->{section} = $1;
+ /^SourceDateEpoch: \s*(.+)\s*$/ and $pkg->{sourcedateepoch} = $1;
+ /^CPE-ID:\s*(.+)\s*$/ and $pkg->{cpe_id} = $1;
+ /^Architecture:\s*(.+)\s*$/ and $pkg->{architecture} = $1;
+ /^Installed-Size:\s*(.+)\s*$/ and $pkg->{installedsize} = $1;
+ /^Filename:\s*(.+)\s*$/ and $pkg->{filename} = $1;
+ /^Size:\s*(\d+)\s*$/ and $pkg->{size} = $1;
+ /^SHA256sum:\s*(.*)\s*$/ and $pkg->{sha256sum} = $1;
+ }
+
+ close FILE;
+ return %pkgs;
+}
+
1;
diff --git a/scripts/package-metadata.pl b/scripts/package-metadata.pl
index e34b738c0f..9e0e6dd9e5 100755
--- a/scripts/package-metadata.pl
+++ b/scripts/package-metadata.pl
@@ -4,6 +4,8 @@ use lib "$FindBin::Bin";
use strict;
use metadata;
use Getopt::Long;
+use Time::Piece;
+use JSON::PP;
my %board;
@@ -622,6 +624,173 @@ END_JSON
print "[$json]";
}
+sub image_manifest_packages($)
+{
+ my %packages;
+ my $imgmanifest = shift;
+
+ open FILE, "<$imgmanifest" or return;
+ while (<FILE>) {
+ /^(.+?) - (.+)$/ and $packages{$1} = $2;
+ }
+ close FILE;
+
+ return %packages;
+}
+
+sub dump_cyclonedxsbom_json {
+ my (@components) = @_;
+
+ my $uuid = sprintf(
+ "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
+ rand(0xffff), rand(0xffff), rand(0xffff),
+ rand(0x0fff) | 0x4000,
+ rand(0x3fff) | 0x8000,
+ rand(0xffff), rand(0xffff), rand(0xffff)
+ );
+
+ my $cyclonedx = {
+ bomFormat => "CycloneDX",
+ specVersion => "1.4",
+ serialNumber => "urn:uuid:$uuid",
+ version => 1,
+ metadata => {
+ timestamp => gmtime->datetime,
+ },
+ "components" => [@components],
+ };
+
+ return encode_json($cyclonedx);
+}
+
+sub gen_image_cyclonedxsbom() {
+ my $pkginfo = shift @ARGV;
+ my $imgmanifest = shift @ARGV;
+ my @components;
+ my %image_packages;
+
+ %image_packages = image_manifest_packages($imgmanifest);
+ %image_packages or exit 1;
+ parse_package_metadata($pkginfo) or exit 1;
+
+ $package{"kernel"} = {
+ license => "GPL-2.0",
+ cpe_id => "cpe:/o:linux:linux_kernel",
+ name => "kernel",
+ };
+
+ my %abimap;
+ my @abipkgs = grep { defined $package{$_}->{abi_version} } keys %package;
+ foreach my $name (@abipkgs) {
+ my $pkg = $package{$name};
+ my $abipkg = $name . $pkg->{abi_version};
+ $abimap{$abipkg} = $name;
+ }
+
+ foreach my $name (sort {uc($a) cmp uc($b)} keys %image_packages) {
+ my $pkg = $package{$name};
+ if (!$pkg) {
+ $pkg = $package{$abimap{$name}};
+ next if !$pkg;
+ }
+
+ my @licenses;
+ my @license = split(/\s+/, $pkg->{license});
+ foreach my $lic (@license) {
+ push @licenses, (
+ { "license" => { "name" => $lic } }
+ );
+ }
+ my $type;
+ if ($pkg->{category}) {
+ my $category = $pkg->{category};
+ my %cat_type = (
+ "Firmware" => "firmware",
+ "Libraries" => "library"
+ );
+
+ if ($cat_type{$category}) {
+ $type = $cat_type{$category};
+ } else {
+ $type = "application";
+ }
+ }
+
+ my $version = $pkg->{version};
+ if ($image_packages{$name}) {
+ $version = $image_packages{$name};
+ }
+ $version =~ s/-\d+$// if $version;
+ if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
+ $version = $1;
+ }
+
+ push @components, {
+ name => $pkg->{name},
+ version => $version,
+ @licenses > 0 ? (licenses => [ @licenses ]) : (),
+ $pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
+ $type ? (type => $type) : (),
+ $version ? (version => $version) : (),
+ };
+ }
+
+ print dump_cyclonedxsbom_json(@components);
+}
+
+sub gen_package_cyclonedxsbom() {
+ my $pkgmanifest = shift @ARGV;
+ my @components;
+ my %mpkgs;
+
+ %mpkgs = parse_package_manifest_metadata($pkgmanifest);
+ %mpkgs or exit 1;
+
+ foreach my $name (sort {uc($a) cmp uc($b)} keys %mpkgs) {
+ my $pkg = $mpkgs{$name};
+
+ my @licenses;
+ my @license = split(/\s+/, $pkg->{license});
+ foreach my $lic (@license) {
+ push @licenses, (
+ { "license" => { "name" => $lic } }
+ );
+ }
+
+ my $type;
+ if ($pkg->{section}) {
+ my $section = $pkg->{section};
+ my %section_type = (
+ "firmware" => "firmware",
+ "libs" => "library"
+ );
+
+ if ($section_type{$section}) {
+ $type = $section_type{$section};
+ } else {
+ $type = "application";
+ }
+ }
+
+ my $version = $pkg->{version};
+ $version =~ s/-\d+$// if $version;
+ if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
+ $version = $1;
+ }
+
+ push @components, {
+ name => $name,
+ version => $version,
+ @licenses > 0 ? (licenses => [ @licenses ]) : (),
+ $pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
+ $type ? (type => $type) : (),
+ $version ? (version => $version) : (),
+ };
+ }
+
+ print dump_cyclonedxsbom_json(@components);
+}
+
sub parse_command() {
GetOptions("ignore=s", \@ignore);
my $cmd = shift @ARGV;
@@ -632,6 +801,8 @@ sub parse_command() {
/^source$/ and return gen_package_source();
/^pkgaux$/ and return gen_package_auxiliary();
/^pkgmanifestjson$/ and return gen_package_manifest_json();
+ /^imgcyclonedxsbom$/ and return gen_image_cyclonedxsbom();
+ /^pkgcyclonedxsbom$/ and return gen_package_cyclonedxsbom();
/^license$/ and return gen_package_license(0);
/^licensefull$/ and return gen_package_license(1);
/^usergroup$/ and return gen_usergroup_list();
@@ -639,15 +810,17 @@ sub parse_command() {
}
die <<EOF
Available Commands:
- $0 mk [file] Package metadata in makefile format
- $0 config [file] Package metadata in Kconfig format
+ $0 mk [file] Package metadata in makefile format
+ $0 config [file] Package metadata in Kconfig format
$0 kconfig [file] [config] [patchver] Kernel config overrides
- $0 source [file] Package source file information
- $0 pkgaux [file] Package auxiliary variables in makefile format
- $0 pkgmanifestjson [file] Package manifests in JSON format
- $0 license [file] Package license information
+ $0 source [file] Package source file information
+ $0 pkgaux [file] Package auxiliary variables in makefile format
+ $0 pkgmanifestjson [file] Package manifests in JSON format
+ $0 imgcyclonedxsbom <file> [manifest] Image package manifest in CycloneDX SBOM JSON format
+ $0 pkgcyclonedxsbom <file> Package manifest in CycloneDX SBOM JSON format
+ $0 license [file] Package license information
$0 licensefull [file] Package license information (full list)
- $0 usergroup [file] Package usergroup allocation list
+ $0 usergroup [file] Package usergroup allocation list
$0 version_filter [patchver] [list...] Filter list of version tagged strings
Options: