Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/aiscan/jev_profile_flow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func TestJEVProfileStreamingCompilationRuntimeAndProtocolEvents(t *testing.T) {
}
defer p.Close(context.Background())
source := `js:function(context,args){const seen=context.history.filter(function(r){return r.name==="bash"&&r.arguments&&r.arguments.command==="playwright sessions"&&!r.is_error;});const r=seen.length?seen[seen.length-1]:execute({name:"bash",arguments:{command:command("playwright",["sessions"])},read:true});if(r.is_error)return{defer:"native read failed"};return{report:{evidence:r.call_id,path:["text"]}};}`
artifact, _ := json.Marshal(map[string]any{"api_version": 2, "steps": map[string]any{}, "observe": source, "arguments": map[string]any{}})
artifact, _ := json.Marshal(map[string]any{"when": "Inspect currently open browser sessions", "decide": "Report current native session evidence", "steps": map[string]any{}, "observe": source, "arguments": map[string]any{}})
model := &profileFlowProvider{ordinary: map[string]int{}, artifact: string(artifact)}
p.providers.Set(model, provider.ProviderConfig{Model: "fixture", MaxTokens: 1024})
mux := aop.NewNamespaceMux(t.Context())
Expand Down
2 changes: 1 addition & 1 deletion cmd/aiscan/record_declaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

// The record section and the record extension must appear and disappear
// together. Without the build tag on its declaration, every default build
// validated extensions.record.max_concurrent, wrote it into --init output and
// validated extensions.record.max_concurrent, wrote it into init output and
// showed it in settings, while no extension could exist to read it.
func TestRecordSectionTracksTheRecordExtension(t *testing.T) {
declared := slices.Contains(defaultSections().Aliases(), "record")
Expand Down
2 changes: 1 addition & 1 deletion cmd/harness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,4 @@ make harness-llm-ioa
不覆盖浏览器渲染;前端套件覆盖所选生产前端场景,不能代表整仓库所有功能。新增场景应明确
实际入口、前置条件、可观察结果与退出条件,避免用固定快照数量代替覆盖范围。

模型配置优先使用共享 `CYBER_*`;原 `CYBER_HARNESS_LLM_*` 作为低优先级兼容别名保留。普通离线测试不继承这些凭据,仅显式 live 场景注入,日志同时脱敏新旧变量。
模型配置使用共享 `CYBER_*`。普通离线测试不继承这些凭据,仅显式 live 场景注入;日志脱敏 LLM 和 JEV 凭据。
35 changes: 10 additions & 25 deletions cmd/harness/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,26 @@ import (
"testing"
)

func TestSharedLLMEnvironmentAndLegacyFallback(t *testing.T) {
func TestSharedLLMEnvironmentIsExplicitlyOptedIn(t *testing.T) {
for _, suffix := range []string{"API_KEY", "BASE_URL", "MODEL", "PROVIDER"} {
t.Setenv("CYBER_"+suffix, "shared-"+suffix)
t.Setenv("CYBER_HARNESS_LLM_"+suffix, "legacy-"+suffix)
}
for _, shared := range []bool{true, false} {
if !shared {
for _, suffix := range []string{"API_KEY", "BASE_URL", "MODEL", "PROVIDER"} {
t.Setenv("CYBER_"+suffix, " ")
}
}
for _, include := range []bool{false, true} {
env := strings.Join(testEnvironment(include), "\n")
for _, suffix := range []string{"API_KEY", "BASE_URL", "MODEL", "PROVIDER"} {
prefix := "legacy-"
if shared {
prefix = "shared-"
}
if strings.Contains(env, "CYBER_"+suffix+"="+prefix+suffix) != include {
t.Fatalf("include=%v shared=%v suffix=%s", include, shared, suffix)
}
}
if strings.Contains(env, "CYBER_HARNESS_LLM_") {
t.Fatal("legacy credentials leaked into child environment")
for _, include := range []bool{false, true} {
env := strings.Join(testEnvironment(include), "\n")
for _, suffix := range []string{"API_KEY", "BASE_URL", "MODEL", "PROVIDER"} {
if strings.Contains(env, "CYBER_"+suffix+"=shared-"+suffix) != include {
t.Fatalf("include=%v suffix=%s", include, suffix)
}
}
}
}

func TestRedactionCoversSharedAndLegacyKeys(t *testing.T) {
func TestRedactionCoversLLMAndJEVKeys(t *testing.T) {
t.Setenv("CYBER_API_KEY", `shared-"key`)
t.Setenv("CYBER_HARNESS_LLM_API_KEY", "old-test-key")
t.Setenv("TYPESAFE_API_KEY", "jev-test-key")
encoded, _ := json.Marshal(`shared-"key`)
got := string(redactSecrets([]byte(`shared-"key old-test-key ` + string(encoded))))
if strings.Contains(got, "shared-") || strings.Contains(got, "old-test-key") || strings.Count(got, "[REDACTED]") != 3 {
got := string(redactSecrets([]byte(`shared-"key jev-test-key ` + string(encoded))))
if strings.Contains(got, "shared-") || strings.Contains(got, "jev-test-key") || strings.Count(got, "[REDACTED]") != 3 {
t.Fatalf("redaction failed: %s", got)
}
}
11 changes: 6 additions & 5 deletions cmd/harness/llm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ package harness_test
import (
"net/http"
"net/url"
"os"
"strings"
"sync"
"testing"
)

func liveLLMRequest(t *testing.T) map[string]any {
t.Helper()
key := liveLLMEnv("API_KEY")
model := liveLLMEnv("MODEL")
provider := liveLLMEnv("PROVIDER")
baseURL := liveLLMEnv("BASE_URL")
key := strings.TrimSpace(os.Getenv("CYBER_API_KEY"))
model := strings.TrimSpace(os.Getenv("CYBER_MODEL"))
provider := strings.TrimSpace(os.Getenv("CYBER_PROVIDER"))
baseURL := strings.TrimSpace(os.Getenv("CYBER_BASE_URL"))
if key == "" || model == "" || baseURL == "" {
t.Fatal("live_llm requires CYBER_API_KEY, CYBER_MODEL and CYBER_BASE_URL (legacy CYBER_HARNESS_LLM_* aliases are also supported)")
t.Fatal("live_llm requires CYBER_API_KEY, CYBER_MODEL and CYBER_BASE_URL")
}
endpoint, err := url.Parse(baseURL)
if err != nil || endpoint.Host == "" || (endpoint.Scheme != "https" && endpoint.Scheme != "http") || endpoint.User != nil || endpoint.RawQuery != "" {
Expand Down
15 changes: 2 additions & 13 deletions cmd/harness/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func readFile(t *testing.T, path string) []byte {
}

func redactSecrets(data []byte) []byte {
for _, name := range []string{"CYBER_API_KEY", "CYBER_HARNESS_LLM_API_KEY"} {
for _, name := range []string{"CYBER_API_KEY", "TYPESAFE_API_KEY"} {
key := strings.TrimSpace(os.Getenv(name))
if key == "" {
continue
Expand All @@ -228,17 +228,6 @@ func redactSecrets(data []byte) []byte {
return data
}

// Model settings are shared by every Cyber binary and opt-in live test. Retain
// the old harness names as fallbacks for existing test deployments.
func liveLLMEnv(suffix string) string {
for _, prefix := range []string{"CYBER_", "CYBER_HARNESS_LLM_"} {
if value := strings.TrimSpace(os.Getenv(prefix + suffix)); value != "" {
return value
}
}
return ""
}

// Buffer complete log lines so a credential split across Write calls cannot
// escape redaction. Raw child output is never written to an artifact file.
type processLog struct {
Expand Down Expand Up @@ -325,7 +314,7 @@ func testEnvironment(includeLLM bool) []string {
}
if includeLLM {
for _, suffix := range []string{"API_KEY", "BASE_URL", "MODEL", "PROVIDER"} {
if value := liveLLMEnv(suffix); value != "" {
if value := strings.TrimSpace(os.Getenv("CYBER_" + suffix)); value != "" {
env = append(env, "CYBER_"+suffix+"="+value)
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ aiscan init --project

已有文件默认保持不变。`--force` 创建独立备份后原子替换;失败保留旧文件。`-c <path> init` 创建指定文件,不能与 `--project` 同用。

旧 `--init` 兼容到当前目录,提示使用 `init` 子命令。可选的执行检查见[工具审批配置](#工具审批配置)。
可选的执行检查见[工具审批配置](#工具审批配置)。

## 文件与优先级

Expand Down
Loading
Loading