Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DEPLOY_DIR="$(cd "${TEST_DIR}/.." && pwd)"
|
||||
SCRIPT="${DEPLOY_DIR}/apple-container.sh"
|
||||
TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/sub2api-apple-test.XXXXXX")"
|
||||
STATE_DIR="${TEST_ROOT}/state"
|
||||
ENV_FILE="${TEST_ROOT}/sub2api.env"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "${TEST_ROOT}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_exists() {
|
||||
[[ -e "$1" ]] || fail "Expected path to exist: $1"
|
||||
}
|
||||
|
||||
assert_missing() {
|
||||
[[ ! -e "$1" ]] || fail "Expected path to be absent: $1"
|
||||
}
|
||||
|
||||
export FAKE_CONTAINER_STATE="${STATE_DIR}"
|
||||
export PATH="${TEST_DIR}/fixtures/bin:${PATH}"
|
||||
export SUB2API_ENV_FILE="${ENV_FILE}"
|
||||
|
||||
mkdir -p "${STATE_DIR}"
|
||||
|
||||
"${SCRIPT}" init
|
||||
[[ "$(stat -f '%Lp' "${ENV_FILE}")" == "600" ]] || fail "init did not create a mode-600 env file"
|
||||
grep -q '^POSTGRES_PASSWORD=change_this_secure_password$' "${ENV_FILE}" && fail "init retained the placeholder password"
|
||||
|
||||
chmod 644 "${ENV_FILE}"
|
||||
if "${SCRIPT}" up >/dev/null 2>&1; then
|
||||
fail "up accepted an insecure env file"
|
||||
fi
|
||||
chmod 600 "${ENV_FILE}"
|
||||
|
||||
"${SCRIPT}" up
|
||||
assert_exists "${STATE_DIR}/containers/sub2api-apple"
|
||||
assert_exists "${STATE_DIR}/containers/sub2api-apple-postgres"
|
||||
assert_exists "${STATE_DIR}/containers/sub2api-apple-redis"
|
||||
assert_exists "${STATE_DIR}/running/sub2api-apple"
|
||||
"${SCRIPT}" status >/dev/null
|
||||
|
||||
"${SCRIPT}" up --recreate
|
||||
assert_exists "${STATE_DIR}/running/sub2api-apple"
|
||||
"${SCRIPT}" down
|
||||
assert_missing "${STATE_DIR}/running/sub2api-apple"
|
||||
assert_missing "${STATE_DIR}/running/sub2api-apple-postgres"
|
||||
assert_missing "${STATE_DIR}/running/sub2api-apple-redis"
|
||||
|
||||
"${SCRIPT}" destroy --yes
|
||||
assert_missing "${STATE_DIR}/containers/sub2api-apple"
|
||||
assert_missing "${STATE_DIR}/networks/sub2api-apple"
|
||||
assert_exists "${STATE_DIR}/volumes/sub2api-apple-data"
|
||||
|
||||
"${SCRIPT}" up
|
||||
"${SCRIPT}" destroy --volumes --yes
|
||||
assert_missing "${STATE_DIR}/volumes/sub2api-apple-data"
|
||||
assert_missing "${STATE_DIR}/volumes/sub2api-apple-postgres-data"
|
||||
assert_missing "${STATE_DIR}/volumes/sub2api-apple-redis-data"
|
||||
|
||||
touch "${STATE_DIR}/system-running"
|
||||
touch "${STATE_DIR}/containers/sub2api-apple"
|
||||
touch "${STATE_DIR}/unowned/container/sub2api-apple"
|
||||
if "${SCRIPT}" status >/dev/null 2>&1; then
|
||||
fail "status accepted an unowned same-name container"
|
||||
fi
|
||||
|
||||
printf 'Apple container lifecycle tests passed.\n'
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)
|
||||
cd "$repo_root"
|
||||
|
||||
check_application_security_opt() {
|
||||
file=$1
|
||||
count=$(
|
||||
awk '
|
||||
$0 == " sub2api:" {
|
||||
in_application = 1
|
||||
next
|
||||
}
|
||||
in_application && $0 ~ /^ [A-Za-z0-9_-]+:$/ {
|
||||
in_application = 0
|
||||
}
|
||||
in_application && $0 == " security_opt:" {
|
||||
in_security_opt = 1
|
||||
next
|
||||
}
|
||||
in_application && in_security_opt && $0 == " - no-new-privileges:true" {
|
||||
count++
|
||||
}
|
||||
END { print count + 0 }
|
||||
' "$file"
|
||||
)
|
||||
|
||||
if [ "$count" -ne 1 ]; then
|
||||
printf '%s must enable no-new-privileges exactly once for the sub2api service\n' "$file" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for compose_file in \
|
||||
deploy/docker-compose.yml \
|
||||
deploy/docker-compose.local.yml \
|
||||
deploy/docker-compose.standalone.yml \
|
||||
deploy/docker-compose.dev.yml
|
||||
do
|
||||
check_application_security_opt "$compose_file"
|
||||
done
|
||||
|
||||
printf 'docker compose security test passed\n'
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)
|
||||
cd "$repo_root"
|
||||
|
||||
fail() {
|
||||
printf 'docker runtime resources test failed: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_line() {
|
||||
file=$1
|
||||
line=$2
|
||||
grep -Fqx "$line" "$file" || fail "$file is missing: $line"
|
||||
}
|
||||
|
||||
assert_count() {
|
||||
file=$1
|
||||
line=$2
|
||||
expected=$3
|
||||
actual=$(grep -Fxc "$line" "$file" || true)
|
||||
[ "$actual" -eq "$expected" ] || fail "$file has $actual occurrences of '$line', expected $expected"
|
||||
}
|
||||
|
||||
test -s backend/resources/model-pricing/model_prices_and_context_window.json || \
|
||||
fail 'fallback pricing data is missing or empty'
|
||||
|
||||
assert_line Dockerfile.goreleaser 'COPY --chown=sub2api:sub2api backend/resources /app/resources'
|
||||
assert_line deploy/Dockerfile 'COPY --from=backend-builder --chown=sub2api:sub2api /app/backend/resources /app/resources'
|
||||
assert_count .goreleaser.yaml ' - backend/resources' 4
|
||||
assert_count .goreleaser.simple.yaml ' - backend/resources' 1
|
||||
|
||||
printf 'docker runtime resources test passed\n'
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
STATE_DIR="${FAKE_CONTAINER_STATE:?FAKE_CONTAINER_STATE is required}"
|
||||
mkdir -p \
|
||||
"${STATE_DIR}/containers" \
|
||||
"${STATE_DIR}/running" \
|
||||
"${STATE_DIR}/networks" \
|
||||
"${STATE_DIR}/volumes" \
|
||||
"${STATE_DIR}/unowned/container" \
|
||||
"${STATE_DIR}/unowned/network" \
|
||||
"${STATE_DIR}/unowned/volume"
|
||||
|
||||
list_names() {
|
||||
local directory=$1
|
||||
local path
|
||||
|
||||
for path in "${directory}"/*; do
|
||||
[[ -e "${path}" ]] || continue
|
||||
basename "${path}"
|
||||
done
|
||||
}
|
||||
|
||||
last_argument() {
|
||||
local value=""
|
||||
|
||||
for value in "$@"; do :; done
|
||||
printf '%s\n' "${value}"
|
||||
}
|
||||
|
||||
inspect_resource() {
|
||||
local resource_type=$1
|
||||
local resource_name=$2
|
||||
local label_value="apple-container"
|
||||
local address="192.168.65.4/24"
|
||||
|
||||
if [[ -e "${STATE_DIR}/unowned/${resource_type}/${resource_name}" ]]; then
|
||||
label_value="other"
|
||||
fi
|
||||
case "${resource_name}" in
|
||||
sub2api-apple-postgres) address="192.168.65.2/24" ;;
|
||||
sub2api-apple-redis) address="192.168.65.3/24" ;;
|
||||
esac
|
||||
|
||||
printf '[{"configuration":{"labels":{"org.sub2api.stack":"%s"}},"status":{"networks":[{"ipv4Address":"%s"}]}}]\n' \
|
||||
"${label_value}" "${address}"
|
||||
}
|
||||
|
||||
command=${1-}
|
||||
if [[ $# -gt 0 ]]; then shift; fi
|
||||
|
||||
case "${command}" in
|
||||
--version)
|
||||
echo "container CLI version 1.1.0 (build: release, commit: fake)"
|
||||
;;
|
||||
system)
|
||||
subcommand=${1-}
|
||||
case "${subcommand}" in
|
||||
status) [[ -e "${STATE_DIR}/system-running" ]] ;;
|
||||
start) touch "${STATE_DIR}/system-running" ;;
|
||||
stop) rm -f "${STATE_DIR}/system-running" "${STATE_DIR}/running"/* ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
list)
|
||||
include_all=false
|
||||
for argument in "$@"; do
|
||||
[[ "${argument}" == "--all" || "${argument}" == "-a" ]] && include_all=true
|
||||
done
|
||||
if [[ "${include_all}" == true ]]; then
|
||||
list_names "${STATE_DIR}/containers"
|
||||
else
|
||||
list_names "${STATE_DIR}/running"
|
||||
fi
|
||||
;;
|
||||
network)
|
||||
subcommand=${1-}
|
||||
shift || true
|
||||
case "${subcommand}" in
|
||||
list)
|
||||
echo default
|
||||
list_names "${STATE_DIR}/networks"
|
||||
;;
|
||||
create) touch "${STATE_DIR}/networks/$(last_argument "$@")" ;;
|
||||
inspect) inspect_resource network "${1}" ;;
|
||||
delete) rm -f "${STATE_DIR}/networks/${1}" ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
volume)
|
||||
subcommand=${1-}
|
||||
shift || true
|
||||
case "${subcommand}" in
|
||||
list) list_names "${STATE_DIR}/volumes" ;;
|
||||
create) touch "${STATE_DIR}/volumes/$(last_argument "$@")" ;;
|
||||
inspect) inspect_resource volume "${1}" ;;
|
||||
delete) rm -f "${STATE_DIR}/volumes/${1}" ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
image)
|
||||
subcommand=${1-}
|
||||
case "${subcommand}" in
|
||||
inspect|pull) exit 0 ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
create)
|
||||
name=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--name)
|
||||
name=$2
|
||||
shift 2
|
||||
;;
|
||||
--label|--network|--platform|--ulimit|--env-file|--volume|--entrypoint|--publish)
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ -n "${name}" ]]
|
||||
touch "${STATE_DIR}/containers/${name}"
|
||||
;;
|
||||
inspect)
|
||||
inspect_resource container "${1}"
|
||||
;;
|
||||
start)
|
||||
touch "${STATE_DIR}/running/${1}"
|
||||
;;
|
||||
stop)
|
||||
for argument in "$@"; do
|
||||
case "${argument}" in
|
||||
--time|--signal) skip_next=true ;;
|
||||
[0-9]*|SIG*) ;;
|
||||
*) rm -f "${STATE_DIR}/running/${argument}" ;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
delete)
|
||||
for argument in "$@"; do
|
||||
case "${argument}" in
|
||||
--force|-f) ;;
|
||||
*)
|
||||
rm -f "${STATE_DIR}/running/${argument}"
|
||||
rm -f "${STATE_DIR}/containers/${argument}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
exec)
|
||||
echo 1
|
||||
;;
|
||||
logs|copy)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported fake container command: ${command} $*" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
printf '{"status":"ok"}\n'
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
|
||||
cat > "$TEMP_DIR/curl" <<'EOF'
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$@" > "$CURL_ARGS_LOG"
|
||||
env > "${CURL_ARGS_LOG}.env"
|
||||
cat > "${CURL_ARGS_LOG}.stdin"
|
||||
EOF
|
||||
chmod +x "$TEMP_DIR/curl"
|
||||
|
||||
mkdir "$TEMP_DIR/home"
|
||||
cat > "$TEMP_DIR/home/.curlrc" <<'EOF'
|
||||
url = "https://example.com/collect"
|
||||
header = "X-Leaked-From-Curlrc: yes"
|
||||
EOF
|
||||
|
||||
run_api_curl() {
|
||||
CURL_ARGS_LOG="$1" HOME="$TEMP_DIR/home" PATH="$TEMP_DIR:$PATH" UPDATE_GITHUB_TOKEN="${2:-}" \
|
||||
GITHUB_TOKEN="github-fallback" GH_TOKEN="gh-fallback" \
|
||||
bash -c 'source <(head -n -1 "$1"); github_api_curl -s "$2"' bash \
|
||||
"$ROOT_DIR/deploy/install.sh" "https://api.github.com/repos/Wei-Shaw/sub2api/releases/latest"
|
||||
}
|
||||
|
||||
run_api_curl "$TEMP_DIR/authenticated" "update-secret"
|
||||
test "$(head -n 1 "$TEMP_DIR/authenticated")" = '-q'
|
||||
grep -Fxq -- '--config' "$TEMP_DIR/authenticated"
|
||||
grep -Fxq -- '-' "$TEMP_DIR/authenticated"
|
||||
grep -Fxq -- '--globoff' "$TEMP_DIR/authenticated"
|
||||
grep -Fxq 'header = "Authorization: Bearer update-secret"' "$TEMP_DIR/authenticated.stdin"
|
||||
if grep -Fq 'update-secret' "$TEMP_DIR/authenticated"; then
|
||||
echo "installer exposed the update token in curl argv" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -Eq 'update-secret|github-fallback|gh-fallback' "$TEMP_DIR/authenticated.env"; then
|
||||
echo "installer exposed a token in curl environment" >&2
|
||||
exit 1
|
||||
fi
|
||||
test "$(grep -Fxc 'https://api.github.com/repos/Wei-Shaw/sub2api/releases/latest' "$TEMP_DIR/authenticated")" -eq 1
|
||||
if grep -Fq 'example.com/collect' "$TEMP_DIR/authenticated" || grep -Fq 'X-Leaked-From-Curlrc' "$TEMP_DIR/authenticated" ||
|
||||
grep -Fq 'example.com/collect' "$TEMP_DIR/authenticated.stdin" || grep -Fq 'X-Leaked-From-Curlrc' "$TEMP_DIR/authenticated.stdin"; then
|
||||
echo "installer allowed hostile curl config into authenticated invocation" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_api_curl "$TEMP_DIR/anonymous"
|
||||
test "$(head -n 1 "$TEMP_DIR/anonymous")" = '-q'
|
||||
if grep -Eq 'github-fallback|gh-fallback' "$TEMP_DIR/anonymous.env"; then
|
||||
echo "installer exposed a fallback token in anonymous curl environment" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -Fq 'Authorization:' "$TEMP_DIR/anonymous"; then
|
||||
echo "installer unexpectedly used a fallback token" >&2
|
||||
exit 1
|
||||
fi
|
||||
test ! -s "$TEMP_DIR/anonymous.stdin"
|
||||
test "$(grep -Fxc 'https://api.github.com/repos/Wei-Shaw/sub2api/releases/latest' "$TEMP_DIR/anonymous")" -eq 1
|
||||
if grep -Fq 'example.com/collect' "$TEMP_DIR/anonymous" || grep -Fq 'X-Leaked-From-Curlrc' "$TEMP_DIR/anonymous"; then
|
||||
echo "installer allowed hostile curl config into anonymous invocation" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
assert_unsafe_invocation_rejected() {
|
||||
local name=$1
|
||||
shift
|
||||
rm -f "$TEMP_DIR/$name" "$TEMP_DIR/$name.stdin"
|
||||
if CURL_ARGS_LOG="$TEMP_DIR/$name" PATH="$TEMP_DIR:$PATH" UPDATE_GITHUB_TOKEN="update-secret" \
|
||||
bash -c 'source <(head -n -1 "$1"); shift; github_api_curl "$@"' bash \
|
||||
"$ROOT_DIR/deploy/install.sh" "$@" 2>/dev/null; then
|
||||
echo "installer accepted unsafe curl invocation: $name" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -e "$TEMP_DIR/$name" ]; then
|
||||
echo "installer invoked curl for unsafe request: $name" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_unsafe_invocation_rejected non-api -s \
|
||||
"https://github.com/Wei-Shaw/sub2api/releases/download/v1/asset"
|
||||
assert_unsafe_invocation_rejected mixed-host -s \
|
||||
"https://api.github.com/repos/Wei-Shaw/sub2api/releases/latest" \
|
||||
"https://example.com/collect"
|
||||
assert_unsafe_invocation_rejected multiple-api -s \
|
||||
"https://api.github.com/repos/Wei-Shaw/sub2api/releases/latest" \
|
||||
"https://api.github.com/repos/Wei-Shaw/sub2api/releases"
|
||||
assert_unsafe_invocation_rejected url-option -s --url \
|
||||
"https://example.com/collect" \
|
||||
"https://api.github.com/repos/Wei-Shaw/sub2api/releases/latest"
|
||||
|
||||
# Every installer release API request must use the scoped helper.
|
||||
test "$(grep -c 'github_api_curl .*https://api.github.com/' "$ROOT_DIR/deploy/install.sh")" -eq 3
|
||||
|
||||
# Asset and checksum downloads must continue to call curl directly.
|
||||
grep -Fq 'curl -sL "$download_url"' "$ROOT_DIR/deploy/install.sh"
|
||||
grep -Fq 'curl -sL "$checksum_url"' "$ROOT_DIR/deploy/install.sh"
|
||||
|
||||
echo "install GitHub token checks passed"
|
||||
Reference in New Issue
Block a user