Skip to content

Commit

Permalink
sblookupxl: first version
Browse files Browse the repository at this point in the history
  • Loading branch information
luisguillenc committed Nov 4, 2020
1 parent 599fa7a commit 5ed82f2
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/xlistd/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
_ "github.com/luids-io/xlist/pkg/xlistd/components/memxl"
_ "github.com/luids-io/xlist/pkg/xlistd/components/mockxl"
_ "github.com/luids-io/xlist/pkg/xlistd/components/parallelxl"
_ "github.com/luids-io/xlist/pkg/xlistd/components/sblookupxl"
_ "github.com/luids-io/xlist/pkg/xlistd/components/selectorxl"
_ "github.com/luids-io/xlist/pkg/xlistd/components/sequencexl"
_ "github.com/luids-io/xlist/pkg/xlistd/components/wbeforexl"
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.13

require (
github.com/cavaliercoder/grab v2.0.0+incompatible
github.com/google/safebrowsing v0.0.0-20190624211811-bbf0d20d26b3
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/luids-io/api v0.0.0-20201020041807-6f7e0a27e8de
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/safebrowsing v0.0.0-20190624211811-bbf0d20d26b3 h1:4SV2fLwScO6iAgUKNqXwIrz9Fq2ykQxbSV4ObXtNCWY=
github.com/google/safebrowsing v0.0.0-20190624211811-bbf0d20d26b3/go.mod h1:hT4r/grkURkgVSWJaWd6PyS4xfAb+vb34DyMDYiOGa8=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
Expand Down Expand Up @@ -294,6 +296,7 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.5/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
Expand Down
93 changes: 93 additions & 0 deletions pkg/xlistd/components/sblookupxl/builder.go
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{}))
}
169 changes: 169 additions & 0 deletions pkg/xlistd/components/sblookupxl/list.go
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)
}

0 comments on commit 5ed82f2

Please sign in to comment.