Repository navigation
0.5.8
The third review of the hardening work (2026-10-06) found seven things,
every one of them a fix that had landed at the site a reproducer named and
not at its siblings. This release fixes each at every site of its kind and,
where the siblings can be listed, puts the list in the tests
(test-review3.R, test-src-hygiene.R), so a new one cannot appear
unnoticed.
Language-model routes and the signed services document
bricklayer_llm_ask()tries an endpoint of your own, then a local Ollama server, then the hosted MORIE tier.MORIE_LLM_BASE_URL(withMORIE_LLM_API_KEY,MORIE_LLM_MODEL) names any OpenAI-compatible server;OLLAMA_HOST/OLLAMA_BASE_URL(defaulthttp://localhost:11434,offto skip) a local Ollama, whose first listed model is the default unlessOLLAMA_MODELsays otherwise.route = "own" | "ollama" | "hosted"insists on one.bricklayer_llm_status()andrmoriebricklayer doctorreport all three, in order;agent_bundle()follows the same order.- The hosted tier's addresses come from a signed document, not from the package.
bricklayer_services()readshttps://rmorie.com/.well-known/morie-services.jsonand its detached ML-DSA-44 signature, verifies it against the public key pinned in the package, caches it for a day undertools::R_user_dir(), refuses a document older than the one it holds, and falls back to the cached copy, then the copy bundled ininst/services/, then a document with every service switched off. The endpoint, the model list, the sign-in address and whether the tier is open can now change without a package release; a document that does not verify is ignored.MORIE_HOSTED_BASE_URL,MORIE_HOSTED_AUTH_URL,MORIE_HOSTED_MODELandMORIE_DATA_URLstill override. The hosted tier is a last resort; keys are personal and issued on request at https://rmorie.com/access, and every hint in the package says so. The GitHub and emailed-code sign-ins keep working. - Plain http on the loopback host is admitted for the local routes only. The URL policy that guards every other address refuses
http://and loopback twice over; the own-endpoint and Ollama calls setoptions(rmoriebricklayer.allow_loopback = TRUE)for their own duration, which admits exactly the loopback host (localhost,127.0.0.0/8,::1), nothing else private, and a redirect off it meets the ordinary rules (test-llm-routes.R). - SIU core: every string-to-integer conversion on a regex capture goes through one bounded helper (
small_int());std::stoion a tag such asSO #4444444444444444threwstd::out_of_rangeand aborted the process (found by the libFuzzer target; the input is in the fuzz corpus). Ported to the rmorie and morie copies. bricklayer_siu_text()andbricklayer_parse_siu()killed R on 25 KB of whitespace. libstdc++'s regex executor recurses once per character a repeated atom consumes, so\s+over a long run overflowed the C stack, which notryCatch()and no exception barrier can see. Every pass that runs over a whole document (newline normalisation, script and style removal, tags, entities, whitespace) is a plain loop now, byte-identical to the regexes it replaced (the test keeps the old pipeline as an R reference and compares); the text every field extractor sees has its spaces collapsed and no line longer than 2,000 characters, so no remaining regex can recurse further than that (and MSVC's regex, which has a step budget instead of a stack, stays inside it -- the one time it did not, the public entry points now answer "no match" instead of throwing); the input cap of every SIU entry point is 2 MiB (a report page is a few hundred KB). The guard is a test that enumerates every one-string SIU entry point from the namespace and runs ten hostile inputs through each in a subprocess. XMSS key generation polls for Ctrl-C with the throwing check like the other ten sites.- One attestation still verified two documents. 0.5.7 wrote a level whose names were all empty as an array, so
{"k":{"":"X"}}and{"k":["X"]}shared a digest. The names attribute, present or absent, is now the one thing that tells an object from an array at every level, empty names included, and a named atomic vector is an object too ({"a":1}is not[1]). Attestations of documents whose fields are named vectors change digest; capsules verified against a stored digest should be re-attested. - Every network byte goes through the compiled transport.
bricklayer_download()gated with the validator and then transported with base R'surl(): no DNS check, no pin, no redirect re-validation, no size cap on the package's own documented pipeline;bricklayer_json_from_json()fetched a bare URL with no gate at all;download_data()wrappeddownload.file(). All three usesrc/rmbl_fetch.cpp, with the live progress bar driven by a callback from libcurl. The transport itself now dropsAuthorization,CookieandProxy-Authorizationon a cross-host redirect, refuses a redirect from https to plain http even when the http option is set, checks a URL before opening the destination and writes beside it, moving the body into place only on a 2xx (a refused URL or a 404 no longer deletes a pre-existing file). The redirect rule is one pure function with its own.Callentry and tests. A single-label hostname (metadata,instance-data) is refused by shape, without DNS. A test lists every network open inR/and allows exactly the two base-R fallbacks a standalone capsule bundle needs. - The interrupt flag. The barrier cleared
rmbl_kernel_interruptedonly on the normal path; it is cleared on entry and on every exit now, and a kernel called with no barrier active (a sibling package's own.Call) re-raises the pending interrupt instead of eating it and returningNA.rmbl_pbkdf2_sha256()in the C API returns an int (0, or -1 for an argument it refuses); an interrupt inside it is raised, never a zero key. capsule_drift()'s identifier heuristic applies its ratio test from a hundred rows a side: a five-level factor on ten rows gets a verdict again.mahalanobis_outliers()carrieslog_p_value, the fifth chi-square site;log_p_valueis documented at every site.drift_psi()accepts anyepsin (0, 1) again (0.5.7 refused values above 0.01, a new hard error on a previously accepted argument) and the help explains what the floor does to the index.- The Mann-Kendall exact-distribution memo is bounded; hostnames may carry underscores;
Makevars.win's libcurl fallback link line is wired in;rmbl_digest_equal()gets the same value barrier as the ML-KEM select.