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}