master
  1package get
  2
  3import (
  4	"fmt"
  5	"io"
  6	"net/http"
  7	"os"
  8	"sort"
  9	"strings"
 10
 11	"github.com/kahnwong/swissknife/configs/color"
 12	"github.com/kahnwong/swissknife/internal/utils"
 13	wappalyzer "github.com/projectdiscovery/wappalyzergo"
 14)
 15
 16// categoryMapping maps wappalyzer category names to our display categories
 17var categoryMapping = map[string]string{
 18	"CMS":                             "CMS",
 19	"Ecommerce":                       "Ecommerce",
 20	"Analytics":                       "Analytics",
 21	"Blogs":                           "CMS",
 22	"JavaScript frameworks":           "JavaScript Frameworks",
 23	"Web frameworks":                  "Web Frameworks",
 24	"Web servers":                     "Web Servers",
 25	"CDN":                             "CDN",
 26	"Caching":                         "Caching",
 27	"Programming languages":           "Programming Languages",
 28	"Operating systems":               "Operating Systems",
 29	"Databases":                       "Databases",
 30	"Message boards":                  "CMS",
 31	"Payment processors":              "Payment",
 32	"Security":                        "Security",
 33	"Tag managers":                    "Analytics",
 34	"Marketing automation":            "Marketing",
 35	"Advertising":                     "Marketing",
 36	"SEO":                             "Marketing",
 37	"Live chat":                       "Communication",
 38	"Font scripts":                    "UI",
 39	"Mobile frameworks":               "Mobile",
 40	"PaaS":                            "Cloud",
 41	"IaaS":                            "Cloud",
 42	"Reverse proxies":                 "Infrastructure",
 43	"Load balancers":                  "Infrastructure",
 44	"Web server extensions":           "Web Servers",
 45	"JavaScript libraries":            "JavaScript Frameworks",
 46	"UI frameworks":                   "UI",
 47	"Hosting panels":                  "Hosting",
 48	"Comment systems":                 "Communication",
 49	"Widgets":                         "UI",
 50	"Video players":                   "Media",
 51	"Maps":                            "Maps",
 52	"Remote access":                   "Infrastructure",
 53	"Network devices":                 "Infrastructure",
 54	"Control systems":                 "Infrastructure",
 55	"Static site generator":           "Static Site Generator",
 56	"Development":                     "Development",
 57	"CI":                              "Development",
 58	"Page builders":                   "CMS",
 59	"Wikis":                           "CMS",
 60	"Documentation":                   "Documentation",
 61	"Issue trackers":                  "Development",
 62	"Photo galleries":                 "Media",
 63	"LMS":                             "Education",
 64	"Rich text editors":               "UI",
 65	"Editors":                         "UI",
 66	"Search engines":                  "Search",
 67	"Feed readers":                    "Content",
 68	"DMS":                             "Content",
 69	"Webmail":                         "Communication",
 70	"CRM":                             "Business",
 71	"Accounting":                      "Business",
 72	"User onboarding":                 "UI",
 73	"A/B Testing":                     "Marketing",
 74	"Accessibility":                   "Accessibility",
 75	"Authentication":                  "Security",
 76	"Build/Task runners":              "Development",
 77	"Containers":                      "Infrastructure",
 78	"Cookie compliance":               "Legal",
 79	"Cryptominers":                    "Cryptocurrency",
 80	"Database managers":               "Databases",
 81	"Date & time pickers":             "UI",
 82	"DevOps":                          "Development",
 83	"Error logging":                   "Monitoring",
 84	"Feature management":              "Development",
 85	"Geolocation":                     "Location",
 86	"GraphQL":                         "API",
 87	"Headless CMS":                    "CMS",
 88	"Media servers":                   "Media",
 89	"Miscellaneous":                   "Miscellaneous",
 90	"Monitoring":                      "Monitoring",
 91	"Network storage":                 "Storage",
 92	"Performance":                     "Performance",
 93	"Personalisation":                 "Marketing",
 94	"Privacy":                         "Privacy",
 95	"Proxies":                         "Infrastructure",
 96	"RUM":                             "Performance",
 97	"Retargeting":                     "Marketing",
 98	"SSL/TLS certificate authorities": "Security",
 99	"Search engine crawlers":          "SEO",
100	"Server-side rendering":           "Web Frameworks",
101	"Webcams":                         "Media",
102}
103
104func GetSiteInfo(args []string) error {
105	// set URL
106	url := utils.SetURL(args)
107	fmt.Println(url)
108
109	// fetch site
110	resp, err := http.DefaultClient.Get(url)
111	if err != nil {
112		return fmt.Errorf("failed to fetch URL %s: %w", url, err)
113	}
114	defer func(Body io.ReadCloser) {
115		if err := Body.Close(); err != nil {
116			fmt.Fprintf(os.Stderr, "warning: failed to close response body: %v\n", err)
117		}
118	}(resp.Body)
119
120	data, err := io.ReadAll(resp.Body)
121	if err != nil {
122		return fmt.Errorf("failed to read response body: %w", err)
123	}
124
125	// init wappalyzer
126	wappalyzerClient, err := wappalyzer.New()
127	if err != nil {
128		return fmt.Errorf("failed to initialize wappalyzer: %w", err)
129	}
130
131	appsInfo := wappalyzerClient.FingerprintWithInfo(resp.Header, data)
132
133	// categorize tech info
134	categoryTechs := make(map[string][]string)
135	for appName, info := range appsInfo {
136		cleanName := strings.Split(appName, ":")[0]
137
138		for _, wapCat := range info.Categories {
139			displayCat := categoryMapping[wapCat]
140			if displayCat == "" {
141				displayCat = wapCat // Use original if no mapping exists
142			}
143
144			techs := categoryTechs[displayCat]
145			found := false
146			for _, t := range techs {
147				if t == cleanName {
148					found = true
149					break
150				}
151			}
152			if !found {
153				categoryTechs[displayCat] = append(techs, cleanName)
154			}
155		}
156	}
157
158	if len(categoryTechs) == 0 {
159		fmt.Println("No technologies detected")
160		return nil
161	}
162
163	categories := make([]string, 0, len(categoryTechs))
164	for cat := range categoryTechs {
165		categories = append(categories, cat)
166	}
167	sort.Strings(categories)
168
169	// display technologies
170	for _, category := range categories {
171		techs := categoryTechs[category]
172		sort.Strings(techs)
173
174		fmt.Printf("%s:\n", color.Green(category))
175		for _, tech := range techs {
176			fmt.Printf("- %s\n", tech)
177		}
178	}
179	return nil
180}