Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package build for openSUSE:Factory checked in at 2023-02-16 21:09:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/build (Old) and /work/SRC/openSUSE:Factory/.build.new.22824 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "build" Thu Feb 16 21:09:52 2023 rev:149 rq:1065954 version:20230215 Changes: -------- --- /work/SRC/openSUSE:Factory/build/build.changes 2023-02-10 14:34:04.253472567 +0100 +++ /work/SRC/openSUSE:Factory/.build.new.22824/build.changes 2023-02-16 21:09:54.088241696 +0100 @@ -1,0 +2,11 @@ +Wed Feb 15 12:35:03 UTC 2023 - Adrian Schröter <adr...@suse.de> + +- Support SBOM generation for KIWI VM images +- CycloneDX SBOM support added + +------------------------------------------------------------------- +Fri Feb 10 14:35:55 UTC 2023 - Adrian Schröter <adr...@suse.de> + +- added support for generating VCS url information into rpms + +------------------------------------------------------------------- Old: ---- obs-build-20230208.tar.gz New: ---- obs-build-20230215.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ build.spec ++++++ --- /var/tmp/diff_new_pack.BODjoL/_old 2023-02-16 21:09:55.140247429 +0100 +++ /var/tmp/diff_new_pack.BODjoL/_new 2023-02-16 21:09:55.144247451 +0100 @@ -28,7 +28,7 @@ Summary: A Script to Build SUSE Linux RPMs License: GPL-2.0-only OR GPL-3.0-only Group: Development/Tools/Building -Version: 20230208 +Version: 20230215 Release: 0 Source: obs-build-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build ++++++ PKGBUILD ++++++ --- /var/tmp/diff_new_pack.BODjoL/_old 2023-02-16 21:09:55.192247713 +0100 +++ /var/tmp/diff_new_pack.BODjoL/_new 2023-02-16 21:09:55.196247734 +0100 @@ -1,5 +1,5 @@ pkgname=build -pkgver=20230208 +pkgver=20230215 pkgrel=0 pkgdesc="Build packages in sandbox" arch=('i686' 'x86_64') ++++++ _service ++++++ --- /var/tmp/diff_new_pack.BODjoL/_old 2023-02-16 21:09:55.224247887 +0100 +++ /var/tmp/diff_new_pack.BODjoL/_new 2023-02-16 21:09:55.228247909 +0100 @@ -1,7 +1,7 @@ <services> <service name="tar_scm" mode="manual"> - <param name="revision">20230208</param> - <param name="version">20230208</param> + <param name="revision">20230215</param> + <param name="version">20230215</param> <param name="url">https://github.com/openSUSE/obs-build.git</param> <param name="scm">git</param> <param name="extract">dist/build.changes</param> ++++++ build.dsc ++++++ --- /var/tmp/diff_new_pack.BODjoL/_old 2023-02-16 21:09:55.252248040 +0100 +++ /var/tmp/diff_new_pack.BODjoL/_new 2023-02-16 21:09:55.256248062 +0100 @@ -1,6 +1,6 @@ Format: 1.0 Source: build -Version: 20230208 +Version: 20230215 Binary: build Maintainer: Adrian Schroeter <adr...@suse.de> Architecture: all ++++++ debian.changelog ++++++ --- /var/tmp/diff_new_pack.BODjoL/_old 2023-02-16 21:09:55.288248236 +0100 +++ /var/tmp/diff_new_pack.BODjoL/_new 2023-02-16 21:09:55.292248258 +0100 @@ -1,4 +1,4 @@ -build (20230208) unstable; urgency=low +build (20230215) unstable; urgency=low * Update to current git trunk - add sles11sp2 build config and adapt autodetection ++++++ obs-build-20230208.tar.gz -> obs-build-20230215.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/Build/Rpm.pm new/obs-build-20230215/Build/Rpm.pm --- old/obs-build-20230208/Build/Rpm.pm 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/Build/Rpm.pm 2023-02-15 13:29:25.000000000 +0100 @@ -976,6 +976,7 @@ "BUILDTIME" => 1006, "VENDOR" => 1011, "LICENSE" => 1014, + "URL" => 1020, "ARCH" => 1022, "OLDFILENAMES" => 1027, "FILEMODES" => 1030, @@ -1011,6 +1012,7 @@ "OLDENHANCESVERSION" => 1160, "OLDENHANCESFLAGS" => 1161, "FILEDIGESTALGO" => 5011, + "VCS" => 5034, "RECOMMENDNAME" => 5046, "RECOMMENDVERSION" => 5047, "RECOMMENDFLAGS" => 5048, @@ -1024,6 +1026,7 @@ "ENHANCEVERSION" => 5056, "ENHANCEFLAGS" => 5057, "MODULARITYLABEL" => 5096, + "UPSTREAMRELEASES" => 5101, ); sub rpmq { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/Build/Rpmmd.pm new/obs-build-20230215/Build/Rpmmd.pm --- old/obs-build-20230208/Build/Rpmmd.pm 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/Build/Rpmmd.pm 2023-02-15 13:29:25.000000000 +0100 @@ -117,6 +117,7 @@ name => { _text => 1, _end => \&generic_store_text, _tag => 'name' }, arch => { _text => 1, _end => \&generic_store_text, _tag => 'arch' }, version => { _start => \&primary_handle_version }, + url => { _text => 1, _end => \&generic_store_text, _tag => 'url' }, checksum => { _start => \&generic_store_attr, _attr => 'type', _tag => 'checksum', _text => 1, _end => \&primary_handle_checksum }, 'time' => { _start => \&primary_handle_time }, format => { @@ -212,6 +213,7 @@ delete $data->{'checksum'} unless $options->{'withchecksum'}; delete $data->{'license'} unless $options->{'withlicense'}; delete $data->{'vendor'} unless $options->{'withvendor'}; + delete $data->{'url'} unless $options->{'withurl'}; return generic_add_result(@_); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/build-recipe new/obs-build-20230215/build-recipe --- old/obs-build-20230208/build-recipe 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/build-recipe 2023-02-15 13:29:25.000000000 +0100 @@ -28,6 +28,7 @@ RPM_BUILD_IN_PLACE= RPM_RECIPE_IN_SUBDIR= RPM_NOPREP= +VCSURL= for i in spec dsc kiwi arch collax preinstallimage simpleimage mock livebuild snapcraft debootstrap debbuild appimage docker podman fissile helm flatpak mkosi; do . "$BUILD_DIR/build-recipe-$i" @@ -83,6 +84,11 @@ -rpm-noprep) RPM_NOPREP=1 ;; + -vcs) + needarg + VCSURL="$ARG" + shift + ;; -*) return 1 ;; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/build-recipe-docker new/obs-build-20230215/build-recipe-docker --- old/obs-build-20230208/build-recipe-docker 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/build-recipe-docker 2023-02-15 13:29:25.000000000 +0100 @@ -288,11 +288,11 @@ rm -rf "$BUILD_ROOT/$TOPDIR/SOURCES/repos/UPLOAD" # create sbom if requested - if test -n "$(queryconfig --dist "$BUILD_DIST" --configdir "$CONFIG_DIR" --archpath "$BUILD_ARCH" buildflags spdx)" ; then - echo "Generating sbom file" - generate_sbom "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.tar" > "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.sbom.json" - test -s "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.sbom.json" || rm -f "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.sbom.json" - fi + for format in $(queryconfig --dist "$BUILD_DIST" --configdir "$CONFIG_DIR" --archpath "$BUILD_ARCH" buildflags+ sbom) ; do + echo "Generating $format sbom file" + generate_sbom --format "$format" "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.tar" > "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.$format.json" + test -s "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.$format.json" || rm -f "$BUILD_ROOT$TOPDIR/DOCKER/$FILENAME.$format.json" + done # We're done. Clean up. recipe_cleanup_docker diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/build-recipe-kiwi new/obs-build-20230215/build-recipe-kiwi --- old/obs-build-20230208/build-recipe-kiwi 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/build-recipe-kiwi 2023-02-15 13:29:25.000000000 +0100 @@ -696,11 +696,11 @@ test -e "$r" && perl -I$BUILD_DIR -MBuild::Kiwi -e Build::Kiwi::showcontainerinfo -- "${args[@]}" $BUILD_ROOT/$TOPDIR/SOURCES/$RECIPEFILE "$r" > "${r%.tar}.containerinfo" if test -s "${r%.tar}.containerinfo" ; then # create sbom if requested - if test -n "$(queryconfig --dist "$BUILD_DIST" --configdir "$CONFIG_DIR" --archpath "$BUILD_ARCH" buildflags spdx)" ; then - echo "Generating sbom file for ${r##*/}" - generate_sbom "$r" > "${r%.tar}.sbom.json" - test -s "${r%.tar}.sbom.json" || rm -f "${r%.tar}.sbom.json" - fi + for format in $(queryconfig --dist "$BUILD_DIST" --configdir "$CONFIG_DIR" --archpath "$BUILD_ARCH" buildflags+ sbom) ; do + echo "Generating $format sbom file for ${r##*/}" + generate_sbom --format "$format" "$r" > "${r%.tar}.$format.json" + test -s "${r%.tar}.$format.json" || rm -f "${r%.tar}.$format.json" + done else rm -f "${r%.tar}.containerinfo" fi @@ -894,6 +894,13 @@ build_kiwi_appliance fi + # Hook for creating SBOM data + for format in $(queryconfig --dist "$BUILD_DIST" --configdir "$CONFIG_DIR" --archpath "$BUILD_ARCH" buildflags+ sbom) ; do + if ! chroot $BUILD_ROOT su -c "/.build/generate_sbom --format $format --dir $BUILD_ROOT/$TOPDIR/KIWIROOT-$imgtype > $BUILD_ROOT/$TOPDIR/OTHER/kiwi-$imgtype.${format}.json" ; then + cleanup_and_exit 1 "/usr/lib/build/generate_sbom script failed!" + fi + done + # Hook for running post kiwi build scripts like QA scripts if installed if test -x $BUILD_ROOT/usr/lib/build/kiwi_post_run ; then if ! chroot $BUILD_ROOT su -c /usr/lib/build/kiwi_post_run ; then diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/build-recipe-spec new/obs-build-20230215/build-recipe-spec --- old/obs-build-20230208/build-recipe-spec 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/build-recipe-spec 2023-02-15 13:29:25.000000000 +0100 @@ -62,6 +62,10 @@ args[${#args[@]}]="--obspackage" args[${#args[@]}]="$OBS_PACKAGE" fi + if test -n "$VCSURL" ; then + args[${#args[@]}]="--vcs" + args[${#args[@]}]="$VCSURL" + fi if test -n "$CHANGELOG" -a -f "$BUILD_ROOT/.build-changelog" ; then args[${#args[@]}]="--changelog" args[${#args[@]}]="$BUILD_ROOT/.build-changelog" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/build-vm new/obs-build-20230215/build-vm --- old/obs-build-20230208/build-vm 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/build-vm 2023-02-15 13:29:25.000000000 +0100 @@ -998,6 +998,7 @@ echo "CHANGELOG='${CHANGELOG//"'"/$Q}'" >> $BUILD_ROOT/.build/build.data echo "INCARNATION='${INCARNATION//"'"/$Q}'" >> $BUILD_ROOT/.build/build.data echo "DISTURL='${DISTURL//"'"/$Q}'" >> $BUILD_ROOT/.build/build.data + echo "VCSURL='${VCSURL//"'"/$Q}'" >> $BUILD_ROOT/.build/build.data echo "DO_INIT='${DO_INIT//"'"/$Q}'" >> $BUILD_ROOT/.build/build.data echo "DO_INIT_TOPDIR='${DO_INIT_TOPDIR//"'"/$Q}'" >> $BUILD_ROOT/.build/build.data echo "KIWI_PARAMETERS='${KIWI_PARAMETERS//"'"/$Q}'" >> $BUILD_ROOT/.build/build.data diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/dist/build.changes new/obs-build-20230215/dist/build.changes --- old/obs-build-20230208/dist/build.changes 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/dist/build.changes 2023-02-15 13:29:25.000000000 +0100 @@ -1,4 +1,14 @@ ------------------------------------------------------------------- +Wed Feb 15 07:41:28 UTC 2023 - Adrian Schröter <adr...@suse.de> + +- CycloneDX SBOM support added + +------------------------------------------------------------------- +Fri Feb 10 14:35:55 UTC 2023 - Adrian Schröter <adr...@suse.de> + +- added support for generating VCS url information into rpms + +------------------------------------------------------------------- Wed Feb 8 11:54:16 UTC 2023 - Adrian Schröter <adr...@suse.de> - SPDX SBOM generation for container and product builds diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/generate_sbom new/obs-build-20230215/generate_sbom --- old/obs-build-20230208/generate_sbom 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/generate_sbom 2023-02-15 13:29:25.000000000 +0100 @@ -35,33 +35,8 @@ use Build::Rpm; use Build::SimpleJSON; -my $spdx_json_template = { - '_order' => [ qw{spdxVersion dataLicense SPDXID name documentNamespace creationInfo packages files relationships} ], - 'creationInfo' => { - '_order' => [ qw{created creators licenseListVersion} ], - }, - 'packages' => { - '_order' => [ qw{name SPDXID versionInfo originator downloadLocation sourceInfo licenseConcluded licenseDeclared copyrightText externalRefs} ], - 'externalRefs' => { - '_order' => [ qw{referenceCategory referenceType referenceLocator} ], - }, - }, - 'files' => { - '_order' => [ qw{fileName SPDXID checksums licenseConcluded copyrightText comment} ], - }, - 'relationships' => { - '_order' => [ qw{spdxElementId relatedSpdxElement relationshipType} ], - }, -}; - -my $intoto_json_template = { - '_order' => [ qw{_type predicateType subject predicate} ], - 'subject' => { - '_order' => [ qw{name digest} ], - }, - 'predicate' => $spdx_json_template, -}; - +my $tool_name = 'obs_build_generate_sbom'; +my $tool_version = '1.0'; sub urlencode { my ($str, $iscgi) = @_; @@ -180,7 +155,9 @@ uncompress_container($container, "$dir/cont"); systemq('skopeo', 'copy', "docker-archive:$dir/cont", "oci:$dir/image:latest"); unlink("$dir/cont"); - systemq('umoci', 'unpack', '--image', "$dir/image:latest", "$dir/unpack"); + my @rootless; + push @rootless, '--rootless' if $>; + systemq('umoci', 'unpack', @rootless, '--image', "$dir/image:latest", "$dir/unpack"); return "$dir/unpack/rootfs"; } @@ -269,9 +246,9 @@ sub read_rpm { my ($rpm) = @_; - my %r = Build::Rpm::rpmq($rpm, qw{NAME VERSION RELEASE EPOCH ARCH LICENSE SOURCERPM DISTURL FILENAMES VENDOR FILEMODES FILEDIGESTS FILEDIGESTALGO SIGMD5}); + my %r = Build::Rpm::rpmq($rpm, qw{NAME VERSION RELEASE EPOCH ARCH LICENSE SOURCERPM DISTURL FILENAMES URL VENDOR FILEMODES FILEDIGESTS FILEDIGESTALGO SIGMD5}); delete $r{$_} for qw{BASENAMES DIRNAMES DIRINDEXES}; # save mem - for (qw{NAME VERSION RELEASE EPOCH ARCH LICENSE SOURCERPM DISTURL VENDOR FILEDIGESTALGO SIGMD5}) { + for (qw{NAME VERSION RELEASE EPOCH ARCH LICENSE SOURCERPM DISTURL URL VENDOR FILEDIGESTALGO SIGMD5}) { next unless $r{$_}; die("bad rpm entry for $_\n") unless ref($r{$_}) eq 'ARRAY' && @{$r{$_}} == 1; $r{$_} = $r{$_}->[0]; @@ -319,9 +296,9 @@ open($fh, '<', $primaryfile) || die("$primaryfile: $!\n"); } my @rpms; - for my $pkg (@{Build::Rpmmd::parse($fh, undef, 'withlicense' => 1, 'withchecksum' => 1, 'withvendor' => 1)}) { + for my $pkg (@{Build::Rpmmd::parse($fh, undef, 'withlicense' => 1, 'withchecksum' => 1, 'withvendor' => 1, 'withurl' => 1)}) { my $r = {}; - for (qw{name epoch version release arch vendor sourcerpm license checksum}) { + for (qw{name epoch version release arch url vendor sourcerpm license checksum}) { $r->{uc($_)} = $pkg->{$_} if defined $pkg->{$_}; } push @rpms, $r; @@ -372,10 +349,203 @@ return $purlurl; } +sub gen_uuid { + my $uuid = pack('H*', '1e9d579964de4594a4e835719a1c259f'); # uuid ns + $uuid = substr(Digest::SHA::sha1($uuid . Build::SimpleJSON::unparse($_[0], 'keepspecial' => 1)), 0, 16); + substr($uuid, 6, 1, pack('C', unpack('@6C', $uuid) & 0x0f | 0x50)); + substr($uuid, 8, 1, pack('C', unpack('@8C', $uuid) & 0x3f | 0x80)); + return join('-', unpack("H8H4H4H4H12", $uuid)); +} + +sub gen_pkg_id { + my ($p) = @_; + if ($p->{'SIGMD5'}) { + return unpack('H*', $p->{'SIGMD5'}); + } elsif ($p->{'CHECKSUM'}) { + my $id = $p->{'CHECKSUM'}; + $id =~ s/.*://; + return substr($id, 0, 32); + } + return Digest::MD5::md5_hex(Build::SimpleJSON::unparse($p)); +} + +################################################################################################## +# +# CycloneDX support +# + +my $cyclonedx_json_template_component = { + '_order' => [ qw{bom-ref type name version description cpe purl externalReferences properties } ], + 'externalReferences' => { '_order' => [ qw{url comment type} ] }, +}; + +my $cyclonedx_json_template = { + '_order' => [ qw{bomFormat specVersion serialNumber version metadata components services externalReferences dependencies compositions vulnerabilities signature} ], + 'version' => 'number', + 'metadata' => { + '_order' => [ qw{timestamp tools component} ], + 'tools' => { '_order' => [ qw{vendor name version } ] }. + 'component' => $cyclonedx_json_template_component, + }, + 'components' => $cyclonedx_json_template_component, + 'dependencies' => { '_order' => [ qw{ref dependsOn} ] } +}; + +sub cyclonedx_encode_pkg { + my ($p, $distro) = @_; + my $vr = $p->{'VERSION'}; + $vr = "$vr-$p->{'RELEASE'}" if defined $p->{'RELEASE'}; + my $cyc = { + 'type' => 'library', + 'name' => $p->{'NAME'}, + 'version' => $vr, + }; + $cyc->{'publisher'} = $p->{'VENDOR'} if $p->{'VENDOR'}; + my $license = $p->{'LICENSE'}; + if ($license) { + $license =~ s/ and / AND /g; + if ($license =~ /\s+/) { + push @{$cyc->{'licenses'}}, { 'expression' => $license }; + } else { + push @{$cyc->{'licenses'}}, { 'license' => {'id' => $p->{'LICENSE'} } }; + } + } + my $purlurl = gen_purl_rpm($p, $distro); + $cyc->{'purl'} = $purlurl if $purlurl; + if (!$p->{'cyc_id'}) { + $p->{'cyc_id'} = "pkg:$p->{'NAME'}-" . gen_pkg_id($p); + $p->{'cyc_id'} =~ s/[^a-zA-Z0-9\.\-]/-/g; + $p->{'cyc_id'} =~ s/-/:/; + } + $cyc->{'bom-ref'} = $p->{'cyc_id'}; + return $cyc; +} + +sub cyclonedx_encode_dist { + my ($dist) = @_; + my $cyc = { + 'type' => 'operating-system', + 'name' => $dist->{'id'}, + }; + $cyc->{'version'} = $dist->{'version_id'} if defined($dist->{'version_id'}) && $dist->{'version_id'} ne ''; + $cyc->{'description'} = $dist->{'pretty_name'} if $dist->{'pretty_name'}; + push @{$cyc->{'externalReferences'}}, { 'url' => $dist->{'bug_report_url'}, 'type' => 'issue-tracker' } if $dist->{'bug_report_url'}; + push @{$cyc->{'externalReferences'}}, { 'url' => $dist->{'home_url'}, 'type' => 'website' } if $dist->{'home_url'}; + return $cyc; +} + +sub cyclonedx_encode_header { + my ($subjectname) = @_; + my $cyc = { + 'bomFormat' => 'CycloneDX', + 'specVersion' => '1.4', + 'version' => 1, + 'metadata' => { + 'timestamp' => rfc3339time(time()), + 'tools' => [ {'name' => $tool_name, 'version' => $tool_version } ], + }, + }; + return $cyc; +} + +################################################################################################## +# +# SPDX support +# + +my $spdx_json_template = { + '_order' => [ qw{spdxVersion dataLicense SPDXID name documentNamespace creationInfo packages files relationships} ], + 'creationInfo' => { + '_order' => [ qw{created creators licenseListVersion} ], + }, + 'packages' => { + '_order' => [ qw{name SPDXID versionInfo originator downloadLocation sourceInfo homepage licenseConcluded licenseDeclared copyrightText externalRefs} ], + 'externalRefs' => { + '_order' => [ qw{referenceCategory referenceType referenceLocator} ], + }, + }, + 'files' => { + '_order' => [ qw{fileName SPDXID checksums licenseConcluded copyrightText comment} ], + }, + 'relationships' => { + '_order' => [ qw{spdxElementId relatedSpdxElement relationshipType} ], + }, +}; + +sub spdx_encode_pkg { + my ($p, $distro) = @_; + my $vr = $p->{'VERSION'}; + $vr = "$vr-$p->{'RELEASE'}" if defined $p->{'RELEASE'}; + my $evr = $vr; + $evr = "$p->{'EPOCH'}:$evr" if $p->{'EPOCH'}; + my $spdx = { + 'name' => $p->{'NAME'}, + 'versionInfo' => $evr, + }; + $spdx->{'originator'} = "Organization: $p->{'VENDOR'}" if $p->{'VENDOR'}; + $spdx->{'downloadLocation'} = 'NOASSERTION'; + $spdx->{'sourceInfo'} = 'acquired package info from RPM DB'; + $spdx->{'licenseConcluded'} = 'NOASSERTION'; + $spdx->{'licenseDeclared'} = 'NOASSERTION'; + my $license = $p->{'LICENSE'}; + if ($license) { + $license =~ s/ and / AND /g; + $spdx->{'licenseConcluded'} = $license; + $spdx->{'licenseDeclared'} = $license; + } + $spdx->{'copyrightText'} = 'NOASSERTION'; + $spdx->{'homepage'} = $p->{'URL'} if $p->{'URL'}; + my $purlurl = gen_purl_rpm($p, $distro); + push @{$spdx->{'externalRefs'}}, { 'referenceCategory' => 'PACKAGE-MANAGER', 'referenceType' => 'purl', 'referenceLocator', $purlurl } if $purlurl; + if (!$p->{'spdx_id'}) { + $p->{'spdx_id'} = "SPDXRef-Package-$p->{'NAME'}-" . gen_pkg_id($p); + $p->{'spdx_id'} =~ s/[^a-zA-Z0-9\.\-]/-/g; + } + $spdx->{'SPDXID'} = $p->{'spdx_id'}; + return $spdx; +} + +sub spdx_encode_file { + my ($f) = @_; + my $spdx = { + 'fileName' => $f->{'name'}, + 'licenseConcluded' => 'NOASSERTION', + 'copyrightText' => '', + }; + my @chks; + push @chks, { 'algorithm' => 'SHA256', 'checksumValue' => $f->{'sha256sum'} } if $f->{'sha256sum'}; + $spdx->{'checksums'} = \@chks if @chks; + $f->{'spdx_id'} = "SPDXRef-".Digest::MD5::md5_hex($f->{'name'}.($f->{'sha256sum'} || '')); + $spdx->{'SPDXID'} = $f->{'spdx_id'}; + return $spdx; +} + +sub spdx_encode_header { + my ($subjectname) = @_; + my $spdx = { + 'spdxVersion' => 'SPDX-2.3', + 'dataLicense' => 'CC0-1.0', + 'SPDXID' => 'SPDXRef-DOCUMENT', + 'name' => $subjectname, + }; + my $creationinfo = { + 'created' => rfc3339time(time()), + 'creators' => [ "Tool: $tool_name-$tool_version" ], + 'licenseListVersion' => '3.19', + }; + $spdx->{'creationInfo'} = $creationinfo; + return $spdx; +} + +################################################################################################## + + my $wrap_intoto; my $isproduct; +my $isdir; my $distro; my $rpmmd; +my $format; while (@ARGV && $ARGV[0] =~ /^-/) { my $opt = shift @ARGV; @@ -385,15 +555,21 @@ $wrap_intoto = 1; } elsif ($opt eq '--product') { $isproduct = 1; + } elsif ($opt eq '--dir') { + $isdir = 1; } elsif ($opt eq '--rpmmd') { $rpmmd = 1; + } elsif ($opt eq '--format') { + $format = shift @ARGV; } else { last if $opt eq '--'; die("unknown option: $opt\n"); } } +$format ||= 'spdx'; +die("unknown format $format\n") unless $format eq 'spdx' || $format eq 'cyclonedx'; -die("usage: generate_spdx_sbom [--disto NAME] [--intoto] [--product] PRODUCT_DIRECTORY|CONTAINER_TAR\n") unless @ARGV == 1; +die("usage: generate_sbom [--disto NAME] [--format spdx|cyclonedx] [--intoto] [--product DIRECTORY]|[--rpmmd DIRECTORY]|CONTAINER_TAR\n") unless @ARGV == 1; my $toprocess = $ARGV[0]; my $tmpdir = File::Temp::tempdir( CLEANUP => 1 ); @@ -409,6 +585,7 @@ require Build::Rpmmd; my $primary; if (-d $toprocess) { + $toprocess = "$toprocess/repodata" unless -f "$toprocess/repomd.xml"; my %d = map {$_->{'type'} => $_} @{Build::Rpmmd::parse_repomd("$toprocess/repomd.xml")}; my $primary = $d{'primary'}; die("no primary type in repomd.xml\n") unless $primary; @@ -418,12 +595,16 @@ } die("$toprocess: $!\n") unless -e $toprocess; $pkgs = read_pkgs_from_rpmmd($toprocess); +} elsif ($isdir) { + dump_rpmdb($toprocess, "$tmpdir/rpmdb"); + $files = gen_filelist($toprocess) if $format eq 'spdx'; + $pkgs = read_pkgs_rpmdb("$tmpdir/rpmdb"); + $dist = read_dist($toprocess); } else { # container tar case my $unpackdir = unpack_container($tmpdir, $toprocess); dump_rpmdb($unpackdir, "$tmpdir/rpmdb"); - - $files = gen_filelist($unpackdir); + $files = gen_filelist($unpackdir) if $format eq 'spdx'; $pkgs = read_pkgs_rpmdb("$tmpdir/rpmdb"); $dist = read_dist($unpackdir); } @@ -437,125 +618,78 @@ $distro .= "-$dist->{'build_id'}" if defined($dist->{'build_id'}) && $dist->{'build_id'} ne ''; } -my $spdx = { - 'spdxVersion' => 'SPDX-2.3', - 'dataLicense' => 'CC0-1.0', - 'SPDXID' => 'SPDXRef-DOCUMENT', - 'name' => $subjectname, -}; - -my $creationinfo = { - 'created' => rfc3339time(time()), - 'creators' => [ 'Tool: obs_build_generate_spdx_sbom-1.0' ], - 'licenseListVersion' => '3.19', -}; -$spdx->{'creationInfo'} = $creationinfo; - -for my $p (@$pkgs) { - my $vr = $p->{'VERSION'}; - $vr = "$vr-$p->{'RELEASE'}" if defined $p->{'RELEASE'}; - my $evr = $vr; - $evr = "$p->{'EPOCH'}:$evr" if $p->{'EPOCH'}; - my $spdxpkg = { - 'name' => $p->{'NAME'}, - 'versionInfo' => $evr, - }; - $spdxpkg->{'originator'} = "Organization: $p->{'VENDOR'}" if $p->{'VENDOR'}; - $spdxpkg->{'downloadLocation'} = 'NOASSERTION'; - $spdxpkg->{'sourceInfo'} = 'acquired package info from RPM DB'; - $spdxpkg->{'licenseConcluded'} = 'NOASSERTION'; - $spdxpkg->{'licenseDeclared'} = 'NOASSERTION'; - my $license = $p->{'LICENSE'}; - if ($license) { - $license =~ s/ and / AND /g; - $spdxpkg->{'licenseConcluded'} = $license; - $spdxpkg->{'licenseDeclared'} = $license; +my $json_template; +my $intoto_type; +my $doc; + +if ($format eq 'spdx') { + $json_template = $spdx_json_template; + $intoto_type = 'https://spdx.dev/Document'; + $doc = spdx_encode_header($subjectname); + for my $p (@$pkgs) { + push @{$doc->{'packages'}}, spdx_encode_pkg($p, $distro); } - $spdxpkg->{'copyrightText'} = 'NOASSERTION'; - my $purlurl = gen_purl_rpm($p, $distro); - my @xref; - push @xref, { 'referenceCategory' => 'PACKAGE-MANAGER', 'referenceType' => 'purl', 'referenceLocator', $purlurl } if $purlurl; - $spdxpkg->{'externalRefs'} = \@xref if @xref; - if (!$p->{'spdx_id'}) { - if ($p->{'SIGMD5'}) { - $p->{'spdx_id'} = "SPDXRef-Package-$p->{'NAME'}-".unpack('H*', $p->{'SIGMD5'}); - } elsif ($p->{'CHECKSUM'}) { - my $id = $p->{'CHECKSUM'}; - $id =~ s/.*://; - $id = substr($id, 0, 32); - $p->{'spdx_id'} = "SPDXRef-Package-$p->{'NAME'}-$id"; - } else { - my $id = Digest::MD5::md5_hex(Build::SimpleJSON::unparse($p)); - $p->{'spdx_id'} = "SPDXRef-Package-$p->{'NAME'}-$id"; - } - $p->{'spdx_id'} =~ s/[^a-zA-Z0-9\.\-]/-/g; + for my $f (@$files) { + next if $f->{'SKIP'}; + push @{$doc->{'files'}}, spdx_encode_file($f); } - $spdxpkg->{'SPDXID'} = $p->{'spdx_id'}; - push @{$spdx->{'packages'}}, $spdxpkg; -} -for my $f (@$files) { - next if $f->{'SKIP'}; - my $spdxfile = { - 'fileName' => $f->{'name'}, - 'licenseConcluded' => 'NOASSERTION', - 'copyrightText' => '', + if (@$files) { + my %f2p; + for my $p (@$pkgs) { + push @{$f2p{$_}}, $p for @{$p->{'FILENAMES'} || []}; + } + for my $f (@$files) { + next if $f->{'SKIP'}; + #warn("unpackaged file: $f->{'name'}\n") unless @{$f2p{$f->{'name'}} || []}; + for my $p (@{$f2p{$f->{'name'}} || []}) { + next unless $f->{'spdx_id'} && $p->{'spdx_id'}; + my $rel = { + 'spdxElementId' => $p->{'spdx_id'}, + 'relatedSpdxElement' => $f->{'spdx_id'}, + 'relationshipType', 'CONTAINS', + }; + push @{$doc->{'relationships'}}, $rel; + } + } + } + push @{$doc->{'relationships'}}, { + 'spdxElementId' => 'SPDXRef-DOCUMENT', + 'relatedSpdxElement' => 'SPDXRef-DOCUMENT', + 'relationshipType', 'DESCRIBES', }; - my @chks; - push @chks, { 'algorithm' => 'SHA256', 'checksumValue' => $f->{'sha256sum'} } if $f->{'sha256sum'}; - $spdxfile->{'checksums'} = \@chks if @chks; - $f->{'spdx_id'} = "SPDXRef-".Digest::MD5::md5_hex($f->{'name'}.($f->{'sha256sum'} || '')); - $spdxfile->{'SPDXID'} = $f->{'spdx_id'}; - push @{$spdx->{'files'}}, $spdxfile; -} - -if (@$files) { - my %f2p; + $doc->{'documentNamespace'} = 'http://open-build-service.org/spdx/'.urlencode($subjectname).'-'.gen_uuid($doc); +} elsif ($format eq 'cyclonedx') { + $json_template = $cyclonedx_json_template; + $intoto_type = 'https://cyclonedx.org/bom'; + $doc = cyclonedx_encode_header($subjectname); for my $p (@$pkgs) { - push @{$f2p{$_}}, $p for @{$p->{'FILENAMES'} || []}; + push @{$doc->{'components'}}, cyclonedx_encode_pkg($p, $distro); } - for my $f (@$files) { - next if $f->{'SKIP'}; - #warn("unpackaged file: $f->{'name'}\n") unless @{$f2p{$f->{'name'}} || []}; - for my $p (@{$f2p{$f->{'name'}} || []}) { - next unless $f->{'spdx_id'} && $p->{'spdx_id'}; - my $rel = { - 'spdxElementId' => $p->{'spdx_id'}, - 'relatedSpdxElement' => $f->{'spdx_id'}, - 'relationshipType', 'CONTAINS', - }; - push @{$spdx->{'relationships'}}, $rel; - } + if ($dist && %$dist) { + push @{$doc->{'components'}}, cyclonedx_encode_dist($dist); } + $doc->{'serialNumber'} = 'urn:uuid:'.gen_uuid($doc); +} else { + die("internal error\n"); } -push @{$spdx->{'relationships'}}, { - 'spdxElementId' => 'SPDXRef-DOCUMENT', - 'relatedSpdxElement' => 'SPDXRef-DOCUMENT', - 'relationshipType', 'DESCRIBES', -}; - -my $uuid = pack('H*', '1e9d579964de4594a4e835719a1c259f'); # uuid ns -$uuid = substr(Digest::SHA::sha1($uuid . Build::SimpleJSON::unparse($spdx, 'template' => $spdx_json_template, 'keepspecial' => 1)), 0, 16); -substr($uuid, 6, 1, pack('C', unpack('@6C', $uuid) & 0x0f | 0x50)); -substr($uuid, 8, 1, pack('C', unpack('@8C', $uuid) & 0x3f | 0x80)); -$uuid = join('-', unpack("H8H4H4H4H12", $uuid)); - -$spdx->{'documentNamespace'} = 'http://open-build-service.org/spdx/'.urlencode($subjectname).'-'.$uuid; - if ($wrap_intoto) { my $subject = { 'name' => $subjectname }; # no digest for products as it might be a directory. And an iso file would change the checksum later while signing. - $subject->{'digest'} = { 'sha256' => sha256file($toprocess) } unless $isproduct; - my $intoto = { + $subject->{'digest'} = { 'sha256' => sha256file($toprocess) } unless $isproduct || $isdir; + $doc = { '_type' => 'https://in-toto.io/Statement/v0.1', 'subject' => [ $subject ], - 'predicateType' => 'https://spdx.dev/Document', - 'predicate' => $spdx, + 'predicateType' => $intoto_type, + 'predicate' => $doc, + }; + $json_template = { + '_order' => [ qw{_type predicateType subject predicate} ], + 'subject' => { '_order' => [ qw{name digest} ] }, + 'predicate' => $json_template, }; - print Build::SimpleJSON::unparse($intoto, 'template' => $intoto_json_template, 'keepspecial' => 1)."\n"; -} else { - print Build::SimpleJSON::unparse($spdx, 'template' => $spdx_json_template, 'keepspecial' => 1)."\n"; } +print Build::SimpleJSON::unparse($doc, 'template' => $json_template, 'keepspecial' => 1)."\n"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-build-20230208/substitutedeps new/obs-build-20230215/substitutedeps --- old/obs-build-20230208/substitutedeps 2023-02-08 14:13:47.000000000 +0100 +++ new/obs-build-20230215/substitutedeps 2023-02-15 13:29:25.000000000 +0100 @@ -37,7 +37,7 @@ return @xspec && ref($xspec[0]) ? $xspec[0]->[1] : ''; } -my ($dist, $buildroot, $rpmdeps, $archs, $configdir, $release, $changelog, $buildflavor, $obspackage); +my ($dist, $buildroot, $rpmdeps, $archs, $configdir, $release, $changelog, $buildflavor, $obspackage, $vcsurl); $buildflavor = ''; # default to empty $configdir = ($::ENV{'BUILD_DIR'} || '/usr/lib/build') . '/configs'; @@ -63,6 +63,11 @@ $configdir = shift @ARGV; next; } + if ($ARGV[0] eq '--vcs') { + shift @ARGV; + $vcsurl = shift @ARGV; + next; + } if ($ARGV[0] eq '--release') { shift @ARGV; $release = shift @ARGV; @@ -85,7 +90,7 @@ } last; } -die("Usage: substitutedeps --dist <dist> --archpath <archpath> [--configdir <configdir>] <specin> <specout>\n") unless @ARGV == 2; +die("Usage: substitutedeps --dist <dist> --archpath <archpath> [--configdir <configdir>] [--vcs <url>] <specin> <specout>\n") unless @ARGV == 2; my $spec = $ARGV[0]; my $specdir = $spec; $specdir =~ s/[^\/]*$//; @@ -142,9 +147,13 @@ next; } + next if $vcsurl && $line =~ /^VCS\s*:/i; + if ($line =~ /^Name\s*:\s*(\S+)/i) { $pkg = $mainpkg = $1 unless $mainpkg; + $line .= "\nVCS: $vcsurl" if $vcsurl; } + if ($line =~ /^\s*%package\s+(-n\s+)?(\S+)/) { if ($1) { $pkg = $2;