@@ -11,7 +11,7 @@ import (
1111 "gopkg.in/yaml.v3"
1212)
1313
14- // ContextBudgetMigrationResult 汇总 config.yaml 预算配置迁移的执行结果 。
14+ // ContextBudgetMigrationResult 汇总 config.yaml schema 升级的执行结果 。
1515type ContextBudgetMigrationResult struct {
1616 Path string
1717 Changed bool
@@ -21,7 +21,7 @@ type ContextBudgetMigrationResult struct {
2121}
2222
2323const (
24- // ContextBudgetMigrationNoteEnabledDeprecated 标记旧开关被废弃且预算门禁不可关闭 。
24+ // ContextBudgetMigrationNoteEnabledDeprecated 提示旧 enabled 开关已废弃 。
2525 ContextBudgetMigrationNoteEnabledDeprecated = "旧 context.auto_compact.enabled 已废弃,新预算门禁不可关闭"
2626)
2727
@@ -35,7 +35,7 @@ func UpgradeConfigSchema(path string) (ContextBudgetMigrationResult, error) {
3535 return MigrateContextBudgetConfigFile (path , false )
3636}
3737
38- // MigrateContextBudgetConfigFile 将 config.yaml 中的 context.auto_compact 迁移到 context.budget 。
38+ // MigrateContextBudgetConfigFile 将 config.yaml 中的旧 schema 迁移到当前实现 。
3939func MigrateContextBudgetConfigFile (path string , dryRun bool ) (ContextBudgetMigrationResult , error ) {
4040 if path == "" {
4141 path = DefaultConfigPath ()
@@ -76,7 +76,7 @@ func MigrateContextBudgetConfigFile(path string, dryRun bool) (ContextBudgetMigr
7676 return result , nil
7777}
7878
79- // MigrateContextBudgetConfigContent 将旧预算 YAML 块替换为当前预算 YAML 块 ,并返回迁移说明。
79+ // MigrateContextBudgetConfigContent 将旧 YAML schema 迁移为当前 schema ,并返回迁移说明。
8080func MigrateContextBudgetConfigContent (raw []byte ) ([]byte , bool , []string , error ) {
8181 if len (bytes .TrimSpace (raw )) == 0 {
8282 return raw , false , nil , nil
@@ -127,6 +127,14 @@ func MigrateContextBudgetConfigContent(raw []byte) ([]byte, bool, []string, erro
127127 }
128128 }
129129
130+ verificationChanged , err := migrateVerificationConfig (doc )
131+ if err != nil {
132+ return nil , false , nil , err
133+ }
134+ if verificationChanged {
135+ changed = true
136+ }
137+
130138 if ! changed {
131139 return raw , false , nil , nil
132140 }
@@ -138,6 +146,116 @@ func MigrateContextBudgetConfigContent(raw []byte) ([]byte, bool, []string, erro
138146 return out , true , notes , nil
139147}
140148
149+ // migrateVerificationConfig 清理已废弃的 verification 字段,并将安全的旧 command string 收敛成 argv。
150+ func migrateVerificationConfig (doc map [string ]any ) (bool , error ) {
151+ runtimeValue , ok := doc ["runtime" ]
152+ if ! ok {
153+ return false , nil
154+ }
155+ runtimeMap , ok := migrationStringMap (runtimeValue )
156+ if ! ok {
157+ return false , nil
158+ }
159+ verificationValue , ok := runtimeMap ["verification" ]
160+ if ! ok {
161+ return false , nil
162+ }
163+ verificationMap , ok := migrationStringMap (verificationValue )
164+ if ! ok {
165+ return false , nil
166+ }
167+
168+ changed := false
169+ for _ , key := range []string {"enabled" , "default_task_policy" , "final_intercept" , "max_retries" , "hooks" } {
170+ if _ , exists := verificationMap [key ]; exists {
171+ delete (verificationMap , key )
172+ changed = true
173+ }
174+ }
175+
176+ verifiersValue , ok := verificationMap ["verifiers" ]
177+ if ok {
178+ verifiersMap , ok := migrationStringMap (verifiersValue )
179+ if ok {
180+ for name , rawVerifier := range verifiersMap {
181+ verifierMap , ok := migrationStringMap (rawVerifier )
182+ if ! ok {
183+ continue
184+ }
185+ for _ , key := range []string {"enabled" , "required" , "fail_open" , "fail_closed" } {
186+ if _ , exists := verifierMap [key ]; exists {
187+ delete (verifierMap , key )
188+ changed = true
189+ }
190+ }
191+ commandChanged , err := migrateVerifierCommandField (verifierMap )
192+ if err != nil {
193+ return false , err
194+ }
195+ if commandChanged {
196+ changed = true
197+ }
198+ verifiersMap [name ] = verifierMap
199+ }
200+ verificationMap ["verifiers" ] = verifiersMap
201+ }
202+ }
203+
204+ runtimeMap ["verification" ] = verificationMap
205+ doc ["runtime" ] = runtimeMap
206+ return changed , nil
207+ }
208+
209+ // migrateVerifierCommandField 将简单的旧 command string 迁移为 argv;含 shell 语义时直接报错。
210+ func migrateVerifierCommandField (verifierMap map [string ]any ) (bool , error ) {
211+ value , ok := verifierMap ["command" ]
212+ if ! ok {
213+ return false , nil
214+ }
215+ command , ok := value .(string )
216+ if ! ok {
217+ return false , nil
218+ }
219+ fields , err := parseLegacyVerificationCommand (command )
220+ if err != nil {
221+ return false , err
222+ }
223+ if len (fields ) == 0 {
224+ delete (verifierMap , "command" )
225+ return true , nil
226+ }
227+
228+ args := make ([]any , 0 , len (fields ))
229+ for _ , field := range fields {
230+ args = append (args , field )
231+ }
232+ verifierMap ["command" ] = args
233+ return true , nil
234+ }
235+
236+ // parseLegacyVerificationCommand 仅接受不含 shell 语义的简单空白分隔命令。
237+ func parseLegacyVerificationCommand (command string ) ([]string , error ) {
238+ trimmed := strings .TrimSpace (command )
239+ if trimmed == "" {
240+ return nil , nil
241+ }
242+ if containsUnsafeLegacyVerifierCommandSyntax (trimmed ) {
243+ return nil , errors .New ("runtime.verification.verifiers.command uses unsupported shell syntax; rewrite it as argv" )
244+ }
245+ return strings .Fields (trimmed ), nil
246+ }
247+
248+ // containsUnsafeLegacyVerifierCommandSyntax 判断旧命令是否包含无法安全自动迁移的 shell 结构。
249+ func containsUnsafeLegacyVerifierCommandSyntax (command string ) bool {
250+ unsafeTokens := []string {"'" , "\" " , "`" , "|" , "&&" , "||" , ";" , ">" , "<" , "$(" , "\n " , "\r " }
251+ for _ , token := range unsafeTokens {
252+ if strings .Contains (command , token ) {
253+ return true
254+ }
255+ }
256+ return false
257+ }
258+
141259// collectContextBudgetMigrationNotes 汇总迁移过程中需要提示给用户的行为变化说明。
142260func collectContextBudgetMigrationNotes (autoCompact map [string ]any ) []string {
143261 if value , ok := autoCompact ["enabled" ]; ok && migrationExplicitFalse (value ) {
0 commit comments