scripts/ohos-secrets-store-smoke.sh
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env sh
# Verify the file-backed secret store against a real directory, including mounts
# that cannot hold a 0600 mode or an advisory lock.
#
# The unit tests in crates/secrets cover both branches of the mode check, but no
# portable filesystem synthesises mode bits or refuses flock, so a test cannot
# build the branch that matters on HarmonyOS hmdfs. This script covers it by
# driving the real binary against a real directory instead of stubbing one, and
# it says which layer refused when something fails.
#
# Run it against each filesystem you care about and compare:
#
# scripts/ohos-secrets-store-smoke.sh /tmp
# scripts/ohos-secrets-store-smoke.sh "$HOME"
#
# On a HarmonyOS 2in1, the user directory (/storage/Users/currentUser, hmdfs)
# exercises the mode-ignoring and no-advisory-lock fallbacks, and /data (hmfs)
# proves the strict path still holds where mode bits are stored.
#
# Over a device connection, run it inside the device shell:
#
# hdc file send target/aarch64-unknown-linux-ohos/release/codewhale /data/local/tmp/codewhale
# hdc file send scripts/ohos-secrets-store-smoke.sh /data/local/tmp/smoke.sh
# hdc shell "chmod +x /data/local/tmp/codewhale /data/local/tmp/smoke.sh; \
# CODEWHALE_BIN=/data/local/tmp/codewhale sh /data/local/tmp/smoke.sh /data/local/tmp"
#
# Exit status:
# 0 every check passed
# 1 the secret store failed
# 2 bad usage
# 3 a different layer refused before the secret store was reached
set -eu
usage() {
cat >&2 <<'USAGE'
usage: scripts/ohos-secrets-store-smoke.sh <directory> [binary]
<directory> an existing, writable directory. The script creates and removes
its own subdirectory there; nothing else is touched.
[binary] the binary to test. Defaults to $CODEWHALE_BIN, then
target/debug/codewhale, then target/release/codewhale.
USAGE
exit 2
}
[ $# -ge 1 ] || usage
dir=${1%/}
bin=${2:-${CODEWHALE_BIN:-}}
if [ -z "$bin" ]; then
for candidate in target/debug/codewhale target/release/codewhale; do
if [ -x "$candidate" ]; then
bin=$candidate
break
fi
done
fi
fail() {
echo "FAIL: $*" >&2
exit 1
}
mode_of() {
stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1" 2>/dev/null || echo '?'
}
[ -n "$bin" ] || fail "no binary found; pass one or set CODEWHALE_BIN"
[ -x "$bin" ] || fail "binary is not executable: $bin"
[ -d "$dir" ] || fail "not a directory: $dir"
home=$dir/codewhale-secrets-smoke.$$
log=$dir/codewhale-secrets-smoke.$$.log
trap 'rm -rf "$home"; rm -f "$log"' EXIT HUP INT TERM
mkdir -p "$home" || fail "cannot create $home"
echo "binary : $bin"
echo "version: $("$bin" --version 2>/dev/null | head -1)"
echo "home : $home"
# What this mount does with a mode it is asked to store. Create a file, try to
# narrow it to 0600, and read back what the mount kept. A filesystem that stores
# mode bits lands on 0600; a mount that synthesises them stays wider and either
# refuses the chmod or ignores it.
probe=$home/.mode-probe
: >"$probe" || fail "cannot create a file in $home"
probe_before=$(mode_of "$probe")
chmod 600 "$probe" 2>/dev/null || true
probe_after=$(mode_of "$probe")
rm -f "$probe"
echo "mount : $dir"
echo " file created at $probe_before; after chmod 600 it is $probe_after"
case $probe_after in
600 | 400 | 0)
mount_note='stores mode bits'
echo ' -> stores mode bits; the strict path is expected'
;;
*)
mount_note='synthesises mode bits'
echo ' -> does not store mode bits; the fallback path is expected'
;;
esac
echo
# A key that is obviously not a credential, and never leaves this host.
api_key='[redacted]'
echo '--- 1. auth set writes the key'
if ! printf '%s\n' "$api_key" | CODEWHALE_HOME="$home" "$bin" auth set --provider deepseek >"$log" 2>&1; then
if grep -q 'config lock' "$log"; then
cat "$log" >&2
echo 'BLOCKED: the config layer refused before the secret store was reached.' >&2
echo ' That is the crates/config write lock, not this store.' >&2
exit 3
fi
cat "$log" >&2
fail 'auth set failed; the store could not be written on this mount'
fi
grep -q 'saved API key' "$log" || {
cat "$log" >&2
fail 'auth set did not report saving the key'
}
echo 'ok: key accepted'
echo '--- 2. the key is readable back through the store'
# `auth status` names the layer that answered, so this proves the store supplied
# the key rather than the environment or config.toml.
if ! CODEWHALE_HOME="$home" "$bin" auth status --provider deepseek >"$log" 2>&1; then
cat "$log" >&2
fail 'auth status failed'
fi
grep -q 'active source: secret store' "$log" || {
cat "$log" >&2
fail 'the stored key is not the active source'
}
grep -q 'secret store: file-based.*(set,' "$log" || {
cat "$log" >&2
fail 'the secret store does not report the key as set'
}
if grep -q 'env var: DEEPSEEK_API_KEY (set' "$log"; then
cat "$log" >&2
fail 'DEEPSEEK_API_KEY is set in this shell and would mask the store; unset it and retry'
fi
echo 'ok: auth status resolves the key from the secret store'
echo '--- 3. the config file holds no plaintext key'
config=$home/config.toml
[ -f "$config" ] || fail "no config.toml written at $config"
if grep -qF -- "$api_key" "$config"; then
fail 'the API key was written to config.toml in plaintext'
fi
echo 'ok: config.toml has no plaintext key'
echo '--- 4. the secrets file exists, and is owner-only where the mount can express that'
secrets=$home/secrets/secrets.json
[ -f "$secrets" ] || fail "no secrets.json written at $secrets"
secrets_mode=$(mode_of "$secrets")
echo " secrets.json mode: $secrets_mode"
if [ "$mount_note" = 'stores mode bits' ]; then
case $secrets_mode in
600 | 400 | 0) echo 'ok: owner-only' ;;
*) fail "a mount that stores mode bits left secrets.json at $secrets_mode, not 0600" ;;
esac
else
echo 'ok: the mount reports its own mode, and the store accepted it deliberately'
fi
echo
echo "PASS: the secret store works on $dir"