diff --git a/internal/nodes/http_request_waf.go b/internal/nodes/http_request_waf.go
index 4611de8..f9e3be7 100644
--- a/internal/nodes/http_request_waf.go
+++ b/internal/nodes/http_request_waf.go
@@ -257,7 +257,7 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
return
}
- goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer)
+ goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer, this.web.FirewallRef.DefaultCaptchaType)
if forceLog && logRequestBody && hasRequestBody && ruleSet != nil && ruleSet.HasAttackActions() {
this.wafHasRequestBody = true
}
@@ -307,7 +307,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
}
if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn {
- blocked := this.checkWAFResponse(this.web.FirewallPolicy, resp, forceLog, forceLogRequestBody, false)
+ blocked = this.checkWAFResponse(this.web.FirewallPolicy, resp, forceLog, forceLogRequestBody, false)
if blocked {
return true
}
@@ -315,7 +315,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
// 公用的防火墙设置
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn {
- blocked := this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog, forceLogRequestBody, this.web.FirewallRef.IgnoreGlobalRules)
+ blocked = this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog, forceLogRequestBody, this.web.FirewallRef.IgnoreGlobalRules)
if blocked {
return true
}
diff --git a/internal/waf/action_captcha.go b/internal/waf/action_captcha.go
index cc19334..d9b3fc4 100644
--- a/internal/waf/action_captcha.go
+++ b/internal/waf/action_captcha.go
@@ -1,6 +1,7 @@
package waf
import (
+ "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
@@ -27,6 +28,8 @@ type CaptchaAction struct {
CountLetters int8 `yaml:"countLetters" json:"countLetters"`
+ CaptchaType firewallconfigs.CaptchaType `yaml:"captchaType" json:"captchaType"`
+
UIIsOn bool `yaml:"uiIsOn" json:"uiIsOn"` // 是否使用自定义UI
UITitle string `yaml:"uiTitle" json:"uiTitle"` // 消息标题
UIPrompt string `yaml:"uiPrompt" json:"uiPrompt"` // 消息提示
@@ -36,6 +39,22 @@ type CaptchaAction struct {
UIFooter string `yaml:"uiFooter" json:"uiFooter"` // 页脚
UIBody string `yaml:"uiBody" json:"uiBody"` // 内容轮廓
+ OneClickUIIsOn bool `yaml:"oneClickUIIsOn" json:"oneClickUIIsOn"` // 是否使用自定义UI
+ OneClickUITitle string `yaml:"oneClickUITitle" json:"oneClickUITitle"` // 消息标题
+ OneClickUIPrompt string `yaml:"oneClickUIPrompt" json:"oneClickUIPrompt"` // 消息提示
+ OneClickUIShowRequestId bool `yaml:"oneClickUIShowRequestId" json:"oneClickUIShowRequestId"` // 是否显示请求ID
+ OneClickUICss string `yaml:"oneClickUICss" json:"oneClickUICss"` // CSS样式
+ OneClickUIFooter string `yaml:"oneClickUIFooter" json:"oneClickUIFooter"` // 页脚
+ OneClickUIBody string `yaml:"oneClickUIBody" json:"oneClickUIBody"` // 内容轮廓
+
+ SlideUIIsOn bool `yaml:"sliceUIIsOn" json:"sliceUIIsOn"` // 是否使用自定义UI
+ SlideUITitle string `yaml:"slideUITitle" json:"slideUITitle"` // 消息标题
+ SlideUIPrompt string `yaml:"slideUIPrompt" json:"slideUIPrompt"` // 消息提示
+ SlideUIShowRequestId bool `yaml:"SlideUIShowRequestId" json:"SlideUIShowRequestId"` // 是否显示请求ID
+ SlideUICss string `yaml:"slideUICss" json:"slideUICss"` // CSS样式
+ SlideUIFooter string `yaml:"slideUIFooter" json:"slideUIFooter"` // 页脚
+ SlideUIBody string `yaml:"slideUIBody" json:"slideUIBody"` // 内容轮廓
+
Lang string `yaml:"lang" json:"lang"` // 语言,zh-CN, en-US ...
AddToWhiteList bool `yaml:"addToWhiteList" json:"addToWhiteList"` // 是否加入到白名单
Scope string `yaml:"scope" json:"scope"`
@@ -81,6 +100,10 @@ func (this *CaptchaAction) Init(waf *WAF) error {
if len(this.Lang) == 0 {
this.Lang = waf.DefaultCaptchaAction.Lang
}
+
+ if len(this.CaptchaType) == 0 {
+ this.CaptchaType = waf.DefaultCaptchaAction.CaptchaType
+ }
}
return nil
diff --git a/internal/waf/captcha_validator.go b/internal/waf/captcha_validator.go
index e327af4..7b86cef 100644
--- a/internal/waf/captcha_validator.go
+++ b/internal/waf/captcha_validator.go
@@ -3,11 +3,16 @@ package waf
import (
"bytes"
"encoding/base64"
+ "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
+ "github.com/TeaOSLab/EdgeNode/internal/ttlcache"
"github.com/TeaOSLab/EdgeNode/internal/utils"
+ "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
"github.com/dchest/captcha"
"github.com/iwind/TeaGo/logs"
+ "github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
+ stringutil "github.com/iwind/TeaGo/utils/string"
"net/http"
"strconv"
"strings"
@@ -23,7 +28,7 @@ func NewCaptchaValidator() *CaptchaValidator {
return &CaptchaValidator{}
}
-func (this *CaptchaValidator) Run(req requests.Request, writer http.ResponseWriter) {
+func (this *CaptchaValidator) Run(req requests.Request, writer http.ResponseWriter, defaultCaptchaType firewallconfigs.ServerCaptchaType) {
var info = req.WAFRaw().URL.Query().Get("info")
if len(info) == 0 {
req.ProcessResponseHeaders(writer.Header(), http.StatusBadRequest)
@@ -71,16 +76,39 @@ func (this *CaptchaValidator) Run(req requests.Request, writer http.ResponseWrit
return
}
+ var captchaType = captchaActionConfig.CaptchaType
+ if len(defaultCaptchaType) > 0 && defaultCaptchaType != firewallconfigs.ServerCaptchaTypeNone {
+ captchaType = defaultCaptchaType
+ }
+
if req.WAFRaw().Method == http.MethodPost && len(req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")) > 0 {
- this.validate(captchaActionConfig, policyId, groupId, setId, originURL, req, writer)
+ switch captchaType {
+ case firewallconfigs.CaptchaTypeOneClick:
+ this.validateOneClickForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer)
+ case firewallconfigs.CaptchaTypeSlide:
+ this.validateSlideForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer)
+ default:
+ this.validateVerifyCodeForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer)
+ }
} else {
// 增加计数
CaptchaIncreaseFails(req, captchaActionConfig, policyId, groupId, setId, CaptchaPageCodeShow)
- this.show(captchaActionConfig, req, writer)
+ this.show(captchaActionConfig, req, writer, captchaType)
}
}
-func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
+func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter, captchaType firewallconfigs.ServerCaptchaType) {
+ switch captchaType {
+ case firewallconfigs.CaptchaTypeOneClick:
+ this.showOneClickForm(actionConfig, req, writer)
+ case firewallconfigs.CaptchaTypeSlide:
+ this.showSlideForm(actionConfig, req, writer)
+ default:
+ this.showVerifyCodesForm(actionConfig, req, writer)
+ }
+}
+
+func (this *CaptchaValidator) showVerifyCodesForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
// show captcha
var countLetters = 6
if actionConfig.CountLetters > 0 && actionConfig.CountLetters <= 10 {
@@ -168,7 +196,7 @@ func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Req
` + `
@@ -176,7 +204,7 @@ func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Req
` + requestIdBox + `
-` + msgFooter + ``
+` + msgFooter
// Body
if actionConfig.UIIsOn {
@@ -221,8 +249,7 @@ func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Req
_, _ = writer.Write([]byte(msgHTML))
}
-func (this *CaptchaValidator) validate(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) {
-
+func (this *CaptchaValidator) validateVerifyCodeForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) {
var captchaId = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")
if len(captchaId) > 0 {
var captchaCode = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_CODE")
@@ -255,3 +282,356 @@ func (this *CaptchaValidator) validate(actionConfig *CaptchaAction, policyId int
return true
}
+
+func (this *CaptchaValidator) showOneClickForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
+ var lang = actionConfig.Lang
+ if len(lang) == 0 {
+ var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
+ if len(acceptLanguage) > 0 {
+ langIndex := strings.Index(acceptLanguage, ",")
+ if langIndex > 0 {
+ lang = acceptLanguage[:langIndex]
+ }
+ }
+ }
+ if len(lang) == 0 {
+ lang = "en-US"
+ }
+
+ var msgTitle string
+ var msgPrompt string
+ var msgRequestId string
+
+ switch lang {
+ case "zh-CN":
+ msgTitle = "身份验证"
+ msgPrompt = "我不是机器人"
+ msgRequestId = "请求ID"
+ case "zh-TW":
+ msgTitle = "身份驗證"
+ msgPrompt = "我不是機器人"
+ msgRequestId = "請求ID"
+ default:
+ msgTitle = "Verify Yourself"
+ msgPrompt = "I'm not a robot"
+ msgRequestId = "Request ID"
+ }
+
+ var msgCss = ""
+ var requestIdBox = `` + msgRequestId + `: ` + req.Format("${requestId}") + ``
+ var msgFooter = ""
+
+ // 默认设置
+ if actionConfig.OneClickUIIsOn {
+ if len(actionConfig.OneClickUIPrompt) > 0 {
+ msgPrompt = actionConfig.OneClickUIPrompt
+ }
+ if len(actionConfig.OneClickUITitle) > 0 {
+ msgTitle = actionConfig.OneClickUITitle
+ }
+ if len(actionConfig.OneClickUICss) > 0 {
+ msgCss = actionConfig.OneClickUICss
+ }
+ if !actionConfig.OneClickUIShowRequestId {
+ requestIdBox = ""
+ }
+ if len(actionConfig.OneClickUIFooter) > 0 {
+ msgFooter = actionConfig.OneClickUIFooter
+ }
+ }
+
+ var captchaId = stringutil.Md5(req.WAFRemoteIP() + "@" + stringutil.Rand(32))
+ var nonce = rands.Int64()
+ if !ttlcache.SharedInt64Cache.Write("WAF_CAPTCHA:"+captchaId, nonce, fasttime.Now().Unix()+600) {
+ return
+ }
+
+ var body = `
+` + requestIdBox + `
+` + msgFooter
+
+ // Body
+ if actionConfig.OneClickUIIsOn {
+ if len(actionConfig.OneClickUIBody) > 0 {
+ var index = strings.Index(actionConfig.OneClickUIBody, "${body}")
+ if index < 0 {
+ body = actionConfig.OneClickUIBody + body
+ } else {
+ body = actionConfig.OneClickUIBody[:index] + body + actionConfig.OneClickUIBody[index+7:] // 7是"${body}"的长度
+ }
+ }
+ }
+
+ var msgHTML = `
+
+
+ ` + msgTitle + `
+
+
+
+
+
+` + body + `
+
+`
+
+ req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
+ writer.Header().Set("Content-Type", "text/html; charset=utf-8")
+ writer.Header().Set("Content-Length", types.String(len(msgHTML)))
+ writer.WriteHeader(http.StatusOK)
+ _, _ = writer.Write([]byte(msgHTML))
+}
+
+func (this *CaptchaValidator) validateOneClickForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) {
+ var captchaId = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")
+ var nonce = req.WAFRaw().FormValue("nonce")
+ if len(captchaId) > 0 {
+ var key = "WAF_CAPTCHA:" + captchaId
+ var cacheItem = ttlcache.SharedInt64Cache.Read(key)
+ ttlcache.SharedInt64Cache.Delete(key)
+ if cacheItem != nil {
+ // 清除计数
+ CaptchaDeleteCacheKey(req)
+
+ if cacheItem.Value == types.Int64(nonce) {
+ var life = CaptchaSeconds
+ if actionConfig.Life > 0 {
+ life = types.Int(actionConfig.Life)
+ }
+
+ // 加入到白名单
+ SharedIPWhiteList.RecordIP("set:"+strconv.FormatInt(setId, 10), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
+
+ req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
+ http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
+
+ return false
+ }
+ } else {
+ // 增加计数
+ if !CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit) {
+ return false
+ }
+
+ req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
+ http.Redirect(writer, req.WAFRaw(), req.WAFRaw().URL.String(), http.StatusSeeOther)
+ }
+ }
+
+ return true
+}
+
+func (this *CaptchaValidator) showSlideForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
+ var lang = actionConfig.Lang
+ if len(lang) == 0 {
+ var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
+ if len(acceptLanguage) > 0 {
+ langIndex := strings.Index(acceptLanguage, ",")
+ if langIndex > 0 {
+ lang = acceptLanguage[:langIndex]
+ }
+ }
+ }
+ if len(lang) == 0 {
+ lang = "en-US"
+ }
+
+ var msgTitle string
+ var msgPrompt string
+ var msgRequestId string
+
+ switch lang {
+ case "zh-CN":
+ msgTitle = "身份验证"
+ msgPrompt = "滑动上面方块到右侧解锁"
+ msgRequestId = "请求ID"
+ case "zh-TW":
+ msgTitle = "身份驗證"
+ msgPrompt = "滑動上面方塊到右側解鎖"
+ msgRequestId = "請求ID"
+ default:
+ msgTitle = "Verify Yourself"
+ msgPrompt = "Slide to Unlock"
+ msgRequestId = "Request ID"
+ }
+
+ var msgCss = ""
+ var requestIdBox = `` + msgRequestId + `: ` + req.Format("${requestId}") + ``
+ var msgFooter = ""
+
+ // 默认设置
+ if actionConfig.OneClickUIIsOn {
+ if len(actionConfig.OneClickUIPrompt) > 0 {
+ msgPrompt = actionConfig.OneClickUIPrompt
+ }
+ if len(actionConfig.OneClickUITitle) > 0 {
+ msgTitle = actionConfig.OneClickUITitle
+ }
+ if len(actionConfig.OneClickUICss) > 0 {
+ msgCss = actionConfig.OneClickUICss
+ }
+ if !actionConfig.OneClickUIShowRequestId {
+ requestIdBox = ""
+ }
+ if len(actionConfig.OneClickUIFooter) > 0 {
+ msgFooter = actionConfig.OneClickUIFooter
+ }
+ }
+
+ var captchaId = stringutil.Md5(req.WAFRemoteIP() + "@" + stringutil.Rand(32))
+ var nonce = rands.Int64()
+ if !ttlcache.SharedInt64Cache.Write("WAF_CAPTCHA:"+captchaId, nonce, fasttime.Now().Unix()+600) {
+ return
+ }
+
+ var body = `
+` + requestIdBox + `
+` + msgFooter
+
+ // Body
+ if actionConfig.OneClickUIIsOn {
+ if len(actionConfig.OneClickUIBody) > 0 {
+ var index = strings.Index(actionConfig.OneClickUIBody, "${body}")
+ if index < 0 {
+ body = actionConfig.OneClickUIBody + body
+ } else {
+ body = actionConfig.OneClickUIBody[:index] + body + actionConfig.OneClickUIBody[index+7:] // 7是"${body}"的长度
+ }
+ }
+ }
+
+ var msgHTML = `
+
+
+ ` + msgTitle + `
+
+
+
+
+
+` + body + `
+
+`
+
+ req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
+ writer.Header().Set("Content-Type", "text/html; charset=utf-8")
+ writer.Header().Set("Content-Length", types.String(len(msgHTML)))
+ writer.WriteHeader(http.StatusOK)
+ _, _ = writer.Write([]byte(msgHTML))
+}
+
+func (this *CaptchaValidator) validateSlideForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) {
+ var captchaId = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")
+ var nonce = req.WAFRaw().FormValue("nonce")
+ if len(captchaId) > 0 {
+ var key = "WAF_CAPTCHA:" + captchaId
+ var cacheItem = ttlcache.SharedInt64Cache.Read(key)
+ ttlcache.SharedInt64Cache.Delete(key)
+ if cacheItem != nil {
+ // 清除计数
+ CaptchaDeleteCacheKey(req)
+
+ if cacheItem.Value == types.Int64(nonce) {
+ var life = CaptchaSeconds
+ if actionConfig.Life > 0 {
+ life = types.Int(actionConfig.Life)
+ }
+
+ // 加入到白名单
+ SharedIPWhiteList.RecordIP("set:"+strconv.FormatInt(setId, 10), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
+
+ req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
+ http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
+
+ return false
+ }
+ } else {
+ // 增加计数
+ if !CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit) {
+ return false
+ }
+
+ req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
+ http.Redirect(writer, req.WAFRaw(), req.WAFRaw().URL.String(), http.StatusSeeOther)
+ }
+ }
+
+ return true
+}
diff --git a/internal/waf/rule_group.go b/internal/waf/rule_group.go
index f21ae67..21cc235 100644
--- a/internal/waf/rule_group.go
+++ b/internal/waf/rule_group.go
@@ -74,7 +74,7 @@ func (this *RuleGroup) RemoveRuleSet(id int64) {
this.RuleSets = result
}
-func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, set *RuleSet, err error) {
+func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, resultSet *RuleSet, err error) {
if !this.hasRuleSets {
return
}
@@ -93,7 +93,7 @@ func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, hasRequestBod
return
}
-func (this *RuleGroup) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, set *RuleSet, err error) {
+func (this *RuleGroup) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, resultSet *RuleSet, err error) {
if !this.hasRuleSets {
return
}
diff --git a/internal/waf/waf.go b/internal/waf/waf.go
index 4559170..97433e4 100644
--- a/internal/waf/waf.go
+++ b/internal/waf/waf.go
@@ -242,7 +242,7 @@ func (this *WAF) MoveOutboundRuleGroup(fromIndex int, toIndex int) {
this.Outbound = result
}
-func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter) (goNext bool, hasRequestBody bool, group *RuleGroup, sets *RuleSet, err error) {
+func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter, defaultCaptchaType firewallconfigs.ServerCaptchaType) (goNext bool, hasRequestBody bool, resultGroup *RuleGroup, resultSet *RuleSet, err error) {
if !this.hasInboundRules {
return true, hasRequestBody, nil, nil, nil
}
@@ -251,7 +251,7 @@ func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter)
var rawPath = req.WAFRaw().URL.Path
if rawPath == CaptchaPath {
req.DisableAccessLog()
- captchaValidator.Run(req, writer)
+ captchaValidator.Run(req, writer, defaultCaptchaType)
return
}
@@ -284,7 +284,7 @@ func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter)
return true, hasRequestBody, nil, nil, nil
}
-func (this *WAF) MatchResponse(req requests.Request, rawResp *http.Response, writer http.ResponseWriter) (goNext bool, hasRequestBody bool, group *RuleGroup, set *RuleSet, err error) {
+func (this *WAF) MatchResponse(req requests.Request, rawResp *http.Response, writer http.ResponseWriter) (goNext bool, hasRequestBody bool, resultGroup *RuleGroup, resultSet *RuleSet, err error) {
if !this.hasOutboundRules {
return true, hasRequestBody, nil, nil, nil
}
@@ -310,7 +310,7 @@ func (this *WAF) MatchResponse(req requests.Request, rawResp *http.Response, wri
return true, hasRequestBody, nil, nil, nil
}
-// Save save to file path
+// Save to file path
func (this *WAF) Save(path string) error {
if len(path) == 0 {
return errors.New("path should not be empty")
@@ -385,7 +385,7 @@ func (this *WAF) FindCheckpointInstance(prefix string) checkpoints.CheckpointInt
return nil
}
-// Start start
+// Start
func (this *WAF) Start() {
for _, checkpoint := range this.checkpointsMap {
checkpoint.Start()
diff --git a/internal/waf/waf_manager.go b/internal/waf/waf_manager.go
index 59eac4c..644ab38 100644
--- a/internal/waf/waf_manager.go
+++ b/internal/waf/waf_manager.go
@@ -192,6 +192,7 @@ func (this *WAFManager) ConvertWAF(policy *firewallconfigs.HTTPFirewallPolicy) (
FailBlockTimeout: policy.CaptchaOptions.FailBlockTimeout,
FailBlockScopeAll: policy.CaptchaOptions.FailBlockScopeAll,
CountLetters: policy.CaptchaOptions.CountLetters,
+ CaptchaType: policy.CaptchaOptions.CaptchaType,
UIIsOn: policy.CaptchaOptions.UIIsOn,
UITitle: policy.CaptchaOptions.UITitle,
UIPrompt: policy.CaptchaOptions.UIPrompt,