Skip to content

Commit

Permalink
Merge pull request #1089 from sundowndev/feat/plugins
Browse files Browse the repository at this point in the history
Add support for plugins
  • Loading branch information
sundowndev committed Jul 23, 2022
2 parents 4df45da + bb8b9f8 commit 269400d
Show file tree
Hide file tree
Showing 22 changed files with 328 additions and 74 deletions.
8 changes: 4 additions & 4 deletions api/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func localScan(c *gin.Context) {
return
}

result, err := remote.NewLocalScanner().Scan(num)
result, err := remote.NewLocalScanner().Scan(*num)
if err != nil {
handleError(c, errors.NewInternalError(err))
return
Expand All @@ -100,7 +100,7 @@ func numverifyScan(c *gin.Context) {
return
}

result, err := remote.NewNumverifyScanner(suppliers.NewNumverifySupplier()).Scan(num)
result, err := remote.NewNumverifyScanner(suppliers.NewNumverifySupplier()).Scan(*num)
if err != nil {
handleError(c, errors.NewInternalError(err))
return
Expand All @@ -127,7 +127,7 @@ func googleSearchScan(c *gin.Context) {
return
}

result, err := remote.NewGoogleSearchScanner().Scan(num)
result, err := remote.NewGoogleSearchScanner().Scan(*num)
if err != nil {
handleError(c, errors.NewInternalError(err))
return
Expand All @@ -154,7 +154,7 @@ func ovhScan(c *gin.Context) {
return
}

result, err := remote.NewOVHScanner(suppliers.NewOVHSupplier()).Scan(num)
result, err := remote.NewOVHScanner(suppliers.NewOVHSupplier()).Scan(*num)
if err != nil {
handleError(c, errors.NewInternalError(err))
return
Expand Down
11 changes: 7 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package cmd

import (
"fmt"
"github.com/fatih/color"
"os"

"github.com/spf13/cobra"
)

var inputNumber string

var rootCmd = &cobra.Command{
Use: "phoneinfoga [COMMANDS] [OPTIONS]",
Short: "Advanced information gathering & OSINT tool for phone numbers",
Expand All @@ -19,7 +18,11 @@ var rootCmd = &cobra.Command{
// Execute is a function that executes the root command
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
exitWithError(err)
}
}

func exitWithError(err error) {
fmt.Println(color.RedString(err.Error()))
os.Exit(1)
}
20 changes: 14 additions & 6 deletions cmd/scan.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"errors"
"fmt"
"github.com/fatih/color"
"github.com/sirupsen/logrus"
Expand All @@ -12,7 +13,9 @@ import (
"os"
)

var inputNumber string
var disabledScanners []string
var pluginPaths []string

func init() {
// Register command
Expand All @@ -21,6 +24,7 @@ func init() {
// Register flags
scanCmd.PersistentFlags().StringVarP(&inputNumber, "number", "n", "", "The phone number to scan (E164 or international format)")
scanCmd.PersistentFlags().StringArrayVarP(&disabledScanners, "disable", "D", []string{}, "A list of scanners to skip for this scan.")
scanCmd.PersistentFlags().StringSliceVar(&pluginPaths, "plugin", []string{}, "Extra scanner plugin to use for the scan")
// scanCmd.PersistentFlags().StringVarP(&input, "input", "i", "", "Text file containing a list of phone numbers to scan (one per line)")
// scanCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "Output to save scan results")
}
Expand All @@ -42,14 +46,19 @@ func runScan() {
"input": inputNumber,
"valid": valid,
}).Debug("Input phone number is invalid")
fmt.Println(color.RedString("Given phone number is not valid"))
os.Exit(1)
exitWithError(errors.New("given phone number is not valid"))
}

num, err := number.NewNumber(inputNumber)
if err != nil {
fmt.Println(color.RedString(err.Error()))
os.Exit(1)
exitWithError(err)
}

for _, p := range pluginPaths {
err := remote.OpenPlugin(p)
if err != nil {
exitWithError(err)
}
}

f := filter.NewEngine()
Expand All @@ -62,7 +71,6 @@ func runScan() {

err = output.GetOutput(output.Console, os.Stdout).Write(result, errs)
if err != nil {
fmt.Println(color.RedString(err.Error()))
os.Exit(1)
exitWithError(err)
}
}
13 changes: 13 additions & 0 deletions docs/scanners.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

PhoneInfoga provide several scanners to extract as much information as possible from a given phone number. Those scanners may require authentication, so they're automatically skipped when no authentication credentials are found. Note that all scanners use environment variables to find credentials.

## Building your own scanner

PhoneInfoga can now be extended with plugins! You can build your own scanner and PhoneInfoga will use it to scan the given phone number.

```shell
$ phoneinfoga scan -n +4176418xxxx --plugin ./custom_scanner.so
```

!!! info
For now, plugins are only supported through the CLI.

To get started, [see this example](https://github.com/sundowndev/phoneinfoga/tree/master/examples/plugin).

## Local

The local scan is probably the simplest scan of PhoneInfoga. By default, the tool statically parse the phone number and convert it to several formats, it also tries to recognize the country and the carrier. Those information are passed to all scanners in order to provide further analysis. The local scanner simply return those information to the end user so they can exploit it as well.
Expand Down
29 changes: 29 additions & 0 deletions examples/plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Example plugin

This is an example scanner plugin.

## Build

```shell
$ go build -buildmode=plugin ./customscanner.go
```

Depending on your OS, it will create a plugin file (e.g. `customscanner.so` for linux).

## Usage

You can now use this plugin with phoneinfoga.

```shell
$ phoneinfoga scan -n <number> --plugin ./customscanner.so

Running scan for phone number <number>...

Results for customscanner
Valid: true
Info: This number is known for scams!

...
```

The `--plugin` flag can be used multiple times to use several plugins at once.
43 changes: 43 additions & 0 deletions examples/plugin/customscanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"github.com/sundowndev/phoneinfoga/v2/lib/number"
"github.com/sundowndev/phoneinfoga/v2/lib/remote"
)

type customScanner struct{}

type customScannerResponse struct {
Valid bool `json:"valid" console:"Valid"`
Info string `json:"info" console:"Info"`
Hidden string `json:"-" console:"-"`
}

// Name returns the unique name this scanner.
func (s *customScanner) Name() string {
return "customscanner"
}

// ShouldRun returns a boolean indicating whether
// this scanner should be used or not.
// This can be useful to check for authentication or
// country code support for example, and avoid running
// the scanner when it just can't work.
func (s *customScanner) ShouldRun(n number.Number) bool {
return true
}

// Scan does the actual scan of the phone number.
// Note this function will be executed in a goroutine.
func (s *customScanner) Scan(n number.Number) (interface{}, error) {
data := customScannerResponse{
Valid: true,
Info: "This number is known for scams!",
Hidden: "This will not appear in the output",
}
return data, nil
}

func init() {
remote.RegisterPlugin(&customScanner{})
}
47 changes: 47 additions & 0 deletions examples/plugin/customscanner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import (
"github.com/stretchr/testify/assert"
"github.com/sundowndev/phoneinfoga/v2/lib/number"
"testing"
)

func TestCustomScanner(t *testing.T) {
testcases := []struct {
name string
number *number.Number
expected customScannerResponse
wantError string
}{
{
name: "test successful scan",
number: func() *number.Number {
n, _ := number.NewNumber("15556661212")
return n
}(),
expected: customScannerResponse{
Valid: true,
Info: "This number is known for scams!",
Hidden: "This will not appear in the output",
},
},
}

for _, tt := range testcases {
t.Run(tt.name, func(t *testing.T) {
scanner := &customScanner{}

if !scanner.ShouldRun(*tt.number) {
t.Fatal("ShouldRun() should be truthy")
}

got, err := scanner.Scan(*tt.number)
if tt.wantError != "" {
assert.EqualError(t, err, tt.wantError)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expected, got)
})
}
}
16 changes: 8 additions & 8 deletions lib/remote/googlesearch_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ func NewGoogleSearchScanner() *googlesearchScanner {
return &googlesearchScanner{}
}

func (s *googlesearchScanner) Identifier() string {
func (s *googlesearchScanner) Name() string {
return Googlesearch
}

func (s *googlesearchScanner) ShouldRun() bool {
func (s *googlesearchScanner) ShouldRun(_ number.Number) bool {
return true
}

func (s *googlesearchScanner) Scan(n *number.Number) (interface{}, error) {
func (s *googlesearchScanner) Scan(n number.Number) (interface{}, error) {
res := GoogleSearchResponse{
SocialMedia: getSocialMediaDorks(n),
DisposableProviders: getDisposableProvidersDorks(n),
Expand All @@ -50,7 +50,7 @@ func (s *googlesearchScanner) Scan(n *number.Number) (interface{}, error) {
return res, nil
}

func getDisposableProvidersDorks(number *number.Number) (results []*GoogleSearchDork) {
func getDisposableProvidersDorks(number number.Number) (results []*GoogleSearchDork) {
var dorks = []*dorkgen.GoogleSearch{
(&dorkgen.GoogleSearch{}).
Site("hs3x.com").
Expand Down Expand Up @@ -168,7 +168,7 @@ func getDisposableProvidersDorks(number *number.Number) (results []*GoogleSearch
return results
}

func getIndividualsDorks(number *number.Number) (results []*GoogleSearchDork) {
func getIndividualsDorks(number number.Number) (results []*GoogleSearchDork) {
var dorks = []*dorkgen.GoogleSearch{
(&dorkgen.GoogleSearch{}).
Site("numinfo.net").
Expand Down Expand Up @@ -224,7 +224,7 @@ func getIndividualsDorks(number *number.Number) (results []*GoogleSearchDork) {
return results
}

func getSocialMediaDorks(number *number.Number) (results []*GoogleSearchDork) {
func getSocialMediaDorks(number number.Number) (results []*GoogleSearchDork) {
var dorks = []*dorkgen.GoogleSearch{
(&dorkgen.GoogleSearch{}).
Site("facebook.com").
Expand Down Expand Up @@ -274,7 +274,7 @@ func getSocialMediaDorks(number *number.Number) (results []*GoogleSearchDork) {
return results
}

func getReputationDorks(number *number.Number) (results []*GoogleSearchDork) {
func getReputationDorks(number number.Number) (results []*GoogleSearchDork) {
var dorks = []*dorkgen.GoogleSearch{
(&dorkgen.GoogleSearch{}).
Site("whosenumber.info").
Expand Down Expand Up @@ -328,7 +328,7 @@ func getReputationDorks(number *number.Number) (results []*GoogleSearchDork) {
return results
}

func getGeneralDorks(number *number.Number) (results []*GoogleSearchDork) {
func getGeneralDorks(number number.Number) (results []*GoogleSearchDork) {
var dorks = []*dorkgen.GoogleSearch{
(&dorkgen.GoogleSearch{}).
Intext(number.International).
Expand Down
2 changes: 1 addition & 1 deletion lib/remote/googlesearch_scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func TestGoogleSearchScanner(t *testing.T) {
remote := NewLibrary(filter.NewEngine())
remote.AddScanner(scanner)

if !scanner.ShouldRun() {
if !scanner.ShouldRun(*tt.number) {
t.Fatal("ShouldRun() should be truthy")
}

Expand Down
2 changes: 2 additions & 0 deletions lib/remote/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ func InitScanners(remote *Library) {
remote.AddScanner(NewNumverifyScanner(numverifySupplier))
remote.AddScanner(NewGoogleSearchScanner())
remote.AddScanner(NewOVHScanner(ovhSupplier))

remote.LoadPlugins()
}
6 changes: 3 additions & 3 deletions lib/remote/local_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ func NewLocalScanner() *localScanner {
return &localScanner{}
}

func (s *localScanner) Identifier() string {
func (s *localScanner) Name() string {
return Local
}

func (s *localScanner) ShouldRun() bool {
func (s *localScanner) ShouldRun(_ number.Number) bool {
return true
}

func (s *localScanner) Scan(n *number.Number) (interface{}, error) {
func (s *localScanner) Scan(n number.Number) (interface{}, error) {
data := LocalScannerResponse{
RawLocal: n.RawLocal,
Local: n.Local,
Expand Down
2 changes: 1 addition & 1 deletion lib/remote/local_scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestLocalScanner(t *testing.T) {
remote := NewLibrary(filter.NewEngine())
remote.AddScanner(scanner)

if !scanner.ShouldRun() {
if !scanner.ShouldRun(*tt.number) {
t.Fatal("ShouldRun() should be truthy")
}

Expand Down
6 changes: 3 additions & 3 deletions lib/remote/numverify_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ func NewNumverifyScanner(s suppliers.NumverifySupplierInterface) *numverifyScann
return &numverifyScanner{client: s}
}

func (s *numverifyScanner) Identifier() string {
func (s *numverifyScanner) Name() string {
return Numverify
}

func (s *numverifyScanner) ShouldRun() bool {
func (s *numverifyScanner) ShouldRun(_ number.Number) bool {
return s.client.IsAvailable()
}

func (s *numverifyScanner) Scan(n *number.Number) (interface{}, error) {
func (s *numverifyScanner) Scan(n number.Number) (interface{}, error) {
res, err := s.client.Validate(n.International)
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit 269400d

Please sign in to comment.