Skip to content

Commit

Permalink
feat: basic implementation for filter expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
vdjagilev committed May 3, 2024
1 parent 2ad1104 commit 3f0c983
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 21 deletions.
8 changes: 6 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ var config = formatter.Config{
SqliteOutputOptions: formatter.SqliteOutputOptions{},
ExcelOptions: formatter.ExcelOutputOptions{},
},
ShowVersion: false,
CurrentVersion: VERSION,
ShowVersion: false,
CurrentVersion: VERSION,
FilterExpressions: []string{},
}

// VERSION is describing current version of the nmap-formatter
Expand Down Expand Up @@ -125,6 +126,9 @@ func init() {
// Configs related to D2 language
rootCmd.Flags().BoolVar(&config.OutputOptions.D2LangOptions.SkipDownHosts, "d2-skip-down-hosts", true, "--d2-skip-down-hosts=false, would print all hosts that are offline in D2 language output")

// Multiple filter expressions supported
rootCmd.Flags().StringArrayVar(&config.FilterExpressions, "filter", []string{}, "--filter '.Status.State == \"up\" && any(.Port, { .PortID in [80,443] })'")

workflow = &formatter.MainWorkflow{}
}

Expand Down
19 changes: 10 additions & 9 deletions formatter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ import (
// where output will be delivered, desired output format, input file path, output file path
// and different output options
type Config struct {
Writer io.WriteCloser
OutputFormat OutputFormat
InputFileConfig InputFileConfig
OutputFile OutputFile
OutputOptions OutputOptions
ShowVersion bool
TemplatePath string
CustomOptions []string
CurrentVersion string
Writer io.WriteCloser
OutputFormat OutputFormat
InputFileConfig InputFileConfig
OutputFile OutputFile
OutputOptions OutputOptions
ShowVersion bool
TemplatePath string
CustomOptions []string
CurrentVersion string
FilterExpressions []string
}

// CustomOptionsMap returns custom options provided in the CLI
Expand Down
52 changes: 52 additions & 0 deletions formatter/expr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package formatter

import (
"fmt"

"github.com/expr-lang/expr"
)

// filterExpr filters NMAPRun.Hosts by given expression
func filterExpr(r NMAPRun, code string) (NMAPRun, error) {
program, err := expr.Compile(
fmt.Sprintf("filter(Host, { %s })", code),
expr.Env(r),
)

if err != nil {
return r, err
}

output, err := expr.Run(program, r)
if err != nil {
return r, err
}

hosts, err := convertToHosts(output)

if err != nil {
return r, err
}

r.Host = hosts
return r, nil
}

// convertToHosts converts output from expression engine to []Host
func convertToHosts(output interface{}) ([]Host, error) {
outputInterfaces, ok := output.([]interface{})
if !ok {
return nil, fmt.Errorf("output is not []interface{}")
}

hosts := make([]Host, len(outputInterfaces))
for i, v := range outputInterfaces {
host, ok := v.(Host)
if !ok {
return nil, fmt.Errorf("element is not Host")
}
hosts[i] = host
}

return hosts, nil
}
133 changes: 133 additions & 0 deletions formatter/expr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package formatter

import (
"reflect"
"testing"
)

func Test_filterExpr(t *testing.T) {
type args struct {
nmapRUN NMAPRun
code string
}
tests := []struct {
name string
args args
want NMAPRun
wantErr bool
}{
{
name: "Basic test",
args: args{
nmapRUN: NMAPRun{
Scanner: "",
Args: "",
Start: 0,
StartStr: "",
Version: "",
ScanInfo: ScanInfo{},
Host: []Host{
{
StartTime: 0,
EndTime: 0,
Port: []Port{
{
Protocol: "http",
PortID: 80,
State: PortState{},
Service: PortService{},
Script: []Script{},
},
},
HostAddress: []HostAddress{
{
Address: "10.10.10.1",
AddressType: "ipv4",
Vendor: "",
},
},
HostNames: HostNames{},
Status: HostStatus{
State: "up",
Reason: "",
},
OS: OS{},
Trace: Trace{},
Uptime: Uptime{},
Distance: Distance{},
TCPSequence: TCPSequence{},
IPIDSequence: IPIDSequence{},
TCPTSSequence: TCPTSSequence{},
},
{
StartTime: 0,
EndTime: 0,
Port: []Port{
{
Protocol: "",
PortID: 22,
State: PortState{},
Service: PortService{},
Script: []Script{},
},
},
HostAddress: []HostAddress{
{
Address: "10.10.10.20",
AddressType: "ipv4",
Vendor: "",
},
},
HostNames: HostNames{},
Status: HostStatus{},
OS: OS{},
Trace: Trace{},
Uptime: Uptime{},
Distance: Distance{},
TCPSequence: TCPSequence{},
IPIDSequence: IPIDSequence{},
TCPTSSequence: TCPTSSequence{},
},
},
Verbose: Verbose{},
Debugging: Debugging{},
RunStats: RunStats{},
},
code: `.Status.State == "up" && any(.Port, { .PortID in [80] })`,
},
want: NMAPRun{
Scanner: "",
Args: "",
Start: 0,
StartStr: "",
Version: "",
ScanInfo: ScanInfo{},
Host: []Host{{StartTime: 0, EndTime: 0, Port: []Port{
{
Protocol: "http",
PortID: 80,
State: PortState{},
Service: PortService{},
Script: []Script{},
},
}, HostAddress: []HostAddress{{Address: "10.10.10.1", AddressType: "ipv4", Vendor: ""}}, HostNames: HostNames{}, Status: HostStatus{State: "up", Reason: ""}, OS: OS{}, Trace: Trace{}, Uptime: Uptime{}, Distance: Distance{}, TCPSequence: TCPSequence{}, IPIDSequence: IPIDSequence{}, TCPTSSequence: TCPTSSequence{}}},
Verbose: Verbose{},
Debugging: Debugging{},
RunStats: RunStats{},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := filterExpr(tt.args.nmapRUN, tt.args.code)
if (err != nil) != tt.wantErr {
t.Errorf("filterExpr() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("filterExpr() = %+v, want %+v", got, tt.want)
}
})
}
}
12 changes: 11 additions & 1 deletion formatter/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package formatter
import (
"encoding/xml"
"fmt"
"log"
"os"
)

Expand Down Expand Up @@ -57,9 +58,18 @@ func (w *MainWorkflow) Execute() (err error) {
return
}

filteredRun := NMAPRun
for _, expr := range w.Config.FilterExpressions {
log.Printf("filtering with expression: %s", expr)
filteredRun, err = filterExpr(filteredRun, expr)
if err != nil {
return fmt.Errorf("error filtering: %v", err)
}
}

// Build template data with NMAPRun entry & various output options
templateData := TemplateData{
NMAPRun: NMAPRun,
NMAPRun: filteredRun,
OutputOptions: w.Config.OutputOptions,
}

Expand Down
12 changes: 5 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module github.com/vdjagilev/nmap-formatter/v2
go 1.22.2

require (
github.com/expr-lang/expr v1.16.5
github.com/google/uuid v1.6.0
github.com/mattn/go-sqlite3 v1.14.22
github.com/spf13/cobra v1.8.0
github.com/xuri/excelize/v2 v2.8.1
golang.org/x/net v0.24.0
oss.terrastruct.com/d2 v0.6.5
)
Expand All @@ -23,6 +25,7 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand All @@ -31,8 +34,9 @@ require (
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
github.com/yuin/goldmark v1.6.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
Expand All @@ -45,9 +49,3 @@ require (
gonum.org/v1/plot v0.14.0 // indirect
oss.terrastruct.com/util-go v0.0.0-20231101220827-55b3812542c2 // indirect
)

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xuri/excelize/v2 v2.8.1
)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/expr-lang/expr v1.16.5 h1:m2hvtguFeVaVNTHj8L7BoAyt7O0PAIBaSVbjdHgRXMs=
github.com/expr-lang/expr v1.16.5/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/go-fonts/liberation v0.3.1 h1:9RPT2NhUpxQ7ukUvz3jeUckmN42T9D9TpjtQcqK/ceM=
Expand Down Expand Up @@ -141,8 +143,8 @@ github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ=
github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4=
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand Down

0 comments on commit 3f0c983

Please sign in to comment.