diff --git a/docs/guide/waf-ip-group-expr.md b/docs/guide/waf-ip-group-expr.md index 3bd20050..044ed5f6 100644 --- a/docs/guide/waf-ip-group-expr.md +++ b/docs/guide/waf-ip-group-expr.md @@ -12,7 +12,7 @@ "rules": [ { "name": "单 IP 404 高频扫描", - "expr": "request_count > 100 && status_404_ratio >= 0.8" + "expr": "request_count > 100 && StatusRatio(404) >= 0.8" } ] } @@ -74,12 +74,12 @@ Host 是否为“通过 IP 访问”按请求日志中的 `Host` 字段判断: | --- | --- | --- | | `>`、`>=`、`<`、`<=` | 数值比较 | `request_count > 100` | | `==`、`!=` | 相等或不相等 | `ip != "127.0.0.1"` | -| `&&` | 并且 | `request_count > 100 && status_404_ratio >= 0.8` | -| `||` | 或者 | `status_404_ratio >= 0.8 || server_error_count > 20` | +| `&&` | 并且 | `request_count > 100 && StatusRatio(404) >= 0.8` | +| `||` | 或者 | `StatusRatio(404) >= 0.8 || server_error_count > 20` | | `!` | 取反 | `!(ip == "127.0.0.1")` | | `in` | 判断值是否在列表中 | `ip in ["203.0.113.10", "198.51.100.20"]` | | `not in` | 判断值是否不在列表中 | `ip not in ["127.0.0.1"]` | -| `()` | 分组控制优先级 | `(request_count > 100 && status_404_ratio >= 0.8) || server_error_count > 50` | +| `()` | 分组控制优先级 | `(request_count > 100 && StatusRatio(404) >= 0.8) || server_error_count > 50` | ## 内置预设 @@ -88,7 +88,7 @@ Host 是否为“通过 IP 访问”按请求日志中的 `Host` 字段判断: ```json { "name": "单 IP 404 高频扫描", - "expr": "request_count > 100 && status_404_ratio >= 0.8" + "expr": "request_count > 100 && StatusRatio(404) >= 0.8" } ``` @@ -113,7 +113,7 @@ Host 是否为“通过 IP 访问”按请求日志中的 `Host` 字段判断: "rules": [ { "name": "高频 404 扫描", - "expr": "request_count > 100 && status_404_ratio >= 0.8" + "expr": "request_count > 100 && StatusRatio(404) >= 0.8" } ] } @@ -155,7 +155,7 @@ IP 直连访问异常: "rules": [ { "name": "排除可信 IP 的 404 扫描", - "expr": "ip not in [\"203.0.113.10\", \"198.51.100.20\"] && request_count > 100 && status_404_ratio >= 0.8" + "expr": "ip not in [\"203.0.113.10\", \"198.51.100.20\"] && request_count > 100 && StatusRatio(404) >= 0.8" } ] } diff --git a/docs/guide/waf-usage.md b/docs/guide/waf-usage.md index b002505a..bdf62400 100644 --- a/docs/guide/waf-usage.md +++ b/docs/guide/waf-usage.md @@ -48,7 +48,7 @@ IP 组是进行大批量 IP 过滤的基石。OpenFlare 提供了极富弹性的 #### 3. 自动 IP 组 (Automatic) * **用途**:**最具杀伤力的防扫描、防爆破自动通道**。 * **配置**:类型选择「自动」-> 编写 Expr 日志聚合逻辑。你可以直接引用系统内置的预设: - * **单 IP 404 高频扫描**:`request_count > 100 && status_404_ratio >= 0.8` (单个 IP 最近一小时请求超 100 次且 404 响应占比超 80%)。 + * **单 IP 404 高频扫描**:`request_count > 100 && StatusRatio(404) >= 0.8` (单个 IP 最近一小时请求超 100 次且 404 响应占比超 80%)。 * **单 IP 直连访问异常**:`ip_host_count > 50 && ip_host_ratio > 0.5` (绕过域名直接通过 IP 地址进行高频请求)。 * **测试与立即执行**:保存前可点击 **「测试规则」** 按钮预览当前日志窗口被命中的 IP。保存后可点击 **「立即执行」** 直接聚合日志并生成封禁名单。 diff --git a/docs/reference/api.md b/docs/reference/api.md index 577d15d2..393a220d 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -41,23 +41,7 @@ OpenFlare 的管理端 API 与 Agent API 都使用 JSON。 | `POST` | `/api/waf/ip-groups/:id/delete` | 删除 IP 组;已被规则组引用时会拒绝 | | `POST` | `/api/waf/ip-groups/:id/sync` | 立即同步订阅型 IP 组或立即执行自动型 IP 组 | -IP 组 `type` 支持 `manual`、`automatic`、`subscription`。自动型 IP 组的 `auto_config` 是 JSON 对象,当前支持: - -```json -{ - "lookback_minutes": 60, - "rules": [ - { - "name": "单 IP 404 高频扫描", - "expr": "request_count > 100 && status_404_ratio >= 0.8" - }, - { - "name": "单 IP 直连访问异常", - "expr": "ip_host_count > 50 && ip_host_ratio > 0.5" - } - ] -} -``` +IP 组 `type` 支持 `manual`、`automatic`、`subscription`。自动型 IP 组的 `auto_config` 是 JSON 对象 自动规则使用 Expr 语法,表达式必须返回布尔值。规则按单个 IP 的请求日志聚合指标计算,可用字段包括 `ip`、`request_count`、`status_404_count`、`status_404_ratio`、`ip_host_count`、`ip_host_ratio`、`client_error_count`、`server_error_count`、`last_seen_unix`。完整语法和字段含义见 [WAF 自动 IP 组规则语法](../guide/waf-ip-group-expr.md)。订阅格式支持 `text` 与 `json`:文本格式按行解析 IP/IP 段并忽略空行和 `#` 开头的注释;JSON 格式可通过映射规则选择数组,默认读取根数组。 diff --git a/openflare_server/service/waf_test.go b/openflare_server/service/waf_test.go index b27e64d2..831f0b28 100644 --- a/openflare_server/service/waf_test.go +++ b/openflare_server/service/waf_test.go @@ -228,7 +228,7 @@ func TestSyncWAFIPGroupAutomaticExprRules(t *testing.T) { AutoConfig: json.RawMessage(`{ "lookback_minutes": 60, "rules": [ - {"name":"单 IP 404 高频扫描","expr":"request_count > 100 && status_404_ratio >= 0.8"}, + {"name":"单 IP 404 高频扫描","expr":"request_count > 100 && StatusRatio(404) >= 0.8"}, {"name":"单 IP 直连访问异常","expr":"ip_host_count > 50 && ip_host_ratio > 0.5"} ] }`), @@ -267,7 +267,7 @@ func TestWAFIPGroupAutoConfigReturnsMatchedIPs(t *testing.T) { AutoConfig: json.RawMessage(`{ "lookback_minutes": 60, "rules": [ - {"name":"单 IP 404 高频扫描","expr":"request_count > 100 && status_404_ratio >= 0.8"}, + {"name":"单 IP 404 高频扫描","expr":"request_count > 100 && StatusRatio(404) >= 0.8"}, {"name":"单 IP 直连访问异常","expr":"ip_host_count > 50 && ip_host_ratio > 0.5"} ] }`), @@ -383,7 +383,7 @@ func TestWAFIPGroupAutomaticTTLExpiration(t *testing.T) { "lookback_minutes": 60, "ttl": 10, "rules": [ - {"name":"404 Scan","expr":"request_count > 100 && status_404_ratio >= 0.8"} + {"name":"404 Scan","expr":"request_count > 100 && StatusRatio(404) >= 0.8"} ] }`), }) diff --git a/openflare_server/web/features/waf/components/ip-groups-page.tsx b/openflare_server/web/features/waf/components/ip-groups-page.tsx index ac74cd8d..bb600cb4 100644 --- a/openflare_server/web/features/waf/components/ip-groups-page.tsx +++ b/openflare_server/web/features/waf/components/ip-groups-page.tsx @@ -75,7 +75,7 @@ const typeLabels: Record = { const automaticPresetRules = [ { name: '单 IP 404 高频扫描', - expr: 'request_count > 100 && status_404_ratio >= 0.8', + expr: 'request_count > 100 && StatusRatio(404) >= 0.8', }, { name: '单 IP 直连访问异常', diff --git a/openflare_server/web/tests/unit/waf-ip-groups-page.test.tsx b/openflare_server/web/tests/unit/waf-ip-groups-page.test.tsx index 006db5fc..2842107c 100644 --- a/openflare_server/web/tests/unit/waf-ip-groups-page.test.tsx +++ b/openflare_server/web/tests/unit/waf-ip-groups-page.test.tsx @@ -200,7 +200,7 @@ describe('WAF IP groups', () => { const textarea = screen.getByLabelText(/自动配置 JSON/); const value = (textarea as HTMLTextAreaElement).value; - expect(value).toContain('request_count > 100 && status_404_ratio >= 0.8'); + expect(value).toContain('request_count > 100 && StatusRatio(404) >= 0.8'); expect(value).toContain('ip_host_count > 50 && ip_host_ratio > 0.5'); }); @@ -260,7 +260,7 @@ describe('WAF IP groups', () => { lookback_minutes: 60, rules: [ expect.objectContaining({ - expr: 'request_count > 100 && status_404_ratio >= 0.8', + expr: 'request_count > 100 && StatusRatio(404) >= 0.8', }), ], }),