Repository navigation
Fix/hunt2 repl - #373
Fix/hunt2 repl#373
Conversation
Strix Security ReviewWarning This pull request has 84 commits after the last Strix review ( 1 open security finding on this PR:
Review summaryReviewed PR #373 (fix/hunt2-repl), which reworks the REPL line editor, history handling, completion quoting, the formatter (Fmt), and the LSP error/lifecycle handling. Most changes are hardening or correctness improvements (path quoting, header-case-insensitive LSP framing, formatter idempotency and BOM preservation, error responses for every request). One finding was identified: the destructuring Fixed the findings? re-run the review, or tag Updated for Reviewed by Strix |
| for n, sch in schemes do | ||
| Console.WriteLine $"{n} : {formatEchoTy sch.Ty}" | ||
| match bindings |> List.tryFind (fun (bn, _) -> bn = n) with | ||
| | Some(_, bv) when bv <> Eval.VUnit -> | ||
| let rendered, hint = Eval.echoValue cap (Eval.echoPrep bv) | ||
|
|
||
| Console.WriteLine | ||
| $"{n} : {formatEchoTy sch.Ty} = {Eval.sanitizeTtyData rendered}{Eval.echoTail hint}" | ||
| | _ -> Console.WriteLine $"{n} : {formatEchoTy sch.Ty}" |
There was a problem hiding this comment.
🟢 Destructuring let echo executes command-backed (lazy) bindings, bypassing the REPL's command-echo guard
Severity: LOW
In the REPL, a destructuring let binding (for example let (a, b) = (1, g) where g is a lazy command-backed value such as let g = sh -c "…") now evaluates and renders each binding's value when echoing the result. Unlike the single-let form, this path never applies the command-backing guard, so rendering the value enumerates it and runs the deferred command immediately at echo time — contradicting the documented behavior that the REPL echo of a command-backed let never runs it.
| for n, sch in schemes do | |
| Console.WriteLine $"{n} : {formatEchoTy sch.Ty}" | |
| match bindings |> List.tryFind (fun (bn, _) -> bn = n) with | |
| | Some(_, bv) when bv <> Eval.VUnit -> | |
| let rendered, hint = Eval.echoValue cap (Eval.echoPrep bv) | |
| Console.WriteLine | |
| $"{n} : {formatEchoTy sch.Ty} = {Eval.sanitizeTtyData rendered}{Eval.echoTail hint}" | |
| | _ -> Console.WriteLine $"{n} : {formatEchoTy sch.Ty}" | |
| for n, sch in schemes do | |
| match bindings |> List.tryFind (fun (bn, _) -> bn = n) with | |
| | Some(_, bv) when bv <> Eval.VUnit -> | |
| if letCommandBacked te bv then | |
| noteCommandSeq n te bv | |
| echoMeta (commandBackedEcho n sch.Ty) | |
| else | |
| let rendered, hint = Eval.echoValue cap (Eval.echoPrep bv) | |
| Console.WriteLine | |
| $"{n} : {formatEchoTy sch.Ty} = {Eval.sanitizeTtyData rendered}{Eval.echoTail hint}" | |
| | _ -> Console.WriteLine $"{n} : {formatEchoTy sch.Ty}" |
Prompt to fix with AI
This is a security vulnerability found during a code review.
Vulnerability: Destructuring `let` echo executes command-backed (lazy) bindings, bypassing the REPL's command-echo guard
Severity: LOW
In the REPL, a destructuring `let` binding (for example `let (a, b) = (1, g)` where `g` is a lazy command-backed value such as `let g = sh -c "…"`) now evaluates and renders each binding's value when echoing the result. Unlike the single-`let` form, this path never applies the command-backing guard, so rendering the value enumerates it and runs the deferred command immediately at echo time — contradicting the documented behavior that the REPL echo of a command-backed `let` never runs it.
Location: src/Weir/Repl.fs:2099-2106
Context: Destructuring let echo evaluates bindings without the command-backing guard
```
// Before:
for n, sch in schemes do
match bindings |> List.tryFind (fun (bn, _) -> bn = n) with
| Some(_, bv) when bv <> Eval.VUnit ->
let rendered, hint = Eval.echoValue cap (Eval.echoPrep bv)
Console.WriteLine
$"{n} : {formatEchoTy sch.Ty} = {Eval.sanitizeTtyData rendered}{Eval.echoTail hint}"
| _ -> Console.WriteLine $"{n} : {formatEchoTy sch.Ty}"
// After:
for n, sch in schemes do
match bindings |> List.tryFind (fun (bn, _) -> bn = n) with
| Some(_, bv) when bv <> Eval.VUnit ->
if letCommandBacked te bv then
noteCommandSeq n te bv
echoMeta (commandBackedEcho n sch.Ty)
else
let rendered, hint = Eval.echoValue cap (Eval.echoPrep bv)
Console.WriteLine
$"{n} : {formatEchoTy sch.Ty} = {Eval.sanitizeTtyData rendered}{Eval.echoTail hint}"
| _ -> Console.WriteLine $"{n} : {formatEchoTy sch.Ty}"
```
How to fix:
Apply the same command-backing guard used by the single-`let` echo path to the destructuring path: for each destructured binding, detect whether its value is a command-backed (unforced) sequence using `letCommandBacked`, and when it is, record the binding via `noteCommandSeq` and print only the type-and-hint metadata (`commandBackedEcho`) instead of enumerating and rendering the value. Pure/forced values should continue to echo normally.
Please fix this vulnerability. If you propose a fix, make it concise and minimal.React 👍 / 👎 to tune Strix for this repo. A repo collaborator (or the PR author) can resolve this thread to dismiss the finding.
…es, case-insensitive headers (H29)
…on what it accepts (H30)
…e, #help map answers as Seq.map, #infer notes name the field (H57)
… H47 accepts the entry-read exit
No description provided.