Skip to content

csmock: add experimental --hermetic-build option#224

Draft
sfowl wants to merge 1 commit intomainfrom
exp-hermetic
Draft

csmock: add experimental --hermetic-build option#224
sfowl wants to merge 1 commit intomainfrom
exp-hermetic

Conversation

@sfowl
Copy link
Contributor

@sfowl sfowl commented Mar 9, 2026

Using this requires setting up the lockfile and local repo earlier using regular mock, as described here:

https://github.com/rpm-software-management/mock/blob/main/docs/feature-hermetic-builds.md

This requires that the required scanners are either provided by the host or present in the buildroot. For instance -t gcc will work only if gcc is a build dependency. -t cppcheck will work if the --use-host-cppcheck param is used.

PSSECAUT-1524

@sfowl sfowl requested a review from kdudka March 9, 2026 07:21
csmock/csmock Outdated
if not os.path.isdir(repo_dir):
parser.error("not a directory: %s" % repo_dir)
if not os.path.isdir(os.path.join(repo_dir, "repodata")):
parser.error("repo directory missing repodata/: %s" % repo_dir)

Check warning

Code scanning / vcs-diff-lint

main: Formatting a regular string which could be an f-string

main: Formatting a regular string which could be an f-string
@sfowl
Copy link
Contributor Author

sfowl commented Mar 9, 2026

This is an experimental feature that I'm opening now for early feedback. It can be tested locally with something like:

$ mock --calculate-build-dependencies -r rhel-10-x86_64 ./libecpg-16.1-16.el10.src.rpm
$ export lockfile=/var/lib/mock/rhel-10-x86_64/result/buildroot_lock.json
$ export myrepo=/var/lib/mock/myrepo
$ mock-hermetic-repo --lockfile $lockfile --output-repo $myrepo
$ ./csmock/csmock --hermetic-build $lockfile $myrepo ./libecpg-16.1-16.el10.src.rpm -t gcc,cppcheck --use-host-cppcheck

This would raise follow up questions like: how to handle scanners that aren't in the buildroot? I thought perhaps early experimental support could be expecting users to set that up with regular mock, otherwise just fail. I've experiment a bit though with making scanners like gcc conditional, i.e. only try to use it if in buildroot, otherwise skip.

Though this is linked to an internal jira, it's not a pressing part of any ongoing project, so not much urgency here.

Using this requires setting up the lockfile and local
repo earlier using regular mock, as described here:

https://github.com/rpm-software-management/mock/blob/main/docs/feature-hermetic-builds.md

This requires that the required scanners are either
provided by the host or present in the buildroot. For
instance `-t gcc` will work only if `gcc` is a build
dependency. `-t cppcheck` will work if the `--use-host-cppcheck`
param is used.

PSSECAUT-1524
@kdudka
Copy link
Member

kdudka commented Mar 9, 2026

I tried the above steps with a Fedora package and csmock failed in my env with a cryptic error message:

% csmock --hermetic-build $lockfile $myrepo ./units-2.26-1.fc45.src.rpm -t gcc,cppcheck --use-host-cppcheck                                                                                         EC=2  9:18:19
scan.ini: [scan]
scan.ini: tool = csmock
scan.ini: tool-version = csmock-3.8.4.20260309.172745.g5690d5f.exp_hermetic-1.fc42
scan.ini: tool-args = '/usr/bin/csmock' '--hermetic-build' '/var/lib/mock/fedora-42-x86_64/result/buildroot_lock.json' '/var/lib/mock/myrepo' './units-2.26-1.fc45.src.rpm' '-t' 'gcc,cppcheck' '--use-host-cppcheck'
scan.ini: host = f42
scan.ini: store-results-to = /home/kdudka/fedora/units/units-2.26-1.fc45.tar.xz
scan.ini: time-created = 2026-03-09 09:18:31
scan.ini: enabled-plugins = cppcheck, gcc
scan.ini: mock-config = hermetic-build
scan.ini: project-name = units-2.26-1.fc45
scan.ini: known-false-positives = /usr/share/csmock/known-false-positives.js
scan.ini: known-false-positives-rpm = known-false-positives-2.2.1.20260210.092522.gef164d24-1.fc42.noarch
>>> 2026-03-09 09:18:31 rpm -qa | sort -V > '/tmp/csmocksd76e5sw/units-2.26-1.fc45/debug/rpm-list-host.txt'

>>> 2026-03-09 09:18:32 "/usr/bin/mock" "--hermetic-build" "/var/lib/mock/fedora-42-x86_64/result/buildroot_lock.json" "/var/lib/mock/myrepo" "--short-circuit=prep" "-N" "./units-2.26-1.fc45.src.rpm"
INFO: mock.py version 6.0 starting (python version = 3.13.12, NVR = mock-6.0-1.osh.1.fc42), args: /usr/libexec/mock/mock --hermetic-build /var/lib/mock/fedora-42-x86_64/result/buildroot_lock.json /var/lib/mock/myrepo --short-circuit=prep -N ./units-2.26-1.fc45.src.rpm
Start(bootstrap): init plugins
INFO: selinux enabled
Finish(bootstrap): init plugins
Start: init plugins
INFO: selinux enabled
Finish: init plugins
INFO: Signal handler active
Start: run
Start: scrub ['all']
INFO: scrubbing everything for hermetic-build
Finish: scrub ['all']
INFO: Start(./units-2.26-1.fc45.src.rpm)  Config(hermetic-build)
Mock Version: 6.0
INFO: Mock Version: 6.0
Start(bootstrap): chroot init
INFO: calling preinit hooks
INFO: enabled package manager cache
Start(bootstrap): cleaning package manager metadata
Finish(bootstrap): cleaning package manager metadata
INFO: Guessed host environment type: unknown
INFO: Using container image: oci-archive:/var/lib/mock/myrepo/bootstrap.tar
INFO: Pulling image: oci-archive:/var/lib/mock/myrepo/bootstrap.tar
INFO: Tagging container image as mock-bootstrap-ba4c0b52-c91e-4b32-92fd-ad31c31398ec
INFO: Checking image digest: ed98a0a45126a633b8f514fd9f51ed93393e9ad414cb6f7b0ede84e5279477bc
INFO: Calculating mock-bootstrap-ba4c0b52-c91e-4b32-92fd-ad31c31398ec image OCI digest
INFO: Checking that e638a12b50b68a5fd799979a44aef6b24fb6decd78bc27a896b83b436d066170 image matches host's architecture
INFO: Copy content of container e638a12b50b68a5fd799979a44aef6b24fb6decd78bc27a896b83b436d066170 to /var/lib/mock/hermetic-build-bootstrap/root
INFO: mounting e638a12b50b68a5fd799979a44aef6b24fb6decd78bc27a896b83b436d066170 with podman image mount
INFO: image e638a12b50b68a5fd799979a44aef6b24fb6decd78bc27a896b83b436d066170 as /var/lib/containers/storage/overlay/bb414e5ed804faff43852dd3d96bdf6019114ba4de65ec256c934f4939a3ae19/merged
INFO: umounting image e638a12b50b68a5fd799979a44aef6b24fb6decd78bc27a896b83b436d066170 (/var/lib/containers/storage/overlay/bb414e5ed804faff43852dd3d96bdf6019114ba4de65ec256c934f4939a3ae19/merged) with podman image umount
INFO: Removing image mock-bootstrap-ba4c0b52-c91e-4b32-92fd-ad31c31398ec
INFO: Package manager dnf5 detected and used (fallback)
INFO: Not updating bootstrap chroot, bootstrap_image_ready=True
Finish(bootstrap): chroot init
Start: chroot init
INFO: calling preinit hooks
INFO: enabled package manager cache
Start: cleaning package manager metadata
Finish: cleaning package manager metadata
INFO: enabled HW Info plugin
INFO: Package manager dnf5 detected and used (direct choice)
Start: installing minimal buildroot with dnf5
Finish: installing minimal buildroot with dnf5
Finish: chroot init
INFO: Installed packages:
INFO: add-determinism-0.6.0-3.fc42.x86_64
alternatives-1.33-3.fc42.x86_64
annobin-docs-12.94-1.fc42.noarch
annobin-plugin-gcc-12.94-1.fc42.x86_64
ansible-srpm-macros-1-17.1.fc42.noarch
audit-libs-4.1.3-1.fc42.x86_64
basesystem-11-22.fc42.noarch
bash-5.2.37-1.fc42.x86_64
binutils-2.44-12.fc42.x86_64
bison-3.8.2-11.fc42.x86_64
build-reproducibility-srpm-macros-0.6.0-3.fc42.noarch
bzip2-1.0.8-20.fc42.x86_64
bzip2-libs-1.0.8-20.fc42.x86_64
ca-certificates-2025.2.80_v9.0.304-1.0.fc42.noarch
coreutils-9.6-7.fc42.x86_64
coreutils-common-9.6-7.fc42.x86_64
cpio-2.15-4.fc42.x86_64
cpp-15.2.1-7.fc42.x86_64
crypto-policies-20251125-1.git3839d0e.fc42.noarch
curl-8.11.1-7.fc42.x86_64
cyrus-sasl-lib-2.1.28-30.fc42.x86_64
debugedit-5.1-7.fc42.x86_64
diffutils-3.12-1.fc42.x86_64
dwz-0.16-1.fc42.x86_64
ed-1.21-2.fc42.x86_64
efi-srpm-macros-6-3.fc42.noarch
elfutils-0.194-1.fc42.x86_64
elfutils-debuginfod-client-0.194-1.fc42.x86_64
elfutils-default-yama-scope-0.194-1.fc42.noarch
elfutils-libelf-0.194-1.fc42.x86_64
elfutils-libs-0.194-1.fc42.x86_64
expat-2.7.3-1.fc42.x86_64
fedora-gpg-keys-42-1.noarch
fedora-release-42-30.noarch
fedora-release-common-42-30.noarch
fedora-release-identity-basic-42-30.noarch
fedora-repos-42-1.noarch
file-5.46-3.fc42.x86_64
file-libs-5.46-3.fc42.x86_64
filesystem-3.18-47.fc42.x86_64
filesystem-srpm-macros-3.18-47.fc42.noarch
findutils-4.10.0-5.fc42.x86_64
fonts-srpm-macros-2.0.5-22.fc42.noarch
forge-srpm-macros-0.4.0-2.fc42.noarch
fpc-srpm-macros-1.3-14.fc42.noarch
gawk-5.3.1-1.fc42.x86_64
gcc-15.2.1-7.fc42.x86_64
gcc-plugin-annobin-15.2.1-7.fc42.x86_64
gdb-minimal-16.3-1.fc42.x86_64
gdbm-libs-1.23-9.fc42.x86_64
ghc-srpm-macros-1.9.2-2.fc42.noarch
glibc-2.41-16.fc42.x86_64
glibc-common-2.41-16.fc42.x86_64
glibc-devel-2.41-16.fc42.x86_64
glibc-gconv-extra-2.41-16.fc42.x86_64
glibc-minimal-langpack-2.41-16.fc42.x86_64
gmp-6.3.0-4.fc42.x86_64
gnat-srpm-macros-6-7.fc42.noarch
gnulib-l10n-20241231-1.fc42.noarch
go-srpm-macros-3.8.0-1.fc42.noarch
grep-3.11-10.fc42.x86_64
gzip-1.13-3.fc42.x86_64
info-7.2-3.fc42.x86_64
jansson-2.14-2.fc42.x86_64
json-c-0.18-2.fc42.x86_64
kernel-headers-6.18.3-100.fc42.x86_64
kernel-srpm-macros-1.0-25.fc42.noarch
keyutils-libs-1.6.3-5.fc42.x86_64
krb5-libs-1.21.3-6.fc42.x86_64
libacl-2.3.2-3.fc42.x86_64
libarchive-3.8.1-1.fc42.x86_64
libattr-2.5.2-5.fc42.x86_64
libb2-0.98.1-13.fc42.x86_64
libblkid-2.40.4-10.fc42.x86_64
libbrotli-1.2.0-1.fc42.x86_64
libcap-2.73-2.fc42.x86_64
libcap-ng-0.9.1-1.fc42.x86_64
libcom_err-1.47.2-3.fc42.x86_64
libcurl-8.11.1-7.fc42.x86_64
libeconf-0.7.6-2.fc42.x86_64
libevent-2.1.12-15.fc42.x86_64
libfdisk-2.40.4-10.fc42.x86_64
libffi-3.4.6-5.fc42.x86_64
libgcc-15.2.1-7.fc42.x86_64
libgomp-15.2.1-7.fc42.x86_64
libidn2-2.3.8-1.fc42.x86_64
libmount-2.40.4-10.fc42.x86_64
libmpc-1.3.1-7.fc42.x86_64
libnghttp2-1.64.0-3.fc42.x86_64
libpkgconf-2.3.0-2.fc42.x86_64
libpsl-0.21.5-5.fc42.x86_64
libselinux-3.8-3.fc42.x86_64
libsemanage-3.8.1-2.fc42.x86_64
libsepol-3.8-1.fc42.x86_64
libsmartcols-2.40.4-10.fc42.x86_64
libssh-0.11.4-1.fc42.x86_64
libssh-config-0.11.4-1.fc42.noarch
libstdc++-15.2.1-7.fc42.x86_64
libtasn1-4.20.0-1.fc42.x86_64
libtool-ltdl-2.5.4-4.fc42.x86_64
libunistring-1.1-9.fc42.x86_64
libuuid-2.40.4-10.fc42.x86_64
libverto-0.3.2-10.fc42.x86_64
libxcrypt-4.5.2-1.fc42.x86_64
libxcrypt-devel-4.5.2-1.fc42.x86_64
libxml2-2.12.10-1.fc42.x86_64
libzstd-1.5.7-1.fc42.x86_64
lua-libs-5.4.8-4.fc42.x86_64
lua-srpm-macros-1-15.fc42.noarch
lz4-libs-1.10.0-2.fc42.x86_64
m4-1.4.19-12.fc42.x86_64
make-4.4.1-10.fc42.x86_64
mpdecimal-4.0.1-1.fc42.x86_64
mpfr-4.2.2-1.fc42.x86_64
ncurses-base-6.5-5.20250125.fc42.noarch
ncurses-c++-libs-6.5-5.20250125.fc42.x86_64
ncurses-devel-6.5-5.20250125.fc42.x86_64
ncurses-libs-6.5-5.20250125.fc42.x86_64
ocaml-srpm-macros-10-4.fc42.noarch
openblas-srpm-macros-2-19.fc42.noarch
openldap-2.6.10-1.fc42.x86_64
openssl-libs-3.2.6-3.fc42.x86_64
p11-kit-0.26.2-1.fc42.x86_64
p11-kit-trust-0.26.2-1.fc42.x86_64
package-notes-srpm-macros-0.5-13.fc42.noarch
pam-libs-1.7.0-7.fc42.x86_64
patch-2.8-1.fc42.x86_64
pcre2-10.46-1.fc42.x86_64
pcre2-syntax-10.46-1.fc42.noarch
perl-srpm-macros-1-57.fc42.noarch
pkgconf-2.3.0-2.fc42.x86_64
pkgconf-m4-2.3.0-2.fc42.noarch
pkgconf-pkg-config-2.3.0-2.fc42.x86_64
popt-1.19-8.fc42.x86_64
publicsuffix-list-dafsa-20260116-1.fc42.noarch
pyproject-rpm-macros-1.18.6-1.fc42.noarch
pyproject-srpm-macros-1.18.6-1.fc42.noarch
python-pip-wheel-24.3.1-5.fc42.noarch
python-rpm-macros-3.13-5.fc42.noarch
python-srpm-macros-3.13-5.fc42.noarch
python3-3.13.12-1.fc42.x86_64
python3-devel-3.13.12-1.fc42.x86_64
python3-libs-3.13.12-1.fc42.x86_64
python3-packaging-24.2-3.fc42.noarch
python3-rpm-generators-14-12.fc42.noarch
python3-rpm-macros-3.13-5.fc42.noarch
qt5-srpm-macros-5.15.18-1.fc42.noarch
qt6-srpm-macros-6.10.2-1.fc42.noarch
readline-8.2-13.fc42.x86_64
readline-devel-8.2-13.fc42.x86_64
redhat-rpm-config-342-4.fc42.noarch
rpm-4.20.1-1.fc42.x86_64
rpm-build-4.20.1-1.fc42.x86_64
rpm-build-libs-4.20.1-1.fc42.x86_64
rpm-libs-4.20.1-1.fc42.x86_64
rpm-sequoia-1.10.0-1.fc42.x86_64
rust-srpm-macros-28.4-1.fc42.noarch
sed-4.9-4.fc42.x86_64
setup-2.15.0-13.fc42.noarch
shadow-utils-4.17.4-1.fc42.x86_64
sqlite-libs-3.47.2-5.fc42.x86_64
systemd-libs-257.11-1.fc42.x86_64
systemd-standalone-sysusers-257.11-1.fc42.x86_64
tar-1.35-5.fc42.x86_64
tree-sitter-srpm-macros-0.1.0-8.fc42.noarch
tzdata-2025c-1.fc42.noarch
unzip-6.0-66.fc42.x86_64
util-linux-2.40.4-10.fc42.x86_64
util-linux-core-2.40.4-10.fc42.x86_64
which-2.23-2.fc42.x86_64
xxhash-libs-0.8.3-2.fc42.x86_64
xz-5.8.1-4.fc42.x86_64
xz-libs-5.8.1-4.fc42.x86_64
zig-srpm-macros-1-4.fc42.noarch
zip-3.0-43.fc42.x86_64
zlib-ng-compat-2.2.5-2.fc42.x86_64
zstd-1.5.7-1.fc42.x86_64
Start: build phase for units-2.26-1.fc45.src.rpm
Start: build setup for units-2.26-1.fc45.src.rpm
Finish: build setup for units-2.26-1.fc45.src.rpm
Start: rpmbuild units-2.26-1.fc45.src.rpm
Start: Outputting list of installed packages
Finish: Outputting list of installed packages
Finish: rpmbuild units-2.26-1.fc45.src.rpm
Finish: build phase for units-2.26-1.fc45.src.rpm
ERROR: Exception(./units-2.26-1.fc45.src.rpm) Config(hermetic-build) 0 minutes 14 seconds
INFO: Results and/or logs in: /var/lib/mock/hermetic-build/result
ERROR: Command failed: 
 # /usr/bin/systemd-nspawn -q -M 493c4d367fb34238b9b8c957abf189ba -D /var/lib/mock/hermetic-build/root -a -u mockbuild --capability=cap_ipc_lock --bind=/tmp/mock-resolv.x_yrfn52:/etc/resolv.conf --bind=/dev/btrfs-control --bind=/dev/mapper/control --bind=/dev/fuse --bind=/dev/loop-control --bind=/dev/loop0 --bind=/dev/loop1 --bind=/dev/loop2 --bind=/dev/loop3 --bind=/dev/loop4 --bind=/dev/loop5 --bind=/dev/loop6 --bind=/dev/loop7 --bind=/dev/loop8 --bind=/dev/loop9 --bind=/dev/loop10 --bind=/dev/loop11 --console=pipe --setenv=TERM=vt100 --setenv=SHELL=/bin/bash --setenv=HOME=/builddir --setenv=HOSTNAME=mock --setenv=PATH=/usr/bin:/bin:/usr/sbin:/sbin '--setenv=PROMPT_COMMAND=printf "\033]0;<mock-chroot>\007"' '--setenv=PS1=<mock-chroot> \s-\v\$ ' --setenv=LANG=C.UTF-8 --resolv-conf=off bash --login -c '/usr/bin/rpmbuild -bp --short-circuit --noclean --target x86_64 --nodeps /builddir/build/SPECS/units.spec'


!!! 2026-03-09 09:18:47 error: failed to set up hermetic chroot

scan.ini: time-finished = 2026-03-09 09:18:47
scan.ini: exit-code = 1
<<< 2026-03-09 09:18:47 csmock exit code: 1

Wrote: /home/kdudka/fedora/units/units-2.26-1.fc45.tar.xz

This would raise follow up questions like: how to handle scanners that aren't in the buildroot? I thought perhaps early experimental support could be expecting users to set that up with regular mock, otherwise just fail. I've experiment a bit though with making scanners like gcc conditional, i.e. only try to use it if in buildroot, otherwise skip.

I would not worry about gcc because the tool is mainly useful for RPMs that are built by gcc natively. IIRC the only tool in the default scanning profile that is installed into the chroot from an RPM is shellcheck. As the tool does a buildless analysis, I believe it should not be difficult to implement the --use-host-shellcheck option that would run shellcheck in the host environment and subsequently transform file paths in the results with csgrep --strip-path-prefix, as we do in the sast-shell-check Tekton task in Konflux CI.

@kdudka
Copy link
Member

kdudka commented Mar 9, 2026

Actually, we also use clippy, which is hooked on rpmbuild. That makes it more complicated to support hermetic builds fully transparently for end users.

@sfowl
Copy link
Contributor Author

sfowl commented Mar 9, 2026

I tried the above steps with a Fedora package and csmock failed in my env with a cryptic error message:

Thanks! will test more and fix.

I would not worry about gcc because the tool is mainly useful for RPMs that are built by gcc natively.

Agree, but there are some RPMs that don't pull in gcc. For those I was leaning toward something like: once buildroot setup, check if gcc is present, if not log error and warn that gcc analyzer wont' work, continue scanning as normal.

IIRC the only tool in the default scanning profile that is installed into the chroot from an RPM is shellcheck. As the tool does a buildless analysis, I believe it should not be difficult to implement the --use-host-shellcheck option that would run shellcheck in the host environment and subsequently transform file paths in the results with csgrep --strip-path-prefix, as we do in the sast-shell-check Tekton task in Konflux CI.

Sounds good to me

Actually, we also use clippy, which is hooked on rpmbuild. That makes it more complicated to support hermetic builds fully transparently for end users.

Yep, this the one that trapped me up the most. I don't have a great answer in mind right now.

@kdudka
Copy link
Member

kdudka commented Mar 9, 2026

Agree, but there are some RPMs that don't pull in gcc. For those I was leaning toward something like: once buildroot setup, check if gcc is present, if not log error and warn that gcc analyzer wont' work, continue scanning as normal.

I think this should be a warning at most. csmock has always used only the applicable tools. Most of the packages being scanned do not contain any Rust code, so the clippy plug-in does nothing for them. Same with shellcheck and shell code. If someone explicitly requests GCC Analyzer and the tool is not available in the chroot, it is logged and the scan continues successfully. I would not treat absence of the gcc package in the buildroot as an error even if the gcc plug-in is enabled in csmock.

The idea behind this is that one should be able to define a common csmock configuration for all Fedora packages (including packages that contain no source code at all) rather than having to maintain or guess the configuration for specific packages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants