diff --git a/back/search/dataleak/dataleak.go b/back/search/dataleak/dataleak.go
index d48212c..c1469d8 100644
--- a/back/search/dataleak/dataleak.go
+++ b/back/search/dataleak/dataleak.go
@@ -117,40 +117,63 @@ func removeDuplicateMaps(maps []map[string]string) []map[string]string {
}
func buildSqlQuery(s *server.Server, queryText, column string, exactMatch bool) string {
- limit := strconv.Itoa(s.Settings.Limit)
- from := getFromClause(s)
- if column == "name" {
+ // Normalize "name" -> "full_name"
+ if strings.EqualFold(column, "name") {
column = "full_name"
}
- columns := []string{column}
+
+ // Step 1: Determine candidate columns to search
+ var candidateColumns []string
if column == "all" || column == "" {
- columns = s.Settings.BaseColumns
+ // Use base columns if "all" or empty
+ candidateColumns = s.Settings.BaseColumns
+ } else {
+ // Otherwise, only search the given column
+ candidateColumns = []string{column}
}
- columnsFiltered := []string{}
- allColumns := []string{}
- // TODO: Add columns that ends with _col aswell
+
+ // Step 2: Collect all available columns across dataleaks
+ allColumns := make([]string, 0)
+ seen := make(map[string]struct{})
for _, dataleak := range *s.Dataleaks {
for _, col := range dataleak.Columns {
- if !slices.Contains(allColumns, col) {
+ if _, ok := seen[col]; !ok {
+ seen[col] = struct{}{}
allColumns = append(allColumns, col)
}
}
}
- if column == "full_text" {
+
+ // Step 3: Resolve which columns should actually be used in the WHERE clause
+ var columnsFiltered []string
+ if strings.EqualFold(column, "full_text") {
+ // "full_text" means search across all columns
columnsFiltered = allColumns
} else {
- for _, col := range columns {
- if slices.Contains(allColumns, col) {
- columnsFiltered = append(columnsFiltered, col)
+ for _, candidate := range candidateColumns {
+ for _, available := range allColumns {
+ // Exact match (case-insensitive)
+ if strings.EqualFold(available, candidate) {
+ columnsFiltered = append(columnsFiltered, available)
+ continue
+ }
+ // Match columns ending with "_"
+ if strings.HasSuffix(strings.ToLower(available), "_"+strings.ToLower(candidate)) {
+ columnsFiltered = append(columnsFiltered, available)
+ }
}
}
}
+ limit := strconv.Itoa(s.Settings.Limit)
+ from := getFromClause(s)
+
if len(columnsFiltered) == 0 {
return fmt.Sprintf("SELECT * FROM %s LIMIT %s", from, limit)
}
where := getWhereClause(queryText, columnsFiltered, exactMatch)
+
return fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT %s", from, where, limit)
}
@@ -162,8 +185,19 @@ func getWhereClause(queryText string, columns []string, exactMatch bool) string
var orClausesForTerm []string
termEscaped := strings.ReplaceAll(term, "'", "''")
+ startsWith := false
+ endsWith := false
+ if strings.HasPrefix(termEscaped, "^") {
+ startsWith = true
+ termEscaped = strings.TrimPrefix(termEscaped, "^")
+ }
+ if strings.HasSuffix(termEscaped, "$") {
+ endsWith = true
+ termEscaped = strings.TrimSuffix(termEscaped, "$")
+ }
+
for _, col := range columns {
- if exactMatch {
+ if exactMatch || (startsWith && endsWith) {
termEscapedILike := strings.ReplaceAll(termEscaped, "_", "\\_")
termEscapedILike = strings.ReplaceAll(termEscapedILike, "%", "\\%")
orClausesForTerm = append(orClausesForTerm, fmt.Sprintf("\"%s\" ILIKE '%s' ESCAPE '\\'", col, strings.ToLower(termEscapedILike)))
@@ -171,7 +205,13 @@ func getWhereClause(queryText string, columns []string, exactMatch bool) string
// Escape characters for ILIKE
termEscapedILike := strings.ReplaceAll(termEscaped, "_", "\\_")
termEscapedILike = strings.ReplaceAll(termEscapedILike, "%", "\\%")
- orClausesForTerm = append(orClausesForTerm, fmt.Sprintf("\"%s\" ILIKE '%%%s%%' ESCAPE '\\'", col, strings.ToLower(termEscapedILike)))
+ if startsWith {
+ orClausesForTerm = append(orClausesForTerm, fmt.Sprintf("\"%s\" ILIKE '%s%%' ESCAPE '\\'", col, strings.ToLower(termEscapedILike)))
+ } else if endsWith {
+ orClausesForTerm = append(orClausesForTerm, fmt.Sprintf("\"%s\" ILIKE '%%%s' ESCAPE '\\'", col, strings.ToLower(termEscapedILike)))
+ } else {
+ orClausesForTerm = append(orClausesForTerm, fmt.Sprintf("\"%s\" ILIKE '%%%s%%' ESCAPE '\\'", col, strings.ToLower(termEscapedILike)))
+ }
}
}
andClauses = append(andClauses, "("+strings.Join(orClausesForTerm, " OR ")+")")
diff --git a/back/search/search.go b/back/search/search.go
index 31605cb..a03e146 100644
--- a/back/search/search.go
+++ b/back/search/search.go
@@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
+ "strings"
"sync"
"time"
@@ -35,6 +36,9 @@ type Result struct {
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"
@@ -65,9 +69,13 @@ func Search(s *server.Server, q Query, r *Result, mu *sync.RWMutex) {
wg.Done()
return
}
- githubResult := osint.Search(s, q.Text, q.Column)
+ githubResult := osint.Search(s, cleanQueryText, q.Column)
mu.Lock()
- r.GithubResult = *githubResult
+ if githubResult == nil {
+ r.GithubResult = osint.GithubResult{}
+ } else {
+ r.GithubResult = *githubResult
+ }
mu.Unlock()
wg.Done()
}()
diff --git a/front/src/lib/components/index/search/howToSearch.svelte b/front/src/lib/components/index/search/howToSearch.svelte
index 5c77e35..8852191 100644
--- a/front/src/lib/components/index/search/howToSearch.svelte
+++ b/front/src/lib/components/index/search/howToSearch.svelte
@@ -34,7 +34,9 @@
Standard Search: By default,
Eleakxir uses a "fuzzy" search. This means it will find results where your search
terms are part of a larger string. For example, searching for 1234 would find
- john.doe@1234.com.
+ john.doe@1234.com. Multiple terms are combined with AND, so all terms
+ must be present, while multiple columns are combined with OR, so a match
+ in any column counts.
@@ -43,4 +45,13 @@
is an exact match for your search term. This is useful for finding specific,
unique values.
+
+
+ Starts With or Ends With:
+ You can refine your search by using ^ at the beginning of a term
+ to match only values that start with that term, or $ at the
+ end of a term to match only values that end with that term. For example,
+ ^admin will find entries starting with "admin", while
+ user$ will find entries ending with "user".
+
diff --git a/front/src/routes/search/[id]/index.svelte b/front/src/routes/search/[id]/index.svelte
index 11dc40c..96b4c5e 100644
--- a/front/src/routes/search/[id]/index.svelte
+++ b/front/src/routes/search/[id]/index.svelte
@@ -41,7 +41,6 @@
})
.then((r) => {
result = r.data;
- console.log(r.data);
if (result && result.Status !== "pending") {
clearInterval(intervalId);
}
@@ -120,7 +119,7 @@
- {#if result.LeakResult.Error !== "not enabled" }
+ {#if result.LeakResult.Error !== "not enabled"}