package onboarding import ( "context" "data-terminal-cloud/internal/config" "errors" captcha "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/captcha/v20190722" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "net/http/httptest" "strings" "testing" ) type fakeCaptchaAPI struct { web, mini int result int64 err error t *testing.T } func (f *fakeCaptchaAPI) DescribeCaptchaResultWithContext(_ context.Context, r *captcha.DescribeCaptchaResultRequest) (*captcha.DescribeCaptchaResultResponse, error) { f.web++ if *r.CaptchaAppId != 123 || *r.AppSecretKey != "web-secret-123456" || *r.Randstr != "random" || *r.CaptchaType != 9 { f.t.Fatal("wrong web request") } return &captcha.DescribeCaptchaResultResponse{Response: &captcha.DescribeCaptchaResultResponseParams{CaptchaCode: common.Int64Ptr(f.result)}}, f.err } func (f *fakeCaptchaAPI) DescribeCaptchaMiniResultWithContext(_ context.Context, r *captcha.DescribeCaptchaMiniResultRequest) (*captcha.DescribeCaptchaMiniResultResponse, error) { f.mini++ if *r.CaptchaAppId != 456 || *r.AppSecretKey != "mini-secret-12345" || *r.CaptchaType != 9 { f.t.Fatal("wrong mini request") } return &captcha.DescribeCaptchaMiniResultResponse{Response: &captcha.DescribeCaptchaMiniResultResponseParams{CaptchaCode: common.Int64Ptr(f.result)}}, f.err } func TestTencentCaptchaProviderSeparatesClientsAndFailsClosed(t *testing.T) { f := &fakeCaptchaAPI{t: t, result: 1} p := &tencentCaptchaProvider{api: f, config: config.TencentCaptchaConfig{SaaS: config.CaptchaClientConfig{AppID: "123", AppSecretKey: "web-secret-123456"}, Mini: config.CaptchaClientConfig{AppID: "456", AppSecretKey: "mini-secret-12345"}}} ctx := context.Background() if err := p.Verify(ctx, "saas", "ticket", "random", "203.0.113.1"); err != nil { t.Fatal(err) } if err := p.Verify(ctx, "mini_program", "ticket", "", "203.0.113.1"); err != nil { t.Fatal(err) } if f.web != 1 || f.mini != 1 { t.Fatal("wrong API") } for _, code := range []int64{0, 7, 8, 9, 21, 100} { f.result = code if p.Verify(ctx, "saas", "ticket", "random", "203.0.113.1") == nil { t.Fatal("rejection accepted", code) } } f.result = 1 f.err = errors.New("contains-secret-value") if err := p.Verify(ctx, "saas", "ticket", "random", "203.0.113.1"); err == nil || strings.Contains(err.Error(), "secret-value") { t.Fatal("error must be closed and sanitized") } before := f.web for _, ticket := range []string{"", "trerror_123", strings.Repeat("x", 4097)} { if p.Verify(ctx, "saas", ticket, "random", "203.0.113.1") == nil { t.Fatal("bad ticket accepted") } } if f.web != before { t.Fatal("bad ticket caused API call") } } func TestCaptchaClientIPTrustBoundary(t *testing.T) { s := &Service{options: Options{TrustedProxyCIDRs: []string{"172.18.0.0/16"}}} for _, tc := range []struct{ remote, forwarded, want string }{ {"203.0.113.9:123", "1.1.1.1", "203.0.113.9"}, {"172.18.0.4:123", "1.1.1.1, 203.0.113.9", "203.0.113.9"}, {"172.18.0.4:123", "203.0.113.9, 172.18.0.3", "203.0.113.9"}, {"[2001:db8::1]:123", "1.1.1.1", "2001:db8::1"}, } { r := httptest.NewRequest("POST", "https://example.test", nil) r.RemoteAddr = tc.remote r.Header.Set("X-Forwarded-For", tc.forwarded) if got := s.clientIP(r); got != tc.want { t.Fatalf("ip %s want %s", got, tc.want) } } }