Init gravatar recon

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2025-09-25 19:47:06 +02:00
parent a6383c9de3
commit 8729b0c779
15 changed files with 359 additions and 103 deletions

View File

@@ -4,6 +4,7 @@ go 1.25.0
require (
github.com/anotherhadi/github-recon v1.5.6
github.com/anotherhadi/gravatar-recon v1.0.1
github.com/charmbracelet/log v0.4.2
github.com/gin-gonic/gin v1.10.1
github.com/marcboeker/go-duckdb v1.8.5
@@ -49,7 +50,7 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/saran13raj/go-pixels v0.0.0-20250629121333-58b240a3ae51 // indirect
github.com/spf13/pflag v1.0.7 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect

View File

@@ -2,6 +2,8 @@ github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7X
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/anotherhadi/github-recon v1.5.6 h1:IN3lQZRqqNbPpSyP5fvNoJrYODbM2tNwS5tiRgD+i1s=
github.com/anotherhadi/github-recon v1.5.6/go.mod h1:E2tmCmjEZdJeBx8u1J8sSMtnmU8aDQ6IjCoq3ykoHtY=
github.com/anotherhadi/gravatar-recon v1.0.1 h1:Js3NCrVXhJb/ShG6PMzma1bsER0lQi9qbFHK1uABMm4=
github.com/anotherhadi/gravatar-recon v1.0.1/go.mod h1:cMP1mqW5vxwRCIZDbQGr0gb/SzbAveM2EcdJ/BwXQN8=
github.com/apache/arrow-go/v18 v18.1.0 h1:agLwJUiVuwXZdwPYVrlITfx7bndULJ/dggbnLFgDp/Y=
github.com/apache/arrow-go/v18 v18.1.0/go.mod h1:tigU/sIgKNXaesf5d7Y95jBBKS5KsxTqYBKXFsvKzo0=
github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE=
@@ -115,8 +117,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/saran13raj/go-pixels v0.0.0-20250629121333-58b240a3ae51 h1:H/XUfYcLxI3CBmDlgBpnOeTntRgqWvIoUXnqhCF5a0s=
github.com/saran13raj/go-pixels v0.0.0-20250629121333-58b240a3ae51/go.mod h1:sqhdZVLvqzTEBtmZBuTnFDUW0Lsryw2X2/wrLgqLEYg=
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

View File

@@ -15,13 +15,14 @@ type LeakResult struct {
Duration time.Duration
Rows []map[string]string
Error string
Inactive bool
LimitHit bool // Whether the search hit the limit
}
func Search(s *server.Server, queryText, column string, exactMatch bool) LeakResult {
if len(*(s.Dataleaks)) == 0 {
return LeakResult{
Error: "No dataleak configured",
Inactive: true,
}
}
now := time.Now()

View File

@@ -14,15 +14,13 @@ import (
type GithubResult struct {
Duration time.Duration
Error string
Inactive bool
UsernameResult *recon_username.UsernameResult
EmailResult *recon_email.EmailResult
}
func Search(s *server.Server, queryText, column string) *GithubResult {
if !s.Settings.GithubRecon {
return nil
}
func GithubSearch(s *server.Server, queryText, queryType string) GithubResult {
gr := GithubResult{}
now := time.Now()
settings := github_recon_settings.GetDefaultSettings()
@@ -35,56 +33,27 @@ func Search(s *server.Server, queryText, column string) *GithubResult {
queryText = strings.TrimSpace(queryText)
if column == "email" || strings.HasSuffix(column, "_email") ||
column == "username" || strings.HasSuffix(column, "_username") ||
column == "" || column == "all" {
if isValidEmail(queryText) {
settings.Target = queryText
settings.TargetType = github_recon_settings.TargetEmail
result := recon_email.Email(settings)
gr.EmailResult = &result
} else if isValidUsername(queryText) {
settings.Target = queryText
settings.TargetType = github_recon_settings.TargetUsername
result, err := recon_username.Username(settings)
if err != nil {
gr.Error = err.Error()
}
if result.User.Username == "" {
gr.UsernameResult = nil
} else {
gr.UsernameResult = &result
}
if queryType == "email" {
settings.Target = queryText
settings.TargetType = github_recon_settings.TargetEmail
result := recon_email.Email(settings)
gr.EmailResult = &result
} else if queryType == "username" {
settings.Target = queryText
settings.TargetType = github_recon_settings.TargetUsername
result, err := recon_username.Username(settings)
if err != nil {
gr.Error = err.Error()
}
if result.User.Username == "" {
gr.UsernameResult = nil
} else {
return nil
gr.UsernameResult = &result
}
} else {
return nil
return GithubResult{Inactive: true}
}
gr.Duration = time.Since(now)
return &gr
}
func isValidEmail(email string) bool {
if !strings.Contains(email, "@") || !strings.Contains(email, ".") {
return false
}
if strings.HasPrefix(email, "@") || strings.HasSuffix(email, "@") {
return false
}
if strings.Contains(email, " ") {
return false
}
return true
}
func isValidUsername(username string) bool {
if len(username) < 1 || len(username) > 39 {
return false
}
if strings.Contains(username, " ") {
return false
}
return true
return gr
}

View File

@@ -0,0 +1,32 @@
package osint
import (
"time"
"github.com/anotherhadi/eleakxir/backend/server"
gravatar_recon "github.com/anotherhadi/gravatar-recon"
)
type GravatarResult struct {
Duration time.Duration
Error string
Inactive bool
Results []gravatar_recon.GravatarProfile
}
func GravatarSearch(s *server.Server, queryText string) GravatarResult {
gr := GravatarResult{}
now := time.Now()
results, err := gravatar_recon.GetGravatarProfiles(queryText)
if err != nil {
gr.Error = err.Error()
return gr
}
gr.Results = *results
gr.Duration = time.Since(now)
return gr
}

View File

@@ -19,8 +19,9 @@ type Query struct {
ExactMatch bool // Whether to search for an exact match
// Services
Datawells bool // Whether to include datawells in the search
GithubRecon bool // Whether to include github-recon in the search
Datawells bool // Whether to include datawells in the search
GithubRecon bool // Whether to include github-recon in the search
GravatarRecon bool // Whether to include gravatar-recon in the search
}
type Result struct {
@@ -29,27 +30,25 @@ type Result struct {
Status string // "pending", "completed"
Query Query
LeakResult dataleak.LeakResult
GithubResult osint.GithubResult
LeakResult dataleak.LeakResult
GithubResult osint.GithubResult
GravatarResult osint.GravatarResult
}
func Search(s *server.Server, q Query, r *Result, mu *sync.RWMutex) {
var wg sync.WaitGroup
cleanQueryText := strings.TrimPrefix(q.Text, "^")
cleanQueryText = strings.TrimSuffix(q.Text, "$")
mu.Lock()
r.Date = time.Now()
r.Status = "pending"
r.Query = q
mu.Unlock()
wg.Add(2)
wg.Add(3)
go func() {
if !q.Datawells {
mu.Lock()
r.LeakResult = dataleak.LeakResult{Error: "not enabled"}
r.LeakResult = dataleak.LeakResult{Inactive: true}
mu.Unlock()
wg.Done()
return
@@ -61,21 +60,52 @@ func Search(s *server.Server, q Query, r *Result, mu *sync.RWMutex) {
wg.Done()
}()
cleanQueryText := strings.TrimPrefix(q.Text, "^")
cleanQueryText = strings.TrimSuffix(q.Text, "$")
isEmail := false
isUsername := false
if q.Column == "email" || strings.HasSuffix(q.Column, "_email") ||
q.Column == "username" || strings.HasSuffix(q.Column, "_username") ||
q.Column == "" || q.Column == "all" {
if isValidEmail(cleanQueryText) {
isEmail = true
} else if isValidUsername(cleanQueryText) {
isUsername = true
}
}
go func() {
if !q.GithubRecon {
if !q.GithubRecon || !s.Settings.GithubRecon || (!isEmail && !isUsername) {
mu.Lock()
r.GithubResult = osint.GithubResult{Error: "not enabled"}
r.GithubResult = osint.GithubResult{Inactive: true}
mu.Unlock()
wg.Done()
return
}
githubResult := osint.Search(s, cleanQueryText, q.Column)
mu.Lock()
if githubResult == nil {
r.GithubResult = osint.GithubResult{}
} else {
r.GithubResult = *githubResult
var githubResult osint.GithubResult
if isEmail {
githubResult = osint.GithubSearch(s, cleanQueryText, "email")
} else if isUsername {
githubResult = osint.GithubSearch(s, cleanQueryText, "username")
}
mu.Lock()
r.GithubResult = githubResult
mu.Unlock()
wg.Done()
}()
go func() {
if !q.GravatarRecon || !s.Settings.GravatarRecon || !isEmail {
mu.Lock()
r.GravatarResult = osint.GravatarResult{Inactive: true}
mu.Unlock()
wg.Done()
return
}
gravatarResult := osint.GravatarSearch(s, cleanQueryText)
mu.Lock()
r.GravatarResult = gravatarResult
mu.Unlock()
wg.Done()
}()
@@ -91,3 +121,26 @@ func EncodeQueryID(q Query, dataleaksCount uint64) string {
raw, _ := json.Marshal(q)
return fmt.Sprintf("%d:%s", dataleaksCount, base64.URLEncoding.EncodeToString(raw))
}
func isValidEmail(email string) bool {
if !strings.Contains(email, "@") || !strings.Contains(email, ".") {
return false
}
if strings.HasPrefix(email, "@") || strings.HasSuffix(email, "@") {
return false
}
if strings.Contains(email, " ") {
return false
}
return true
}
func isValidUsername(username string) bool {
if len(username) < 1 || len(username) > 39 {
return false
}
if strings.Contains(username, " ") {
return false
}
return true
}

View File

@@ -28,6 +28,7 @@ type ServerSettings struct {
GithubToken string `json:"-"` // Github token for github-recon
GithubTokenLoaded bool
GithubDeepMode bool // Deep mode for github-recon
GravatarRecon bool // Activate gravatar-recon OSINT tool
}
func LoadServerSettings() ServerSettings {
@@ -49,6 +50,7 @@ func LoadServerSettings() ServerSettings {
GithubRecon: getEnvBoolOrDefault("GITHUB_RECON", true),
GithubToken: getEnvStringOrDefault("GITHUB_TOKEN", "null"),
GithubDeepMode: getEnvBoolOrDefault("GITHUB_DEEP_MODE", false),
GravatarRecon: getEnvBoolOrDefault("GRAVATAR_RECON", true),
}
if ss.GithubToken == "null" || strings.TrimSpace(ss.GithubToken) == "" {