Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,47 @@ default:
@just --list

# start nerfs artifact builder in demo mode
[group('build')]
run mode: compile
$GOBIN/nerfs {{mode}}

# compile the nerfs executable
[group('build')]
compile: tidy
cd cmds/nerfs && go install

# tidy up Go modules
[group('build')]
tidy:
go mod tidy

# vet the nerfs source tree
[group('lint')]
vet:
go vet ./...

# run go test on the source tree
[group('testing')]
tests:
go test -race ./...

# run specific unit test
[group('testing')]
[no-cd]
test unit:
go test -v -count=1 -race -run {{unit}} 2>/dev/null

# lint the nerfs source tree
[group('lint')]
lint: vet
$GOBIN/golangci-lint run --config $scripts/golangci.yaml

# show host system information
[group('build')]
@sysinfo:
echo "{{os()/arch()}} {{num_cpus()}}c"

# locally install build dependencies
[group('build')]
init:
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
8 changes: 5 additions & 3 deletions cmds/nerfs/commands/commands.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package commands

import (
"fmt"
"os"

"cattlecloud.net/go/babycli"
"cattlecloud.net/go/ulog"
"cattlecloud.net/nerfs/cmds/nerfs/domains"
"cattlecloud.net/nerfs/cmds/nerfs/wordlist"
)
Expand Down Expand Up @@ -40,17 +40,19 @@ func invoke(args []string) babycli.Code {
},
},
Function: func(c *babycli.Component) babycli.Code {
log := ulog.New("nerfs")

output := c.GetString("output")

buildDomains := domains.NewBuilder()
if err := buildDomains.Build(output); err != nil {
fmt.Println("build failure:", err)
log.E.Fmt("unable to build domains list: %v", err)
return babycli.Failure
}

buildWords := wordlist.NewBuilder()
if err := buildWords.Build(output); err != nil {
fmt.Println("build failure:", err)
log.E.Fmt("unable to build words list: %v", err)
return babycli.Failure
}

Expand Down
25 changes: 14 additions & 11 deletions nerfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,15 @@ import (

const (
DomainsFile = "domains.txt"
WordsFile = "wordlist.txt"
WordsFile = "wordlist.json"
)

// An Artifact contains the in-memory optimized form of the domain and word
// block-list files created by the nerfs tool.
//
// Create an Artifact by calling Load() with the directory of the compiled
// artifacts.
type Artifact struct {
domains *set.Set[string]
words []*regexp.Regexp
// Call Synopsis on some text to analyze it.
type Artifact interface {
Synopsis(r io.Reader) *Synopsis
}

// A Synopsis contains the result of analyzing some text.
Expand All @@ -36,6 +34,11 @@ type Synopsis struct {
Words int
}

type artifact struct {
domains *set.Set[string]
words []*regexp.Regexp
}

// Any returns whether any domains or words appear on the block lists.
func (s *Synopsis) Any() bool {
return s.Domains > 0 || s.Words > 0
Expand All @@ -47,7 +50,7 @@ func (s *Synopsis) Any() bool {
// Note that this should be used carefully - scanning large text consumes lots
// of CPU due to regular expression matching against every token. Many use cases
// need only scan against blocked domains which is must faster.
func (a *Artifact) Synopsis(r io.Reader) *Synopsis {
func (a *artifact) Synopsis(r io.Reader) *Synopsis {
syn := new(Synopsis)
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanWords)
Expand All @@ -63,7 +66,7 @@ func (a *Artifact) Synopsis(r io.Reader) *Synopsis {
return syn
}

func (a *Artifact) matchDomain(s string) bool {
func (a *artifact) matchDomain(s string) bool {
s, _ = strings.CutPrefix(s, "https://")
s, _ = strings.CutPrefix(s, "http://")
if i := strings.IndexAny(s, "/?#"); i >= 0 {
Expand All @@ -72,7 +75,7 @@ func (a *Artifact) matchDomain(s string) bool {
return a.domains.Contains(s)
}

func (a *Artifact) matchWord(s string) bool {
func (a *artifact) matchWord(s string) bool {
for _, reg := range a.words {
if reg.MatchString(s) {
return true
Expand All @@ -81,7 +84,7 @@ func (a *Artifact) matchWord(s string) bool {
return false
}

func Load(directory string) (*Artifact, error) {
func Load(directory string) (Artifact, error) {
domainsFile := filepath.Join(directory, DomainsFile)
wordsFile := filepath.Join(directory, WordsFile)

Expand All @@ -95,7 +98,7 @@ func Load(directory string) (*Artifact, error) {
return nil, werr
}

return &Artifact{
return &artifact{
domains: domains,
words: words,
}, nil
Expand Down
8 changes: 4 additions & 4 deletions nerfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ func TestArtifact_Load(t *testing.T) {

art, lerr := Load(d)
must.NoError(t, lerr)
must.Size(t, 3, art.domains)
must.Len(t, 2, art.words)
must.Size(t, 3, art.(*artifact).domains)
must.Len(t, 2, art.(*artifact).words)
}

func TestSynopsis_matching(t *testing.T) {
t.Parallel()

a := &Artifact{
a := &artifact{
domains: set.From([]string{"example.com", "example.org", "example.xyz"}),
words: []*regexp.Regexp{
regexp.MustCompile(`p[oO0]+p`),
Expand Down Expand Up @@ -116,7 +116,7 @@ visit example.org we have pOop and f4rts! example.xyz and example.com too!
func TestArtifact_matchDomain(t *testing.T) {
t.Parallel()

a := &Artifact{
a := &artifact{
domains: set.From([]string{"example.com", "example.xyz"}),
}

Expand Down