diff --git a/scripts/install-cli.sh b/scripts/install-cli.sh index bc964ca9743e..397d35ef250f 100755 --- a/scripts/install-cli.sh +++ b/scripts/install-cli.sh @@ -255,19 +255,31 @@ parse_args() { shift ;; --prefix) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + fail "Missing value for $1" + fi PREFIX="$2" shift 2 ;; --version) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + fail "Missing value for $1" + fi OPENCLAW_VERSION="$2" shift 2 ;; --node-version) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + fail "Missing value for $1" + fi NODE_VERSION="$2" NODE_VERSION_REQUESTED=1 shift 2 ;; --install-method|--method) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + fail "Missing value for $1" + fi INSTALL_METHOD="$2" shift 2 ;; @@ -280,6 +292,9 @@ parse_args() { shift ;; --git-dir|--dir) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + fail "Missing value for $1" + fi GIT_DIR="$2" shift 2 ;; diff --git a/scripts/install.sh b/scripts/install.sh index 44d430d4206e..a417bff39bf4 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1232,10 +1232,18 @@ parse_args() { shift ;; --install-method|--method) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + ui_error "Missing value for $1" + return 2 + fi INSTALL_METHOD="$2" shift 2 ;; --version) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + ui_error "Missing value for $1" + return 2 + fi OPENCLAW_VERSION="$2" shift 2 ;; @@ -1252,6 +1260,10 @@ parse_args() { shift ;; --git-dir|--dir) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + ui_error "Missing value for $1" + return 2 + fi GIT_DIR="$2" shift 2 ;; @@ -1260,7 +1272,8 @@ parse_args() { shift ;; *) - shift + ui_error "Unknown option: $1" + return 2 ;; esac done diff --git a/test/scripts/install-cli.test.ts b/test/scripts/install-cli.test.ts index 40bf2dd6a1bd..cfebf359c8be 100644 --- a/test/scripts/install-cli.test.ts +++ b/test/scripts/install-cli.test.ts @@ -104,6 +104,18 @@ function writeNpmBeforePolicyFixture(path: string, argsLog: string) { describe("install-cli.sh", () => { const script = readFileSync(SCRIPT_PATH, "utf8"); + it("rejects installer options with missing values", () => { + const result = runInstallCliShell(` + set -euo pipefail + source "${SCRIPT_PATH}" + parse_args --prefix --no-onboard + `); + + expect(result.status).toBe(1); + expect(result.stdout + result.stderr).toContain("Missing value for --prefix"); + expect(result.stdout + result.stderr).not.toContain("unbound variable"); + }); + it("keeps HOME for default prefix while OPENCLAW_HOME controls git checkout paths", () => { const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-cli-home-")); const osHome = join(tmp, "os-home"); diff --git a/test/scripts/install-sh.test.ts b/test/scripts/install-sh.test.ts index a9f984dd74c3..aba98c017aab 100644 --- a/test/scripts/install-sh.test.ts +++ b/test/scripts/install-sh.test.ts @@ -102,6 +102,28 @@ describe("install.sh", () => { expect(rawAptInstalls).toStrictEqual([]); }); + it("rejects unknown installer options", () => { + const result = runInstallShell(` + set -euo pipefail + source "${SCRIPT_PATH}" + parse_args --bogus + `); + + expect(result.status).toBe(2); + expect(result.stdout + result.stderr).toContain("Unknown option: --bogus"); + }); + + it("rejects installer options with missing values", () => { + const result = runInstallShell(` + set -euo pipefail + source "${SCRIPT_PATH}" + parse_args --version --no-onboard + `); + + expect(result.status).toBe(2); + expect(result.stdout + result.stderr).toContain("Missing value for --version"); + }); + it("accepts GNU and musl Linux shells in OS detection", () => { expect(script).toContain('[[ "$OSTYPE" == "linux"* ]]'); expect(script).not.toContain('[[ "$OSTYPE" == "linux-gnu"* ]]');