Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 5770f30

Browse files
authored
feat/enterpriseportal: validate and normalize instance domains (#63415)
Validates domains on update (subscription and member) to ensure that we don't get anything too wonky. ## Test plan Tests
1 parent dfabe64 commit 5770f30

File tree

10 files changed

+130
-240
lines changed

10 files changed

+130
-240
lines changed

cmd/enterprise-portal/internal/subscriptionsservice/v1.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@ func (s *handlerV1) UpdateEnterpriseSubscription(ctx context.Context, req *conne
335335
}
336336
}
337337

338+
// Validate and normalize the domain
339+
if opts.InstanceDomain != "" {
340+
opts.InstanceDomain, err = subscriptionsv1.NormalizeInstanceDomain(opts.InstanceDomain)
341+
if err != nil {
342+
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err, "invalid instance domain"))
343+
}
344+
}
345+
338346
subscription, err := s.store.UpsertEnterpriseSubscription(ctx, subscriptionID, opts)
339347
if err != nil {
340348
return nil, connectutil.InternalError(ctx, logger, err, "upsert subscription")
@@ -396,6 +404,11 @@ func (s *handlerV1) UpdateEnterpriseSubscriptionMembership(ctx context.Context,
396404
return nil, connect.NewError(connect.CodeNotFound, errors.New("subscription not found"))
397405
}
398406
} else if instanceDomain != "" {
407+
// Validate and normalize the domain
408+
instanceDomain, err = subscriptionsv1.NormalizeInstanceDomain(instanceDomain)
409+
if err != nil {
410+
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err, "invalid instance domain"))
411+
}
399412
subscriptions, err := s.store.ListEnterpriseSubscriptions(
400413
ctx,
401414
database.ListEnterpriseSubscriptionsOptions{

lib/enterpriseportal/subscriptions/v1/BUILD.bazel

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("//dev:go_defs.bzl", "go_test")
12
load("@io_bazel_rules_go//go:def.bzl", "go_library")
23
load("@rules_proto//proto:defs.bzl", "proto_library")
34
load("@rules_proto_grpc//doc:defs.bzl", "doc_template_compile")
@@ -30,6 +31,7 @@ go_library(
3031
importpath = "github.com/sourcegraph/sourcegraph/lib/enterpriseportal/subscriptions/v1",
3132
visibility = ["//visibility:public"],
3233
deps = [
34+
"//lib/errors",
3335
"@org_golang_google_grpc//:grpc",
3436
"@org_golang_google_grpc//codes",
3537
"@org_golang_google_grpc//status",
@@ -46,3 +48,14 @@ buf_lint_test(
4648
config = "//internal:buf.yaml",
4749
targets = [":v1_proto"],
4850
)
51+
52+
go_test(
53+
name = "subscriptions_test",
54+
srcs = ["v1_test.go"],
55+
embed = [":subscriptions"],
56+
deps = [
57+
"@com_github_hexops_autogold_v2//:autogold",
58+
"@com_github_stretchr_testify//assert",
59+
"@com_github_stretchr_testify//require",
60+
],
61+
)

lib/enterpriseportal/subscriptions/v1/v1.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package v1
22

3+
import (
4+
"net/url"
5+
"strings"
6+
7+
"github.com/sourcegraph/sourcegraph/lib/errors"
8+
)
9+
310
const (
411
// EnterpriseSubscriptionIDPrefix is the prefix for a subscription ID
512
// ('es' for 'Enterprise Subscription').
@@ -8,3 +15,30 @@ const (
815
// ('esl' for 'Enterprise Subscription License').
916
EnterpriseSubscriptionLicenseIDPrefix = "esl_"
1017
)
18+
19+
// NormalizeInstanceDomain normalizes the given instance domain to just the
20+
// hostname.
21+
func NormalizeInstanceDomain(domain string) (string, error) {
22+
// Basic validation because url.Parse is VERY generous
23+
if domain == "" {
24+
return "", errors.New("domain is empty")
25+
}
26+
if !strings.Contains(domain, ".") {
27+
return "", errors.New("domain does contain a '.'")
28+
}
29+
30+
u, err := url.Parse(domain)
31+
if err != nil {
32+
return "", errors.Wrap(err, "domain is not a valid URL")
33+
}
34+
35+
// If the parsing didn't find a scheme, assume HTTP and try again.
36+
if u.Scheme == "" && u.Host == "" {
37+
u, err = url.Parse("http://" + domain)
38+
if err != nil {
39+
return "", errors.Wrap(err, "invalid URL")
40+
}
41+
}
42+
43+
return u.Host, nil
44+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package v1
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hexops/autogold/v2"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestNormalizeInstanceDomain(t *testing.T) {
12+
for _, tc := range []struct {
13+
name string
14+
domain string
15+
wantDomain autogold.Value
16+
wantError autogold.Value
17+
}{{
18+
name: "normal URL",
19+
domain: "https://souregraph.com/",
20+
wantDomain: autogold.Expect("souregraph.com"),
21+
}, {
22+
name: "already a host",
23+
domain: "sourcegraph.com",
24+
wantDomain: autogold.Expect("sourcegraph.com"),
25+
}, {
26+
name: "subdomain",
27+
domain: "foo.sourcegraph.com",
28+
wantDomain: autogold.Expect("foo.sourcegraph.com"),
29+
}, {
30+
name: "host with trailing slash",
31+
domain: "sourcegraph.com/",
32+
wantDomain: autogold.Expect("sourcegraph.com"),
33+
}, {
34+
name: "normal URL with path",
35+
domain: "https://souregraph.com/search",
36+
wantDomain: autogold.Expect("souregraph.com"),
37+
}, {
38+
name: "clearly not a domain",
39+
domain: "foo-bar",
40+
wantError: autogold.Expect("domain does contain a '.'"),
41+
}, {
42+
name: "empty value",
43+
domain: "",
44+
wantError: autogold.Expect("domain is empty"),
45+
}} {
46+
t.Run(tc.name, func(t *testing.T) {
47+
gotDomain, err := NormalizeInstanceDomain(tc.domain)
48+
if tc.wantError != nil {
49+
require.Error(t, err)
50+
tc.wantError.Equal(t, err.Error())
51+
} else {
52+
assert.NoError(t, err)
53+
}
54+
if tc.wantDomain != nil {
55+
tc.wantDomain.Equal(t, gotDomain)
56+
} else {
57+
assert.Empty(t, gotDomain)
58+
}
59+
})
60+
}
61+
}

lib/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ require (
2020
github.com/google/uuid v1.6.0
2121
github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db
2222
github.com/hexops/autogold/v2 v2.0.3
23-
github.com/jackc/pgconn v1.14.0
23+
github.com/jackc/pgconn v1.14.3
2424
github.com/json-iterator/go v1.1.12
2525
github.com/klauspost/pgzip v1.2.5
2626
github.com/mattn/go-isatty v0.0.18

0 commit comments

Comments
 (0)