Skip to content

0.5.6

Choose a tag to compare

@rootcoder007 rootcoder007 released this 06 Oct 01:52
· 179 commits to main since this release

A security and correctness release from a full external review of 0.5.5 (every item below was reproduced against the installed package before it was fixed, and each has a test that fails on 0.5.5).

  • verify_capsule() fails when it cannot check. A provenance file that lacked resource$sha256, resource$filename or the whole resource block -- or was exactly {} -- verified a capsule with ALTERED data as ok = TRUE, because an absent field appended no row and all() of the remaining rows was still true. Every check that cannot be made is now a FAILED row with the reason, ok requires the three required rows (provenance_readable, data_present, data_sha256) to exist and pass, a pinned script digest is checked whenever one exists, a non-CSV file with a recorded row count or schema is a failed check, a <data>.synthetic sidecar is a failed check, and the result has a class and a print method that shows what was skipped.
  • The DER parser no longer aborts R. A 22-byte file made cert_parse() (and every OCSP, timestamp and X.509 path that parses network bytes) throw std::bad_alloc through std::terminate, killing the session past any tryCatch(): a length field could be read as 2^64-1 and the bound checks wrapped. Lengths are bounded without wrapping, the node count is capped, a child must advance the cursor, and no C++ exception can cross the R boundary (rmbl_asn1.cpp). The same discipline for Rf_error() over live C++ objects in the RSA, BLAKE2b, SHAKE and digest entry points.
  • ML-DSA signing cannot loop forever. A length-correct but corrupt secret key (s1/s2 outside +-eta) is refused in words, and the rejection loop is capped. The seven unguarded FIPS 204 arithmetic probes that read past a 1-byte allocation are removed (nothing called them); the two other unreachable .Call registrations are gone too. pack_sig() uses memmove() for its aliased copy; the poly_chknorm() comment says what the code does.
  • Every .Call entry that reads a string checks it is one (rmbl_str0()); random_bytes() prefers getrandom(2) where glibc has it; XMSS key generation can be interrupted.
  • Paths and URLs are checked before use. resource$filename, bundle paths and the pipeline's download target must be plain relative paths inside the capsule (.., absolute paths, drive letters and symlinks out are refused). Every URL the resolvers return and every URL bricklayer_download(), friendly_download() and download_data() fetch must be https (plain http only with options(rmoriebricklayer.allow_http = TRUE)), never file:// (an offline test passes allow_file = TRUE), and never a loopback, link-local, private or metadata address. The Wayback availability API is queried over https; the snapshot URL it returns passes the same gate. libcurl transfers are bounded: 5 redirects pinned to http(s), 2 GiB to a file, 64 MiB into memory, and the "available": true check reads the literal value.
  • JSON. A repeated key is an error (duplicate_keys = "keep" restores jsonlite's behaviour); nesting is capped at 200 levels; a lone surrogate escape is an error rather than the text "NA"; an integer above 2^53 warns once unless bigint_as_char = TRUE. bricklayer_json_unserialize() rebuilds data types only by default and refuses functions, calls, expressions, namespaces and S4 objects (trusted = TRUE for your own session's JSON). manifest_canonical() sorts a level with an empty-string key, so a re-serialised manifest keeps its digest. json_gzip_decode() refuses a member whose ISIZE trailer declares more than 512 MiB and checks the trailer after inflating.
  • Synthetic data say so. make_synthetic_csv() writes a <path>.synthetic sidecar and returns a classed object; make_manifest() and write_manifest_json() set meta$synthetic from BRICKLAYER_SYNTHETIC themselves, and record() marks every comparison INFO on a synthetic manifest, whatever the analysis script did.
  • The reference pipeline stops on a digest mismatch (exit 7) unless --allow-digest-drift is given, and then records the override in the manifest. Its subprocess command line is fully quoted.
  • capsule_bundle() hashes dotfiles and capsule_bundle_verify() lists them; only the bundle's own file is exempt from "unlisted".
  • The hosted-tier endpoints taken from MORIE_DATA_URL / MORIE_HOSTED_BASE_URL must be https and public before a bearer key is sent; the credentials file is created 0600 before the key is written; a DRBG reseeds itself from the operating system in a forked or restored process; the launcher install_cli() writes quotes its library path.
  • Statistics. Chi-square p-values are the upper tail (they underflowed to exactly 0 in drift_chisq(), drift_homogeneity(), benford_test(), mcar_test() and the Mahalanobis screen); the KS p-value uses the theta-function series for small t (two million-row columns differing in one value were flagged as drifted); drift_psi() no longer collapses to one bin on a constant or mostly-constant reference (a 1000x rescale reported PSI = 0); drift_homogeneity() gives a Monte Carlo p-value below five expected counts, and capsule_drift() reports a PSI and the unseen-category share for categorical columns, so two disjoint date columns are flagged; a one-category table reports df 0 and NA. morans_i() honours the magnitudes of a weight matrix. The exact Mann-Kendall p-value enumerates the observed values, ties kept. eb_rates() refuses negative, missing and infinite counts. parse_bands() strips thousands separators, reads "ages 18-24", and leaves a bare "-5", inverted bounds, "100-200-300" and "15 to 19 and over" as NA; expand_bands() errors (or warns, with drop_unparsed = TRUE) instead of dropping units; band_values() refuses a cap below a band's lower bound. count_trend() uses t quantiles in its quasi-Poisson branch; trend_test() reports slope_ci_clamped when the pairwise slopes do not reach the requested confidence and refuses infinite values. Benford digits are read from the decimal rendering (2e-300 was digit 1).
  • Packaging. graphics, grDevices and tools are declared; R CMD check fails CI on a WARNING; a configure script probes pkg-config / curl-config for libcurl on Unix; an ASAN + UBSAN job runs the suite; DESCRIPTION names every scheme the package implements and states that none has been audited; the key and signature help pages carry a Security section; a FIPS 202 known-answer test covers SHA-3 and SHAKE directly; the LinkingTo consumer test calls all 45 published kernels; the stale CHANGELOG.md is gone (NEWS.md is the record).