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
29 changes: 28 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ jobs:
- name: shellcheck
uses: docker://koalaman/shellcheck@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d # v0.11.0
with:
args: -x scripts/lib.sh scripts/resolve.sh scripts/install.sh scripts/verify-checksum.sh scripts/check.sh test/checksum-test.sh
args: >-
-x scripts/lib.sh scripts/resolve.sh scripts/install.sh scripts/verify-checksum.sh scripts/check.sh
test/checksum-test.sh test/harness.sh test/lib-test.sh test/resolve-test.sh
test/verify-checksum-test.sh test/install-test.sh test/check-test.sh test/fixtures/fake-cli/vericto

- name: Every action is pinned by full commit SHA with a version comment
shell: bash
Expand All @@ -47,6 +50,30 @@ jobs:
exit 1
fi

unit:
name: Unit tests, offline (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
# GNU (Linux) and BSD (macOS) userlands: tar, sed, mktemp, sha256sum/shasum.
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# scripts/*.sh against local fixtures and a stand-in CLI: no network.
- name: test/*-test.sh
shell: bash
run: |
set -euo pipefail
for t in lib resolve verify-checksum install check; do
echo "::group::test/$t-test.sh"
bash "test/$t-test.sh"
echo "::endgroup::"
done

checksum:
name: Checksum gate (tampered checksum and archive must fail)
runs-on: ubuntu-latest
Expand Down
30 changes: 29 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ and this project adheres to

## [Unreleased]

## [1.0.2] - 2026-10-18

### Fixed
- Multi-line inputs no longer pass validation. The validators matched with
`grep`, which works line by line, so a `cli-version`, `dialect`, `timeout`,
`api-url`, `workspace` or `since` with a line break passed as long as one of
its lines was valid; `check.sh` then handed the CLI one argument with a
newline in it (not a shell injection, but validation was defeated), and
`resolve.sh` stopped with exit 1 at `set_output` instead of the input error.
A value containing a line feed or a carriage return, including a trailing
one, is now an input error (exit 2) with the input's usual message. All
validators share one whole-value helper, `matches` in `scripts/lib.sh`
(bash `=~`, bash 3.2 and 5.x). If a workflow passes one of these inputs as a
YAML block scalar (`dialect: |`), which adds a trailing newline, write it on
one line instead.
- `check.sh` no longer aborts with "VA_FILES: unbound variable" (exit 1) on
bash 4+ when `VA_FILES` is unset. Only direct runs of the script were
affected; `action.yml` always sets it.

### Added
- Offline unit tests for every script (`test/lib-test.sh`,
`test/resolve-test.sh`, `test/verify-checksum-test.sh`,
`test/install-test.sh`, `test/check-test.sh`), with a stand-in CLI and a
`file://` release fixture, run in CI on Linux and macOS and covered by
shellcheck. Line coverage of `scripts/` measured with kcov: 21% to 97%. No
behaviour change.

## [1.0.1] - 2026-10-12

### Changed
Expand All @@ -33,6 +60,7 @@ and this project adheres to
- SARIF upload to Code Scanning, with paths made relative to the repository
root, and a job summary.

[Unreleased]: https://github.com/vericto/check-sql/compare/v1.0.1...HEAD
[Unreleased]: https://github.com/vericto/check-sql/compare/v1.0.2...HEAD
[1.0.2]: https://github.com/vericto/check-sql/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/vericto/check-sql/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/vericto/check-sql/releases/tag/v1.0.0
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,22 @@ Releases follow [Semantic Versioning](https://semver.org). The major tag
Actions; `@v1.0.0` or a commit SHA stays fixed. Each action release pins a
CLI version, and a CLI bump ships as a new action release.

## Tests

The scripts in `scripts/` have offline unit tests (bash 3.2+, `git` and `jq`),
run in CI on Linux and macOS:

```bash
for t in lib resolve verify-checksum install check; do bash "test/$t-test.sh"; done
```

They use local fixtures only: `test/install-test.sh` serves the release
download from `file://` through a `curl` stand-in, and `test/check-test.sh`
runs `check.sh` against a stand-in CLI (`test/fixtures/fake-cli/vericto`). A
case marked `xfail` is a known bug, kept visible until it is fixed.
`test/checksum-test.sh VERSION` checks the same gate against the real release
asset and needs the network.

## License

[Elastic License 2.0](LICENSE) (SPDX `Elastic-2.0`), the same license as the
Expand Down
12 changes: 6 additions & 6 deletions scripts/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ bool_input() { # NAME VALUE VAR: stores true/false in VAR
esac
}

matches() { printf '%s' "$2" | grep -Eq "$1"; }

: "${VA_BIN:?the install step did not provide a binary}"

# ── Validate inputs ──────────────────────────────────────────────────────────
Expand Down Expand Up @@ -131,7 +129,9 @@ if [ -n "$since" ] && ! matches '^[^-[:space:][:cntrl:]][^[:space:][:cntrl:]]*$'
fail 2 "input 'since' must be a git ref, e.g. origin/main (got '$since')"
fi
baseline=${VA_BASELINE:-}
case "$baseline" in *$'\n'* | *$'\r'*) fail 2 "input 'baseline' must be a single path" ;; esac
is_single_line "$baseline" || fail 2 "input 'baseline' must be a single path"
# Optional: action.yml always sets it, a direct run may not (set -u).
files=${VA_FILES:-}

# ── Credentials ──────────────────────────────────────────────────────────────
args=(check "--format=$format")
Expand Down Expand Up @@ -235,7 +235,7 @@ expand_files() { # OUT
local out=$1 pat m n f i
local -a pats found
shopt -s nullglob
read -r -d '' -a pats <<<"$VA_FILES" || true
read -r -d '' -a pats <<<"$files" || true
[ "${#pats[@]}" -gt 0 ] || fail 2 "input 'files' holds no patterns"
# From here on, expand globs without word splitting.
local IFS=''
Expand All @@ -260,7 +260,7 @@ expand_files() { # OUT
while [ "$i" -lt "${#found[@]}" ]; do
m=${found[$i]}
i=$((i + 1))
case "$m" in *$'\n'* | *$'\r'*) fail 2 "file names with line breaks are not supported" ;; esac
is_single_line "$m" || fail 2 "file names with line breaks are not supported"
m=${m#./}
if [ -d "$m" ]; then
while IFS= read -r f; do
Expand All @@ -277,7 +277,7 @@ expand_files() { # OUT
awk '!seen[$0]++' "$out.raw" >"$out"
}

if [ -n "${VA_FILES//[[:space:]]/}" ]; then
if [ -n "${files//[[:space:]]/}" ]; then
[ "$changed" = true ] && fail 2 "set either files or changed: true, not both"
[ -n "$since" ] && fail 2 "set either files or since, not both"
mode=files
Expand Down
29 changes: 23 additions & 6 deletions scripts/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,32 @@ $1
EOF
}

# is_single_line VALUE: true when VALUE holds no line feed and no carriage
# return.
is_single_line() {
case "$1" in
*$'\n'* | *$'\r'*) return 1 ;;
esac
return 0
}

# matches ERE VALUE: true when VALUE is a single line and matches the extended
# regex ERE as a whole (anchor ERE with ^...$). Every input validator uses
# this. grep would not do: it matches line by line, so a multi-line value would
# pass as long as one of its lines matched. Bash's =~ matches the whole string,
# in bash 3.2 (the macOS runners' bash) and 5.x alike.
matches() {
is_single_line "$2" || return 1
[[ $2 =~ $1 ]]
}

# set_output NAME VALUE. Values are single-line by construction (paths and
# numbers); a newline would be rejected rather than allowed to forge an output.
set_output() {
case "$2" in
*$'\n'* | *$'\r'*)
gh_error "internal: refusing a multi-line value for output '$1'"
return 1
;;
esac
if ! is_single_line "$2"; then
gh_error "internal: refusing a multi-line value for output '$1'"
return 1
fi
if [ -n "${GITHUB_OUTPUT:-}" ]; then
printf '%s=%s\n' "$1" "$2" >>"$GITHUB_OUTPUT"
else
Expand Down
2 changes: 1 addition & 1 deletion scripts/resolve.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ set -euo pipefail

version=${VA_CLI_VERSION:-}
version=${version#v}
if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$'; then
if ! matches '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$' "$version"; then
gh_error "cli-version must be an exact release such as 1.7.1 (got '${VA_CLI_VERSION:-}'). 'latest' is not accepted: pin a version."
exit 2
fi
Expand Down
2 changes: 1 addition & 1 deletion scripts/verify-checksum.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ name=${line#"${line%%[[:space:]]*}"}
name=${name#"${name%%[![:space:]]*}"}
name=${name#\*}

if ! printf '%s' "$expected" | grep -Eq '^[0-9a-f]{64}$'; then
if ! matches '^[0-9a-f]{64}$' "$expected"; then
gh_error "checksum: $sumfile does not hold a SHA-256 digest"
exit 1
fi
Expand Down
Loading
Loading