yellow.git


yellow

~ opinionated web scanning pipeline for pentesters in a hurry
git clone https://github.com/djnnvx/yellow.git
log | files | refs | archive | README | LICENSE

commit 870dc5b4d83067846b7e214839f102586112430a
parent 5f7f05fe9a345e49f29e3bbdc097fabc05fea8a1
Author: djnn <[email protected]>
Date: Tue, 16 Jun 2026 10:46:53 +0900

refactoring(core): remove dead code, simplify codebase

Diffstat:
MCHANGELOG.txt | 2++
Mcli/parser.go | 83++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mcli/types.go | 18+++---------------
Acore/io.go | 15+++++++++++++++
Mcore/module.go | 14+++-----------
Mhelpers/directories.go | 53++++++++++-------------------------------------------
Mhelpers/file.go | 11++---------
Mhelpers/net.go | 4----
Mhelpers/strings.go | 16++--------------
Mosint/alterx.go | 8+-------
Mosint/gau.go | 18++++++++++--------
Mosint/run.go | 76++++++++++++++++++++++++----------------------------------------------------
Mprune/run.go | 43++++++++++---------------------------------
Mscan/fingerprint.go | 14+++++++-------
Mscan/portscan.go | 10+++++-----
Mscan/run.go | 104+++++++++++++++++++++++--------------------------------------------------------

16 files changed, 166 insertions(+), 323 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
@@ -1,5 +1,7 @@
 ## v0.0.7
 
+- refactor(core): dead code deletion, simplification ~ @djnnvx
+- fix(osint): gau init errors for one provider dont bring everything down  ~ @djnnvx
 
 ## v0.0.6
 
diff --git a/cli/parser.go b/cli/parser.go
@@ -34,12 +34,13 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 				os.Exit(1)
 			}
 
-			pruneOpts := prune.PruneOpts{}
-			pruneOpts.SetProxy(opts.Proxy)
-			pruneOpts.SetDryRun(opts.RunDry)
-			pruneOpts.SetForceInsecure(opts.UseHttpInsecure)
-			pruneOpts.SetInFilePath(opts.TargetFilePath)
-			pruneOpts.SetOutFilePath(opts.OutName)
+			pruneOpts := prune.PruneOpts{
+				Proxy:         opts.Proxy,
+				DryRun:        opts.RunDry,
+				ForceInsecure: opts.UseHttpInsecure,
+				InFilePath:    opts.TargetFilePath,
+				OutFilePath:   opts.OutName,
+			}
 
 			pruneOpts.Run()
 		},
@@ -65,15 +66,16 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 				fmt.Println("[!] collected assets will be sent to current working directory")
 			}
 
-			scanOpts := scan.ScanOpts{}
-			scanOpts.SetDomain(opts.OutName)
-			scanOpts.SetScanPath(opts.OutName)
-			scanOpts.SetProxy(opts.Proxy)
-			scanOpts.SetDryRun(opts.RunDry)
-			scanOpts.SetRateLimit(opts.RateLimit)
-			scanOpts.SetWordlistPath(opts.WordlistPath)
-			scanOpts.SetForceInsecure(opts.UseHttpInsecure)
-			scanOpts.SetGobuster(opts.Gobuster)
+			scanOpts := scan.ScanOpts{
+				Domain:        opts.OutName,
+				ScanPath:      opts.OutName,
+				Proxy:         opts.Proxy,
+				DryRun:        opts.RunDry,
+				RateLimit:     opts.RateLimit,
+				WordlistPath:  opts.WordlistPath,
+				ForceInsecure: opts.UseHttpInsecure,
+				Gobuster:      opts.Gobuster,
+			}
 
 			if opts.TargetFilePath == "" {
 				scanOpts.Run()
@@ -84,8 +86,7 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 			defer scanner.Close()
 
 			for scanner.Scan() {
-				targetDomain := scanner.Text()
-				scanOpts.SetDomain(targetDomain)
+				scanOpts.Domain = scanner.Text()
 				scanOpts.Fingerprint()
 			}
 		},
@@ -111,18 +112,19 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 				fmt.Println("[!] collected assets will be sent to current working directory")
 			}
 
-			scanOpts := scan.ScanOpts{}
-			scanOpts.SetDomain(opts.OutName)
-			scanOpts.SetScanPath(opts.OutName)
-			scanOpts.SetProxy(opts.Proxy)
-			scanOpts.SetDryRun(opts.RunDry)
-			scanOpts.SetRateLimit(opts.RateLimit)
-			scanOpts.SetWordlistPath(opts.WordlistPath)
-			scanOpts.SetForceInsecure(opts.UseHttpInsecure)
-			scanOpts.SetGobuster(opts.Gobuster)
-			scanOpts.SetPortScan(opts.PortScan)
-			scanOpts.SetPorts(opts.Ports)
-			scanOpts.SetNuclei(opts.Nuclei)
+			scanOpts := scan.ScanOpts{
+				Domain:        opts.OutName,
+				ScanPath:      opts.OutName,
+				Proxy:         opts.Proxy,
+				DryRun:        opts.RunDry,
+				RateLimit:     opts.RateLimit,
+				WordlistPath:  opts.WordlistPath,
+				ForceInsecure: opts.UseHttpInsecure,
+				Gobuster:      opts.Gobuster,
+				PortScan:      opts.PortScan,
+				Ports:         opts.Ports,
+				Nuclei:        opts.Nuclei,
+			}
 
 			if opts.TargetFilePath == "" {
 				scanOpts.Run()
@@ -133,8 +135,7 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 			defer scanner.Close()
 
 			for scanner.Scan() {
-				targetDomain := scanner.Text()
-				scanOpts.SetDomain(targetDomain)
+				scanOpts.Domain = scanner.Text()
 				scanOpts.Run()
 			}
 		},
@@ -160,17 +161,18 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 				fmt.Println("[!] collected assets will be sent to current working directory")
 			}
 
-			osintOpts := osint.OsintOpts{}
-			osintOpts.SetDomain(opts.OutName)
-			osintOpts.SetScanPath(opts.OutName)
-			osintOpts.SetProxy(opts.Proxy)
-			osintOpts.SetDryRun(opts.RunDry)
-			osintOpts.SetRateLimit(opts.RateLimit)
-			osintOpts.SetEmailsFile(opts.EmailsFilePath)
+			osintOpts := osint.OsintOpts{
+				Domain:     opts.OutName,
+				ScanPath:   opts.OutName,
+				Proxy:      opts.Proxy,
+				DryRun:     opts.RunDry,
+				RateLimit:  opts.RateLimit,
+				EmailsFile: opts.EmailsFilePath,
+			}
 
 			// if used create subcommand, put the results in scans
 			if helper.Exists(opts.OutName + "/scans/") {
-				osintOpts.SetScanPath(opts.OutName + "/scans")
+				osintOpts.ScanPath = opts.OutName + "/scans"
 			}
 
 			if opts.TargetFilePath == "" {
@@ -182,8 +184,7 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 			defer scanner.Close()
 
 			for scanner.Scan() {
-				targetDomain := scanner.Text()
-				osintOpts.SetDomain(targetDomain)
+				osintOpts.Domain = scanner.Text()
 				osintOpts.Run()
 			}
 		},
diff --git a/cli/types.go b/cli/types.go
@@ -42,20 +42,8 @@ type StandardOptions struct {
 }
 
 func GetDefaultOptions() StandardOptions {
-	opts := StandardOptions{
-		OutName:         "",
-		RunDry:          false,
-		Proxy:           "",
-		UseHttpInsecure: false,
-		RateLimit:       0,
-		WordlistPath:    "/usr/share/wordlists/seclists/Discovery/Web-Content/common.txt",
-		TargetFilePath:  "",
-		Gobuster:        false,
-		PortScan:        false,
-		Ports:           "21,22,23,25,53,80,110,135,139,143,161,389,443,445,636,993,995,1099,1433,1521,1723,2375,2376,3000,3268,3269,3306,3389,5000,5432,5672,5900,5985,5986,6379,6443,8000,8080,8081,8111,8112,8443,8888,9000,9081,9090,9092,9200,9300,9443,10250,27017",
-		Nuclei:          false,
-		EmailsFilePath:  "",
+	return StandardOptions{
+		WordlistPath: "/usr/share/wordlists/seclists/Discovery/Web-Content/common.txt",
+		Ports:        "21,22,23,25,53,80,110,135,139,143,161,389,443,445,636,993,995,1099,1433,1521,1723,2375,2376,3000,3268,3269,3306,3389,5000,5432,5672,5900,5985,5986,6379,6443,8000,8080,8081,8111,8112,8443,8888,9000,9081,9090,9092,9200,9300,9443,10250,27017",
 	}
-
-	return opts
 }
diff --git a/core/io.go b/core/io.go
@@ -0,0 +1,15 @@
+package core
+
+import (
+	"os"
+	"strings"
+)
+
+// WriteLines writes one entry per line, with a trailing newline unless empty.
+func WriteLines(path string, lines []string) error {
+	s := strings.Join(lines, "\n")
+	if s != "" {
+		s += "\n"
+	}
+	return os.WriteFile(path, []byte(s), 0644)
+}
diff --git a/core/module.go b/core/module.go
@@ -2,10 +2,7 @@ package core
 
 import "fmt"
 
-// Context carries the configuration and shared state for a single target
-// through every Module. It replaces the per-module map[string]any config and
-// the previous wappalyzerResults global. A single Context flows through both
-// the OSINT and scan phases of a run.
+// Context carries config and shared state for a single target through every Module.
 type Context struct {
 	Domain    string // current target (full URL for scan, domain for osint)
 	ScanPath  string // per-target output directory
@@ -17,13 +14,8 @@ type Context struct {
 	Insecure   bool   // scan: skip TLS verification / force http
 	EmailsFile string // osint: leaker emails file
 
-	// Techs holds technologies detected by wappalyzer (scan), consumed by
-	// cvemap and template scanners.
-	Techs []string
-
-	// Domains holds assets discovered during OSINT enumeration, consumed by
-	// shodan.
-	Domains []string
+	Techs   []string // scan: wappalyzer detections, consumed by cvemap
+	Domains []string // osint: discovered assets, consumed by shodan
 }
 
 // Module is a single recon step. Run reports its own progress to stdout and
diff --git a/helpers/directories.go b/helpers/directories.go
@@ -5,57 +5,24 @@ import (
 	"os"
 )
 
-type Folder struct {
-	Name     string
-	Children []Folder
-}
-
 func SetUpDirectoryArchitecture(target string) {
 	dirname := ReplaceWithHyphen(target)
 	fmt.Println("[+] setting up directory architecture for", dirname)
 
-	if err := os.MkdirAll(dirname, 0775); err != nil {
-		fmt.Printf("[!] could not create %s: %v\n", dirname, err)
-		return
+	dirs := []string{
+		"scans/infra", "scans/ssl", "scans/screenshots", "scans/nessus",
+		"extracted/assets", "extracted/creds", "extracted/code",
+		"www/exploits", "www/tools",
+	}
+	for _, d := range dirs {
+		full := dirname + "/" + d
+		if err := os.MkdirAll(full, 0775); err != nil {
+			fmt.Printf("[!] could not create %s: %v\n", full, err)
+		}
 	}
-	CreateDirectory(dirname, []Folder{
-		{
-			Name:     "scans",
-			Children: FolderNameFactory("infra", "ssl", "screenshots", "nessus"),
-		},
-		{
-			Name:     "extracted",
-			Children: FolderNameFactory("assets", "creds", "code"),
-		},
-		{
-			Name:     "www",
-			Children: FolderNameFactory("exploits", "tools"),
-		},
-	})
 }
 
 func Exists(path string) bool {
 	_, err := os.Stat(path)
 	return err == nil
 }
-
-func CreateDirectory(base string, folders []Folder) {
-	for _, f := range folders {
-		current := fmt.Sprintf("%s/%s", base, f.Name)
-		if err := os.Mkdir(current, 0775); err != nil && !os.IsExist(err) {
-			fmt.Printf("[!] could not create %s: %v\n", current, err)
-		}
-		if len(f.Children) != 0 {
-			CreateDirectory(current, f.Children)
-		}
-	}
-}
-
-func FolderNameFactory(names ...string) []Folder {
-	f := make([]Folder, 0, len(names))
-	for _, name := range names {
-		f = append(f, Folder{Name: name})
-	}
-
-	return f
-}
diff --git a/helpers/file.go b/helpers/file.go
@@ -19,13 +19,6 @@ func LoadTargetFile(targetPath string) *FileScanner {
 		os.Exit(1)
 	}
 
-	scanner := bufio.NewScanner(file)
-	body, err := os.ReadFile(targetPath)
-	if err != nil {
-		fmt.Printf("%v, %+v", err, targetPath)
-	}
-
-	fmt.Println("[+] Loaded targets: \n", string(body))
-
-	return &FileScanner{file, scanner}
+	fmt.Println("[+] Loaded targets from", targetPath)
+	return &FileScanner{file, bufio.NewScanner(file)}
 }
diff --git a/helpers/net.go b/helpers/net.go
@@ -76,10 +76,6 @@ func DisplayNetInfo() {
 	ip := GetCurrentIP()
 	ua := GetUserAgent()
 
-	if ip == "" {
-		os.Exit(1)
-	}
-
 	fmt.Println("[~] Current Public IP: " + ip)
 	fmt.Println("[~] Prefered User-Agent: " + ua)
 }
diff --git a/helpers/strings.go b/helpers/strings.go
@@ -22,22 +22,10 @@ func ReplaceWithHyphen(input string) string {
 // if its not a valid domain in the end, not too big of a deal,
 // we just want an approximate response
 func StringHasUnwantedCharactersForDomainName(url string) bool {
-	badChars := ":;\t\n, +\\\"[]{}()="
-	for _, b := range badChars {
-		if strings.Contains(url, string(b)) {
-			return true
-		}
-	}
-
-	if strings.HasSuffix(url, ".") {
+	if strings.ContainsAny(url, ":;\t\n, +\\\"[]{}()=") {
 		return true
 	}
-
-	if strings.HasPrefix(url, "Name") {
-		return true
-	}
-
-	return false
+	return strings.HasSuffix(url, ".") || strings.HasPrefix(url, "Name")
 }
 
 func ExtractString(m map[string]any, k string) string {
diff --git a/osint/alterx.go b/osint/alterx.go
@@ -4,8 +4,6 @@ import (
 	"context"
 	"fmt"
 	"net"
-	"os"
-	"strings"
 	"sync"
 	"time"
 
@@ -76,11 +74,7 @@ func (*Alterx) Run(ctx *core.Context) error {
 	ctx.Domains = append(ctx.Domains, added...)
 
 	outfile := fmt.Sprintf("%s/alterx.txt", ctx.ScanPath)
-	content := strings.Join(added, "\n")
-	if content != "" {
-		content += "\n"
-	}
-	if err := os.WriteFile(outfile, []byte(content), 0644); err != nil {
+	if err := core.WriteLines(outfile, added); err != nil {
 		fmt.Printf("[!] alterx: could not write %s: %v\n", outfile, err)
 	}
 
diff --git a/osint/gau.go b/osint/gau.go
@@ -5,7 +5,6 @@ import (
 	"crypto/tls"
 	"fmt"
 	"net/url"
-	"os"
 	"strings"
 	"time"
 
@@ -50,9 +49,16 @@ func (*Gau) Run(ctx *core.Context) error {
 		// URLScan.Host left empty so gau uses its built-in default base URL
 	}
 
+	// init providers one at a time so a single unreachable one (commoncrawl
+	// fetches its index at instantiation) doesn't take down the rest.
 	gau := &runner.Runner{}
-	if err := gau.Init(config, config.Providers, providers.Filters{}); err != nil {
-		fmt.Printf("[!] gau: could not init: %v\n", err)
+	for _, p := range config.Providers {
+		if err := gau.Init(config, []string{p}, providers.Filters{}); err != nil {
+			fmt.Printf("[!] gau: skipping provider %s: %v\n", p, err)
+		}
+	}
+	if len(gau.Providers) == 0 {
+		fmt.Println("[!] gau: no providers available, skipping")
 		return nil
 	}
 
@@ -86,11 +92,7 @@ func (*Gau) Run(ctx *core.Context) error {
 	<-done
 
 	outfile := fmt.Sprintf("%s/urls.txt", ctx.ScanPath)
-	content := strings.Join(urls, "\n")
-	if content != "" {
-		content += "\n"
-	}
-	if err := os.WriteFile(outfile, []byte(content), 0644); err != nil {
+	if err := core.WriteLines(outfile, urls); err != nil {
 		fmt.Printf("[!] gau: could not write %s: %v\n", outfile, err)
 		return nil
 	}
diff --git a/osint/run.go b/osint/run.go
@@ -12,46 +12,22 @@ import (
 )
 
 type OsintOpts struct {
-	domain     string
-	scanPath   string
-	proxy      string
-	dryRun     bool
-	rateLimit  int32
-	emailsFile string
-}
-
-func (opts *OsintOpts) SetRateLimit(data int32) {
-	opts.rateLimit = data
-}
-
-func (opts *OsintOpts) SetDomain(domain string) {
-	opts.domain = domain
-}
-
-func (opts *OsintOpts) SetScanPath(data string) {
-	opts.scanPath = data
-}
-
-func (opts *OsintOpts) SetProxy(data string) {
-	opts.proxy = data
-}
-
-func (opts *OsintOpts) SetDryRun(data bool) {
-	opts.dryRun = data
-}
-
-func (opts *OsintOpts) SetEmailsFile(data string) {
-	opts.emailsFile = data
+	Domain     string
+	ScanPath   string
+	Proxy      string
+	DryRun     bool
+	RateLimit  int32
+	EmailsFile string
 }
 
 func (opts *OsintOpts) context() *core.Context {
 	return &core.Context{
-		Domain:     opts.domain,
-		ScanPath:   opts.scanPath,
-		Proxy:      opts.proxy,
-		DryRun:     opts.dryRun,
-		RateLimit:  opts.rateLimit,
-		EmailsFile: opts.emailsFile,
+		Domain:     opts.Domain,
+		ScanPath:   opts.ScanPath,
+		Proxy:      opts.Proxy,
+		DryRun:     opts.DryRun,
+		RateLimit:  opts.RateLimit,
+		EmailsFile: opts.EmailsFile,
 	}
 }
 
@@ -59,8 +35,8 @@ func (opts OsintOpts) RunCleanup() {
 	fmt.Println("[+] cleaning up...")
 
 	/* remove superfluous files */
-	asfFilepath := fmt.Sprintf("%s/assetfinder.txt", opts.scanPath)
-	sbfOutfile := fmt.Sprintf("%s/subfinder.txt", opts.scanPath)
+	asfFilepath := fmt.Sprintf("%s/assetfinder.txt", opts.ScanPath)
+	sbfOutfile := fmt.Sprintf("%s/subfinder.txt", opts.ScanPath)
 
 	for _, f := range []string{asfFilepath, sbfOutfile} {
 		if err := os.Remove(f); err != nil && !os.IsNotExist(err) {
@@ -70,7 +46,7 @@ func (opts OsintOpts) RunCleanup() {
 }
 
 func (opts *OsintOpts) Run() {
-	fmt.Printf("\n[OSINT] domain: %s\n\n", opts.domain)
+	fmt.Printf("\n[OSINT] domain: %s\n\n", opts.Domain)
 
 	ctx := opts.context()
 
@@ -87,19 +63,15 @@ func (opts *OsintOpts) Run() {
 
 	core.RunModules(ctx, []core.Module{&Alterx{}, &Shodan{}})
 
-	uniqueOutfile := fmt.Sprintf("%s/domains.txt", opts.scanPath)
-	content := strings.Join(ctx.Domains, "\n")
-	if content != "" {
-		content += "\n"
-	}
-	if err := os.WriteFile(uniqueOutfile, []byte(content), 0644); err != nil {
+	uniqueOutfile := fmt.Sprintf("%s/domains.txt", opts.ScanPath)
+	if err := core.WriteLines(uniqueOutfile, ctx.Domains); err != nil {
 		fmt.Printf("[!] osint: could not write %s: %v\n", uniqueOutfile, err)
 		return
 	}
 
-	fmt.Printf("[OSINT %s] Registered %v IP addresses and assets.\n", opts.domain, len(ctx.Domains))
-	fmt.Printf("[OSINT %s] Location of unique domain names: %s\n", opts.domain, uniqueOutfile)
-	fmt.Printf("[OSINT %s] done.\n", opts.domain)
+	fmt.Printf("[OSINT %s] Registered %v IP addresses and assets.\n", opts.Domain, len(ctx.Domains))
+	fmt.Printf("[OSINT %s] Location of unique domain names: %s\n", opts.Domain, uniqueOutfile)
+	fmt.Printf("[OSINT %s] done.\n", opts.Domain)
 
 	opts.RunCleanup()
 }
@@ -123,9 +95,9 @@ func (opts *OsintOpts) aggregateDomains() []string {
 		}
 	}
 
-	asfFilepath := fmt.Sprintf("%s/assetfinder.txt", opts.scanPath)
-	dnsxFilepath := fmt.Sprintf("%s/dnsx.json", opts.scanPath)
-	sbfOutfile := fmt.Sprintf("%s/subfinder.txt", opts.scanPath)
+	asfFilepath := fmt.Sprintf("%s/assetfinder.txt", opts.ScanPath)
+	dnsxFilepath := fmt.Sprintf("%s/dnsx.json", opts.ScanPath)
+	sbfOutfile := fmt.Sprintf("%s/subfinder.txt", opts.ScanPath)
 
 	for _, file := range []string{asfFilepath, dnsxFilepath, sbfOutfile} {
 		data, err := os.ReadFile(file)
@@ -138,7 +110,7 @@ func (opts *OsintOpts) aggregateDomains() []string {
 		}
 	}
 
-	urlsFile := fmt.Sprintf("%s/urls.txt", opts.scanPath)
+	urlsFile := fmt.Sprintf("%s/urls.txt", opts.ScanPath)
 	if data, err := os.ReadFile(urlsFile); err == nil {
 		for _, line := range strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n") {
 			if u, err := url.Parse(strings.TrimSpace(line)); err == nil {
diff --git a/prune/run.go b/prune/run.go
@@ -2,54 +2,33 @@ package prune
 
 import (
 	"fmt"
-	"os"
-	"strings"
 
+	"evil.djnn.sh/djnn/yellow/core"
 	helper "evil.djnn.sh/djnn/yellow/helpers"
 )
 
 type PruneOpts struct {
-	proxy         string
-	dryRun        bool
-	forceInsecure bool
+	Proxy         string
+	DryRun        bool
+	ForceInsecure bool
 
-	inFilePath  string
-	outFilePath string
-}
-
-func (opts *PruneOpts) SetInFilePath(data string) {
-	opts.inFilePath = data
-}
-
-func (opts *PruneOpts) SetOutFilePath(data string) {
-	opts.outFilePath = data
-}
-
-func (opts *PruneOpts) SetForceInsecure(data bool) {
-	opts.forceInsecure = data
-}
-
-func (opts *PruneOpts) SetProxy(data string) {
-	opts.proxy = data
-}
-
-func (opts *PruneOpts) SetDryRun(data bool) {
-	opts.dryRun = data
+	InFilePath  string
+	OutFilePath string
 }
 
 func (opts *PruneOpts) Run() {
 
-	helper.CheckProxy(opts.proxy)
+	helper.CheckProxy(opts.Proxy)
 	helper.DisplayNetInfo()
 
 	results := make([]string, 0)
-	scanner := helper.LoadTargetFile(opts.inFilePath)
+	scanner := helper.LoadTargetFile(opts.InFilePath)
 	defer scanner.Close()
 
 	for scanner.Scan() {
 		targetDomain := scanner.Text()
 		httpAddr := "https://" + targetDomain
-		if opts.forceInsecure {
+		if opts.ForceInsecure {
 			httpAddr = "http://" + targetDomain
 		}
 
@@ -59,9 +38,7 @@ func (opts *PruneOpts) Run() {
 		}
 	}
 
-	content := strings.Join(results, "\n")
-	err := os.WriteFile(opts.outFilePath, []byte(content), 0644)
-	if err != nil {
+	if err := core.WriteLines(opts.OutFilePath, results); err != nil {
 		fmt.Printf("Error writing to file: %v\n", err)
 		return
 	}
diff --git a/scan/fingerprint.go b/scan/fingerprint.go
@@ -16,21 +16,21 @@ import (
 */
 
 func (opts *ScanOpts) Fingerprint() {
-	fmt.Printf("\n[SCAN] domain: %s\n", opts.domain)
+	fmt.Printf("\n[SCAN] domain: %s\n", opts.Domain)
 
-	fullDomain := "https://" + opts.domain
-	if opts.forceInsecure {
-		fullDomain = "http://" + opts.domain
+	fullDomain := "https://" + opts.Domain
+	if opts.ForceInsecure {
+		fullDomain = "http://" + opts.Domain
 	}
 
 	if helper.HasUnavailableWebInterface(fullDomain) {
-		fmt.Printf("[FINGERPRINT %s] => no web panel online. skipping\n", opts.domain)
+		fmt.Printf("[FINGERPRINT %s] => no web panel online. skipping\n", opts.Domain)
 		return
 	}
 
-	pathForDomain := opts.scanPath + "/" + opts.domain
+	pathForDomain := opts.ScanPath + "/" + opts.Domain
 	if err := os.MkdirAll(pathForDomain, 0755); err != nil {
-		fmt.Printf("[!] fingerprint: could not create %s, skipping %s: %v\n", pathForDomain, opts.domain, err)
+		fmt.Printf("[!] fingerprint: could not create %s, skipping %s: %v\n", pathForDomain, opts.Domain, err)
 		return
 	}
 
diff --git a/scan/portscan.go b/scan/portscan.go
@@ -46,18 +46,18 @@ type PortScanner struct {
 
 func (opts ScanOpts) runPortScan() {
 	ps := PortScanner{
-		scanPath: opts.scanPath + "/" + opts.domain,
-		ports:    opts.ports,
+		scanPath: opts.ScanPath + "/" + opts.Domain,
+		ports:    opts.Ports,
 		timeout:  2 * time.Second,
 		workers:  100,
 	}
-	fmt.Printf("[+] Running port scan on %s\n", opts.domain)
-	if !opts.dryRun {
+	fmt.Printf("[+] Running port scan on %s\n", opts.Domain)
+	if !opts.DryRun {
 		if err := os.MkdirAll(ps.scanPath, 0755); err != nil {
 			fmt.Printf("[!] port scan: could not create %s: %v\n", ps.scanPath, err)
 			return
 		}
-		ps.Run(opts.domain)
+		ps.Run(opts.Domain)
 	}
 }
 
diff --git a/scan/run.go b/scan/run.go
@@ -9,73 +9,29 @@ import (
 )
 
 type ScanOpts struct {
-	domain        string
-	scanPath      string
-	proxy         string
-	dryRun        bool
-	rateLimit     int32
-	wordlistPath  string
-	forceInsecure bool
-	gobuster      bool
-	portScan      bool
-	ports         string
-	nuclei        bool
-}
-
-func (opts *ScanOpts) SetGobuster(data bool) {
-	opts.gobuster = data
-}
-
-func (opts *ScanOpts) SetForceInsecure(data bool) {
-	opts.forceInsecure = data
-}
-
-func (opts *ScanOpts) SetWordlistPath(data string) {
-	opts.wordlistPath = data
-}
-
-func (opts *ScanOpts) SetRateLimit(data int32) {
-	opts.rateLimit = data
-}
-
-func (opts *ScanOpts) SetDomain(data string) {
-	opts.domain = data
-}
-
-func (opts *ScanOpts) SetScanPath(data string) {
-	opts.scanPath = data
-}
-
-func (opts *ScanOpts) SetProxy(data string) {
-	opts.proxy = data
-}
-
-func (opts *ScanOpts) SetDryRun(data bool) {
-	opts.dryRun = data
-}
-
-func (opts *ScanOpts) SetPortScan(data bool) {
-	opts.portScan = data
-}
-
-func (opts *ScanOpts) SetPorts(data string) {
-	opts.ports = data
-}
-
-func (opts *ScanOpts) SetNuclei(data bool) {
-	opts.nuclei = data
+	Domain        string
+	ScanPath      string
+	Proxy         string
+	DryRun        bool
+	RateLimit     int32
+	WordlistPath  string
+	ForceInsecure bool
+	Gobuster      bool
+	PortScan      bool
+	Ports         string
+	Nuclei        bool
 }
 
 // context builds a per-target Context from the current options.
 func (opts *ScanOpts) context() *core.Context {
 	return &core.Context{
-		Domain:    opts.domain,
-		ScanPath:  opts.scanPath,
-		Proxy:     opts.proxy,
-		DryRun:    opts.dryRun,
-		RateLimit: opts.rateLimit,
-		Wordlist:  opts.wordlistPath,
-		Insecure:  opts.forceInsecure,
+		Domain:    opts.Domain,
+		ScanPath:  opts.ScanPath,
+		Proxy:     opts.Proxy,
+		DryRun:    opts.DryRun,
+		RateLimit: opts.RateLimit,
+		Wordlist:  opts.WordlistPath,
+		Insecure:  opts.ForceInsecure,
 	}
 }
 
@@ -90,38 +46,38 @@ func (opts *ScanOpts) webModules() []core.Module {
 		&Cvemap{},
 		&Httpx{},
 	}
-	if opts.nuclei {
+	if opts.Nuclei {
 		modules = append(modules, &Nuclei{})
 	}
-	if opts.gobuster {
+	if opts.Gobuster {
 		modules = append(modules, &Gobuster{})
 	}
 	return modules
 }
 
 func (opts *ScanOpts) Run() {
-	fmt.Printf("\n[SCAN] domain: %s\n", opts.domain)
+	fmt.Printf("\n[SCAN] domain: %s\n", opts.Domain)
 
-	fullDomain := "https://" + opts.domain
-	if opts.forceInsecure {
-		fullDomain = "http://" + opts.domain
+	fullDomain := "https://" + opts.Domain
+	if opts.ForceInsecure {
+		fullDomain = "http://" + opts.Domain
 	}
 
-	if opts.portScan {
+	if opts.PortScan {
 		opts.runPortScan()
 	}
 
 	if helper.HasUnavailableWebInterface(fullDomain) {
-		fmt.Printf("[SCAN %s] => no web panel online. skipping\n", opts.domain)
+		fmt.Printf("[SCAN %s] => no web panel online. skipping\n", opts.Domain)
 		return
 	}
 
-	fmt.Printf("[SCAN %s] web panel online....running web scans\n", opts.domain)
+	fmt.Printf("[SCAN %s] web panel online....running web scans\n", opts.Domain)
 
-	/* per-domain output dir; setting opts.scanPath directly would nest dirs */
-	pathForDomain := opts.scanPath + "/" + opts.domain
+	/* per-domain output dir; setting opts.ScanPath directly would nest dirs */
+	pathForDomain := opts.ScanPath + "/" + opts.Domain
 	if err := os.MkdirAll(pathForDomain, 0755); err != nil {
-		fmt.Printf("[!] scan: could not create %s, skipping %s: %v\n", pathForDomain, opts.domain, err)
+		fmt.Printf("[!] scan: could not create %s, skipping %s: %v\n", pathForDomain, opts.Domain, err)
 		return
 	}