Skip to content

Commit 4827202

Browse files
authored
Merge pull request #5 from Escape-Technologies/feat/major-refactor
feat: major refactor
2 parents 741614e + 6a34793 commit 4827202

29 files changed

Lines changed: 559 additions & 451 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ testers
88
!internal/utils/assets/wordlist.txt
99

1010
# ignore builds
11-
goctopus
11+
/goctopus
1212

1313
dist/
1414
.env

cmd/goctopus/goctopus.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@ package main
33
import (
44
"os"
55

6-
"github.com/Escape-Technologies/goctopus/internal/config"
76
"github.com/Escape-Technologies/goctopus/internal/utils"
8-
"github.com/Escape-Technologies/goctopus/pkg/run"
7+
"github.com/Escape-Technologies/goctopus/pkg/config"
8+
"github.com/Escape-Technologies/goctopus/pkg/goctopus"
99

1010
log "github.com/sirupsen/logrus"
1111
)
1212

1313
func main() {
14-
config.ParseFlags()
15-
if !config.Conf.Silent {
14+
config.LoadFromArgs()
15+
if !config.Get().Silent {
1616
utils.PrintASCII()
1717
}
1818

19-
input, err := os.Open(config.Conf.InputFile)
20-
if err != nil {
21-
log.Error(err)
22-
os.Exit(1)
19+
if config.Get().InputFile != "" {
20+
input, err := os.Open(config.Get().InputFile)
21+
if err != nil {
22+
log.Error(err)
23+
os.Exit(1)
24+
}
25+
defer input.Close()
26+
goctopus.FingerprintFromFile(input)
27+
} else {
28+
goctopus.FingerprintFromSlice(config.Get().Addresses)
2329
}
24-
defer input.Close()
25-
26-
run.RunFromFile(input)
2730
}

internal/test/helpers/http.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package helpers
22

3-
import "github.com/Escape-Technologies/goctopus/internal/http"
3+
import "github.com/Escape-Technologies/goctopus/pkg/http"
44

55
func MockHttpResponse(statusCode int, body string) *http.Response {
66
bodyBytes := []byte(body)

internal/utils/usage.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package utils
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
)
7+
8+
func PrintUsage() {
9+
PrintASCII()
10+
fmt.Println("Usage: goctopus [options] [addresses]")
11+
fmt.Println("[addresses]: A list of addresses to fingerprint, comma separated.\nAddresses can be in the form of http://example.com/graphql or example.com.\n If an input file is specified, this argument is ignored.")
12+
fmt.Println("[options]:")
13+
flag.PrintDefaults()
14+
}

internal/workers/workers.go

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package config
22

33
import (
4+
"errors"
45
"flag"
56
"os"
7+
"strings"
68

9+
"github.com/Escape-Technologies/goctopus/internal/utils"
710
log "github.com/sirupsen/logrus"
811
)
912

10-
var (
11-
Conf *Config
12-
)
13-
1413
type Config struct {
1514
InputFile string
15+
Addresses []string
1616
OutputFile string
1717
MaxWorkers int
1818
Verbose bool
@@ -24,16 +24,34 @@ type Config struct {
2424
SubdomainEnumeration bool
2525
}
2626

27-
func ParseFlags() {
27+
var (
28+
c *Config
29+
)
30+
31+
func Get() *Config {
32+
if c == nil {
33+
log.Panic("Config not initialized")
34+
}
35+
return c
36+
}
37+
38+
func parseArgs() []string {
39+
if flag.Arg(0) == "" {
40+
log.Error("Invalid addresses argument")
41+
utils.PrintUsage()
42+
os.Exit(1)
43+
}
44+
input := flag.Arg(0)
45+
return strings.Split(input, ",")
46+
}
47+
48+
func LoadFromArgs() {
49+
flag.Usage = utils.PrintUsage
2850
config := Config{}
2951
// -- INPUT --
30-
flag.StringVar(&config.InputFile, "i", "", "Input file")
31-
// @TODO
32-
// flag.StringVar(&config.InputFile, "d", "", "Input domains (comma separated)")
33-
// flag.StringVar(&config.InputFile, "u", "", "Input urls (comma separated)")
52+
flag.StringVar(&config.InputFile, "f", "", "Input file")
3453

3554
// -- CONFIG --
36-
// @todo make output file optional ?
3755
flag.StringVar(&config.OutputFile, "o", "output.jsonl", "Output file (json-lines format)")
3856
flag.StringVar(&config.WebhookUrl, "webhook", "", "Webhook URL")
3957
flag.IntVar(&config.MaxWorkers, "w", 100, "Max workers")
@@ -46,6 +64,10 @@ func ParseFlags() {
4664

4765
flag.Parse()
4866

67+
if config.InputFile == "" {
68+
config.Addresses = parseArgs()
69+
}
70+
4971
if config.Verbose {
5072
log.SetLevel(log.DebugLevel)
5173
}
@@ -54,34 +76,40 @@ func ParseFlags() {
5476
log.SetLevel(log.ErrorLevel)
5577
}
5678

57-
ValidateConfig(&config)
58-
Conf = &config
79+
if err := ValidateConfig(&config); err != nil {
80+
log.Error(err)
81+
flag.PrintDefaults()
82+
os.Exit(1)
83+
}
84+
85+
c = &config
5986
}
6087

61-
func ValidateConfig(conf *Config) {
88+
func ValidateConfig(conf *Config) error {
6289
if conf.MaxWorkers < 1 {
63-
log.Error("[Invalid args] Max workers must be greater than 0")
64-
configError()
90+
return errors.New("[Invalid config] Max workers must be greater than 0")
6591
}
6692

6793
if conf.Timeout < 1 {
68-
log.Error("[Invalid args] Timeout must be greater than 0")
69-
configError()
94+
return errors.New("[Invalid config] Timeout must be greater than 0")
7095
}
7196

7297
if !conf.Introspection && conf.FieldSuggestion {
73-
log.Error("[Invalid args] Introspection has to be enabled to use field suggestion fingerprinting")
74-
configError()
98+
return errors.New("[Invalid config] Introspection has to be enabled to use field suggestion fingerprinting")
7599
}
76100

77-
if conf.InputFile == "" {
78-
log.Error("[Invalid args] Please specify an input file")
79-
configError()
101+
if conf.InputFile == "" && len(conf.Addresses) == 0 {
102+
return errors.New("[Invalid config] Please specify an input file or a list of addresses")
80103
}
81104

105+
return nil
82106
}
83107

84-
func configError() {
85-
flag.PrintDefaults()
86-
os.Exit(1)
108+
func Load(config *Config) {
109+
if err := ValidateConfig(config); err != nil {
110+
log.Error(err)
111+
flag.PrintDefaults()
112+
os.Exit(1)
113+
}
114+
c = config
87115
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
package crawl
1+
package domain
22

33
import (
44
"io"
55

6-
"github.com/Escape-Technologies/goctopus/internal/config"
6+
"github.com/Escape-Technologies/goctopus/pkg/config"
77
"github.com/projectdiscovery/subfinder/v2/pkg/resolve"
88
"github.com/projectdiscovery/subfinder/v2/pkg/runner"
99
)
1010

11-
func CrawlDomain(domain string, subDomains chan string, c *config.Config) (err error) {
11+
func EnumerateSubdomains(domain string, subDomains chan string) (err error) {
1212
subDomains <- domain
13+
c := config.Get()
1314

1415
if !c.SubdomainEnumeration {
1516
return nil
Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,29 @@
1-
package crawl
1+
package domain
22

33
import (
44
"errors"
5-
"fmt"
65
"net"
76

8-
"github.com/Escape-Technologies/goctopus/internal/config"
9-
"github.com/Escape-Technologies/goctopus/pkg/fingerprint"
10-
out "github.com/Escape-Technologies/goctopus/pkg/output"
7+
"github.com/Escape-Technologies/goctopus/pkg/endpoint"
8+
"github.com/Escape-Technologies/goctopus/pkg/output"
119
log "github.com/sirupsen/logrus"
1210
"github.com/valyala/fasthttp"
1311
)
1412

15-
func CrawlSubDomain(domain string) (*out.FingerprintOutput, error) {
16-
routes := []string{
17-
"",
18-
"graphql",
19-
"graphql/v2",
20-
"graphql/v1",
21-
"/api",
22-
"api/graphql",
23-
"api/v2/graphql",
24-
"api/v1/graphql",
25-
"appsync",
26-
"altair",
27-
"graph",
28-
}
29-
// @todo refactor this
30-
for _, route := range routes {
31-
url := fmt.Sprintf("https://%s/%s", domain, route)
32-
fp := fingerprint.NewFingerprinter(url)
33-
output, err := fingerprint.FingerprintUrl(url, fp, config.Conf)
34-
fp.Close()
13+
// @todo test this
14+
func FingerprintSubDomain(domain string) (*output.FingerprintOutput, error) {
15+
endpoints := endpoint.FuzzRoutes(domain)
16+
17+
for _, url := range endpoints {
18+
output, err := endpoint.FingerprintEndpoint(url)
19+
// @todo close client
20+
// fp.Close()
3521

3622
// At the first timeout, drop the domain
3723
// @todo number of tries in the config
3824
if err != nil {
3925
// If the domain is not a graphql endpoint, continue
40-
if errors.Is(err, fingerprint.ErrNotGraphql) {
26+
if errors.Is(err, endpoint.ErrNotGraphql) {
4127
continue
4228
}
4329

pkg/endpoint/endpoint.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package endpoint
2+
3+
import (
4+
"github.com/Escape-Technologies/goctopus/pkg/graphql"
5+
"github.com/Escape-Technologies/goctopus/pkg/http"
6+
"github.com/Escape-Technologies/goctopus/pkg/introspection"
7+
"github.com/Escape-Technologies/goctopus/pkg/suggestion"
8+
)
9+
10+
type _endpointFingerprinter struct {
11+
url string
12+
client http.Client
13+
}
14+
15+
type endpointFingerprinter interface {
16+
IsOpenGraphql() (bool, error)
17+
IsAuthentifiedGraphql() (bool, error)
18+
HasFieldSuggestion() (bool, error)
19+
HasIntrospectionOpen() (bool, error)
20+
}
21+
22+
func NewEndpointFingerprinter(url string, client http.Client) endpointFingerprinter {
23+
return &_endpointFingerprinter{
24+
url: url,
25+
client: client,
26+
}
27+
}
28+
29+
func (e *_endpointFingerprinter) IsOpenGraphql() (bool, error) {
30+
return graphql.FingerprintOpenGraphql(e.url, e.client)
31+
}
32+
33+
func (e *_endpointFingerprinter) IsAuthentifiedGraphql() (bool, error) {
34+
return graphql.FingerprintAuthentifiedGraphql(e.url, e.client)
35+
}
36+
37+
func (e *_endpointFingerprinter) HasFieldSuggestion() (bool, error) {
38+
return suggestion.FingerprintFieldSuggestion(e.url, e.client), nil
39+
}
40+
41+
func (e *_endpointFingerprinter) HasIntrospectionOpen() (bool, error) {
42+
return introspection.FingerprintIntrospection(e.url, e.client)
43+
}

0 commit comments

Comments
 (0)