mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-18 07:32:20 +02:00
feat: add configurable tool call blocking and monitoring
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"cyberstrike-ai/internal/termout"
|
||||
"cyberstrike-ai/internal/toolguard"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -30,6 +31,7 @@ type Config struct {
|
||||
Shodan SpaceSearchConfig `yaml:"shodan,omitempty" json:"shodan,omitempty"`
|
||||
Agent AgentConfig `yaml:"agent"`
|
||||
Hitl HitlConfig `yaml:"hitl,omitempty" json:"hitl,omitempty"`
|
||||
ToolGuard *toolguard.Config `yaml:"tool_guard,omitempty" json:"tool_guard,omitempty"`
|
||||
Security SecurityConfig `yaml:"security"`
|
||||
Database DatabaseConfig `yaml:"database"`
|
||||
Auth AuthConfig `yaml:"auth"`
|
||||
@@ -1439,6 +1441,14 @@ func Load(path string) (*Config, error) {
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("解析配置文件失败: %w", err)
|
||||
}
|
||||
if cfg.ToolGuard != nil {
|
||||
if err := validateToolGuardYAML(data); err != nil {
|
||||
return nil, fmt.Errorf("调用拦截配置无效: %w", err)
|
||||
}
|
||||
}
|
||||
if _, err := toolguard.Compile(cfg.EffectiveToolGuard()); err != nil {
|
||||
return nil, fmt.Errorf("调用拦截配置无效: %w", err)
|
||||
}
|
||||
|
||||
if cfg.Auth.SessionDurationHours <= 0 {
|
||||
cfg.Auth.SessionDurationHours = 12
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"cyberstrike-ai/internal/toolguard"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// EffectiveToolGuard enables the default government-domain protection for old
|
||||
// configurations as well as new installs. An explicit config may disable it.
|
||||
func (c *Config) EffectiveToolGuard() toolguard.Config {
|
||||
if c.ToolGuard == nil {
|
||||
return toolguard.DefaultConfig()
|
||||
}
|
||||
return *c.ToolGuard
|
||||
}
|
||||
|
||||
// validateToolGuardYAML requires an explicit decision for both protection and
|
||||
// its rules whenever a non-null section is supplied. Otherwise a typo or partial
|
||||
// section could silently turn the enabled-by-default protection off. Pointer
|
||||
// fields distinguish false/[] from omitted or null values, and the YAML decoder
|
||||
// continues to support aliases and merged configuration mappings.
|
||||
func validateToolGuardYAML(data []byte) error {
|
||||
var document struct {
|
||||
ToolGuard *struct {
|
||||
Enabled *bool `yaml:"enabled"`
|
||||
Rules *[]toolguard.Rule `yaml:"rules"`
|
||||
} `yaml:"tool_guard"`
|
||||
}
|
||||
if err := yaml.Unmarshal(data, &document); err != nil {
|
||||
return err
|
||||
}
|
||||
if section := document.ToolGuard; section != nil && (section.Enabled == nil || section.Rules == nil) {
|
||||
return fmt.Errorf("tool_guard 必须明确提供 enabled 和 rules;清空规则请提供空数组")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadToolGuardDefaultsAndValidation(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name, yaml string
|
||||
enabled, wantErr bool
|
||||
}{
|
||||
{"legacy config", "server: {port: 8080}\n", true, false},
|
||||
{"null section", "tool_guard: null\n", true, false},
|
||||
{"implicit null section", "tool_guard:\n", true, false},
|
||||
{"explicit off", "tool_guard: {enabled: false, rules: []}\n", false, false},
|
||||
{"explicit empty", "tool_guard: {enabled: true, rules: []}\n", true, false},
|
||||
{"merged explicit config", "guard_defaults: &guard_defaults {enabled: false, rules: []}\ntool_guard: {<<: *guard_defaults}\n", false, false},
|
||||
{"empty section", "tool_guard: {}\n", false, true},
|
||||
{"missing enabled", "tool_guard: {rules: []}\n", false, true},
|
||||
{"null enabled", "tool_guard: {enabled: null, rules: []}\n", false, true},
|
||||
{"missing rules while off", "tool_guard: {enabled: false}\n", false, true},
|
||||
{"missing rules while on", "tool_guard: {enabled: true}\n", false, true},
|
||||
{"null rules", "tool_guard: {enabled: false, rules: null}\n", false, true},
|
||||
{"mistyped enabled field", "tool_guard: {enable: false, rules: []}\n", false, true},
|
||||
{"malformed rules while off", "tool_guard: {enabled: false, rules: disabled}\n", false, true},
|
||||
{"malformed rule while off", "tool_guard: {enabled: false, rules: [invalid]}\n", false, true},
|
||||
{"invalid pattern", "tool_guard:\n enabled: false\n rules:\n - {id: invalid, name: invalid, enabled: false, pattern: '['}\n", false, true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "config.yaml")
|
||||
if err := os.WriteFile(path, []byte(tc.yaml), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := Load(path)
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Fatalf("load error: %v", err)
|
||||
}
|
||||
if err == nil && cfg.EffectiveToolGuard().Enabled != tc.enabled {
|
||||
t.Fatal("wrong effective enabled state")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user