Skip to content

Commit

Permalink
improve JSON output and add Name()
Browse files Browse the repository at this point in the history
  • Loading branch information
Jozef Reisinger committed Nov 5, 2021
1 parent ebfce2c commit 72c7a9d
Show file tree
Hide file tree
Showing 15 changed files with 88 additions and 51 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ run: install
checkip 140.82.114.4
checkip 218.92.0.158
checkip 92.118.160.17
checkip -j 92.118.160.17 | jq -r '.[] | select(.Type=="Sec") | "\(.Name) => \(.IsMalicious)"'

PLATFORMS := linux/amd64 darwin/amd64 linux/arm windows/amd64

Expand Down
2 changes: 2 additions & 0 deletions abuseipdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type AbuseIPDB struct {
} `json:"data"`
}

func (a *AbuseIPDB) Name() string { return "abuseipdb.com" }

// Check fills in AbuseIPDB data for a given IP address. It gets the data from
// api.abuseipdb.com/api/v2/check (docs.abuseipdb.com/#check-endpoint).
func (a *AbuseIPDB) Check(ipaddr net.IP) error {
Expand Down
4 changes: 3 additions & 1 deletion as.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type AS struct {
CountryCode string
}

func (a *AS) Name() string { return "iptoasn.com" }

// Check fills in AS data for a given IP address. The data is taken from a TSV
// file ip2asn-combined downloaded from iptoasn.com. The file is created or
// updated as needed.
Expand All @@ -39,7 +41,7 @@ func (a *AS) Check(ipaddr net.IP) error {

// Info returns interesting information from the check.
func (a *AS) Info() string {
return fmt.Sprintf("AS description\t%s", a.Description)
return a.Description
}

// search searches the ippadrr in tsvFile and if found fills in AS data.
Expand Down
96 changes: 56 additions & 40 deletions checkip.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ import (
"fmt"
"net"
"os"
"regexp"
"sort"
"sync"

"github.com/logrusorgru/aurora"
)

// Checker runs a check of an IP address.
// Checker runs a check of an IP address. It also returns its name.
type Checker interface {
Check(ip net.IP) error
Name() string
}

// InfoChecker finds information about an IP address.
Expand All @@ -33,72 +34,87 @@ type SecChecker interface {
}

// Run runs checkers concurrently checking the ipaddr.
func Run(checkers []Checker, ipaddr net.IP) Result {
var res Result
func Run(checkers []Checker, ipaddr net.IP) []Result {
var res []Result

var wg sync.WaitGroup
for _, c := range checkers {
for _, chk := range checkers {
wg.Add(1)
go func(c Checker) {
defer wg.Done()
if err := c.Check(ipaddr); err != nil {
res.Errors = append(res.Errors, redactSecrets(err.Error()))
err := c.Check(ipaddr)
switch v := c.(type) {
case InfoChecker:
r := Result{Name: v.Name(), Type: "Info", Data: v, Info: v.Info(), Err: err}
res = append(res, r)
case SecChecker:
r := Result{Name: c.Name(), Type: "Sec", Data: v, IsMalicious: v.IsMalicious(), Err: err}
res = append(res, r)
}
}(c)
}
wg.Wait()

var total, malicious int
for _, c := range checkers {
switch ip := c.(type) {
case InfoChecker:
res.Infos = append(res.Infos, ip.Info())
case SecChecker:
total++
if ip.IsMalicious() {
malicious++
}
}

}(chk)
}
res.ProbabilityMalicious = float64(malicious) / float64(total)
wg.Wait()

return res
}

func redactSecrets(s string) string {
key := regexp.MustCompile(`(key|pass|password)=\w+`)
return key.ReplaceAllString(s, "${1}=REDACTED")
}
// func redactSecrets(s string) string {
// key := regexp.MustCompile(`(key|pass|password)=\w+`)
// return key.ReplaceAllString(s, "${1}=REDACTED")
// }

// Result holds the result of running a check.
// Result holds the result of a check.
type Result struct {
Infos []string
ProbabilityMalicious float64
Errors []string
Name string
Type string
Data Checker
Info string
IsMalicious bool
Err error
}

func (res Result) Print() error {
for _, i := range res.Infos {
fmt.Println(i)
type byName []Result

func (x byName) Len() int { return len(x) }
func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }

// Print prints condensed results to stdout.
func Print(results []Result) error {
sort.Sort(byName(results))

var malicious, total float64
for _, r := range results {
if r.Type == "Info" {
fmt.Printf("%-15s %s\n", r.Name, r.Info)
continue
}
if r.IsMalicious {
malicious++
}
total++
}
probabilityMalicious := malicious / total

var msg string

switch {
case res.ProbabilityMalicious < 0.15:
case probabilityMalicious < 0.15:
msg = fmt.Sprint(aurora.Green("Malicious"))
case res.ProbabilityMalicious < 0.50:
case probabilityMalicious < 0.50:
msg = fmt.Sprint(aurora.Yellow("Malicious"))
default:
msg = fmt.Sprint(aurora.Red("Malicious"))
}

_, err := fmt.Printf("%s\t%.0f%%\n", msg, res.ProbabilityMalicious*100)
_, err := fmt.Printf("%s\t%.0f%%\n", msg, probabilityMalicious*100)
return err
}

func (res Result) PrintJSON() error {
// PrintJSON prints all data from results in JSON format to stdout.
func PrintJSON(results []Result) error {
sort.Sort(byName(results))

enc := json.NewEncoder(os.Stdout)
return enc.Encode(&res)
return enc.Encode(&results)
}
2 changes: 2 additions & 0 deletions cins.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type CINSArmy struct {
CountIPs int
}

func (c *CINSArmy) Name() string { return "cinsscore.com" }

// Check fills in the CINSArmy data.
func (c *CINSArmy) Check(ipaddr net.IP) error {
file := "/var/tmp/cins.txt"
Expand Down
7 changes: 2 additions & 5 deletions cmd/checkip.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,8 @@ func main() {

results := checkip.Run(checkers, ipaddr)
if *j {
results.PrintJSON()
checkip.PrintJSON(results)
} else {
for _, e := range results.Errors {
log.Println(e)
}
results.Print()
checkip.Print(results)
}
}
5 changes: 3 additions & 2 deletions dns.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package checkip

import (
"fmt"
"net"
"strings"
)
Expand All @@ -11,6 +10,8 @@ type DNS struct {
Names []string
}

func (d *DNS) Name() string { return "net.LookupAddr" }

// Check does a reverse lookup for a given IP address.
func (d *DNS) Check(ipaddr net.IP) error {
// NOTE: We are ignoring error. It says: "nodename nor servname
Expand All @@ -22,5 +23,5 @@ func (d *DNS) Check(ipaddr net.IP) error {

// Info returns interesting information from the check.
func (d *DNS) Info() string {
return fmt.Sprintf("DNS names\t%s", strings.Join(d.Names, ", "))
return strings.Join(d.Names, ", ")
}
2 changes: 2 additions & 0 deletions et.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type ET struct {
CountIPs int
}

func (e *ET) Name() string { return "rules.emergingthreats.net" }

// Check checks whether the ippaddr is not among compromised IP addresses from
// The Emerging Threats Intelligence feed (ET). I found ET mentioned at
// https://logz.io/blog/open-source-threat-intelligence-feeds/
Expand Down
4 changes: 3 additions & 1 deletion geo.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type Geo struct {
City, Country, IsoCode string
}

func (g *Geo) Name() string { return "maxmind.com" }

// Check fills in the geolocation data. The data is taken from
// GeoLite2-City.mmdb file that gets downloaded and regularly updated.
func (g *Geo) Check(ip net.IP) error {
Expand Down Expand Up @@ -56,5 +58,5 @@ func (g *Geo) Info() string {
if g.IsoCode == "" {
g.IsoCode = "ISO code unknown"
}
return fmt.Sprintf("Geolocation\t%s, %s (%s)", g.City, g.Country, g.IsoCode)
return fmt.Sprintf("%s, %s (%s)", g.City, g.Country, g.IsoCode)
}
4 changes: 3 additions & 1 deletion ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ type IP struct {
DefaultMask net.IPMask
}

func (i *IP) Name() string { return "net.IP" }

// Check fills in IP data.
func (i *IP) Check(ipaddr net.IP) error {
i.Private = ipaddr.IsPrivate()
Expand All @@ -30,5 +32,5 @@ func (i *IP) Info() string {
for _, b := range i.DefaultMask {
mask = append(mask, strconv.Itoa(int(b)))
}
return fmt.Sprintf("IP address\t%s, default mask %s", private, strings.Join(mask, "."))
return fmt.Sprintf("%s, default mask %s", private, strings.Join(mask, "."))
}
2 changes: 2 additions & 0 deletions ipsum.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type IPsum struct {
NumOfBlacklists int
}

func (ip *IPsum) Name() string { return "github.com/stamparm/ipsum" }

// Check checks how many blackists the IP address is found on.
func (ip *IPsum) Check(ipaddr net.IP) error {
file := "/var/tmp/ipsum.txt"
Expand Down
2 changes: 2 additions & 0 deletions otx.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type OTX struct {
} `json:"pulse_info"`
}

func (otx *OTX) Name() string { return "otx.alienvault.com" }

// Check gets data from https://otx.alienvault.com/api.
func (otx *OTX) Check(ipaddr net.IP) error {
apiurl := fmt.Sprintf("https://otx.alienvault.com/api/v1/indicators/IPv4/%s/", ipaddr.String())
Expand Down
4 changes: 3 additions & 1 deletion shodan.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type data []struct {
Transport string `json:"transport"` // tcp, udp
}

func (s *Shodan) Name() string { return "shodan.io" }

// Check fills in Shodan data for a given IP address. Its get the data from
// https://api.shodan.io.
func (s *Shodan) Check(ipaddr net.IP) error {
Expand Down Expand Up @@ -92,5 +94,5 @@ func (s *Shodan) Info() string {
portStr += ":"
}

return fmt.Sprintf("OS and ports\t%s, %d open %s %s", os, len(portInfo), portStr, strings.Join(portInfo, ", "))
return fmt.Sprintf("%s, %d open %s %s", os, len(portInfo), portStr, strings.Join(portInfo, ", "))
}
2 changes: 2 additions & 0 deletions threatcrowd.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type ThreatCrowd struct {
Votes int `json:"votes"`
}

func (t *ThreatCrowd) Name() string { return "threatcrowd.org" }

// Check retrieves information from
// https://www.threatcrowd.org/searchApi/v2/ip/report.
func (t *ThreatCrowd) Check(ipaddr net.IP) error {
Expand Down
2 changes: 2 additions & 0 deletions virustotal.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type VirusTotal struct {
} `json:"data"`
}

func (vt *VirusTotal) Name() string { return "virustotal.com" }

// Check fills in data about ippaddr from https://www.virustotal.com/api
func (vt *VirusTotal) Check(ipaddr net.IP) error {
apiKey, err := getConfigValue("VIRUSTOTAL_API_KEY")
Expand Down

0 comments on commit 72c7a9d

Please sign in to comment.