feat(安全): 添加安全开关并完善测试流程

实现安全开关默认关闭与响应头透传逻辑
- URL 校验与响应头过滤支持开关并覆盖流式路径
- 非流式 Content-Type 透传/默认值按配置生效
- 接入 go test、golangci-lint 与前端 lint/typecheck
- 补充相关测试与配置/文档说明
This commit is contained in:
yangjianbo
2026-01-05 13:54:43 +08:00
parent c8e5455df0
commit 794a9f969b
24 changed files with 1811 additions and 14 deletions

View File

@@ -126,6 +126,7 @@ type SecurityConfig struct {
}
type URLAllowlistConfig struct {
Enabled bool `mapstructure:"enabled"`
UpstreamHosts []string `mapstructure:"upstream_hosts"`
PricingHosts []string `mapstructure:"pricing_hosts"`
CRSHosts []string `mapstructure:"crs_hosts"`
@@ -133,6 +134,7 @@ type URLAllowlistConfig struct {
}
type ResponseHeaderConfig struct {
Enabled bool `mapstructure:"enabled"`
AdditionalAllowed []string `mapstructure:"additional_allowed"`
ForceRemove []string `mapstructure:"force_remove"`
}
@@ -381,6 +383,13 @@ func Load() (*Config, error) {
return nil, fmt.Errorf("validate config error: %w", err)
}
if !cfg.Security.URLAllowlist.Enabled {
log.Println("Warning: security.url_allowlist.enabled=false; URL validation is disabled.")
}
if !cfg.Security.ResponseHeaders.Enabled {
log.Println("Warning: security.response_headers.enabled=false; response header filtering is disabled.")
}
if cfg.Server.Mode != "release" && cfg.JWT.Secret != "" && isWeakJWTSecret(cfg.JWT.Secret) {
log.Println("Warning: JWT secret appears weak; use a 32+ character random secret in production.")
}
@@ -410,6 +419,7 @@ func setDefaults() {
viper.SetDefault("cors.allow_credentials", true)
// Security
viper.SetDefault("security.url_allowlist.enabled", false)
viper.SetDefault("security.url_allowlist.upstream_hosts", []string{
"api.openai.com",
"api.anthropic.com",
@@ -425,6 +435,7 @@ func setDefaults() {
})
viper.SetDefault("security.url_allowlist.crs_hosts", []string{})
viper.SetDefault("security.url_allowlist.allow_private_hosts", false)
viper.SetDefault("security.response_headers.enabled", false)
viper.SetDefault("security.response_headers.additional_allowed", []string{})
viper.SetDefault("security.response_headers.force_remove", []string{})
viper.SetDefault("security.csp.enabled", true)

View File

@@ -68,3 +68,19 @@ func TestLoadSchedulingConfigFromEnv(t *testing.T) {
t.Fatalf("StickySessionMaxWaiting = %d, want 5", cfg.Gateway.Scheduling.StickySessionMaxWaiting)
}
}
func TestLoadDefaultSecurityToggles(t *testing.T) {
viper.Reset()
cfg, err := Load()
if err != nil {
t.Fatalf("Load() error: %v", err)
}
if cfg.Security.URLAllowlist.Enabled {
t.Fatalf("URLAllowlist.Enabled = true, want false")
}
if cfg.Security.ResponseHeaders.Enabled {
t.Fatalf("ResponseHeaders.Enabled = true, want false")
}
}