Files
SamWaf/plugin/init.go
T
samwaf 24227923e8 feat: strengthen WAF detection coverage and gate the new inspection paths
Detection engine:
- Normalize request data (body, cookies, form and JSON values, request headers)
  before inspection instead of only the URL query string, so encoded variants
  are evaluated consistently.
- Extend XSS / SQLi / command-injection inspection to request bodies and to
  custom request headers; bodies and headers are scanned per value rather than
  as one blob to keep false positives low.
- Add a configurable body inspection mode (body_detect_mode: observe/block/off,
  default observe) so the new deep checks record rather than block until an
  operator confirms them, plus body_detect_field_exclude to skip fields that
  legitimately carry rich text.
- Keep false positives down on the new paths: skip structured values, require a
  corroborating signal before flagging body/header XSS, and leave dual-use
  keywords out of the fallback lists.
- Path traversal is now normalized and judged on whether it escapes the site
  root instead of matching literally; this also clears a long-standing false
  positive on legitimate in-site relative paths. Adds a sensitive-file list.
- Scanner detection now covers all request headers and known probe paths.

Plugins:
- Suspend plugin loading pending signing and admission work. The code path is
  retained but gated off, and shipped disabled by default.

Validated against a local test corpus and a 33k real-traffic benchmark:
detection improved across every category while the real-traffic false-positive
count stayed flat.
2026-08-23 11:29:22 +08:00

156 lines
4.5 KiB
Go

package plugin
import (
"SamWaf/common/zlog"
"SamWaf/globalobj"
pluginconfig "SamWaf/plugin/config"
"SamWaf/plugin/manager"
"SamWaf/utils"
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
// InitPluginSystem 初始化插件系统
// 从 plugins.yml 配置文件加载插件配置(不使用数据库)
func InitPluginSystem() error {
zlog.Info("开始初始化插件系统...")
// 1. 读取插件配置文件
configPath := filepath.Join(utils.GetCurrentDir(), "conf", "plugins.yml")
systemConfig, err := loadPluginConfigFromFile(configPath)
if err != nil {
zlog.Error("加载插件配置文件失败", "path", configPath, "error", err)
// 配置文件加载失败不应该阻止系统启动,使用默认配置
zlog.Info("使用默认插件配置")
systemConfig = pluginconfig.DefaultPluginSystemConfig()
systemConfig.Enabled = false // 默认禁用
}
// 2. 创建插件管理器
pluginManager := manager.NewPluginManager(systemConfig)
globalobj.GWAF_RUNTIME_OBJ_PLUGIN_MANAGER = pluginManager
// 3. 待验证总闸:不加载任何插件二进制,只保留管理器空壳让上层判空逻辑保持一致
if manager.PendingVerification {
configuredCount := 0
for _, pluginConfig := range systemConfig.List {
if pluginConfig.Enabled {
configuredCount++
}
}
zlog.Warn(manager.PendingVerificationReason,
"配置文件中已启用但被跳过的插件数", configuredCount,
"恢复条件", "完成准备后,将 manager.PendingVerification 置为 false")
return nil
}
// 4. 如果插件系统未启用,直接返回
if !systemConfig.Enabled {
zlog.Info("插件系统未启用,跳过插件加载")
return nil
}
// 5. 加载每个启用的插件
loadedCount := 0
for _, pluginConfig := range systemConfig.List {
if !pluginConfig.Enabled {
zlog.Debug("插件未启用,跳过加载", "plugin_id", pluginConfig.ID, "plugin_name", pluginConfig.Name)
continue
}
zlog.Info("加载插件", "plugin_id", pluginConfig.ID, "plugin_name", pluginConfig.Name)
if err := pluginManager.LoadPlugin(&pluginConfig); err != nil {
zlog.Error("加载插件失败", "plugin_id", pluginConfig.ID, "error", err)
continue
}
loadedCount++
zlog.Info("插件加载成功", "plugin_id", pluginConfig.ID, "plugin_name", pluginConfig.Name)
}
zlog.Info("插件系统初始化完成", "总插件数", len(systemConfig.List), "已加载", loadedCount)
return nil
}
// loadPluginConfigFromFile 从 YAML 配置文件加载插件配置
func loadPluginConfigFromFile(configPath string) (*pluginconfig.PluginSystemConfig, error) {
// 检查文件是否存在
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return nil, fmt.Errorf("配置文件不存在: %s", configPath)
}
// 读取文件内容
data, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("读取配置文件失败: %w", err)
}
// 定义配置文件结构
type ConfigFile struct {
Plugins pluginconfig.PluginSystemConfig `yaml:"plugins"`
}
var configFile ConfigFile
if err := yaml.Unmarshal(data, &configFile); err != nil {
return nil, fmt.Errorf("解析配置文件失败: %w", err)
}
zlog.Info("成功加载插件配置文件",
"enabled", configFile.Plugins.Enabled,
"plugin_count", len(configFile.Plugins.List))
return &configFile.Plugins, nil
}
// SavePluginConfigToFile 保存插件配置到文件
// 供 API 使用,用于在线修改配置
func SavePluginConfigToFile(systemConfig *pluginconfig.PluginSystemConfig) error {
configPath := filepath.Join(utils.GetCurrentDir(), "conf", "plugins.yml")
// 构造配置文件结构
type ConfigFile struct {
Plugins pluginconfig.PluginSystemConfig `yaml:"plugins"`
}
configFile := ConfigFile{
Plugins: *systemConfig,
}
// 序列化为 YAML
data, err := yaml.Marshal(&configFile)
if err != nil {
return fmt.Errorf("序列化配置失败: %w", err)
}
// 备份原配置文件
backupPath := configPath + ".backup"
if _, err := os.Stat(configPath); err == nil {
os.Rename(configPath, backupPath)
}
// 写入文件
if err := os.WriteFile(configPath, data, 0644); err != nil {
// 如果写入失败,恢复备份
if _, err := os.Stat(backupPath); err == nil {
os.Rename(backupPath, configPath)
}
return fmt.Errorf("写入配置文件失败: %w", err)
}
zlog.Info("插件配置已保存", "path", configPath)
return nil
}
// ShutdownPluginSystem 关闭插件系统
func ShutdownPluginSystem() error {
if globalobj.GWAF_RUNTIME_OBJ_PLUGIN_MANAGER == nil {
return nil
}
zlog.Info("关闭插件系统...")
return globalobj.GWAF_RUNTIME_OBJ_PLUGIN_MANAGER.Shutdown()
}