Skip to content

fix(settings): read the settings file with a cap, and repeat a bad value back short - #58

Open
HackPoint wants to merge 5 commits into
mainfrom
agents/settings-hardening
Open

HackPoint wants to merge 5 commits into
mainfrom
agents/settings-hardening

Conversation

@HackPoint

@HackPoint HackPoint commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

What

lumen_settings.json is read on every tools/call. read_file read it whole, whatever it was, and a long bad value in it was repeated back whole in the fault row and on stderr. This adds two limits. A valid file is read exactly as before.

Change

  • The file is read through a 64 KiB cap (MAX_FILE_BYTES, in read_capped). One byte over the cap is enough to know it is over, so the reader stops there. A larger file is Unreadable("larger than 65536 bytes"): repeat reads off and reported, as for any unreadable file.
  • A path that is not a regular file (a named pipe, a device, a directory) is Unreadable("not a regular file") and is not opened. A named pipe used to block the call until something wrote to it.
  • A wrong-typed value in the file and an unrecognised LUMEN_REPEAT_READS are repeated back as at most 64 characters and an ellipsis (bounded, cut on a character boundary). A short value is repeated whole; a value of exactly 64 characters is too.

Precedence (env, then file, then default ON), OFF-on-trouble and the doctor finding are as they were. No schema change, no new dependency, no new fault kind.

Evidence

Order of commits: the tests first (red), then the fix, then two commits that pin what a mutation run and the review found loose.

  • Red, commit 1 (constants stubbed so it compiles): 4 of 23 settings tests fail, the cap test, the pipe test (after its 10 s guard, not by hanging), and both echo tests.
  • Green after the fix: 25 of 25 with the boundary test of the last commit.
  • Mutations, each reverted:
mutation caught by
cap off by one (take(cap)) the cap test and the never-ending-reader test
cap removed (take(u64::MAX)) the never-ending-reader test
is_file check removed the pipe test
bounded returns its input both echo tests
echo limit 65 both echo tests
bounded cuts on a byte, not a character the env echo test (multibyte value)
echo cut at the 64th or the 66th character the boundary test (64 kept whole, the 65th cut)

The first run of this table showed two mutations slipping through: removing take left every test green, because an oversized file was still rejected, only after being read whole; and the echo tests were relative to the constant, so changing it passed. The last commit moves the cap into read_capped, adds a test that feeds it a reader that never ends and fails if it is read past one byte over the cap, and makes the echo tests assert the 64 characters and the ellipsis exactly.

  • Cost of the read, medians (release, read_file): no file about 1 us, a 120-byte file about 25 us. Both are what they were before: the added stat is not visible against the open and the read.
  • A file of exactly 64 KiB is read, one byte more is not. A 10,000-character value, ASCII and multibyte, comes back as 64 characters and …; "yes" and bogus come back whole.

Gate

Local gate green on b47f06c (740 s): fmt, clippy -D warnings on the workspace and on the Tauri crate, workspace tests, frontend build and tests with thresholds, e2e (read path, ingest path, decision metering), Tauri crate tests, hook e2e, MSRV 1.94.0 build, install verify. Not run locally: the debug app bundle, because no app code changed. Run with LUMEN_RANKED_TIME_BUDGET_MS=60000, because the lumen-core ranked-outline budget test is wall-clock bound and flakes when the machine is loaded; nothing here touches it.

After review, 365cdff adds one test, shortens one doc comment and rewords one CHANGELOG sentence. For it: fmt, clippy -D warnings on lumen-core with all targets, the 25 settings tests, the silent-fallback guard and scripts/test-release-notes.sh, all green. The head, a96eafb, rewords that sentence again, CHANGELOG only: a value of at most 64 characters is repeated whole, so "the first 64 characters and an ellipsis" was wrong for it; scripts/test-release-notes.sh passes.

The pipe test is Unix-only (mkfifo); the cap, echo and reader tests run everywhere.

Not done

The third review nit on the settings work, that the in-process lumen-mcp tests read the developer's own settings file and environment and could write to the real fault spool, is not fixed here. It needs its own change, a hermetic harness for those tests, and it does not affect 1.8.0 behaviour.

…choed short

Four tests, red against the reader as it is: a file one byte over 64 KiB is still read whole,
a named pipe where the file should be blocks the read, and a wrong-typed value or an
unrecognised LUMEN_REPEAT_READS is repeated back at any length into the fault row.
…lue back short

lumen_settings.json is read on every tool call. It was read whole whatever it was, and a
named pipe or device where the file should be could block the call. A file over 64 KiB or one
that is not a regular file is now unreadable (repeat reads off, reported as for any unreadable
file). A wrong-typed value and an unrecognised LUMEN_REPEAT_READS are echoed as at most 64
characters into the fault row and the stderr line.
Taking the cap off the reader was not caught: a file over the cap was still rejected, only after
being read whole. The cap now lives in read_capped, and a test feeds it a reader that never ends
and fails if it is read past one byte over the cap. The echo tests assert the exact 64 characters
and ellipsis, so changing the limit fails them too.
…angelog what an echo keeps

A value of exactly 64 characters is repeated whole; the 65th is cut and an ellipsis added.
… is cut

The entry said every echo is the first 64 characters and an ellipsis. A value of at most 64 characters comes back as written, with no ellipsis.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant