feat: add TallyNote local reimbursement ledger
TallyNote release / linux-x64 (push) Failing after 2m41s
TallyNote release / linux-x64 (push) Failing after 2m41s
This commit is contained in:
Executable
+194
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
root=$(cd "$(dirname "$0")/.." && pwd)
|
||||
bash -n "$root/install.sh" "$root/scripts/tallynote-update.sh"
|
||||
output=$(bash "$root/install.sh" --dry-run --release-base-url https://releases.example.test/releases)
|
||||
grep -q 'dry-run' <<<"$output"
|
||||
output=$(bash "$root/install.sh" --dry-run --version 1.2.3 --release-base-url https://releases.example.test/releases)
|
||||
grep -q 'release: 1.2.3' <<<"$output"
|
||||
if bash "$root/install.sh" --dry-run --release-base-url http://insecure.example.test/releases >/dev/null 2>&1; then
|
||||
echo 'expected non-HTTPS URL to fail' >&2
|
||||
exit 1
|
||||
fi
|
||||
tmp=$(mktemp -d)
|
||||
cleanup_tmp() {
|
||||
if [[ -d "$tmp" ]]; then
|
||||
rm -r "$tmp" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup_tmp EXIT
|
||||
cat >"$tmp/uname" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
printf 'i686\n'
|
||||
EOF
|
||||
chmod +x "$tmp/uname"
|
||||
if TALLYNOTE_UNAME_BIN="$tmp/uname" bash "$root/install.sh" --dry-run >/dev/null 2>&1; then
|
||||
echo 'expected ia32 to fail' >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$(uname -s)" != Linux ]]; then
|
||||
if bash "$root/scripts/build-release.sh" 1.0.0 /tmp/tallynote-installer-release-test >/dev/null 2>&1; then
|
||||
echo 'expected non-Linux release build to fail on this host' >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Exercise installer helpers without mutating the host. Removing the final
|
||||
# main invocation lets this subprocess source the exact production code.
|
||||
installer_lib="$tmp/install-lib.sh"
|
||||
sed '$d' "$root/install.sh" > "$installer_lib"
|
||||
bash -c '
|
||||
script=$1
|
||||
mode_dir=$2
|
||||
owner_parent=$3
|
||||
set --
|
||||
source "$script"
|
||||
mkdir -p "$mode_dir"
|
||||
chmod 700 "$mode_dir"
|
||||
[[ "$(stat_mode_bits "$mode_dir")" == 448 ]]
|
||||
mkdir -p "$owner_parent"
|
||||
if (assert_path_chain "$owner_parent/child") >/dev/null 2>&1; then
|
||||
echo "expected non-root path parent to fail" >&2
|
||||
exit 1
|
||||
fi
|
||||
' _ "$installer_lib" "$tmp/mode" "$tmp/user-parent"
|
||||
|
||||
# Duplicate security-sensitive EnvironmentFile assignments are rejected even
|
||||
# when the first value looks valid (systemd uses the later value).
|
||||
duplicate_env="$tmp/duplicate.env"
|
||||
printf '%s\n' 'TALLYNOTE_UPDATE_REQUIRE_SIGNATURE=true' 'TALLYNOTE_UPDATE_REQUIRE_SIGNATURE=false' > "$duplicate_env"
|
||||
bash -c '
|
||||
script=$1
|
||||
env_file=$2
|
||||
set --
|
||||
source "$script"
|
||||
stat_uid() { printf "0"; }
|
||||
stat_mode_bits() { printf "384"; }
|
||||
if (validate_existing_env "$env_file") >/dev/null 2>&1; then
|
||||
echo "expected duplicate environment assignment to fail" >&2
|
||||
exit 1
|
||||
fi
|
||||
' _ "$installer_lib" "$duplicate_env"
|
||||
|
||||
# A release archive is extracted under umask 077, then explicitly normalized
|
||||
# so the tallynote system user can traverse and execute the shipped tree.
|
||||
source_tmp="$tmp/source"
|
||||
mkdir -p "$source_tmp/dist/server" "$source_tmp/bin" "$source_tmp/scripts" "$source_tmp/runtime/bin"
|
||||
printf '%s\n' 'server' > "$source_tmp/dist/server/index.js"
|
||||
printf '%s\n' '#!/bin/sh' > "$source_tmp/bin/tallynote"
|
||||
printf '%s\n' '#!/bin/sh' > "$source_tmp/scripts/runner.sh"
|
||||
printf '%s\n' 'node' > "$source_tmp/runtime/bin/node"
|
||||
chmod 755 "$source_tmp/bin/tallynote" "$source_tmp/scripts/runner.sh" "$source_tmp/runtime/bin/node"
|
||||
archive_tmp="$tmp/release.tar.gz"
|
||||
tar -C "$source_tmp" -czf "$archive_tmp" .
|
||||
bash -c '
|
||||
script=$1
|
||||
archive=$2
|
||||
destination=$3
|
||||
set --
|
||||
source "$script"
|
||||
safe_extract "$archive" "$destination"
|
||||
normalize_release_tree "$destination"
|
||||
[[ "$(stat_mode "$destination/dist")" == 755 ]]
|
||||
[[ "$(stat_mode "$destination/dist/server/index.js")" == 644 ]]
|
||||
[[ "$(stat_mode "$destination/bin/tallynote")" == 755 ]]
|
||||
' _ "$installer_lib" "$archive_tmp" "$tmp/unpacked"
|
||||
|
||||
# Newline/control characters in release configuration must never become extra
|
||||
# systemd EnvironmentFile assignments.
|
||||
if TALLYNOTE_RELEASE_API_URL=$'https://git.awaioi.com/api/v1\nEVIL=1' bash "$root/install.sh" --dry-run >/dev/null 2>&1; then
|
||||
echo 'expected control characters in release URL to fail' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The publisher is safe to exercise on every host in dry-run mode. When an
|
||||
# OpenSSL build supports Ed25519, also verify the exact detached signature.
|
||||
publisher_tmp=$(mktemp -d)
|
||||
printf 'test-release' > "$publisher_tmp/tallynote-1.0.0-linux-x64-glibc.tar.gz"
|
||||
if "$root/scripts/publish-gitea-release.sh" v1.0.0 "$publisher_tmp" --dry-run >/dev/null 2>&1; then
|
||||
test -s "$publisher_tmp/SHA256SUMS"
|
||||
else
|
||||
echo 'publisher dry-run failed' >&2
|
||||
exit 1
|
||||
fi
|
||||
openssl_test_bin=${TALLYNOTE_OPENSSL_BIN:-$(command -v openssl || true)}
|
||||
if [[ -n "$openssl_test_bin" ]] && "$openssl_test_bin" genpkey -algorithm ED25519 -out "$publisher_tmp/key" >/dev/null 2>&1; then
|
||||
TALLYNOTE_RELEASE_SIGNING_KEY_FILE="$publisher_tmp/key" TALLYNOTE_OPENSSL_BIN="$openssl_test_bin" \
|
||||
"$root/scripts/publish-gitea-release.sh" v1.0.0 "$publisher_tmp" --dry-run >/dev/null 2>&1
|
||||
"$openssl_test_bin" pkey -in "$publisher_tmp/key" -pubout -out "$publisher_tmp/pub" >/dev/null 2>&1
|
||||
"$openssl_test_bin" pkeyutl -verify -pubin -inkey "$publisher_tmp/pub" -rawin \
|
||||
-in "$publisher_tmp/SHA256SUMS" -sigfile "$publisher_tmp/SHA256SUMS.sig" >/dev/null 2>&1
|
||||
|
||||
# Exercise the 404 -> create -> assets -> upload flow with a local curl
|
||||
# shim. The shim records argv and verifies the secret only arrives through
|
||||
# the temporary curl config file, never as a process argument.
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
fake_curl="$publisher_tmp/fake-curl"
|
||||
fake_trace="$publisher_tmp/curl-args"
|
||||
fake_config_seen="$publisher_tmp/curl-config-seen"
|
||||
cat > "$fake_curl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
out=''; format=''; method='GET'; url=''; previous=''; config=''
|
||||
for arg in "$@"; do
|
||||
case "$previous" in
|
||||
out) out=$arg; previous=''; continue ;;
|
||||
format) format=$arg; previous=''; continue ;;
|
||||
method) method=$arg; previous=''; continue ;;
|
||||
config) config=$arg; previous=''; continue ;;
|
||||
esac
|
||||
case "$arg" in
|
||||
-o) previous=out ;;
|
||||
-w) previous=format ;;
|
||||
-X) previous=method ;;
|
||||
--config) previous=config ;;
|
||||
-d*|-F*) method=POST ;;
|
||||
http://*|https://*) url=$arg ;;
|
||||
esac
|
||||
done
|
||||
printf '%s\n' "$*" >> "$TALLYNOTE_FAKE_CURL_TRACE"
|
||||
[[ "$*" != *"$TALLYNOTE_FAKE_TOKEN"* ]] || { echo 'token leaked in curl argv' >&2; exit 91; }
|
||||
[[ -n "$config" && -s "$config" ]] || { echo 'curl auth config missing' >&2; exit 92; }
|
||||
grep -q "Authorization: token $TALLYNOTE_FAKE_TOKEN" "$config"
|
||||
printf '%s\n' seen > "$TALLYNOTE_FAKE_CURL_CONFIG_SEEN"
|
||||
code=200; body='{}'
|
||||
if [[ "$url" == */releases/tags/* ]]; then
|
||||
if [[ ! -f "$TALLYNOTE_FAKE_RELEASE_CREATED" ]]; then code=404; body='{}'; else code=200; body='{"id":42}'; fi
|
||||
elif [[ "$url" == */releases && "$method" == POST ]]; then
|
||||
printf '%s' created > "$TALLYNOTE_FAKE_RELEASE_CREATED"
|
||||
code=201; body='{"id":42}'
|
||||
elif [[ "$url" == */assets && "$method" == GET ]]; then
|
||||
code=200; body='[]'
|
||||
elif [[ "$url" == */assets\?name=* ]]; then
|
||||
code=201; body='{"id":1}'
|
||||
elif [[ "$method" == DELETE ]]; then
|
||||
code=204; body=''
|
||||
fi
|
||||
if [[ -n "$out" ]]; then
|
||||
printf '%s' "$body" > "$out"
|
||||
else
|
||||
printf '%s' "$body"
|
||||
fi
|
||||
if [[ "$format" == '%{http_code}' ]]; then
|
||||
printf '%s' "$code"
|
||||
fi
|
||||
EOF
|
||||
chmod 700 "$fake_curl"
|
||||
TALLYNOTE_FAKE_CURL_TRACE="$fake_trace" TALLYNOTE_FAKE_CURL_CONFIG_SEEN="$fake_config_seen" \
|
||||
TALLYNOTE_FAKE_RELEASE_CREATED="$publisher_tmp/release-created" TALLYNOTE_FAKE_TOKEN='secret-token' \
|
||||
TALLYNOTE_CURL_BIN="$fake_curl" GITEA_API_URL='https://gitea.example/api/v1' \
|
||||
GITHUB_REPOSITORY='awaioi/TallyNote' GITEA_TOKEN='secret-token' \
|
||||
TALLYNOTE_RELEASE_SIGNING_KEY_FILE="$publisher_tmp/key" \
|
||||
TALLYNOTE_OPENSSL_BIN="$openssl_test_bin" \
|
||||
"$root/scripts/publish-gitea-release.sh" v1.0.0 "$publisher_tmp" >/dev/null
|
||||
if grep -q 'secret-token' "$fake_trace"; then
|
||||
echo 'token leaked in curl argv' >&2
|
||||
exit 1
|
||||
fi
|
||||
test -s "$fake_config_seen"
|
||||
fi
|
||||
fi
|
||||
if [[ -d "$publisher_tmp" ]]; then
|
||||
rm -r "$publisher_tmp" 2>/dev/null || true
|
||||
fi
|
||||
printf '%s\n' 'installer shell tests passed'
|
||||
Reference in New Issue
Block a user