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 7d2a7a3c66c89b8f535648aa49a9e16f7e5fbfe1
parent 070c65c4d248cd49d9c05a9502d954d6f6550fb9
Author: djnn <[email protected]>
Date: Sat, 25 Oct 2025 00:00:46 +0200

feat(cmd): adding prune subcommand

Diffstat:
MChangelog.md | 1+
Mcli/parser.go | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mcli/types.go | 7++++---
Aprune/run.go | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

4 files changed, 129 insertions(+), 25 deletions(-)

diff --git a/Changelog.md b/Changelog.md
@@ -5,6 +5,7 @@
 - fix(scan): adding --no-dirbusting ~ @djnnvx
 - feat(meta): adding run-web-recon script ~ @djnnvx
 - feat(cmd): adding fingerprint subcommand ~ @djnnvx
+- feat(cmd): adding prune subcommand ~ @djnnvx
 
 
 ## v0.0.3
diff --git a/cli/parser.go b/cli/parser.go
@@ -9,11 +9,36 @@ import (
 
 	"evil.djnn.sh/djnn/yellow/helpers"
 	"evil.djnn.sh/djnn/yellow/osint"
+	"evil.djnn.sh/djnn/yellow/prune"
 	"evil.djnn.sh/djnn/yellow/scan"
 )
 
 func GetParser(opts *StandardOptions) *cobra.Command {
 
+	var pruneCmd = &cobra.Command{
+		Use:   "prune",
+		Short: "filters a file full of domains",
+		Long:  "filters a file full of domains to only get the reachable domains",
+		Args:  cobra.MinimumNArgs(0),
+		Run: func(cmd *cobra.Command, args []string) {
+
+			if opts.TargetFilePath == "" {
+				fmt.Println("[!] Error: target cannot be empty. Please run with --file [something]")
+				fmt.Println("\nIf you're confused, feel free to use --help option. :)~")
+				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.Run()
+		},
+	}
+
 	var fingerprintCmd = &cobra.Command{
 		Use:   "fingerprint",
 		Short: "Runs fingerprinting against websites",
@@ -21,7 +46,7 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 		Args:  cobra.MinimumNArgs(0),
 		Run: func(cmd *cobra.Command, args []string) {
 
-			if opts.OutDirName == "" && opts.TargetFilePath == "" {
+			if opts.OutName == "" && opts.TargetFilePath == "" {
 				fmt.Println("[!] Error: target cannot be empty. Please run with --target [something] or --file [something]")
 				fmt.Println("\nIf you're confused, feel free to use --help option. :)~")
 				os.Exit(1)
@@ -30,13 +55,13 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 			helper.CheckProxy(opts.Proxy)
 			helper.DisplayNetInfo()
 
-			if opts.OutDirName == "" || !helper.Exists(opts.OutDirName) {
+			if opts.OutName == "" || !helper.Exists(opts.OutName) {
 				println("[!] collected assets will be sent to current working directory")
 			}
 
 			scanOpts := scan.ScanOpts{}
-			scanOpts.SetDomain(opts.OutDirName)
-			scanOpts.SetScanPath(opts.OutDirName)
+			scanOpts.SetDomain(opts.OutName)
+			scanOpts.SetScanPath(opts.OutName)
 			scanOpts.SetProxy(opts.Proxy)
 			scanOpts.SetDryRun(opts.RunDry)
 			scanOpts.SetRateLimit(opts.RateLimit)
@@ -67,7 +92,7 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 		Args:  cobra.MinimumNArgs(0),
 		Run: func(cmd *cobra.Command, args []string) {
 
-			if opts.OutDirName == "" && opts.TargetFilePath == "" {
+			if opts.OutName == "" && opts.TargetFilePath == "" {
 				fmt.Println("[!] Error: target cannot be empty. Please run with --target [something] or --file [something]")
 				fmt.Println("\nIf you're confused, feel free to use --help option. :)~")
 				os.Exit(1)
@@ -76,13 +101,13 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 			helper.CheckProxy(opts.Proxy)
 			helper.DisplayNetInfo()
 
-			if opts.OutDirName == "" || !helper.Exists(opts.OutDirName) {
+			if opts.OutName == "" || !helper.Exists(opts.OutName) {
 				println("[!] collected assets will be sent to current working directory")
 			}
 
 			scanOpts := scan.ScanOpts{}
-			scanOpts.SetDomain(opts.OutDirName)
-			scanOpts.SetScanPath(opts.OutDirName)
+			scanOpts.SetDomain(opts.OutName)
+			scanOpts.SetScanPath(opts.OutName)
 			scanOpts.SetProxy(opts.Proxy)
 			scanOpts.SetDryRun(opts.RunDry)
 			scanOpts.SetRateLimit(opts.RateLimit)
@@ -113,7 +138,7 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 		Args:  cobra.MinimumNArgs(0),
 		Run: func(cmd *cobra.Command, args []string) {
 
-			if opts.OutDirName == "" && opts.TargetFilePath == "" {
+			if opts.OutName == "" && opts.TargetFilePath == "" {
 				fmt.Println("[!] Error: target cannot be empty. Please run with --target [something] or --file [something]")
 				fmt.Println("\nIf you're confused, feel free to use --help option. :)~")
 				os.Exit(1)
@@ -122,20 +147,20 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 			helper.CheckProxy(opts.Proxy)
 			helper.DisplayNetInfo()
 
-			if opts.OutDirName == "" || !helper.Exists(opts.OutDirName) {
+			if opts.OutName == "" || !helper.Exists(opts.OutName) {
 				println("[!] collected assets will be sent to current working directory")
 			}
 
 			osintOpts := osint.OsintOpts{}
-			osintOpts.SetDomain(opts.OutDirName)
-			osintOpts.SetScanPath(opts.OutDirName)
+			osintOpts.SetDomain(opts.OutName)
+			osintOpts.SetScanPath(opts.OutName)
 			osintOpts.SetProxy(opts.Proxy)
 			osintOpts.SetDryRun(opts.RunDry)
 			osintOpts.SetRateLimit(opts.RateLimit)
 
 			// if used create subcommand, put the results in scans
-			if helper.Exists(opts.OutDirName + "/scans/") {
-				osintOpts.SetScanPath(opts.OutDirName + "/scans")
+			if helper.Exists(opts.OutName + "/scans/") {
+				osintOpts.SetScanPath(opts.OutName + "/scans")
 			}
 
 			if opts.TargetFilePath == "" {
@@ -161,19 +186,19 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 		Args:  cobra.MinimumNArgs(0),
 		Run: func(cmd *cobra.Command, args []string) {
 
-			if opts.OutDirName == "" {
+			if opts.OutName == "" {
 				fmt.Println("[!] Error: target cannot be empty. Please run with --target [something]")
 				fmt.Println("\nIf you're confused, feel free to use --help option. :)~")
 				os.Exit(1)
 			}
 
-			if strings.Contains(opts.OutDirName, ",") {
-				directories := strings.Split(opts.OutDirName, ",")
+			if strings.Contains(opts.OutName, ",") {
+				directories := strings.Split(opts.OutName, ",")
 				for _, d := range directories {
 					helper.SetUpDirectoryArchitecture(d)
 				}
 			} else {
-				helper.SetUpDirectoryArchitecture(opts.OutDirName)
+				helper.SetUpDirectoryArchitecture(opts.OutName)
 			}
 
 			fmt.Println("[+] Done. Happy hunting :)~")
@@ -182,17 +207,17 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 
 	defaults := GetDefaultOptions()
 	var rootCmd = createDirectories
-	rootCmd.Flags().StringVarP(&opts.OutDirName, "dir-name", "d", defaults.OutDirName, "Directory name to create (you can also put multiple names and separate them with a ,)")
+	rootCmd.Flags().StringVarP(&opts.OutName, "dir-name", "d", defaults.OutName, "Directory name to create (you can also put multiple names and separate them with a ,)")
 
 	rootCmd.AddCommand(osintCmd)
-	osintCmd.Flags().StringVarP(&opts.OutDirName, "dir-name", "d", defaults.OutDirName, "Outfile directory name (if no target-file is specified, will also be target domain)")
+	osintCmd.Flags().StringVarP(&opts.OutName, "dir-name", "d", defaults.OutName, "Outfile directory name (if no target-file is specified, will also be target domain)")
 	osintCmd.Flags().StringVarP(&opts.Proxy, "proxy", "p", defaults.Proxy, "Proxy URL (used for the tools supporting it. Other will prompt a warning msg)")
 	osintCmd.Flags().BoolVarP(&opts.RunDry, "dry", "", defaults.RunDry, "Run a dry-run (test mode)")
 	osintCmd.Flags().Int32VarP(&opts.RateLimit, "rate-limit", "r", defaults.RateLimit, "Requests rate-limit (used for the tools supporting it. Other will prompt a warning msg)")
 	osintCmd.Flags().StringVarP(&opts.TargetFilePath, "file", "f", defaults.TargetFilePath, "File containing list of targets (should be a list of IP Addresses or domains)")
 
 	rootCmd.AddCommand(scanCmd)
-	scanCmd.Flags().StringVarP(&opts.OutDirName, "dir-name", "d", defaults.OutDirName, "Outfile directory name (if no target-file is specified, will also be target domain)")
+	scanCmd.Flags().StringVarP(&opts.OutName, "dir-name", "d", defaults.OutName, "Outfile directory name (if no target-file is specified, will also be target domain)")
 	scanCmd.Flags().StringVarP(&opts.Proxy, "proxy", "p", defaults.Proxy, "Proxy URL (used for the tools supporting it. Other will prompt a warning msg)")
 	scanCmd.Flags().BoolVarP(&opts.RunDry, "dry", "", defaults.RunDry, "Run a dry-run (test mode)")
 	scanCmd.Flags().BoolVarP(&opts.NoGobuster, "disable-dirbusting", "", defaults.NoGobuster, "Disable directory bruteforce (sometimes you don't need it yk...)")
@@ -201,8 +226,15 @@ func GetParser(opts *StandardOptions) *cobra.Command {
 	scanCmd.Flags().StringVarP(&opts.WordlistPath, "wordlist", "w", defaults.WordlistPath, "Wordlist to use")
 	scanCmd.Flags().StringVarP(&opts.TargetFilePath, "file", "f", defaults.TargetFilePath, "File containing list of targets (should be a list of IP Addresses or domains)")
 
+	rootCmd.AddCommand(pruneCmd)
+	pruneCmd.Flags().StringVarP(&opts.OutName, "dir-name", "d", defaults.OutName, "Outfile directory name (if no target-file is specified, will also be target domain)")
+	pruneCmd.Flags().StringVarP(&opts.Proxy, "proxy", "p", defaults.Proxy, "Proxy URL (used for the tools supporting it. Other will prompt a warning msg)")
+	pruneCmd.Flags().BoolVarP(&opts.UseHttpInsecure, "insecure", "k", defaults.UseHttpInsecure, "Ignore SSL warnings and force http")
+	pruneCmd.Flags().StringVarP(&opts.TargetFilePath, "file", "f", defaults.TargetFilePath, "File containing list of targets (should be a list of IP Addresses or domains)")
+	pruneCmd.Flags().StringVarP(&opts.OutName, "out", "o", defaults.OutName, "output file path")
+
 	rootCmd.AddCommand(fingerprintCmd)
-	fingerprintCmd.Flags().StringVarP(&opts.OutDirName, "dir-name", "d", defaults.OutDirName, "Outfile directory name (if no target-file is specified, will also be target domain)")
+	fingerprintCmd.Flags().StringVarP(&opts.OutName, "dir-name", "d", defaults.OutName, "Outfile directory name (if no target-file is specified, will also be target domain)")
 	fingerprintCmd.Flags().StringVarP(&opts.Proxy, "proxy", "p", defaults.Proxy, "Proxy URL (used for the tools supporting it. Other will prompt a warning msg)")
 	fingerprintCmd.Flags().BoolVarP(&opts.UseHttpInsecure, "insecure", "k", defaults.UseHttpInsecure, "Ignore SSL warnings and force http")
 	fingerprintCmd.Flags().StringVarP(&opts.TargetFilePath, "file", "f", defaults.TargetFilePath, "File containing list of targets (should be a list of IP Addresses or domains)")
diff --git a/cli/types.go b/cli/types.go
@@ -3,8 +3,9 @@ package cli
 type StandardOptions struct {
 
 	// Pentest target, will be the name of the directories created, for instance
-	// if no target file is specified for OSINT command, will use that instead
-	OutDirName string
+	// if no target file is specified for OSINT command, will use that instead.
+	// Can be Outfile or OutDir
+	OutName string
 
 	// Run a dry-run (test mode)
 	RunDry bool
@@ -30,7 +31,7 @@ type StandardOptions struct {
 
 func GetDefaultOptions() StandardOptions {
 	opts := StandardOptions{
-		OutDirName:      "",
+		OutName:         "",
 		RunDry:          false,
 		Proxy:           "",
 		UseHttpInsecure: false,
diff --git a/prune/run.go b/prune/run.go
@@ -0,0 +1,70 @@
+package prune
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	helper "evil.djnn.sh/djnn/yellow/helpers"
+)
+
+type PruneOpts struct {
+	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
+}
+
+func (opts *PruneOpts) Run() {
+
+	helper.CheckProxy(opts.proxy)
+	helper.DisplayNetInfo()
+
+	results := make([]string, 0)
+	scanner := helper.LoadTargetFile(opts.inFilePath)
+	defer scanner.Close()
+
+	for scanner.Scan() {
+		targetDomain := scanner.Text()
+		httpAddr := "https://" + targetDomain
+		if opts.forceInsecure {
+			httpAddr = "http://" + targetDomain
+		}
+
+		if !helper.HasUnavailableWebInterface(httpAddr) {
+			fmt.Printf("[+] %s is alive !\n", targetDomain)
+			results = append(results, targetDomain)
+		}
+	}
+
+	content := strings.Join(results, "\n")
+	err := os.WriteFile(opts.outFilePath, []byte(content), 0644)
+	if err != nil {
+		fmt.Printf("Error writing to file: %v\n", err)
+		return
+	}
+
+	fmt.Printf("[+] Done. Saved %d alive domains\n", len(results))
+}