Use auth helpers from utils (#4620)

dev
Ramana Reddy 2024-01-11 19:51:54 +05:30 committed by GitHub
parent 5a430a31e5
commit 53207a0bb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 22 additions and 268 deletions

6
go.mod
View File

@ -90,14 +90,14 @@ require (
github.com/projectdiscovery/sarif v0.0.1
github.com/projectdiscovery/tlsx v1.1.6-0.20231116215000-e842dc367a74
github.com/projectdiscovery/uncover v1.0.7
github.com/projectdiscovery/utils v0.0.72
github.com/projectdiscovery/utils v0.0.73-0.20240110205148-46f474b2947f
github.com/projectdiscovery/wappalyzergo v0.0.109
github.com/redis/go-redis/v9 v9.1.0
github.com/ropnop/gokrb5/v8 v8.0.0-20201111231119-729746023c02
github.com/sashabaranov/go-openai v1.15.3
github.com/stretchr/testify v1.8.4
github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706
golang.org/x/term v0.15.0
golang.org/x/term v0.16.0
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/yaml.v3 v3.0.1
)
@ -286,7 +286,7 @@ require (
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.15.0 // indirect
google.golang.org/appengine v1.6.7 // indirect

12
go.sum
View File

@ -851,8 +851,8 @@ github.com/projectdiscovery/tlsx v1.1.6-0.20231116215000-e842dc367a74 h1:G0gw+3z
github.com/projectdiscovery/tlsx v1.1.6-0.20231116215000-e842dc367a74/go.mod h1:YH8el7/6pyZbNed1IibjzbGpeigiCVyvE28g5+LsPAw=
github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7siFy9sj0A=
github.com/projectdiscovery/uncover v1.0.7/go.mod h1:HFXgm1sRPuoN0D4oATljPIdmbo/EEh1wVuxQqo/dwFE=
github.com/projectdiscovery/utils v0.0.72 h1:sJ1lBcaWO6dJ65F+fVhSJbguhgWjixgy9mjj7jKBUW8=
github.com/projectdiscovery/utils v0.0.72/go.mod h1:VPnijH51D8wB1VJiEujUp7UZ+TUTKN8PpoW82nivUVY=
github.com/projectdiscovery/utils v0.0.73-0.20240110205148-46f474b2947f h1:gjp9RyGSh507ARIrUUBE0u13mMYPDWZxoZEOAneCT4E=
github.com/projectdiscovery/utils v0.0.73-0.20240110205148-46f474b2947f/go.mod h1:B+4cyms2mQtG6KC2qaV5zEVJgyIWzjCwrgO+kpSR5SY=
github.com/projectdiscovery/wappalyzergo v0.0.109 h1:BERfwTRn1dvB1tbhyc5m67R8VkC9zbVuPsEq4VEm07k=
github.com/projectdiscovery/wappalyzergo v0.0.109/go.mod h1:4Z3DKhi75zIPMuA+qSDDWxZvnhL4qTLmDx4dxNMu7MA=
github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE=
@ -1348,8 +1348,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -1361,8 +1361,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -1,76 +0,0 @@
// pdcp contains projectdiscovery cloud platform related features
// like result upload , dashboard etc.
package pdcp
import (
"fmt"
"os"
"strings"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/utils/env"
"golang.org/x/term"
)
var (
DashBoardURL = "https://cloud.projectdiscovery.io"
DefaultApiServer = "https://api.projectdiscovery.io"
)
// CheckNValidateCredentials checks if credentials exist on filesystem
// if not waits for user to enter credentials and validates them
// and saves them to filesystem
// when validate is true any existing credentials are validated
// Note: this is meant to be used in cli only (interactive mode)
func CheckNValidateCredentials(toolName string) {
h := &PDCPCredHandler{}
creds, err := h.GetCreds()
if err == nil {
// validate by fetching user profile
gotCreds, err := h.ValidateAPIKey(creds.APIKey, creds.Server, config.BinaryName)
if err == nil {
gologger.Info().Msgf("You are logged in as (@%v)", gotCreds.Username)
os.Exit(0)
}
gologger.Error().Msgf("Invalid API key found in file, please recheck or recreate your API key and retry.")
}
if err != nil && err != ErrNoCreds {
// this is unexpected error log it
gologger.Error().Msgf("Could not read credentials from file: %s\n", err)
}
// if we are here, we need to get credentials from user
gologger.Info().Msgf("Get your free api key by signing up at %v", DashBoardURL)
fmt.Printf("[*] Enter PDCP API Key (exit to abort): ")
bin, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
gologger.Fatal().Msgf("Could not read input from terminal: %s\n", err)
}
apiKey := string(bin)
if strings.EqualFold(apiKey, "exit") {
os.Exit(0)
}
fmt.Println()
// if env variable is set use that for validating api key
apiServer := env.GetEnvOrDefault(apiServerEnv, DefaultApiServer)
// validate by fetching user profile
validatedCreds, err := h.ValidateAPIKey(apiKey, apiServer, toolName)
if err == nil {
gologger.Info().Msgf("Successfully logged in as (@%v)", validatedCreds.Username)
if saveErr := h.SaveCreds(validatedCreds); saveErr != nil {
gologger.Warning().Msgf("Could not save credentials to file: %s\n", saveErr)
}
os.Exit(0)
}
gologger.Error().Msgf("Invalid API key '%v' got error: %v", maskKey(apiKey), err)
gologger.Fatal().Msgf("please recheck or recreate your API key and retry")
}
func maskKey(key string) string {
if len(key) < 6 {
// this is invalid key
return key
}
return fmt.Sprintf("%v%v", key[:3], strings.Repeat("*", len(key)-3))
}

View File

@ -1,139 +0,0 @@
package pdcp
import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"github.com/projectdiscovery/retryablehttp-go"
"github.com/projectdiscovery/utils/env"
fileutil "github.com/projectdiscovery/utils/file"
folderutil "github.com/projectdiscovery/utils/folder"
urlutil "github.com/projectdiscovery/utils/url"
"gopkg.in/yaml.v3"
)
var (
PDCPDir = filepath.Join(folderutil.HomeDirOrDefault(""), ".pdcp")
PDCPCredFile = filepath.Join(PDCPDir, "credentials.yaml")
ErrNoCreds = fmt.Errorf("no credentials found in %s", PDCPDir)
)
const (
userProfileURL = "https://%s/v1/user?utm_source=%s"
apiKeyEnv = "PDCP_API_KEY"
apiServerEnv = "PDCP_API_SERVER"
ApiKeyHeaderName = "X-Api-Key"
dashBoardEnv = "PDCP_DASHBOARD_URL"
)
type PDCPCredentials struct {
Username string `yaml:"username"`
APIKey string `yaml:"api-key"`
Server string `yaml:"server"`
}
type PDCPUserProfileResponse struct {
UserName string `json:"name"`
// there are more fields but we don't need them
/// below fields are added later on and not part of the response
}
// PDCPCredHandler is interface for adding / retrieving pdcp credentials
// from file system
type PDCPCredHandler struct{}
// GetCreds retrieves the credentials from the file system or environment variables
func (p *PDCPCredHandler) GetCreds() (*PDCPCredentials, error) {
credsFromEnv := p.getCredsFromEnv()
if credsFromEnv != nil {
return credsFromEnv, nil
}
if !fileutil.FolderExists(PDCPDir) || !fileutil.FileExists(PDCPCredFile) {
return nil, ErrNoCreds
}
bin, err := os.Open(PDCPCredFile)
if err != nil {
return nil, err
}
// for future use-cases
var creds []PDCPCredentials
err = yaml.NewDecoder(bin).Decode(&creds)
if err != nil {
return nil, err
}
if len(creds) == 0 {
return nil, ErrNoCreds
}
return &creds[0], nil
}
// getCredsFromEnv retrieves the credentials from the environment
// if not or incomplete credentials are found it return nil
func (p *PDCPCredHandler) getCredsFromEnv() *PDCPCredentials {
apiKey := env.GetEnvOrDefault(apiKeyEnv, "")
apiServer := env.GetEnvOrDefault(apiServerEnv, "")
if apiKey == "" || apiServer == "" {
return nil
}
return &PDCPCredentials{APIKey: apiKey, Server: apiServer}
}
// SaveCreds saves the credentials to the file system
func (p *PDCPCredHandler) SaveCreds(resp *PDCPCredentials) error {
if resp == nil {
return fmt.Errorf("invalid response")
}
if !fileutil.FolderExists(PDCPDir) {
_ = fileutil.CreateFolder(PDCPDir)
}
bin, err := yaml.Marshal([]*PDCPCredentials{resp})
if err != nil {
return err
}
return os.WriteFile(PDCPCredFile, bin, 0600)
}
// ValidateAPIKey validates the api key and retrieves associated user metadata like username
// from given api server/host
func (p *PDCPCredHandler) ValidateAPIKey(key string, host string, toolName string) (*PDCPCredentials, error) {
// get address from url
urlx, err := urlutil.Parse(host)
if err != nil {
return nil, err
}
req, err := retryablehttp.NewRequest("GET", fmt.Sprintf(userProfileURL, urlx.Host, toolName), nil)
if err != nil {
return nil, err
}
req.Header.Set(ApiKeyHeaderName, key)
resp, err := retryablehttp.DefaultHTTPClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
}
defer resp.Body.Close()
bin, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var profile PDCPUserProfileResponse
err = json.Unmarshal(bin, &profile)
if err != nil {
return nil, err
}
if profile.UserName == "" {
return nil, fmt.Errorf("invalid response from server got %v", string(bin))
}
return &PDCPCredentials{Username: profile.UserName, APIKey: key, Server: host}, nil
}
func init() {
DashBoardURL = env.GetEnvOrDefault("PDCP_DASHBOARD_URL", DashBoardURL)
}

View File

@ -1,33 +0,0 @@
package pdcp
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
var exampleCred = `
- username: test
api-key: testpassword
server: https://scanme.sh
`
func TestLoadCreds(t *testing.T) {
// temporarily change PDCP file location for testing
f, err := os.CreateTemp("", "creds-test-*")
require.Nil(t, err)
_, _ = f.WriteString(strings.TrimSpace(exampleCred))
defer os.Remove(f.Name())
PDCPCredFile = f.Name()
PDCPDir = filepath.Dir(f.Name())
h := &PDCPCredHandler{}
value, err := h.GetCreds()
require.Nil(t, err)
require.NotNil(t, value)
require.Equal(t, "test", value.Username)
require.Equal(t, "testpassword", value.APIKey)
require.Equal(t, "https://scanme.sh", value.Server)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/retryablehttp-go"
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
errorutil "github.com/projectdiscovery/utils/errors"
fileutil "github.com/projectdiscovery/utils/file"
folderutil "github.com/projectdiscovery/utils/folder"
@ -32,14 +33,14 @@ var _ output.Writer = &UploadWriter{}
// server to enable web dashboard and more
type UploadWriter struct {
*output.StandardWriter
creds *PDCPCredentials
creds *pdcpauth.PDCPCredentials
tempFile *os.File
done atomic.Bool
uploadURL *url.URL
}
// NewUploadWriter creates a new upload writer
func NewUploadWriter(creds *PDCPCredentials) (*UploadWriter, error) {
func NewUploadWriter(creds *pdcpauth.PDCPCredentials) (*UploadWriter, error) {
if creds == nil {
return nil, fmt.Errorf("no credentials provided")
}
@ -107,7 +108,7 @@ func (u *UploadWriter) upload() (string, error) {
if err != nil {
return "", errorutil.NewWithErr(err).Msgf("could not create cloud upload request")
}
req.Header.Set(ApiKeyHeaderName, u.creds.APIKey)
req.Header.Set(pdcpauth.ApiKeyHeaderName, u.creds.APIKey)
req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("Accept", "application/json")
@ -149,7 +150,7 @@ func (u *UploadWriter) Close() {
}
func getScanDashBoardURL(id string) string {
ux, _ := urlutil.Parse(DashBoardURL)
ux, _ := urlutil.Parse(pdcpauth.DashBoardURL)
ux.Path = "/scans/" + id
ux.Update()
return ux.String()

View File

@ -5,8 +5,8 @@ import (
"fmt"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/internal/pdcp"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
updateutils "github.com/projectdiscovery/utils/update"
)
@ -33,5 +33,5 @@ func NucleiToolUpdateCallback() {
// AuthWithPDCP is used to authenticate with PDCP
func AuthWithPDCP() {
showBanner()
pdcp.CheckNValidateCredentials(config.BinaryName)
pdcpauth.CheckNValidateCredentials(config.BinaryName)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/internal/pdcp"
"github.com/projectdiscovery/nuclei/v3/pkg/installer"
uncoverlib "github.com/projectdiscovery/uncover"
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
"github.com/projectdiscovery/utils/env"
fileutil "github.com/projectdiscovery/utils/file"
permissionutil "github.com/projectdiscovery/utils/permission"
@ -351,18 +352,18 @@ func (r *Runner) setupPDCPUpload(writer output.Writer) output.Writer {
return writer
}
color := aurora.NewAurora(!r.options.NoColor)
h := &pdcp.PDCPCredHandler{}
h := &pdcpauth.PDCPCredHandler{}
creds, err := h.GetCreds()
if err != nil {
if err != pdcp.ErrNoCreds && !HideAutoSaveMsg {
if err != pdcpauth.ErrNoCreds && !HideAutoSaveMsg {
gologger.Verbose().Msgf("Could not get credentials for cloud upload: %s\n", err)
}
r.pdcpUploadErrMsg = fmt.Sprintf("[%v] To view results on Cloud Dashboard, Configure API key from %v", color.BrightYellow("WRN"), pdcp.DashBoardURL)
r.pdcpUploadErrMsg = fmt.Sprintf("[%v] To view results on Cloud Dashboard, Configure API key from %v", color.BrightYellow("WRN"), pdcpauth.DashBoardURL)
return writer
}
uploadWriter, err := pdcp.NewUploadWriter(creds)
if err != nil {
r.pdcpUploadErrMsg = fmt.Sprintf("[%v] PDCP (%v) Auto-Save Failed: %s\n", color.BrightYellow("WRN"), pdcp.DashBoardURL, err)
r.pdcpUploadErrMsg = fmt.Sprintf("[%v] PDCP (%v) Auto-Save Failed: %s\n", color.BrightYellow("WRN"), pdcpauth.DashBoardURL, err)
return writer
}
return output.NewMultiWriter(writer, uploadWriter)
@ -592,7 +593,7 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) {
if r.pdcpUploadErrMsg != "" {
gologger.Print().Msgf("%s", r.pdcpUploadErrMsg)
} else {
gologger.Info().Msgf("To view results on cloud dashboard, visit %v/scans upon scan completion.", pdcp.DashBoardURL)
gologger.Info().Msgf("To view results on cloud dashboard, visit %v/scans upon scan completion.", pdcpauth.DashBoardURL)
}
}