-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
409 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package check | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net" | ||
"sort" | ||
"strings" | ||
"strconv" | ||
|
||
"github.com/jreisinger/checkip" | ||
) | ||
|
||
type onyphe struct { | ||
Text string `json:"text"` | ||
Vulns []string `json:"vulns"` | ||
Results onypheData `json:"results"` | ||
} | ||
|
||
type onypheData []struct { | ||
OS string `json:"os"` | ||
OsVendor string `json:"osvendor"` | ||
//Version string `json:"version"` | ||
Port interface{} `json:"port"` | ||
Protocol string `json:"protocol"` | ||
Product string `json:"product"` | ||
Transport string `json:"transport"` // tcp, udp | ||
} | ||
|
||
var onypheUrl = "https://www.onyphe.io/api/v2" | ||
|
||
// Onyphe gets generic information from api.onyphe.io. | ||
func Onyphe(ipaddr net.IP) (checkip.Result, error) { | ||
result := checkip.Result{ | ||
Name: "onyphe.io", | ||
Type: checkip.TypeInfoSec, | ||
} | ||
|
||
apiKey, err := getConfigValue("ONYPHE_API_KEY") | ||
if err != nil { | ||
return result, newCheckError(err) | ||
} | ||
if apiKey == "" { | ||
return result, nil | ||
} | ||
|
||
headers := map[string]string{ | ||
"Authorization": "bearer " + apiKey, | ||
"Accept": "application/json", | ||
//"Content-Type": "application/x-www-form-urlencoded", | ||
} | ||
var onyphe onyphe | ||
apiURL := fmt.Sprintf("%s/simple/datascan/%s", onypheUrl, ipaddr) | ||
if err := defaultHttpClient.GetJson(apiURL, headers, map[string]string{}, &onyphe); err != nil { | ||
return result, newCheckError(err) | ||
} | ||
|
||
for _, d := range onyphe.Results { | ||
var port string | ||
switch v := d.Port.(type) { | ||
case float64: | ||
port = fmt.Sprintf("%d", int(v)) | ||
case string: | ||
port = v | ||
} | ||
if port != "80" && port != "443" && port != "53" { // undecidable ports | ||
result.Malicious = true | ||
} | ||
} | ||
|
||
result.Info = onyphe | ||
|
||
return result, nil | ||
} | ||
|
||
type byPortO onypheData | ||
|
||
func (x byPortO) Len() int { return len(x) } | ||
func (x byPortO) Less(i, j int) bool { | ||
var portI float64 | ||
var portJ float64 | ||
switch v := x[i].Port.(type) { | ||
case float64: | ||
portI = v | ||
case string: | ||
portI, _ = strconv.ParseFloat(v, 64) | ||
} | ||
switch v := x[j].Port.(type) { | ||
case float64: | ||
portJ = v | ||
case string: | ||
portJ, _ = strconv.ParseFloat(v, 64) | ||
} | ||
return portI < portJ | ||
} | ||
|
||
func (x byPortO) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | ||
|
||
// Info returns interesting information from the check. | ||
func (o onyphe) Summary() string { | ||
var portInfo []string | ||
service := make(map[string]int) | ||
sort.Sort(byPortO(o.Results)) | ||
for _, d := range o.Results { | ||
var os string | ||
if d.OS != "" { | ||
os = d.OS | ||
} | ||
|
||
var osvendor string | ||
if d.OsVendor != "" { | ||
osvendor = d.OsVendor | ||
} | ||
|
||
var product string | ||
if d.Product != "" { | ||
product = d.Product + " " | ||
} | ||
|
||
var port string | ||
switch v := d.Port.(type) { | ||
case float64: | ||
port = fmt.Sprintf("%d", int(v)) | ||
case string: | ||
port = v | ||
} | ||
|
||
sport := fmt.Sprintf("%s/%s", d.Transport, port) | ||
service[sport]++ | ||
|
||
if service[sport] > 1 { | ||
continue | ||
} | ||
|
||
if os == "" && osvendor == "" { | ||
portInfo = append(portInfo, fmt.Sprintf("%s %s/%s", d.Protocol, d.Transport, port)) | ||
} else { | ||
ss := nonEmpty(os, osvendor) | ||
portInfo = append(portInfo, fmt.Sprintf("%s %s/%s %s(%s)", d.Protocol, d.Transport, port, product, strings.Join(ss, ", "))) | ||
} | ||
} | ||
|
||
return fmt.Sprintf("Open: %s", strings.Join(portInfo, ", ")) | ||
} | ||
|
||
func (o onyphe) Json() ([]byte, error) { | ||
return json.Marshal(o) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package check | ||
|
||
import ( | ||
"net" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/jreisinger/checkip" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestOnyphe(t *testing.T) { | ||
|
||
apiKey, err := getConfigValue("CENSYS_KEY") | ||
if err != nil || apiKey == "" { | ||
return | ||
} | ||
apiSec, err := getConfigValue("CENSYS_SEC") | ||
if err != nil || apiSec == "" { | ||
return | ||
} | ||
|
||
t.Run("given valid response then result and no error is returned", func(t *testing.T) { | ||
handlerFn := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { | ||
rw.WriteHeader(http.StatusOK) | ||
rw.Write(loadResponse(t, "onyphe_response.json")) | ||
}) | ||
|
||
testUrl := setMockHttpClient(t, handlerFn) | ||
setOnypheUrl(t, testUrl) | ||
|
||
result, err := Onyphe(net.ParseIP("118.25.6.39")) | ||
require.NoError(t, err) | ||
assert.Equal(t, "onyphe.io", result.Name) | ||
assert.Equal(t, checkip.TypeInfoSec, result.Type) | ||
assert.Equal(t, true, result.Malicious) | ||
assert.Equal(t, "Open: snmp udp/161 (RouterOS, Mikrotik), winbox tcp/8291 (Linux Kernel, Linux)", result.Info.Summary()) | ||
}) | ||
|
||
|
||
t.Run("given non 2xx response then error is returned", func(t *testing.T) { | ||
handlerFn := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { | ||
rw.WriteHeader(http.StatusInternalServerError) | ||
}) | ||
|
||
testUrl := setMockHttpClient(t, handlerFn) | ||
setOnypheUrl(t, testUrl) | ||
|
||
_, err := Onyphe(net.ParseIP("118.25.6.39")) | ||
require.Error(t, err) | ||
}) | ||
} | ||
|
||
// --- test helpers --- | ||
|
||
func setOnypheUrl(t *testing.T, testUrl string) { | ||
url := onypheUrl | ||
onypheUrl = testUrl | ||
t.Cleanup(func() { | ||
onypheUrl = url | ||
}) | ||
} |
Oops, something went wrong.