security: add checksum verification and safe .env parser (v0.1.3)#27
Conversation
4c58a99 to
fad8bfb
Compare
canoo
left a comment
There was a problem hiding this comment.
Solid PR — both fixes address real security risks with clean implementations. One issue to fix before merge.
ollama-delegate.sh — The _load_env() parser is exactly what #7 asked for. Rejects command substitution, validates key format, strips quotes. Clean.
install.sh — Checksum verification logic is sound. The sha256sum/shasum fallback and the soft-fail when neither tool is available are reasonable choices. One bug to fix (see inline comment).
Design note: the soft-fail path (warn + continue when no hash tool is available) is a conscious tradeoff — don't break installs on minimal systems. That's fine for now.
📝 WalkthroughWalkthroughinstall.sh downloads Changes
Sequence Diagram(s)sequenceDiagram
participant Installer as Installer (install.sh)
participant Remote as Release Server (GitHub)
participant FS as Filesystem
participant Verifier as Hash Tool (sha256sum / shasum)
Installer->>Remote: GET nexus-${OS}-${ARCH}
Installer->>Remote: GET checksums.txt
Remote-->>Installer: binary + checksums.txt
Installer->>FS: save binary, save checksums.txt
Installer->>FS: extract expected hash for binary
Installer->>Verifier: run checksum verification
Verifier-->>Installer: OK / FAIL
alt OK
Installer->>FS: remove checksums.txt
Installer-->>Installer: print success (verified)
else FAIL
Installer->>FS: remove binary and checksums.txt
Installer-->>Installer: abort install (verification failed)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@install.sh`:
- Around line 92-94: The installer currently logs a warning when neither
sha256sum nor shasum is available (the else branch that calls warn "Neither
sha256sum nor shasum found — skipping checksum verification"); change this to
fail closed by emitting an explicit error and aborting the install (exit
non‑zero) so the script does not continue without checksum verification —
replace the warn call with a fatal/error path (use the existing die/error
function if present, otherwise call echo >&2 and exit 1) in the same conditional
that checks for sha256sum/shasum.
- Around line 73-76: When a checksum entry is missing or a verification fails,
the script currently leaves the downloaded binary on disk; update the failure
branches that check "if [ -z \"$expected\" ]" and the later checksum-failure
block to also remove the downloaded binary by adding a safe, quoted removal
(e.g. rm -f "$binary_file") alongside rm -f "$checksum_file"; use the existing
variables binary_name, checksum_file and binary_file and ensure all variable
expansions are quoted to avoid accidental word-splitting or globbing.
In `@tools/automation/ollama-delegate.sh`:
- Around line 45-48: The current logic unconditionally strips leading and
trailing quotes from the variable value (see the manipulations of value), which
can corrupt strings when quotes don't match; change it to only remove
surrounding quotes when both the first and last characters are matching quotes
(either both " or both '): detect the first and last char of value, and if they
are both double-quotes or both single-quotes, strip them; otherwise leave value
untouched; apply this check where value is currently being trimmed.
- Around line 36-38: Replace the stderr log that prints the raw rejected line
(the echo referencing $line) so secrets are not leaked: add and maintain a line
counter (e.g., lineno incremented each loop) and change the warning to only emit
metadata such as "WARN: Skipping unsafe .env line at line $lineno" (do not
include or expand $line), leaving the continue in place; ensure the read loop
uses safe read -r and the new warning only outputs the line number or other
non-sensitive context.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 433c5dc0-3533-4f4c-9d67-bcdb1eb3640b
📒 Files selected for processing (2)
install.shtools/automation/ollama-delegate.sh
- install.sh: anchor grep with ^binary_name to prevent substring matches - install.sh: remove dest binary on checksum-missing failure paths - ollama-delegate.sh: log line number instead of raw content on unsafe line - ollama-delegate.sh: guard quote stripping to matching surrounding pairs only
6d4a57e to
1c9b5b2
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tools/automation/ollama-delegate.sh`:
- Around line 47-48: The current quoted-value trimming uses a Bash 4.x
negative-length expansion (value="${value:1:-1}") which fails on macOS Bash 3.2;
change the logic inside the quoted branch that operates on the variable value to
first take the substring from index 1 onward and then remove the final character
(i.e., strip the leading quote, then strip the trailing quote) using POSIX/Bash
3.2-compatible parameter expansions (take value from offset 1, then remove its
last character via a trailing-pattern removal), so the quoted branch that checks
value with the regexes still ends up with the unquoted content but without using
negative-length expansions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e65723dd-9e26-4542-8abf-f037eda9b1b4
📒 Files selected for processing (2)
install.shtools/automation/ollama-delegate.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- install.sh
${value:1:-1} negative substring length is unsupported in Bash 3.2
(macOS default). Replace with case + # / % parameter expansions.
4188c93 to
bf568dd
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tools/automation/ollama-delegate.sh (1)
55-55: UseBASH_SOURCE[0]for stable.envpath resolution.
$(dirname "$0")can resolve unexpectedly depending on invocation style. Prefer resolving fromBASH_SOURCE[0]to reliably target repo-root.env.♻️ Suggested fix
-_load_env "$(dirname "$0")/../../.env" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +_load_env "${SCRIPT_DIR}/../../.env"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tools/automation/ollama-delegate.sh` at line 55, Replace the unstable "$(dirname "$0")/../../.env" invocation passed to _load_env with a path resolved from BASH_SOURCE[0]; specifically, update the _load_env call that currently uses "$0" so it uses "${BASH_SOURCE[0]}" (and keep proper quoting and dirname usage) to reliably resolve the repo-root .env. Ensure the change targets the existing _load_env invocation and preserves the surrounding dirname and ../../.env path traversal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tools/automation/ollama-delegate.sh`:
- Around line 43-52: The current loop that parses lines and exports variables
(matching the regex and using local key="${line%%=*}" and export "$key=$value")
should be restricted to an allowlist to prevent exporting dangerous variables
like PATH or IFS; modify the code so after extracting key and value you check
key against a predefined allowlist (e.g., an array or a regex of permitted
names) and only perform export "$key=$value" when the key is in that allowlist,
otherwise skip or log/ignore the entry—apply this change in the block that
handles the case stripping and before the export.
---
Nitpick comments:
In `@tools/automation/ollama-delegate.sh`:
- Line 55: Replace the unstable "$(dirname "$0")/../../.env" invocation passed
to _load_env with a path resolved from BASH_SOURCE[0]; specifically, update the
_load_env call that currently uses "$0" so it uses "${BASH_SOURCE[0]}" (and keep
proper quoting and dirname usage) to reliably resolve the repo-root .env. Ensure
the change targets the existing _load_env invocation and preserves the
surrounding dirname and ../../.env path traversal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 05d96903-71f0-4f11-bf07-819099e6f373
📒 Files selected for processing (1)
tools/automation/ollama-delegate.sh
| if [[ "$line" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then | ||
| local key="${line%%=*}" | ||
| local value="${line#*=}" | ||
| # Strip surrounding quotes only if they match as a pair (Bash 3.2 portable) | ||
| case "$value" in | ||
| \"*\") value="${value#\"}"; value="${value%\"}" ;; | ||
| \'*\') value="${value#\'}"; value="${value%\'}" ;; | ||
| esac | ||
| export "$key=$value" | ||
| fi |
There was a problem hiding this comment.
Restrict allowed .env keys before export to prevent environment poisoning.
Lines 43-52 currently export any syntactically valid key. That allows .env entries like PATH=... / IFS=... to influence later command execution and weaken the security objective.
🔒 Suggested fix (allowlist keys)
if [[ "$line" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then
local key="${line%%=*}"
local value="${line#*=}"
+ case "$key" in
+ OLLAMA_HOST_URL|NEXUS_* ) ;;
+ *)
+ echo "WARN: Ignoring unsupported .env key at line ${lineno}" >&2
+ continue
+ ;;
+ esac
# Strip surrounding quotes only if they match as a pair (Bash 3.2 portable)
case "$value" in
\"*\") value="${value#\"}"; value="${value%\"}" ;;
\'*\') value="${value#\'}"; value="${value%\'}" ;;
esac
export "$key=$value"
fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tools/automation/ollama-delegate.sh` around lines 43 - 52, The current loop
that parses lines and exports variables (matching the regex and using local
key="${line%%=*}" and export "$key=$value") should be restricted to an allowlist
to prevent exporting dangerous variables like PATH or IFS; modify the code so
after extracting key and value you check key against a predefined allowlist
(e.g., an array or a regex of permitted names) and only perform export
"$key=$value" when the key is in that allowlist, otherwise skip or log/ignore
the entry—apply this change in the block that handles the case stripping and
before the export.
canoo
left a comment
There was a problem hiding this comment.
All original review items addressed. The grep anchoring is fixed, binary cleanup on failure is solid, and the .env parser handles secrets and quotes correctly now.
One last suggestion inline for the soft-fail → hard-fail change. Apply it and this is ready to merge.
Nice work on the Bash 3.2 compatibility fix for quote stripping — that was a good catch.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
install.sh (1)
81-91:⚠️ Potential issue | 🔴 CriticalSame syntax and logic errors in shasum fallback block.
Lines 83-87 have the same issues as the sha256sum block above: duplicate
expected=assignment (lines 83/84), missingif [ -z "$expected" ]; then, and wrong grep pattern.🐛 Proposed fix
elif command -v shasum &>/dev/null; then local expected - expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}') - expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}') + expected=$(grep " ${binary_name}$" "$checksum_file" | awk '{print $1}') + if [ -z "$expected" ]; then rm -f "$dest" "$checksum_file" fail "Checksum entry for $binary_name not found in checksums.txt" fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@install.sh` around lines 81 - 91, In the shasum fallback block, remove the duplicated expected= assignment and replace the fragile grep+awk with a single, reliable lookup that finds the checksum by matching the filename column; for example set expected by running awk to check $2==binary_name and print $1 (e.g. expected=$(awk -v name="$binary_name" '$2==name {print $1; exit}' "$checksum_file")), then add the missing if [ -z "$expected" ]; then rm -f "$dest" "$checksum_file" && fail "Checksum entry for $binary_name not found in checksums.txt" fi before running shasum -a 256 --check --status so the script fails early when no entry is found and uses the correct checksum value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@install.sh`:
- Around line 69-80: The sha256sum verification block is broken: remove the
duplicate expected=$(grep ...) line, add a proper check for an empty expected
(if [ -z "$expected" ]; then ... fi) before using it, and fix the grep to match
filename at end of line (e.g. grep "<pattern matching filename at end>"
"$checksum_file" to extract the hash into the variable expected) so that when
expected is empty you run rm -f "$dest" "$checksum_file" and call fail "Checksum
entry for $binary_name not found in checksums.txt", otherwise echo "$expected
$dest" | sha256sum --check --status || { rm -f "$dest" "$checksum_file"; fail
"Checksum verification failed — binary may be corrupted or tampered with"; }
referencing the variables binary_name, checksum_file, expected, dest and the
sha256sum verification block.
- Around line 92-97: The else branch inside the checksum selection block should
not emit a misleading warn and must be closed: remove the redundant warn
"Neither sha256sum nor shasum found — skipping checksum verification" and keep
the fail call that aborts ("fail 'Neither sha256sum nor shasum found — cannot
verify binary integrity. Install coreutils and retry.'"), then add the missing
fi to close the if/elif/else block that begins around the checksum detection,
and ensure the standalone rm -f "$checksum_file" that follows is
positioned/executed only on the successful path after that fi (i.e., leave the
cleanup rm for the success flow, not inside the else).
---
Duplicate comments:
In `@install.sh`:
- Around line 81-91: In the shasum fallback block, remove the duplicated
expected= assignment and replace the fragile grep+awk with a single, reliable
lookup that finds the checksum by matching the filename column; for example set
expected by running awk to check $2==binary_name and print $1 (e.g.
expected=$(awk -v name="$binary_name" '$2==name {print $1; exit}'
"$checksum_file")), then add the missing if [ -z "$expected" ]; then rm -f
"$dest" "$checksum_file" && fail "Checksum entry for $binary_name not found in
checksums.txt" fi before running shasum -a 256 --check --status so the script
fails early when no entry is found and uses the correct checksum value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: be4b2a7b-a570-410b-afc7-56a55b6060ed
📒 Files selected for processing (1)
install.sh
| if command -v sha256sum &>/dev/null; then | ||
| # Extract expected hash for this binary and verify | ||
| local expected | ||
| expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}') | ||
| expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}') | ||
| rm -f "$dest" "$checksum_file" | ||
| fail "Checksum entry for $binary_name not found in checksums.txt" | ||
| fi | ||
| echo "$expected $dest" | sha256sum --check --status || { | ||
| rm -f "$dest" "$checksum_file" | ||
| fail "Checksum verification failed — binary may be corrupted or tampered with" | ||
| } |
There was a problem hiding this comment.
Critical syntax and logic errors in sha256sum verification block.
Multiple issues that will break the script:
- Duplicate line: Lines 72 and 73 are identical — the second
expected=assignment is redundant. - Missing conditional: Lines 74-76 have
rm -f,fail, andfibut no correspondingif [ -z "$expected" ]; thenstatement. This is a syntax error. - Wrong grep pattern: The pattern
"^${binary_name} "looks for the filename at the start of each line, but GoReleaser produces<hash> <filename>format (hash first, two spaces, then filename). The pattern should match the filename at the end of the line.
🐛 Proposed fix
if command -v sha256sum &>/dev/null; then
# Extract expected hash for this binary and verify
local expected
- expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}')
- expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}')
+ expected=$(grep " ${binary_name}$" "$checksum_file" | awk '{print $1}')
+ if [ -z "$expected" ]; then
rm -f "$dest" "$checksum_file"
fail "Checksum entry for $binary_name not found in checksums.txt"
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if command -v sha256sum &>/dev/null; then | |
| # Extract expected hash for this binary and verify | |
| local expected | |
| expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}') | |
| expected=$(grep "^${binary_name} " "$checksum_file" | awk '{print $1}') | |
| rm -f "$dest" "$checksum_file" | |
| fail "Checksum entry for $binary_name not found in checksums.txt" | |
| fi | |
| echo "$expected $dest" | sha256sum --check --status || { | |
| rm -f "$dest" "$checksum_file" | |
| fail "Checksum verification failed — binary may be corrupted or tampered with" | |
| } | |
| if command -v sha256sum &>/dev/null; then | |
| # Extract expected hash for this binary and verify | |
| local expected | |
| expected=$(grep " ${binary_name}$" "$checksum_file" | awk '{print $1}') | |
| if [ -z "$expected" ]; then | |
| rm -f "$dest" "$checksum_file" | |
| fail "Checksum entry for $binary_name not found in checksums.txt" | |
| fi | |
| echo "$expected $dest" | sha256sum --check --status || { | |
| rm -f "$dest" "$checksum_file" | |
| fail "Checksum verification failed — binary may be corrupted or tampered with" | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@install.sh` around lines 69 - 80, The sha256sum verification block is broken:
remove the duplicate expected=$(grep ...) line, add a proper check for an empty
expected (if [ -z "$expected" ]; then ... fi) before using it, and fix the grep
to match filename at end of line (e.g. grep "<pattern matching filename at end>"
"$checksum_file" to extract the hash into the variable expected) so that when
expected is empty you run rm -f "$dest" "$checksum_file" and call fail "Checksum
entry for $binary_name not found in checksums.txt", otherwise echo "$expected
$dest" | sha256sum --check --status || { rm -f "$dest" "$checksum_file"; fail
"Checksum verification failed — binary may be corrupted or tampered with"; }
referencing the variables binary_name, checksum_file, expected, dest and the
sha256sum verification block.
| else | ||
| warn "Neither sha256sum nor shasum found — skipping checksum verification" | ||
| rm -f "$dest" "$checksum_file" | ||
| fail "Neither sha256sum nor shasum found — cannot verify binary integrity. Install coreutils and retry." | ||
|
|
||
| rm -f "$checksum_file" |
There was a problem hiding this comment.
Missing fi and redundant warn in else block.
- Redundant warn: Line 93 says "skipping checksum verification" but line 95 immediately fails. The warn is misleading and unnecessary since we're aborting.
- Missing
fi: Theif/elif/elseblock starting at line 69 is never closed. Line 97 (rm -f "$checksum_file") should execute only on the success path after the block ends, but withoutfithis is a syntax error.
🐛 Proposed fix
else
- warn "Neither sha256sum nor shasum found — skipping checksum verification"
rm -f "$dest" "$checksum_file"
fail "Neither sha256sum nor shasum found — cannot verify binary integrity. Install coreutils and retry."
-
+ fi
+
rm -f "$checksum_file"
chmod +x "$dest"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@install.sh` around lines 92 - 97, The else branch inside the checksum
selection block should not emit a misleading warn and must be closed: remove the
redundant warn "Neither sha256sum nor shasum found — skipping checksum
verification" and keep the fail call that aborts ("fail 'Neither sha256sum nor
shasum found — cannot verify binary integrity. Install coreutils and retry.'"),
then add the missing fi to close the if/elif/else block that begins around the
checksum detection, and ensure the standalone rm -f "$checksum_file" that
follows is positioned/executed only on the successful path after that fi (i.e.,
leave the cleanup rm for the success flow, not inside the else).
12876f6 to
ac8a88a
Compare
Summary
Closes #6 and #7 — completes the v0.1.3 Security Hardening milestone.
Changes
install.sh— checksum verification (fixes #6)GoReleaser publishes
checksums.txtalongside each release binary. This change:checksums.txtfrom the same release tagsha256sum(Linux) orshasum -a 256(macOS)tools/automation/ollama-delegate.sh— safe.envparser (fixes #7)Replaces
source .envwith a line-by-line parser (_load_env) that:$(or backticksKEY=VALUElines matching[A-Za-z_][A-Za-z0-9_]*=exportto set each variableTesting
bash -nsyntax check.envparser rejectsFOO=$(rm -rf /)andBAR=`whoami`style injectionsSummary by CodeRabbit
Bug Fixes
Chores