<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/reviewdog/reviewdog/doghouse"
	"github.com/reviewdog/reviewdog/doghouse/server"
	"github.com/reviewdog/reviewdog/doghouse/server/ciutil"
	"github.com/reviewdog/reviewdog/doghouse/server/storage"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/urlfetch"
)

type githubChecker struct {
	privateKey       []byte
	integrationID    int
	ghInstStore      storage.GitHubInstallationStore
	ghRepoTokenStore storage.GitHubRepositoryTokenStore
}

func (gc *githubChecker) handleCheck(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	ctx := appengine.NewContext(r)

	var req doghouse.CheckRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "failed to decode request: %v", err)
		return
	}

	// Check authorization.
	if !gc.validateCheckRequest(ctx, w, r, req.Owner, req.Repo) {
		return
	}

	opt := &server.NewGitHubClientOption{
		PrivateKey:        gc.privateKey,
		IntegrationID:     gc.integrationID,
		RepoOwner:         req.Owner,
		Client:            urlfetch.Client(ctx),
		InstallationStore: gc.ghInstStore,
	}

	gh, err := server.NewGitHubClient(ctx, opt)
	if err != nil {
		log.Errorf(ctx, "failed to create GitHub client: %v", err)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, err)
		return
	}

	res, err := server.NewChecker(&req, gh).Check(ctx)
	if err != nil {
		log.Errorf(ctx, "failed to run checker: %v", err)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, err)
		return
	}
	if err := json.NewEncoder(w).Encode(res); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, err)
		return
	}
}

func (gc *githubChecker) validateCheckRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, owner, repo string) bool {
	log.Infof(ctx, "Remote Addr: %s", r.RemoteAddr)
	// Always update Travis IP Addresss before checking IP to reduce the # of
	// flaky errors.
	if err := ciutil.UpdateTravisCIIPAddrs(urlfetch.Client(ctx)); err != nil {
		log.Errorf(ctx, "failed to update travis CI IP addresses: %v", err)
	}
	if ciutil.IsFromCI(r) {
		// Skip token validation if it's from trusted CI providers.
		return true
	}
	return gc.validateCheckToken(ctx, w, r, owner, repo)
}

func (gc *githubChecker) validateCheckToken(ctx context.Context, w http.ResponseWriter, r *http.Request, owner, repo string) bool {
	token := extractBearerToken(r)
	if token == "" {
		w.Header().Set("The WWW-Authenticate", `error="invalid_request", error_description="The access token not provided"`)
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintf(w, "The access token not provided. Get token from %s", githubRepoURL(ctx, r, owner, repo))
		return false
	}
	_, wantToken, err := gc.ghRepoTokenStore.Get(ctx, owner, repo)
	if err != nil {
		log.Errorf(ctx, "failed to get repository (%s/%s) token: %v", owner, repo, err)
	}
	if wantToken == nil {
		w.WriteHeader(http.StatusNotFound)
		return false
	}
	if token != wantToken.Token {
		w.Header().Set("The WWW-Authenticate", `error="invalid_token", error_description="The access token is invalid"`)
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintf(w, "The access token is invalid. Get valid token from %s", githubRepoURL(ctx, r, owner, repo))
		return false
	}
	return true
}

func githubRepoURL(ctx context.Context, r *http.Request, owner, repo string) string {
	u := doghouseBaseURL(ctx, r)
	u.Path = fmt.Sprintf("/gh/%s/%s", owner, repo)
	return u.String()
}

func doghouseBaseURL(ctx context.Context, r *http.Request) *url.URL {
	scheme := ""
	if r.URL != nil && r.URL.Scheme != "" {
		scheme = r.URL.Scheme
	}
	if scheme == "" {
		scheme = "https"
		if appengine.IsDevAppServer() {
			scheme = "http"
		}
	}
	u, err := url.Parse(scheme + "://" + r.Host)
	if err != nil {
		log.Errorf(ctx, "%v", err)
	}
	return u
}

func extractBearerToken(r *http.Request) string {
	auth := r.Header.Get("Authorization")
	prefix := "bearer "
	if strings.HasPrefix(strings.ToLower(auth), prefix) {
		return auth[len(prefix):]
	}
	return ""
}
<commit_msg>Check only when token is not present<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/reviewdog/reviewdog/doghouse"
	"github.com/reviewdog/reviewdog/doghouse/server"
	"github.com/reviewdog/reviewdog/doghouse/server/ciutil"
	"github.com/reviewdog/reviewdog/doghouse/server/storage"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/urlfetch"
)

type githubChecker struct {
	privateKey       []byte
	integrationID    int
	ghInstStore      storage.GitHubInstallationStore
	ghRepoTokenStore storage.GitHubRepositoryTokenStore
}

func (gc *githubChecker) handleCheck(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	ctx := appengine.NewContext(r)

	var req doghouse.CheckRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "failed to decode request: %v", err)
		return
	}

	// Check authorization.
	if !gc.validateCheckRequest(ctx, w, r, req.Owner, req.Repo) {
		return
	}

	opt := &server.NewGitHubClientOption{
		PrivateKey:        gc.privateKey,
		IntegrationID:     gc.integrationID,
		RepoOwner:         req.Owner,
		Client:            urlfetch.Client(ctx),
		InstallationStore: gc.ghInstStore,
	}

	gh, err := server.NewGitHubClient(ctx, opt)
	if err != nil {
		log.Errorf(ctx, "failed to create GitHub client: %v", err)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, err)
		return
	}

	res, err := server.NewChecker(&req, gh).Check(ctx)
	if err != nil {
		log.Errorf(ctx, "failed to run checker: %v", err)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, err)
		return
	}
	if err := json.NewEncoder(w).Encode(res); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(w, err)
		return
	}
}

func (gc *githubChecker) validateCheckRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, owner, repo string) bool {
	if extractBearerToken(r) == "" {
		// Update Travis IP Addresss before checking IP to reduce the # of
		// flaky errors when token is not present.
		if err := ciutil.UpdateTravisCIIPAddrs(urlfetch.Client(ctx)); err != nil {
			log.Errorf(ctx, "failed to update travis CI IP addresses: %v", err)
		}
	}
	log.Infof(ctx, "Remote Addr: %s", r.RemoteAddr)
	if ciutil.IsFromCI(r) {
		// Skip token validation if it's from trusted CI providers.
		return true
	}
	return gc.validateCheckToken(ctx, w, r, owner, repo)
}

func (gc *githubChecker) validateCheckToken(ctx context.Context, w http.ResponseWriter, r *http.Request, owner, repo string) bool {
	token := extractBearerToken(r)
	if token == "" {
		w.Header().Set("The WWW-Authenticate", `error="invalid_request", error_description="The access token not provided"`)
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintf(w, "The access token not provided. Get token from %s", githubRepoURL(ctx, r, owner, repo))
		return false
	}
	_, wantToken, err := gc.ghRepoTokenStore.Get(ctx, owner, repo)
	if err != nil {
		log.Errorf(ctx, "failed to get repository (%s/%s) token: %v", owner, repo, err)
	}
	if wantToken == nil {
		w.WriteHeader(http.StatusNotFound)
		return false
	}
	if token != wantToken.Token {
		w.Header().Set("The WWW-Authenticate", `error="invalid_token", error_description="The access token is invalid"`)
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintf(w, "The access token is invalid. Get valid token from %s", githubRepoURL(ctx, r, owner, repo))
		return false
	}
	return true
}

func githubRepoURL(ctx context.Context, r *http.Request, owner, repo string) string {
	u := doghouseBaseURL(ctx, r)
	u.Path = fmt.Sprintf("/gh/%s/%s", owner, repo)
	return u.String()
}

func doghouseBaseURL(ctx context.Context, r *http.Request) *url.URL {
	scheme := ""
	if r.URL != nil && r.URL.Scheme != "" {
		scheme = r.URL.Scheme
	}
	if scheme == "" {
		scheme = "https"
		if appengine.IsDevAppServer() {
			scheme = "http"
		}
	}
	u, err := url.Parse(scheme + "://" + r.Host)
	if err != nil {
		log.Errorf(ctx, "%v", err)
	}
	return u
}

func extractBearerToken(r *http.Request) string {
	auth := r.Header.Get("Authorization")
	prefix := "bearer "
	if strings.HasPrefix(strings.ToLower(auth), prefix) {
		return auth[len(prefix):]
	}
	return ""
}

{'fraction_non_alphanumeric': 0.13963230160577147, 'fraction_numerical': 0.0, 'mean_word_length': 4.177710843373494, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7293849', 'n_tokens_mistral': 3159, 'n_tokens_neox': 3028, 'n_words': 720}