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;

Reply via email to