Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions examples/executor-tools/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# executor-tools-example

## 1.0.6

### Patch Changes

- Updated dependencies [[`7a5a0b9`](https://github.com/vercel-labs/just-bash/commit/7a5a0b9ae3bf0524722653cbf4b45e6bc176cf22), [`af2e0f4`](https://github.com/vercel-labs/just-bash/commit/af2e0f4cdeb5417ea59e25140038c239dd8fd92d)]:
- just-bash@3.1.0
- @just-bash/executor@2.0.0

## 1.0.5

### Patch Changes

- Updated dependencies [[`aec5643`](https://github.com/vercel-labs/just-bash/commit/aec56431d7d9b6fcb141bbfe25d26f4931f54f80), [`1ec5eec`](https://github.com/vercel-labs/just-bash/commit/1ec5eec0aefd099d23ac9f056df1e6612c81d49b), [`cb2b583`](https://github.com/vercel-labs/just-bash/commit/cb2b583b3f46e6bb4e6982c4bfe19903ec811a87)]:
- just-bash@3.0.3
- @just-bash/executor@1.0.4

## 1.0.4

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion examples/executor-tools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "executor-tools-example",
"version": "1.0.4",
"version": "1.0.6",
"description": "Example of @just-bash/executor — inline tools + GraphQL/OpenAPI/MCP discovery",
"type": "module",
"scripts": {
Expand Down
14 changes: 14 additions & 0 deletions packages/just-bash-executor/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# @just-bash/executor

## 2.0.0

### Patch Changes

- Updated dependencies [[`7a5a0b9`](https://github.com/vercel-labs/just-bash/commit/7a5a0b9ae3bf0524722653cbf4b45e6bc176cf22), [`af2e0f4`](https://github.com/vercel-labs/just-bash/commit/af2e0f4cdeb5417ea59e25140038c239dd8fd92d)]:
- just-bash@3.1.0

## 1.0.4

### Patch Changes

- Updated dependencies [[`aec5643`](https://github.com/vercel-labs/just-bash/commit/aec56431d7d9b6fcb141bbfe25d26f4931f54f80), [`1ec5eec`](https://github.com/vercel-labs/just-bash/commit/1ec5eec0aefd099d23ac9f056df1e6612c81d49b), [`cb2b583`](https://github.com/vercel-labs/just-bash/commit/cb2b583b3f46e6bb4e6982c4bfe19903ec811a87)]:
- just-bash@3.0.3

## 1.0.3

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/just-bash-executor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@just-bash/executor",
"version": "1.0.3",
"version": "2.0.0",
"description": "Experimental tool-invocation companion for just-bash. Wires @executor-js/sdk into js-exec via the invokeTool hook.",
"repository": {
"type": "git",
Expand Down
86 changes: 86 additions & 0 deletions packages/just-bash/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,91 @@
# just-bash

## 3.1.0

### Minor Changes

- [#284](https://github.com/vercel-labs/just-bash/pull/284) [`af2e0f4`](https://github.com/vercel-labs/just-bash/commit/af2e0f4cdeb5417ea59e25140038c239dd8fd92d) Thanks [@arimxyer](https://github.com/arimxyer)! - sandbox: forward capability flags from `SandboxOptions` into the underlying `Bash`

`Sandbox.create(opts)` previously constructed its internal `Bash` with only a subset
of `BashOptions`, silently dropping the optional capability flags (`python`,
`javascript`, `commands`, `customCommands`, `fetch`). A host that drives just-bash
through the `Sandbox` API (rather than `new Bash(...)`) therefore could not enable
python3, js-exec, a restricted command set, custom commands, or a custom fetch — even
though the runtimes ship in the package.

`SandboxOptions` now exposes those fields and `Sandbox.create` forwards them into the
`Bash` it builds. Behavior is unchanged when a caller omits them (each falls back to
its existing `BashOptions` default — Python/js-exec stay off, the full command set
stays available). Fixes the root cause behind vercel/eve#431.

### Patch Changes

- [#268](https://github.com/vercel-labs/just-bash/pull/268) [`7a5a0b9`](https://github.com/vercel-labs/just-bash/commit/7a5a0b9ae3bf0524722653cbf4b45e6bc176cf22) Thanks [@trieloff](https://github.com/trieloff)! - jq: allow nested double-quoted strings inside `"\(...)"` string interpolation

jq string interpolation of the form `"\(...)"` that contained a nested double-quoted string — for example `"\(sub("T.*";""))"` or `"\(ltrimstr("ab"))"` — previously failed with a parse error. The tokenizer terminated the outer string at the first `"` it saw inside the interpolation expression, so the rest of the expression became orphaned tokens.

The lexer now tracks `\(...)` depth while consuming a string literal and treats nested `"..."` pairs as opaque content while inside an interpolation, restoring them verbatim into the captured interpolation source. `parseStringInterpolation` similarly skips over nested strings when balancing parentheses, so the interpolation expression is captured as a whole and handed to the expression parser intact.

## 3.0.3

### Patch Changes

- [#277](https://github.com/vercel-labs/just-bash/pull/277) [`aec5643`](https://github.com/vercel-labs/just-bash/commit/aec56431d7d9b6fcb141bbfe25d26f4931f54f80) Thanks [@mutewinter](https://github.com/mutewinter)! - interpreter: avoid lazy import in variable assignment path that trips defense-in-depth (fixes [#273](https://github.com/vercel-labs/just-bash/issues/273))

Any non-`export` variable assignment (bare `SECRET=s`, prefixed `SECRET=s cmd`,
or before a custom command) failed with a defense-in-depth security violation
(`dynamic import of Node.js builtin 'node:module' is blocked during script
execution`), while plain commands and `export`-ed assignments passed.

`processScalarAssignment()` resolved `isArray` via `await import("./expansion.js")`
in two spots. In the bundled `dist`, that dynamic `import()` marks `expansion.js`
as a lazily-linked chunk whose `createRequire` banner imports `node:module`; the
defense layer's ESM `resolve` hook blocks that builtin import when the sandbox is
active and untrusted, so it blocked just-bash's own chunk load. The file already
statically imports from `./expansion.js`, so `isArray` is now pulled from that
static import and the two lazy imports are removed — no lazy `node:module`-bearing
chunk is linked at runtime. No public API change.

- [#276](https://github.com/vercel-labs/just-bash/pull/276) [`1ec5eec`](https://github.com/vercel-labs/just-bash/commit/1ec5eec0aefd099d23ac9f056df1e6612c81d49b) Thanks [@mutewinter](https://github.com/mutewinter)! - interpreter: preserve leading whitespace in multi-line quoted strings (fixes [#259](https://github.com/vercel-labs/just-bash/issues/259))

`exec()` runs each script through `normalizeScript()`, which `trimStart()`s
leading indentation from lines so indented template-literal scripts parse. It
was applied line-by-line and stripped the leading whitespace inside multi-line
single- and double-quoted strings too. The visible symptom was `python3 -c
'...'` (and `node -e`, `awk`, etc.) with an indented body failing with
`IndentationError`, while the same code via heredoc or pipe worked.

`normalizeScript()` is now quote-aware (mirroring the earlier heredoc-aware
fix): it only strips indentation from lines that begin outside any quote, and
preserves lines that begin inside an unterminated single- or double-quoted
string verbatim. This also un-skips four sed spec tests whose indented stdin
was previously being corrupted.

- [#286](https://github.com/vercel-labs/just-bash/pull/286) [`cb2b583`](https://github.com/vercel-labs/just-bash/commit/cb2b583b3f46e6bb4e6982c4bfe19903ec811a87) Thanks [@privatenumber](https://github.com/privatenumber)! - interpreter: deliver redirected output to each fd's final target (fixes `cmd > file 2>&1` leaking stderr to stdout)

`applyRedirections()` processed a command's redirection list sequentially over
the result's stdout/stderr strings, moving content at each step. The
duplication operators (`2>&1`, `1>&2`) merged into the live stream regardless
of where the source fd pointed, so the canonical `cmd > file 2>&1` wrote
stdout to the file but leaked stderr onto the caller's stdout — including
"command not found" errors and custom-command stderr. Any wrapper protocol
that parses the enclosing script's stdout (e.g. a runner emitting a JSON
payload after `eval "$CMD" > "$OUT" 2>&1`) saw the leaked stderr corrupt its
stream. Ordering variants were wrong in other ways: `cmd 2>&1 > file` put
stderr in the file instead of on stdout, and `cmd > a > b` wrote content to
`a` instead of `b`.

The pass now mirrors how bash sets up fds before running the command: each
output redirection only opens/truncates its target and re-points the fd's
sink (file, /dev/null, or a snapshot of the caller-visible stream), and
duplication operators copy the source fd's current sink. Stream content is
delivered once, after the whole list is processed, to each fd's final sink.
This makes `cmd > file 2>&1` send stderr to the file, `cmd 2>&1 > file` keep
stderr on the caller's stdout, `cmd > all 2>&1 2> err` let the later `2> err`
reclaim stderr, and `cmd > a > b` truncate `a` while writing content to `b`.
The `/dev/null`-as-regular-VFS-file behavior for stdout redirects is
preserved.

## 3.0.2

### Patch Changes
Expand Down
68 changes: 64 additions & 4 deletions packages/just-bash/dist/Bash.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,51 @@ export class Bash {
}
}
/**
* Normalize a script by stripping leading whitespace from lines,
* while preserving whitespace inside heredoc content.
* Track open single/double-quote state across one physical line, starting from
* `start` (the state carried over from the previous line). Used by
* normalizeScript to know whether a line begins inside a multi-line quoted
* string, where leading whitespace is literal and must not be trimmed.
*
* Only single and double quotes matter: those are the contexts where leading
* whitespace is significant. Backslash escapes and `#` comments are honored so
* a quote inside a comment (e.g. `# don't`) doesn't desync the state.
*/
function scanLineQuoteState(line, start) {
let state = start;
for (let i = 0; i < line.length; i++) {
const ch = line[i];
if (state === "single") {
// Inside single quotes only a closing quote is special (no escapes).
if (ch === "'")
state = "none";
}
else if (state === "double") {
if (ch === "\\") {
i++; // backslash escapes the next char inside double quotes
}
else if (ch === '"') {
state = "none";
}
}
else if (ch === "'") {
state = "single";
}
else if (ch === '"') {
state = "double";
}
else if (ch === "\\") {
i++; // backslash escapes the next char (e.g. \" or \')
}
else if (ch === "#" && (i === 0 || /\s/.test(line[i - 1]))) {
break; // start of a comment: the rest of the line is not shell-significant
}
}
return state;
}
/**
* Normalize a script by stripping leading whitespace from lines, while
* preserving whitespace inside heredoc content and inside multi-line quoted
* strings.
*
* This allows writing indented bash scripts in template literals:
* ```
Expand All @@ -561,13 +604,21 @@ export class Bash {
* `);
* ```
*
* Leading whitespace is only stripped from lines that begin outside any quote.
* A line that begins inside an unterminated single- or double-quoted string is
* literal quoted content (e.g. the body of `python3 -c "..."`), so it is kept
* verbatim.
*
* Heredocs are detected by looking for << or <<- operators and their delimiters.
*/
function normalizeScript(script) {
const lines = script.split("\n");
const result = [];
// Stack of pending heredoc delimiters (for nested heredocs)
const pendingDelimiters = [];
// Open single/double-quote state carried across lines. When a line begins
// inside an open quote, its leading whitespace is literal and preserved.
let quoteState = "none";
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// If we're inside a heredoc, check if this line ends it
Expand All @@ -586,9 +637,18 @@ function normalizeScript(script) {
result.push(line);
continue;
}
// Not inside a heredoc - normalize the line and check for heredoc starts
const normalizedLine = line.trimStart();
// Not inside a heredoc. Lines that begin inside an open quote are literal
// quoted content (leading whitespace is significant), so preserve them
// verbatim; otherwise strip leading indentation.
const startState = quoteState;
const normalizedLine = startState === "none" ? line.trimStart() : line;
result.push(normalizedLine);
quoteState = scanLineQuoteState(line, startState);
// Only detect heredoc operators on lines that begin outside any quote;
// a `<<WORD` inside quoted content is not a heredoc.
if (startState !== "none") {
continue;
}
// Check for heredoc operators in this line
// Match: <<DELIM, <<-DELIM, << 'DELIM', <<- "DELIM", etc.
// Multiple heredocs on one line are possible: cmd <<EOF1 <<EOF2
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading