// Package builtin - bash_secret_ctx.go 定义 Bash 工具子进程 secret env 注入的 context key. // // # 为什么用 context 而不是 BashTool 字段? // // BashTool 是引擎级别的单例(tools.Registry 持有一个实例,所有请求共用). // 若将 per-request secret envs 写入 BashTool 字段: // - 并发请求 A 和 B 会互相覆盖字段值(data race) // - 即使加锁,B 在 A 执行工具前改写了字段,A 会注入 B 的 secret // // context.Context 天然是 per-request 的,且 Execute 函数签名已携带 ctx-- // 无需修改任何调用链,注入点精准,隔离性完美. // // 升华改进(ELEVATED): 和早期实现 的 GHA_SUBPROCESS_SCRUB 黑名单对比, 我们反向设计: // 用户通过 engine.SetSecret / WithSecret 主动注册 secret → context 携带 → // 子进程环境追加注入. 秘密管理的唯一正确路径是 SecretStore, 引擎不做 env 黑名单 // 过滤 (2026-04-15 L513 审计决策, 详见 bash.go ExecuteBash 设计注释). // 替代方案: // - 否决:goroutineID 在 Go 中不是一等公民,实现脆弱且不可移植. package builtin import "context" // secretEnvsKey 是 context 中存储 secret env var 列表的类型安全 key. // // 精妙之处(CLEVER): 使用私有空结构体作为 key-- // 外部包无法伪造此 key(类型不导出),杜绝 key 冲突. // 值类型 []string 是 "NAME=VALUE" 格式的快照,与 os.Environ() 格式一致, // 可直接追加到 cmd.Env. type secretEnvsKey struct{} // ContextWithSecretEnvs 将 secret env var 列表注入 context. // // envs 的格式应为 ["NAME=VALUE", ...],与 os.Environ() 一致. // 引擎在调用工具前调用此函数,为该次工具执行创建带 secret 的 context. // // 调用方(engine.runLoop)负责提供 envs,Bash 工具只负责读取, // 职责分离:secret 管理在引擎层,env 组装在工具层. func ContextWithSecretEnvs(ctx context.Context, envs []string) context.Context { if len(envs) == 0 { return ctx // 无 secret 时不创建新 context(节省内存分配) } return context.WithValue(ctx, secretEnvsKey{}, envs) } // SecretEnvsFromCtx 从 context 中读取 secret env var 列表. // 若 context 中没有 secret(未调用 ContextWithSecretEnvs),返回 nil. // 返回 nil 时调用方不应 append 到 cmd.Env(append(nil, nil...) 是安全的,但多余). func SecretEnvsFromCtx(ctx context.Context) []string { envs, _ := ctx.Value(secretEnvsKey{}).([]string) return envs }