-
Notifications
You must be signed in to change notification settings - Fork 0
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
1 parent
599fa7a
commit 5ed82f2
Showing
5 changed files
with
267 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
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
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
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,93 @@ | ||
// Copyright 2020 Luis Guillén Civera <luisguillenc@gmail.com>. See LICENSE. | ||
|
||
package sblookupxl | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/luids-io/api/xlist" | ||
"github.com/luids-io/core/option" | ||
"github.com/luids-io/xlist/pkg/xlistd" | ||
) | ||
|
||
// Builder returns a builder function. | ||
func Builder(defaultCfg Config) xlistd.BuildListFn { | ||
return func(b *xlistd.Builder, parents []string, def xlistd.ListDef) (xlistd.List, error) { | ||
//get cache file | ||
dbname := fmt.Sprintf("%s.db", def.ID) | ||
if def.Source != "" { | ||
dbname = def.Source | ||
} | ||
cfg := defaultCfg | ||
cfg.Database = b.DataPath(dbname) | ||
//check resources | ||
resources := xlist.ClearResourceDups(def.Resources, true) | ||
if len(resources) != 1 || resources[0] != xlist.Domain { | ||
return nil, errors.New("invalid 'resources': sblookup only supports domain resource") | ||
} | ||
//get options | ||
if def.Opts != nil { | ||
var err error | ||
cfg, err = parseOptions(cfg, def.Opts) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
//create RBL list | ||
bl, err := New(def.ID, cfg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
//register shutdown | ||
b.OnShutdown(func() error { | ||
b.Logger().Debugf("shutting down '%s'", def.ID) | ||
bl.Close() | ||
return nil | ||
}) | ||
return bl, nil | ||
} | ||
} | ||
|
||
func parseOptions(src Config, opts map[string]interface{}) (Config, error) { | ||
dst := src | ||
reason, ok, err := option.String(opts, "reason") | ||
if err != nil { | ||
return dst, err | ||
} | ||
if ok { | ||
dst.Reason = reason | ||
} | ||
|
||
apikey, ok, err := option.String(opts, "apikey") | ||
if err != nil { | ||
return dst, err | ||
} | ||
if ok { | ||
dst.APIKey = apikey | ||
} | ||
|
||
serverurl, ok, err := option.String(opts, "serverurl") | ||
if err != nil { | ||
return dst, err | ||
} | ||
if ok { | ||
dst.ServerURL = serverurl | ||
} | ||
|
||
threats, ok, err := option.SliceString(opts, "threats") | ||
if err != nil { | ||
return dst, err | ||
} | ||
if ok { | ||
dst.Threats = make([]string, 0, len(threats)) | ||
for _, t := range threats { | ||
dst.Threats = append(dst.Threats, t) | ||
} | ||
} | ||
return dst, nil | ||
} | ||
|
||
func init() { | ||
xlistd.RegisterListBuilder(ComponentClass, Builder(Config{})) | ||
} |
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,169 @@ | ||
// Copyright 2020 Luis Guillén Civera <luisguillenc@gmail.com>. See LICENSE. | ||
|
||
// Package sblookupxl provides a xlistd.List implementation that uses | ||
// google safe browsing api as source. | ||
// | ||
// This package is a work in progress and makes no API stability promises. | ||
package sblookupxl | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/google/safebrowsing" | ||
|
||
"github.com/luids-io/api/xlist" | ||
) | ||
|
||
// ComponentClass registered. | ||
const ComponentClass = "sblookup" | ||
|
||
// Config options. | ||
type Config struct { | ||
ServerURL string | ||
APIKey string | ||
Database string | ||
ForceValidation bool | ||
Reason string | ||
Threats []string | ||
} | ||
|
||
type options struct { | ||
forceValidation bool | ||
reason string | ||
} | ||
|
||
// List implements an RBL that uses google safe browsing api as source | ||
type List struct { | ||
id string | ||
opts options | ||
closed bool | ||
sbrowser *safebrowsing.SafeBrowser | ||
} | ||
|
||
// New constructs a new List with config. | ||
func New(id string, cfg Config) (*List, error) { | ||
threats, err := getThreats(cfg.Threats) | ||
if err != nil { | ||
return nil, err | ||
} | ||
sb, err := safebrowsing.NewSafeBrowser(safebrowsing.Config{ | ||
ServerURL: cfg.ServerURL, | ||
APIKey: cfg.APIKey, | ||
DBPath: cfg.Database, | ||
ThreatLists: threats, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
l := &List{ | ||
id: id, | ||
sbrowser: sb, | ||
opts: options{ | ||
forceValidation: cfg.ForceValidation, | ||
reason: cfg.Reason, | ||
}, | ||
} | ||
return l, nil | ||
} | ||
|
||
// ID implements xlistd.List interface. | ||
func (l *List) ID() string { | ||
return l.id | ||
} | ||
|
||
// Class implements xlistd.List interface. | ||
func (l *List) Class() string { | ||
return ComponentClass | ||
} | ||
|
||
// Check implements xlist.Checker interface. | ||
func (l *List) Check(ctx context.Context, name string, resource xlist.Resource) (xlist.Response, error) { | ||
if l.closed { | ||
return xlist.Response{}, xlist.ErrUnavailable | ||
} | ||
if resource != xlist.IPv4 && resource != xlist.Domain { | ||
return xlist.Response{}, xlist.ErrNotSupported | ||
} | ||
name, _, err := xlist.DoValidation(ctx, name, resource, l.opts.forceValidation) | ||
if err != nil { | ||
return xlist.Response{}, err | ||
} | ||
threads, err := l.sbrowser.LookupURLsContext(ctx, []string{name}) | ||
if err != nil { | ||
return xlist.Response{}, err | ||
} | ||
if len(threads[0]) > 0 { | ||
if l.opts.reason != "" { | ||
return xlist.Response{Result: true, Reason: l.opts.reason}, nil | ||
} | ||
reasons := make([]string, 0, len(threads[0])) | ||
for _, t := range threads[0] { | ||
reasons = append(reasons, fmt.Sprintf("%v", t.ThreatDescriptor)) | ||
} | ||
return xlist.Response{Result: true, Reason: strings.Join(reasons, ",")}, nil | ||
} | ||
return xlist.Response{}, nil | ||
} | ||
|
||
// Resources implements xlist.Checker interface. | ||
func (l *List) Resources() []xlist.Resource { | ||
return []xlist.Resource{xlist.IPv4, xlist.Domain} | ||
} | ||
|
||
// Ping implements xlist.Checker interface. | ||
func (l *List) Ping() error { | ||
if l.closed { | ||
return errors.New("list is closed") | ||
} | ||
_, err := l.sbrowser.Status() | ||
return err | ||
} | ||
|
||
// Close closes the database file. | ||
func (l *List) Close() { | ||
if !l.closed { | ||
l.closed = true | ||
l.sbrowser.Close() | ||
} | ||
} | ||
|
||
func getThreats(items []string) ([]safebrowsing.ThreatDescriptor, error) { | ||
var ret []safebrowsing.ThreatDescriptor | ||
if len(items) == 0 { | ||
//ret nil slice | ||
return ret, nil | ||
} | ||
ret = make([]safebrowsing.ThreatDescriptor, 0, len(items)) | ||
for _, i := range items { | ||
ttype, err := getThreatType(i) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ret = append(ret, safebrowsing.ThreatDescriptor{ | ||
ThreatType: ttype, | ||
ThreatEntryType: safebrowsing.ThreatEntryType_URL, | ||
PlatformType: safebrowsing.PlatformType_AllPlatforms, | ||
}) | ||
} | ||
return ret, nil | ||
} | ||
|
||
func getThreatType(s string) (safebrowsing.ThreatType, error) { | ||
s = strings.ToLower(s) | ||
switch s { | ||
case "malware": | ||
return safebrowsing.ThreatType_Malware, nil | ||
case "phising": | ||
return safebrowsing.ThreatType_SocialEngineering, nil | ||
case "social_engineering": | ||
return safebrowsing.ThreatType_SocialEngineering, nil | ||
case "unwanted": | ||
return safebrowsing.ThreatType_UnwantedSoftware, nil | ||
case "unwanted_software": | ||
return safebrowsing.ThreatType_UnwantedSoftware, nil | ||
} | ||
return safebrowsing.ThreatType_Malware, fmt.Errorf("unknown ThreatType value '%s'", s) | ||
} |