mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-10-31 02:01:44 +08:00
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.
156 lines
4.5 KiB
Go
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()
|
|
}
|