|
// SPDX-License-Identifier: MIT |
|
// SPDX-FileCopyrightText: 2025 Sébastien L. |
|
// SPDX-FilePurpose: POC |
|
// SPDX-FileComment: Programme à usage démonstratif |
|
// Version: 1.0.0 |
|
// Date: 2025-06-14 |
|
// |
|
// slack-look-up.go — Vérifie l'existence de workspaces Slack en générant |
|
// et testant des sous-domaines potentiels (HTTP HEAD). |
|
// |
|
// Usage : |
|
// go run slack-look-up.go -help |
|
// go run slack-look-up.go -quiet |
|
// go run slack-look-up.go -quiet -workers 2 -delay 1s |
|
// Build : |
|
// go build -o slack-look-up slack-look-up.go |
|
|
|
package main |
|
|
|
import ( |
|
"bufio" |
|
"flag" |
|
"fmt" |
|
"net/http" |
|
"os" |
|
"strings" |
|
"sync" |
|
"time" |
|
) |
|
|
|
// Config contient les paramètres de configuration pour l'exécution du script |
|
// comme les combinaisons de noms, les délais, la sortie de log, etc. |
|
type Config struct { |
|
Prefixes []string |
|
Fixes []string |
|
Suffixes []string |
|
LogFile string |
|
Quiet bool |
|
Delay time.Duration |
|
Timeout time.Duration |
|
Workers int |
|
} |
|
|
|
// cfg contient la configuration par défaut utilisée pour l'exécution... |
|
var cfg = Config{ |
|
Prefixes: []string{"itmanager", "itmanagers", "it-manager", "it-managers"}, |
|
Fixes: []string{"stepup", "step-up", "scaleup", "startup", "team", "group", "lead", "leaders"}, |
|
Suffixes: []string{"fr"}, |
|
LogFile: "/tmp/slack_check.log", |
|
Delay: 2 * time.Second, |
|
Timeout: 10 * time.Second, |
|
Workers: 4, |
|
} |
|
|
|
// init configure les flags en ligne de commande pour le script. |
|
// --quiet : masque les sorties des tests en cours. |
|
// --delay : définit le délai entre les requêtes (ex: 2s, 500ms…). |
|
// --workers : nombre de goroutines concurrentes pour les vérifications. |
|
// Les valeurs sont liées directement au champ correspondant de la struct Config. |
|
func init() { |
|
flag.BoolVar(&cfg.Quiet, "quiet", false, "Ne pas afficher les tests en cours") |
|
flag.DurationVar(&cfg.Delay, "delay", cfg.Delay, "Temps entre chaque requête") |
|
flag.IntVar(&cfg.Workers, "workers", cfg.Workers, "Nombre de workers parallèles") |
|
flag.Parse() |
|
} |
|
|
|
// generateDomains crée toutes les combinaisons possibles de sous-domaines Slack |
|
// en combinant préfixes, fixes et suffixes selon la logique souhaitée. |
|
func generateDomains(c Config) []string { |
|
domains := make([]string, 0) |
|
for _, p := range c.Prefixes { |
|
domains = append(domains, p) |
|
for _, f := range c.Fixes { |
|
domains = append(domains, p+f) |
|
for _, s := range c.Suffixes { |
|
domains = append(domains, p+f+s) |
|
domains = append(domains, fmt.Sprintf("%s-%s-%s", p, f, s)) |
|
} |
|
} |
|
} |
|
return domains |
|
} |
|
|
|
// checkURL effectue une requête HEAD HTTP sur l'URL donnée avec un timeout défini. |
|
// Retourne le code HTTP reçu ou une erreur si la requête échoue. |
|
func checkURL(url string, timeout time.Duration) (int, error) { |
|
client := http.Client{Timeout: timeout} |
|
resp, err := client.Head(url) |
|
if err != nil { |
|
return 0, err |
|
} |
|
defer resp.Body.Close() |
|
return resp.StatusCode, nil |
|
} |
|
|
|
// writeLog ajoute une ligne de texte au fichier de log spécifié. |
|
// - Crée le fichier s'il n'existe pas, ou l'ouvre en mode ajout. |
|
// - Écrit la ligne suivie d’un saut de ligne. |
|
// - Gère les erreurs d'ouverture avec un message sur stderr. |
|
func writeLog(path, line string) { |
|
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) |
|
if err != nil { |
|
fmt.Fprintf(os.Stderr, "Erreur d'écriture du log: %v\n", err) |
|
return |
|
} |
|
defer f.Close() |
|
f.WriteString(line + "\n") |
|
} |
|
|
|
// worker consomme des sous-domaines depuis un canal, les teste avec checkURL, |
|
// affiche le résultat formaté avec couleurs, et écrit dans le fichier de log. |
|
// Chaque worker attend une pause entre les requêtes pour éviter le flood. |
|
func worker(domains <-chan string, wg *sync.WaitGroup, cfg Config, mu *sync.Mutex) { |
|
defer wg.Done() |
|
for sub := range domains { |
|
url := fmt.Sprintf("https://%s.slack.com", sub) |
|
if !cfg.Quiet { |
|
fmt.Printf("Testing %s ...\n", url) |
|
} |
|
code, err := checkURL(url, cfg.Timeout) |
|
var result string |
|
if err != nil { |
|
result = fmt.Sprintf("[NO RESPONSE] %s", url) |
|
} else { |
|
switch code { |
|
case 200: |
|
result = fmt.Sprintf("\033[1;32m[FOUND: HTTP 200]\033[0m %s", url) |
|
case 301, 302, 303: |
|
result = fmt.Sprintf("\033[1;33m[REDIRECT: %d]\033[0m %s", code, url) |
|
case 403, 404: |
|
result = fmt.Sprintf("\033[1;31m[NOT FOUND: %d]\033[0m %s", code, url) |
|
default: |
|
result = fmt.Sprintf("\033[1;90m[UNKNOWN STATUS: %d]\033[0m %s", code, url) |
|
} |
|
} |
|
fmt.Println(result) |
|
mu.Lock() |
|
writeLog(cfg.LogFile, result) |
|
mu.Unlock() |
|
time.Sleep(cfg.Delay) |
|
} |
|
} |
|
|
|
// runChecks orchestre l'exécution parallèle des vérifications HTTP. |
|
// - Supprime le fichier de log s'il existe déjà. |
|
// - Génère la liste complète des sous-domaines à tester. |
|
// - Lance un pool de workers concurrents pour traiter les domaines via un canal. |
|
// - Attend la fin de tous les workers avant de terminer. |
|
func runChecks(cfg Config) { |
|
os.Remove(cfg.LogFile) |
|
domains := generateDomains(cfg) |
|
jobs := make(chan string, len(domains)) |
|
var wg sync.WaitGroup |
|
var mu sync.Mutex |
|
|
|
for i := 0; i < cfg.Workers; i++ { |
|
wg.Add(1) |
|
go worker(jobs, &wg, cfg, &mu) |
|
} |
|
for _, d := range domains { |
|
jobs <- d |
|
} |
|
close(jobs) |
|
wg.Wait() |
|
} |
|
|
|
// printSummary affiche la liste des sous-domaines avec code 200 (trouvés), |
|
// suivie d'un compteur total. Utilise des couleurs ANSI pour plus de lisibilité. |
|
func printSummary(path string) { |
|
fmt.Println("\n\033[1;32m=== SUMMARY: FOUND ===\033[0m") |
|
file, err := os.Open(path) |
|
if err != nil { |
|
fmt.Fprintf(os.Stderr, "Erreur lecture log: %v\n", err) |
|
return |
|
} |
|
defer file.Close() |
|
|
|
scanner := bufio.NewScanner(file) |
|
count := 0 |
|
for scanner.Scan() { |
|
line := scanner.Text() |
|
if strings.Contains(line, "[FOUND: HTTP 200]") { |
|
fmt.Printf("\033[1;32m%s\033[0m\n", line) |
|
count++ |
|
} |
|
} |
|
fmt.Printf("\033[1;32mTotal FOUND: %d\033[0m\n", count) |
|
} |
|
|
|
// Main |
|
func main() { |
|
runChecks(cfg) |
|
printSummary(cfg.LogFile) |
|
} |