fix: https jump

#670
This commit is contained in:
samwaf
2026-02-06 10:37:26 +08:00
parent 0d005177a1
commit a1fe250172
6 changed files with 641 additions and 29 deletions
+24
View File
@@ -94,6 +94,30 @@ func (w *WafSystemConfigApi) ModifyApi(c *gin.Context) {
response.FailWithMessage("解析失败", c)
}
}
// ModifyByItemApi 通过 item 修改系统配置的 value
func (w *WafSystemConfigApi) ModifyByItemApi(c *gin.Context) {
var req request.WafSystemConfigEditByItemReq
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage("解析失败", c)
return
}
if req.Item == "" {
response.FailWithMessage("item 不能为空", c)
return
}
err = wafSystemConfigService.ModifyByItemApi(req)
if err != nil {
response.FailWithMessage("编辑发生错误: "+err.Error(), c)
} else {
waftask.TaskLoadSetting(true)
response.OkWithMessage("编辑成功", c)
}
}
func (w *WafSystemConfigApi) GetDetailByItemApi(c *gin.Context) {
var req request.WafSystemConfigDetailByItemReq
err := c.ShouldBind(&req)
+4
View File
@@ -33,3 +33,7 @@ type WafSystemConfigSearchReq struct {
type WafSystemConfigDetailByItemReq struct {
Item string `json:"item" form:"item"` //item
}
type WafSystemConfigEditByItemReq struct {
Item string `json:"item" form:"item"` //item
Value string `json:"value" form:"value"` //value
}
+1
View File
@@ -17,4 +17,5 @@ func (receiver *SystemConfigRouter) InitSystemConfigRouter(group *gin.RouterGrou
router.POST("/api/v1/systemconfig/add", api.AddApi)
router.GET("/api/v1/systemconfig/del", api.DelApi)
router.POST("/api/v1/systemconfig/edit", api.ModifyApi)
router.POST("/api/v1/systemconfig/editByItem", api.ModifyByItemApi)
}
+19
View File
@@ -58,6 +58,25 @@ func (receiver *WafSystemConfigService) ModifyApi(req request.WafSystemConfigEdi
return err
}
// ModifyByItemApi 通过 item 修改系统配置的 value
func (receiver *WafSystemConfigService) ModifyByItemApi(req request.WafSystemConfigEditByItemReq) error {
var sysConfig model.SystemConfig
err := global.GWAF_LOCAL_DB.Where("item = ?", req.Item).First(&sysConfig).Error
if err != nil {
return errors.New("配置项不存在")
}
editMap := map[string]interface{}{
"Value": req.Value,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}
err = global.GWAF_LOCAL_DB.Model(model.SystemConfig{}).Where("item = ?", req.Item).Updates(editMap).Error
return err
}
func (receiver *WafSystemConfigService) GetDetailApi(req request.WafSystemConfigDetailReq) model.SystemConfig {
var bean model.SystemConfig
global.GWAF_LOCAL_DB.Where("id=?", req.Id).Find(&bean)
+134 -21
View File
@@ -73,9 +73,10 @@ type WafEngine struct {
}
func (waf *WafEngine) Error() string {
fs := "HTTP: %d, HostCode: %d, Message: %s"
zlog.Error(fmt.Sprintf(fs))
return fmt.Sprintf(fs)
// 返回错误信息的固定格式
const errFormat = "WafEngine error occurred"
zlog.Error(errFormat)
return errFormat
}
// inferAttackType 根据检测规则标题推断攻击类型
@@ -440,26 +441,23 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
if host == hostTarget.Host.Host+":80" && !strings.HasPrefix(weblogbean.URL, global.GSSL_HTTP_CHANGLE_PATH) && hostTarget.Host.AutoJumpHTTPS == 1 && hostTarget.Host.Ssl == 1 {
domainJump := ""
if strings.Contains(host, ":") {
partsJump := strings.Split(host, ":")
if len(partsJump) == 2 {
domainJump = partsJump[0]
if r.TLS == nil {
// 检查是否需要自动跳转到HTTPS
shouldJump, domainJump := shouldAutoJumpHTTPS(host, hostTarget.Host.Host, weblogbean.URL, hostTarget.Host.AutoJumpHTTPS, hostTarget.Host.Ssl)
if shouldJump {
// 重定向到 HTTPS 版本的 URL
targetHttpsUrl := fmt.Sprintf("%s%s%s", "https://", domainJump, r.URL.Path)
// 只有在非标准端口时才显示端口号(443是标准端口)
if hostTarget.Host.Port != 443 {
targetHttpsUrl = fmt.Sprintf("%s%s:%d%s", "https://", domainJump, hostTarget.Host.Port, r.URL.Path)
}
if r.URL.RawQuery != "" {
targetHttpsUrl += "?" + r.URL.RawQuery
}
zlog.Info(innerLogName, "jump https", targetHttpsUrl)
http.Redirect(w, r, targetHttpsUrl, int(global.GCONFIG_RECORD_REDIRECT_HTTPS_CODE))
return
}
// 重定向到 HTTPS 版本的 URL
targetHttpsUrl := fmt.Sprintf("%s%s%s", "https://", domainJump, r.URL.Path)
// 只有在非标准端口时才显示端口号(443是标准端口)
if hostTarget.Host.Port != 443 {
targetHttpsUrl = fmt.Sprintf("%s%s:%d%s", "https://", domainJump, hostTarget.Host.Port, r.URL.Path)
}
if r.URL.RawQuery != "" {
targetHttpsUrl += "?" + r.URL.RawQuery
}
zlog.Info(innerLogName, "jump https", targetHttpsUrl)
http.Redirect(w, r, targetHttpsUrl, int(global.GCONFIG_RECORD_REDIRECT_HTTPS_CODE))
return
}
r.Header.Add("waf_req_uuid", weblogbean.REQ_UUID)
@@ -1817,3 +1815,118 @@ func (waf *WafEngine) checkWithPlugins(r *http.Request, weblogbean *innerbean.We
// 插件检查通过,不拦截
return detection.Result{IsBlock: false}
}
// shouldAutoJumpHTTPS 判断是否应该自动跳转到HTTPS
// 参数:
// - requestHost: 请求的host(如: aaa.samwaf.com:80 或 bbb.aaa.samwaf.com:8080)
// - configHost: 配置的host(如: *.samwaf.com 或 example.com)
// - requestURL: 请求的URL路径
// - autoJumpHTTPS: 是否开启自动跳转HTTPS
// - ssl: 是否启用了SSL
//
// 返回:
// - bool: 是否应该跳转
// - string: 跳转的域名部分(不含端口)
//
// 支持场景:
// - 精确匹配: example.com:80 匹配 example.com
// - 二级泛域名: aaa.samwaf.com:80 匹配 *.samwaf.com
// - 多级泛域名: bbb.aaa.samwaf.com:8080 匹配 *.samwaf.com (利用MaskSubdomain)
func shouldAutoJumpHTTPS(requestHost, configHost, requestURL string, autoJumpHTTPS, ssl int) (bool, string) {
// 检查基本条件:必须开启自动跳转和SSL
if autoJumpHTTPS != 1 || ssl != 1 {
return false, ""
}
// 排除SSL证书验证路径
if strings.HasPrefix(requestURL, global.GSSL_HTTP_CHANGLE_PATH) {
return false, ""
}
// 从请求host中提取域名和端口
requestDomain := ""
requestPort := ""
if strings.Contains(requestHost, ":") {
parts := strings.Split(requestHost, ":")
if len(parts) == 2 {
requestDomain = parts[0]
requestPort = parts[1]
}
} else {
requestDomain = requestHost
}
// 判断是否是HTTP端口(80或其他非443端口)
// 如果没有端口号或者端口是443,不需要跳转
if requestPort == "" || requestPort == "443" {
return false, ""
}
// 检查域名是否匹配
matched := false
// 情况1: 精确匹配(如: example.com 匹配 example.com
if requestDomain == configHost {
matched = true
}
// 情况2: 配置host带端口的精确匹配(如: example.com:80 匹配 example.com:80
if !matched && strings.Contains(configHost, ":") {
if requestHost == configHost {
matched = true
}
}
// 情况3: 泛域名匹配(支持多级域名)
// 例如: aaa.samwaf.com:80 匹配 *.samwaf.com
// bbb.aaa.samwaf.com:80 匹配 *.samwaf.com
// ccc.bbb.aaa.samwaf.com:8080 匹配 *.samwaf.com
if !matched && strings.HasPrefix(configHost, "*.") {
// 方法1: 直接匹配(二级域名)
// 提取主域名部分(去掉*.
baseDomain := configHost[2:] // 去掉 "*."
// 检查请求域名是否以主域名结尾
if strings.HasSuffix(requestDomain, baseDomain) {
// 确保是完整的子域名匹配,而不是部分匹配
// 例如: aaa.samwaf.com 匹配 *.samwaf.com
// 但 aaasamwaf.com 不匹配 *.samwaf.com
if requestDomain != baseDomain && strings.HasSuffix(requestDomain, "."+baseDomain) {
matched = true
}
}
// 方法2: 使用MaskSubdomain处理多级域名
// 例如: bbb.aaa.samwaf.com:80 -> *.aaa.samwaf.com:80
// 然后递归检查 *.aaa.samwaf.com 是否匹配 *.samwaf.com
if !matched {
maskedHost := domaintool.MaskSubdomain(requestHost)
// maskedHost 示例: *.aaa.samwaf.com:80
// 如果 maskedHost 和 requestHost 不同,说明有多级域名
if maskedHost != requestHost {
// 提取 maskedHost 的域名部分(去掉端口)
maskedDomain := maskedHost
if strings.Contains(maskedHost, ":") {
maskedDomain = strings.Split(maskedHost, ":")[0]
}
// 递归检查: *.aaa.samwaf.com 是否匹配 *.samwaf.com
// 即检查 aaa.samwaf.com 是否匹配 *.samwaf.com 的baseDomain
if strings.HasPrefix(maskedDomain, "*.") {
checkDomain := maskedDomain[2:] // 去掉 "*.",得到 aaa.samwaf.com
// 检查 aaa.samwaf.com 是否匹配 *.samwaf.com
if strings.HasSuffix(checkDomain, baseDomain) {
if checkDomain != baseDomain && strings.HasSuffix(checkDomain, "."+baseDomain) {
matched = true
}
}
}
}
}
}
if matched {
return true, requestDomain
}
return false, ""
}
+459 -8
View File
@@ -274,7 +274,7 @@ func TestGetOrgContent(t *testing.T) {
}
// 调用测试函数
result, _, err := waf.getOrgContent(resp, false)
result, _, err := waf.getOrgContent(resp, false, "")
// 验证结果
if tc.expectedErr {
@@ -337,7 +337,7 @@ func TestGetOrgContentWithChunkedEncoding(t *testing.T) {
resp.Header.Set("Content-Type", "text/html; charset=utf-8")
// 调用测试函数
result, _, err := waf.getOrgContent(resp, false)
result, _, err := waf.getOrgContent(resp, false, "")
// 验证结果
if err != nil {
@@ -365,7 +365,7 @@ func TestGetOrgContentWithEmptyBody(t *testing.T) {
resp.Header.Set("Content-Type", "text/html; charset=utf-8")
// 调用测试函数
result, _, err := waf.getOrgContent(resp, false)
result, _, err := waf.getOrgContent(resp, false, "")
// 验证结果
if err != nil {
@@ -399,7 +399,7 @@ func TestGetOrgContentWithErrors(t *testing.T) {
resp.Header.Set("Content-Encoding", "gzip")
// 调用测试函数
_, _, err := waf.getOrgContent(resp, false)
_, _, err := waf.getOrgContent(resp, false, "")
// 验证结果
if err == nil {
@@ -422,7 +422,7 @@ func TestGetOrgContent_MetaAndDoctypeCharset(t *testing.T) {
Body: io.NopCloser(strings.NewReader(htmlMetaUtf8)),
}
resp1.Header.Set("Content-Type", "text/html")
content1, charset1, err1 := waf.getOrgContent(resp1, false)
content1, charset1, err1 := waf.getOrgContent(resp1, false, "")
if err1 != nil || charset1 != "utf-8" || !strings.Contains(string(content1), "utf8内容") {
t.Errorf("meta标签utf-8检测失败: err=%v, charset=%s, content=%s", err1, charset1, string(content1))
}
@@ -435,7 +435,7 @@ func TestGetOrgContent_MetaAndDoctypeCharset(t *testing.T) {
Body: io.NopCloser(bytes.NewReader(gbkBody)),
}
resp2.Header.Set("Content-Type", "text/html")
content2, charset2, err2 := waf.getOrgContent(resp2, false)
content2, charset2, err2 := waf.getOrgContent(resp2, false, "")
if err2 != nil || charset2 != "gbk" || !strings.Contains(string(content2), "这是GBK编码的内容") {
t.Errorf("meta标签gbk检测失败: err=%v, charset=%s, content=%s", err2, charset2, string(content2))
}
@@ -448,7 +448,7 @@ func TestGetOrgContent_MetaAndDoctypeCharset(t *testing.T) {
Body: io.NopCloser(strings.NewReader(htmlDoctype)),
}
resp3.Header.Set("Content-Type", "text/html")
content3, charset3, err3 := waf.getOrgContent(resp3, false)
content3, charset3, err3 := waf.getOrgContent(resp3, false, "")
if err3 != nil || charset3 != "utf-8" || !strings.Contains(string(content3), "doctype内容") {
t.Errorf("DOCTYPE声明检测失败: err=%v, charset=%s, content=%s", err3, charset3, string(content3))
}
@@ -461,8 +461,459 @@ func TestGetOrgContent_MetaAndDoctypeCharset(t *testing.T) {
Body: io.NopCloser(strings.NewReader(htmlMetaUnknown)),
}
resp4.Header.Set("Content-Type", "text/html")
_, _, err4 := waf.getOrgContent(resp4, false)
_, _, err4 := waf.getOrgContent(resp4, false, "")
if err4 == nil || !strings.Contains(err4.Error(), "编码检测不确定") {
t.Errorf("未知meta编码检测失败,期望返回错误,实际: %v", err4)
}
}
// TestShouldAutoJumpHTTPS 测试HTTPS自动跳转判断逻辑
func TestShouldAutoJumpHTTPS(t *testing.T) {
// 初始化全局变量(如果有的话)
global.GSSL_HTTP_CHANGLE_PATH = "/.well-known/acme-challenge/"
tests := []struct {
name string
requestHost string
configHost string
requestURL string
autoJumpHTTPS int
ssl int
expectedJump bool
expectedDomain string
description string
}{
// ========== 基本条件测试 ==========
{
name: "未开启自动跳转",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 0, // 未开启
ssl: 1,
expectedJump: false,
expectedDomain: "",
description: "autoJumpHTTPS=0,不应该跳转",
},
{
name: "未启用SSL",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 0, // 未启用SSL
expectedJump: false,
expectedDomain: "",
description: "ssl=0,不应该跳转",
},
{
name: "SSL证书验证路径不跳转",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "/.well-known/acme-challenge/xxxxx",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: false,
expectedDomain: "",
description: "SSL证书验证路径应排除",
},
// ========== 端口测试 ==========
{
name: "标准80端口应跳转",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "标准80端口应触发跳转",
},
{
name: "非标准8080端口应跳转",
requestHost: "example.com:8080",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "8080端口应触发跳转",
},
{
name: "非标准8888端口应跳转",
requestHost: "example.com:8888",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "8888端口应触发跳转",
},
{
name: "443端口不应跳转",
requestHost: "example.com:443",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: false,
expectedDomain: "",
description: "443端口已是HTTPS,不应跳转",
},
{
name: "无端口不应跳转",
requestHost: "example.com",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: false,
expectedDomain: "",
description: "无端口号不应跳转",
},
// ========== 精确匹配测试 ==========
{
name: "精确匹配-域名完全一致",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "请求域名与配置域名完全一致应跳转",
},
{
name: "精确匹配-域名不一致",
requestHost: "test.com:80",
configHost: "example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: false,
expectedDomain: "",
description: "请求域名与配置域名不一致不应跳转",
},
{
name: "带端口的精确匹配",
requestHost: "example.com:8080",
configHost: "example.com:8080",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "配置host带端口且完全匹配应跳转",
},
// ========== 二级泛域名测试 ==========
{
name: "二级泛域名-正常匹配",
requestHost: "aaa.samwaf.com:80",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "aaa.samwaf.com",
description: "二级域名应匹配泛域名配置",
},
{
name: "二级泛域名-非标准端口",
requestHost: "bbb.samwaf.com:8080",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "bbb.samwaf.com",
description: "二级域名+非标准端口应匹配泛域名配置",
},
{
name: "二级泛域名-根域名不应匹配",
requestHost: "samwaf.com:80",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: false,
expectedDomain: "",
description: "根域名不应匹配*.samwaf.com",
},
{
name: "二级泛域名-部分匹配不应成功",
requestHost: "aaasamwaf.com:80",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: false,
expectedDomain: "",
description: "aaasamwaf.com不应匹配*.samwaf.com",
},
// ========== 三级泛域名测试 ==========
{
name: "三级泛域名-正常匹配",
requestHost: "bbb.aaa.samwaf.com:80",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "bbb.aaa.samwaf.com",
description: "三级域名应匹配泛域名配置(通过MaskSubdomain",
},
{
name: "三级泛域名-非标准端口",
requestHost: "ccc.bbb.samwaf.com:8080",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "ccc.bbb.samwaf.com",
description: "三级域名+非标准端口应匹配泛域名配置",
},
// ========== 四级泛域名测试 ==========
{
name: "四级泛域名-正常匹配",
requestHost: "ddd.ccc.bbb.samwaf.com:80",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "ddd.ccc.bbb.samwaf.com",
description: "四级域名应匹配泛域名配置",
},
{
name: "四级泛域名-非标准端口8888",
requestHost: "eee.ddd.ccc.samwaf.com:8888",
configHost: "*.samwaf.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "eee.ddd.ccc.samwaf.com",
description: "四级域名+8888端口应匹配泛域名配置",
},
// ========== 边界情况测试 ==========
{
name: "空路径",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "空路径应正常跳转",
},
{
name: "根路径",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "/",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "根路径应正常跳转",
},
{
name: "带查询参数的路径",
requestHost: "example.com:80",
configHost: "example.com",
requestURL: "/test?param=value",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "example.com",
description: "带查询参数应正常跳转",
},
// ========== 不同域名后缀测试 ==========
{
name: "不同域名后缀-com",
requestHost: "test.example.com:80",
configHost: "*.example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "test.example.com",
description: ".com域名应正常匹配",
},
{
name: "不同域名后缀-cn",
requestHost: "test.example.cn:80",
configHost: "*.example.cn",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "test.example.cn",
description: ".cn域名应正常匹配",
},
{
name: "不同域名后缀-io",
requestHost: "api.service.io:8080",
configHost: "*.service.io",
requestURL: "/api/v1",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "api.service.io",
description: ".io域名+非标准端口应正常匹配",
},
// ========== 特殊字符测试 ==========
{
name: "域名含中划线",
requestHost: "test-api.example.com:80",
configHost: "*.example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "test-api.example.com",
description: "域名包含中划线应正常匹配",
},
{
name: "域名含数字",
requestHost: "api123.example.com:80",
configHost: "*.example.com",
requestURL: "/test",
autoJumpHTTPS: 1,
ssl: 1,
expectedJump: true,
expectedDomain: "api123.example.com",
description: "域名包含数字应正常匹配",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotJump, gotDomain := shouldAutoJumpHTTPS(
tt.requestHost,
tt.configHost,
tt.requestURL,
tt.autoJumpHTTPS,
tt.ssl,
)
if gotJump != tt.expectedJump {
t.Errorf("%s\n请求Host: %s, 配置Host: %s\n期望跳转: %v, 实际跳转: %v",
tt.description, tt.requestHost, tt.configHost, tt.expectedJump, gotJump)
}
if gotJump && gotDomain != tt.expectedDomain {
t.Errorf("%s\n请求Host: %s, 配置Host: %s\n期望域名: %s, 实际域名: %s",
tt.description, tt.requestHost, tt.configHost, tt.expectedDomain, gotDomain)
}
// 成功的测试用例也输出日志
if gotJump == tt.expectedJump {
t.Logf("✅ %s - 通过", tt.name)
}
})
}
}
// TestShouldAutoJumpHTTPS_Comprehensive 综合场景测试
func TestShouldAutoJumpHTTPS_Comprehensive(t *testing.T) {
global.GSSL_HTTP_CHANGLE_PATH = "/.well-known/acme-challenge/"
// 场景1: 用户反馈的实际问题场景
t.Run("用户反馈场景1-非标准80端口", func(t *testing.T) {
// 场景:用户使用8080端口,之前无法跳转
jump, domain := shouldAutoJumpHTTPS("example.com:8080", "example.com", "/test", 1, 1)
if !jump || domain != "example.com" {
t.Errorf("非标准80端口应该能跳转,实际: jump=%v, domain=%s", jump, domain)
} else {
t.Logf("✅ 非标准80端口测试通过")
}
})
t.Run("用户反馈场景2-泛域名匹配", func(t *testing.T) {
// 场景:host变量: aaa.samwaf.com:80, hostTarget.Host.Host变量: *.samwaf.com
jump, domain := shouldAutoJumpHTTPS("aaa.samwaf.com:80", "*.samwaf.com", "/test", 1, 1)
if !jump || domain != "aaa.samwaf.com" {
t.Errorf("泛域名应该能匹配,实际: jump=%v, domain=%s", jump, domain)
} else {
t.Logf("✅ 泛域名匹配测试通过")
}
})
t.Run("用户反馈场景3-三级泛域名", func(t *testing.T) {
// 场景:bbb.aaa.samwaf.com:80 匹配 *.samwaf.com
jump, domain := shouldAutoJumpHTTPS("bbb.aaa.samwaf.com:80", "*.samwaf.com", "/test", 1, 1)
if !jump || domain != "bbb.aaa.samwaf.com" {
t.Errorf("三级泛域名应该能匹配,实际: jump=%v, domain=%s", jump, domain)
} else {
t.Logf("✅ 三级泛域名匹配测试通过")
}
})
// 场景2: SSL证书自动续期路径
t.Run("SSL证书续期路径不应跳转", func(t *testing.T) {
paths := []string{
"/.well-known/acme-challenge/test123",
"/.well-known/acme-challenge/",
}
for _, path := range paths {
jump, _ := shouldAutoJumpHTTPS("example.com:80", "example.com", path, 1, 1)
if jump {
t.Errorf("SSL证书路径 %s 不应跳转", path)
} else {
t.Logf("✅ SSL证书路径 %s 正确排除", path)
}
}
})
// 场景3: 多个非标准端口
t.Run("各种非标准HTTP端口", func(t *testing.T) {
ports := []string{"80", "8080", "8888", "9090", "3000", "5000"}
for _, port := range ports {
requestHost := "example.com:" + port
jump, domain := shouldAutoJumpHTTPS(requestHost, "example.com", "/test", 1, 1)
if !jump || domain != "example.com" {
t.Errorf("端口 %s 应该能跳转,实际: jump=%v, domain=%s", port, jump, domain)
} else {
t.Logf("✅ 端口 %s 测试通过", port)
}
}
})
// 场景4: 多级域名递归测试
t.Run("多级域名递归匹配", func(t *testing.T) {
testCases := []struct {
requestHost string
configHost string
}{
{"a.samwaf.com:80", "*.samwaf.com"},
{"b.a.samwaf.com:80", "*.samwaf.com"},
{"c.b.a.samwaf.com:80", "*.samwaf.com"},
{"d.c.b.a.samwaf.com:80", "*.samwaf.com"},
{"e.d.c.b.a.samwaf.com:80", "*.samwaf.com"},
}
for _, tc := range testCases {
jump, domain := shouldAutoJumpHTTPS(tc.requestHost, tc.configHost, "/test", 1, 1)
expectedDomain := strings.Split(tc.requestHost, ":")[0]
if !jump || domain != expectedDomain {
t.Errorf("多级域名 %s 匹配 %s 失败,实际: jump=%v, domain=%s",
tc.requestHost, tc.configHost, jump, domain)
} else {
t.Logf("✅ 多级域名 %s 匹配成功", tc.requestHost)
}
}
})
}