You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit aaea78d
Browse filesBrowse the repository at this point in the historyBrowse files
* Add literal twins of Bash loop values
Add BashParser.TryProjectLiteralTwins. For each occurrence whose
changeable argument words have a proved finite set of values, the
projection writes one literal twin for each combination of values. A
twin is the full source with each such word written as one literal
word. The parser parses it again, so the twin occurrence has the facts
of the typed literal: command words, path facts, and expansion facts.
An occurrence gets no twins when it is incomplete, when a verb word is
not static, when a word has no complete finite value set, when an
unquoted value can split or glob, when a redirect depends on a value,
or when the combinations pass the limit (32 for one occurrence, 128 in
total). An unquoted word uses its authored values only under
FreshNonInteractiveNoStartup, and only when each value is nonempty and
has no space, tab, newline, glob character, or backslash.
The rules use general shell facts only. A twin grants no authority.
Parse output does not change. Prepare 0.4.0-beta.22.
* Close review round 1 gaps in literal twins
Give no twins for text that the lexer does not model as Bash does:
- a source with a backslash-newline or a carriage return;
- an occurrence with `=~` or `:~` in a word;
- an occurrence in a source with `~` or `HOME` when no live launch
HOME proves the home directory;
- an unquoted value with a parenthesis, which an extglob pattern needs.
Count every twin parse, also a failed one, against one budget for a
call: 128 parses and 32,768 source characters. An occurrence whose
parses do not fit gets no twins and costs no parse. Run the cheap
checks for all occurrences first.
Add a Linux test that runs each source and each twin in GNU Bash and
compares the argv. Document the consumer warnings. Move the release
to 0.4.0-beta.23, after the beta.22 lexer fix.
* Parse literal twins as single commands and guard list tildes
Check `=~` and `:~` in the full source, as the backslash-newline and
carriage-return guards do. Bash expands a tilde in a loop list item
such as `for f in if=~/x`, and the parser keeps it as text.
Parse each twin text as one command, not as the full source. The twin
text is the authored text from the first to the last word of the
command. The parser parses it in the exact directory of the command,
with the launch facts that are live there. A twin parse then costs
about its own length. Compute the length of every twin text before any
build, and check the budget first: 128 twin parses and 2,048 + 8 x
(source length) characters. The projection now costs about one Parse.
On an 80 KB source with 8,000 commands it takes about as long as Parse
(before: 23 s and 80 GB).
A command gets no twins when its directory is not exact, when it has an
assignment prefix, or when it has a heredoc. BashLiteralTwin gets a
WorkingDirectory property.
* Give no twins under an unknown state or an unproved home directory
Under BashInitialStateMode.Unknown, the parser can report an exact
value for a word that Bash expands: `-o"$n"` gives `-o$n`, but Bash
passes `-oa`. The projection now gives no twins in that mode. Parse is
not changed here.
A cd without an operand goes to $HOME. Without a live launch HOME, the
parser takes that directory from HomeDirectory, but Bash uses its real
HOME. The projection now parses the source once more with another
HomeDirectory. A command whose exact directory changes gets no twins.
The check does not read the cd forms, so it also covers `cd --`,
`builtin cd`, `command cd`, and `if cd; then`.
Add the inputs as tests and to the real-Bash comparison, which now
also compares the working directory. Fix the stale parser comment.
* Prepare 0.4.0-beta.23 on top of the beta.22 lexer fix
Add the 0.4.0-beta.23 release notes above 0.4.0-beta.22, and set the
README version line to 0.4.0-beta.23. Move the beta.22 plan section to
Completed.
Since 0.4.0-beta.22, Parse gives Unknown or the Bash value for a loop
list item with `=~` or `:~`, never the text. Update the real-Bash test
to that rule. Keep the line-text guards as defense in depth and say so
in the code, the SPEC, and the consumer guide. Add a carriage return in
single quotes as a guard test, because a carriage return outside quotes
is now unparseable.
Copy file name to clipboardExpand all lines: IMPLEMENTATION_PLAN.md
+13-3Lines changed: 13 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,15 +10,25 @@ priorities.
10
10
11
11
---
12
12
13
-
## NOW (0.4.0-beta.22 Bash line continuations, security)
13
+
## NOW (0.4.0-beta.23 literal twins of loop values)
14
+
15
+
-[x] Add `BashParser.TryProjectLiteralTwins`. Write each changeable argument word as one proved literal value.
16
+
-[x] Give no twins for an incomplete occurrence, a verb word that is not static, a word without a complete finite value set, an unquoted value that can split or glob, a redirect that depends on a value, and combinations over the limit. Kill a hand mutant of each gate with a test.
17
+
-[x] Review round 1: guard a backslash-newline, a carriage return, `=~` and `:~`, an unproved home directory, and `extglob` patterns. Count every twin parse against one budget of parses and source characters. Compare twins with GNU Bash argv on Linux.
18
+
-[x] Review round 2: check `=~` and `:~` in the full source. Parse each twin as one command in its exact directory, and check the budget before a twin is built.
19
+
-[x] Verification round: give no twins under the Unknown initial state, and for a directory that comes from an unproved `HOME`.
20
+
-[x] Rebase on 0.4.0-beta.22 (#243). Keep the line-text guards. Run the twin tests, the real-Bash comparison, and the twin probe again.
21
+
-[ ] Merge the PR after Linux and Windows CI. Tag `0.4.0-beta.23` and verify NuGet publication.
22
+
23
+
## Completed (0.4.0-beta.22 Bash line continuations, security)
14
24
15
25
-[x] Remove a Bash line continuation before the lexer reads the next character of an expansion or an operator (#243). `echo "$\⏎(touch /tmp/x)"` gives the `touch` occurrence. Fail closed on a split `$((` marker.
16
26
-[x] Fail closed on a CR in Bash code and on `\` + CR (owner decision). Comments end only at LF.
17
27
-[x] Take a Bash `--name=value` value from the decoded word (owner decision).
18
28
-[x] Review fixes: `#` after a word part is word text; reserved words, `((`, and `;;` read across a continuation; a `~` after `=` or `:` of an assignment-shaped word expands from HOME.
19
29
-[x] Pin each case against real Bash. Prove the general rule: a continuation at any point gives the same facts or fails closed. Run manual mutants and an output comparison against beta.21.
20
-
-[] Merge the PR after Linux and Windows CI. Tag `0.4.0-beta.22` and verify NuGet publication.
21
-
-[] Move the literal twins of #242 to 0.4.0-beta.23.
30
+
-[x] Merge the PR after Linux and Windows CI. Tag `0.4.0-beta.22`.
31
+
-[x] Move the literal twins of #242 to 0.4.0-beta.23.
22
32
23
33
## Completed (0.4.0-beta.21 restore the beta.19 verb-slot rule)
Copy file name to clipboardExpand all lines: RELEASE_NOTES.md
+26Lines changed: 26 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,3 +1,29 @@
1
+
#### 0.4.0-beta.23 2026-10-06 ####
2
+
3
+
This prerelease adds literal twins of Bash commands whose argument words have a proved finite set of values.
4
+
5
+
## Added
6
+
7
+
-`BashParser.TryProjectLiteralTwins` and the records `BashLiteralTwinProjection`, `BashLiteralTwinCommand`, `BashLiteralTwin`, and `BashLiteralTwinWord`.
8
+
- A twin writes each changeable argument word of one command (an expansion or a tilde word) as one literal value. The parser parses the text of that one command in the exact directory of the command, with the launch facts that are live there. The twin occurrence has the normal facts of the typed literal: command words, path facts, and expansion facts. `BashLiteralTwin.WorkingDirectory` names the directory.
9
+
- The projection gives one twin for each combination of values, up to 32 for one command.
10
+
- Example: `for n in 8250 8244; do gh api repos/o/r/issues/$n; done` gives the twins `gh api repos/o/r/issues/8250` and `gh api repos/o/r/issues/8244`. `for f in a.txt b.txt; do cat "$f"; done` gives twins with the command words `cat`.
11
+
12
+
## Security and compatibility
13
+
14
+
- A command gets no twins when it is incomplete, when its directory is not exact, when it has an assignment prefix, when a verb word is not static, when a word has no complete finite value set, when an unquoted value can split or glob, when a redirect depends on a value, when it has a heredoc, or when its twins do not fit in the budget. The caller keeps its current behavior for such a command.
15
+
- An unquoted word uses its authored values only under `FreshNonInteractiveNoStartup`, and only when each value is nonempty and has no space, tab, newline, `*`, `?`, `[`, parenthesis, or backslash. The parenthesis rule covers a Bash build with `extglob` on by default.
16
+
- A source with a backslash-newline, a carriage return, `=~`, or `:~` anywhere gets no twins. The lexer does not model this text as Bash does. The `=~` and `:~` rule covers a loop list item such as `for f in if=~/x`, and it also stops `[[ $x =~ re ]]`.
17
+
- When the source has `~` or `HOME`, a command gets twins only with a live launch `HOME`. Without it, the home directory is the caller assumption `HomeDirectory`.
18
+
- A command whose exact directory comes from `HOME` (after `cd`, `cd --`, `builtin cd`, or `if cd; then`) gets twins only with a live launch `HOME`. The projection finds such a command with one more parse that uses another `HomeDirectory`.
19
+
- The projection gives no twins under `BashInitialStateMode.Unknown`. In that mode the parser can report `-o"$n"` as the exact value `-o$n`.
20
+
- Budget: one call parses at most 128 twins and at most 2,048 + 8 x (source length) characters of twin text, plus at most one more parse of the source for the `HOME` check. The projection checks the budget before it builds a twin, so its cost stays a small constant factor of one `Parse`. On an 80 KB source with 8,000 commands, the projection takes about as long as `Parse`.
21
+
- Values of different words combine independently. The twin set can hold combinations that never run, so a check of every twin is stricter, not weaker.
22
+
- A Linux test runs each source and each twin in GNU Bash and compares the argv and the working directory.
23
+
- With the 0.4.0-beta.22 lexer, a backslash-newline, a carriage return, and an assignment-shaped `=~` or `:~` give correct `Parse` facts or fail closed. The projection keeps its guards for this text as defense in depth. Twins of a source without such text do not change.
24
+
- A twin grants no authority. Existing facts do not change: `Parse` gives the same output as 0.4.0-beta.22.
25
+
- Public API: additive.
26
+
1
27
#### 0.4.0-beta.22 2026-10-06 ####
2
28
3
29
This prerelease fixes security bugs. A Bash line continuation, a carriage return, or a `#` after a quoted part could hide a command from `Parse` (#243). In the examples, `⏎` is a real newline and CR is a carriage return.
0 commit comments