Path: blob/master/lib/remote/googlecse_scanner.go
988 views
package remote12import (3"context"4"errors"5"fmt"6"github.com/sundowndev/dorkgen"7"github.com/sundowndev/dorkgen/googlesearch"8"github.com/sundowndev/phoneinfoga/v2/lib/number"9"google.golang.org/api/customsearch/v1"10"google.golang.org/api/googleapi"11"google.golang.org/api/option"12"net/http"13"os"14"strconv"15)1617const GoogleCSE = "googlecse"1819type googleCSEScanner struct {20MaxResults int6421httpClient *http.Client22}2324type ResultItem struct {25Title string `json:"title,omitempty" console:"Title,omitempty"`26URL string `json:"url,omitempty" console:"URL,omitempty"`27}2829type GoogleCSEScannerResponse struct {30Homepage string `json:"homepage,omitempty" console:"Homepage,omitempty"`31ResultCount int `json:"result_count" console:"Results shown"`32TotalResultCount int `json:"total_result_count" console:"Total number of results"`33TotalRequestCount int `json:"total_request_count" console:"Requests made"`34Items []ResultItem `json:"items,omitempty" console:"Items,omitempty"`35}3637func NewGoogleCSEScanner(HTTPclient *http.Client) Scanner {38// CSE limits you to 10 pages of results with max 10 results per page39// We only fetch the first page of results by default for each request40maxResults := 1041if v := os.Getenv("GOOGLECSE_MAX_RESULTS"); v != "" {42val, err := strconv.Atoi(v)43if err == nil {44if val > 100 {45val = 10046}47maxResults = val48}49}5051return &googleCSEScanner{52MaxResults: int64(maxResults),53httpClient: HTTPclient,54}55}5657func (s *googleCSEScanner) Name() string {58return GoogleCSE59}6061func (s *googleCSEScanner) Description() string {62return "Googlecse searches for footprints of a given phone number on the web using Google Custom Search Engine."63}6465func (s *googleCSEScanner) DryRun(_ number.Number, opts ScannerOptions) error {66if opts.GetStringEnv("GOOGLECSE_CX") == "" || opts.GetStringEnv("GOOGLE_API_KEY") == "" {67return errors.New("search engine ID and/or API key is not defined")68}69return nil70}7172func (s *googleCSEScanner) Run(n number.Number, opts ScannerOptions) (interface{}, error) {73var allItems []*customsearch.Result74var dorks []*GoogleSearchDork75var totalResultCount int76var totalRequestCount int77var cx = opts.GetStringEnv("GOOGLECSE_CX")78var apikey = opts.GetStringEnv("GOOGLE_API_KEY")7980dorks = append(dorks, s.generateDorkQueries(n)...)8182customsearchService, err := customsearch.NewService(83context.Background(),84option.WithAPIKey(apikey),85option.WithHTTPClient(s.httpClient),86)87if err != nil {88return nil, err89}9091for _, req := range dorks {92n, items, err := s.search(customsearchService, req.Dork, cx)93if err != nil {94if s.isRateLimit(err) {95return nil, errors.New("rate limit exceeded, see https://developers.google.com/custom-search/v1/overview#pricing")96}97return nil, err98}99allItems = append(allItems, items...)100totalResultCount += n101totalRequestCount++102}103104var data GoogleCSEScannerResponse105for _, item := range allItems {106data.Items = append(data.Items, ResultItem{107Title: item.Title,108URL: item.Link,109})110}111data.Homepage = fmt.Sprintf("https://cse.google.com/cse?cx=%s", cx)112data.ResultCount = len(allItems)113data.TotalResultCount = totalResultCount114data.TotalRequestCount = totalRequestCount115116return data, nil117}118119func (s *googleCSEScanner) search(service *customsearch.Service, q string, cx string) (int, []*customsearch.Result, error) {120var results []*customsearch.Result121var totalResultCount int122123offset := int64(0)124for offset < s.MaxResults {125search := service.Cse.List()126search.Cx(cx)127search.Q(q)128search.Start(offset)129searchQuery, err := search.Do()130if err != nil {131return 0, nil, err132}133results = append(results, searchQuery.Items...)134totalResultCount, err = strconv.Atoi(searchQuery.SearchInformation.TotalResults)135if err != nil {136return 0, nil, err137}138if totalResultCount <= int(s.MaxResults) {139break140}141offset += int64(len(searchQuery.Items))142}143144return totalResultCount, results, nil145}146147func (s *googleCSEScanner) isRateLimit(theError error) bool {148if theError == nil {149return false150}151var err *googleapi.Error152if !errors.As(theError, &err) {153return false154}155if theError.(*googleapi.Error).Code != 429 {156return false157}158return true159}160161func (s *googleCSEScanner) generateDorkQueries(number number.Number) (results []*GoogleSearchDork) {162var dorks = []*googlesearch.GoogleSearch{163dorkgen.NewGoogleSearch().164InText(number.International).165Or().166InText(number.E164).167Or().168InText(number.RawLocal).169Or().170InText(number.Local),171dorkgen.NewGoogleSearch().172Group(dorkgen.NewGoogleSearch().173Ext("doc").174Or().175Ext("docx").176Or().177Ext("odt").178Or().179Ext("pdf").180Or().181Ext("rtf").182Or().183Ext("sxw").184Or().185Ext("psw").186Or().187Ext("ppt").188Or().189Ext("pptx").190Or().191Ext("pps").192Or().193Ext("csv").194Or().195Ext("txt").196Or().197Ext("xls")).198InText(number.International).199Or().200InText(number.E164).201Or().202InText(number.RawLocal).203Or().204InText(number.Local),205}206207for _, dork := range dorks {208results = append(results, &GoogleSearchDork{209Number: number.E164,210Dork: dork.String(),211URL: dork.URL(),212})213}214215return results216}217218219