Security

This document collects the integrity and resource-safety rules applied at the release, installer and runtime boundaries. It does not claim protection against a compromised GitHub account or compromised release signing infrastructure; the current model uses HTTPS and published SHA-256 files.

Trust boundaries

Validation occurs in three places:

release pipeline
  protects the asset set before publication

cup asset installer
  protects the initial executable and configuration before installation

cup runtime
  protects metadata, component packages and cup-update assets before use

Some values are checked more than once. This is defense in depth at distinct boundaries, not duplicate business logic. The publisher validates what it publishes; each consumer validates what it receives.

HTTPS policy

Runtime downloads use libcurl and require HTTPS. Redirects are followed only when the resulting protocol remains HTTPS. Plain HTTP component or checksum URL templates are rejected during catalog loading.

Transfers use:

  • connection and total timeouts;
  • a low-speed timeout;
  • interrupt-aware progress callbacks;
  • explicit response-size limits;
  • exclusive temporary files;
  • cleanup of partial files after failure.

Network, TLS, timeout and size-limit failures remain distinct CupError values.

Embedded CA bundle

The versioned trust source is:

certs/cacert.pem

Every configured build deterministically generates:

ca_bundle.h
ca_bundle.c

under the selected build directory. When embedded-bundle support is enabled, libcurl receives the in-memory CA data instead of a distribution-specific certificate path.

This avoids embedding a build-machine path such as an OpenSSL configuration or CA location into a standalone release. Linux fully static releases and macOS releases with statically linked third-party dependencies initialize OpenSSL without loading external configuration. Windows releases use the selected Schannel-based libcurl stack and retain only allowlisted system DLL imports.

The source PEM changes only through make update-ca-bundle. The adjacent certs/cacert.meta binds it to its HTTPS source, Mozilla source date, SHA-256, certificate count and freshness limit. make check-ca-bundle is offline and rejects tampering, future dates, suspiciously small stores and stale release inputs. The update script downloads, validates, generates and compiles a temporary representation, rejects date rollback and commits PEM plus metadata with rollback protection. Release workflows never mutate the selected commit.

POSIX OpenSSL archives are configured with no-autoload-config and no-dso; compiled default directories use the deterministic, deliberately absent /__cup_runtime__/openssl namespace. Compiler prefix maps and release path scans prevent checkout, dependency-root and transactional staging paths from entering published executables.

SHA-256 implementation

File hashing is implemented in-tree by src/sha256.c, adapted from Brad Conte's public-domain crypto-algorithms implementation. OpenSSL is not used as a direct checksum API.

SHA-256 is used because release assets publish SHA256SUMS. Policy remains outside the hash primitive:

checksum file parsing
duplicate filename rejection
expected asset selection
hash comparison
cache invalidation
release metadata consistency

A digest proves that bytes match the published checksum file. HTTPS is still required to retrieve the checksum and asset from the intended release boundary.

Checksum file parsing

Checksum readers reject:

  • malformed digest length or characters;
  • missing filenames;
  • unsafe filenames;
  • duplicate records for the selected asset;
  • absent expected asset names;
  • mismatching content.

Asset names are constructed from validated platform and package identities. They are not accepted directly from arbitrary network input.

Package downloads and cache

A package archive is usable only after:

resolve immutable package/checksum URLs from the catalog
download or inspect the cache entry
read the matching SHA256SUMS record
hash the complete archive
compare the digest

A mismatching cached archive is removed. If a checksum-valid cached archive fails extraction or package validation, it is discarded and fetched once from the network. The retry distinguishes local cache corruption from a consistently invalid published package.

Maximum download sizes are defined for metadata, cup asset binaries and package archives. The write callback refuses to exceed the selected limit even when a server omits or lies about Content-Length.

Archive preflight

The package archive domain is closed to tar.xz, tar.gz and zip. package_archive_is_valid enables only the corresponding libarchive readers, traverses the complete stream within the configured resource limits and compares the detected format/filter stack with the catalog selection. Renaming a ZIP as tar.xz, using an uncompressed TAR or relying on another format understood by libarchive is rejected.

Preflight is not the complete security boundary; extraction validates every entry again while creating files.

Archive extraction

Libarchive is used directly. Extraction rejects:

  • absolute POSIX paths;
  • drive-qualified or UNC-style Windows paths;
  • parent traversal;
  • unsafe backslash forms;
  • paths deeper than the configured limit;
  • more than the configured number of entries;
  • extracted content beyond the configured byte limit;
  • a missing or non-directory top-level root;
  • multiple unrelated top-level roots;
  • exact duplicates, ASCII case collisions and file/directory collisions;
  • Windows reserved names, trailing dots/spaces and non-ASCII internal names;
  • device nodes, FIFOs, sockets and unsupported entry types;
  • symbolic links that escape the package root;
  • hard links to anything except an already extracted regular file.

Files are extracted below a fresh staging directory opened without following a link. Privileged mode bits, ownership, timestamps, filesystem flags and unsafe inherited metadata are discarded. Directories become 0755; regular files become 0644 or 0755 according to their executable bits. Final protected metadata is made read-only through the platform abstraction.

No archive entry is written directly into the final package path.

Path validation

Domain identifiers use safe single path segments. Relative package paths reject empty segments, . and .., absolute roots and unsafe separators. Canonical paths are always assembled locally from validated values.

Filesystem inspection does not follow links when deciding an object's type. POSIX recursive removal is descriptor-relative (openat, fstatat, unlinkat) and refuses linked parents. Windows uses wide APIs, explicit reparse-point classification and long-path prefixes. This prevents a canonical package-level link from redirecting validation or cleanup outside the selected CUP root.

The cup root is private to its owner. POSIX enforces owner-only permissions; Windows uses a protected DACL limited to the current user, Local System and Administrators. doctor reports ownership or permission drift.

Package metadata

info.txt is validated after extraction and whenever a package is used for a default, inspection, diagnosis or repair. Required identity fields must match the command/catalog identity and canonical path.

Declared package commands must be safe relative paths to regular executable files within the package. Metadata is protected read-only after installation. cup does not repair individual metadata fields; an invalid package is replaced or quarantined as a whole.

cup assets

The installed cup asset generation is checked against:

SHA256SUMS.common
SHA256SUMS.<host>

SHA256SUMS.common contains exactly packages.cfg, install.cfg, install.sh and install.ps1. The installers verify their delegated counterpart before execution, while cup consumes the configuration records required for its asset generation. The platform checksum file contains the executable, uninstall helper and release.txt records for that release generation. The cup asset inspection verifies the catalog, installation policy, executable and uninstall helper against those sets; release.txt is consumed during installer and cup-update staging rather than retained as mutable local state. doctor reports missing or altered files and permissions. An official repair can refresh checksum files, the catalog and the uninstall helper from its own immutable release. On both POSIX and Windows it never removes or replaces the canonical cup or cup.exe executable used by the current installation. A missing or altered executable must be recovered by the official installer, or by the detached update helper when a valid interrupted-update journal assigns the rollback to it. A development build cannot restore official cup assets.

cup-update assets

The latest alias is used only to discover release.txt. After a concrete version is known, every replacement file comes from the immutable vX.Y.Z release URL.

The latest and versioned metadata must agree. The platform set verifies the executable, uninstall helper and release metadata. The common set verifies packages.cfg and install.cfg. Both text assets are parsed before staging is accepted, so checksum-valid but structurally invalid policy cannot become the active generation.

The detached helper replaces all six persistent cup assets as one recoverable generation. A mixed or partially published set is rejected before a journal is created. The transactional replacement is described in TRANSACTIONS.

Release publication

The release pipeline verifies:

  • version, tag and commit agreement;
  • the exact expected asset set;
  • checksum files with one record per expected asset;
  • native execution of each platform candidate;
  • installer consumption of the candidate assets;
  • the bytes uploaded to a draft or existing release.

A resumed draft is accepted only when it belongs to the expected tag/commit; unexpected or partial assets are replaced and the final remote set is compared with the verified candidate before publication.

THIRD_PARTY_NOTICES.txt accompanies the release with the notices and license texts for the pinned build. The copy maintained beside the dependency source lock also identifies the corresponding archives; integrity still comes from the tested candidate, published checksums and exact remote byte comparison.

See RELEASES.

Read-only protection

Read-only attributes protect against accidental local modification of:

packages.cfg
SHA256SUMS files
uninstall helper
installed package info.txt

They are not treated as a cryptographic trust mechanism. Integrity still comes from published checksums and package validation.

Current boundary

The project currently does not implement a separate signature or key-rotation infrastructure beyond GitHub HTTPS delivery and published SHA-256 files. Adding signatures would require a documented trust root, key distribution, rotation, revocation and recovery model; it should not be represented as a small checksum extension.

Implementation and verification

Security-boundary responsibilities are listed in ARCHITECTURE. Runtime, integration and release-contract verification are described in TESTING.

  • PACKAGES — catalog and metadata contract;
  • TRANSACTIONS — staged commit and recovery;
  • BUILD — CA generation and linked libraries;
  • RELEASES — publication gates.

Need help?
Please wait: the project is still pre-alpha!

Cup of Coffee is an open source project inspired by Cargo, Rustup Crates, and Conda-forge.