Skip to content

fix(interp): restore prior value for same-name inline-command vars#262

Merged
AlexandreYang merged 3 commits into
mainfrom
alex/fix-inline_var
May 19, 2026
Merged

fix(interp): restore prior value for same-name inline-command vars#262
AlexandreYang merged 3 commits into
mainfrom
alex/fix-inline_var

Conversation

@AlexandreYang

@AlexandreYang AlexandreYang commented May 19, 2026

Copy link
Copy Markdown
Member

Summary

  • Fix vuln-hunt F-2 (inline_var, P3): A=v1 A=v2 cmd was leaving A=v1 in the parent shell instead of restoring A's true pre-command value.
  • Root cause: each iteration recaptured prev after the previous assignment took effect, so the 2nd restore entry held the intermediate. The forward-order defer then applied the stale restore last.
  • Fix: dedupe the restore list by name — only the first prev per name is recorded, matching bash semantics.
  • Regression scenario added covering the 2- and 3-assignment cases.

Test plan

  • go test ./interp/... ./tests/ passes locally
  • New scenario same_name_multi_assign_restores_prior_value.yaml passes (and is asserted against bash by default in CI)
  • Verified bash output of the scenario matches expectation by running the script directly in debian:bookworm-slim
  • CI bash-comparison suite (RSHELL_BASH_TEST=1) green

Reference

vuln-hunt F-2 (inline_var, P3): `A=v1 A=v2 cmd` left `A=v1` in the
parent shell instead of restoring A's true pre-command value. Each loop
iteration recaptured `prev` after the previous assignment took effect,
so the second restore entry held an intermediate, and the forward-order
defer applied it last.

Dedupe the restore list by name so only the first prev per name is
recorded, matching bash semantics. Adds a regression scenario covering
the 2- and 3-assignment cases.
@AlexandreYang

Copy link
Copy Markdown
Member Author

@codex review this PR

Please use the following severity format for all findings:

  • P0 (red): Exploitable vulnerability with high impact (RCE, sandbox bypass, data breach). Blocking merge.
  • P1 (orange): Likely exploitable or high-risk — correctness bugs vs bash, data races, panics.
  • P2 (yellow): Potential vulnerability, bash divergence, missing test coverage, missing docs.
  • P3 (blue): Style, minor simplification, hardening suggestion, nice-to-have test.

Prefix each finding title with its priority label, e.g. "P0: ...", "P1: ...", etc.
Include a summary table at the top with columns: # | Priority | File | Finding.

@AlexandreYang AlexandreYang left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review (iteration 1)

Overall assessment: safe to merge — small, focused fix that moves rshell behaviour closer to bash. No security concerns. Only nice-to-have improvements to test coverage.

Summary table

# Priority File Finding
1 P3 Badge tests/scenarios/shell/inline_var/same_name_multi_assign_restores_prior_value.yaml Add interleaved same-name/different-name case (A=v1 B=vb A=v2 cmd) to strengthen regression coverage of the dedup logic
2 P3 Badge tests/scenarios/shell/inline_var/same_name_multi_assign_restores_prior_value.yaml Assert restore to unset for an unset-before variable (currently only the set-before case is covered)

Coverage

Code path Scenario test Status
A=v1 A=v2 cmd restores prior set value same_name_multi_assign_restores_prior_value.yaml Covered
A=v1 A=v2 A=v3 cmd restores prior set value same_name_multi_assign_restores_prior_value.yaml Covered
Same-name dedup with interleaved different names Missing (P3)
A=v1 A=v2 cmd when A is unset before Missing (P3)
Distinct-name multi-assign (existing) multi_assign_all_restored.yaml Covered (no regression)

Positive observations

  • Fix is minimal and matches the finding's preferred Option 1 (dedup by name).
  • Inline comment explains why only the first prev is the true pre-command value — good for future readers.
  • cd PWD/OLDPWD skip is preserved (the dedup happens before defer; the loop's restore filter still applies).
  • Restore semantics are unchanged for the readonly-target case (FOO=ok RO_VAR=evil cmd): existing pentest TestVulnHuntShellFeatureReadonlyBypass_MultiVarInlineWithReadonly still passes.
  • No filesystem, no new imports, no concurrency surface.

@AlexandreYang

Copy link
Copy Markdown
Member Author

Iteration 1 self-review: 0 P0 / 0 P1 / 0 P2 / 2 P3 — no blocking findings. P0_P1_P2_COUNT=0.

The two P3 findings are nice-to-have test-coverage extensions (interleaved same/different name case, and unset-before case). See the review at #262 (review).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f89a95915b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/scenarios/shell/inline_var/same_name_multi_assign_restores_prior_value.yaml Outdated
Add interleaved same/different-name case (`A=v1 B=vb A=v2 cmd`) and a
never-set-before case (`C=v1 C=v2 cmd`) to the regression scenario.
Both verified against debian:bookworm-slim bash.
@AlexandreYang

Copy link
Copy Markdown
Member Author

@codex review this PR

Please use the following severity format for all findings:

  • P0 (red): Exploitable vulnerability with high impact (RCE, sandbox bypass, data breach). Blocking merge.
  • P1 (orange): Likely exploitable or high-risk — correctness bugs vs bash, data races, panics.
  • P2 (yellow): Potential vulnerability, bash divergence, missing test coverage, missing docs.
  • P3 (blue): Style, minor simplification, hardening suggestion, nice-to-have test.

Prefix each finding title with its priority label, e.g. "P0: ...", "P1: ...", etc.
Include a summary table at the top with columns: # | Priority | File | Finding.

@AlexandreYang AlexandreYang left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review (iteration 2)

Overall assessment: safe to merge — no new findings. All P3 items from iteration 1 have been addressed (interleaved + never-set cases added to scenario; expect.stderr uses |+ block scalar per AGENTS.md).

Summary table

No findings.

Coverage

Code path Scenario test Status
A=v1 A=v2 cmd restores prior set value same_name_multi_assign_restores_prior_value.yaml Covered
A=v1 A=v2 A=v3 cmd restores prior set value same_name_multi_assign_restores_prior_value.yaml Covered
Same-name dedup interleaved with distinct name same_name_multi_assign_restores_prior_value.yaml Covered
Same-name dedup when var was never set before same_name_multi_assign_restores_prior_value.yaml Covered
Distinct-name multi-assign (existing) multi_assign_all_restored.yaml Covered (no regression)

P0_P1_P2_COUNT=0 for this iteration.

@AlexandreYang

Copy link
Copy Markdown
Member Author

Iteration 2 self-review: 0 P0 / 0 P1 / 0 P2 / 0 P3 — clean. P0_P1_P2_COUNT=0. See #262 (review).

@AlexandreYang

Copy link
Copy Markdown
Member Author

@codex review this PR

Please use the following severity format for all findings:

  • P0 (red): Exploitable vulnerability with high impact (RCE, sandbox bypass, data breach). Blocking merge.
  • P1 (orange): Likely exploitable or high-risk — correctness bugs vs bash, data races, panics.
  • P2 (yellow): Potential vulnerability, bash divergence, missing test coverage, missing docs.
  • P3 (blue): Style, minor simplification, hardening suggestion, nice-to-have test.

Prefix each finding title with its priority label, e.g. "P0: ...", "P1: ...", etc.
Include a summary table at the top with columns: # | Priority | File | Finding.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@AlexandreYang AlexandreYang left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review (iteration 3)

No new commits since iteration 2 (HEAD still at 3969b86b). Re-ran the same review checks: security (no fs/exec/import deltas), bash compatibility (verified against debian:bookworm-slim), correctness (dedup logic preserves the existing cd PWD/OLDPWD restore filter), coverage (4 scenario cases for the same-name path).

No findings.

P0_P1_P2_COUNT=0 for this iteration.

@AlexandreYang

Copy link
Copy Markdown
Member Author

Iteration 3 self-review: 0 P0 / 0 P1 / 0 P2 / 0 P3 — clean (no new commits since iter 2). P0_P1_P2_COUNT=0.

@AlexandreYang

Copy link
Copy Markdown
Member Author

@codex review this PR

Please use the following severity format for all findings:

  • P0 (red): Exploitable vulnerability with high impact (RCE, sandbox bypass, data breach). Blocking merge.
  • P1 (orange): Likely exploitable or high-risk — correctness bugs vs bash, data races, panics.
  • P2 (yellow): Potential vulnerability, bash divergence, missing test coverage, missing docs.
  • P3 (blue): Style, minor simplification, hardening suggestion, nice-to-have test.

Prefix each finding title with its priority label, e.g. "P0: ...", "P1: ...", etc.
Include a summary table at the top with columns: # | Priority | File | Finding.

@AlexandreYang AlexandreYang left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review (iteration 4)

No new commits since iteration 3 (HEAD still at 3969b86b). No findings.

P0_P1_P2_COUNT=0.

@AlexandreYang

Copy link
Copy Markdown
Member Author

Iteration 4 self-review: 0 P0 / 0 P1 / 0 P2 / 0 P3 — clean (no new commits since iter 3). P0_P1_P2_COUNT=0.

@AlexandreYang

Copy link
Copy Markdown
Member Author

@codex review this PR

Please use the following severity format for all findings:

  • P0 (red): Exploitable vulnerability with high impact (RCE, sandbox bypass, data breach). Blocking merge.
  • P1 (orange): Likely exploitable or high-risk — correctness bugs vs bash, data races, panics.
  • P2 (yellow): Potential vulnerability, bash divergence, missing test coverage, missing docs.
  • P3 (blue): Style, minor simplification, hardening suggestion, nice-to-have test.

Prefix each finding title with its priority label, e.g. "P0: ...", "P1: ...", etc.
Include a summary table at the top with columns: # | Priority | File | Finding.

@AlexandreYang AlexandreYang left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review (iteration 5)

No new commits since iteration 4 (HEAD still at 3969b86b). No findings.

P0_P1_P2_COUNT=0.

@AlexandreYang

Copy link
Copy Markdown
Member Author

Iteration 5 self-review: 0 P0 / 0 P1 / 0 P2 / 0 P3 — clean (no new commits since iter 4). P0_P1_P2_COUNT=0.

@AlexandreYang

Copy link
Copy Markdown
Member Author

Review-Fix Loop Summary

Iteration log

# Unresolved threads P0/P1/P2 findings Fixes applied CI status
1 1 (codex P3 YAML style) 0 3 (interleaved + never-set test cases, |+ block scalar for expect.stderr) Passing
2 0 0 Passing
3 0 0 Passing
4 0 0 Passing
5 0 0 Passing

Final state

  • Unresolved threads: 0
  • P0/P1/P2 findings: 0
  • CI: Passing (39/40 green, 1 still IN_PROGRESS at summary time; no failures)
  • PR description: includes a clickable link to the vuln-hunt finding doc

Remaining issues

None.

@AlexandreYang
AlexandreYang marked this pull request as ready for review May 19, 2026 12:37
Comment thread interp/runner_exec.go
vr expand.Variable
}
var restores []restoreVar
seenRestore := map[string]bool{}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this could be a set. I don't think Go has one in the std impl thouhg?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, there is no set in Go 😭

@AlexandreYang
AlexandreYang added this pull request to the merge queue May 19, 2026
Merged via the queue into main with commit 2904b38 May 19, 2026
40 checks passed
@AlexandreYang
AlexandreYang deleted the alex/fix-inline_var branch May 19, 2026 13:37
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.

2 participants