Skip to content

Commit 9e81f96

Browse files
committed
[pat] Validate PAT name
1 parent 64ed64a commit 9e81f96

File tree

5 files changed

+44
-6
lines changed

5 files changed

+44
-6
lines changed

components/public-api-server/pkg/apiv1/tokens.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"context"
99
"errors"
1010
"fmt"
11+
"regexp"
1112
"strings"
1213

1314
connect "github.com/bufbuild/connect-go"
@@ -45,9 +46,9 @@ type TokensService struct {
4546
func (s *TokensService) CreatePersonalAccessToken(ctx context.Context, req *connect.Request[v1.CreatePersonalAccessTokenRequest]) (*connect.Response[v1.CreatePersonalAccessTokenResponse], error) {
4647
tokenReq := req.Msg.GetToken()
4748

48-
name := strings.TrimSpace(tokenReq.GetName())
49-
if name == "" {
50-
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("Token Name is a required parameter."))
49+
name, err := validatePersonalAccessTokenName(tokenReq.GetName())
50+
if err != nil {
51+
return nil, err
5152
}
5253

5354
expiry := tokenReq.GetExpirationTime()
@@ -328,3 +329,21 @@ func personalAccessTokenToAPI(t db.PersonalAccessToken, value string) *v1.Person
328329
CreatedAt: timestamppb.New(t.CreatedAt),
329330
}
330331
}
332+
333+
var (
334+
// alpha-numeric characters, dashes, underscore, spaces, between 3 and 63 chars
335+
personalAccessTokenNameRegex = regexp.MustCompile(`^[a-zA-Z0-9-_ ]{3,63}$`)
336+
)
337+
338+
func validatePersonalAccessTokenName(name string) (string, error) {
339+
trimmed := strings.TrimSpace(name)
340+
if trimmed == "" {
341+
return "", connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Token Name is a required parameter, but got empty."))
342+
}
343+
344+
if !personalAccessTokenNameRegex.MatchString(trimmed) {
345+
return "", connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Token Name is required to match regexp %s.", personalAccessTokenNameRegex.String()))
346+
}
347+
348+
return trimmed, nil
349+
}

components/public-api-server/pkg/apiv1/tokens_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"net/http"
1111
"net/http/httptest"
12+
"strings"
1213
"testing"
1314
"time"
1415

@@ -74,6 +75,21 @@ func TestTokensService_CreatePersonalAccessTokenWithoutFeatureFlag(t *testing.T)
7475
require.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err))
7576
})
7677

78+
t.Run("invalid argument when name does not match required regex", func(t *testing.T) {
79+
_, _, client := setupTokensService(t, withTokenFeatureDisabled)
80+
81+
names := []string{"a", "ab", strings.Repeat("a", 64), "!#$!%"}
82+
83+
for _, name := range names {
84+
_, err := client.CreatePersonalAccessToken(context.Background(), connect.NewRequest(&v1.CreatePersonalAccessTokenRequest{
85+
Token: &v1.PersonalAccessToken{
86+
Name: name,
87+
},
88+
}))
89+
require.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err))
90+
}
91+
})
92+
7793
t.Run("invalid argument when expiration time is unspecified", func(t *testing.T) {
7894
_, _, client := setupTokensService(t, withTokenFeatureDisabled)
7995

components/public-api/gitpod/experimental/v1/tokens.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ message PersonalAccessToken {
1919
// Read only.
2020
string value = 2;
2121

22-
// name is the name of the token for humans, set by the user
22+
// name is the name of the token for humans, set by the user.
23+
// Must match regexp ^[a-zA-Z0-9-_ ]{3,63}$
2324
string name = 3;
2425

2526
// expiration_time is the time when the token expires

components/public-api/go/experimental/v1/tokens.pb.go

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/public-api/typescript/src/gitpod/experimental/v1/tokens_pb.ts

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)