aboutsummaryrefslogtreecommitdiff
path: root/doc.tex
diff options
context:
space:
mode:
authorW. Kosior <koszko@koszko.org>2025-09-19 12:47:44 +0200
committerW. Kosior <koszko@koszko.org>2025-09-19 13:09:45 +0200
commit7000b1f2bb2fca5b7707c9b1a9f58f43100212d3 (patch)
treecc5531b93b8e76c39e3c58d52574f68e86c3f05e /doc.tex
downloadAGH-msc-thesis-magister.tar.gz
AGH-msc-thesis-magister.zip
Initial commit.HEADmagister
Diffstat (limited to 'doc.tex')
-rw-r--r--doc.tex3450
1 files changed, 3450 insertions, 0 deletions
diff --git a/doc.tex b/doc.tex
new file mode 100644
index 0000000..a017369
--- /dev/null
+++ b/doc.tex
@@ -0,0 +1,3450 @@
+%% SPDX-License-Identifier: CC0-1.0
+%%
+%% Copyright (C) 2025 Woj. Kosior <koszko@koszko.org>
+
+\documentclass[a4paper,12pt,twoside,notitlepage]{report}
+
+\usepackage{rotating}
+
+\usepackage[
+ margin=2.5cm,
+ marginparwidth=1.5cm,
+ marginparsep=2mm,
+ foot=1cm,
+ head=1cm
+]{geometry}
+
+\renewcommand{\baselinestretch}{1.5}
+\newcommand{\smallStretchValue}{1.2}
+\newenvironment{smallStretch}{\setstretch{\smallStretchValue}}{}
+
+\usepackage{titlesec}
+\titleformat
+{\chapter} % command
+[hang] % shape
+{\bfseries\Large} % format
+{\thechapter{}.{ }} % label
+{0pt} % sep
+{} % before-code
+[] % after-code
+
+\usepackage{fancyhdr}
+
+\usepackage[T1]{fontenc}
+
+\usepackage{xurl}
+
+\usepackage[style=alphabetic,backend=biber,urldate=long,maxbibnames=9]{biblatex}
+
+\usepackage[polish,english]{babel}
+\DefineBibliographyStrings{english}{%
+ mathesis = {Master's thesis},
+}
+
+\addbibresource{doc.bib}
+
+\renewcommand*\finalnamedelim{, \addspace{}and\addspace{}}
+
+\usepackage{mathptmx}
+\usepackage{fontspec}
+\usepackage{newunicodechar}
+\newfontfamily{\fallbackfont}{DejaVu Sans}[Scale=MatchLowercase]
+\DeclareTextFontCommand{\textfallback}{\fallbackfont}
+\newunicodechar{│}{\textfallback{│}}
+\newunicodechar{├}{\textfallback{├}}
+\newunicodechar{─}{\textfallback{─}}
+\newunicodechar{˷}{,,}
+
+\usepackage{footnote}
+
+\usepackage{caption}
+\usepackage{setspace}
+\captionsetup[lstlisting]{
+ font={stretch=\smallStretchValue}
+} % fix to prevent listings' changed stretch from affecting their captions
+
+\usepackage[table]{xcolor}
+\usepackage{array}
+\usepackage{calc}
+
+% https://tex.stackexchange.com/questions/249040/scale-column-of-a-table-in-percentage-of-textwidth#answer-249043
+\newdimen\netTableWidth
+\newcommand{\columnsCount}[1]{%
+ \netTableWidth=\dimexpr
+ \linewidth
+ - #1\tabcolsep - #1\tabcolsep
+ - #1\arrayrulewidth -2\arrayrulewidth
+ \relax%
+}
+
+\newenvironment{fancycell}
+ {%
+ \hspace{0.1in}%
+ \nolinebreak%
+ \begin{minipage}[t]{\dimexpr\linewidth-0.1in\relax}
+ \raggedright
+ \hspace{-0.15in}%
+ }
+ {%
+ \end{minipage}%
+ \vspace{0.07in}
+ }
+
+\usepackage{tikz}
+
+\newcommand{\lightbullet}{%
+ \begin{tikzpicture}
+ \pgfmathsetlengthmacro\Xheight{height("X")}
+ \draw (0, 0) node[
+ inner sep=0,
+ anchor=east,
+ minimum height=\Xheight
+ ] {};
+ \fill[radius=0.25*\Xheight] circle;
+ \end{tikzpicture}%
+}
+
+\def\cellitems{\def\item{\par
+ \noindent\hbox to1.2em{\hss\lightbullet\hss\hss\hss}\hangindent=1.5em }}
+
+\usepackage[hyperfootnotes=false,pdftex]{hyperref}
+
+\hypersetup{
+ colorlinks = true,
+ linkbordercolor = .,
+ linkcolor = [rgb]{0,0.2,0}, % to be changed after ToC&friends
+ urlcolor = [rgb]{0,0,0.5},
+ citecolor = [rgb]{0.5,0,0}
+}
+
+\definecolor{PATCHGREEN}{rgb}{0,0.5,0}
+\definecolor{PATCHRED}{rgb}{0.5,0,0}
+\definecolor{TEXTGRAY}{gray}{0.2}
+\definecolor{FRAMEGRAY}{gray}{0.6}
+
+\usepackage{listings}
+\usepackage{accsupp}
+
+\renewcommand{\lstlistlistingname}{List of Listings}
+
+\lstset{
+ basicstyle=\fontsize{7}{9}\selectfont\ttfamily,
+% lineskip=-0.2em,
+ numbers=left,
+ numberstyle=\fontsize{7}{9}\color{TEXTGRAY},
+ inputencoding=utf8,
+ escapeinside={(*}{*)},
+ extendedchars=true,
+ breaklines=true,
+ postbreak=\mbox{%
+ \hspace{-5em}%
+ {\BeginAccSupp{ActualText={}}%
+ \textcolor{TEXTGRAY}{$\hookrightarrow$}%
+ \space%
+ \EndAccSupp{}%
+ }
+ },
+ breakautoindent=false,
+ captionpos=b,
+ frame=tbrl,
+ rulecolor=\color{FRAMEGRAY}
+}
+
+\lstdefinelanguage{diffoscope}{
+ delim = [s][\bfseries]{@@}{@@},
+ moredelim = [l][\color{PATCHRED}]{\ -},
+ moredelim = [l][\color{PATCHGREEN}]{\ +}
+}
+
+\lstdefinelanguage{shc-patch}{
+ delim = [s][\bfseries]{@@}{@@},
+ moredelim = [l][\color{PATCHRED}]{-shell},
+ moredelim = [l][\color{PATCHRED}\bfseries]{---},
+ moredelim = [l][\color{PATCHGREEN}]{+shell},
+ moredelim = [l][\color{PATCHGREEN}\bfseries]{+++}
+}
+
+\definecolor{tango-string}{HTML}{5c3566}
+\definecolor{tango-comment}{HTML}{5f615c}
+\definecolor{tango-keyword}{HTML}{346604}
+
+\lstdefinelanguage{guix-package-definition}[]{Lisp}{
+ stringstyle = \color{tango-string},
+ commentstyle = \itshape\color{tango-comment},
+ keywordsprefix = {\#:},
+ keywords = {},
+ keywordstyle = \color{tango-string},
+ keywords = [2]{list,lambda,for-each,define-public},
+ keywordstyle = [2]\color{tango-keyword},
+ keywords = [3]{\#t},
+ keywordstyle = [3]\bfseries
+}
+
+\lstdefinelanguage{guix-commit}[]{guix-package-definition}{
+ delim = [s][\bfseries]{@@}{@@},
+ moredelim = [l][\color{PATCHRED}]{-\ },
+ moredelim = [l][\color{PATCHGREEN}]{+\ }
+}
+
+\lstdefinelanguage{shell-command}{
+ delim = [s][\color{tango-keyword}]{--}{\ },
+ alsoletter = {\\},
+ keywords = {\\},
+ keywordstyle = \color{tango-string}
+}
+
+\lstdefinelanguage{package-lock-json}{
+ stringstyle = \color{tango-string},
+ string = [b]{"}
+}
+
+\newcommand{\code}[1]{\mbox{\color{TEXTGRAY}\fontsize{8}{10}\texttt{#1}}}
+
+%% https://tex.stackexchange.com/questions/320342/lstinputlisting-ranges-and-unicode-characters
+\makeatletter
+\lst@InputCatcodes
+\def\lst@DefEC{%
+ \lst@CCECUse \lst@ProcessLetter
+ ^^80^^81^^82^^83^^84^^85^^86^^87^^88^^89^^8a^^8b^^8c^^8d^^8e^^8f%
+ ^^90^^91^^92^^93^^94^^95^^96^^97^^98^^99^^9a^^9b^^9c^^9d^^9e^^9f%
+ ^^a0^^a1^^a2^^a3^^a4^^a5^^a6^^a7^^a8^^a9^^aa^^ab^^ac^^ad^^ae^^af%
+ ^^b0^^b1^^b2^^b3^^b4^^b5^^b6^^b7^^b8^^b9^^ba^^bb^^bc^^bd^^be^^bf%
+ ^^c0^^c1^^c2^^c3^^c4^^c5^^c6^^c7^^c8^^c9^^ca^^cb^^cc^^cd^^ce^^cf%
+ ^^d0^^d1^^d2^^d3^^d4^^d5^^d6^^d7^^d8^^d9^^da^^db^^dc^^dd^^de^^df%
+ ^^e0^^e1^^e2^^e3^^e4^^e5^^e6^^e7^^e8^^e9^^ea^^eb^^ec^^ed^^ee^^ef%
+ ^^f0^^f1^^f2^^f3^^f4^^f5^^f6^^f7^^f8^^f9^^fa^^fb^^fc^^fd^^fe^^ff%
+ ^^^^2502^^^^251c^^^^2500% `│', `├' and `─'
+ ^^00}
+\lst@RestoreCatcodes
+\makeatother
+
+%% was useful when I had no chapters and current chapters were sections
+% \usepackage[section]{placeins}
+
+% https://aty.sdsu.edu/bibliog/latex/floats.html
+
+% Alter some LaTeX defaults for better treatment of figures:
+% See p.105 of "TeX Unbound" for suggested values.
+% See pp. 199-200 of Lamport's "LaTeX" book for details.
+% General parameters, for ALL pages:
+\renewcommand{\topfraction}{0.9} % max fraction of floats at top
+\renewcommand{\bottomfraction}{0.8} % max fraction of floats at bottom
+% Parameters for TEXT pages (not float pages):
+\setcounter{topnumber}{2}
+\setcounter{bottomnumber}{2}
+\setcounter{totalnumber}{4} % 2 may work better
+\setcounter{dbltopnumber}{2} % for 2-column pages
+\renewcommand{\dbltopfraction}{0.9} % fit big float above 2-col. text
+\renewcommand{\textfraction}{0.07} % allow minimal text w. figs
+% Parameters for FLOAT pages (not text pages):
+\renewcommand{\floatpagefraction}{0.7} % require fuller float pages
+% N.B.: floatpagefraction MUST be less than topfraction !!
+\renewcommand{\dblfloatpagefraction}{0.7} % require fuller float pages
+
+%% \usepackage{float}
+
+%% \newfloat{flist}{H}{dummyfile}
+%% \newenvironment{fitemize}
+%% {\begin{itemize}}
+%% {\end{itemize}}
+%% %% {\begin{flist}\begin{itemize}}
+%% %% {\end{itemize}\vspace{-2.5em}\end{flist}}
+%% \newenvironment{fenumerate}
+%% {\begin{enumerate}}
+%% {\end{enumerate}}
+%% %% {\begin{flist}\begin{enumerate}}
+%% %% {\end{enumerate}\vspace{-2.5em}\end{flist}}
+
+\usepackage{enumitem}
+\usepackage{svg}
+\usepackage{graphics}
+
+%% \newcommand{\workNote}[1]{\emph{\color{red}\footnotesize(#1)}}
+%% \newcommand{\TODONote}[1]{\emph{\color{red}\footnotesize(TODO: #1)}}
+%% \newcommand{\marginNote}[2][9in]{%
+%% \marginpar{%
+%% \setstretch{1.1}%
+%% \begin{turn}{90}%
+%% \begin{minipage}{#1}%
+%% \workNote{#2}%
+%% \end{minipage}%
+%% \end{turn}%
+%% }%
+%% }
+
+\newcommand{\givemeatilde}{%
+ {\raisebox{0.5ex}{\texttildelow}}%
+}
+
+\newcommand{\tresholdDate}{April 14th, 2025}
+\newcommand{\tresholdGuixCommit}{\code{143faecec3}}
+\newcommand{\tresholdDateAndCommit}
+ {\tresholdDate{}, GNU Guix Git commit \tresholdGuixCommit{}}
+\newcommand{\debianTresholdDate}{June 3rd, 2025}
+
+\input{definitions-computed-from-results.tex}
+
+\fancypagestyle{fancyplain}{ %
+ \fancyfoot[C]{Kraków, 2025}
+ \renewcommand{\headrulewidth}{0pt} % remove lines as well
+ \renewcommand{\footrulewidth}{0pt}
+}
+
+\title{Software Provenance Assurance through Reproducible Builds}
+
+% https://tex.stackexchange.com/questions/15804/how-to-use-the-content-of-title-as-a-reference
+\makeatletter
+\let\inserttitle\@title
+\makeatother
+
+\newcommand{\insertauthor}{Wojciech Kosior}
+
+\hypersetup{
+ pdftitle = {\inserttitle},
+ pdfauthor = {\insertauthor}
+}
+
+\begin{document}
+
+\newpage
+
+\pagenumbering{roman}
+
+\titlepage
+
+\thispagestyle{fancyplain}
+
+\fancyhf{}
+\fancyfoot[C]{Kraków, 2025}
+
+\mbox{}
+\vspace{0.5in}
+
+\begin{smallStretch}
+ \fontfamily{cmr}\selectfont
+ \sffamily
+
+ \begin{center}
+ \large
+
+ \includesvg[
+ width=0.2\linewidth,
+ inkscapelatex=false
+ ]{agh.svg}
+
+ \vspace{0.2in}
+
+ \MakeUppercase{\small\bfseries {\large{}AGH} {\large{}U}niversity of
+ {\large{}K}rakow}
+
+ \vspace{0.2in}
+
+ \MakeUppercase{\small\bfseries THE FACULTY OF COMPUTER SCIENCE,
+ \\ ELECTRONICS AND TELECOMMUNICATIONS}
+
+ \vspace{0.45in}
+
+ Master's thesis
+
+ \vspace{0.45in}
+
+ \textit{\inserttitle}
+
+ \vspace{0.2in}
+
+ \textit{\normalsize{}Potwierdzanie autentyczności oprogramowania \\ poprzez
+ powtarzalność kompilacji}
+
+ \vspace{0.45in}
+
+ Keywords: software supply chain threats, reproducible builds, software
+ provenance, software packaging, npm Registry
+ \end{center}
+
+ \vspace*{\fill}
+
+ \renewcommand{\arraystretch}{\baselinestretch}
+ \columnsCount{2}
+ \begin{tabular}{ m{.35\netTableWidth} m{.65\netTableWidth} }
+
+ Author: & \insertauthor \\
+ Major: & Cybersecurity \\
+ Supervisor: & dr hab.\ inż.\ Piotr Pacyna, prof.\ AGH
+ \end{tabular}
+\end{smallStretch}
+
+\clearpage
+
+\chapter*{Acknowledgements}
+
+I could have decided not to enroll for the MSc course in the first place. But,
+having experienced some failures in my career, I started asking what God would
+like me to do. I recalled that even though at various moments it was unpleasant
+to be a student, university was a place that suited me more that places I have
+been to afterwards. I assumed that maybe -- just maybe -- God did not want me
+to succeed anywhere else because He wants me to be happy here, in the academia.
+And so I am, finishing this unusual piece of research. Thank God.
+
+I also want to thank my wife, Joanna, who supported me even though she regularly
+had to suffer listening about my boring computer topics. I thank my father, who
+at times seems to care for my successful defence even more than I do.
+
+Finally, I could not forget about my supervisor who -- luckily for me -- was
+corageous enough to undertake supervising a thesis in the software supply chain
+field, which few students pursued before. Thank you!
+
+\begin{smallStretch}
+ \clearpage
+
+ \setcounter{tocdepth}{1}
+ \tableofcontents
+
+ \clearpage
+
+ \phantomsection{}
+ \addcontentsline{toc}{chapter}{\listfigurename}
+ \listoffigures
+
+ \clearpage
+
+ \phantomsection{}
+ \addcontentsline{toc}{chapter}{\lstlistlistingname}
+ \lstlistoflistings
+
+ \clearpage
+
+ \phantomsection{}
+ \addcontentsline{toc}{chapter}{\listtablename}
+ \listoftables
+\end{smallStretch}
+
+\clearpage
+
+\phantomsection{}
+\addcontentsline{toc}{chapter}{\abstractname}
+\begin{abstract}
+ Software faces risk of contamination on multiple stages of its creation and
+ distribution. One such stage is software's build, where its initial form --
+ the source code -- is transformed into a form suitable for distribution. A
+ build is called reproducible when it yields bit-to-bit identical outputs when
+ repeated. The reproducible build can be secured by repeating it on multiple
+ infrastructures and comparing the outputs. This decreaces the risk of the
+ software getting contaminated through the build infrastructure used. Certain
+ software projects -- in particular, the Debian operating system -- already
+ levarage reproducible builds as a security tool. Meanwhile, several software
+ ecosystems rely on repositories whose packages cannot be reliably rebuilt and
+ tested for reproducibility. An example is a popular repository called npm
+ Registry. The software available through the npm Registry gets included in
+ reproducibility-focused distributions like Debian at slow pace. A great
+ number and complexity of dependency relations between npm packages were
+ hypothesized to be the primary hindrances to this process. In this work, a
+ statistical analysis of the npm ecosystem was performed and existing
+ approaches to reproducibility in the context of dependency resolution were
+ identified. Additionally, alternative approaches were proposed that would
+ allow for easier packaging of npm software while making reproducibility
+ achievable. To verify several stated hypotheses, an experiment was performed,
+ where builds of the most popular npm projects were attempted. Projects were
+ built multiple times to test what subset of their npm Registry dependencies
+ can be removed without causing the build to fail. The complexity and size of
+ project's minimal dependency tree was found not to be related to the
+ likelihood of the project having a corresponding Debian package. The results
+ lead to conclusion that even numerous and complex dependency relations can be
+ handled in existing reproducibility-focused software distributions. This
+ means that employing the proposed new approaches is not necessary to apply
+ reproducibility to npm software in the future. It also means that the
+ inclusion of npm software in reproducibility-focused software distributions is
+ mostly hindered by other factors that need to be countered. These were also
+ pointed at the end of this work.
+\end{abstract}
+
+\clearpage
+
+\selectlanguage{polish} \phantomsection{}
+\addcontentsline{toc}{chapter}{\abstractname}
+\begin{abstract}
+ Bezpieczeństwo oprogramowania może zostać zagrożone na różnych etapach jego
+ tworzenia i~dystrybucji. Jednym z~nich jest szeroko rozumiana kompilacja
+ oprogramowania, gdzie jego pierwotna postać -- kod źródłowy -- jest
+ przekształcana do postaci odpowiedniej dla dystrybucji. Proces kompilacji
+ nazywamy powtarzalnym, jeśli przy wielokrotnym przeprowadzeniu daje wyniki bit
+ do bitu identyczne. Powtarzalny proces kompilacji może zostać zabezpieczony
+ poprzez przeprowadzenie go na różnych infrastrukturach i~porównanie wyników.
+ Zmniejsza to ryzyko zanieczyszczenia kodu oprogramowania przez użytą
+ infrastrukturę. Pewne oprogramowanie -- w~szczególności system Debian -- już
+ wykorzystuje powtarzalność jako narzędzie bezpieczeństwa. Jednocześnie,
+ niektóre ekosystemy oprogramowania polegają na repozytoriach, których pakiety
+ nie mogą być w~sposób niezawodny przekompilowane i~sprawdzone pod kątem
+ powtarzalności. Przykładem jest popularne repozytorium o~nazwie npm Registry.
+ Oprogramowanie dostępne przez~npm Registry jest też, aczkolwiek w~wolnym
+ tempie, włączane do dystrybucji typu Debian dbających o~powtarzalność
+ kompilacji. Według postawionej hipotezy to duża liczba i~złożoność relacji
+ zależności między pakietami npm są głównymi utrudnieniami w~tym procesie.
+ W~ramach pracy została wykonana analiza statystyczna ekosystemu npm oraz
+ zostały zidentyfikowane istniejące podejścia do powtarzalności w~kontekście
+ procesu rozwiązywania zależności. Dodatkowo, zostały zaproponowane
+ alternatywne podejścia, w~których pakowanie oprogramowania npm miałoby być
+ łatwiejsze, a~powtarzalność byłaby wciąż osiągalna. Dla zweryfikowania
+ postawionych hipotez przeprowadzony został eksperyment -- próba kompilacji
+ najpopularniejszych projektów npm. Projekty kompilowano wielokrotnie,
+ sprawdzając, jaka część ich zależności z~npm Registry może zostać usunięta
+ z~zachowaniem powodzenia procesu kompilacji. Złożoność i~rozmiar minimalnego
+ drzewa zależności projektu okazały się nie być powiązane
+ z~prawdopodobieństwiem istnienia odpowiadającego pakietu w~Debianie. Wyniki
+ prowadzą do wniosku, że istniejące dystrubucje oprogramowania dbające
+ o~powtarzalność mogą sobie poradzić także z~licznymi i~złożonymi relacjami
+ zależności. Oznacza to, że wprowadzenie zaproponowanych nowych podejść nie
+ jest konieczne, żeby można było w~przyszłości zastosować powtarzalność do
+ oprogramowania npm. Oznacza to też, że włączanie oprogramowania npm do
+ dystrybucji oprogramowania dbających o~powtarzalność jest w~głównej mierze
+ powstrzymywane przez inne czynniki wymagające zwalczenia. Zostały one
+ wskazane pod koniec pracy.
+\end{abstract}
+\selectlanguage{english}
+
+\chapter{Introduction}
+
+\hypersetup{ linkcolor = [rgb]{0,0.5,0} }
+
+\renewcommand{\arraystretch}{1.5}
+
+\pagenumbering{arabic}
+
+\fancypagestyle{plain}{
+ \renewcommand{\headrulewidth}{0pt} \renewcommand{\footrulewidth}{0pt} }
+
+\rowcolors{2}{gray!20}{white}
+
+%% W opisie warto dodac uzupelnienie - wyjasnienie szerokiego podloza problemu,
+%% ktory jest glownym tematem. Moznaby zacząc od kwestii problemu "supply chain
+%% management" w ogolnosci (w gospodarce) - wymienic problemy, troski, obawy i
+%% potrzeby.
+
+Most products of modern industry are composed or made using a number of
+half-products and tools. These tend to come from different producers, who
+themselves rely on their suppliers. Such half-products might be produced with
+violations of human rights, unecologically, without meeting certain quality
+standards, or with patent violations. The assembly from half-products also
+creates an opportunity for deliberate sabotage on part of the supplier. So far
+businesses have not always successfully mitigated these threats, which later
+reverberated in many ways.
+
+%% W kolejnym akapicie podac krotką charakterystyki problemow z tym związanych,
+%% ale juz w kontekscie procesu wytworczego oprogramowania (powtorzyc istotne
+%% zagadnienia "supply ..." lub uszczegolowic)
+
+Just as a design is often used to manufacture physical products, code written in
+a programming language is used to produce software in its target form, e.g., an
+executable, a firmware image, or a collection of files. This process of
+producing software in its target form can be referred to as software
+\textbf{build}. Items resulting from it are \textbf{build outputs}. A software
+build process overview is presented in Figure~\ref{fig:build-process-overview}.
+Depending on the programming languages and technologies in use, the build might
+encompass different actions, e.g., macro processing, compilation, linking,
+bundling, or compressed archive creation. It can also utilize a multitude of
+different tools. Adjusting the build process to meet certain requirements is
+often a complex task that requires understanding the workings of various tools
+involved. In some software distribution projects, hundreds of lines of scripts
+are maintained to allow proper building of a single piece of software written by
+another party. Builds can even involve applying changes to upstream code, often
+through the use of patches, i.e., machine-readable files describing changes to
+project code.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[width=\linewidth,inkscapelatex=false]{build-process-overview.svg}
+ \caption{Overview of a sample software build process.}
+ \label{fig:build-process-overview}
+\end{figure}
+
+Similarly to physical products, software is made using preexisting elements
+delivered by other parties. These are often called \textbf{dependencies}. They
+include
+
+\begin{itemize}
+\item
+ runtime dependencies -- components to be distributed inside or alognside the
+ final program and used during its execution, for example reusable modules of
+ code called libraries or special fonts, and
+\item
+ development dependencies -- elements not needed during program's execution but
+ useful to its developers, furter classifiable as
+ \begin{itemize}
+ \item
+ build dependencies -- compilers, linkers, test frameworks, etc.\ needed in
+ the build process, typically able to function non-intereactively and invoked
+ through some layer of automation, sometimes categorized further, for example
+ into native and non-native or into build and test dependencies, and
+ \item other development tools -- tools to work with software that are not
+ needed in the actual build process, more often manually-operated, like
+ IDEs\footnote{Integrated Development Environments} and their plugins,
+ debuggers, or linters.
+ \end{itemize}
+\end{itemize}
+
+\section{Problem formulation}
+
+\textbf{If either an external dependency suffers from contamination, the
+ infrastructure handling the build is compromised, or the organization or
+ individuals attempt a sabotage, then a backdoor or other security
+ vulnerability can be implanted in the software being built. In a basic
+ setting, each dependency, the infrastructure, and the organization are all
+ single points of failure. The last two of these points can be secured through
+ additional build outputs verification that utilizes software reproducibility.
+ This work aims at exploiting reproducibility to secure software's build
+ process, with special focus on eliminating the gaps that would leave single
+ points of failure.}
+
+A reproducible build is one that produces the same, bit-to-bit identical outputs
+when repeated. For example, the resulting program executables are bit-to-bit
+identical. This concept assumes that a set of \textbf{build inputs} with the
+same contents is used in every repetition. E.g., program sources and
+dependencies in exact same versions are used. As a consequence, one
+prerequisite of reproducibility is \textbf{hermeticity} -- the quality of a
+build process that depends exclusively on a set of predefined dependencies. A
+hermetic build must not depend on changeable network resources nor on machine's
+installed software other than build's formal inputs. Hermeticity is usually
+ensured by performing software build inside a minimal, isolated environment,
+often a container utilizing Linux namespaces.
+
+Multiparty verification of build's reproducible output can help increase
+confidence that built software is not contaminated due to compromise of
+infrastructure underpinning the build environment nor malicious actions of
+infrastructure operators. The verification shall be unsuccessful if the
+contamination is present in an output of one build and not in those of the
+others. The overviews of successful and unsuccessful verification performed by
+end user -- a scheme that does not create unnecessary single points of failure
+-- are presented in Figures \ref{fig:rebuilds-no-contamination-diagram}
+and~\ref{fig:rebuilds-contamination-diagram}, respectively. Contamination is
+represented by a frowning face. The extra confidence coming from verification
+can empower both software vendors willing to reduce the risk of distributing
+compromised code and software consumers wanting to secure their operations.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[
+ width=\linewidth,
+ inkscapelatex=false
+ ]{rebuilds-no-contamination-diagram.svg}
+ \caption{Overview of a successful multiparty build verification process.}
+ \label{fig:rebuilds-no-contamination-diagram}
+\end{figure}
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[
+ width=\linewidth,
+ inkscapelatex=false
+ ]{rebuilds-contamination-diagram.svg}
+ \caption{Overview of an unsuccessful build verification process.}
+ \label{fig:rebuilds-contamination-diagram}
+\end{figure}
+
+Single-party verification is also applicable if only the infrastructure threats
+are considered. Meanwhile, the party itself retains the ability to undetectably
+compromise software builds, i.e., implant backdoors. Figure
+~\ref{fig:rebuilds-contamination-1party} depicts an occurrence of such
+compromise while single-party verification of build's reproducible output is
+taking place. Contamination is represented by a frowning face.
+
+In addition to the above, just as reproducible builds performed by a single
+organization are insufficient to protect from contamination introduced
+deterministically by the organization, reproducible builds performed on a single
+infrastructure would be insufficient to protect from contamination spreading
+deterministically from that infrastructure.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[
+ width=\linewidth,
+ inkscapelatex=false
+ ]{rebuilds-contamination-1party.svg}
+ \caption{Overview of a build verification process which only compared the
+ outputs of builds performed by a single party and failed to detect malice.}
+ \label{fig:rebuilds-contamination-1party}
+\end{figure}
+
+For software to be secured with reproducible builds, its build process has to
+gain the quality of reproducibility, where repetition of the same build produces
+output without variations. Achieving that quality can itself be challenging.
+This involves identification of sources of build process' nondeterminism -- like
+timestamps and a changing order of filenames in directory listings. Identified
+sources need to be removed, e.g., by use of fixed date in timestamps or sorting
+of filenames obtained from directory scans. Achieving this is nowadays easier
+because common sources of nondeterminism have already been investigated and
+workarounds have been implemented. For example, since version 7 the GNU C
+Compiler checks for the existence of a \code{SOURCE\_DATE\_EPOCH} environment
+variable containing a time value. It automatically uses this value in generated
+file timestamps~\cite{source-date-epoch}. Additionally, dedicated tooling for
+investigating non-reproducibility issues has been developed, notably the
+\textbf{diffoscope} program~\cite{DBLP:journals/corr/abs-2104-06020}. To
+decrease the chance of contamination from a compromized operating system,
+firmware, and hardware, the actual build -- once its reproducibility issues are
+resolved -- should be performed on infrastructures that differ as much as
+possible, except for the invariant set of build inputs and environment
+configuration needed to ensure reproducibile outputs.
+
+This work does not address the challenges of avoiding nondeterminism in software
+builds. Instead, the work's goal is to ensure that -- in practical scenarios --
+the build inputs remain invariant in all build repetition attempts. The work's
+second major concern is that all machine-performed operations -- even those
+deemed preparatory -- can have their effect on build output controlled through
+reproducibility. All of this, in turn, can make reproducible builds a more
+reliable and more complete security mechanism.
+
+Despite their benefits, one should nevertheless realize that reproducible builds
+only address a particular subset of software supply chain threats -- ones
+affecting the build process -- and are mostly useful if other stages of that
+chain are also secured. Some of the practices that can help with this are
+
+\begin{enumerate}
+\item
+ making sure the software sources relied upon are audited against backdoors, at
+ least informally, e.g., by the virtue of being developed in the Bazaar model,
+ where the public has a high chance of noticing malicious
+ changes~\cite{raymond2001cathedral},
+\item
+ making sure the software sources do not get altered by any party after the
+ verification from the step above, and
+\item
+ using reproducible builds and other mechanisms to verify software's
+ dependencies, possibly recursively.
+\end{enumerate}
+
+One example of a threat not remediated through reproducible builds alone is the
+loud XZ backdoor from 2024. Among others, it targeted Debian and Fedora
+software distributions. Backdoor's activation code was only present in the
+official source release archives and not in the source repository, which is most
+often looked at by programmers~\cite{journals/corr/abs-2404-08987}. When the
+software was built from source archives, it had backdoor code linked in, but
+build results were deterministic. Attacks in such form would not be possible if
+the source archives were verified to correspond to version-controlled software
+sources.
+
+\chapter{Contemporary guidance and standards in the field of software supply
+ chain security}
+
+Threats related to software build and delivery process were known already long
+ago. One interesting self-implanting compiler backdoor was described by Ken
+Thompson in his Turing Award lecture in 1984, ``Reflections on Trusting
+Trust''~\cite{Thompson:1984}. Later signs of interest in supply chain threats
+in certain circles include for example David A.\ Wheeler's PhD dissertation
+titled ``Fully Countering Trusting Trust through Diverse Double-Compiling''
+\cite{phd/basesearch/Wheeler09a} and eventually Bitcoin's use of Gitian
+deterministic builder\footnote{before replacing Gitian with GNU Guix in 2021}.
+Also, for many years certain software distributions have been compiling their
+software from sources on dedicated servers, in isolated environments with only
+mininum dependencies for a given build. One example of such distribution is
+Debian.
+
+At the same time, for a wide public it's been a norm to rely on prebuilt
+software that gives virtually no guarantees that it was built in a secure
+environment. Multiple software repositories
+% like npm Registry and PyPI\footnote{Python Package Index}
+allow publishing developer-built software packages. Such software -- built from
+a valid VCS\footnote{version control system} checkout but on developer's
+infected machine -- could be maliciously modified and distributed to unaware
+software integrators wishing to utilize it in their projects. Neither
+cryptographic signing of packages nor VCS source code audits would mitigate such
+attacks.
+
+Several spectacular supply chain attacks of recent years became the catalyst of
+work towards increasing the level of security. In case of the SolarWinds attack
+from 2020, also known under the name Sunburst, software distributed among
+reportedly more than $18\,000$ customers turned out to contain a backdoor
+implanted after a compromise of vendor's
+infrastructure~\cite{conf/uic/SterleB21}. It was exactly the kind of threat
+that reproducible builds address. As a result of the event, SolarWinds
+Corporation suffered great economic losses and pejoration of its brand's image.
+Additionally, the company exposed thousands of customers to cyberattacks
+leveraging its compromised software. All of this could have been avoided
+through reproducible verification of software build outputs.
+
+As more attacks on software build and distribution are reported, software supply
+chain security becomes a hot topic. It attracts the attention of public
+institutions and private organizations alike. Some prominent undertakings by
+nonprofits are: launch of OWASP's\footnote{Open Worldwide Application Security
+Project} SCVS\footnote{Software Component Verification Standard} in 2019,
+foundation of OpenSSF\footnote{Open Source Security Foundation} in 2020, launch
+of its SLSA\footnote{Supply Chain Levels for Software Artifacts} framework in
+2021, Microsoft's donation of S2C2F\footnote{Secure Supply Chain Consumption
+Framework} to OpenSSF in 2022, as well as the publishing of
+CNCF's\footnote{Cloud Native Computing Foundation, a project of Linux
+Foundation} ``Software Supply Chain Best Practices'' in 2021. State actors also
+took voice by the means of ``Securing the Software Supply Chain: Recommended
+Practices for Developers'' and subsequent two guides from 2022 developed by the
+ESF\footnote{Enduring Security Framework} partnership with support from
+CISA\footnote{Cybersecurity and Infrastructure Security Agency}, the
+NSA\footnote{National Security Agency}, and the Office of the Director of
+National Intelligence. Another possibly relevant document is NSA's
+``Recommendations for Software Bill of Materials (SBOM) Management'' from 2023.
+
+\section{Software Component Verification Standard}
+
+SCVS \cite{owasp-scvs} describes itself as a ``community-driven effort to
+establish a framework for identifying activities, controls, and best practices,
+which can help in identifying and reducing risk in a software supply chain''.
+Despite being developed by OWASP it is generic and not limited to web
+applications in its scope. Authors recognize the unfeasibility of applying all
+good practices and threat mitigations at every phase of every software project
+and categorize their security requirements into three levels, each implying the
+previous one and extending it.
+
+Practices listed in SCVS are grouped into six topics and formulated briefly.
+They are agnostic about the technology stack and data formats in use. At length
+explanation of the importance of prescribed actions is not part of the document.
+
+As of version 1.0 of the standard, level 2 requirements include a method to
+locate ``specific source codes in version control'' that correspond to a given
+version of a third-party package from software repository. While it is stated
+that the correspondence must be verifiable, further details are not given.
+SBOM\footnote{software bill of materials} and repeatable build process are
+required for an application being developed but not for third-party components.
+Additionally, listed practices regarding the build environment mention neither
+the goal of reproducibility nor the weaker hermeticity. While authors might
+have -- justifiably -- judged such rules as unfeasible given the current state
+of the software ecosystem, it is interesting from the point of view of this
+work. Threats that could not be addressed a few years ago in a generic setting
+might be remediable now in the context of one or several technology stacks.
+
+\section{Supply Chain Levels for Software Artifacts}
+
+SLSA \cite{slsa} uses similar but conceptually more complex categorization than
+SCVS. Practices are going to be assigned to so-called ``tracks'' which
+correspond to different aspects of software supply chain security and which
+might use different numbers of security levels. As of framwork version 1.0
+there only exists a ``Build'' track with three levels, not counting the empty
+zeroth level. In addition to the specification of requirements, SLSA documents
+different threats grouped into those concerning the source, dependencies, build,
+availability, and verification of an artifact. Historical examples of attacks
+using some of these techniques are listed in the documentation. Finally, it
+also includes instructions how to apply SLSA and how to use it with attestation
+formats from the in-toto framework~\cite{conf/uss/Torres-AriasAKC19}.
+
+Many aspects of the build process are addressed in the specified requirements
+set but the qualities of hermeticity and reproducibility were removed from the
+set at the drafting stage. SLSA explicitly calls verified reproducible builds
+one of multiple methods of implementing the requirements. In the context of the
+particular threat of compromised infrastructure, framework's focus is instead on
+stronger security controls for the build platform. The platform, however,
+remains a single point of failure. Incidents like that of SolarWinds could
+still occur. Reproducibility and hermeticity might be re-introduced in
+subsequent revisions of SLSA, as explained on its ``Future directions'' page.
+
+The specification currently also does not cover the recursive application of its
+requirements to input artifacts used. It is nonetheless suggested that users
+could apply SLSA independently to transitive dependencies. This approach is
+presented as a possible mitigation to attacks like that performed on
+event-stream library in 2018.
+
+\section{Secure Supply Chain Consumption Framework}
+
+S2C2F \cite{s2c2f} is complementary to SLSA in that it embraces software
+consumer's point of view. It introduces four ``levels of maturity'' of
+requirements with the highest level mandating a consumer-performed rebuild of
+all artifacts. Having the artifact built reproducibly by several third parties
+is mentioned as an alternative approach. Neither method is presented as more
+secure, even though local rebuild still suffers from being a single point of
+failure.
+
+\section{``Software Supply Chain Best Practices''}
+
+As of version 1, this paper \cite{cncf-sscp} recognizes three categories of risk
+environments and three categories of assurance requirements. These are -- in
+both cases -- ``low'', ``moderate'', and ``high''. A methodology for securing
+the software supply chain is presented in five stages, with four themes of
+``Verification'', ``Automation'', ``Authorization in Controlled Environments'',
+and ``Secure Authentication'' being repeated in them. Recommendations are
+organized into paragraphs rather than tables or lists. Authors point towards
+existing tools useful for some of the tasks, notably the in-toto
+framwork~\cite{conf/uss/Torres-AriasAKC19} and Rebuilderd
+system~\cite{drexel2025reproduciblebuildsinsightsindependent}. At the same
+time, they openly admit that some of the practices they describe might require
+extra effort to implement because certain challenges have not yet been countered
+by the supply chain industry.
+
+Reproducible builds are presented as potentially leverageable when high
+assurance is needed. The topic is discussed in more detail than in the previous
+documents from OWASP and OpenSSF. In addition, hermeticity is included as a
+recommendation for high-risk and high-assurance environments. Recursive
+dependencies are treated with equal care to the direct ones, consistently with
+authors' statement that ``a supply chain's security is defined by its weakest
+link''. The issue of bootstrapping a system image for builds is also discussed
+in the paper.
+
+\section{``Securing the Software Supply Chain: Recommended Practices Guide''}
+
+The series was developed by a public-private working group with members from
+both the industry and U.S.\ government agencies. It is described as
+informational only and does not define any standard. Subsequent parts are
+addressed at software developers, suppliers -- who are considered to be
+``liaising between the customer and software developer'' -- and customers.
+
+Although these series do not group recommendations into levels, two mitigations
+in the first guide from August 2022 \cite{nsa-esf-recommended-practices-devs}
+are called ``advanced'' and described as providing ``additional protection''.
+These are the hermetic and reproducible builds. A suggestion is made that the
+same builds are performed ``in both cloud and on-premise environments'' and
+their outputs compared. Additionally, authors state a justification should be
+required when it is impossible to perform certain build reproducibly. The text
+of this requirement has been copied verbatim from SLSA draft back before being
+removed there.
+
+The guide also recommends that images used to deploy the build environment
+should be created from sources except where ``there is an understanding of the
+provenance and trust of delivery''. No statements explicitly concerning
+rebuilds of transitive dependencies of a product are made.
+
+\section{``Recommendations for SBOM Management''}
+\label{sec:recommendations-for-sbom}
+
+The paper \cite{nsa-sbom-management} calls itself a guidance. It lists
+recommendations for general software suppliers and consumers but also dedicates
+a big part to users and owners of NSS\footnote{U.S.\ national security systems
+-- a specific category of information systems used on behalf of U.S.\ agencies}.
+Document's primary focus is on functionalities that tools used to manage SBOMs
+should provide.
+
+NSA's guidance concerns SBOMs, which hold information about software components
+comprising final product. The guidance does not directly address build process
+threats and does not touch the topics of reproducibility and transitive
+dependencies of software. In fact, the industry recognizes another type of bill
+of materials, not mentioned in the document, which is more relevant to the topic
+of reproducibility than SBOM. It is manufacturing bill of materials. In the
+context of software, MBOM conveys information about all components needed for
+its build. This also includes project's build dependencies which would not be
+recorded in an SBOM. MBOMs are relevant from reproducibility perspective
+because information in them can make software rebuilds possible. Even though
+MBOMs are not directly mentioned in version 1.1 of NSA's guidance, one of the
+recommendations present there is labeled as ``Scalable architecture''. It is
+described as one that can also ``handle other types of BOMs''.
+
+\section{Summary}
+
+Published documents' attitutes to reproducibility and hermeticity range from
+agnosticism to suggestion and recommendation in the context of certain
+environments. Reproducibility and its requisite -- hermeticity -- are difficult
+to achieve with a great subset of existing popular software projects. This
+difficulty might stand behind the limited focus on these measures in documents
+other than CNCF's ``Software Supply Chain Best Practices''. It appears that the
+means of securing the software supply chain which are more straightforward to
+employ are also more often recommended. In such case, making reproducibility
+easier to achieve for all kinds of software projects should lead to it being
+more frequently discussed and therefore more broadly leveraged.
+
+\chapter{Security tools leveraging reproducible builds}
+\label{chp:existing-security-tools}
+
+Several initiatives and pieces of software exist that are concerned with the
+verification of reproducibility of software packages. The champion of these
+efforts is the Reproducible Builds project, also affiliated with Debian.
+
+\section{in-toto apt-transport for Debian packages}
+
+in-toto framework, developed under the CNCF, aims to secure the integrity of
+software supply chains~\cite{conf/uss/Torres-AriasAKC19}. Debian GNU/Linux is
+an operating system distribution founded in 1993. It provides thousands of
+pieces of software in form of \textbf{packages} that can be installed in the
+system via Debian's package manager, APT\footnote{Advanced Package Tool}.
+
+In 2018 it became possible to use in-toto together with Debian's APT, to verify
+that a package being installed has been verified through reproducible builds.
+The package manager can be configured to abort installation if the package was
+not reproduced by at least $k$ independent rebuilders, with $k$ configurable by
+the user. In the process, cryptographically signed attestations of rebuilt
+packages are fetched over the network from rebuilder URIs that are also
+configured by the user.
+
+\subsection{How a Debian package is made and rebuilt}
+\label{sub:how-debian-package-is-made}
+
+Most packages available in Debian contain software written by third parties,
+i.e., the \textbf{upstream}. That software was released in source form and
+subsequently integrated into Debian. A package typically has a maintainer who
+is a Debian volunteer taking care of the package, possibly with the aid of other
+people~\cite[Chapter~1]{debian-new-maintainers-guide}.
+
+Upon initial creation or a version update of a Debian package, its maintainer
+first prepares what is called a \textbf{source package}. It is the primary
+input of the build process that will produce the final, installable package.
+The installable package is also sometimes called a \textbf{binary package} to
+distinguish it from the source package. A single build process with a single
+source package can also prouce multiple binary packages. For example, a
+programming library written in the C programming language can have its
+dynamically linked binary and its header files placed in distinct binary
+packages. As of \debianTresholdDate{}, the current release of Debian -- Debian
+12, codenamed ``Bookworm'' -- offered $63\,465$ packages for x86\_64
+architecture, as found in its \code{main} pool. They were produced from
+$34\,217$ source packages.
+
+%% This is actually 38,546 buildinfo files and this many builds -- because
+%% separates builds are conducted with a single source pacakge to produce
+%% architecture-specific and architecture-independent outputs…
+
+%% gawk '
+%% BEGIN{
+%% delete registered_builds[""];
+%% }
+%% /^Package:/{
+%% source = 0;
+%% ver = 0;
+%% arch = 0;
+%% }
+%% /^Source:/{
+%% source = $2;
+%% }
+%% /^Source:.*[(].*[)]/{
+%% ver = gensub("Source: .*[(](.*)[)]", "\\1", 1);
+%% }
+%% /^Architecture:/{
+%% arch = $2;
+%% }
+%% /^Filename:/{
+%% prefix_dir = gensub("Filename: pool/main/([^/]+).*", "\\1", 1);
+%% if (!source) {
+%% source = gensub("Filename: pool/main/[^/]+/([^_/]+).*", "\\1", 1);
+%% }
+%% if (!ver) {
+%% ver = gensub("[^_]+_([^_]+).*", "\\1", 1);
+%% }
+%% source_id = source "_" ver;
+%% build_id = source_id "_" arch;
+%% dir_url = "https://buildinfos.debian.net/buildinfo-pool/" \
+%% prefix_dir "/" source
+%% url = dir_url "/" build_id ".buildinfo";
+%% if (!(build_id in registered_builds)) {
+%% print source_id " " build_id " " url;
+%% registered_builds[build_id] = 1;
+%% alt_url = dir_url "/" build_id "-source.buildinfo";
+%% print source_id " " build_id " " alt_url;
+%% }
+%% }
+%% ' Packages > buildinfo-urls
+
+%% gawk '{print $1}' buildinfo-urls | sort | uniq | wc -l
+
+%% mkdir -p buildinfos
+%% process_line() {
+%% local SOURCE_ID=$1
+%% local BUILD_ID=$2
+%% local URL=$3
+%% if [ ! -e buildinfos/$BUILD_ID.buildinfo ]; then
+%% if [ -e buildinfos-dumb/$BUILD_ID.buildinfo ]; then
+%% mv buildinfos-dumb/$BUILD_ID.buildinfo buildinfos/$BUILD_ID.buildinfo;
+%% else
+%% wget --no-verbose -O buildinfos/$BUILD_ID.buildinfo $URL \
+%% >> buildinfos-download.log
+%% fi
+%% fi
+%% }
+%% while read LINE; do
+%% process_line $LINE;
+%% done < buildinfo-urls
+
+An official Debian source package typically consists of
+
+\begin{enumerate}
+\item
+ software sources taken from the upstream -- possibly with inappropriately
+ licensed components removed and other changes applied to meet Debian
+ guidelines -- taking form of one or more compressed archives,
+\item
+ package's recipe, taking form of a compressed archive, including, among others,
+ \begin{itemize}
+ \item
+ a list of software's build and runtime dependencies, placed -- with other
+ metadata -- in a file named \code{debian-control}, with an example in
+ Listing~\ref{lst:debian-control-excerpts},
+ \item
+ optional patch files that describe Debian-specific changes which are to be
+ applied to upstream software as part of the automated build process, with an
+ example in Listing~\ref{lst:debian-shc-patch}, and
+ \item
+ a script directing the build process, placed in a file named
+ \code{debian/rules}, invoked as a Makefile, with an example in
+ Listing~\ref{lst:debian-rules-excerpt}, and
+ \end{itemize}
+\item
+ a text file with \code{.dsc} suffix containing cryptographically signed source
+ package metadata, including hashes of compressed archives from the above
+ points.
+\end{enumerate}
+
+\lstinputlisting[
+ float=htpb,
+ caption=Excerpts from a $176$-lines long \code{debian/control} file of the
+ \code{nodejs} Debian package.,
+ label=lst:debian-control-excerpts,
+ numbers=none
+]{debian-control-excerpt.txt}
+
+\lstinputlisting[
+ float=htpb,
+ language=shc-patch,
+ caption={A patch used by Debian package \code{shc} to provide an upstream
+ script with the correct path of the \code{rc} executable, as present in
+ Debian.},
+ label=lst:debian-shc-patch
+]{debian-shc.patch}
+
+\lstinputlisting[
+ float=htpb,
+ caption=Excerpt from a $2045$-lines long \code{debian/rules} Makefile of the
+ \code{binutils} Debian package.,
+ label=lst:debian-rules-excerpt,
+ numbers=none
+]{debian-rules-excerpt.txt}
+
+The package maintainer is likely to perform one or several builds when working
+on a new or updated source package. However, except for special cases, it is
+only the source package and not the maintainer-built binary packages that gets
+uploaded to what is called the \textbf{Debian archive}. Uploaded source
+packages are ``built automatically by the build daemons in a controlled and
+predictable environment''~\cite[Chapter~5]{debian03developers}.
+
+Besides producing binary packages, the build daemons also record the metadata of
+performed builds, which is later published with cryptographic signatures as
+\code{.buildinfo} files\footnote{which can be considered a type of MBOM that was
+described in \ref{sec:recommendations-for-sbom}}. For a given binary package,
+it is usually possible to locate and download its corresponding
+\code{.buildinfo} file. That file contains, among others, a list of names and
+versions of Debian packages that were installed in the minimal build
+environment. An example of a \code{.buildinfo} file is shown in Listing
+\ref{lst:haskell-sth-buildinfo-excerpts}.
+
+\lstinputlisting[
+ float=htpb,
+ caption=Excerpt from a $256$-lines long \code{.buildinfo} file of the
+ \code{haskell-base-compat-batteries} Debian package.,
+ label=lst:haskell-sth-buildinfo-excerpts,
+ numbers=none
+]{haskell-sth-buildinfo-excerpts.txt}
+
+The \code{.buildinfo} files can be used by parties other that The Debian
+Project to rebuild the official packages, write in-toto metadata of the process,
+sign it, and subsequently serve it to the users.
+
+\section{\code{guix challenge} command of GNU Guix}
+
+GNU Guix is an operating system distribution and a package manager that appeared
+in 2013~\cite{conf/els/Courtes13}. It implements functional package management
+model described by Eelco Dolstra in ``The Purely Functional Software Deployment
+Model'' in 2006 \cite{phd/basesearch/Dolstra06} and pioneered by Nix package
+manager. For avoidance of confusion with an unrelated ``GUIX'' U.S.\ trademark
+registered in 2019 and owned by Microsoft, the GNU Guix package manager shall be
+referred to with its ``GNU'' prefix throughout this document.
+
+Similarly to Debian, GNU Guix relies on software written by upstream authors and
+makes it available in the form of installable packages. However, the data
+formats, build mechanisms, and nomenclature differ. The equivalent of Debian's
+binary package is referred to as a \textbf{substitute}. Since 2015, GNU Guix
+provides a \code{guix challenge} command which ``allows users to challenge
+the authenticity of substitutes provided by a
+server''~\cite{gnu-guix-0.9.0-released}. End users can invoke this command to
+compare the outputs of package builds performed on multiple infrastructures and
+report which packages were not built reproducibly -- either due to
+nondeterminism of the build process or because of build's compromise.
+
+\subsection{How a GNU Guix package is made and built}
+\label{sub:how-gnu-guix-package-is-made}
+
+GNU Guix package collection is determined by a set of package recipes. Unlike
+Debian package recipes, these do not take the form of compressed achives. Here,
+packages are defined in Scheme programming language from Lisp family. A recipe
+consists of code that instantiates and populates a \code{<package>} data
+structure representing a package that can be built. Recipe's code can use the
+facilities of the Turing-complete Scheme programming language to dynamically
+compute parts of this new \code{<package>} instance. A \code{<package>}
+instance does -- in most cases -- get bound to a Scheme variable, which other
+recipes' code can reference. Lists of package's explicit build and runtime
+dependencies are typically constructed using references to other package
+variables. A package recipe example is shown in Listing
+\ref{lst:guix-python-axolotl-package}. It defines a variable of the same name
+as the package and declares its explicit dependencies by referencing
+\code{python-protobuf} and other two package variables. A Scheme code snippet
+is supplied to be executed during the hermetic build process. It customizes the
+process by deleting unnecessary files.
+
+\lstinputlisting[
+ float=htpb,
+ language=guix-package-definition,
+ caption=Recipe of \code{python-axolotl} GNU Guix package.,
+ label=lst:guix-python-axolotl-package,
+ numbers=none
+]{guix-python-axolotl-package.scm}
+
+The recipes of all official GNU Guix packages are kept and maintained in a
+single VCS repository. As of \tresholdDate{}, this is a repository that houses
+both the recipes collection and the GNU Guix application, although this setup is
+not imposed by design and might change in the future. Recipes in the repository
+can also have accompanying patch files. However, patches are used less
+frequently here than in Debian package recipes. Regular expression
+substitutions performed by Scheme code are preferred by GNU Guix developers for
+trivial modifications of upstream source.
+
+Package recipes in GNU Guix generally reference remote software sources using
+URLs and hashes that are considered cryptographically secure. The hashes are
+used for verification of sources' integrity upon download and make it possible
+to safely download them from fallback servers, among which is the archive of
+Software Heritage \cite{journals/corr/abs-2405-15516}. Several remote resource
+formats are supported, including traditional compressed archives as well as
+repositories of popular VCSes. Referencing VSC repositories of upstream
+projects -- although not practiced universally in the recipes collection --
+allows the correspondence of build inputs to public-reviewed sources to be more
+easily tracked.
+
+The GNU Guix package manager is able to build packages locally, on the system on
+which their installation was requested. Each deployment of GNU Guix comes -- by
+design -- with a copy of the recipes collection, such that only the source
+inputs -- identified by cryptographic hashes -- need to be downloaded for a
+build to be performed. Local builds are fully automated, are performed in
+isolated environments created in the background and are a first-class citizen in
+the model of GNU Guix. For practical reasons, it is made possible to instead
+download prebuilt packages -- the substitutes that substitute their
+locally-built equivalents.
+
+The \code{guix challenge} command allows the build outputs advertised by
+configured substitute servers to be compared with each other and with the
+outputs of local builds, when available.
+
+Lack of automatized integration of reproducibility verification with package
+deployment is a notable limitation of the \code{guix challenge} command of GNU
+Guix. The command has to be invoked explicitly by the user. As of
+\tresholdDate{}, there is no official way to \textbf{automatically} challenge
+the binary substitutes that GNU Guix downloads as part of other actions, such as
+software installation. Thus, in practice, the command is less easily usable as
+an end user's preventive security tool and more as an investigation and internal
+verification aid.
+
+Bitcoin Core, possibly the most famous piece of software using GNU Guix to
+reproducibly verify its binaries, does not rely on the
+\code{guix challenge} command and instead uses its own custom scripts to
+perform code signing.
+
+\section{Continuous tests}
+
+The tools presented so far allow the end users to verify binaries before they
+are put in use. The user can first learn whether a set of software packages was
+rebuilt with bit-to-bit identical results on independent infrastructure and can
+then make an informed decision whether to install the packages. The benefit of
+this type of verification is that it leaves no single point of failure, except
+for end user's device. However, if the latter were compromised in the first
+place, no software-based scheme would reliably remediate that. This scenario is
+therefore out of scope of this work.
+
+The drawback of this type of verification is that accidential
+non-reproducibility due to an overlooked source of nondeterminism in the build
+process leads to verification failures, as depicted in
+Figure~\ref{fig:rebuilds-inconclusive-diagram}. A lag of independent rebuilders
+can likewise make verification impossible, as also shown in the figure. If
+reproducible builds are to be used as a preventive security measure, any such
+failure would need to stop the end user from performing the attempted software
+installation or update. Until the percentage of reproducibly buildable software
+in distributions is close to 100\% and enough resources are invested in
+independent infrastructure performing continuous rebuilding, this problem can be
+prohibitive.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[
+ width=\linewidth,
+ inkscapelatex=false
+ ]{rebuilds-inconclusive-diagram.svg}
+ \caption{Overview of a verification process inconclusive due to rebuilder's
+ delay and lack of determinism.}
+ \label{fig:rebuilds-inconclusive-diagram}
+\end{figure}
+
+However, there are several projects where verification of reproducibility is
+performed by someone else than the end user. Although this does not eliminate
+the single point of failure from software installation process, such
+verification can still make supply chain attacks harder. For example, if an
+organization performs internal tests of reproducibility and analyzes their
+results, it is more likely to detect code contamination early on and react to
+it.
+
+As of \debianTresholdDate{}, the Reproducible Builds project performs continuous
+tests of the reproducibility of
+
+\begin{itemize}
+\item files from coreboot, FreeBSD, NetBSD, and OpenWrt projects, as well as
+\item packages from Debian repositories, with package reproducibility statistics
+ being reported, as in Figure~\ref{fig:bookworm-stats-pkg-state}.
+\end{itemize}
+
+\begin{figure}[htpb]
+\centering
+\includegraphics[width=\linewidth]{bookworm-stats-pkg-state.png}
+\caption{Reproducibility of Debian Bookworm packages over time, as presented on
+ Reproducible Builds' continuous tests website.}
+\label{fig:bookworm-stats-pkg-state}
+\end{figure}
+
+As of \debianTresholdDate{}, $33\,214$ source packages from Debian Bookworm were
+reported to have been rebuilt reproducibly for the x86\_64 architecture. That
+means approximately 97\% reproducibility in the collection. The remaining
+packages either could not be built on the Reproducible Builds infrastructure,
+with various possible reasons, or were built with outputs differing on binary
+level.
+
+The Reproducible Builds project also lists several others -- including GNU Guix
+mentioned earlier and its predecessor NixOS -- that monitor the reproducibility
+of their files and/or repository packages without relying on the Reproducible
+Builds' infrastructure~\cite{reproducible-builds-continuous}. One notable
+undertaking in this category is the development of Rebuilderd tool for
+reproducible rebuilds of packages from Arch Linux and recently other
+distributions~\cite{drexel2025reproduciblebuildsinsightsindependent}. An
+application also exists that can consult a Rebuilderd instance to automatically
+verify packages installed in user's Arch Linux system~\cite{archlinux-repro}.
+
+The continuous tests platform used by GNU Guix is capable of generating
+reproducibility reports, which are viewable on pages at
+\url{https://data.guix.gnu.org}. Part of such report is shown in
+Figure~\ref{fig:guix-pkg-repro-stats}. According to it, there were $39\,344$
+packages available for the x86\_64 architecture as of \tresholdDateAndCommit{}.
+$35\,415$ of them -- approximately 90\% -- were rebuilt reproducibly, albeit
+with $2\,087$ remaining untested. Frequent package updates and builders' lag
+are possible reasons for the large number of untested packages. Out of all the
+successfully rebuilt GNU Guix packages, approximately 95\% had outputs that were
+bit-to-bit identical with those produced on another infrastructure.
+
+\begin{figure}[htpb]
+ \centering
+ \includegraphics[width=\linewidth]{guix-pkg-repro-stats.png}
+ \caption{Reproducibility of GNU Guix packages as reported by its continuous
+ tests platform.}
+ \label{fig:guix-pkg-repro-stats}
+\end{figure}
+
+Unfortunately, several of the reproducibility tests listed on the Reproducible
+Builds website have become unmaintained. The testing for Fedora and Alpine
+operating system distributions was disabled at some point. Although the
+reproducibility statistics of GNU Guix packages are still delivered, their web
+pages sometimes cannot be viewed due to timeouts, as also witnessed by Internet
+Archive's Wayback
+Machine\footnote{\url{https://web.archive.org/web/20250625124729/https://data.guix.gnu.org/repository/1/branch/master/latest-processed-revision/package-reproducibility}}.
+
+\chapter{Applicability of reproducibility workflows to different software ecosystems}
+\label{chp:applicability-of-workflows}
+
+Current reproducible software distributions, like GNU Guix and Debian, are
+\textbf{system software distributions} -- ones that contain a collection of
+packages that can form a complete operating system. As such, a mixture of
+software technologies can be found in them.
+
+Certain programming languages and computing platforms form software ecosystems
+centered around them, for instance, the ecosystem of the Python programming
+language with CPython\footnote{the most popular implementation of the Python
+programming language, written in C} and PyPy being its most popular runtimes.
+These ecosystems evolve their specific software package formats and workflows
+for building these packages. Many popular ecosystems have their dedicated
+package repositories that usually serve as primary distribution channels of
+software written for the ecosystem's computing platform. Such
+ecosystem-specific software repositories are often, although imprecisely,
+referred to as language-specific repositories. They are typically open to the
+public for registration and package creation. As such, they form environments
+of software with varying levels of quality and significance.
+
+Many ecosystem-specific repositories distribute software without all the
+metadata that is necessary to automate rebuilding it. Moreover, if a project
+uses multiple packages from such repository, it relies on security of each of
+the machines used by these packages' developers for builds and uploads. A
+partial remedy -- facility to publish packages together with build provenance
+data cryptographically signed by a build service -- was employed by
+ecosystem-specific repositories \textbf{npm Registry} and
+\textbf{PyPI}\footnote{Python Package Index} in 2023 and 2024, respectively. A
+dedicated build service -- with the most popular ones being GitHub Actions and
+GitLab CI/CD -- can be considered better secured than an average developer's
+computer, for the benefit of packages that are confirmed to have been built
+there. In addition, build provenance data identifies the source repository used
+in the build. However, even when secured, build service remains a single point
+of failure of the process. In addition, support for only one or few selected
+build services -- as offered by the npm Registry as of \tresholdDate{} -- leads
+to vendor lock-in.
+
+\section{Degree of inclusion in Debian and GNU Guix}
+
+Software utilizing the respective computing platforms and distributed primarily
+through an ecosystem-specific software repository might, at some point, also get
+included in a system software distribution. However, so far it did not happen
+with certain popular and strategic pieces of software. One example is Electron
+framework that is used, among others, by Signal application and Visual Studio
+Code IDEs. As of \tresholdDate{}, Electron is declared a development dependency
+by $4\,533$ packages in the npm Registry. At the same time, software
+distributions that test for package reproducibility usually lack Electron and
+Electron-based applications, as do Debian and GNU Guix. Another software
+distribution that tests for package reproducibility, NixOS, redistributes
+Electron's upstream binaries without actually building the software. In this
+case, the build itself is not being verified through reproducibility.
+
+Certain programming languages and computing platforms have seen more packaging
+progress in system software distributions. Let us consider the PyPI, npm, and
+crates ecosystems, which are centered around their respective repositories and
+technologies, as shown in Table \ref{tab:software-ecosystems}. For this work,
+repositories were chosen as a basis for distinguishing the ecosystems. It is a
+feasible criterion, although not the only possible one. There are overlaps of
+various sizes between different repositories, runtimes, and project management
+tools. Also, in some cases a software package has formal of informal
+dependencies that are not distributed through the reposotory that the package
+itself uses.
+
+\begin{table}[htpb]
+ \caption{Considered software ecosystems.}
+ \centering
+ \label{tab:software-ecosystems}
+ \footnotesize
+ \columnsCount{4}
+ \begin{tabular}{
+ >{\raggedright\arraybackslash}p{.31\netTableWidth}
+ >{\raggedright\arraybackslash}p{.23\netTableWidth}
+ >{\raggedright\arraybackslash}p{.23\netTableWidth}
+ >{\raggedright\arraybackslash}p{.23\netTableWidth}
+ }
+ \rowcolor{gray!40}
+ &
+ \textbf{PyPI} &
+ \textbf{npm} &
+ \textbf{crates} \\
+
+ \textbf{repository} &
+ PyPI &
+ npm Registry &
+ crates.io \\
+
+ \textbf{{primary} \mbox{programming} \mbox{languages}} &
+ \cellitems \item Python, \item Cython &
+ \cellitems \item JavaScript, \item TypeScript, \item WASM &
+ \cellitems \item Rust \\
+
+ \textbf{\mbox{sample} \mbox{runtimes} or \mbox{compilers}} &
+ \cellitems \item CPython, \item PyPy\ &
+ \cellitems \item Node.js, \item Deno, \item Bun &
+ \cellitems \item rustc \\
+
+ \textbf{sample project management tools} &
+ \cellitems \item setuptools, \item Poetry, \item Hatch &
+ \cellitems \item npm, \item Yarn, \item pnpm &
+ \cellitems \item Cargo
+ \end{tabular}
+\end{table}
+
+We shall compare the numbers of software projects from the chosen ecosystems
+that are packaged in system software distributions described in detail
+in~\ref{chp:existing-security-tools}. The numbers presented in Table
+\ref{tab:ecosystem-packaged-numbers} were estimated based on snapshots of
+package collections offered by Debian Bookworm as of \debianTresholdDate{} and
+GNU Guix as of \tresholdDateAndCommit{}.
+
+% grep -R node-build-system "$GUIX_CHECKOUT"/gnu/packages | wc -l
+% grep -Re '\(pyproject\|python\)-build-system' "$GUIX_CHECKOUT"/gnu/packages | wc -l
+
+%% $ grep -R dh-nodejs ../buildinfos | awk -F _ '{print $1 "_" $2}' | sort | uniq | wc -l
+%% 998
+
+%% grep -R dh-cargo ../buildinfos | awk -F _ '{print $1 "_" $2}' | sort | uniq | wc -l
+%% 1424
+
+%% $ grep -R dh-python ../buildinfos | awk -F _ '{print $1 "_" $2}' | sort | uniq | wc -l
+%% 5312
+
+\begin{table}[htpb]
+ \caption{Estimated numbers of Debian and GNU Guix packages corresponding to
+ software from considered ecosystems.} \centering
+ \label{tab:ecosystem-packaged-numbers}
+ \footnotesize
+ \columnsCount{4}
+ \begin{tabular}{
+ >{\raggedright\arraybackslash}p{.31\netTableWidth}
+ >{\raggedright\arraybackslash}p{.23\netTableWidth}
+ >{\raggedright\arraybackslash}p{.23\netTableWidth}
+ >{\raggedright\arraybackslash}p{.23\netTableWidth}
+ }
+ \rowcolor{gray!40}
+ &
+ \textbf{PyPI} &
+ \textbf{npm} &
+ \textbf{crates} \\
+
+ \textbf{GNU Guix packages} &
+ $3\,699$ &
+ $55$ &
+ $3\,704$ \\
+
+ estimated as use counts of which \code{build-system}s in recipes &
+ \code{pyproject-build-system}, \code{python-build-system} &
+ \code{node-build-system} &
+ \code{cargo-build-system} \\
+
+ \textbf{Debian packages} &
+ $5\,312$ &
+ $998$ &
+ $1\,424$ \\
+
+ estimated as counts of source packages referencing which
+ debhelper package &
+ \code{dh-python} &
+ \code{dh-nodejs} &
+ \code{dh-cargo}
+ \end{tabular}
+\end{table}
+
+A conclusion arises that for some reason npm packages are less likely to be
+packaged when adhering to the rigor of existing software distributions that
+utilize hermetic and reproducible builds. We can try to name the main causes
+and judge whether the difficulties could be worked around without sacrificing
+security.
+
+\section{Dependency tree sizes}
+
+It can be noticed that on avarage, npm projects have more recursive dependencies
+than, for example, Python projects~\cite{btao-wot-for-npm}. This means that
+packaging an end-user application written in JavaScript\footnote{also referred
+to by its official name: ECMAScript} typically requires more labor of bringing
+the intermediate packages to the distribution -- an issue that has been talked
+about in the GNU Guix community for at least ten
+years~\cite{lets-package-jquery}.
+
+Large dependency trees can be partially caused by the JavaScript language
+historically having a relatively modest standard library. While such design can
+bring some benefits, it might also lead to proliferation of small libraries that
+have overlapping functionality. Independent, competing packages with similar
+purposes are then more likely to appear together in a single dependency tree.
+
+Creation of many small packages and eager use of dependencies for simple tasks
+-- all of which leads to larger dependency trees -- can also be attributed to
+the culture of developers working with the npm Registry~\cite{Abdalkareem2020}.
+
+\section{Age of the ecosystem}
+
+The npm tool first appeared in 2010. The PyPI ecosystem is older, with its
+repository being launched in 2002. It can therefore be argued that software
+from the latter has had more time to be included in Debian and several other
+software distributions. However, this is not sufficient to explain the lack of
+inclusion of npm packages in GNU Guix, which itself came to existence in 2012.
+Additionally, the crates ecosystem, which came to existence in 2014, is younger
+than all of the previous repositories. Despite that, software from it has
+larger presence in Debian an GNU Guix than software from the npm ecosystem.
+
+\section{Conflicting dependencies}
+\label{sec:conflicting-deps}
+
+System software distributions typically only allow a single version of a package
+to be installed at any given time. This rule is sometimes relaxed in various
+ways. For example, as of \debianTresholdDate{}, Debian Bookworm had distinct
+packages named \code{gcc-12} and \code{gcc-11}. Both of them provide the GNU C
+Compiler, albeit in different major versions. These packages can be installed
+side-by-side. GNU Guix, on the other hand, has facilities to create independent
+environments with different sets of packages in each. If multiple versions of
+the same package reside in different environments, they do not cause a conflict.
+There are also other nuances that provide some degree of flexibility.
+
+Nonetheless, an application that requires multiple versions of a single
+dependency is more difficult to include in such software distributions. This is
+a relative small issue for, e.g., Python applications. Their runtime does not
+support simultaneous loading of multiple versions of the same Python library in
+the first place. I.e., if it is possible to install package's dependencies from
+PyPI and use that package, it means there are no conflicting dependencies. At
+the same time, npm and the Node.js runtime allow multiple versions of the same
+library to appear in the dependency tree of a project.
+
+\subsection{Support in npm\&Node.js}
+\label{sub:conflicting-deps-in-npm}
+
+Let us consider the dependency tree recorded in \code{package-lock.json} file of
+the sigstore project
+repository\footnote{\url{https://raw.githubusercontent.com/sigstore/sigstore-js/759e4d9f706aa0bea883267009fa1da8f2705eab/package-lock.json}}.
+We shall look at the revision designated by Git commit \code{759e4d9f70} from
+Aug 5, 2024. Entries of interest are shown in Listing
+\ref{lst:occurances-of-tslib}. A library identified as \code{tslib} appears two
+times in the tree. There is a copy of version 2.6.3 and a copy of version
+1.14.1. This happened because a dependency, \code{tsyringe}, has a requirement
+on a version of \code{tslib} that is at least 1.9.3 but lower than 2.0.0.
+Version 1.14.1 present in the tree satisfies this requirement. Another
+dependency, \code{pvtsutils}, requires \code{tslib} in version that is at least
+2.6.1 but lower than 3.0.0. Several other entries, omitted for clarity, have a
+different requirement on \code{tslib}. All these are satisfied by version
+2.6.3.
+
+\lstinputlisting[
+ float=htpb,
+ language=package-lock-json,
+ caption=Multiple occurances of \code{tslib} package in a dependency tree.,
+ label=lst:occurances-of-tslib,
+ numbers=none
+]{occurances-of-tslib.txt}
+
+When this project's code is run and the \code{tsyringe} library tries to load
+\code{tslib}, Node.js runtime instantiates version 1.14.1 of \code{tslib}
+from \code{tsyringe}'s private subtree. \code{tsyringe} then works with that
+instance of \code{tslib}. For all other parts of the project that attempt to
+load \code{tslib}, version 2.6.3 is instantiated and used.
+
+\subsection{Effects of Semantic Versioning}
+\label{sub:effects-of-semver}
+
+In the npm ecosystem a system called ``Semantic Versioning''
+\cite{prestonwerner2013semantic} is widely applied. This system assumes that
+software version is given as three numbers -- major, minor, and patch. E.g.,
+2.6.3. It also permits optional labels for pre-release and build
+metadata. When Semantic Versioning is followed, then a new software release
+that breaks backward compatibility with the previous release has its major
+version number increased and the other two numbers reset to zero. A release
+that adds new functionality without breaking backward compatibility has only its
+minor number increased, with patch number reset to zero. And a release that
+adds no new functionality -- typically a bugfix release -- has its patch number
+increased.
+
+Assume project \code{foo} utilizes a semantically versioned dependency
+\code{bar}. The developers could verify that \code{foo}'s code integrates
+properly with a particular version of \code{bar}, e.g., version 3.4.5. The
+developers would then record a requirement that in the future, either this
+version of \code{bar} or a later one -- but without compatibility-breaking
+changes -- can be used by \code{foo}. This means only \code{bar} versions
+between 3.4.5 and 4.0.0, excluding 4.0.0 itself, would satisfy the requirement.
+If \code{bar} then increases its major number in a new release, the developers
+of \code{foo} can ensure that its code integrates properly with the newer
+\code{bar}. They would then update the requirements in \code{foo} and the next
+release of \code{foo} could officially use the 4.x.x series of \code{bar}. It
+would, however, still be forbidden from using the hypothetical 5.x.x series that
+could bring subsequent compatibility breakages.
+
+In our example from~\ref{sub:conflicting-deps-in-npm}, the libraries
+\code{tsyringe} and \code{pvtsutils} both apply this approach to their
+dependency, \code{tslib}. As a result, \code{tsyringe} is protected from
+possible breaking changes introduced by version 2.0.0 of \code{tslib},
+but at the same time it is impossible to satisfy all requirements of the project
+with just a single copy of \code{tslib}. In practice, there are sometimes tens
+or hundreds such conflicts in a single dependency tree.
+
+The breaking changes that necessitate increasing package's major version number
+sometimes concern a part of package's functionality that the specific user does
+not rely upon. When project's developers know or suspect that the requirements
+specified by certain dependencies could be safely loosened, they can forcibly
+override them. Such overrides, supported natively in npm, are used by some when
+addressing security vulnerabilities deep in a project's dependency tree. The
+same approach could be used to eliminate all dependency conflicts. However,
+with many overrides there's a lower chance of avoiding a breakage due to
+inter-package compatibility issues.
+
+\section{Difficult bootstrappability}
+
+GNU Guix and Debian are self-contained in the sense that build dependencies of
+their packages are also their packages. When packaging a program that requires,
+e.g., a C compiler to build, no problems arise -- C compilers are already
+present in these system software distributions and one of them can be used as a
+build dependency of the new package. However, packaging a program written in a
+new programming language requires a compiler or interpreter of that programming
+language to be present in the distribution in the first place. The same applies
+to other types of build tools, e.g., bundlers that amalgamate many JavaScript
+files into a few or a single file.
+
+Packaging a program for such distribution involves first packaging all its build
+tools. Making a package buildable with only the tools from the distribution is
+sometimes referred to as \textbf{bootstrapping}.
+
+\subsection{Self-depending software}
+\label{sub:self-depending-software}
+
+Certain tools exist that depend on themselves to build, making bootstrapping
+challenging. Selected examples from the npm ecosystem are presented in Table
+\ref{tab:self-depending-packages}. Packages in the table were ranked based on
+how many other npm Registry packages specified them as development dependencies
+as of \tresholdDate{}. The presented selection is by no means exhaustive, more
+highly popular self-depending npm packages might exist.
+
+\begin{table}[htpb]
+ \caption{npm Registry packages that require themselves to build.}
+ \label{tab:self-depending-packages}
+ \centering
+ \footnotesize
+ \columnsCount{3}
+ \begin{tabular}{
+ >{\raggedright\arraybackslash}p{.13\netTableWidth}
+ >{\raggedright\arraybackslash}p{.27\netTableWidth}
+ >{\raggedright\arraybackslash}p{.6\netTableWidth}
+ }
+ \rowcolor{gray!40}
+ \textbf{name} &
+ \textbf{popularity ranking} &
+ \textbf{notes} \\
+
+ \code{typescript} &
+ $1$ ($473\,235$ dependees) &
+ the original implementation of TypeScript programming language \\
+
+ \code{@babel/core} &
+ $10$ ($138\,704$ dependees) &
+ part of a JavaScript compiler, requring itself indirectly through
+ dependencies that themselves build with \code{@babel/core} \\
+
+ \code{rollup} &
+ $26$ ($95\,965$ dependees) &
+ a bundler \\
+
+ \code{gulp} &
+ $40$ ($61\,077$ dependees) &
+ a build system, requiring itself through its runtime dependency
+ \code{gulp-cli} \\
+
+ \code{sucrase} &
+ $1\,793$ ($528$ dependees) &
+ an alternative to Babel, used as a proof of concept for bootstrapping a GNU
+ Guix package
+ \end{tabular}
+\end{table}
+
+In GNU Guix, the preferred approach to packaging a self-depending tool is making
+it bootstrappable~\cite{courtès2022buildingsecuresoftwaresupply}. This can
+happen by packaging a chain of historical versions of the tool, where each can
+be built with the nearest older packaged one, down to an early version that did
+not have a self-dependency. Sometimes it is possible to eliminate or shorten
+such ``bootstrap chain'', for example by replacing a complex build tool with
+scripts or by using a bootstrappable drop-in replacement to some tool. The
+latter was an approach used to package the official, self-hosting implementation
+of the Rust programming language for GNU Guix in 2018. There, an unofficial
+Rust compiler, written in C++, was used to compile an official Rust release from
+July 2017~\cite{bootstraping-rust}.
+
+Bootstrapping helps prevent the ``Trusting Trust'' attack demonstrated by Ken
+Thompson in 1984, but as of today there is little evidence of such attack type
+ever being used by threat actors. In some cases software distributions under
+consideration make exceptions and allow a non-bootstrappable program prebuilt by
+another party to be made into a distribution package. For example, the set of
+OCaml and Haskell compilers in GNU Guix depends on such third party binaries
+that cannot be rebuilt from any package recipe in the distribution.
+
+In 2022 a proof of concept GNU Guix bootstrap of \code{sucrase}, a
+self-depending build tool from the npm ecosystem, was
+done~\cite{re-bringing-npm-to-guix}.
+
+\subsection{Recursive dependency closure}
+
+By package's recursive development dependency closure we mean a set containing
+all its declared runtime dependencies and development dependencies, their
+runtime dependencies and development dependencies, etc. In other words, the
+closure is the minimal set that contains the dependencies of its every member
+and also of the package for which the closure is being computed. The size of
+package's recursive dependency closure can illustrate the bootstrapping
+challenge complexity. An attempt to compute such closure was made for npm
+package \code{typescript} as part of this work. npm Registry metadata limited
+to package releases from before \tresholdDate{} was used. For simplicity,
+version constraints were disregarded and packages' all historical dependencies
+were considered. The result was a $60\,843$-elements big set of package names,
+with additional $2\,433$ referenced names that do not exists in the Registry.
+These were largely the results of mistakes and possibly private/unpublished
+packages. Of course, non-crucial tools like linters tend to be declared
+developments dependencies and the closure of truly necessary dependencies would
+be much smaller, as also reported in~\ref{sec:typical-dep-tree-sizes}.
+Nonetheless, this example shows how difficult it is to reason about what is
+needed for bootstrapping tasks.
+
+\section{Inconvenience of system software distributions}
+\label{sec:inconvenience-of-distros}
+
+Despite looser security practices and more frequent reports of malicious
+packages in repositories like the npm Registry
+\cite{malicious-npm-techtarget-nichols,malicious-npm-infosec-muncaster,malicious-npm-cybernews-naprys,malicious-npm-bleep-toulas,malicious-npm-hacker-news-ravie},
+many developers still prefer to work with them rather than with system software
+distributions. Packages in the latter are adjusted to work with and inside
+their distributions and are typically not compatible with the usual workflow of
+developers of, e.g., npm projects. For example, it could be unstraightforward
+to use npm libraries from Debian for producing distribution files of a mobile
+application. Another deterrent is the delay with which newer releases of
+packages reach system software distributions.
+
+This limited interest in availability of software from certain ecosystems in
+distributions like Debian and GNU Guix also leads to decreased incentive for
+authors of these distributions to work on it.
+
+\chapter{Overview of the npm ecosystem}
+\label{chp:npm-ecosystem-overview}
+
+Software from the npm ecosystem was found to be more challenging to be made into
+system software distribution packages. To provide deeper insight into this
+problem, this chapter provides more information about this ecosystem, with focus
+on dependency relations between npm packages.
+
+npm Registry -- the software repository around which the npm ecosystem is
+centered -- was created as a distribution channel for JavaScript libraries,
+frameworks, and applications using Node.js runtime. It was targeted towards
+server-side developers. The platform allows the general public to register
+accounts and publish software packages. Throughout the years, the npm Registry
+also became home to client-side JavaScript, i.e., software to be executed in web
+browsers. The repository is nowadays also used by related programming
+languages, notably TypeScript. As of \tresholdDate{}, the repository was
+serving over $3.5$ million published packages, many of which come in multiple
+versions.
+
+\section{Recognized dependency types}
+
+Projects using npm can use a structured format to list their dependencies, i.e.,
+the npm packages they use. Four types of dependencies can be specified in
+package's metadata kept in a file named \code{package.json} in project's source
+directory. These types are described in Table~\ref{tab:npm-dep-types}.
+Throughout the rest of this work, the opposite of a dependency shall be called a
+\textbf{dependee}.
+
+\begin{table}[htpb]
+ \caption{Dependency types recognized by npm.}
+ \label{tab:npm-dep-types}
+ \centering
+ \footnotesize
+ \columnsCount{2}
+ \begin{tabular}{
+ >{\raggedright\arraybackslash}p{.3\netTableWidth}
+ >{\raggedright\arraybackslash}p{.7\netTableWidth}
+ }
+ \rowcolor{gray!40}
+
+ \textbf{Metadata key} &
+ \textbf{Meaning} \\
+
+ \code{dependencies} &
+ Packages needed at runtime. \\
+
+ \code{devDependencies} &
+ Packages needed or useful for development, often minifiers/bundlers, test
+ frameworks, linters, and version control integration tools. \\
+
+ \code{optionalDependencies} &
+ Similar to \code{dependencies} but only needed for some additional
+ functionality. npm supports installing a package without its optional
+ dependencies, but by default it does install them. \\
+
+ \code{peerDependencies} &
+ Used to specify compatible versions of tools for which the dependee is a
+ plugin.
+\end{tabular}
+\end{table}
+
+\section{Statistical analysis of the npm ecosystem}
+
+To determine which projects using npm Registry are the most popular among
+developers, the dependency relations between packages were counted and analyzed.
+First, the metadata of all published packages in JSON format was downloaded.
+Download took place on the days following \tresholdDate{}. The metadata was
+processed to only include information about releases made after \tresholdDate{},
+yielding $3\,519\,767$ package entries. Curl program was used to make requests
+to Registry's CouchDB view at \url{https://replicate.npmjs.com/_all_docs}. It
+is worth noting that this API endpoint's functionality has since changed and
+other means would be necessary to download the entire Registry metadata again in
+the future.
+
+For the purpose of rankings discussed next, the dependees being multiple
+versions of a single package were counted as one. Similiarly, version
+constraints in dependency specifications were ignored.
+
+\subsection{The most popular dependencies -- changes over five years}
+\label{sub:npm-changes-5-years}
+
+One of several metrics of package's popularity is its number of public
+dependees. Up to 2019 such a ranking of $1\,000$ packages most often specified
+as others' dependencies used to be published by Andrei
+Kashcha~\cite{anvaka-rank-gist}. A similar list computed from newer data for
+the purpose of this work was used to check how much the set of the most popular
+packages changed between August 2019 and April 2025. The goal was to find out
+how many of the previously popular projects keep to be chosen by developers and
+how many stopped being actively used, perhaps becoming legacy software. The
+overlap between the rankings is visualised in
+Figure~\ref{fig:common-2019-2025-percent}. For each natural $n$ in the range
+$[1, 1000]$, $n$ most popular dependencies from both rankings were selected.
+The overlap of selected packages from first and second ranking was computed and
+plotted with the values of $n$ on the X axis of the figure.
+
+\begin{figure}[htpb]
+\centering
+\includesvg[width=\linewidth,inkscapelatex=false]{common_2019_2025_percent.svg}
+\caption{Overlap of the most popular npm dependencies from 2019 and 2025.}
+\label{fig:common-2019-2025-percent}
+\end{figure}
+
+The ``winners'' in 2019 and 2025 were \code{lodash} and \code{react} with
+$69\,147$ and $263\,038$ dependees, repsectively. It can be seen that about
+$150$ most depended packages form a relatively stable forefront, with further
+part of the ranking having changed more over five years. Nevertheless, it is
+worth noting that certain packages with no new releases for several years still
+rank relatively high in 2025. Examples are \code{lodash}, \code{request}, and
+\code{q} ranking third, $17$th, and $157$th, respectively.
+
+As a conclusion, if certain software is intended to be used for more than a few
+years, dependencies for it must be considered more carefully when they are not
+from among the \givemeatilde150 most popular ones. Otherwise, the risk of
+project's direct dependency becoming legacy software grows. However, often
+other characteristics of a package will determine whether it should be
+considered reliable. Ultimately, the matter of who maintains a package and how
+it could help the project are more relevant than a ranking position.
+
+\subsection{The most popular dependencies -- popularity tresholds}
+\label{sub:runtime-deps-popularity-tresholds}
+
+The numbers of dependees corresponding to ranking positions can be used to infer
+some qualities of the ecosystem. This correspondence is presented in
+Figure~\ref{fig:dependee-counts}.
+
+\begin{figure}[htpb]
+\centering
+\includesvg[width=\linewidth,inkscapelatex=false]{dependee_counts.svg}
+\caption{Number of packages using the most popular dependencies.}
+\label{fig:dependee-counts}
+\end{figure}
+
+In 2019, the $1000$th most popular dependency package in the npm Registry had
+$346$ dependees. By April 2025, the $1000$th package in the ranking had already
+$4\,771$ dependees. This reflects the growth of the entire ecosystem, whose
+package repository had about one million packages in July 2019 and about $3.5$
+million packages in April 2025. However, this would by itself only explain a
+rise in dependee counts by about a ratio of $3.5$. The aforementioned increase
+from $346$ to $4\,771$ dependees is over four times greater. This needs to be
+attributed to growing projects' complexity, as there is a tendency to use more
+dependencies. A plausible additional explanation is higher overlap of
+functionalities between packages, i.e., situations occur where multiple popular
+libraries exist for a single task.
+
+\section{The most popular development dependencies}
+\label{chp:most-popular-dev-deps}
+
+In the context of supply chain security, the development dependencies are as
+important to research as the runtime dependencies. A popularity ranking similar
+to the previous one was compiled for packages occuring the most in the
+\code{devDependencies} collections of others. An analogous correspondence of
+ranking position to development dependee count is presented in
+Figure~\ref{fig:dev-dependee-counts}. No development dependencies ranking from
+2019 was found that could be used for comparison. Instead, the runtime
+dependencies plot from Figure \ref{fig:dependee-counts} was re-included for
+easier reference.
+
+\begin{figure}[htpb]
+\centering
+\includesvg[width=\linewidth,inkscapelatex=false]{dev_dependee_counts.svg}
+\caption{Number of packages using the most popular development dependencies.}
+\label{fig:dev-dependee-counts}
+\end{figure}
+
+The first position belongs to \code{typescript} with $840\,161$ development
+dependees. A treshold of $2\,185$ development dependees needed to be reached by
+a package to be included in the ranking. The curve for development dependencies
+is steeper, meaning there is a clearer forefront. This in turn indicates that
+the functionality overlap mentioned in
+\ref{sub:runtime-deps-popularity-tresholds} is possibly a smaller problem in
+this case.
+
+\section{Overlap of the most popular runtime and development dependencies}
+\label{sec:overlap-of-runtime-and-dev-deps}
+
+It is possible for a popular development dependency to also be specified as a
+runtime dependency by some packages. Realizing how often this happens can help
+judge whether certain kinds of issues are likely to occur in the ecosystem. The
+overlap of runtime and development dependencies is visualized in
+Figure~\ref{fig:common-nondev-dev-percent}, using the same approach as for the
+overlap in Figure \ref{fig:common-2019-2025-percent} discussed in
+\ref{sub:npm-changes-5-years}.
+
+\begin{figure}[htpb]
+\centering
+\includesvg[width=\linewidth,inkscapelatex=false]{common_nondev_dev_percent.svg}
+\caption{Overlap of the most popular npm runtime and development dependencies in
+ 2025.}
+\label{fig:common-nondev-dev-percent}
+\end{figure}
+
+Since packages listed as \code{dependencies} are often libraries or
+frameworks and those listed as \code{devDependensies} are commonly
+applications, one could expect a smaller overlap than that of about 15-30\%
+which was found. A possible explanation is that unlisting a package from
+\code{devDependencies} add instead including it among
+\code{dependencies} creates no major change for project developers. A
+command like \code{npm install} shall still resolve that dependency and
+include it in the environment it creates. It is therefore possible that a
+non-negligible number of dependencies is incorrectly categorized by the
+dependees.
+
+It used to be a known fact that among packages listed as \code{devDependencies}
+there are many which are not needed to merely rebuild a project. These could be
+automatic code formatters, tools responsible for integration with version
+control, etc.\ and they could be eliminated from automated builds to make them
+lighter on resources and to decrease the attack surface. Based on these results
+it is reasonable to expect that the similar holds for runtime dependencies.
+This provides a justification for experiments aimed at eliminating the
+extraneous dependencies without breaking the functionality of packages.
+
+\chapter{Possible paradigms for hermeticity and reproducibility}
+
+Certain popular software technologies -- with npm being one of them -- prove
+difficult to combine with existing reproducibility-focused workflows. Possible
+causes of this were discussed in~\ref{chp:applicability-of-workflows}. The
+package creation workflows of -- largely reproducible -- system software
+distributions Debian and GNU Guix were explained in
+\ref{sub:how-debian-package-is-made} and~\ref{sub:how-gnu-guix-package-is-made},
+respectively. An explanation of the npm ecosystem followed in
+\ref{chp:npm-ecosystem-overview}.
+
+With all the above being considered, the ability to handle software with
+numerous dependencies -- which can have complex relatonships -- appears relevant
+to the goal of rebuilding parts of the npm ecosystem hermetically and
+reproducibly. Based on the knowledge gathered, possible approaches to
+hermeticity and reproducibility in the context of dependency resolution shall be
+critically analyzed. They shall be classified as distinct paradigms. The
+introduced paradigms shall be discussed in the context of security and their
+applicability to the build process of npm pacakges.
+
+Paradigms 0 through 2 represent existing approaches. Paradigm 3 is an
+intermediate one that leads to 4, which is a generalization of the former.
+Paradigms 3 and 4 are an innovation originating from this work. They are meant
+to optimize the way build inputs are determined and also ensure that no
+unnecessary single points of failure are created which would not be secured
+through verified reproducibility. The new paradigms are suggested as bases for
+hypothetical new software packaging workflows that would make hermeticity and
+reproducibility easier to achieve with software from, among others, the npm
+ecosystem.
+
+\section{Paradigm 0 -- lack of actual reproducibility}
+\label{sec:paradigm-0}
+
+If one is to build an npm package in the most basic way, with use of commands
+like \code{npm install} and without a pre-computed dependency tree, then network
+connectivity is necessary. Without it, the npm tool cannot download packages
+metadata from its repository, the npm Registry. But if network access is
+allowed, then the build -- and therefore its result -- might depend on
+downloaded code and data other than dependencies' metadata. Some commonly used
+npm packages require additional connections to function. For example, the
+\code{playwright-webkit} library, upon installation by npm, downloads
+executables from a third party server. That library is an intermediate test
+dependency of a popular web library, JQuery, used by about 74\% of the most
+popular websites~\cite{w3techs-javascript-library}.
+
+Author of an npm package can assign so-called \textbf{distribution tags} to its
+specific versions. The tag can be though of as a string label that points to a
+package version. Tags can be used by dependees as an alternative way of
+specifying the depended version of the package. Some of the commonly used tag
+names are \code{next}, \code{beta}, and \code{latest}. When the developer
+publishes a new package version, the npm tool by default automatically assigns
+the \code{latest} tag to that version.
+
+The build process of an npm project relies on downloaded metadata of packages.
+As a result, if a dependency author publishes its new version or alters the
+distribution tags, it might cause later rebuilds of the package to use a
+different set of inputs. The final result can be different, so it is not
+reproducible. Additionally, the repository constitutes a single point of
+failure because compromising the repository allows altering the served metadata
+of package versions. The compromised repository could, for example, spoof
+package's distribution tags or hide the existence of certain versions of a
+package, thus allowing only vulnerable versions to be used.
+
+With files coming from a third party server, we have even less guarantee that
+they were not maliciously tampered with. A library that downloads such files
+during package build could verify them. For example, its authors could make the
+library contain cryptographic hashes of the required external files. The
+library could then check that every downloaded file has a matching hash.
+Unfortunately, we have no mechanisms to mandate that this kind of verification
+takes place in cases like that of \code{playwright-webkit}. This means ad-hoc
+file downloads are a bad security practice. They would need to be eliminated or
+restricted for reproducibility to be leveraged.
+
+Despite the above, reproducibility tests of npm packages are actually attempted,
+with one example being Pronnoy Goswami's research
+\cite{goswami-reproducibility}. It has to be noted that the results of such
+tests are likely to be unstable, yielding different results if repeated at a
+later date.
+
+\section{Paradigm 1 -- inputs determined by human-maintained references}
+
+One of the possible methods of determining the set of build inputs is used by
+GNU Guix. Its package recipes contain references to dependency packages that
+have their versions predetermined. As a result, questions like ``Should
+\code{foo} use its dependency \code{bar} in version 2.4.5 or 3.0.1?'' are
+already answered. An update to a package definition results in the updated
+package being used everywhere that particular definition was referenced. The
+update takes form of a commit or commit series in the Git VCS and is subject to
+review by co-authors of the distribution.
+
+If we fix a GNU Guix revision to be used, then in a package build -- which is
+hermetic by design -- all inputs are unambigiously determined. No repository of
+metadata can open the user to the risk of using incorrect dependency versions.
+In other words: the threat on the part of improperly defined dependency versions
+is of the same nature as that on the part of improperly written code. And -- as
+users of any kind of software -- we are deemed to accept threats of this nature.
+
+Maintenance of this kind of system is, of course, more labor-intensive. Every
+alteration of a package recipe -- also including software's version updates --
+is an update to GNU Guix' Git repository. Such update involves labor of
+distribution maintainers, similarly to Debian's case. A sample list of $26$
+consecutive commits to GNU Guix' repository -- with $15$ package updates among
+them -- is presented in Listing~\ref{lst:guix-package-update-commits}. The
+changes in the list were made by multiple contributors during an hour between
+12:00 and 13:00 on April 11, 2025. The changes are listed oldest to newets.
+Details of the newest one are additionally shown. A non-negligible amount of
+work is clearly needed to handle many changes manually. The great number of
+small changes might therefore lead to a yet unverified assumption that too big
+effort is required of distribution maintainers. If true, this could hamper the
+growth of the software collection available through GNU Guix.
+
+\lstinputlisting[
+ float=htpb,
+ language=guix-commit,
+ caption={List of consecutive changes committed to GNU Guix, with contents of
+ the bottommost one included for reference.},
+ label=lst:guix-package-update-commits,
+ numbers=none
+]{guix-package-update-commits.txt}
+
+In addition, many package managers following other paradigms can make use of
+permitted dependency version ranges declared by packages. This way npm, APT,
+and others can automatically avoid using incompatible dependency versions.
+However, Paradigm 1 does not allow such optimization to be employed.
+
+It is worth highlighting that in GNU Guix the URLs and hashes that comprise
+identification data of program sources are maintained together with package
+recipes, as can be seen in Listing~\ref{lst:guix-package-update-commits}. As
+explained, this approach might have additional consequences in the amount of
+distribution maintainers' labor. Nonetheless, it can also positively or
+negatively affect the chances of malicious sources being referenced in a recipe.
+This is an important supply chain issue to recognize, but it is independent from
+the concept of paradigms introduced in this chapter.
+
+\section{Paradigm 2 -- reproducibility not applied to dependency resolution}
+
+The problem of the dependency resolution process being unreproducible was
+explained in~\ref{sec:paradigm-0}. In this context, the actual package build
+can be partitioned into several distinct steps, for example
+
+\begin{enumerate}
+\item dependency resolution,
+\item dependency installation,
+\item code transformation/generation,
+\item automated tests, and
+\item installation/packing.
+\end{enumerate}
+
+Steps 1 and 2 are sometimes performed together, for example as part of a single
+command invocation. However, in case of some package managers -- including npm
+-- the set of resolved dependencies with their versions can also be recorded for
+later reuse. It is done with so-called \textbf{lockfile} -- a file that project
+developers can add to a VCS and which allows dependency installation to be
+repeated without re-downloading metadata nor re-running the resolution
+algorithm. In npm projects this file is saved as \code{package-lock.json} or
+\code{npm-shrinkwrap.json}.
+
+With a precomputed \code{package-lock.json} we can therefore download the
+dependencies and use them as inputs of the hermetized build, narrowed to steps
+2-5. Upstream software's original build procedures sporadically expect network
+access during these steps. The build process of the aforementioned JQuery is
+one of those few cases where this occurs. Such problems would need to be
+corrected manually, in package recipes of a hypothetical distribution applying
+Paradigm 2 to a broader population of npm packages. A typical solution in,
+e.g., Debian is a patch that eliminates such access attempt or replaces it with
+a reference to a local, formally-approved input.
+
+If npm project authors fail to provide an appropriate lockfile -- which can
+happen -- it could be generated by one of the parties that rebuild the software.
+Step 1 would then need to be performed unhermetically, with network access. The
+obtained \code{package-lock.json} would then be treated as additional build
+metadata, distributed to the other parties. When a build were to be repeated to
+verify the reproducibiliy of the result or for other purposes, presence of this
+metadata would be required.
+
+The benefit of Paradigm 2 is that one can proceed in achieving reproducibility
+of most of the build process and further leverage it. In fact, comments in the
+source code of JQuery indicate that its developers -- to some extent and with
+disregard for possible changes in files being downloaded during the build
+process -- did actually work on making JQuery's build process deterministic when
+the \code{package-lock.json} is used.
+
+The main disadvantage of Paradigm 2 is that dependency resolution is still not
+secured by hermeticity nor reproducibility. Even when changes to project's
+\code{package-lock.json} take the form of version control system commits, these
+are unlikely to be reviewed as carefully as ordinary software code changes.
+Dependency trees can be complex. \code{package-lock.json} files counting over
+$1000$ entries are common. As a result, the shape of a particular resolved
+dependency tree is difficult to explain without additional tools.
+
+The described approach requires generalization to building a project that uses
+multiple repositories, e.g., npm Registry + Python Package Index + Rust Package
+Registry. That is because multiple dependency trees from multiple software
+ecosystems are involved. Theoretically, even in terms of a single ecosystem and
+a single repository, we might need to resolve multiple sets of dependencies in
+step 1. In effect, an actual collection of lockfiles would need to be treated
+as the aforementioned additional build metadata.
+
+\subsection{Debian implementation}
+
+Interestingly, a variant of Paradigm 2 can be found in Debian, which is
+considered one of the most reproducible software distributions. That is because
+the package recipe shared as \code{debian.tar.xz} file contains the names of
+direct build dependencies but not necessarily their precise versions nor the
+indirect dependency names. It is actually the \code{.buildinfo} files where the
+published packages' build environments metadata can be found. Much of this
+metadata is determined by the dependency resolution process, as performed by
+Debian's APT tool during the initial Debian package build.
+
+Although this does formally fall into the scope of Paradigm 2, Debian packagers'
+perspective is still similar to that of Paradigm 1 users. That is because -- as
+explained in~\ref{sec:conflicting-deps} -- a single Debian release typically
+only advertises a single version of a given package at any point in time.
+Unless multiple Debian releases are mixed together, this makes the input
+metadata of APT's dependency resolution process flat. This, in turn, makes
+packagers ultimately responsible for ensuring version compatibility between
+packages in this flat space.
+
+\section{Paradigm 3 -- deterministic dependency resolution inputs ensured}
+\label{sec:paradigm-3}
+
+For our dependency trees from Paradigm 2's step 1 to be secured through
+reproducibility, we need to be able to repeat the dependency resolution step
+using the same data about candidate dependency packages. Neither
+\code{.buildinfo} nor \code{package-lock.json} files preserve all metadata
+actually consulted by the resolution algorithm. They lack information about
+packages that were considered but rejected as final dependency tree members. As
+such, full dependency resolution cannot be performed based on just these files'
+contents. It can be argued that the risks this causes for Debian are small
+because the general public cannot create new packages that could then be
+immediately used as dependencies. Here, one of the most likely dependency
+resolution attack scenarios involves supplying the build with an outdated,
+faulty compiler package already present in the distribution. One theoretical
+attack utilizing a compiler bug was described
+in~\cite{deniable-backdoors-compiler-bugs}. In contrast, manipulation of
+\code{package-lock.json} in an npm package build can more easily lead to an
+attacker-published package being installed in the build environment.
+
+In case of npm projects, one of the simplest solutions would be pointing the npm
+tool to a local mock of a repository server, speaking the HTTP protocol. The
+mock would function as a proxy that downloads required packages' metadata from
+the original npm Registry server, alters it and returns it as responses to the
+npm tool's requests. Each response -- containing the metadata of all versions
+of a single npm package -- would be filtered not to include the versions of
+packages that were published after a chosen time treshold. The treshold could
+be, e.g., the release date of the project version being built. In repeated
+build attempts, the relevant metadata served by mocked registry ought not to
+change. Corner cases shall occur, in form of dependencies being removed from
+the official registry due to copyright claims or in form of projects' dependence
+on particular developer-alterable distribution tags of npm packages. These
+problems should be rare enough to be fixable manually or with reasonable
+defaults. For example, a mock \code{latest} tag could be attached to the newest
+version of each npm package whose metadata is served.
+
+This approach does not completely eliminate the threat of the dependency
+resolution process being maliciously influenced. In particular, the packages'
+metadata could be maliciously modified even earlier, for example as a result of
+the official registry's infrastructure being compromised. However, compared to
+Paradigm 2, the number of moments when malicious modifications could occur is
+decreased. Similarly, the scope of what could be modified is more limited. To
+decrease the changes of the hypothetized attack on the registry being
+successful, additional means of detection and mitigation could be employed. For
+example, trusted third parties can serve as ``canaries'', publishing
+cryptographically signed information about what package metadata was being
+served by the repository as of given date. The initial builder can also record
+the resolution metadata and make it available to rebuilders, effectively acting
+as one of the suggested canaries. The holy grail of avoiding a single point of
+failure -- one in the form of a centralized registry -- would be deriving the
+resolution metadata of packages from those packages themselves once they are
+also rebuilt locally. This would present a bootstrapping challenge that -- when
+solved -- would open the way to dependency resolution without major reliance on
+any centralized service.
+
+Regardless of the employed approach to securing the dependency resolution
+inputs, the actual concept of Paradigm 3 is to make the inputs plausibly
+deterministic and then repeat the dependency resolution process upon every
+repetition of a given package build. The remaining steps of the build process
+are performed analogously to those in Paradigm~2. The issue of generalization
+to projects utilizing multiple repositories is also analogous to that in
+Paradigm~2.
+
+\section{Paradigm 4 -- hermeticity relaxed and deterministic dynamic inputs allowed}
+
+One can notice that in paradigms 2 and 3 the first step, dependency resolution,
+is treated different from the subsequent ones. The result of step 1 is a
+collection of one or more lockfiles that identify dependencies' files, e.g.,
+through names and versions or URLs and hashes. A tool that implements a given
+paradigm would need to -- between steps 1 and 2 -- prepare an appropriate
+isolated environment for package build, for example a Linux container.
+Lockfile-identified dependencies would need to be exposed inside.
+
+In Paradigm 3, the initial download of packages metadata can happen through a
+locally-run mock of a repository server. I.e., the isolated dependency
+resolution process has a service perform possibly hermeticity-violating actions
+on its behalf. Yet, care is taken to make the results of those actions
+deterministic. Paradigm 4 extends this approach to all steps of the build. The
+installation step, knowing project's recursive dependencies identified by the
+lockfiles from step 1, could have the service supply the dependencies' files
+into the otherwise isolated build environment. There is no more need to provide
+separate isolated environments to two different parts of the build process --
+step 1 and the chain of remaining steps. As long as the hermeticity-violating
+actions performed by the service on build's behalf are deterministic, this
+should not make build results less reproducible. The process can be thought of
+as \textbf{eventually-hermetic}, bacause repeated builds are likely to request
+the exact same actions, requiring the same external data, which could be cached
+and reused, making subsequent runs network-independent. At the same time, this
+approach \textbf{removes the need of having all build inputs identified in
+ advance}, simplifying the entire build process.
+
+Let us provide another example of an action that could be deterministically
+carried out on behalf of the build -- checking out of a Git repository revision.
+Under Paradigm 4 this could happen through the hermeticity-violating service,
+making the repository a \textbf{build input determined dynamically}. If the
+checkout operation uses a repository URL and, e.g., a Git tag\footnote{unrelated
+to npm package's distribution tag and not to be confused with it}, it is by
+itself not deterministic -- result can vary in time, for example due to tags
+being changed in the upstream repository. In this case, additional means --
+like those already mentioned -- would be needed to ensure the determinism of the
+checkout action. However, no such extra measures are necessary if the checkout
+operation uses a commit hash made with an algorithm deemed cryptographically
+secure. Preimage attack resistance is of practical relevance, making even SHA-1
+applicable as of 2025.
+
+This approach makes the logic of an eventually-hermetic package build more
+straighforward. If, for example, step 3 required an extra resource or tool, in
+paradigms 1-3 that requisite would need to be identified beforehand. Under
+Paradigm 4 this is not necessary.
+
+\subsection{Security-oriented justification}
+
+How secure would the Paradigm 4 be? Its security relies on the viability of
+employed means of ensuring the determinism of dynamic inputs. A GNU Guix-like
+approach of maintaining the cryptographic hashes of all downloadable resources
+in a VCS is possible. While the collection of resources still needs to be
+identified in advance of any build, there is no more need to record exactly
+which ones are needed for which particular package build -- by itself a huge
+simplification. This makes Paradigm 4 no worse than -- seemingly the most
+secure -- Paradigm 1, as implemented in GNU Guix.
+
+However, the concept of paradigms -- as introduced by this work -- is not
+strictly dependent on the way of ensuring the integrity and determinism of
+software sources and of other build inputs. The approach of keeping hashes of
+packages' sources embedded in code kept in a VCS can be criticized. In theory,
+changes to the version-controlled recipes code -- with input resources' hashes
+-- are subject to review. However, despite the positive security aspects of
+human-conducted code reviews, such system makes it easy for reviewers to lose
+vigilance -- especially when facing a ``flood'' of package recipe updates, as
+shown in Listing \ref{lst:guix-package-update-commits}. Some could argue that
+it would be beneficial to completely replace the version-controlled hashes of
+sources with a network of canaries that record the tagged revisions found in VCS
+repositories and the contents of software's published release files. This
+aproach is applicable to Paradigms 4, 3, and 1 alike. It simply happens not to
+be employed by GNU Guix as of 2025.
+
+\chapter{Automated package builds experiment}
+\label{chp:experiment}
+
+The previous chapters of this work has lead to the following hypotheses and
+questions.
+
+%% \let\labelenumiOld\labelenumi
+%% \newdimen\labelwidthOriginal
+%% \labelwidthOriginal=\labelwidth
+
+\newcounter{hyQuCounter}
+
+\newcommand{\hyQuItem}[1]{%
+ \stepcounter{hyQuCounter}%
+ \item[#1 \thehyQuCounter{}.]%
+}
+
+\newcommand{\hypothesis}{\hyQuItem{HYPOTHESIS}}
+\newcommand{\question}{\hyQuItem{QUESTION}}
+
+\begin{description}
+ \hypothesis{} The dependency tree sizes of npm packages and acceptance of
+ conflicting dependencies by the platform appear to be the major sources of
+ difficulty in packaging the npm ecosystem in reproducibility-focused
+ distributions. Is this truly the main factor?
+
+ \hypothesis{} Re-generation of npm lockfiles -- an operation necessary for the
+ security improvement offered by proposed paradigms 3 and 4 over Paradigm 2 --
+ is expected to rarely cause npm package build failures which would not occur
+ with developer-supplied lockfiles. Are such failures indeed uncommon?
+
+ \hypothesis{} As speculated in \ref{sec:overlap-of-runtime-and-dev-deps}, both
+ direct and indirect dependencies of npm projects are often unnecessary. Is
+ this indeed the case?
+
+ \question{} What are the typical sizes of dependency trees needed to build npm
+ projects and how much can these trees be typically shrinked?
+
+ \question{} How often do dependency conflicts actually occur in npm dependency
+ trees and how often -- or to what extent -- can they usually be forcibly
+ eliminated without causing breakage?
+
+ \question{} Are forced removals of npm project's dependencies and forced
+ elimination of dependency conflicts likely to cause non-obvious breakages that
+ only become apparent when seemingly successfully-built package turns out to be
+ disfunctional or nonfunctional? If so, how best to avoid them?
+
+ \hypothesis{} npm projects' dependencies are seldom specified by distribution
+ tags and removal of distribution tags from npm dependency resolution metadata,
+ with automatic addition of a mock \code{latest} tag as mentioned
+ in~\ref{sec:paradigm-3}, is expected to cause few dependency resolution
+ failures. Is this a valid assumption?
+
+ \question{} Can we deliver a prototype that performs npm project's dependency
+ resolution as proposed in paradigms 3 and 4?
+\end{description}
+
+To verify and answer these, an experiment was conducted which involved automated
+build attempts of top npm projects, selected by their position in the npm
+Registry package rankings. The selected set consisted of projects belonging to
+the first $200$ of either the most popular \code{dependencies} or
+\code{devDependencies} as of \tresholdDate{}. Due to some overlap between the
+rankings, the actual size of the set was \allSelectedCount{}. The build
+procedure, described in the following subsection, was designed with the help of
+a trial-and-error approach.
+
+\section{Method and environment}
+
+The experiment was conducted on an x86\_64 machine. For details, see
+Listing~\ref{lst:cpu-info}. All operations were performed under version
+5.15.0 of the Linux kernel. All filesystem operations were backed by an
+ext4 filesystem. No effort was made to employ means like disorderfs, described
+in~\cite{DBLP:journals/corr/abs-2104-06020}, because this work is not concerned
+with eliminating the traditional sources of nondeterminism.
+
+\lstinputlisting[
+ float=htpb,
+ caption={Details of the processor used during the experiment, as reported by
+ cpuinfo utility.},
+ label=lst:cpu-info
+]{cpu-info.txt}
+
+The diagram in Figure \ref{fig:experiment-activity} describes the flow of
+activities during testing of a single npm project. In the diagram, start and
+end are denoted by a filled circle and a circle with a white ring inside,
+respectively. Flow branches are represented by hexagons and merges -- by
+rhombuses. The particular operations present in the diagram are described
+further below.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[width=\linewidth,inkscapelatex=false]{experiment-activity.svg}
+ \caption{Activity diagram describing the experiment as performed on each
+ tested npm project.}
+ \label{fig:experiment-activity}
+\end{figure}
+
+\subsection{Containerized environment creation}
+
+For each tested project, the version to build was first selected as its highest
+non-pre-release package version published before \tresholdDate{}. Versions'
+publishing dates that were consulted were part of packages' metadata downloaded
+from the npm Registry. For the selected version, the relevant Git repository
+URL and -- where possible -- release's Git commit hash were extracted from the
+available metadata. The URL was sometimes present with a \code{git+} or
+\code{git:} prefix, which had to be dropped. The repository at learned URL
+was then cloned to a local directory, with submodules included through the use
+of Git's \code{--recurse-submodules} option.
+
+Upon successful retrieval of source repository contents, a semi-isolated
+environment, based on Linux containers, was created. Upon creation, the
+environment only had access to a minimal collection of software. It comprised
+
+\begin{itemize}
+\item
+ Node.js runtime including bundled npm application,
+\item
+ Git version control tool,
+\item
+ GNU Bash shell, which served as the POSIX-compliant shell used by \code{exec}
+ function of Node.js,
+\item
+ GNU Coreutils,
+\item
+ GNU which, invoked by experiment's code,
+\item
+ GNU Guile and several Guile libraries, being the driver for the experiment's
+ code, and
+\item
+ dependencies of the above, e.g., a C library.
+\end{itemize}
+
+The environment was created as a container shell managed by GNU Guix. The
+version of GNU Guix used was built from Git revision \tresholdGuixCommit{}, the
+last one before \tresholdDate{}. It featured Node.js runtime in version
+22.14.0, npm in version 10.9.2, and Git in version 2.49.0.
+Inside the environment, the applications listed above were available through the
+\code{PATH} variable and also symlinked under \code{/bin} directory through the
+use of \code{--emulate-fhs} option of \code{guix shell}. The environment had no
+direct access to outside network, allowing us to state that experiment's results
+reflect the behavior of a hermetic build process. Network isolation also helped
+make sure that no dependency was installed ``on the side'', without being
+recorded in project's lockfile. The environment was also isolated
+filsystem-wise, with specially prepared directories shared between the
+environment and the host. They were shared read-write or read-only, according
+to needs. Shared directories allowed container's guest to
+
+\begin{enumerate}
+\item
+ access the code used to drive the experiment,
+\item
+ access npm project's cloned repository,
+\item
+ request npm packages' metadata and files from the host through named fifos,
+\item
+ receive them as files, and
+\item
+ persist lockfiles and final package files generated during build, for later
+ inspection.
+\end{enumerate}
+
+\subsection{Source code checkout preparation}
+
+Inside the just-created environment, a checkout to npm project's appropriate
+revision was attempted in the following ways.
+
+\begin{enumerate}
+\item
+ By switching to the commit hash previously extracted from the package
+ metadata.
+\item
+ By switching to a Git tag identical -- as a string -- to the version being
+ built.
+\item
+ By switching to a Git tag identical to the version being built, prefixed with
+ letter ``v''. E.g., for a project version 3.2.2 a switch to Git tag
+ \code{v3.2.2} would be attempted.
+\end{enumerate}
+
+This sequence of tries was chosen based on findings from initial manual
+experiments and also on prior knowledge of common developer practices. In
+particular, it was found that a Git commit hash is not always advertised for
+a given npm package version. When it is, it sometimes corresponds to a revision
+that was never pushed to project's public repository. This appears to be most
+often caused by an automated release publishing software that makes a local
+commit as part of its operation. It was decided that in both cases -- of the
+unknown and nonexistent commit hash -- it is best to fall back to probable Git
+tags.
+
+If directories named \code{node\_modules} or \code{dist} existed in a
+successfully checked-out source repository, they were deleted before the actual
+build attempt. These directories are used to store npm project's installed
+dependencies and generated files, respectively. Although they are sometimes
+checked into version control, they are not sources per se and a hygienic npm
+project build should be performed without them.
+
+It is worth noting that every build was conducted inside a full git repository
+checkout, with access to the \code{.git} directory containing project's history.
+This is unlike the practice of GNU Guix, Debian, and many other distributions
+where build inputs typically do not include any version control metadata. The
+decision was made based on the following considerations.
+
+\begin{enumerate}
+\item
+ Build procedures most often rely on version control metadata for side tasks
+ like generation of software authors list. These tasks are not highly relevant
+ to our stated questions, but their failures could decrease the number of
+ successful package builds that we seek to further analyze.
+\item
+ In actual distribution software packaging, the build process' reliance on
+ version control metadata is considered easy to solve compared to the issues
+ of dependencies.
+\item
+ While availability of version control metadata could theoretically ease
+ smuggling of backdoor code in XZ-style attacks, it would be hardly practical
+ -- the backdoor would need to be somehow retrieved from version control
+ history and invoked, in a hard-to-notice way. Building with full version
+ control metadata is therefore a secure enough approach to be suggested for
+ adoption by distributions.
+\end{enumerate}
+
+\subsection{Dependency resolution in a network-isolated environment}
+
+Dependency resolution was performed with the help of a dummy
+\code{npm uninstall} command, as shown in Listing~\ref{lst:npm-uninstall}.
+The options used made npm
+
+\begin{itemize}
+\item
+ refrain from attempting network requests unrelated to the actual dependency
+ resolution,
+\item
+ refrain from actually installing the resolved dependencies or running their
+ hooks,
+\item
+ update project's lockfile to the current format if an older one was
+ encountered, and
+\item
+ make requests to a local mock of npm's repository.
+\end{itemize}
+
+The command either created the \code{package-lock.json} file from scratch, wrote
+a new version of it based on an existing lockfile found or left it unchanged.
+The latter happened whenever the existing \code{package-lock.json} was already
+in sync with dependency constraints specified in project's \code{package.json}.
+It can be noted that npm would also automatically use an
+\code{npm-shrinkwrap.json} file over \code{package-lock.json} if the former were
+present. However, this was not the case for any of the npm projects tested.
+
+\lstinputlisting[
+ float=htpb,
+ language=shell-command,
+ caption=The npm command used to produce an up-to-date lockfile.,
+ label=lst:npm-uninstall
+]{npm-uninstall.txt}
+
+For projects that utilize workspaces, attempt was made to also add
+workspace-related options \code{-}\code{-workspaces},
+\code{-}\code{-include-workspace-root}, and
+\code{-}\code{-workspace} as appropriate to \code{npm uninstall}
+and subsequent npm invocations. Workspaces are a feature that allows multiple
+subprojects to be developed in subdirectories of a single npm parent project,
+with the parent and each subproject having its own \code{package.json}
+file. Despite the effort, all workspaced projects that were tested failed to
+build for other reasons. Several tested projects were found to use workspaces
+for smaller satellite utilities while having the project's package described by
+the \code{package.json} file in the root directory of the repository.
+Those were built without any workspace-specific npm options.
+
+A minimal server, written for this experiment, listened for HTTP requests on
+port 8080 inside the network-isolated build environment. It received npm
+package metadata requests and passed the requested package names via a fifo to a
+service running outside the container. The service downloaded the metadata and
+filtered it to only contain information about package versions published before
+\tresholdDate{}. The original npm distribution tags were stripped, but a mock
+\code{latest} tag was added for every package, pointing at its newest
+non-pre-release version from before \tresholdDate{}. Pruned pieces of metadata
+were supplied to the guest-side server for use as responses to the npm tool.
+
+\subsection{Remaining build steps}
+
+After successful dependency resolution, packages listed in the lockfile were
+installed with the help of an \code{npm ci} command, as shown in
+Listing~\ref{lst:npm-ci}. Analogously to the dependency resolution step,
+package file requests were sent through the HTTP protocol to the local port 8080
+and were handled by the guest-side server, which in turn relied on the host-side
+service to perform the actual downloads on guest's behalf.
+
+\lstinputlisting[
+ float=htpb,
+ language=shell-command,
+ caption=The npm command used to install dependencies.,
+ label=lst:npm-ci
+]{npm-ci.txt}
+
+Successful installation of dependencies was followed by an invocation of a
+\code{build} action that npm projects conventionally define in their
+\code{package.json} files. The command used is shown in
+Listing~\ref{lst:npm-run-build}.
+
+\lstinputlisting[
+ float=htpb,
+ language=shell-command,
+ caption=The npm command used to invoke project-specific build operations.,
+ label=lst:npm-run-build
+]{npm-run-build.txt}
+
+At this point, the \code{package-locks.json} file was copied to a subdirectory
+of the results directory to be persisted after experiment's end. The same was
+done at subsequent builds, described further below, which involved modifications
+to the dependency tree. The collected lockfiles later allowed calculation of
+dependency tree sizes. When dependency tree modifications were found to cause
+changes to the built package, these lockfiles were also useful in understanding
+the exact reasons behind those changes.
+
+As of \tresholdDate{}, built npm packages are distributed as \code{.tgz} archive
+files. In the jargon they are called ``tarballs'' and in case of npm packages
+they are compressed using gzip algorithm. An \code{npm pack} command exists
+which can produce such archive from project's files. Although the same could be
+achieved with a traditional tar program, the npm's command is convenient,
+because -- among others -- it automatically omits unneeded files like the
+\code{node\_modules} directory. The exact form of the command used to persist
+the built package is shown in Listing~\ref{lst:npm-pack}.
+
+\lstinputlisting[
+ float=htpb,
+ language=shell-command,
+ caption=The npm command used to create the built package file.,
+ label=lst:npm-pack
+]{npm-pack.txt}
+
+\subsection{Repeated builds without upstream lockfiles}
+
+For a project that was successfully built with the described procedure, the
+process was repeated with alterations. Upon each repetition, the repository was
+brought to a clean state and had added Git hooks -- if any -- removed. However,
+re-creation of the entire semi-isolated environment was deemed unnecessary for
+the purpose of the experiment. After the repository was cleaned, each repeated
+build started with the Git revision checkout attempts described earlier.
+
+The first alteration of the build was the removal of existing lockfiles
+recognized by npm or its alternatives: \code{npm-shrinkwrap.json},
+\code{package-lock.json}, \code{yarn.lock}, and
+\code{pnpm-lock.yaml}. It happened right after the removal of
+version-controlled \code{node\_modules} and \code{dist} directories.
+The removal of lockfiles was done to force a full dependency resolution. If
+successful, the build in this form was performed twice to check if dependency
+resolution and lockfile generation in npm suffer from obvious nondeterminism
+issues.
+
+Additionally, all later builds of the project also involved the removal of
+existing lockfiles, besides other alterations.
+
+\subsection{Elimination of unnecessary direct dependencies}
+
+Each project known to build successfully with and without the removal of its
+version-controlled lockfile -- if any -- was tested further. The experiment
+checked whether it had the ability to build with each of its direct dependencies
+-- tried in reverse alphabetical order -- removed. E.g., a project with nine
+direct dependencies specified in its \code{package.json} -- including those
+listed as \code{dependencies}, \code{devDependencies}, and
+\code{optionalDependencies} but not \code{peerDependencies} -- was built nine
+times, each time with another direct dependency removed for the first time. The
+build was considered successful when the npm commands all finished with zero
+status. For each such successful build the tested dependency was recorded as
+unnecessary and was also removed in all subsequent build attempts, together with
+the dependency tested in a given attempt. E.g., if five out of first eight
+dependencies were found to be unnecessary, then subsequent build was performed
+with the ninth dependency plus the initial five removed. I.e., a total of six
+dependencies were removed in that build.
+
+The removal consisted of erasing of dependency's entry in project's
+\code{package.json} file right after lockfiles deletion. However, the original
+\code{package.json} contents were always recorded and restored before the
+\code{npm pack} invocation. This was done to have the built package tarballs --
+each of which contains a copy of the \code{package.json} -- easier to compare
+for other differences. Interestingly, for some projects the \code{npm pack} did
+not place the \code{package.json} inside the tarball verbatim and instead
+generated a variant of that file with some fields changed in a way custom to the
+project. One such case, concerning the \code{@testing-library/user-event}
+package, is discussed in~\ref{sub:apparently-disfunctional-pkgs}.
+
+All later builds of the project also involved the removal of dependencies
+identified at this point and the described restoration of the original
+\code{package.json} file.
+
+\subsection{Elimination of unnecessary indirect dependencies}
+
+With all apparently-unnecessary direct dependencies identified, the remaining
+indirect dependencies were tested. For it is unstraightforward to forcibly
+remove an indirect dependency from npm project's dependency tree, a choice was
+made to instead attempt ``dummifying'' it. The npm feature of overrides --
+mentioned in~\ref{sub:effects-of-semver} -- was used to force the npm's
+resolution algorithm to always select a mocked, dummy version
+``0.0.0-msc-experiment-dummy'' of a given dependency. At the same time, for the
+dependency package meant to be dummified, the local server providing packages'
+files and metadata on port 8080 would not respond with that package's real
+metadata. Instead, it would give a response indicating that the only available
+version of that package is the dummy version, which has no own dependencies.
+Additionally, it would serve the corresponding dummy package tarball with only
+minimal contents.
+
+This process of identifying project's unnecessary indirect dependencies was
+analogous to that concerning direct dependencies. It involved multiple builds
+-- more than a thousand in case of one npm project tested. In each build a
+single tested indirect dependency -- together with the unnecessary indirect
+dependencies identified previously -- was dummified. Each time the overrides
+were added to project's clean \code{package.json} file. The addition of
+overrides was carried out together with the removal of unnecessary direct
+dependencies from \code{package.json}. Build's all npm commands had to finish
+with zero status for the tested dependency to be assumed dummifiable. Each time
+the \code{package-lock.json} from the last successful build was consulted to
+determine the next dependency to test. Applicable dependencies were tried in
+reverse alphabetical order.
+
+All later builds of the project also involved the dummification of indirect
+dependencies identified at this point. During the entire experiment, whenever a
+dependency to override already had an upstream override specified in
+\code{package.json}, the original override was being removed.
+
+\subsection{Elimination of dependency conflicts}
+
+Even after the elimination of unnecessary direct and indirect dependencies,
+project's dependency tree could still contain extraneous conflicting
+dependencies. Subsequent builds were carried out to forcibly remove those
+conflicts where possible, utilizing overrides. For every dependency that
+occured multiple times in multiple versions in the tree, a build attempt was
+made with an override which forced that package to be always used in the same,
+single version.
+
+\begin{itemize}
+\item
+ If it happened to be both a direct and indirect dependency of the project --
+ it was overriden with the version that was previously used to satisfy
+ project's direct dependency.
+\item
+ If it was only an indirect dependency -- it was overriden with the highest of
+ the versions in which it previously appeared.
+\end{itemize}
+
+Just like before, the build was repeated to identify every dependency conflict
+that -- when forcibly removed -- does not cause any npm invocation finish with
+non-zero status.
+
+\section{Build attempt results}
+\label{sec:build-attempt-results}
+
+Two projects were found not to actually exist as real pieces of software. I.e.,
+their npm packages were placeholders. Another \skippedDueToLimitationsCount{}
+projects could not be tested due to limitations of experment's environment --
+they used dependency packages that are distributed through servers other than
+the official npm Registry. This made the npm tool attempt downloading these
+directly, which failed in a network-isolated environment. The results from
+build attempts of the final \allTestedCount{} projects are presented in
+Figure~\ref{fig:status-counts}. Different types of failures were classified
+based on the first error reported in the build attempt. It means that, e.g., a
+project with unresolvable dependencies and a missing \code{build} action was
+classified as failing at the dependency resolution step.
+
+\begin{figure}[htpb]
+\centering
+\includesvg[width=\linewidth,inkscapelatex=false]{status-counts.svg}
+\caption{Statuses of automated hermetized build of top npm projects.}
+\label{fig:status-counts}
+\end{figure}
+
+\subsection{Projects whose source repositories failed to be cloned}
+
+For projects in this category, sources could not be automatically retrieved.
+Either no repository URL was included in published npm metadata of the package
+version or the published URL was not valid.
+
+Some packages were found to provide SSH URLs to their projects' GitHub
+repositories. Such URLs could not be used for anonymous cloning, despite the
+repositories themselves being -- at least in some cases -- public and
+anonymously cloneable through HTTP. A sample Git error message is presented in
+Listing~\ref{lst:ssh-clone-fail}. It was printed upon an attempt to use the
+\code{ssh://git@github.com/sinonjs/sinon.git} URL in a \code{git clone} command.
+
+\lstinputlisting[
+ float=htpb,
+ caption=Error reported by Git upon an attempt to clone a repository using an
+ SSH URL.,
+ label=lst:ssh-clone-fail,
+ numbers=none
+]{ssh-clone-fail.txt}
+
+There was also a single case where an HTTP URL pointed at a repository that no
+longer existed. Other interesting unworkable URLs were ones with a branch name
+appended\footnote{e.g., \code{https://github.com/emotion-js/emotion.git\#main}}.
+Some unworkable URLs were also pointing to web pages of repositories'
+subdirectories\footnote{e.g.,
+\code{https://github.com/babel/babel/tree/master/packages/babel-core/}}. In a
+vast majority of cases a correction of URL with the help of a simple regular
+expression could be attempted. Interestingly, none of the tested projects were
+found to use a VSC other than Git.
+
+\subsection{Projects whose relevant source control revisions were not found}
+
+For projects in this category, the git source repository could be cloned but it
+contained neither the commit specified in package's metadata nor a tag
+corresponding to the version number being built. Reasons included
+
+\begin{itemize}
+\item
+ VCS revisions being tagged differently, e.g., \code{PACKAGE-NAME@VERSION},
+\item
+ particular version's tag being missing, and
+\item
+ tags not being used altogether.
+\end{itemize}
+
+\subsection{Projects that do not follow the conventions}
+\label{sub:packages-not-following-conventions}
+
+Projects in this category either lacked a \code{package.json} file in
+repository's root or lacked a \code{build} action.
+
+Size of this category seems to suggest that the conventions which we and others
+\cite{goswami-reproducibility} rely upon are very loosely followed. However,
+some projects classified here are trivial ones that simply do not require any
+operations to be performed as part of a \code{build} action. For example,
+package \code{semver}, as distributed through the npm Registry, was found to
+only contain files that are present in its project's source repository. I.e.,
+none of the files were created or modified as part of the build process
+performed by that project's developers. The files in \code{semver}'s built
+package archive were found identical to those in the relevant revision of the
+source repository, with the repository additionally holding some other files,
+e.g., test scripts. \code{semver} does indeed not require compilation nor
+similar build steps. It has no need for a \code{build} action and therefore
+does not have one specified in its \code{package.json} file.
+
+\subsection{Projects with dependency resolution failures}
+\label{sub:resolution-failures}
+
+Projects in this category had the \code{npm uninstall} command fail to
+create or update the lockfile. The predominant source of failure is related to
+peer dependency resolution, with a sample error message shown in
+Listing~\ref{lst:eresolve-error}. Simplifying, peer dependencies are a feature
+through which developers can forbid npm from creating a dependency conflict with
+a particular package. Typically, an add-on package specifies its base package
+-- which it enhances -- as its peer dependency. If the base package were
+specified as add-on's casual dependency, npm's resolution algorithm could make
+the add-on package use its own copy of that base package. This is typically not
+the behavior the developer or user wants. Peer dependencies are a mean to
+prevent it.
+
+\lstinputlisting[
+ float=htpb,
+ caption=Error reported upon peer dependency resolution failure during
+ \code{ts-node} project build.,
+ label=lst:eresolve-error
+]{eresolve-error.txt}
+
+The exact behavior of peer dependencies changed through the history of npm. One
+alternative package manager for the npm ecosystem -- Yarn -- is also known for
+behaving different than npm in some situations. It is suspected that most of
+the projects in this category could have their dependencies resolved
+successfully with older version of npm or with Yarn. It was found that
+\failedToResolveAndUsingYarnCount{} packages in this category do have a
+\code{yarn.lock} file in the VSC, indicating their developers likely use
+Yarn over npm.
+
+\subsection{Projects with invalid upstream lockfiles}
+
+The \code{npm uninstall} command was invoked during every project build to make
+sure an up-to-date lockfile is in place. Despite that, for two packages a
+lockfile was left behind that \code{npm ci} later reported as invalid due to
+being out of sync with project's \code{package.json}. One of these projects had
+a preexisting \code{package-lock.json} file and the other had a \code{yarn.lock}
+file\footnote{npm also reads a \code{yarn.lock} when no other lockfile is
+present}.
+
+\subsection{Projects that expect network access to build}
+\label{sub:expect-network-access}
+
+Projects in this category failed to build due to unsuccessful network request
+attempts other than the attempts mentioned at the beginning
+of~\ref{sec:build-attempt-results}.
+
+The majority of build failures in this category occured when project's
+development dependency was trying to download a web browser binary for
+browser-based tests. Examples of other non-npm resources that projects tried to
+download were font files from Google Fonts and sources for automated native
+compilation of a library whose Node.js bindings package was being installed.
+
+It can be stated that network accesses during npm project builds are commonly
+made to facilitate installation of architecture-specific software binaries, as
+these are inconvenient to distribute through the architecture-agnostic npm
+Registry.
+
+\subsection{Projects that require a build tool other than npm}
+
+Projects in this category are known to require either Yarn or pnpm to build.
+They could be classified with certainty because either
+
+\begin{itemize}
+\item
+ their \code{package.json} files contained special URLs or package names that
+ npm could not handle, or
+\item
+ their build processes printed messages that explicitly informed the developer
+ about the need to use a particular tool.
+\end{itemize}
+
+There are many more projects which likely rely on Yarn or pnpm but could not be
+classified here with certainty, see~\ref{sub:resolution-failures}.
+
+\subsection{Projects with additional non-npm dependencies}
+
+Projects in this category need additional tools that are not installable through
+npm. Unlike projects mentioned in~\ref{sub:expect-network-access}, these rely
+on the developer to install the additional tool.
+
+Experiment logs indicated failures upon searching for Python executable and for
+configuration files of popular shells.
+
+\subsection{Projects with other build failures}
+
+Projects in this category failed to build due to reasons other than those
+discussed up to this point. Failures occured due to problems like missing
+modules, missing variable, and an operation hanging indefinitely.
+
+\subsection{Projects that could be built only when using upstream lockfiles}
+
+Projects in this category failed to build only after their upstream lockfiles
+were removed. After seemingly successfult dependency resolution, errors were
+raised during TypeScript compilation. The errors almost certainly resulted from
+newer versions of project's dependencies being used. This occured despite the
+use of Semantic Versioning and the respecting of dependency constraints declared
+by projects.
+
+\subsection{Projects built with both upstream and re-generated lockfiles}
+
+Packages in this category are considered to have been built successfully,
+because all npm command invocations from the first two builds finished with zero
+status. There is no guarantee that the packages built are fully functional.
+For example, some projects like \code{@testing-library/react} rely on an
+additional tool called semantic-release, which is not invoked as part of
+\code{npm run build}. That tool is responsible for analyzing project's change
+history and determining the right version number to be recorded in project's
+\code{package.json} file~\cite{semantic-release}. When its use is omitted, the
+built package is reported as having a placeholder version, e.g.,
+``0.0.0-semantically-released''.
+
+It is expected that a more polished and defect-free build process would often
+involve a dependency tree of several more or several less packages than in this
+experiment. Nonetheless, it was assumed that dependency sets found necessary
+for successful \code{npm install} and \code{npm build} invocations do
+represent the characteristics of the projects well enough.
+
+Results presented through the rest of this chapter concern the dependency trees
+of projects from this very category.
+
+\section{Dependency trees after removals of dependencies and their conflicts}
+\label{sec:trees-after-pruning}
+
+The sizes of the original dependency trees, produced in project builds with
+upstream lockfiles removed, are shown in Figure~\ref{fig:tree-size-stats},
+together with the sizes of pruned trees. Each pair of boxes represents the
+experiment at a different stage. The left box of each pair shows the average
+number of project dependencies where all distinct versions of the same package
+are counted. E.g., an npm package \code{foo} that occurs in a dependency tree
+three times as \code{foo@1.2.3}, \code{foo@2.1.0}, and again \code{foo@2.1.0} is
+counted as two. The right box of each pair shows the average number of
+dependencies with all versions of a single package counted as one. E.g., the
+\code{foo} introduced before is now counted as one. Standard deviation of the
+sample is additionally plotted over every box.
+
+Individual projects' dependency tree sizes are plotted as circles over every
+box. \inDebianCount{} projects were found to also have their corresponding
+packages present in Debian Bookworm as of \debianTresholdDate. They are
+represented by filled, black circles. No clear relation between dependency tree
+sizes and presence in Debian can be seen at any stage of the experiment. Also
+consult Figure \ref{fig:tree-size-stats-no-bc} for a variant of this plot that
+omits builds of packages which appear to be nonfunctional due to aggressive
+dependency elimination.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[width=\linewidth,inkscapelatex=false]{tree-size-stats.svg}
+ \caption{Dependency tree sizes of built npm projects.}
+ \label{fig:tree-size-stats}
+\end{figure}
+
+\section{Dependency conflict counts}
+\label{sec:dep-conflict-counts}
+
+Projects were further categorized by the number of dependency conflicts that
+could not be removed with the method used. The categories are visualized in
+Figure~\ref{fig:unflattened-multiver-counts}. Counts of all projects and counts
+of projects having corresponding packages in Debian are shown. Also consult
+Figure \ref{fig:unflattened-multiver-counts-no-bc} for a variant of this plot
+that omits builds of packages which appear to be nonfunctional due to aggressive
+dependency elimination.
+
+As can be seen, most of the projects had few to no remaining dependency
+conflicts. Once again, there was no clear relation between the number of
+remaining dependency conflicts and presence in Debian. Given this
+distribution's norms, this suggest that the authors of Debian packaging likely
+managed to further remove some conflicts that could not be eliminated with the
+experiment's method. They possibly used more invasive methods like source code
+patching.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[width=\linewidth,inkscapelatex=false]{unflattened-multiver-counts.svg}
+ \caption{Counts of built projects with different numbers of unremovable
+ dependency conflicts.}
+ \label{fig:unflattened-multiver-counts}
+\end{figure}
+
+\section{Differences in build outputs produced during the experiment}
+
+The repeated builds with upstream lockfiles removed had their produced package
+tarballs and generated \code{package-lock.json} files compared. The files
+produced on two build runs were found identical in case of every successfully
+built project\footnote{npm authors should be credited for making \code{npm pack}
+produce tarballs without nondeterministic timestamps}. This does not yet
+guarantee that the builds and dependency resolutions are reproducible. However,
+it does indicate that differences found after dependency removals, etc.\ are
+likely the effect of those alterations and not manifestations of builds
+nondeterminism.
+
+It was found that \withAnyTarballsDifferentCount{} out of
+\builtSuccessfullyCount{} successfully built projects had their package tarballs
+differ as the result of either dependency removals, dummifications, or conflict
+removals. All these cases were manually inspected. Two differences were found
+to be caused by reordered \code{package.json} entries and appear to be mere
+artifacts of this experiment's method of restoring project's original
+\code{package.json} file before \code{npm pack} invocation. Some of the more
+interesting cases are discussed below.
+
+\subsection{Use of a different dependency version}
+\label{sub:use-of-different-dep-ver}
+
+Several times an alteration of the build process allowed the npm's resolution
+algorithm to use a newer version of a dependency which then caused generation of
+different but still correct code. One example is the \code{concurrently}
+console application written in TypeScript. Package \code{typescript}, providing
+a TypeScript compiler, was specified as its direct development dependency.
+During the experiment this direct dependency was removed, but package
+\code{typescript} still made its way to the dependency tree due to being
+required by another dependency -- \code{@hirez\_io/observer-spy}. Its
+constraint on \code{typescript}'s version was looser than that present before,
+causing version 5.8.3 to be used instead of former 5.2.2. A sample of changes
+caused by the use of that newer TypeScript compiler is presented in
+Listing~\ref{lst:newer-typescript-changes}.
+
+\lstinputlisting[
+ float=htpb,
+ language=diffoscope,
+ caption=Excerpt from diffoscope's report of differences in built
+ \code{concurrently} package tarballs.,
+ label=lst:newer-typescript-changes,
+ numbers=none
+]{newer-typescript-changes.diffoscope}
+
+It is worth noting that the dependency \code{@hirez\_io/observer-spy} -- even
+if not necessary by itself -- could not be eliminated with this experiment's
+method.
+
+\subsection{Inlining of a dependency}
+\label{sub:inlining-of-a-dep}
+
+One similar case was that of built \code{axios} package, which -- besides
+also showing generated code differences resulting from changed compiler/bundler
+version -- had its dependency \code{proxy-from-env} treated differently,
+despite always occuring in the same version 1.1.0. Initially,
+\code{proxy-from-env} was specified as \code{axios}' runtime
+dependency and was merely referenced from the code being generated during build.
+When, as part of the experiment, \code{proxy-from-env} was removed as
+project's direct dependency, it remained present in the dependency tree due to
+being required by certain development dependency. \code{proxy-from-env}
+was therefore itself flagged as an indirect development dependency, which made
+the bundler treat it differently and inline it in \code{axios}' generated
+code.
+
+\subsection{Digest included in the generated package}
+
+If project's generated files include a hash of its dependency specifications or
+a similar derived value, it is an obvious source of difference in this
+experiment. One of such projects is \code{ts-jest}, which places a digest
+like \code{4ec902e59f1ac29ff410d624dcccf9b192920639} in a
+\code{.ts-jest-digest} file inside its package tarball.
+
+\subsection{Apparently disfunctional built packages}
+\label{sub:apparently-disfunctional-pkgs}
+
+Several times a change to project's dependency tree did actually cause a
+significant change to the build output. In multiple cases, a removed package
+could not be found by a bundler tool called Rollup, which then treated it as an
+``external'' dependency than need not be bundled. Rollup merely issued a
+warning about a reference to the now-absent code module and proceeded without
+it. An example of this can be seen in Listing
+\ref{lst:warning-module-as-external} with an excerpt from the log of
+\code{rollup-plugin-typescript2} project build.
+\code{rollup-plugin-typescript2} specified package \code{object-hash} as a
+development dependency and had its code included in an amalgamated script file
+generated by Rollup. After \code{object-hash} was removed, the invocation of
+\code{rollup-plugin-typescript2}'s build action still finished with zero status,
+with the generated amalgamated script file being smaller by the size of the
+missing dependency. If the \code{rollup-plugin-typescript2} package built this
+way were to be later used, its code would likely encounter an error when trying
+to import the \code{object-hash} module.
+
+\lstinputlisting[
+ float=htpb,
+ caption=The output of \code{npm run build} invocation with a missing
+ dependency reported by Rollup.,
+ label=lst:warning-module-as-external
+]{warning-module-as-external.txt}
+
+A single interesting case was that of \code{@testing-library/user-event} project
+and the \code{package.json} file generated for its package tarballs. Several --
+seemingly vital -- \code{package.json} keys were no longer present after
+project's \code{typescript} dependency was removed. The change caused by
+\code{typescript}'s removal is shown in
+Listing~\ref{lst:removed-typescript-changes}).
+
+\lstinputlisting[
+ float=htpb,
+ language=diffoscope,
+ caption=Excerpt from diffoscope's report of differences in
+ \code{package.json} files inside built
+ \code{@testing-library/user-event} package tarballs.,
+ label=lst:removed-typescript-changes,
+ numbers=none
+]{removed-typescript-changes.diffoscope}
+
+In some cases, as subsequent dependencies of a project were eliminated, a
+bundler tool combined the dependencies in a different order, making amalgamated
+scripts difficult to compare with diff-like tools. There were also cases where
+the size of individual generated files would increase by an order of magniture
+or even go up and down during a series of builds. One suspected reason for
+increasing amalgamated script size -- besides the one discussed in
+\ref{sub:inlining-of-a-dep} -- is polyfilling. It is the action of replacing
+newer JavaScript language constructs used by programmers with code that is
+compatible with older language runtimes. An older version of a build tool would
+typically aim to support more legacy runtimes, applying more polyfills and
+increasing the produced script sizes as a result. Nonetheless, for the purpose
+of this experiment, whenever the nature and effect of changes in a build output
+were unclear, the package was considered one of the total of eight disfunctional
+packages.
+
+\subsection{Updated statistics}
+
+We are interested in the relation between project's dependency tree
+characteristics and its packagability for software distributions. The
+statistics presented in \ref{sec:trees-after-pruning} and
+\ref{sec:dep-conflict-counts} included eight projects with assumed
+disfunctionalities introduced by this very experiment. Three of these do have
+corresponding Debian packages. As these eight cases could make our results
+deceptive, the statistics are now presented again, with problematic projects not
+taken into account. Dependency tree sizes at various stages of the experiment
+are presented in Figure \ref{fig:tree-size-stats-no-bc}. Projects'
+categorization by the number of remaining dependency conflicts is visualized in
+Figure~\ref{fig:unflattened-multiver-counts-no-bc}.
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[width=\linewidth,inkscapelatex=false]{tree-size-stats-no-bc.svg}
+ \caption{Dependency tree sizes of built npm projects. Packages which appear
+ to be nonfunctional due to aggressive dependency elimination are not
+ included.}
+ \label{fig:tree-size-stats-no-bc}
+\end{figure}
+
+\begin{figure}[htpb]
+ \centering
+ \includesvg[width=\linewidth,inkscapelatex=false]
+ {unflattened-multiver-counts-no-bc.svg}
+ \caption{Counts of built projects with different numbers of unremovable
+ dependency conflicts. Packages which appear to be nonfunctional due to
+ aggressive dependency elimination are not included.}
+ \label{fig:unflattened-multiver-counts-no-bc}
+\end{figure}
+
+As one can see, even these ``cleaned'' results show no relation between
+project's dependency tree sizes and its Debian presence.
+
+\chapter{Conclusions}
+
+The results of conducted experiment allow the questions stated at the beginning
+of \ref{chp:experiment} to be answered.
+
+\section{Naming the main hindrance to packaging the npm ecosystem}
+\label{sub:naming-the-hindrance}
+
+We expected that huge dependency trees and presence of conflicting dependencies
+are the major obstacles to packaging npm projects into reproducibility-focused
+software distributions. The experiment results show the contrary. If this
+hypothesis were true, we would see npm projects with more complex dependency
+trees less frequently packaged into Debian -- but there is no such relation.
+
+What are then the most likely reasons for relatively small number of software
+from the npm ecosystem in Debian and GNU Guix? For the latter, the challange of
+bootstrapping several popular, self-depending build tools appears relevant.
+Five of these were mentioned in \ref{sub:self-depending-software}. As of
+\debianTresholdDate{}, four of them -- \code{typescript}, \code{@babel/core},
+\code{rollup}, and \code{gulp} -- are present in Debian, albeit under different
+names. The Debian packages of all four were also found to depend on themselves
+to build. Since GNU Guix' policies make it more difficult to add self-depending
+packages to the distribution, this explains the drastically different coverage
+of the npm ecosystem by these system software distributions.
+
+Aside from that, the incompatibility of JavaScript developers' workflows with
+system software distribution packages -- as highlighted
+in~\ref{sec:inconvenience-of-distros} -- should be considered the major issue.
+
+\section{Developer-supplied lockfile being infrequently necessary}
+
+As found, only two tested projects could be built with an upsteam lockfile but
+failed when it was re-generated. Meanwhile, \builtSuccessfullyCount{} packages
+were built successfully in both ways. This is consistent with the expectations.
+Repetition of the dependency resolution is not a likely source of build
+failures.
+
+\section{Indispensibility of direct and indirect npm build dependencies}
+
+It was found that a non-negligible subset of both direct and indrect npm project
+dependencies is unnecessary for a successful build. The effect of removal of
+unnecessary direct dependencies can be comprehended by comparing the leftmost
+two pairs of boxes in Figure~\ref{fig:tree-size-stats-no-bc}. There is on
+average an almost triple reduction in dependency tree sizes, although with a
+huge variance. The average number of direct dependencies shrank from
+\allDirectDepsAvg{} with sample standard deviation of \allDirectDepsStdDev{} to
+\necessaryDepsAvg{} with sample standard deviation of~\necessaryDepsStdDev{}.
+
+Comparing the second and third pair of boxes in Figure
+\ref{fig:tree-size-stats-no-bc} shows that almost a half of projects' remaining
+indirect dependencies is also not necessary during build, again with a huge
+variance. The experiment's automated method of determining the unnecessary
+dependencies was not perfect, as shown in~\ref{sub:use-of-different-dep-ver},
+but sufficient to allow a conclusion that during packaging, the elimination of
+both kinds of dependencies can be worth attempting.
+
+\section{Typical dependency tree sizes of npm projects}
+\label{sec:typical-dep-tree-sizes}
+
+The average sizes of built projects' dependency trees ranged from \origTreeMin{}
+to \origTreeMax{} with sample standard deviation of~\origTreeStdDev{}, as shown
+in Figure~\ref{fig:tree-size-stats-no-bc}. With the experiment's method, it was
+possible to reduce the tree sizes by about a ratio of five on average.
+
+The final dependecy tree sizes of about $160$ are not drastically higher than
+those in other software ecosystems. For example, as of \debianTresholdDate,
+Debian package \code{python-xrt} was found to be built in an environment
+with $180$ installed packages named \code{python-*}, as evidenced in its
+\code{.buildinfo} file.
+
+It is worth noting that the numbers discussed here and in the following section
+might be representative of only the more complex npm packages. As explained
+in~\ref{sub:packages-not-following-conventions}, there can be many popular npm
+projects like \code{semver} that do not require an actual \code{build} action,
+likely have fewer declared dependencies, and likely only require the npm tool to
+create the package tarball.
+
+\section{Frequency of dependency conflicts in npm projects}
+
+Among the npm projects built successfully, only one had no conflicting
+dependencies in its original tree. This shows that dependency conflicts are
+indeed a normal and accepted thing in the npm ecosystem.
+
+In the original dependency trees, the count of dependencies in conflict averaged
+at \origTreeMultiverAvg{} and sample standard deviation
+was~\origTreeMultiverStdDev{}. In case of more than half of the projects the
+conflicts were completely eliminated. Several cases of unremovable conflicts
+remained, as can be sees in Figure~\ref{fig:unflattened-multiver-counts-no-bc}.
+However, this part of the results should not be considered entirely
+representative of the real state of affairs, as explained
+in~\ref{sec:dep-conflict-counts}. It is expected that through manual work the
+build process of many more npm packages can be made free of dependency
+conflicts.
+
+\section{Package disfunctionality caused by dependency tree reduction}
+
+As witnessed in~\ref{sub:apparently-disfunctional-pkgs}, there is a
+non-negligible number of cases where forced removal of direct or indirect
+dependencies causes built package to lack important pieces of code or have other
+deficiencies. Many of these problems were caused by Rollup bundler's liberal
+treatment of missing code modules and could be automatically detected, for
+example by searching the build logs for specific strings.
+
+Nevertheless, the risk of building disfunctional packages appears relatively
+high, which is not acceptable if this experiment's method were to be used for
+preparation of package recipes in a reproducibility-oriented software
+distribution. Since in more than half of all cases the diffoscope's reports on
+built package differences were found comprehensible, it is advisable to manually
+investigate dependency removals whose effects are unclear.
+
+Additionally, the method itself proved to have a weakness of allowing a removed
+direct dependency to still appear as an indirect dependency, as shown in
+\ref{sub:use-of-different-dep-ver} and~\ref{sub:inlining-of-a-dep}. Although
+this does not appear to have lead to built packages' disfunctionalities during
+the experiment, it is a space for improvement. One simple solution would be to
+eliminate direct dependencies through dummification, as it was already done with
+indirect ones.
+
+\section{Relevance of npm package distribution tags for successful dependency resolution}
+
+The dependency resolution failures described in \ref{sub:resolution-failures}
+were all manually analyzed and none was found to be caused by a dependency
+specification referencing a distribution tag omitted in the experiment. Four of
+the built projects were found to have a direct or indirect dependency specified
+by the \code{latest} tag. Among others, \code{typescript} -- the most popular
+npm package according to the ranking in~\ref{chp:most-popular-dev-deps} --
+requires its direct development dependency \code{@types/node} to be in version
+tagged \code{latest}.
+
+Concluding, the special \code{latest} tag should be present in npm
+dependency metadata to avoid needless dependency resolution failures.
+Fortunately, it can usually be derived from the available package versions. All
+other tags can in the vast majority of cases be omitted from the dependency
+resolution matadata, removing the need to rely on external, mutable npm
+distribution tags information.
+
+\section{Prototype for npm dependency resolution under Paradigm 3}
+
+The experiment's environment resembled that proposed in \ref{sec:paradigm-3} for
+Paradigm 3 for hermeticity and reproducibility. The approach with a host-side
+service performing requests on behalf of the isolated guest was indeed workable.
+The experiment also showed that this prototype could benefit from added ability
+to provide the guested npm process with dependency package files hosted on
+different sites than just the npm Registry. This would require additional work
+to have npm's package tarball requests reach the locally-running service. A
+solution could involve configuring npm to use a TLS-enabled HTTP proxy in the
+likes of mitmproxy~\cite{mitmproxy}. While burdensome, it should be workable.
+
+At the same time, obtained results did not contain the expected indicators that
+Paradigm 1 -- and Paradigm 2 with flat input metadata of the dependency
+resolution step -- is insufficient in practice for handling the complex npm
+ecosystem. This means that new paradigms, as proposed in this work, are not
+necessary for further progress in the field of reproducible software builds.
+However, paradigms 3 and 4 can still prove useful in addressing the
+bootstrappability and developer usefulness issues named
+in~\ref{sub:naming-the-hindrance}.
+
+\chapter{Summary}
+
+Throughout the course of this work it was found that software industry shows
+some modest interest in reproducible builds. Practical difficulties in applying
+reproducibility to software projects hinder popularisation of this security
+measure. Software developed around popular technologies must become easier to
+rebuild hermetically and to test for reproducibility. This will allow
+reproducible builds to be more broadly recommended and mandated.
+
+Even though the concept of end user verification of build reproducibility offers
+increase in security confidence, years after 2018 saw little progress in its
+application. Due to the issues of rebuilder lag and occasional build
+nondeterminism, continuous tests performed independently of end users should be
+considered a more practically viable security measure.
+
+Even when build reproducibility is aimed and tested for, software distributions'
+approaches differ. Metadata used to reproduce Debian packages was found
+insufficient to also reproducibly verify the results of dependency resolutions
+that package builds relied on. This is not a severe vulnerability. However, it
+motivates increased interest in purely functional package managers, whose design
+excludes the possibility of such ``verification hole'' occuring.
+
+Contrary to intuition, traditional software packaging scheme of Debian can be
+applicable even to npm software with complex dependency graphs. One could still
+attempt to utilize suggested Paradigm 3 or 4 to replace existing approaches of
+Debian and GNU Guix. However, such efforts would offer no certain benefit.
+
+Although npm packages tend to have large dependency tree sizes and conflicting
+dependencies, this is not the ultimate reason for almost zero coverage of the
+npm ecosystem by GNU Guix. Instead, the issues of package bootstrappability
+appear to be the determining factor. These, however, are practically solvable.
+
+The packages in reproducible software distributions must be bridged with
+developers' preferred workflows. If this does not happen, the distributions
+will not only be slow in including software from the npm Registry and similar
+repositories. The software already in those distributions will fail to bring
+the security benefits that it could.
+
+As long speculated, much of declared dependencies of a typical npm project are
+not needed to build it. It was found that many indirect dependencies are also
+unnecessary. Their omission is crucial both to simplify packaging into software
+distributions and to reduce the software supply chain attack surface.
+
+\chapter{Future work}
+
+During npm project builds analysis it was found that projects exist which
+require no code transformations during build. One such case is described in
+detail in~\ref{sub:packages-not-following-conventions}. If identified,
+projects from this category could be packaged hermetically and reproducibly with
+minimal effort. Automated identification of such projects could be a future
+research goal.
+
+After the experiment, bootstrappability was named a likely major reason for
+small coverage of the npm ecosystem by GNU Guix. The finding of viable and
+optimal bootstrap chains of npm packages could therefore be the subject of
+another research.
+
+Paradigms 3 and 4 for hermeticity and reproducibility were proposed to address
+the issue of incomprehensibly complex dependency relations between npm packages.
+It was found that in the context of reproducible builds, the issue is resolvable
+even without employing these paradigms. However, it is yet to be checked --
+possibly in a new work -- whether these paradigms can actually make software
+packaging process less labor-intensive and therefore more efficient.
+
+This work touches the topic of securing the inputs of software builds. The
+applicability of methods like the suggested canaries could be further
+investigated.
+
+A subset of tested projects did not have their VCS revisions tagged in the
+expected way. The tagging habits of developers and means of automated
+identification of VCS revisions corresponding to releases could be researched.
+A possible approach to solving the problem of missing VCS tags could involve
+comparing commit dates with package version release dates. If devised, a
+successful method would benefit VCS-based software packaging, which appears
+desirable in the light of the XZ backdoor.
+
+npm developers are not incentivized make their software easily bootstrappable.
+Proof of concept of Ken Thompson's Trusting Trust attack~\cite{Thompson:1984}
+could be presented for one or more popular npm packages. It could help raise
+awareness of the supply chain issues and make the community interested in
+rebuildability and ultimately bootstrappability. The PoC could be a
+self-implanting backdoor in one of the self-depending builds tool.
+
+\clearpage
+
+\phantomsection{}
+\addcontentsline{toc}{chapter}{\refname}
+\printbibliography
+
+\end{document}