From 35bc9517ced0564b9dffab4f121d435fdb0d4988 Mon Sep 17 00:00:00 2001 From: liangshuo-1 <266696938+liangshuo-1@users.noreply.github.com> Date: Sat, 4 Jul 2026 14:08:49 +0800 Subject: [PATCH] feat(envvars): add LARKSUITE_CLI_AGENT_NAME accessor envvars.AgentName() reads LARKSUITE_CLI_AGENT_NAME so business code can identify the agent driving the CLI. Value is sanitised (trim, reject control chars, 128-byte cap) and returns "" when unset or invalid. Local-only; not sent to the server. --- internal/envvars/envvars.go | 1 + internal/envvars/read.go | 29 +++++++++++++++++++ internal/envvars/read_test.go | 52 +++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 internal/envvars/read.go create mode 100644 internal/envvars/read_test.go diff --git a/internal/envvars/envvars.go b/internal/envvars/envvars.go index 7b4a23464..36b60e42d 100644 --- a/internal/envvars/envvars.go +++ b/internal/envvars/envvars.go @@ -19,6 +19,7 @@ const ( // Content safety scanning mode CliContentSafetyMode = "LARKSUITE_CLI_CONTENT_SAFETY_MODE" + CliAgentName = "LARKSUITE_CLI_AGENT_NAME" CliAgentTrace = "LARKSUITE_CLI_AGENT_TRACE" CliProxyEnable = "LARKSUITE_CLI_PROXY_ENABLE" diff --git a/internal/envvars/read.go b/internal/envvars/read.go new file mode 100644 index 000000000..ca767e917 --- /dev/null +++ b/internal/envvars/read.go @@ -0,0 +1,29 @@ +// Copyright (c) 2026 Lark Technologies Pte. Ltd. +// SPDX-License-Identifier: MIT + +package envvars + +import ( + "os" + "strings" + "unicode" +) + +const agentNameMaxLen = 128 + +func AgentName() string { + return sanitizeSingleLine(os.Getenv(CliAgentName), agentNameMaxLen) +} + +func sanitizeSingleLine(raw string, maxLen int) string { + v := strings.TrimSpace(raw) + if v == "" || len(v) > maxLen { + return "" + } + for _, r := range v { + if unicode.IsControl(r) { + return "" + } + } + return v +} diff --git a/internal/envvars/read_test.go b/internal/envvars/read_test.go new file mode 100644 index 000000000..3ab3cd0f8 --- /dev/null +++ b/internal/envvars/read_test.go @@ -0,0 +1,52 @@ +// Copyright (c) 2026 Lark Technologies Pte. Ltd. +// SPDX-License-Identifier: MIT + +package envvars + +import ( + "strings" + "testing" +) + +func TestAgentName_EmptyWhenEnvUnset(t *testing.T) { + t.Setenv(CliAgentName, "") + if got := AgentName(); got != "" { + t.Fatalf("AgentName() = %q, want empty when env unset", got) + } +} + +func TestAgentName_ReturnsCleanValue(t *testing.T) { + t.Setenv(CliAgentName, "claude-code") + if got := AgentName(); got != "claude-code" { + t.Fatalf("AgentName() = %q, want %q", got, "claude-code") + } +} + +func TestAgentName_TrimsWhitespace(t *testing.T) { + t.Setenv(CliAgentName, " cursor ") + if got := AgentName(); got != "cursor" { + t.Fatalf("AgentName() = %q, want %q (whitespace trimmed)", got, "cursor") + } +} + +func TestAgentName_RejectsCRLFInjection(t *testing.T) { + t.Setenv(CliAgentName, "agent\r\nX-Evil: attack") + if got := AgentName(); got != "" { + t.Fatalf("AgentName() = %q, want empty for CR/LF value", got) + } +} + +func TestAgentName_RejectsControlChar(t *testing.T) { + t.Setenv(CliAgentName, "agent\x01injected") + if got := AgentName(); got != "" { + t.Fatalf("AgentName() = %q, want empty for control char value", got) + } +} + +func TestAgentName_RejectsOverlongValue(t *testing.T) { + longVal := strings.Repeat("a", agentNameMaxLen+1) + t.Setenv(CliAgentName, longVal) + if got := AgentName(); got != "" { + t.Fatalf("AgentName() returned non-empty for %d-byte value (max %d)", len(longVal), agentNameMaxLen) + } +}