Skip to content
Merged
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
59 changes: 54 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.2.0] - 2026-07-26

This is a large hardening pass focused on security, robustness, and testing.

### Security
Expand Down Expand Up @@ -33,7 +35,17 @@ This is a large hardening pass focused on security, robustness, and testing.
`--config` file (mode 0600, removed after use) instead of a `--header`
argument, and it is no longer logged at debug level.
- ell refuses to send credentials to a plaintext `http://` URL (loopback
excepted); override with `ELL_ALLOW_INSECURE_URL=true`.
excepted); override with `ELL_ALLOW_INSECURE_URL=true`. Bracketed IPv6
loopback (`http://[::1]`, with or without a port) is correctly recognised as
loopback and allowed.
- When `ELL_API_KEY` is empty the backends now send **no** auth header at all,
instead of an empty `Authorization: Bearer ` / `x-goog-api-key:`. This matches
the preflight (which permits running with no key) and avoids the empty header
wrongly tripping the "refuse credential over `http://`" guard.
- The auth header written to curl's `--config` file now escapes backslashes as
well as double quotes, so a key/header containing a backslash is passed to
curl intact instead of being mangled (a lone `\t` etc. was interpreted by
curl's config parser).
- The bundled redaction plugin now ships **disabled by default** (its hook has a
`.disabled` suffix); enable it by removing the suffix.

Expand All @@ -54,15 +66,34 @@ This is a large hardening pass focused on security, robustness, and testing.
- Record mode re-execs ell by an absolute, shell-quoted path, so it works when
ell is run in place or is not on `PATH`.
- Template resolution rejects path-traversal names and accepts an
`ELL_TEMPLATE_PATH` with or without a trailing slash; plugin-hook discovery no
longer breaks on paths containing spaces/newlines.
`ELL_TEMPLATE_PATH` with or without a trailing slash. Plugin hooks in paths
containing spaces/newlines are handled end to end: both discovered
(`list_plugin_hooks`) and executed -- `piping` now shell-quotes a hook stage
whose path contains spaces instead of word-splitting it into a "No such file"
failure.
- Missing `ELL_API_URL` now fails early with a clear, actionable message
(EX_CONFIG) instead of an opaque curl error; a missing `ELL_API_KEY` warns.
- curl failures are reported with a human-readable explanation (DNS, connection
refused, timeout, TLS, …) rather than a bare exit code.
refused, timeout, TLS, …) rather than a bare exit code. The non-streaming path
now propagates curl's real exit code on a transport failure (as the streaming
path and `docs/Backends.md` already promised) instead of collapsing it to `1`.
- The interactive prompt is printed with `printf` instead of `echo -ne`, whose
`-n`/`-e` handling is unreliable across shells (`ELL_PS1` already holds real
escape bytes, matching how `ELL_PS2` is printed).
- Functions are exported with `export -f` rather than as empty variables.
- Bash 4.1 compatibility is restored (the version gate, docs and CI now
consistently target 4.1).
consistently target 4.1). Plugin-hook discovery uses a here-string rather than
process substitution (`< <(…)`), which is a parse error under `set -o posix`
on bash 4.1; and the backend dispatcher resolves its directory correctly when
sourced directly (with `BASE_DIR` unset), not only when launched via ell.sh.
- BSD/macOS toolchain fixes: the syntax-highlight plugin no longer spins into an
infinite loop under BSD `awk` (it used `awk -F ''`, a GNU-only extension) and
now uses bash builtins; the redaction plugin's word boundaries work under BSD
`sed` (which lacks `\b`); and record mode uses the BSD `script(1)` command
syntax on macOS (`script … file command`) rather than the GNU `-c` form.
- Windows (Git Bash / MSYS) fix: a `file://` URL built from a POSIX path is
rewritten to the `file:///C:/…` form the native curl understands, so the
bundled `file://` backend and the offline tests work there.

### Changed

Expand All @@ -85,6 +116,9 @@ This is a large hardening pass focused on security, robustness, and testing.
re-parsing the whole accumulated buffer per line (was O(n²)).
- Streaming loops use bash builtins instead of a `grep`/`cut`/`tr` subprocess
per line, and logging no longer forks `date`/`basename` per line.
- The syntax-highlight and paginator plugins' per-character loops likewise use
bash pattern matching / parameter expansion instead of forking `echo | grep`,
`echo | cut` or `awk` for each character.

### Added

Expand All @@ -95,7 +129,22 @@ This is a large hardening pass focused on security, robustness, and testing.
syntax-highlight and paginator plugins; and end-to-end tests for the request
pipeline, hook stages, interactive mode, output redirection, error paths, the
launcher, record mode, the bash version gate and terminal-size fallback.
- CI now also runs the suite on macOS (BSD toolchain) and on Windows under both
Git Bash and a fuller MSYS2 environment. Tests that depend on a facility a
platform lacks (POSIX permission bits, real symlinks, `script(1)`, an
isolable `bash`) detect that at runtime and `SKIP` instead of failing.
- The test suite no longer depends on `cmp(1)`/diffutils for byte-exact
comparisons (it uses a pure-bash comparison), so it runs on minimal
environments; and CI does not install diffutils, keeping that guarantee under
test.
- Documentation: a "Windows" section in `docs/Configuration.md` and a README
note spelling out Git Bash's limitations (the config-trust check and the
`0600` credential file are not enforceable there) and recommending WSL/MSYS2.

## [0.1.1]

- Baseline release prior to the hardening pass above.

[Unreleased]: https://github.com/simonmysun/ell/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/simonmysun/ell/compare/v0.1.1...v0.2.0
[0.1.1]: https://github.com/simonmysun/ell/releases/tag/v0.1.1
2 changes: 1 addition & 1 deletion ell.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ else
exit 69;
fi

ELL_VERSION="0.1.1";
ELL_VERSION="0.2.0";

: "${ELL_LOG_LEVEL:=2}";

Expand Down
2 changes: 1 addition & 1 deletion tests/bash_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ assert_failure "0 rejected" version_ok 0 0;
ver_out="$("${DIR}/../ell" --version 2>&1)";
ver_status="${?}";
assert_equals "ell --version exits 0 on supported bash" "0" "${ver_status}";
assert_contains "ell --version prints the version" "${ver_out}" "${ELL_VERSION:-0.1.1}";
assert_contains "ell --version prints the version" "${ver_out}" "${ELL_VERSION:-0.2.0}";
assert_not_contains "supported bash is not rejected" "${ver_out}" "is required to run";

# The rejection message (as written in ell.sh) names 4.1, matching README/CI.
Expand Down
Loading