mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00
refactor: 重构common包错误消息系统并优化常量管理
主要改进: - 将output和parsers包中的硬编码错误消息迁移到i18n国际化系统 - 修复constants.go中未引用常量的使用问题 - 消除所有硬编码字符串、魔术数字和协议标识符 - 清理死代码常量MinIPv4OctetValue 具体变更: * output包: 删除19个硬编码错误消息,新增13个i18n消息键 * parsers包: 删除24个硬编码错误模板,新增22个i18n消息键 * 修复8个未引用的Default常量在对应默认选项函数中的使用 * 替换11处网络协议相关的硬编码值为常量引用 * 更新6个错误类型常量和验证逻辑的硬编码使用 * 修复2处IPv4八位组计数的硬编码为常量引用 提升效果: - 支持中英文错误消息国际化切换 - 统一常量管理,提高代码可维护性 - 消除代码重复,符合DRY原则 - 清理死代码,优化代码质量 涉及文件: 11个文件,新增210行,删除103行
This commit is contained in:
parent
84b0bb1e28
commit
20cb3356de
@ -170,6 +170,58 @@ var coreMessages = map[string]map[string]string{
|
|||||||
LangZH: "关闭输出系统失败: %v",
|
LangZH: "关闭输出系统失败: %v",
|
||||||
LangEN: "Failed to close output system: %v",
|
LangEN: "Failed to close output system: %v",
|
||||||
},
|
},
|
||||||
|
"output_config_nil": {
|
||||||
|
LangZH: "配置不能为空",
|
||||||
|
LangEN: "Configuration cannot be nil",
|
||||||
|
},
|
||||||
|
"output_unsupported_format": {
|
||||||
|
LangZH: "不支持的输出格式: %s",
|
||||||
|
LangEN: "Unsupported output format: %s",
|
||||||
|
},
|
||||||
|
"output_writer_init_failed": {
|
||||||
|
LangZH: "初始化写入器失败: %v",
|
||||||
|
LangEN: "Failed to initialize writer: %v",
|
||||||
|
},
|
||||||
|
"output_writer_closed": {
|
||||||
|
LangZH: "写入器已关闭",
|
||||||
|
LangEN: "Writer is closed",
|
||||||
|
},
|
||||||
|
"output_manager_not_init": {
|
||||||
|
LangZH: "输出管理器未初始化",
|
||||||
|
LangEN: "Output manager not initialized",
|
||||||
|
},
|
||||||
|
"output_manager_closed": {
|
||||||
|
LangZH: "输出管理器已关闭",
|
||||||
|
LangEN: "Output manager is closed",
|
||||||
|
},
|
||||||
|
"output_write_failed": {
|
||||||
|
LangZH: "写入结果失败: %v",
|
||||||
|
LangEN: "Failed to write result: %v",
|
||||||
|
},
|
||||||
|
"output_flush_failed": {
|
||||||
|
LangZH: "刷新写入器失败: %v",
|
||||||
|
LangEN: "Failed to flush writer: %v",
|
||||||
|
},
|
||||||
|
"output_create_file_failed": {
|
||||||
|
LangZH: "创建%s文件失败: %v",
|
||||||
|
LangEN: "Failed to create %s file: %v",
|
||||||
|
},
|
||||||
|
"output_write_header_failed": {
|
||||||
|
LangZH: "写入CSV头部失败: %v",
|
||||||
|
LangEN: "Failed to write CSV header: %v",
|
||||||
|
},
|
||||||
|
"output_open_file_failed": {
|
||||||
|
LangZH: "打开CSV文件失败: %v",
|
||||||
|
LangEN: "Failed to open CSV file: %v",
|
||||||
|
},
|
||||||
|
"output_read_file_failed": {
|
||||||
|
LangZH: "读取CSV文件失败: %v",
|
||||||
|
LangEN: "Failed to read CSV file: %v",
|
||||||
|
},
|
||||||
|
"output_parse_time_failed": {
|
||||||
|
LangZH: "无法解析时间: %s",
|
||||||
|
LangEN: "Failed to parse time: %s",
|
||||||
|
},
|
||||||
|
|
||||||
// ========================= 代理系统消息 =========================
|
// ========================= 代理系统消息 =========================
|
||||||
"proxy_init_start": {
|
"proxy_init_start": {
|
||||||
@ -410,6 +462,94 @@ var coreMessages = map[string]map[string]string{
|
|||||||
LangZH: "验证输入为空",
|
LangZH: "验证输入为空",
|
||||||
LangEN: "Validation input is empty",
|
LangEN: "Validation input is empty",
|
||||||
},
|
},
|
||||||
|
"parser_empty_input": {
|
||||||
|
LangZH: "输入参数为空",
|
||||||
|
LangEN: "Input parameters are empty",
|
||||||
|
},
|
||||||
|
"parser_file_empty": {
|
||||||
|
LangZH: "文件名为空",
|
||||||
|
LangEN: "File name is empty",
|
||||||
|
},
|
||||||
|
"parser_file_too_big": {
|
||||||
|
LangZH: "文件过大: %d bytes, 最大限制: %d bytes",
|
||||||
|
LangEN: "File too large: %d bytes, max limit: %d bytes",
|
||||||
|
},
|
||||||
|
"parser_cannot_open_file": {
|
||||||
|
LangZH: "无法打开文件",
|
||||||
|
LangEN: "Cannot open file",
|
||||||
|
},
|
||||||
|
"parser_file_not_exists": {
|
||||||
|
LangZH: "文件不存在或无法访问",
|
||||||
|
LangEN: "File does not exist or cannot be accessed",
|
||||||
|
},
|
||||||
|
"parser_file_read_timeout": {
|
||||||
|
LangZH: "文件读取超时",
|
||||||
|
LangEN: "File read timeout",
|
||||||
|
},
|
||||||
|
"parser_file_scan_failed": {
|
||||||
|
LangZH: "文件扫描失败",
|
||||||
|
LangEN: "File scan failed",
|
||||||
|
},
|
||||||
|
"parser_username_too_long": {
|
||||||
|
LangZH: "用户名过长: %d字符,最大允许: %d",
|
||||||
|
LangEN: "Username too long: %d characters, max allowed: %d",
|
||||||
|
},
|
||||||
|
"parser_username_invalid_chars": {
|
||||||
|
LangZH: "用户名包含非法字符",
|
||||||
|
LangEN: "Username contains invalid characters",
|
||||||
|
},
|
||||||
|
"parser_password_empty": {
|
||||||
|
LangZH: "不允许空密码",
|
||||||
|
LangEN: "Empty passwords not allowed",
|
||||||
|
},
|
||||||
|
"parser_password_too_long": {
|
||||||
|
LangZH: "密码过长: %d字符,最大允许: %d",
|
||||||
|
LangEN: "Password too long: %d characters, max allowed: %d",
|
||||||
|
},
|
||||||
|
"parser_hash_empty": {
|
||||||
|
LangZH: "哈希值为空",
|
||||||
|
LangEN: "Hash value is empty",
|
||||||
|
},
|
||||||
|
"parser_hash_invalid_format": {
|
||||||
|
LangZH: "哈希值格式无效,需要32位十六进制字符",
|
||||||
|
LangEN: "Invalid hash format, requires 32-character hexadecimal",
|
||||||
|
},
|
||||||
|
"parser_proxy_url_invalid": {
|
||||||
|
LangZH: "代理URL格式无效: %v",
|
||||||
|
LangEN: "Invalid proxy URL format: %v",
|
||||||
|
},
|
||||||
|
"parser_proxy_protocol_unsupported": {
|
||||||
|
LangZH: "不支持的代理协议: %s",
|
||||||
|
LangEN: "Unsupported proxy protocol: %s",
|
||||||
|
},
|
||||||
|
"parser_proxy_host_empty": {
|
||||||
|
LangZH: "代理主机名为空",
|
||||||
|
LangEN: "Proxy hostname is empty",
|
||||||
|
},
|
||||||
|
"parser_proxy_port_invalid": {
|
||||||
|
LangZH: "代理端口号无效: %s",
|
||||||
|
LangEN: "Invalid proxy port: %s",
|
||||||
|
},
|
||||||
|
"parser_proxy_port_out_of_range": {
|
||||||
|
LangZH: "代理端口号超出范围: %d",
|
||||||
|
LangEN: "Proxy port out of range: %d",
|
||||||
|
},
|
||||||
|
"parser_proxy_insecure": {
|
||||||
|
LangZH: "不允许使用不安全的HTTP代理",
|
||||||
|
LangEN: "Insecure HTTP proxy not allowed",
|
||||||
|
},
|
||||||
|
"parser_user_agent_too_long": {
|
||||||
|
LangZH: "用户代理字符串过长",
|
||||||
|
LangEN: "User agent string too long",
|
||||||
|
},
|
||||||
|
"parser_user_agent_invalid_chars": {
|
||||||
|
LangZH: "用户代理包含非法字符",
|
||||||
|
LangEN: "User agent contains invalid characters",
|
||||||
|
},
|
||||||
|
"parser_cookie_too_long": {
|
||||||
|
LangZH: "Cookie字符串过长",
|
||||||
|
LangEN: "Cookie string too long",
|
||||||
|
},
|
||||||
"parser_error_count_limit": {
|
"parser_error_count_limit": {
|
||||||
LangZH: "错误数量过多,仅显示前%d个",
|
LangZH: "错误数量过多,仅显示前%d个",
|
||||||
LangEN: "Too many errors, showing only first %d",
|
LangEN: "Too many errors, showing only first %d",
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/shadow1ng/fscan/common/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manager 输出管理器
|
// Manager 输出管理器
|
||||||
@ -26,7 +28,7 @@ type Manager struct {
|
|||||||
// NewManager 创建新的输出管理器
|
// NewManager 创建新的输出管理器
|
||||||
func NewManager(config *ManagerConfig) (*Manager, error) {
|
func NewManager(config *ManagerConfig) (*Manager, error) {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return nil, fmt.Errorf(ErrConfigNil)
|
return nil, fmt.Errorf(i18n.GetText("output_config_nil"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证输出格式
|
// 验证输出格式
|
||||||
@ -73,7 +75,7 @@ func validateFormat(format OutputFormat) error {
|
|||||||
case FormatTXT, FormatJSON, FormatCSV:
|
case FormatTXT, FormatJSON, FormatCSV:
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(ErrUnsupportedFormat, format)
|
return fmt.Errorf(i18n.GetText("output_unsupported_format"), format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,11 +98,11 @@ func (m *Manager) initializeWriter() error {
|
|||||||
case FormatCSV:
|
case FormatCSV:
|
||||||
writer, err = NewCSVWriter(m.config.OutputPath)
|
writer, err = NewCSVWriter(m.config.OutputPath)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(ErrUnsupportedFormat, m.config.Format)
|
return fmt.Errorf(i18n.GetText("output_unsupported_format"), m.config.Format)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(ErrWriterInitFailed, err)
|
return fmt.Errorf(i18n.GetText("output_writer_init_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.writer = writer
|
m.writer = writer
|
||||||
@ -138,11 +140,11 @@ func (m *Manager) SaveResult(result *ScanResult) error {
|
|||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
|
|
||||||
if !m.initialized {
|
if !m.initialized {
|
||||||
return fmt.Errorf(ErrManagerNotInit)
|
return fmt.Errorf(i18n.GetText("output_manager_not_init"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.closed {
|
if m.closed {
|
||||||
return fmt.Errorf(ErrManagerClosed)
|
return fmt.Errorf(i18n.GetText("output_manager_closed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新统计信息
|
// 更新统计信息
|
||||||
@ -188,13 +190,13 @@ func (m *Manager) flushBufferUnsafe() error {
|
|||||||
// 批量写入
|
// 批量写入
|
||||||
for _, result := range m.buffer {
|
for _, result := range m.buffer {
|
||||||
if err := m.writer.Write(result); err != nil {
|
if err := m.writer.Write(result); err != nil {
|
||||||
return fmt.Errorf(ErrWriteFailed, err)
|
return fmt.Errorf(i18n.GetText("output_write_failed"), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新写入器
|
// 刷新写入器
|
||||||
if err := m.writer.Flush(); err != nil {
|
if err := m.writer.Flush(); err != nil {
|
||||||
return fmt.Errorf(ErrFlushFailed, err)
|
return fmt.Errorf(i18n.GetText("output_flush_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空缓冲区
|
// 清空缓冲区
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/shadow1ng/fscan/common/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TXTWriter 文本格式写入器
|
// TXTWriter 文本格式写入器
|
||||||
@ -21,7 +23,7 @@ type TXTWriter struct {
|
|||||||
func NewTXTWriter(filePath string) (*TXTWriter, error) {
|
func NewTXTWriter(filePath string) (*TXTWriter, error) {
|
||||||
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
|
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(ErrCreateFileFailed, "文本", err)
|
return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "文本", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &TXTWriter{
|
return &TXTWriter{
|
||||||
@ -40,7 +42,7 @@ func (w *TXTWriter) Write(result *ScanResult) error {
|
|||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
if w.closed {
|
if w.closed {
|
||||||
return fmt.Errorf(ErrWriterClosed)
|
return fmt.Errorf(i18n.GetText("output_writer_closed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化 Details 为键值对字符串
|
// 格式化 Details 为键值对字符串
|
||||||
@ -111,7 +113,7 @@ type JSONWriter struct {
|
|||||||
func NewJSONWriter(filePath string) (*JSONWriter, error) {
|
func NewJSONWriter(filePath string) (*JSONWriter, error) {
|
||||||
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
|
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(ErrCreateFileFailed, "JSON", err)
|
return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "JSON", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder := json.NewEncoder(file)
|
encoder := json.NewEncoder(file)
|
||||||
@ -134,7 +136,7 @@ func (w *JSONWriter) Write(result *ScanResult) error {
|
|||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
if w.closed {
|
if w.closed {
|
||||||
return fmt.Errorf(ErrWriterClosed)
|
return fmt.Errorf(i18n.GetText("output_writer_closed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.encoder.Encode(result)
|
return w.encoder.Encode(result)
|
||||||
@ -183,7 +185,7 @@ type CSVWriter struct {
|
|||||||
func NewCSVWriter(filePath string) (*CSVWriter, error) {
|
func NewCSVWriter(filePath string) (*CSVWriter, error) {
|
||||||
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
|
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(ErrCreateFileFailed, "CSV", err)
|
return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "CSV", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
csvWriter := csv.NewWriter(file)
|
csvWriter := csv.NewWriter(file)
|
||||||
@ -206,7 +208,7 @@ func (w *CSVWriter) WriteHeader() error {
|
|||||||
headers := CSVHeaders
|
headers := CSVHeaders
|
||||||
err := w.csvWriter.Write(headers)
|
err := w.csvWriter.Write(headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(ErrWriteHeaderFailed, err)
|
return fmt.Errorf(i18n.GetText("output_write_header_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.csvWriter.Flush()
|
w.csvWriter.Flush()
|
||||||
@ -220,7 +222,7 @@ func (w *CSVWriter) Write(result *ScanResult) error {
|
|||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
if w.closed {
|
if w.closed {
|
||||||
return fmt.Errorf(ErrWriterClosed)
|
return fmt.Errorf(i18n.GetText("output_writer_closed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保头部已写入
|
// 确保头部已写入
|
||||||
@ -262,7 +264,7 @@ func (w *CSVWriter) writeHeaderUnsafe() error {
|
|||||||
headers := CSVHeaders
|
headers := CSVHeaders
|
||||||
err := w.csvWriter.Write(headers)
|
err := w.csvWriter.Write(headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(ErrWriteHeaderFailed, err)
|
return fmt.Errorf(i18n.GetText("output_write_header_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.csvWriter.Flush()
|
w.csvWriter.Flush()
|
||||||
@ -333,14 +335,14 @@ func (r *CSVReader) ReadWithFilter(filter *ResultFilter) ([]*ScanResult, error)
|
|||||||
|
|
||||||
file, err := os.Open(r.filePath)
|
file, err := os.Open(r.filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(ErrOpenFileFailed, err)
|
return nil, fmt.Errorf(i18n.GetText("output_open_file_failed"), err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
reader := csv.NewReader(file)
|
reader := csv.NewReader(file)
|
||||||
records, err := reader.ReadAll()
|
records, err := reader.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(ErrReadFileFailed, err)
|
return nil, fmt.Errorf(i18n.GetText("output_read_file_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var results []*ScanResult
|
var results []*ScanResult
|
||||||
@ -453,5 +455,5 @@ func parseTime(timeStr string) (time.Time, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Time{}, fmt.Errorf(ErrParseTimeFailed, timeStr)
|
return time.Time{}, fmt.Errorf(i18n.GetText("output_parse_time_failed"), timeStr)
|
||||||
}
|
}
|
@ -113,25 +113,6 @@ const (
|
|||||||
TxtKeyValueFormat = "%s=%v" // 键值对格式
|
TxtKeyValueFormat = "%s=%v" // 键值对格式
|
||||||
)
|
)
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 错误消息常量
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrConfigNil = "配置不能为空"
|
|
||||||
ErrUnsupportedFormat = "不支持的输出格式: %s"
|
|
||||||
ErrWriterInitFailed = "初始化写入器失败: %v"
|
|
||||||
ErrWriterClosed = "写入器已关闭"
|
|
||||||
ErrManagerNotInit = "输出管理器未初始化"
|
|
||||||
ErrManagerClosed = "输出管理器已关闭"
|
|
||||||
ErrWriteFailed = "写入结果失败: %v"
|
|
||||||
ErrFlushFailed = "刷新写入器失败: %v"
|
|
||||||
ErrCreateFileFailed = "创建%s文件失败: %v"
|
|
||||||
ErrWriteHeaderFailed = "写入CSV头部失败: %v"
|
|
||||||
ErrOpenFileFailed = "打开CSV文件失败: %v"
|
|
||||||
ErrReadFileFailed = "读取CSV文件失败: %v"
|
|
||||||
ErrParseTimeFailed = "无法解析时间: %s"
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// CSV相关常量
|
// CSV相关常量
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/shadow1ng/fscan/common/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CredentialParser 凭据解析器
|
// CredentialParser 凭据解析器
|
||||||
@ -288,12 +290,12 @@ func (cp *CredentialParser) validateUsername(username string) (string, bool, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(username) > cp.options.MaxUsernameLength {
|
if len(username) > cp.options.MaxUsernameLength {
|
||||||
return "", false, fmt.Errorf(ErrUsernameTooLongMsg, len(username), cp.options.MaxUsernameLength)
|
return "", false, fmt.Errorf(i18n.GetText("parser_username_too_long"), len(username), cp.options.MaxUsernameLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查特殊字符
|
// 检查特殊字符
|
||||||
if strings.ContainsAny(username, InvalidUsernameChars) {
|
if strings.ContainsAny(username, InvalidUsernameChars) {
|
||||||
return "", false, fmt.Errorf("%s", ErrUsernameInvalidCharsMsg)
|
return "", false, fmt.Errorf("%s", i18n.GetText("parser_username_invalid_chars"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return username, true, nil
|
return username, true, nil
|
||||||
@ -302,11 +304,11 @@ func (cp *CredentialParser) validateUsername(username string) (string, bool, err
|
|||||||
// validatePassword 验证密码
|
// validatePassword 验证密码
|
||||||
func (cp *CredentialParser) validatePassword(password string) (string, bool, error) {
|
func (cp *CredentialParser) validatePassword(password string) (string, bool, error) {
|
||||||
if len(password) == 0 && !cp.options.AllowEmptyPasswords {
|
if len(password) == 0 && !cp.options.AllowEmptyPasswords {
|
||||||
return "", false, fmt.Errorf("%s", ErrPasswordEmptyMsg)
|
return "", false, fmt.Errorf("%s", i18n.GetText("parser_password_empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(password) > cp.options.MaxPasswordLength {
|
if len(password) > cp.options.MaxPasswordLength {
|
||||||
return "", false, fmt.Errorf(ErrPasswordTooLongMsg, len(password), cp.options.MaxPasswordLength)
|
return "", false, fmt.Errorf(i18n.GetText("parser_password_too_long"), len(password), cp.options.MaxPasswordLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
return password, true, nil
|
return password, true, nil
|
||||||
@ -320,11 +322,11 @@ func (cp *CredentialParser) validateHash(hash string) (bool, error) {
|
|||||||
|
|
||||||
hash = strings.TrimSpace(hash)
|
hash = strings.TrimSpace(hash)
|
||||||
if len(hash) == 0 {
|
if len(hash) == 0 {
|
||||||
return false, fmt.Errorf("%s", ErrHashEmptyMsg)
|
return false, fmt.Errorf("%s", i18n.GetText("parser_hash_empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cp.hashRegex.MatchString(hash) {
|
if !cp.hashRegex.MatchString(hash) {
|
||||||
return false, fmt.Errorf("%s", ErrHashInvalidFormatMsg)
|
return false, fmt.Errorf("%s", i18n.GetText("parser_hash_invalid_format"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/shadow1ng/fscan/common/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileReader 高性能文件读取器
|
// FileReader 高性能文件读取器
|
||||||
@ -62,14 +64,14 @@ type FileReaderOptions struct {
|
|||||||
// DefaultFileReaderOptions 默认文件读取器选项
|
// DefaultFileReaderOptions 默认文件读取器选项
|
||||||
func DefaultFileReaderOptions() *FileReaderOptions {
|
func DefaultFileReaderOptions() *FileReaderOptions {
|
||||||
return &FileReaderOptions{
|
return &FileReaderOptions{
|
||||||
MaxCacheSize: 10,
|
MaxCacheSize: DefaultMaxCacheSize,
|
||||||
EnableCache: true,
|
EnableCache: DefaultEnableCache,
|
||||||
MaxFileSize: 50 * 1024 * 1024, // 50MB
|
MaxFileSize: DefaultFileReaderMaxFileSize,
|
||||||
Timeout: 30 * time.Second,
|
Timeout: DefaultFileReaderTimeout,
|
||||||
EnableValidation: true,
|
EnableValidation: DefaultFileReaderEnableValidation,
|
||||||
TrimSpace: true,
|
TrimSpace: DefaultTrimSpace,
|
||||||
SkipEmpty: true,
|
SkipEmpty: DefaultSkipEmpty,
|
||||||
SkipComments: true,
|
SkipComments: DefaultSkipComments,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +120,7 @@ func (fr *FileReader) ReadFile(filename string, options ...*FileReaderOptions) (
|
|||||||
case err := <-errorChan:
|
case err := <-errorChan:
|
||||||
return nil, err
|
return nil, err
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, NewParseError("TIMEOUT", "文件读取超时", filename, 0, ctx.Err())
|
return nil, NewParseError(ErrorTypeTimeout, "文件读取超时", filename, 0, ctx.Err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +182,7 @@ func (fr *FileReader) readFileSync(filename string, options *FileReaderOptions)
|
|||||||
|
|
||||||
// 检查扫描错误
|
// 检查扫描错误
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
return nil, NewParseError(ErrorTypeReadError, ErrFileScanFailedMsg, filename, lineNum, err)
|
return nil, NewParseError(ErrorTypeReadError, i18n.GetText("parser_file_scan_failed"), filename, lineNum, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新统计信息
|
// 更新统计信息
|
||||||
|
@ -29,11 +29,11 @@ type NetworkParserOptions struct {
|
|||||||
// DefaultNetworkParserOptions 默认网络解析器选项
|
// DefaultNetworkParserOptions 默认网络解析器选项
|
||||||
func DefaultNetworkParserOptions() *NetworkParserOptions {
|
func DefaultNetworkParserOptions() *NetworkParserOptions {
|
||||||
return &NetworkParserOptions{
|
return &NetworkParserOptions{
|
||||||
ValidateProxies: true,
|
ValidateProxies: DefaultValidateProxies,
|
||||||
AllowInsecure: false,
|
AllowInsecure: DefaultAllowInsecure,
|
||||||
DefaultTimeout: 30 * time.Second,
|
DefaultTimeout: DefaultNetworkTimeout,
|
||||||
DefaultWebTimeout: 10 * time.Second,
|
DefaultWebTimeout: DefaultWebTimeout,
|
||||||
DefaultUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
|
DefaultUserAgent: DefaultUserAgent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ func (np *NetworkParser) parseHttpProxy(proxyStr string) (string, []error, []str
|
|||||||
// 验证代理URL
|
// 验证代理URL
|
||||||
if np.options.ValidateProxies {
|
if np.options.ValidateProxies {
|
||||||
if err := np.validateProxyURL(normalizedProxy); err != nil {
|
if err := np.validateProxyURL(normalizedProxy); err != nil {
|
||||||
errors = append(errors, NewParseError("PROXY_ERROR", err.Error(), "http_proxy", 0, err))
|
errors = append(errors, NewParseError(ErrorTypeProxyError, err.Error(), "http_proxy", 0, err))
|
||||||
return "", errors, warnings
|
return "", errors, warnings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ func (np *NetworkParser) parseSocks5Proxy(proxyStr string) (string, []error, []s
|
|||||||
// 验证代理URL
|
// 验证代理URL
|
||||||
if np.options.ValidateProxies {
|
if np.options.ValidateProxies {
|
||||||
if err := np.validateProxyURL(normalizedProxy); err != nil {
|
if err := np.validateProxyURL(normalizedProxy); err != nil {
|
||||||
errors = append(errors, NewParseError("PROXY_ERROR", err.Error(), "socks5_proxy", 0, err))
|
errors = append(errors, NewParseError(ErrorTypeProxyError, err.Error(), "socks5_proxy", 0, err))
|
||||||
return "", errors, warnings
|
return "", errors, warnings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,14 +224,14 @@ func (np *NetworkParser) parseUserAgent(userAgent string) (string, []error, []st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 基本格式验证
|
// 基本格式验证
|
||||||
if len(userAgent) > 512 {
|
if len(userAgent) > MaxUserAgentLength {
|
||||||
errors = append(errors, NewParseError("USERAGENT_ERROR", "用户代理字符串过长", "user_agent", 0, nil))
|
errors = append(errors, NewParseError(ErrorTypeUserAgentError, "用户代理字符串过长", "user_agent", 0, nil))
|
||||||
return "", errors, warnings
|
return "", errors, warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否包含特殊字符
|
// 检查是否包含特殊字符
|
||||||
if strings.ContainsAny(userAgent, "\r\n\t") {
|
if strings.ContainsAny(userAgent, InvalidUserAgentChars) {
|
||||||
errors = append(errors, NewParseError("USERAGENT_ERROR", "用户代理包含非法字符", "user_agent", 0, nil))
|
errors = append(errors, NewParseError(ErrorTypeUserAgentError, "用户代理包含非法字符", "user_agent", 0, nil))
|
||||||
return "", errors, warnings
|
return "", errors, warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,8 +253,8 @@ func (np *NetworkParser) parseCookie(cookie string) (string, []error, []string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 基本格式验证
|
// 基本格式验证
|
||||||
if len(cookie) > 4096 { // HTTP Cookie长度限制
|
if len(cookie) > MaxCookieLength { // HTTP Cookie长度限制
|
||||||
errors = append(errors, NewParseError("COOKIE_ERROR", "Cookie字符串过长", "cookie", 0, nil))
|
errors = append(errors, NewParseError(ErrorTypeCookieError, "Cookie字符串过长", "cookie", 0, nil))
|
||||||
return "", errors, warnings
|
return "", errors, warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,11 +275,11 @@ func (np *NetworkParser) normalizeHttpProxy(proxy string) string {
|
|||||||
return ProxyShortcutSOCKS5
|
return ProxyShortcutSOCKS5
|
||||||
default:
|
default:
|
||||||
// 如果没有协议前缀,默认使用HTTP
|
// 如果没有协议前缀,默认使用HTTP
|
||||||
if !strings.Contains(proxy, "://") {
|
if !strings.Contains(proxy, ProtocolPrefix) {
|
||||||
if strings.Contains(proxy, ":") {
|
if strings.Contains(proxy, ":") {
|
||||||
return "http://" + proxy
|
return HTTPPrefix + proxy
|
||||||
} else {
|
} else {
|
||||||
return "http://127.0.0.1:" + proxy
|
return HTTPPrefix + "127.0.0.1:" + proxy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return proxy
|
return proxy
|
||||||
@ -288,12 +288,12 @@ func (np *NetworkParser) normalizeHttpProxy(proxy string) string {
|
|||||||
|
|
||||||
// normalizeSocks5Proxy 规范化Socks5代理URL
|
// normalizeSocks5Proxy 规范化Socks5代理URL
|
||||||
func (np *NetworkParser) normalizeSocks5Proxy(proxy string) string {
|
func (np *NetworkParser) normalizeSocks5Proxy(proxy string) string {
|
||||||
// 如果没有协议前缀,添加socks5://
|
// 如果没有协议前缀,添加SOCKS5协议
|
||||||
if !strings.HasPrefix(proxy, "socks5://") {
|
if !strings.HasPrefix(proxy, SOCKS5Prefix) {
|
||||||
if strings.Contains(proxy, ":") {
|
if strings.Contains(proxy, ":") {
|
||||||
return "socks5://" + proxy
|
return SOCKS5Prefix + proxy
|
||||||
} else {
|
} else {
|
||||||
return "socks5://127.0.0.1:" + proxy
|
return SOCKS5Prefix + "127.0.0.1:" + proxy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return proxy
|
return proxy
|
||||||
@ -312,7 +312,7 @@ func (np *NetworkParser) validateProxyURL(proxyURL string) error {
|
|||||||
|
|
||||||
// 检查协议
|
// 检查协议
|
||||||
switch parsedURL.Scheme {
|
switch parsedURL.Scheme {
|
||||||
case "http", "https", "socks5":
|
case ProtocolHTTP, ProtocolHTTPS, ProtocolSOCKS5:
|
||||||
// 支持的协议
|
// 支持的协议
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("不支持的代理协议: %s", parsedURL.Scheme)
|
return fmt.Errorf("不支持的代理协议: %s", parsedURL.Scheme)
|
||||||
@ -336,7 +336,7 @@ func (np *NetworkParser) validateProxyURL(proxyURL string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 安全检查
|
// 安全检查
|
||||||
if !np.options.AllowInsecure && parsedURL.Scheme == "http" {
|
if !np.options.AllowInsecure && parsedURL.Scheme == ProtocolHTTP {
|
||||||
return fmt.Errorf("不允许使用不安全的HTTP代理")
|
return fmt.Errorf("不允许使用不安全的HTTP代理")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ func parseShortIPRange(startIPStr, endSuffix string) ([]string, error) {
|
|||||||
|
|
||||||
var hosts []string
|
var hosts []string
|
||||||
startIPParts := strings.Split(startIPStr, ".")
|
startIPParts := strings.Split(startIPStr, ".")
|
||||||
if len(startIPParts) != 4 {
|
if len(startIPParts) != IPv4OctetCount {
|
||||||
return nil, fmt.Errorf("无效的IP格式: %s", startIPStr)
|
return nil, fmt.Errorf("无效的IP格式: %s", startIPStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,7 +541,7 @@ func (tp *TargetParser) parseShortIPRange(startIPStr, endSuffix string) ([]strin
|
|||||||
|
|
||||||
// 分解起始IP
|
// 分解起始IP
|
||||||
ipParts := strings.Split(startIPStr, ".")
|
ipParts := strings.Split(startIPStr, ".")
|
||||||
if len(ipParts) != 4 {
|
if len(ipParts) != IPv4OctetCount {
|
||||||
return nil, fmt.Errorf("无效的IP地址格式: %s", startIPStr)
|
return nil, fmt.Errorf("无效的IP地址格式: %s", startIPStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/shadow1ng/fscan/common/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParsedConfig 解析后的完整配置
|
// ParsedConfig 解析后的完整配置
|
||||||
@ -87,7 +89,7 @@ type ParseResult struct {
|
|||||||
|
|
||||||
// 预定义错误类型
|
// 预定义错误类型
|
||||||
var (
|
var (
|
||||||
ErrEmptyInput = errors.New(ErrEmptyInputMsg)
|
ErrEmptyInput = errors.New(i18n.GetText("parser_empty_input"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParserOptions 解析器选项
|
// ParserOptions 解析器选项
|
||||||
|
@ -145,7 +145,6 @@ const (
|
|||||||
|
|
||||||
// IP地址限制
|
// IP地址限制
|
||||||
MaxIPv4OctetValue = 255
|
MaxIPv4OctetValue = 255
|
||||||
MinIPv4OctetValue = 0
|
|
||||||
IPv4OctetCount = 4
|
IPv4OctetCount = 4
|
||||||
MaxDomainLength = 253
|
MaxDomainLength = 253
|
||||||
|
|
||||||
@ -254,29 +253,6 @@ const (
|
|||||||
ErrorTypePortError = "PORT_ERROR"
|
ErrorTypePortError = "PORT_ERROR"
|
||||||
ErrorTypeExcludePortError = "EXCLUDE_PORT_ERROR"
|
ErrorTypeExcludePortError = "EXCLUDE_PORT_ERROR"
|
||||||
|
|
||||||
// 错误消息模板
|
|
||||||
ErrEmptyInputMsg = "输入参数为空"
|
|
||||||
ErrFileEmptyMsg = "文件名为空"
|
|
||||||
ErrFileTooBigMsg = "文件过大: %d bytes, 最大限制: %d bytes"
|
|
||||||
ErrCannotOpenFileMsg = "无法打开文件"
|
|
||||||
ErrFileNotExistsMsg = "文件不存在或无法访问"
|
|
||||||
ErrFileReadTimeoutMsg = "文件读取超时"
|
|
||||||
ErrFileScanFailedMsg = "文件扫描失败"
|
|
||||||
ErrUsernameTooLongMsg = "用户名过长: %d字符,最大允许: %d"
|
|
||||||
ErrUsernameInvalidCharsMsg = "用户名包含非法字符"
|
|
||||||
ErrPasswordEmptyMsg = "不允许空密码"
|
|
||||||
ErrPasswordTooLongMsg = "密码过长: %d字符,最大允许: %d"
|
|
||||||
ErrHashEmptyMsg = "哈希值为空"
|
|
||||||
ErrHashInvalidFormatMsg = "哈希值格式无效,需要32位十六进制字符"
|
|
||||||
ErrProxyURLInvalidMsg = "代理URL格式无效: %v"
|
|
||||||
ErrProxyProtocolUnsupportedMsg = "不支持的代理协议: %s"
|
|
||||||
ErrProxyHostEmptyMsg = "代理主机名为空"
|
|
||||||
ErrProxyPortInvalidMsg = "代理端口号无效: %s"
|
|
||||||
ErrProxyPortOutOfRangeMsg = "代理端口号超出范围: %d"
|
|
||||||
ErrProxyInsecureMsg = "不允许使用不安全的HTTP代理"
|
|
||||||
ErrUserAgentTooLongMsg = "用户代理字符串过长"
|
|
||||||
ErrUserAgentInvalidCharsMsg = "用户代理包含非法字符"
|
|
||||||
ErrCookieTooLongMsg = "Cookie字符串过长"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
Loading…
Reference in New Issue
Block a user