Deferred from PR #777's review (round 2, resolved there as option (b): comment corrected, contract pinned by test). This issue is option (a), the change that was deliberately not made because it needs a policy decision first.
What to do
Move the cleaning of the remote_config_rolled_back diagnostic message out of the collector and into the single ${d.message} render interpolation, so --json carries the raw assembled prose for diagnostics too, matching LLP 0225 #decision's general rule (the text surface escapes what a person reads; --json stays byte-exact).
- Assembly today:
src/core/daemon/status.js:1062 builds the message from sanitizeLabel'd components, so hyp status --json receives the cleaned sentence at diagnostics[].message (each component stripped, clamped to 120). The long comment above it (src/core/daemon/status.js:1038-1057) states this plainly and names this alternative.
- Target choke point:
src/core/commands/status.js:559, the one ${d.message} interpolation in renderStatusText's diagnostics loop. Cleaning there would incidentally cover the other diagnostic kinds, several of which interpolate untrusted-ish strings raw (an err.message, a validator pointer, file paths).
The open question that blocks it: one clamp width for all 14 diagnostic kinds
A clamp at the render applies to the whole assembled sentence, not per component, and it must hold for all 14 diagnostic kinds in src/core/daemon/status.js (14 distinct kind: values at head 078d762). The longest template, client_attached_not_configured at src/core/daemon/status.js:963, is already roughly 200 characters of the project's own prose before any captured value is substituted. So:
- the label default of 120 cannot even hold the fixed prose;
- the existing error clamp of 400 (
MAX_ERROR_CHARS, defined at src/core/commands/status.js:300 and src/core/commands/daemon.js:66) is tight enough that a legitimate message naming a full path could be truncated.
Someone has to pick that number and defend it across every kind. That is wider than #776 asked for, which is why it was filed rather than done.
What changes with it
- Revert
src/core/daemon/status.js:1062 to raw interpolation and trim the comment above it to match.
- Clean at
src/core/commands/status.js:559 with the chosen width.
- Update the two round-2 tests in
test/core/status-hostile-non-status-file.test.js that pin the current contract (hyp status --json carries the cleaned rollback prose, and the raw values beside it and a long rollback etag is clamped in the --json message but whole in the --json values): under option (a) the --json message becomes byte-exact and the text surface stays clean.
remote_config.last_rollback stays byte-exact either way; it already is.
Classification
Preference, not a blocker (PR #777 round 2's classification, confirmed at triage). Behaviour on the merged branch is safe: nothing hostile reaches a terminal on either surface, the structured values a program should read are byte-exact, and the code describes itself accurately.
Backlink: PR #777 (head 078d7627c9e06207c942bd56bb6740f5d62e44f4), fixing #776; review rounds at #777 (comment) and #777 (comment).
Deferred from PR #777's review (round 2, resolved there as option (b): comment corrected, contract pinned by test). This issue is option (a), the change that was deliberately not made because it needs a policy decision first.
What to do
Move the cleaning of the
remote_config_rolled_backdiagnostic message out of the collector and into the single${d.message}render interpolation, so--jsoncarries the raw assembled prose for diagnostics too, matching LLP 0225 #decision's general rule (the text surface escapes what a person reads;--jsonstays byte-exact).src/core/daemon/status.js:1062builds the message fromsanitizeLabel'd components, sohyp status --jsonreceives the cleaned sentence atdiagnostics[].message(each component stripped, clamped to 120). The long comment above it (src/core/daemon/status.js:1038-1057) states this plainly and names this alternative.src/core/commands/status.js:559, the one${d.message}interpolation inrenderStatusText's diagnostics loop. Cleaning there would incidentally cover the other diagnostic kinds, several of which interpolate untrusted-ish strings raw (anerr.message, a validator pointer, file paths).The open question that blocks it: one clamp width for all 14 diagnostic kinds
A clamp at the render applies to the whole assembled sentence, not per component, and it must hold for all 14 diagnostic kinds in
src/core/daemon/status.js(14 distinctkind:values at head078d762). The longest template,client_attached_not_configuredatsrc/core/daemon/status.js:963, is already roughly 200 characters of the project's own prose before any captured value is substituted. So:MAX_ERROR_CHARS, defined atsrc/core/commands/status.js:300andsrc/core/commands/daemon.js:66) is tight enough that a legitimate message naming a full path could be truncated.Someone has to pick that number and defend it across every kind. That is wider than #776 asked for, which is why it was filed rather than done.
What changes with it
src/core/daemon/status.js:1062to raw interpolation and trim the comment above it to match.src/core/commands/status.js:559with the chosen width.test/core/status-hostile-non-status-file.test.jsthat pin the current contract (hyp status --json carries the cleaned rollback prose, and the raw values beside itanda long rollback etag is clamped in the --json message but whole in the --json values): under option (a) the--jsonmessage becomes byte-exact and the text surface stays clean.remote_config.last_rollbackstays byte-exact either way; it already is.Classification
Preference, not a blocker (PR #777 round 2's classification, confirmed at triage). Behaviour on the merged branch is safe: nothing hostile reaches a terminal on either surface, the structured values a program should read are byte-exact, and the code describes itself accurately.
Backlink: PR #777 (head
078d7627c9e06207c942bd56bb6740f5d62e44f4), fixing #776; review rounds at #777 (comment) and #777 (comment).