From 93ec494c0a2e1444ca47ce4d09a0c1e5e883510a Mon Sep 17 00:00:00 2001 From: Cyrus Date: Thu, 8 Oct 2026 23:27:36 +0800 Subject: [PATCH 1/2] Provision BINANCE_SECRET_KEY for binance-cli v2 binance-cli v2.0.0 reads the API secret from BINANCE_SECRET_KEY instead of BINANCE_API_SECRET. Provision both variables so v1 and v2 work, and import credentials from either naming scheme. --- plugins/binance/api_key.go | 14 +++++++++- plugins/binance/api_key_test.go | 48 ++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/plugins/binance/api_key.go b/plugins/binance/api_key.go index 56d4dc0f2..9fd28a933 100644 --- a/plugins/binance/api_key.go +++ b/plugins/binance/api_key.go @@ -43,10 +43,22 @@ func APIKey() schema.CredentialType { }, }, DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), - Importer: importer.TryEnvVarPair(defaultEnvVarMapping)} + Importer: importer.TryAll( + importer.TryEnvVarPair(map[string]sdk.FieldName{ + "BINANCE_API_KEY": fieldname.APIKey, + "BINANCE_SECRET_KEY": fieldname.APISecret, + }), + importer.TryEnvVarPair(map[string]sdk.FieldName{ + "BINANCE_API_KEY": fieldname.APIKey, + "BINANCE_API_SECRET": fieldname.APISecret, + }), + )} } +// binance-cli v2 reads the secret from BINANCE_SECRET_KEY, while v1 reads BINANCE_API_SECRET, +// so both are provisioned to support either version. var defaultEnvVarMapping = map[string]sdk.FieldName{ "BINANCE_API_KEY": fieldname.APIKey, + "BINANCE_SECRET_KEY": fieldname.APISecret, "BINANCE_API_SECRET": fieldname.APISecret, } diff --git a/plugins/binance/api_key_test.go b/plugins/binance/api_key_test.go index ffc388527..9f5090b4d 100644 --- a/plugins/binance/api_key_test.go +++ b/plugins/binance/api_key_test.go @@ -18,6 +18,7 @@ func TestAPIKeyProvisioner(t *testing.T) { ExpectedOutput: sdk.ProvisionOutput{ Environment: map[string]string{ "BINANCE_API_KEY": "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + "BINANCE_SECRET_KEY": "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", "BINANCE_API_SECRET": "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", }, }, @@ -27,11 +28,50 @@ func TestAPIKeyProvisioner(t *testing.T) { func TestAPIKeyImporter(t *testing.T) { plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{ - "environment": { + "binance-cli v2 environment": { + Environment: map[string]string{ + "BINANCE_API_KEY": "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + "BINANCE_SECRET_KEY": "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + fieldname.APISecret: "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", + }, + }, + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + }, + }, + }, + }, + "binance-cli v1 environment": { Environment: map[string]string{ "BINANCE_API_KEY": "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", "BINANCE_API_SECRET": "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + }, + }, + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + fieldname.APISecret: "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", + }, + }, + }, + }, + "both secret env vars with different values": { + Environment: map[string]string{ + "BINANCE_API_KEY": "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + "BINANCE_SECRET_KEY": "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", + "BINANCE_API_SECRET": "Qx7Vb2NwLk9RtYp4HsMc6JdZf8GaUe3XiOn5TrWq1KyBv0PlCm2AzSj4DEXAMPLE", + }, ExpectedCandidates: []sdk.ImportCandidate{ { Fields: map[sdk.FieldName]string{ @@ -39,6 +79,12 @@ func TestAPIKeyImporter(t *testing.T) { fieldname.APISecret: "2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qjThmEycY2J0RgJgNNrWQBqEXAMPLE", }, }, + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "jThmEycY2J0RgJgNNrWQBq2raPzKvxCkcwPQFk8AuWUu5QxQSWaItIB1qEXAMPLE", + fieldname.APISecret: "Qx7Vb2NwLk9RtYp4HsMc6JdZf8GaUe3XiOn5TrWq1KyBv0PlCm2AzSj4DEXAMPLE", + }, + }, }, }, }) From a07bf14940a874e7157497accdc2a697d348d640 Mon Sep 17 00:00:00 2001 From: Cyrus Date: Fri, 9 Oct 2026 09:14:19 +0800 Subject: [PATCH 2/2] Skip authentication for binance-cli v2 profile and completion commands binance-cli v2 adds the profile and completion subcommands, which only manage local configuration and shell completion. They don't call the Binance API. --- plugins/binance/binance_cli.go | 2 ++ plugins/binance/binance_cli_test.go | 48 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 plugins/binance/binance_cli_test.go diff --git a/plugins/binance/binance_cli.go b/plugins/binance/binance_cli.go index 3a78ae669..d2ab6d875 100644 --- a/plugins/binance/binance_cli.go +++ b/plugins/binance/binance_cli.go @@ -15,6 +15,8 @@ func BinanceCLI() schema.Executable { NeedsAuth: needsauth.IfAll( needsauth.NotForHelpOrVersion(), needsauth.NotWithoutArgs(), + needsauth.NotForCommand("profile"), + needsauth.NotForCommand("completion"), needsauth.NotWhenContainsArgs("t"), needsauth.NotWhenContainsArgs("i"), needsauth.NotWhenContainsArgs("book"), diff --git a/plugins/binance/binance_cli_test.go b/plugins/binance/binance_cli_test.go new file mode 100644 index 000000000..609c25f62 --- /dev/null +++ b/plugins/binance/binance_cli_test.go @@ -0,0 +1,48 @@ +package binance + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk/plugintest" +) + +func TestBinanceCLINeedsAuth(t *testing.T) { + plugintest.TestNeedsAuth(t, BinanceCLI().NeedsAuth, map[string]plugintest.NeedsAuthCase{ + "no without args": { + Args: []string{}, + ExpectedNeedsAuth: false, + }, + "no for --help": { + Args: []string{"--help"}, + ExpectedNeedsAuth: false, + }, + "no for --version": { + Args: []string{"--version"}, + ExpectedNeedsAuth: false, + }, + "no for v1 public market data command": { + Args: []string{"book", "bnbusdt"}, + ExpectedNeedsAuth: false, + }, + "yes for v1 order command": { + Args: []string{"buy", "-s", "BNBUSDT", "-t", "LIMIT", "-q", "0.05", "-p", "350", "-f", "GTC"}, + ExpectedNeedsAuth: true, + }, + "no for v2 profile command": { + Args: []string{"profile", "list"}, + ExpectedNeedsAuth: false, + }, + "no for v2 completion command": { + Args: []string{"completion", "zsh"}, + ExpectedNeedsAuth: false, + }, + "yes for v2 signed command": { + Args: []string{"spot", "get-account"}, + ExpectedNeedsAuth: true, + }, + "yes for a profile named after an exempt command": { + Args: []string{"spot", "get-account", "--profile", "profile"}, + ExpectedNeedsAuth: true, + }, + }) +}