func (vd *validator) extractCSRFToken(body string) string {
	anchor := `X-Csrf-Token":"`
	i := strings.Index(body, anchor)
	if i == -1 {
		return ""
	}
	j := strings.Index(body[i+len(anchor):], `","`)
	if j == -1 {
		return ""
	}
	return body[i+len(anchor) : i+len(anchor)+j]
}
extractCSRFToken extracts CSRF token from HTML response body.
package main
import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"text/template"
	"time"
	"github.com/cockroachdb/errors"
	jsoniter "github.com/json-iterator/go"
	"github.com/mattn/go-isatty"
	"gopkg.in/yaml.v3"
	"github.com/sourcegraph/src-cli/internal/api"
	"github.com/sourcegraph/src-cli/internal/cmderrors"
)
type validationSpec struct {
	FirstAdmin struct {
		Email             string `yaml:"email"`
		Username          string `yaml:"username"`
		Password          string `yaml:"password"`
		CreateAccessToken bool   `yaml:"createAccessToken"`
	} `yaml:"firstAdmin"`
	WaitRepoCloned struct {
		Repo                     string `yaml:"repo"`
		MaxTries                 int    `yaml:"maxTries"`
		SleepBetweenTriesSeconds int    `yaml:"sleepBetweenTriesSecond"`
	} `yaml:"waitRepoCloned"`
	SearchQuery     string `yaml:"searchQuery"`
	ExternalService struct {
		Kind           string                 `yaml:"kind"`
		DisplayName    string                 `yaml:"displayName"`
		Config         map[string]interface{} `yaml:"config"`
		DeleteWhenDone bool                   `yaml:"deleteWhenDone"`
	} `yaml:"externalService"`
}
type validator struct {
	client    *vdClient
	apiClient api.Client
}
func init() {
	usage := `'src validate' is a tool that validates a Sourcegraph instance.
EXPERIMENTAL: 'validate' is an experimental command in the 'src' tool.
Usage:
	src validate [options] src-validate.yml
or
    cat src-validate.yml | src validate [options]
Please visit https: 
`
	flagSet := flag.NewFlagSet("validate", flag.ExitOnError)
	usageFunc := func() {
		_, _ = fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src validate %s':\n", flagSet.Name())
		flagSet.PrintDefaults()
		fmt.Println(usage)
	}
	var (
		contextFlag = flagSet.String("context", "", `Comma-separated list of key=value pairs to add to the script execution context`)
		secretsFlag = flagSet.String("secrets", "", "Path to a file containing key=value lines. The key value pairs will be added to the script context")
		apiFlags    = api.NewFlags(flagSet)
	)
	handler := func(args []string) error {
		if err := flagSet.Parse(args); err != nil {
			return err
		}
		client := cfg.apiClient(apiFlags, flagSet.Output())
		vd := &validator{
			apiClient: client,
		}
		var script []byte
		var isYaml bool
		var err error
		if len(flagSet.Args()) == 1 {
			filename := flagSet.Arg(0)
			script, err = os.ReadFile(filename)
			if err != nil {
				return err
			}
			if strings.HasSuffix(filename, ".yaml") || strings.HasSuffix(filename, ".yml") {
				isYaml = true
			}
		}
		if !isatty.IsTerminal(os.Stdin.Fd()) {
			script, err = io.ReadAll(os.Stdin)
			if err != nil {
				return err
			}
			isYaml = true
		}
		ctxm := vd.parseKVPairs(*contextFlag, ",")
		if *secretsFlag != "" {
			sm, err := vd.readSecrets(*secretsFlag)
			if err != nil {
				return err
			}
			for k, v := range sm {
				ctxm[k] = v
			}
		}
		return vd.validate(script, ctxm, isYaml)
	}
	commands = append(commands, &command{
		flagSet:   flagSet,
		handler:   handler,
		usageFunc: usageFunc,
	})
}
func (vd *validator) parseKVPairs(val string, pairSep string) map[string]string {
	scriptContext := make(map[string]string)
	pairs := strings.Split(val, pairSep)
	for _, pair := range pairs {
		kv := strings.Split(pair, "=")
		if len(kv) == 2 {
			scriptContext[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
		}
	}
	return scriptContext
}
func (vd *validator) readSecrets(path string) (map[string]string, error) {
	bs, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return vd.parseKVPairs(string(bs), "\n"), nil
}
func (vd *validator) validate(script []byte, scriptContext map[string]string, isYaml bool) error {
	tpl, err := template.New("validate").Parse(string(script))
	if err != nil {
		return err
	}
	var ts bytes.Buffer
	err = tpl.Execute(&ts, scriptContext)
	if err != nil {
		return err
	}
	var vspec validationSpec
	if isYaml {
		if err := yaml.Unmarshal(ts.Bytes(), &vspec); err != nil {
			return err
		}
	} else {
		if err := json.Unmarshal(ts.Bytes(), &vspec); err != nil {
			return err
		}
	}
	if vspec.FirstAdmin.Username != "" {
		err = vd.createFirstAdmin(&vspec)
		if err != nil {
			return err
		}
		if vspec.FirstAdmin.CreateAccessToken {
			token, err := vd.createAccessToken(vspec.FirstAdmin.Username)
			if err != nil {
				return err
			}
			fmt.Println(token)
		}
	}
	if vspec.ExternalService.DisplayName != "" {
		extSvcID, err := vd.addExternalService(&vspec)
		if err != nil {
			return err
		}
		defer func() {
			if extSvcID != "" && vspec.ExternalService.DeleteWhenDone {
				_ = vd.deleteExternalService(extSvcID)
			}
		}()
	}
	if vspec.WaitRepoCloned.Repo != "" {
		cloned, err := vd.waitRepoCloned(vspec.WaitRepoCloned.Repo, vspec.WaitRepoCloned.SleepBetweenTriesSeconds,
			vspec.WaitRepoCloned.MaxTries)
		if err != nil {
			return err
		}
		if !cloned {
			return fmt.Errorf("repo %s didn't clone", vspec.WaitRepoCloned.Repo)
		}
	}
	if vspec.SearchQuery != "" {
		matchCount, err := vd.searchMatchCount(vspec.SearchQuery)
		if err != nil {
			return err
		}
		if matchCount == 0 {
			return fmt.Errorf("search query %s returned no results", vspec.SearchQuery)
		}
	}
	return nil
}
const vdAddExternalServiceQuery = `
mutation AddExternalService($kind: ExternalServiceKind!, $displayName: String!, $config: String!) {
  addExternalService(input:{
    kind:$kind,
    displayName:$displayName,
    config: $config
  })
  {
    id
  }
}`
func (vd *validator) addExternalService(vspec *validationSpec) (string, error) {
	configJson, err := json.Marshal(vspec.ExternalService.Config)
	if err != nil {
		return "", err
	}
	var resp struct {
		AddExternalService struct {
			ID string `json:"id"`
		} `json:"addExternalService"`
	}
	err = vd.graphQL(vdAddExternalServiceQuery, map[string]interface{}{
		"kind":        vspec.ExternalService.Kind,
		"displayName": vspec.ExternalService.DisplayName,
		"config":      string(configJson),
	}, &resp)
	return resp.AddExternalService.ID, err
}
const vdDeleteExternalServiceQuery = `
mutation DeleteExternalService($id: ID!) {
  deleteExternalService(externalService: $id){
    alwaysNil
  } 
}`
func (vd *validator) deleteExternalService(id string) error {
	var resp struct{}
	return vd.graphQL(vdDeleteExternalServiceQuery, map[string]interface{}{
		"id": id,
	}, &resp)
}
const vdSearchMatchCountQuery = `
query ($query: String!) {
  search(query: $query, version: V2, patternType:literal) {
    results {
      matchCount
    }
  }
}`
func (vd *validator) searchMatchCount(searchStr string) (int, error) {
	var resp struct {
		Search struct {
			Results struct {
				MatchCount int `json:"matchCount"`
			} `json:"results"`
		} `json:"search"`
	}
	err := vd.graphQL(vdSearchMatchCountQuery, map[string]interface{}{
		"query": searchStr,
	}, &resp)
	return resp.Search.Results.MatchCount, err
}
const vdListRepos = `
query ListRepos($names: [String!]) {
  repositories(
    names: $names
  ) {
    nodes {
      name
      mirrorInfo {
         cloned
      }
    }
  }
}`
func (vd *validator) listClonedRepos(fs []string) ([]string, error) {
	var resp struct {
		Repositories struct {
			Nodes []struct {
				Name       string `json:"name"`
				MirrorInfo struct {
					Cloned bool `json:"cloned"`
				} `json:"mirrorInfo"`
			} `json:"nodes"`
		} `json:"repositories"`
	}
	err := vd.graphQL(vdListRepos, map[string]interface{}{
		"names": fs,
	}, &resp)
	names := make([]string, 0, len(resp.Repositories.Nodes))
	for _, node := range resp.Repositories.Nodes {
		if node.MirrorInfo.Cloned {
			names = append(names, node.Name)
		}
	}
	return names, err
}
func (vd *validator) waitRepoCloned(repoName string, sleepSeconds int, maxTries int) (bool, error) {
	nameFilter := []string{repoName}
	for i := 0; i < maxTries; i++ {
		names, err := vd.listClonedRepos(nameFilter)
		if err != nil {
			return false, err
		}
		if len(names) == 1 {
			return true, nil
		}
		time.Sleep(time.Second * time.Duration(sleepSeconds))
	}
	return false, nil
}
const vdUserQuery = `
query User($username: String) {
  user(username: $username) {
      id
  }
}`
func (vd *validator) userID(username string) (string, error) {
	var resp struct {
		User struct {
			ID string `json:"id"`
		} `json:"user"`
	}
	err := vd.graphQL(vdUserQuery, map[string]interface{}{
		"username": username,
	}, &resp)
	return resp.User.ID, err
}
const vdCreateAccessTokenMutation = `
mutation CreateAccessToken($user: ID!, $scopes: [String!]!, $note: String!) {
  createAccessToken(
    user:$user,
    scopes:$scopes,
    note: $note
  )
  {
    token
  }
}`
func (vd *validator) createAccessToken(username string) (string, error) {
	userID, err := vd.userID(username)
	if err != nil {
		return "", err
	}
	var resp struct {
		CreateAccessToken struct {
			Token string `json:"token"`
		} `json:"createAccessToken"`
	}
	err = vd.graphQL(vdCreateAccessTokenMutation, map[string]interface{}{
		"user":   userID,
		"scopes": []string{"user:all", "site-admin:sudo"},
		"note":   "src_cli_validate",
	}, &resp)
	return resp.CreateAccessToken.Token, err
}
func (vd *validator) siteAdminInit(baseURL, email, username, password string) (*vdClient, error) {
	client, err := vd.newClient(baseURL)
	if err != nil {
		return nil, err
	}
	var request = struct {
		Email    string `json:"email"`
		Username string `json:"username"`
		Password string `json:"password"`
	}{
		Email:    email,
		Username: username,
		Password: password,
	}
	err = client.authenticate("/-/site-init", request)
	if err != nil {
		return nil, err
	}
	return client, nil
}
func (vd *validator) signIn(baseURL string, email, password string) (*vdClient, error) {
	client, err := vd.newClient(baseURL)
	if err != nil {
		return nil, err
	}
	var request = struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}{
		Email:    email,
		Password: password,
	}
	err = client.authenticate("/-/sign-in", request)
	if err != nil {
		return nil, err
	}
	return client, nil
}