diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..c41f69f
Binary files /dev/null and b/.DS_Store differ
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100755
index 0000000..a7cdac7
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/Sowhp.iml b/.idea/Sowhp.iml
new file mode 100755
index 0000000..338a266
--- /dev/null
+++ b/.idea/Sowhp.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100755
index 0000000..705d1fe
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..35f0bf3
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# Sowhp
diff --git a/Screenshot-of-website-homepage.txt b/Screenshot-of-website-homepage.txt
new file mode 100755
index 0000000..e69de29
diff --git a/concert/directoriescreat.go b/concert/directoriescreat.go
new file mode 100755
index 0000000..7563301
--- /dev/null
+++ b/concert/directoriescreat.go
@@ -0,0 +1,46 @@
+package concert
+
+import (
+ "Sowhp/concert/logger"
+ "fmt"
+ "os"
+)
+
+// MkdirResport 创建报告存放目录
+func MkdirResport() {
+ // 获取当前目录地址
+ dir, err := os.Getwd()
+ if err != nil {
+ // panic(err)
+ logger.DebugError(err)
+ }
+ logger.Debug(fmt.Sprintf("当前路径:%s", dir))
+ Dir_mk(dir + "/result/")
+ logger.Debug("当前目录下创建 /result/ 目录成功!")
+
+}
+
+// GetPath 获取当前路径位置
+func GetPath() string {
+ // 获取当前目录地址
+ dir, err := os.Getwd()
+ if err != nil {
+ // panic(err)
+ logger.DebugError(err)
+ }
+ return dir
+}
+
+// dir_mk 判断目录是否存在,若不存在则进行创建
+func Dir_mk(path string) {
+ // 判断目录是否存在
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ err = os.Mkdir(path, 0777)
+ if err != nil {
+ // panic(err)
+ logger.DebugError(err)
+ }
+ logger.Debug(fmt.Sprintf("当前以创建好该目录:%s", path))
+ return
+ }
+}
diff --git a/concert/logger/config.go b/concert/logger/config.go
new file mode 100644
index 0000000..dfea6e3
--- /dev/null
+++ b/concert/logger/config.go
@@ -0,0 +1,8 @@
+package logger
+
+var (
+ LogLevel int // log等级,默认设置3级
+ NoColor bool // 是否开启log输出非颜色版设置
+ OutputFileName string // 用于设置log输出名称设置
+ NoSave bool // not save file // logsync.go 中设置不进行日志写入的设置, 注:在常规的logger中并没有设置该参数
+)
diff --git a/concert/logger/level.go b/concert/logger/level.go
new file mode 100755
index 0000000..c7b4c1c
--- /dev/null
+++ b/concert/logger/level.go
@@ -0,0 +1,12 @@
+package logger
+
+type Level int
+
+const (
+ LevelFatal Level = iota // 值 0
+ LevelError // 值 1
+ LevelInfo // 值 2
+ LevelWarning // 值 3
+ LevelDebug // 值 4
+ LevelVerbose // 值 5
+)
diff --git a/concert/logger/log.go b/concert/logger/log.go
new file mode 100755
index 0000000..1055288
--- /dev/null
+++ b/concert/logger/log.go
@@ -0,0 +1,184 @@
+package logger
+
+import (
+ "fmt"
+ "github.com/gookit/color"
+ "os"
+ "path"
+ "regexp"
+ "runtime"
+ "strings"
+ "time"
+)
+
+var ( // 输出颜色设置
+ Red = color.Red.Render
+ Cyan = color.Cyan.Render
+ Yellow = color.Yellow.Render
+ White = color.White.Render
+ Blue = color.Blue.Render
+ Purple = color.Style{color.Magenta, color.OpBold}.Render
+ LightRed = color.Style{color.Red, color.OpBold}.Render
+ LightGreen = color.Style{color.Green, color.OpBold}.Render
+ LightWhite = color.Style{color.White, color.OpBold}.Render
+ LightCyan = color.Style{color.Cyan, color.OpBold}.Render
+ LightYellow = color.Style{color.Yellow, color.OpBold}.Render
+ //LightBlue = color.Style{color.Blue, color.OpBold}.Render
+)
+
+var (
+ defaultLevel = LevelWarning // 输出等级
+ noWrite int // 不进行写入log
+)
+
+// SetLevel 设置输出等级
+func SetLevel(l Level) {
+ defaultLevel = l
+}
+
+func getCallerInfo(skip int) (info string) {
+ _, file, lineNo, ok := runtime.Caller(skip)
+ if !ok {
+ info = "runtime.Caller() failed"
+ }
+
+ // funcName := runtime.FuncForPC(pc).Name()
+ fileName := path.Base(file) // Base函数返回路径的最后一个元素
+ // return fmt.Sprintf("FuncName:%s, file:%s, line:%d", funcName, fileName, lineNo)
+ return fmt.Sprintf("%s line:%d", fileName, lineNo)
+}
+
+// log 日志输出效验判断
+func log(l Level, w int, detail string) {
+ switch LogLevel { // 判断配置文件中的日志输出等级,并设置到日志等级
+ case 0:
+ SetLevel(0)
+ case 1:
+ SetLevel(1)
+ case 2:
+ SetLevel(2)
+ case 3:
+ SetLevel(3)
+ case 4:
+ SetLevel(4)
+ case 5:
+ SetLevel(5)
+ }
+
+ if l > defaultLevel { // 判断输入等级是否大于设置等级,若大于则当前日志则不进行输出
+ return
+ }
+ if NoColor { // 判断是否关闭颜色输出
+ fmt.Println(clean(detail))
+ return
+ } else {
+ fmt.Println(detail)
+ }
+
+ if noWrite == 0 {
+ // 目前只写入 info 信息 和 Success 的信息
+ strTrim := fmt.Sprintf("[%s] ", Cyan(getDate())) // 匹配log日志前面的日期
+ detail := strings.TrimPrefix(detail, strTrim) // 去除日期信息
+ writeLogFile(clean(detail), OutputFileName) // 写入到本地文件
+ }
+
+ if l == LevelFatal {
+ os.Exit(0)
+ }
+}
+
+// Fatal 严重级别日志 log等级:0
+func Fatal(detail string) {
+ noWrite = 1
+ log(LevelFatal, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("FATAL"), detail))
+}
+
+// Error 错误日志 log等级:1
+func Error(detail string) {
+ noWrite = 1
+ log(LevelError, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("ERROR"), detail))
+}
+
+// Info 消息日志 log等级:2
+func Info(detail string) {
+ noWrite = 0
+ log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("INFO"), detail))
+}
+
+// Warning 告警日志 log等级:3
+func Warning(detail string) {
+ noWrite = 1
+ log(LevelWarning, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightYellow("WARNING"), detail))
+}
+
+// Debug 调试日志 log等级:4
+func Debug(detail string) {
+ noWrite = 1
+ log(LevelDebug, noWrite, fmt.Sprintf("[%s] [%s] [%s] %s", Cyan(getDate()), LightWhite("DEBUG"), Yellow(getCallerInfo(2)), detail))
+}
+
+// Verbose 详细调试信息日志 log等级:5
+func Verbose(detail string) {
+ noWrite = 1
+ log(LevelVerbose, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightCyan("VERBOSE"), detail))
+}
+
+// Success 成功信息日志 log等级:2
+func Success(detail string) {
+ noWrite = 0
+ log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("+"), detail))
+}
+
+// Failed 失败信息日志 log等级:2
+func Failed(detail string) {
+ noWrite = 1
+ log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("-"), detail))
+}
+
+// Common 普通信息日志 log等级:2
+func Common(detail string) {
+ noWrite = 1
+ log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("*"), detail))
+}
+
+func getTime() string {
+ return time.Now().Format("15:04:05")
+}
+
+func getDate() string {
+ return time.Now().Format("2006.1.2")
+}
+
+func DebugError(err error) bool {
+ /* Processing error display */
+ if err != nil {
+ pc, _, line, _ := runtime.Caller(1)
+ Debug(fmt.Sprintf("%s%s%s",
+ White(runtime.FuncForPC(pc).Name()),
+ LightWhite(fmt.Sprintf(" line:%d ", line)),
+ White(err)))
+ return true
+ }
+ return false
+}
+
+// Clean by https://github.com/acarl005/stripansi/blob/master/stripansi.go
+func clean(str string) string {
+ const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
+ var re = regexp.MustCompile(ansi)
+ return re.ReplaceAllString(str, "")
+}
+
+func writeLogFile(result string, filename string) {
+ var text = []byte(result + "\n")
+ fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
+ if err != nil {
+ fmt.Printf("Open %s error, %v\n", filename, err)
+ return
+ }
+ _, err = fl.Write(text)
+ fl.Close()
+ if err != nil {
+ fmt.Printf("Write %s error, %v\n", filename, err)
+ }
+}
diff --git a/concert/logger/logsync.go b/concert/logger/logsync.go
new file mode 100755
index 0000000..f50b357
--- /dev/null
+++ b/concert/logger/logsync.go
@@ -0,0 +1,64 @@
+package logger
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "sync"
+ "time"
+)
+
+var Num int64
+var End int64
+var Results = make(chan *string)
+var Start = true
+var LogSucTime int64
+var LogErrTime int64
+var WaitTime int64
+var Silent bool
+var LogWG sync.WaitGroup
+
+func init() {
+ go SaveLog()
+}
+
+func LogSuccess(result string) {
+ LogWG.Add(1)
+ LogSucTime = time.Now().Unix()
+ Results <- &result
+}
+
+func SaveLog() {
+ for result := range Results {
+ if Silent == false || strings.Contains(*result, "[+]") || strings.Contains(*result, "[*]") {
+ fmt.Println(*result)
+ }
+ if !NoSave {
+ WriteFile(*result, OutputFileName)
+ }
+ LogWG.Done()
+ }
+}
+
+func WriteFile(result string, filename string) {
+ var text = []byte(result + "\n")
+ fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
+ if err != nil {
+ fmt.Printf("Open %s error, %v\n", filename, err)
+ return
+ }
+ _, err = fl.Write(text)
+ fl.Close()
+ if err != nil {
+ fmt.Printf("Write %s error, %v\n", filename, err)
+ }
+}
+
+func LogError(errinfo interface{}) {
+ if WaitTime == 0 {
+ fmt.Println(fmt.Sprintf(" %v/%v %v", End, Num, errinfo))
+ } else if (time.Now().Unix()-LogSucTime) > WaitTime && (time.Now().Unix()-LogErrTime) > WaitTime {
+ fmt.Println(fmt.Sprintf(" %v/%v %v", End, Num, errinfo))
+ LogErrTime = time.Now().Unix()
+ }
+}
diff --git a/core/working.go b/core/working.go
new file mode 100755
index 0000000..8a7ac1a
--- /dev/null
+++ b/core/working.go
@@ -0,0 +1,88 @@
+package core
+
+import (
+ dire "Sowhp/concert"
+ log "Sowhp/concert/logger"
+ "Sowhp/scripts"
+ "flag"
+ "fmt"
+ "os"
+)
+
+// 当前版本信息
+var version = "1.0.0"
+
+// logo
+var slogan = `
+
+ _____ _
+ / ____| | |
+| (___ _____ _| |__ _ __
+ \___ \ / _ \ \ /\ / / '_ \| '_ \
+ ____) | (_) \ V V /| | | | |_) |
+|_____/ \___/ \_/\_/ |_| |_| .__/
+ | |
+ |_|
+
+ Sowhp version: ` + version + `
+
+`
+
+var (
+ txtfilepath string
+ resultmap map[string]map[string][]string
+ arraymap map[string][]string
+)
+
+func Flag() {
+ flag.StringVar(&txtfilepath, "f", "", "URL文件路径地址,请参照格式输入, -f D://url.txt")
+ flag.IntVar(&log.LogLevel, "logl", 3, "设置日志输出等级,默认为3级,-logl 3")
+ print(slogan)
+ flag.Parse()
+}
+
+func WorkIng() {
+ Flag()
+ if isCharacterEmpty(txtfilepath) { // 判断输入的URL路径是否为空,若为空,则停止运行。
+ log.Debug(fmt.Sprintf("当前输入路径为:%s", txtfilepath))
+ run(txtfilepath)
+ } else {
+ flag.Usage()
+ os.Exit(0)
+ }
+
+}
+
+func isCharacterEmpty(char string) bool {
+ return len(char) != 0
+}
+
+func run(path string) {
+ log.Common("正在运行 Sowhp 网站首页截图工具")
+ urls := scripts.FindTextUrl(path) // 获取TXT里url列表
+ log.Debug(fmt.Sprint("以获取本地TXT文本里URL列表:", urls))
+ resultname := fmt.Sprintf("result_%s", scripts.GetTimeStrin())
+ log.Common("以获取本地URL列表信息数据,正在截图拍照中...")
+ arraymap = make(map[string][]string) // 初始化arraymap空间地址
+ resultmap = make(map[string]map[string][]string) // 初始化resultmap空间地址
+ // 格式参考样例 arraymap["Website URL Address"] = []string{"TItle Name", "status", "网站截图路径"}
+ dire.MkdirResport() // 创建默认结果目录
+ dire.Dir_mk(fmt.Sprintf("./result/%s", resultname)) // 创建本次扫描结果目录文件夹
+ dire.Dir_mk(fmt.Sprintf("./result/%s/%s", resultname, "data")) // 创建本次扫描图片存放目录
+ for _, url := range urls {
+ urlresultlist := scripts.ChromeScreenshot(url, resultname)
+
+ // 判断是否拍照成功,并根据拍照结果进行对应的结果填充
+ if len(urlresultlist) == 0 {
+ log.Common(fmt.Sprintf("访问 %s 地址超时,无法进行首页截图拍照!", url))
+ arraymap[url] = []string{"x_x!", "连接失败", "data/"}
+ } else {
+ log.Common(fmt.Sprintf("已完成对 %s 地址的首页截图拍照!", url))
+ arraymap[urlresultlist[0]] = []string{urlresultlist[1], urlresultlist[2], urlresultlist[3]}
+ }
+ }
+ resultmap[resultname] = arraymap
+ log.Common(fmt.Sprintf("已完成所有URL地址的截图拍照,共拍摄 %d 条", len(arraymap)))
+ log.Common("正在生成最终结果报告文件...")
+ scripts.CreateHtml(resultmap)
+}
diff --git a/go.mod b/go.mod
new file mode 100755
index 0000000..b29697f
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,21 @@
+module Sowhp
+
+go 1.20
+
+require (
+ github.com/chromedp/chromedp v0.9.1
+ github.com/gookit/color v1.5.4
+ github.com/klarkxy/gohtml v0.0.0-20220513090653-4beafb6047e9
+)
+
+require (
+ github.com/chromedp/cdproto v0.0.0-20230620000757-8605e5981815 // indirect
+ github.com/chromedp/sysutil v1.0.0 // indirect
+ github.com/gobwas/httphead v0.1.0 // indirect
+ github.com/gobwas/pool v0.2.1 // indirect
+ github.com/gobwas/ws v1.1.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
+ golang.org/x/sys v0.10.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100755
index 0000000..a0c4137
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,35 @@
+github.com/chromedp/cdproto v0.0.0-20230220211738-2b1ec77315c9/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
+github.com/chromedp/cdproto v0.0.0-20230620000757-8605e5981815 h1:o4k6aMs2G/cRe+DSAo3ui+Z8OKVQUcAOEM36BXSaX3M=
+github.com/chromedp/cdproto v0.0.0-20230620000757-8605e5981815/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
+github.com/chromedp/chromedp v0.9.1 h1:CC7cC5p1BeLiiS2gfNNPwp3OaUxtRMBjfiw3E3k6dFA=
+github.com/chromedp/chromedp v0.9.1/go.mod h1:DUgZWRvYoEfgi66CgZ/9Yv+psgi+Sksy5DTScENWjaQ=
+github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
+github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
+github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
+github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
+github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
+github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
+github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
+github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/klarkxy/gohtml v0.0.0-20220513090653-4beafb6047e9 h1:MMZHhkMQ+ODwwcH1cAB54YEnGp5A+Kfsx5SVO1r7UCo=
+github.com/klarkxy/gohtml v0.0.0-20220513090653-4beafb6047e9/go.mod h1:Azf8dj+M8gsc0MkXDXzZp0a4ovsnJkNCKKTdko5SWUY=
+github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
+github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
+github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
+github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
+golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
+golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/main.go b/main.go
new file mode 100755
index 0000000..7dc353b
--- /dev/null
+++ b/main.go
@@ -0,0 +1,7 @@
+package main
+
+import "Sowhp/core"
+
+func main() {
+ core.WorkIng()
+}
diff --git a/scripts/Reporting.go b/scripts/Reporting.go
new file mode 100755
index 0000000..9dfc85f
--- /dev/null
+++ b/scripts/Reporting.go
@@ -0,0 +1,75 @@
+package scripts
+
+import (
+ log "Sowhp/concert/logger"
+ "fmt"
+ "github.com/klarkxy/gohtml"
+ "os"
+)
+
+var resultname string
+
+func CreateHtml(m map[string]map[string][]string) {
+
+ for result_name := range m {
+ resultname = result_name // 获取html结果标题头
+ }
+
+ bootstrap := gohtml.NewHtml()
+ bootstrap.Html()
+ bootstrap.Head().Title().Text(resultname)
+ bootstrap.Meta().Charset("utf-8")
+ bootstrap1 := bootstrap.Body().Tag("div").Align("center").Id("content").Tag("table").Align("center").Border("1").Tag("tbody")
+ bootstrap1.Tr().Align("center").Td().Colspan("7").Text("url探测结果")
+ tr := bootstrap1.Tr().Align("center").Bgcolor("#0080FF").Attr("style", "color:white")
+ tr.Td().Text("URL详情")
+ tr.Td().Text("截图")
+
+ filetxt, err := os.OpenFile(fmt.Sprintf("./result/%s/%s.txt", resultname, resultname), os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.FileMode(0666))
+ if err != nil {
+ // log.Fatal(err)
+ log.DebugError(err)
+
+ }
+ _, err = filetxt.WriteString(fmt.Sprintf("%s %s %s %s \r\n", "Website URL Address", "TItle Name", "status", "Website screenshot path"))
+ if err != nil {
+ // log.Fatal(err)
+ log.DebugError(err)
+ }
+ defer filetxt.Close()
+
+ for urlippath := range m[resultname] {
+ titlename := m[resultname][urlippath][0]
+ statevalue := m[resultname][urlippath][1]
+ photopath := m[resultname][urlippath][2]
+ tr1 := bootstrap1.Tr().Align("center")
+ td := tr1.Td()
+ td.A().Href(urlippath).Target("_blank").Text(urlippath)
+ td.Text(fmt.Sprintf("%s %s", statevalue, titlename))
+ tr1.Td().Img().Src(photopath).Attr("style", "width:800px;hight:200px")
+ _, err = filetxt.WriteString(fmt.Sprintf("%s %s %s %s", urlippath, titlename, statevalue, photopath))
+ if err != nil {
+ // log.Fatal(err)
+ log.DebugError(err)
+ }
+ filetxt.WriteString("\r\n")
+ }
+
+ resultHtml := fmt.Sprintf("./result/%s/%s.html", resultname, resultname)
+ // 生成最终结果HTML展示情况
+ filehtml, err := os.OpenFile(resultHtml, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.FileMode(0666))
+ if err != nil {
+ // log.Fatal(err)
+ log.DebugError(err)
+ }
+ // 关闭文件
+ defer filehtml.Close()
+
+ _, err = filehtml.WriteString(bootstrap.String())
+ if err != nil {
+ // log.Fatal(err)
+ log.DebugError(err)
+ }
+ filehtml.WriteString("\r\n")
+ log.Common(fmt.Sprintf("以生成最终结果报告,报告存放路径为:%s", resultHtml))
+}
diff --git a/scripts/conditionals.go b/scripts/conditionals.go
new file mode 100755
index 0000000..b8b4287
--- /dev/null
+++ b/scripts/conditionals.go
@@ -0,0 +1,31 @@
+package scripts
+
+import "regexp"
+
+// IsIPAddress 判断字符串是否是IP地址的格式
+func IsIPAddress(str string) bool {
+ ipPattern := `^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`
+ match, _ := regexp.MatchString(ipPattern, str)
+ return match
+}
+
+// IsIPAddressWithPort 判断字符串是否是IP地址加端口号的格式
+func IsIPAddressWithPort(str string) bool {
+ ipPortPattern := `^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$`
+ match, _ := regexp.MatchString(ipPortPattern, str)
+ return match
+}
+
+// IsDomainName 判断字符串是否是域名
+func IsDomainName(str string) bool {
+ domainPattern := `^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`
+ match, _ := regexp.MatchString(domainPattern, str)
+ return match
+}
+
+// IsDomainNameWithPort 判断字符串是否是域名加端口形式
+func IsDomainNameWithPort(str string) bool {
+ domainPortPattern := `^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}:\d+$`
+ match, _ := regexp.MatchString(domainPortPattern, str)
+ return match
+}
diff --git a/scripts/ipurlrelated.go b/scripts/ipurlrelated.go
new file mode 100755
index 0000000..f33c84c
--- /dev/null
+++ b/scripts/ipurlrelated.go
@@ -0,0 +1,189 @@
+package scripts
+
+import (
+ log "Sowhp/concert/logger"
+ "bufio"
+ "context"
+ "fmt"
+ "github.com/chromedp/chromedp"
+ "github.com/chromedp/chromedp/device"
+ "net/http"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// extractDomainAndIP 提取URL中的顶级域名
+func extractDomainAndIP(url string) (domain string) {
+ // 匹配域名
+ domainPattern := `^(?:https?://)?([\w.-]+)`
+ domainRegex := regexp.MustCompile(domainPattern)
+ domainMatch := domainRegex.FindStringSubmatch(url)
+ if len(domainMatch) > 1 {
+ domain = domainMatch[1]
+ }
+ return domain
+}
+
+// FindTextUrl 获取本地text中url地址列表
+func FindTextUrl(filepath string) []string {
+ filePath := filepath // 替换为实际的txt文件路径
+
+ // 打开文件
+ file, err := os.Open(filePath)
+ if err != nil {
+ // fmt.Println("无法打开文件:", err)
+ log.DebugError(err)
+ return []string{}
+ }
+ defer file.Close()
+
+ urls := []string{} // 用于存储URL地址的列表
+
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+ // 提取URL地址
+ urllist := extractURL(line)
+ for _, url := range urllist {
+ if url != "" {
+ urls = append(urls, url)
+ }
+ }
+ }
+
+ if err := scanner.Err(); err != nil {
+ // fmt.Println("读取文件错误:", err)
+ log.DebugError(err)
+ return []string{}
+ }
+
+ return urls
+}
+
+// extractURL 提取IP端口地址,并生成url地址
+func extractURL(line string) []string {
+
+ var urlTmpList []string
+ var http string = "http://"
+ var https string = "https://"
+
+ // 判断当前输入的ip地址或者域名地址是否包含 http 或者 https
+ if strings.Contains(line, "http://") || strings.Contains(line, "https://") {
+ urllist := append(urlTmpList, line)
+ return urllist
+ } else if IsIPAddress(line) || IsDomainName(line) {
+ line1 := http + line
+ line2 := https + line
+ urllist := append(urlTmpList, line1, line2)
+ return urllist
+ } else if IsIPAddressWithPort(line) || IsDomainNameWithPort(line) {
+ line1 := http + line
+ line2 := https + line
+ urllist := append(urlTmpList, line1, line2)
+ return urllist
+ }
+
+ return []string{}
+}
+
+// visitURL 访问URL地址,在Tsaks中按顺利执行
+func visitURL(url string) chromedp.Tasks {
+ return chromedp.Tasks{
+ chromedp.Navigate(url),
+ }
+}
+
+// GetTimeStrin 获取当前时间字符串
+func GetTimeStrin() string {
+ currentTime := time.Now()
+ timeString := currentTime.Format("20060102150405")
+ return timeString
+}
+
+// GetUrlStatusCode 获取网站状态码
+func GetUrlStatusCode(url string) string {
+
+ // 创建一个自定义的 http.Client
+ client := &http.Client{
+ Timeout: 5 * time.Second, // 设置超时时间为 5 秒
+ }
+
+ resp, err := client.Head(url)
+ if err != nil {
+ log.DebugError(err)
+ log.Debug(fmt.Sprintf("获取 %s 状态码超时,返回一个空的字符串", url))
+ return " "
+ }
+ defer resp.Body.Close()
+ statuscode := strconv.Itoa(resp.StatusCode)
+ log.Debug(fmt.Sprintf("当前网站URL地址为:%s ,网站状态码为:%s", url, statuscode))
+ return statuscode
+}
+
+// ChromeScreenshot 访问url地址并对地址进行截图
+func ChromeScreenshot(URL string, resultname string) []string {
+
+ // 创建一个上下文,并设置超时时间为 10 秒
+ ct, cancel := context.WithTimeout(context.Background(), 13*time.Second)
+ defer cancel()
+
+ // 创建一个新的上下文
+ ctx, cancel := chromedp.NewContext(ct)
+ defer cancel()
+
+ // 创建一个选项数组,配置浏览器行为
+ opts := append(chromedp.DefaultExecAllocatorOptions[:],
+ chromedp.Flag("ignore-certificate-errors", true), // 忽略证书错误
+ )
+ // 创建一个自定义的执行器
+ allocCtx, cancel := chromedp.NewExecAllocator(ctx, opts...)
+ defer cancel()
+ // 使用自定义执行器创建新的上下文
+ ctx, cancel = chromedp.NewContext(allocCtx)
+ defer cancel()
+
+ // 定义一个变量来保存状态码和标题
+ var infoarray []string
+
+ // var statusCode int
+ var pageTitle string
+
+ // run
+ var b2 []byte
+ if err := chromedp.Run(ctx,
+ // reset
+ chromedp.Emulate(device.Reset),
+
+ // 设置浏览器界面大小,并捕获/截取当前浏览器视口的屏幕截图
+ chromedp.EmulateViewport(1920, 1080),
+ //chromedp.Navigate(URL),
+ visitURL(URL),
+ chromedp.CaptureScreenshot(&b2),
+ chromedp.WaitVisible(`body`, chromedp.ByQuery),
+ chromedp.Evaluate(`document.title`, &pageTitle),
+ ); err != nil {
+ log.DebugError(err)
+ log.Debug(fmt.Sprintf("访问 %s 超时,返回一个空的 []string{} 字典集", URL))
+ // 在这里处理无法访问URL的情况,例如记录错误、重试等
+ cancel() // 强制关闭 chromedp 上下文
+ return []string{}
+ }
+
+ urlname := extractDomainAndIP(URL)
+ photoname := fmt.Sprintf("data/%s-%s.png", urlname, GetTimeStrin())
+ log.Debug(fmt.Sprintf("生成截图照片成功,截图名称为:%s", photoname))
+ result := fmt.Sprintf("./result/%s/%s", resultname, photoname)
+ if err := os.WriteFile(result, b2, 0666); err != nil {
+ // log.Fatal(err)
+ log.DebugError(err)
+ }
+ log.Debug(fmt.Sprintf("本地存储截图照片成功,存储位置为:%s", result))
+
+ statuscode := GetUrlStatusCode(URL)
+ infoarray = append(infoarray, URL, pageTitle, statuscode, photoname)
+ cancel() // 强制关闭 chromedp 上下文
+ return infoarray
+}
diff --git a/url.txt b/url.txt
new file mode 100755
index 0000000..604b4c3
--- /dev/null
+++ b/url.txt
@@ -0,0 +1,2 @@
+https://github.com
+https://sh1yan.top