CVE-2021-21432 (Go)

Vela's direct token authentication flow accepts a user-supplied GitHub token and uses it to authenticate the requester. Tokens issued for Vela's own configured GitHub OAuth application can be injected into build environments, such as through a generated ~/.netrc file, and must not be reusable as bearer credentials against Vela's /authenticate/token-style authentication path. An attacker who can obtain or cause use of such injected Vela OAuth credentials could authenticate as the associated GitHub user and gain access to protected Vela resources or secrets.

Weakness:
CWE-862: name: Missing Authorization
description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
CWE-639: name: Authorization Bypass Through User-Controlled Key
description: The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.

Repository: https://github.com/go-vela/server

Vulnerable code:
file_path: source/github/authentication.go
snippet: func (c *client) AuthenticateToken(r *http.Request) (*library.User, error) {
	logrus.Trace("Authenticating user via token")

	token := r.Header.Get("Token")
	if len(token) == 0 {
		return nil, errors.New("no token provided")
	}

	u, err := c.Authorize(token)
	if err != nil {
		return nil, err
	}

	return &library.User{
		Name:  &u,
		Token: &token,
	}, nil
}

Fixed code:
file_path: source/github/authentication.go
snippet: import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/go-vela/server/random"
	"github.com/go-vela/types/library"
	"github.com/google/go-github/v33/github"

	"github.com/sirupsen/logrus"
)

file_path: source/github/authentication.go
snippet: func (c *client) AuthenticateToken(r *http.Request) (*library.User, error) {
	logrus.Trace("Authenticating user via token")

	token := r.Header.Get("Token")
	if len(token) == 0 {
		return nil, errors.New("no token provided")
	}

	// create http client to connect to GitHub
	//
	// nolint: lll // ignore long line length due to variable names
	transport := github.BasicAuthTransport{Username: c.OConfig.ClientID, Password: c.OConfig.ClientSecret}
	// create client to connect to GitHub API
	client := github.NewClient(transport.Client())
	// check if github url was set
	if c.URL != "" && c.URL != "https://github.com" {
		// check if address has trailing slash
		if !strings.HasSuffix(c.URL, "/") {
			// add trailing slash
			c.URL = c.URL + "/api/v3/"
		}
		// parse the provided url into url type
		enterpriseURL, err := url.Parse(c.URL)
		if err != nil {
			return nil, err
		}
		// set the base and upload url
		client.BaseURL = enterpriseURL
		client.UploadURL = enterpriseURL
	}
	// check if the provided token was created by Vela
	_, resp, err := client.Authorizations.Check(context.Background(), c.OConfig.ClientID, token)
	// check if the error is of type ErrorResponse
	if gerr, ok := err.(*github.ErrorResponse); ok {
		// check the status code
		switch gerr.Response.StatusCode {
		// 404 is expected when non vela token is used
		case http.StatusNotFound:
			break
		default:
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}

	// return error if the token was created by Vela
	if resp.StatusCode != http.StatusNotFound {
		return nil, errors.New("token must not be created by vela")
	}

	u, err := c.Authorize(token)
	if err != nil {
		return nil, err
	}

	return &library.User{
		Name:  &u,
		Token: &token,
	}, nil
}