tomnomnom/hacks: The Unix Philosophy Applied to Bug Bounty Hunting
Hook
The most starred security toolkit on GitHub isn't a framework with extensive documentation—it's a folder of 'hacks' with barely any README files, and that's exactly why it works.
Context
In the world of bug bounty hunting and penetration testing, researchers often find themselves performing the same text manipulation tasks repeatedly: extracting domains from URLs, filtering out duplicates, comparing lists, or transforming data formats. Traditional security frameworks like Burp Suite or OWASP ZAP excel at comprehensive scanning but fall short when you need quick, scriptable data transformation. Writing bash one-liners gets you partway there, but shell scripting becomes unwieldy for anything beyond basic text processing.
Tom Hudson (tomnomnom), a prominent security researcher and bug bounty hunter, solved this by creating purpose-built Go utilities that slot perfectly into Unix pipelines. Rather than building yet another monolithic security framework, he open-sourced his personal collection of scripts—small, focused tools that each solve one problem well. These aren't polished products with marketing pages; they're the actual tools a working security professional reaches for daily. The repository's 2,400+ stars reflect its utility: developers recognize battle-tested code when they see it.
Technical Insight
The genius of tomnomnom/hacks lies in its adherence to the Unix philosophy: write programs that do one thing well, work together, and handle text streams. Each tool is a standalone Go binary with a main package, minimal dependencies, and a clear purpose. Let's examine several tools to understand the architectural patterns.
Consider unfurl, a URL parser that extracts specific components. Instead of providing a complex API, it reads URLs from stdin and outputs structured data:
// Simplified conceptual example from unfurl
package main
import (
"bufio"
"fmt"
"net/url"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
u, err := url.Parse(scanner.Text())
if err != nil {
continue
}
// Extract component based on flag
fmt.Println(u.Host)
}
}
This pattern—read from stdin, process line by line, write to stdout—appears throughout the repository. It enables powerful compositions like: cat urls.txt | unfurl domains | sort -u | httprobe. Each tool focuses on its single responsibility without worrying about what comes before or after.
Another excellent example is gron, which transforms JSON into discrete assignments that are easier to grep:
echo '{"user":{"name":"alice","id":123}}' | gron
# Output:
json = {};
json.user = {};
json.user.id = 123;
json.user.name = "alice";
This transformation seems simple, but it solves a real problem: grepping through complex JSON is painful. By flattening JSON into line-oriented output, gron makes it pipeline-friendly. You can grep for specific paths, filter them, then use gron --ungron to reconstruct valid JSON. The implementation uses Go's encoding/json package for parsing and a recursive descent to generate assignments—straightforward code that does one thing exceptionally well.
The anew utility demonstrates another common pattern: stateful filtering. It appends lines to a file only if they don't already exist, essentially acting as a persistent deduplicator:
# First run
echo -e "example.com\ntest.com" | anew domains.txt
# domains.txt now contains both
# Second run
echo -e "test.com\nnewthing.com" | anew domains.txt
# Only newthing.com is appended; test.com already existed
This is invaluable for incremental reconnaissance—you can continuously feed new discoveries into your target list without manual deduplication. The implementation maintains an in-memory set of existing lines for O(1) lookup, only appending genuinely new entries.
What makes these tools particularly effective is their compilation to static binaries. Unlike Python or bash scripts that require interpreters and dependencies, a Go binary runs anywhere. Security researchers often work on minimal Linux environments, temporary cloud instances, or air-gapped networks—contexts where installing dependencies is difficult or impossible. Dropping a single binary into /usr/local/bin is frictionless.
The repository also showcases Go's strengths for CLI tools: fast startup time (critical for pipeline tools that launch repeatedly), excellent standard library support for text processing and networking (url.Parse, http.Client, bufio.Scanner), and straightforward concurrency primitives when parallelization makes sense. Tools like httprobe, which checks if hosts respond on HTTP/HTTPS, use goroutines to probe hundreds of targets concurrently without complex threading code.
Gotcha
The repository's casual 'hacks' label isn't false advertising—these tools have rough edges. Most lack comprehensive error handling, usage documentation, or input validation. You'll need to read the source code to understand what a tool actually does, and some tools have hardcoded assumptions that may not match your use case. For instance, timeout values might be fixed, output formats might not be configurable, and edge cases in input data might cause silent failures or panics.
More significantly, there's no stability guarantee. Since these are personal tools that Tom Hudson shared publicly, they're maintained primarily for his own use. Tools might be updated in breaking ways, removed entirely, or left unmaintained if they no longer serve his workflows. The repository has no semantic versioning, no changelog, and no issues/PR process typical of community-maintained projects. If you depend on these tools in production environments or automated pipelines, you should vendor them or fork the repository to prevent unexpected breakage. This isn't a criticism—the repository delivers exactly what it promises—but enterprises expecting SLAs or comprehensive support should look elsewhere.
Verdict
Use if: You're engaged in security research, bug bounty hunting, or penetration testing where pipeline-based workflows are essential; you value simple, auditable code over feature-rich frameworks; you're comfortable reading Go source to understand tool behavior; you need lightweight, portable binaries that run anywhere without dependencies; or you want proven tools from a respected practitioner rather than academic implementations. Skip if: You need comprehensive documentation and guaranteed stability for production systems; you require enterprise support or active community maintenance; you're looking for a unified security framework with a cohesive feature set rather than standalone utilities; you prefer tools with extensive error handling and input validation; or you're uncomfortable adapting or forking code to fit your specific requirements. These are power tools for power users—invaluable in the right hands, but deliberately minimalist.