├── .deepsource.toml ├── .gitignore ├── .github └── workflows │ ├── go.yml │ └── codeql.yml ├── go.mod ├── Makefile ├── LICENSE ├── main.go ├── cmd ├── version │ └── version.go ├── root.go ├── md5 │ └── md5.go ├── hex │ └── hex.go ├── search │ └── search.go └── run │ └── run.go ├── table └── tables.go ├── log └── logging.go ├── scan ├── matcher.go ├── scanner_test.go └── scanner.go ├── go.sum └── README.md /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "test-coverage" 5 | enabled = true 6 | 7 | [[analyzers]] 8 | name = "go" 9 | enabled = true 10 | 11 | [analyzers.meta] 12 | import_root = "github.com/auula/owl" 13 | 14 | [[transformers]] 15 | name = "gofmt" 16 | enabled = true 17 | 18 | [[transformers]] 19 | name = "gofumpt" 20 | enabled = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | .vscode 15 | .idea 16 | res.json 17 | owl 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Set up Go 16 | uses: actions/setup-go@v3 17 | with: 18 | go-version: 1.18 19 | 20 | - name: Build 21 | run: go build -v ./... 22 | 23 | - name: Test 24 | run: go test -v ./... 25 | - name: Codecov 26 | run: go test ./... -race -coverprofile=coverage.txt -covermode=atomic -v 27 | 28 | - name: Upload coverage to Codecov 29 | run: bash <(curl -s https://codecov.io/bash) -t ${{ secrets.CODECOV_TOKEN }} 30 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/auula/owl 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/fatih/color v1.13.0 7 | github.com/spf13/cobra v1.4.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.0 // indirect 12 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 13 | github.com/mattn/go-colorable v0.1.9 // indirect 14 | github.com/mattn/go-isatty v0.0.14 // indirect 15 | github.com/mattn/go-runewidth v0.0.9 // indirect 16 | github.com/olekukonko/tablewriter v0.0.5 // indirect 17 | github.com/pmezard/go-difflib v1.0.0 // indirect 18 | github.com/spf13/pflag v1.0.5 // indirect 19 | github.com/stretchr/objx v0.1.0 // indirect 20 | github.com/stretchr/testify v1.7.1 // indirect 21 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build owl tools 2 | 3 | BINARY="owl" 4 | 5 | darwin: 6 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ${BINARY} 7 | @echo "Compile executable binary for MacOS platform successful." 8 | 9 | linux: 10 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY} 11 | @echo "Compile executable binary for Linux platform successful." 12 | 13 | windows: 14 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ${BINARY}.exe 15 | @echo "Compile executable binary for Windows platform successful." 16 | 17 | clean: 18 | @if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi 19 | @if [ -f ${BINARY}.exe ] ; then rm ${BINARY}.exe ; fi 20 | @echo "Clean up executable binary successful." 21 | 22 | help: 23 | @echo "make darwin | Compile executable binary for MacOS platform." 24 | @echo "make linux | Compile executable binary for Linux platform." 25 | @echo "make windows | Compile executable binary for Windows platform." 26 | @echo "make clean | Clean up executable binary." -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Leon Ding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // Owl is a dependency module feature scanning detection tool for static analysis. 24 | // This program supports compiling into binary. 25 | // Author: Leon Ding 26 | // Create Date: 2022-06-05 27 | package main 28 | 29 | import ( 30 | "github.com/auula/owl/cmd" 31 | ) 32 | 33 | // Program main entry 34 | func main() { 35 | cmd.Execute() 36 | } 37 | -------------------------------------------------------------------------------- /cmd/version/version.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package version 24 | 25 | import ( 26 | "fmt" 27 | "runtime" 28 | 29 | "github.com/auula/owl/scan" 30 | "github.com/spf13/cobra" 31 | ) 32 | 33 | var versionStr = ` 34 | Owl Tools related information: 35 | 36 | Repository : github.com/auula/owl 37 | System : %s 38 | Arch : %s 39 | Version : %s 40 | ` 41 | 42 | var Cmd = cobra.Command{ 43 | Use: "version", 44 | Short: "Version information", 45 | Long: "Owl command line tools related information", 46 | Run: func(cmd *cobra.Command, args []string) { 47 | fmt.Println(fmt.Sprintf(versionStr, runtime.GOOS, runtime.GOARCH, scan.Version)) 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /table/tables.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package table 24 | 25 | import ( 26 | "fmt" 27 | "os" 28 | 29 | "github.com/auula/owl/scan" 30 | "github.com/fatih/color" 31 | "github.com/olekukonko/tablewriter" 32 | ) 33 | 34 | var CommonTemplate = func() *tablewriter.Table { 35 | table := tablewriter.NewWriter(os.Stdout) 36 | table.SetHeader([]string{"🔢", "📃File Path", "🧬MD5"}) 37 | table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgYellowColor}, 38 | tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor}, 39 | tablewriter.Colors{tablewriter.Bold, tablewriter.BgRedColor}) 40 | return table 41 | }() 42 | 43 | func WriteTables(table *tablewriter.Table, res []scan.Result) { 44 | var list [][]string 45 | for i, v := range res { 46 | list = append(list, []string{ 47 | fmt.Sprintf("%d", i+1), color.GreenString(v.Path), color.RedString(v.Code), 48 | }) 49 | } 50 | table.AppendBulk(list) 51 | table.Render() 52 | } 53 | -------------------------------------------------------------------------------- /log/logging.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package log 24 | 25 | import ( 26 | "log" 27 | "os" 28 | 29 | "github.com/fatih/color" 30 | ) 31 | 32 | var ( 33 | info *log.Logger 34 | warn *log.Logger 35 | ) 36 | 37 | var ( 38 | // Logger colors and log message prefixes 39 | warnColor = color.New(color.FgWhite, color.Bold, color.BgRed) 40 | infoColor = color.New(color.FgWhite, color.Bold, color.BgGreen) 41 | redFont = color.New(color.FgRed) 42 | greenFont = color.New(color.FgGreen) 43 | warnPrefix = warnColor.Sprintf("WARN:") 44 | infoPrefix = infoColor.Sprintf("INFO:") 45 | ) 46 | 47 | const ( 48 | // Logger message format 49 | format = log.Ldate | log.Ltime 50 | ) 51 | 52 | func init() { 53 | info = log.New(os.Stdout, infoPrefix, format) 54 | warn = log.New(os.Stdout, warnPrefix, format) 55 | } 56 | 57 | func Info(v ...any) { 58 | info.Output(2, greenFont.Sprint(v...)) 59 | } 60 | 61 | func Warn(v ...any) { 62 | warn.Output(2, redFont.Sprint(v...)) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package cmd 24 | 25 | import ( 26 | "fmt" 27 | "os" 28 | 29 | "github.com/auula/owl/cmd/hex" 30 | "github.com/auula/owl/cmd/md5" 31 | "github.com/auula/owl/cmd/run" 32 | "github.com/auula/owl/cmd/search" 33 | "github.com/auula/owl/cmd/version" 34 | "github.com/auula/owl/log" 35 | "github.com/auula/owl/scan" 36 | "github.com/fatih/color" 37 | "github.com/spf13/cobra" 38 | ) 39 | 40 | const ( 41 | bannerStr = ` 42 | _____ _ _ __ 43 | ( _ )( \/\/ )( ) 44 | )(_)( ) ( )(__ 45 | (_____)(__/\__)(____) 🦉 %s 46 | 47 | A dependency module feature scanning detection tool for static analysis. 48 | ` 49 | ) 50 | 51 | var banner string = color.CyanString(fmt.Sprintf(bannerStr, scan.Version)) 52 | 53 | var rootCmd = &cobra.Command{ 54 | Use: "owl", 55 | Short: "Owl is a dependency module feature scanning detection tool for static analysis.", 56 | Long: banner, 57 | } 58 | 59 | func Execute() { 60 | if err := rootCmd.Execute(); err != nil { 61 | log.Warn(err) 62 | os.Exit(1) 63 | } 64 | } 65 | 66 | func init() { 67 | rootCmd.AddCommand(&version.Cmd) 68 | rootCmd.AddCommand(&run.Cmd) 69 | rootCmd.AddCommand(&md5.Cmd) 70 | rootCmd.AddCommand(&hex.Cmd) 71 | rootCmd.AddCommand(&search.Cmd) 72 | } 73 | -------------------------------------------------------------------------------- /cmd/md5/md5.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package md5 24 | 25 | import ( 26 | "github.com/auula/owl/scan" 27 | "github.com/auula/owl/table" 28 | "github.com/fatih/color" 29 | "github.com/spf13/cobra" 30 | ) 31 | 32 | const ( 33 | helpLong = ` 34 | 35 | Example: 36 | 37 | Get the md5 value of the specified file 👇 38 | $ ./owl md5 --path=/user/desktop/test.txt 39 | 40 | Get the md5 value of all files in the specified directory 👇 41 | $ ./owl md5 --path=/user/desktop/directory --out=result.json 42 | ` 43 | ) 44 | 45 | var path, out string 46 | 47 | var Cmd = cobra.Command{ 48 | Use: "md5", 49 | Short: "Collection file md5", 50 | Long: color.GreenString(helpLong), 51 | Run: func(cmd *cobra.Command, args []string) { 52 | scan.Exec(func() error { 53 | scanner := new(scan.Scanner) 54 | scanner.SetPath(path) 55 | if res, err := scanner.List(); err != nil { 56 | return err 57 | } else { 58 | scan.Output(out, scanner, res) 59 | table.WriteTables(table.CommonTemplate, res) 60 | } 61 | return nil 62 | }) 63 | }, 64 | } 65 | 66 | func init() { 67 | Cmd.Flags().StringVar(&out, "out", "", "Data result output is saved to the specified file") 68 | Cmd.Flags().StringVar(&path, "path", "", "The file path where the md5 value needs to be obtained") 69 | } 70 | -------------------------------------------------------------------------------- /cmd/hex/hex.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package hex 24 | 25 | import ( 26 | "fmt" 27 | 28 | "github.com/auula/owl/scan" 29 | "github.com/fatih/color" 30 | "github.com/spf13/cobra" 31 | ) 32 | 33 | const ( 34 | helpLong = ` 35 | 36 | Example: 37 | 38 | File that needs to be converted to hex 👇 39 | $ ./owl hex --path=/user/desktop/test.txt 40 | 41 | A file that needs to be converted to hex and redirected output 👇 42 | $ ./owl hex --path=/user/desktop/test.txt --out=result.json 43 | ` 44 | ) 45 | 46 | var path, out string 47 | 48 | var Cmd = cobra.Command{ 49 | Use: "hex", 50 | Short: "File hex encoding", 51 | Long: color.GreenString(helpLong), 52 | Run: func(cmd *cobra.Command, args []string) { 53 | scan.Exec(func() error { 54 | scanner := new(scan.Scanner) 55 | scanner.SetPath(path) 56 | if hexStr, err := scanner.HexDump(); err != nil { 57 | return err 58 | } else { 59 | scan.OutFileString(out, scanner, hexStr) 60 | fmt.Println(color.GreenString(hexStr)) 61 | } 62 | return nil 63 | }) 64 | }, 65 | } 66 | 67 | func init() { 68 | Cmd.Flags().StringVar(&out, "out", "", "Data result output is saved to the specified file") 69 | Cmd.Flags().StringVar(&path, "path", "", "The path to the file that needs to be converted to a hexadecimal string") 70 | } 71 | -------------------------------------------------------------------------------- /cmd/search/search.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package search 24 | 25 | import ( 26 | "fmt" 27 | "os" 28 | 29 | "github.com/auula/owl/scan" 30 | "github.com/fatih/color" 31 | "github.com/spf13/cobra" 32 | ) 33 | 34 | const ( 35 | helpLong = ` 36 | 37 | Example: 38 | 39 | Scanning and searching of signature files 👇 40 | $ ./owl search --path=/xxx/xxx --code=xxxxxxxxx 41 | ` 42 | ) 43 | 44 | var path, code string 45 | 46 | var Cmd = cobra.Command{ 47 | Use: "search", 48 | Short: "Searching of signature files", 49 | Long: color.GreenString(helpLong), 50 | Run: func(cmd *cobra.Command, args []string) { 51 | code = os.Getenv("FEATURE_CODE") 52 | path = os.Getenv("SOURCE_DIR") 53 | scan.Exec(func() error { 54 | scanner := &scan.Scanner{ 55 | Path: path, 56 | Code: code, 57 | Matcher: new(scan.Md5Matcher), 58 | } 59 | restca := make([]scan.ResultElement, 10) 60 | if res, err := scanner.Search(code); err != nil { 61 | return err 62 | } else { 63 | for _, v := range res { 64 | restca = append(restca, scan.ResultElement{ 65 | Path: v.Path, 66 | Line: "0", 67 | Column: "0", 68 | Msg: fmt.Sprintf("匹配文件路径为:%v", v.Path), 69 | Rule: "", 70 | Refs: []scan.Ref{}, 71 | }) 72 | } 73 | scan.SaveFile("result.json", scanner, restca) 74 | } 75 | return nil 76 | }) 77 | }, 78 | } 79 | 80 | func init() { 81 | Cmd.Flags().StringVar(&code, "code", "", "Requires search signature") 82 | Cmd.Flags().StringVar(&path, "path", "", "The file path where the md5 value needs to be obtained") 83 | } 84 | -------------------------------------------------------------------------------- /scan/matcher.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package scan 24 | 25 | import ( 26 | "errors" 27 | "strings" 28 | ) 29 | 30 | var ( 31 | ErrNotIsDir = errors.New("the current file is not a directory") 32 | NilString = "" 33 | ) 34 | 35 | // Result content scan results 36 | type Result struct { 37 | Index int `json:"index,omitempty"` 38 | Path string `json:"path,omitempty"` 39 | Code string `json:"code,omitempty"` 40 | } 41 | 42 | // Matcher scanner matcher 43 | type Matcher interface { 44 | Search(files []string, searchTerm string) ([]Result, error) 45 | } 46 | 47 | type ( 48 | // Matcher md5 and hex implement 49 | Md5Matcher struct{} 50 | HexMatcher struct{} 51 | ) 52 | 53 | // Search match search signatures by md5 in file collections 54 | func (*Md5Matcher) Search(files []string, searchTerm string) ([]Result, error) { 55 | res := make([]Result, 0) 56 | for i, v := range files { 57 | if md5, err := Md5(v); err != nil { 58 | return nil, err 59 | } else { 60 | if md5 == searchTerm { 61 | res = append(res, Result{ 62 | Index: i + 1, 63 | Path: v, 64 | Code: md5, 65 | }) 66 | } 67 | } 68 | } 69 | return res, nil 70 | } 71 | 72 | // Search match search signatures by hex in file collections 73 | func (*HexMatcher) Search(files []string, searchTerm string) ([]Result, error) { 74 | res := make([]Result, 0) 75 | for i, v := range files { 76 | strHex, err := HexDump(v) 77 | if err != nil { 78 | return nil, err 79 | } 80 | if strings.Contains(strHex, searchTerm) { 81 | md5, err := Md5(v) 82 | if err != nil { 83 | return nil, err 84 | } 85 | res = append(res, Result{ 86 | Index: i + 1, 87 | Path: v, 88 | Code: md5, 89 | }) 90 | } 91 | } 92 | return res, nil 93 | } 94 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '28 6 * * 2' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'go' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /cmd/run/run.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package run 24 | 25 | import ( 26 | "errors" 27 | 28 | "github.com/auula/owl/scan" 29 | "github.com/auula/owl/table" 30 | "github.com/fatih/color" 31 | "github.com/spf13/cobra" 32 | ) 33 | 34 | const ( 35 | helpLong = ` 36 | 37 | Example: 38 | 39 | Scan the target data file or directory according to different feature codes 👇 40 | $ ./owl run --dir=/user/desktop/directory --mode=md5 --code=81129dsxxxxx2d8123 41 | 42 | Search according to different patterns 👇 43 | $ ./owl run --dir=/user/desktop/directory --mode=hex --code=74 63 61 73 63 61 6e 2f --out=result.json 44 | ` 45 | ) 46 | 47 | var mode, code, dir, out string 48 | 49 | var Cmd = cobra.Command{ 50 | Use: "run", 51 | Short: "Execute the scanner", 52 | Long: color.GreenString(helpLong), 53 | Run: func(cmd *cobra.Command, args []string) { 54 | scan.Exec(func() error { 55 | scanner := new(scan.Scanner) 56 | scanner.SetPath(dir) 57 | switch mode { 58 | case "md5": 59 | scanner.SetMatcher(new(scan.Md5Matcher)) 60 | case "hex": 61 | scanner.SetMatcher(new(scan.HexMatcher)) 62 | default: 63 | return errors.New("Match search pattern is not sure") 64 | } 65 | if code == "" { 66 | return errors.New("Match value can not be empty can be md5 or hexadecimal string") 67 | } 68 | if res, err := scanner.Search(code); err != nil { 69 | return err 70 | } else { 71 | scan.Output(out, scanner, res) 72 | table.WriteTables(table.CommonTemplate, res) 73 | } 74 | return nil 75 | }) 76 | }, 77 | } 78 | 79 | func init() { 80 | Cmd.Flags().StringVar(&code, "code", "", "Requires search signature") 81 | Cmd.Flags().StringVar(&mode, "mode", "", "Matcher search mode") 82 | Cmd.Flags().StringVar(&dir, "dir", "", "Directory path to scan") 83 | Cmd.Flags().StringVar(&out, "out", "", "Data result output is saved to the specified file") 84 | } 85 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/auula/deepscan v0.0.0-20220606051620-4b1ab2e631dd h1:6CTdBRaqEz6oXCtr0JrUdy+pnogkAdKPMaw71RTkpHg= 2 | github.com/auula/deepscan v0.0.0-20220606051620-4b1ab2e631dd/go.mod h1:FjyGG4OzHcPIjU9g/EKVsA+u/1gPPTu+rJjMmpRjuKU= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 4 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 7 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 8 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 9 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 10 | github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= 11 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 12 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 13 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 14 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 15 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 16 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 17 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 18 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 19 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 20 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 21 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 22 | github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= 23 | github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= 24 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 25 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 26 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 27 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 28 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 29 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 30 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 31 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 32 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= 33 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 36 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 37 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | -------------------------------------------------------------------------------- /scan/scanner_test.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package scan_test 24 | 25 | import ( 26 | "crypto/md5" 27 | "os" 28 | "testing" 29 | 30 | "github.com/auula/owl/scan" 31 | "github.com/stretchr/testify/assert" 32 | ) 33 | 34 | func TestAllFunction(t *testing.T) { 35 | assert.Equal(t, true, scan.IsDir("/")) 36 | assert.Equal(t, true, scan.IsFile("scanner_test.go")) 37 | 38 | files, err := scan.Files("./") 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | assert.Equal(t, []string{"matcher.go", "scanner.go", "scanner_test.go"}, files) 43 | 44 | assert.Equal(t, func() string { 45 | md5, _ := scan.Md5("scanner_test.go") 46 | return md5 47 | }(), func() string { 48 | md5, _ := scan.Md5("scanner_test.go") 49 | return md5 50 | }()) 51 | 52 | assert.Equal(t, scan.HexDecode("scanner_test.go"), scan.HexDecode("scanner_test.go")) 53 | assert.Equal(t, scan.HexEncode("scanner_test.go"), scan.HexEncode("scanner_test.go")) 54 | 55 | assert.Equal(t, func() string { 56 | strHex, _ := scan.HexDump("scanner_test.go") 57 | return strHex 58 | }(), func() string { 59 | strHex, _ := scan.HexDump("scanner_test.go") 60 | return strHex 61 | }()) 62 | } 63 | 64 | func TestBlackMd5(t *testing.T) { 65 | file, err := os.Open("scanner_test.go") 66 | if err != nil { 67 | t.Fatal(err) 68 | } 69 | fi, err := os.Stat("scanner_test.go") 70 | if err != nil { 71 | t.Fatal(err) 72 | } 73 | assert.Equal(t, func() string { 74 | md5, _ := scan.BlockMd5(file, fi.Size(), md5.New()) 75 | return md5 76 | }(), func() string { 77 | md5, _ := scan.BlockMd5(file, fi.Size(), md5.New()) 78 | return md5 79 | }()) 80 | } 81 | 82 | func TestScanner(t *testing.T) { 83 | scanner := new(scan.Scanner) 84 | scanner.SetPath("./") 85 | res, err := scanner.List() 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | t.Log(res) 90 | 91 | scanner.SetMatcher(new(scan.Md5Matcher)) 92 | res, err = scanner.Search("xxxx") 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | if len(res) != 0 { 97 | t.Fatal(res) 98 | } 99 | 100 | scanner.SetPath("scanner_test.go") 101 | assert.Equal(t, func() string { 102 | str, _ := scanner.HexDump() 103 | return str 104 | }(), func() string { 105 | str, _ := scanner.HexDump() 106 | return str 107 | }()) 108 | 109 | file, err := os.OpenFile("res.json", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666) 110 | if err != nil { 111 | t.Fatal(err) 112 | } 113 | if err := scanner.Output(file, res); err != nil { 114 | t.Fatal(err) 115 | } 116 | os.Remove("res.json") 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Owl🦉 2 | 3 | A dependency module feature scanning detection tool for static analysis. 4 | 5 | --- 6 | 7 | [![DeepSource](https://deepsource.io/gh/auula/owl.svg/?label=active+issues&show_trend=true&token=2dqhjlFmox_IfR5zuVpSv64Q)](https://deepsource.io/gh/auula/owl/?ref=repository-badge) 8 | [![codecov](https://codecov.io/gh/auula/owl/branch/main/graph/badge.svg?token=0i8L7DuJlK)](https://codecov.io/gh/auula/owl) 9 | [![License](https://img.shields.io/badge/license-MIT-db5149.svg)](https://github.com/auula/owl/blob/master/LICENSE) 10 | [![Go Report Card](https://goreportcard.com/badge/github.com/auula/owl)](https://goreportcard.com/report/github.com/auula/owl) 11 | 12 | --- 13 | 14 | ### 介 绍 15 | 16 | `Owl`是一款开源项目依赖分析工具,可以快速在指定的项目目录下查找符合某些特征的源代码文件或者依赖文件。为何开发了这款工具?例如很多时候我们项目太大,项目文件夹下有很多依赖文件,如一个`Java`项目引入了`log4j`这个`jar`依赖,在项目中某文件存在循环依赖问题。当某个依赖包出现了漏洞时,本工具能快速扫描项目目录下存在的可疑依赖文件,并且给出依赖文件所在的地址,帮助开发者能快速进行定位到可疑文件。 17 | 18 | 19 | ### 原 理 20 | 21 | 目前版本的功能比较简单,工作原理很简单,工具会对特定目录进行扫描通过内置的特征码算法匹配到特定文件,然后收集与其特征码匹配的文件地址,然后展示出来,也可以重定向到一个固定`json`文件中保存。 22 | 23 | ![](https://tva1.sinaimg.cn/large/e6c9d24egy1h2yvkgtmbwj20lo0ca0tl.jpg) 24 | 25 | `Owl`类似于杀毒软件一样,和杀毒软件的工作原理差不多,`Owl`会根据依赖文件的特征码来扫描整个项目,和杀毒病毒库工作原理类似。当然如果严格按照杀毒软件那种标准做的话,可能涉及一些汇编相关的,目前`owl`功能实现还没有那么复杂,后面会版本会加入`codeql`代码分析引擎,通过`codeql`的数据库来做静态分析功能增强。 26 | 27 | ### 快速开始 28 | 29 | 如何使用`owl`?你可以克隆仓库然后通过如下命令: 30 | 31 | ```bash 32 | git clone git@github.com:auula/owl.git 33 | ``` 34 | 在仓库内部有一个`Makefile`文件可以快速帮助你构建相应平台的二进制文件,例如: 35 | 36 | ```bash 37 | $: make help 38 | make darwin | Compile executable binary for MacOS platform 39 | make linux | Compile executable binary for Linux platform 40 | make windows | Compile executable binary for Windows platform 41 | make clean | Clean up executable binary 42 | ``` 43 | 44 | `Owl`起因也是为`CodeAnalysis`所编写的特征检测工具,所以你也可以在:[`https://github.com/Tencent/CodeAnalysis`](https://github.com/Tencent/CodeAnalysis/tree/main/tools/owl) 这个项目下面的`tools`目录找到已经编译好的二进制可执行文件,下载对应平台的二进制文件即可。 45 | 46 | ### 如何使用 47 | 48 | 程序构建完成会得到一个二进制文件,程序名称为`owl`,如下为`owl`执行效果,一些子命令参数都已经列出: 49 | 50 | ```bash 51 | $: ./owl 52 | 53 | _____ _ _ __ 54 | ( _ )( \/\/ )( ) 55 | )(_)( ) ( )(__ 56 | (_____)(__/\__)(____) 🦉 v0.1.2 57 | 58 | A dependency module feature scanning detection tool for static analysis. 59 | 60 | 61 | Usage: 62 | owl [command] 63 | 64 | Available Commands: 65 | completion Generate the autocompletion script for the specified shell 66 | help Help about any command 67 | hex File hex encoding 68 | md5 Collection file md5 69 | run Execute the scanner 70 | version Version information 71 | 72 | Flags: 73 | -h, --help help for owl 74 | 75 | Use "owl [command] --help" for more information about a command. 76 | ``` 77 | 78 | 如果不知道子命令如何使用,可以在对应的子命令后面参入`--help`参数,即可得到帮助信息: 79 | 80 | ![](https://tva1.sinaimg.cn/large/e6c9d24egy1h2yz0laxdyj22ax0u07bb.jpg) 81 | 82 | 例如如果你要查找`log4j`,你首先要通过`owl`计算`log4j`特征码,命令如下: 83 | 84 | ```bash 85 | $: ./owl md5 --path=/Users/ding/Downloads/log4j-1.2.17.jar 86 | ``` 87 | 88 | **注意这里的特征码计算必须使用`owl`程序的算法,因为`owl`里面的算法针对大文件我是采用分数据块方案计算的,提升程序运行速度,所以如果使用其他软件的算法那么就会出现问题!** 89 | 90 | 结果如下: 91 | 92 | ![](https://tva1.sinaimg.cn/large/e6c9d24egy1h2yz54cg72j22gm0e0af2.jpg) 93 | 94 | 你也可以使用十六进制字符串特征去查找: 95 | 96 | ```bash 97 | $: ./owl hex --path=/Users/ding/Downloads/log4j-1.2.17.jar 98 | ``` 99 | 100 | 程序会将对应的文件转成十六进制字符串展示,如下图: 101 | 102 | ![](https://tva1.sinaimg.cn/large/e6c9d24egy1h2yz7v68cbj217g0u0h0x.jpg) 103 | 104 | 现在就可以使用扫描器进行扫描了,匹配模式可以指定为`md5`或者`hex`,未来可能会添加跟多的模式,命令如下: 105 | 106 | ```bash 107 | $: ./owl run --dir=/Users/ding/Downloads/ --mode=md5 --code=04a41f0a068986f0f73485cf507c0f40 108 | ``` 109 | 110 | 搜索得到具体依赖文件: 111 | 112 | ![](https://tva1.sinaimg.cn/large/e6c9d24egy1h2yze6emx3j21yq0dajwn.jpg) 113 | 114 | 115 | **搜索结果如果过多,可以通过`--out`参数将结果重定向保存到文件中保存,文件格式为`json`!** 116 | 117 | ### SDK方式 118 | 119 | 上面介绍完是`command line`方式进行的,`owl`程序本身就是一个`command line`,核心逻辑在 [`github.com/auula/owl/scan`](https://github.com/auula/owl/tree/main/scan) 这个包中编写的,如果想二次开发,那么就可以直接使用`go get github.com/auula/owl` 安装这个模块到你项目里面,然后直接通过硬编码的方式进行自定义编程; 120 | 121 | 122 | 一个简单实例,通过自定义代码方式进行依赖文件扫描和收集: 123 | 124 | 125 | ```go 126 | package main 127 | 128 | import ( 129 | "fmt" 130 | 131 | "github.com/auula/owl/scan" 132 | ) 133 | 134 | func main() { 135 | // 创建扫描器 136 | scanner := new(scan.Scanner) 137 | // 设置扫描器路径 138 | scanner.SetPath("github.com/auula/owl") 139 | // 返回对应路径所有文件特征码 140 | res, _ := scanner.List() 141 | fmt.Println(res) 142 | 143 | // 设置指定的匹配器,其他匹配器查看API文档 144 | scanner.SetMatcher(new(scan.Md5Matcher)) 145 | // 搜索包含特征码文件,返回文件记录集合 146 | res, _ = scanner.Search("xxxx") 147 | 148 | // 打开一个文件描述符 149 | file, _ := os.OpenFile("res.json", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666) 150 | // 将结果保存到指定文件中 151 | scanner.Output(file, res) 152 | } 153 | ``` 154 | 155 | **以上就是通过`SDK`方式自定义编码完成依赖特征检测。** 156 | 157 | 158 | ### 其他 159 | 160 | 有问题欢迎提`issue`,工具不错的话记得按一个`⭐`,另外更强代码分析工具使用:[`https://github.com/Tencent/CodeAnalysis`](https://github.com/Tencent/CodeAnalysis)。 161 | -------------------------------------------------------------------------------- /scan/scanner.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Leon Ding 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package scan 24 | 25 | import ( 26 | "crypto/md5" 27 | "encoding/hex" 28 | "encoding/json" 29 | "hash" 30 | "io" 31 | "io/ioutil" 32 | "os" 33 | "path/filepath" 34 | "time" 35 | 36 | "github.com/auula/owl/log" 37 | ) 38 | 39 | const ( 40 | // When a single file exceeds this size limit 41 | // the fragmented md5 algorithm will be used 42 | fileMaxSize = 10 << 10 << 10 // 10MB 43 | 44 | // Application version information 45 | Version = "0.1.3" 46 | ) 47 | 48 | // IsFile check if the path is a file 49 | func IsFile(path string) bool { 50 | return !IsDir(path) 51 | } 52 | 53 | // IsDir check if the path is a directory 54 | func IsDir(path string) bool { 55 | s, err := os.Stat(path) 56 | if err != nil { 57 | return false 58 | } 59 | return s.IsDir() 60 | } 61 | 62 | // Files returns the collection of all file paths under the specified path 63 | func Files(folder string) ([]string, error) { 64 | var result []string 65 | 66 | filepath.Walk(folder, func(filePath string, fi os.FileInfo, err error) error { 67 | if err != nil { 68 | return err 69 | } 70 | if !fi.IsDir() { 71 | // If you want to ignore this directory, return filepath.SkipDir, ie: 72 | // return filepath.SkipDir 73 | result = append(result, filePath) 74 | } 75 | 76 | return nil 77 | }) 78 | 79 | return result, nil 80 | } 81 | 82 | // Md5 get the md5 value of the file based on the path 83 | func Md5(filepath string) (string, error) { 84 | file, err := os.Open(filepath) 85 | if err != nil { 86 | return "", err 87 | } 88 | defer file.Close() 89 | 90 | fileInfo, err := os.Stat(filepath) 91 | if err != nil { 92 | return "", err 93 | } 94 | 95 | hashed := md5.New() 96 | 97 | if fileInfo.Size() >= fileMaxSize { 98 | return BlockMd5(file, fileInfo.Size(), hashed) 99 | } else { 100 | io.Copy(hashed, file) 101 | } 102 | 103 | // io.Copy(hashed, file) 104 | 105 | return hex.EncodeToString(hashed.Sum(nil)), nil 106 | } 107 | 108 | // BlockMd5 Built-in sharding md5 algorithm used 109 | func BlockMd5(file *os.File, size int64, hashed hash.Hash) (string, error) { 110 | // Intercept data segment size window 111 | var ( 112 | head = make([]byte, 500) 113 | body = make([]byte, 24) 114 | tail = make([]byte, 500) 115 | ) 116 | 117 | if _, err := file.ReadAt(head, 0); err != nil { 118 | return "", err 119 | } 120 | 121 | if _, err := file.ReadAt(body, size/4); err != nil { 122 | return "", err 123 | } 124 | 125 | if _, err := file.ReadAt(tail, size-512); err != nil { 126 | return "", err 127 | } 128 | hashed.Write(head) 129 | hashed.Write(body) 130 | hashed.Write(tail) 131 | return hex.EncodeToString(hashed.Sum(nil)), nil 132 | } 133 | 134 | // HexDecode hex decryption 135 | func HexDecode(s string) []byte { 136 | dst := make([]byte, hex.DecodedLen(len(s))) 137 | n, err := hex.Decode(dst, []byte(s)) 138 | if err != nil { 139 | return nil 140 | } 141 | return dst[:n] 142 | } 143 | 144 | // HexEncode hex encoding 145 | func HexEncode(s string) []byte { 146 | dst := make([]byte, hex.EncodedLen(len(s))) 147 | n := hex.Encode(dst, []byte(s)) 148 | return dst[:n] 149 | } 150 | 151 | // HexDump convert the specified file to hexadecimal 152 | func HexDump(path string) (string, error) { 153 | bytes, err := ioutil.ReadFile(path) 154 | if err != nil { 155 | return NilString, err 156 | } 157 | return hex.Dump(bytes), nil 158 | } 159 | 160 | // Scanner universal scanner 161 | type Scanner struct { 162 | Matcher 163 | Path string 164 | Code string 165 | } 166 | 167 | // SetMatcher setting matcher scanner 168 | func (s *Scanner) SetMatcher(m Matcher) { 169 | s.Matcher = m 170 | } 171 | 172 | // SetPath setting path scanner 173 | func (s *Scanner) SetPath(path string) { 174 | s.Path = path 175 | } 176 | 177 | // Search search signature 178 | func (s *Scanner) Search(code string) ([]Result, error) { 179 | s.Code = code 180 | if IsDir(s.Path) { 181 | if files, err := Files(s.Path); err != nil { 182 | return nil, err 183 | } else { 184 | return s.Matcher.Search(files, s.Code) 185 | } 186 | } 187 | return nil, ErrNotIsDir 188 | } 189 | 190 | // List returns all files under the specified path to calculate the signature 191 | func (s *Scanner) List() ([]Result, error) { 192 | res := make([]Result, 0) 193 | if IsFile(s.Path) { 194 | md5, err := Md5(s.Path) 195 | if err != nil { 196 | return nil, err 197 | } 198 | res = append(res, Result{ 199 | Index: 1, 200 | Path: s.Path, 201 | Code: md5, 202 | }) 203 | return res, nil 204 | } 205 | 206 | if files, err := Files(s.Path); err != nil { 207 | return nil, err 208 | } else { 209 | for i, v := range files { 210 | if md5, err := Md5(v); err != nil { 211 | return nil, err 212 | } else { 213 | res = append(res, Result{ 214 | Index: i + 1, 215 | Path: v, 216 | Code: md5, 217 | }) 218 | } 219 | } 220 | } 221 | return res, nil 222 | } 223 | 224 | // HexDump convert path content to hexadecimal 225 | func (s *Scanner) HexDump() (string, error) { 226 | bytes, err := ioutil.ReadFile(s.Path) 227 | if err != nil { 228 | return NilString, err 229 | } 230 | return hex.Dump(bytes), nil 231 | } 232 | 233 | // Exec execution statistics time 234 | func Exec(do func() error) { 235 | defer func() { 236 | if err := recover(); err != nil { 237 | log.Warn(err) 238 | os.Exit(1) 239 | } 240 | }() 241 | log.Info("Loading Files...") 242 | start := time.Now() 243 | if err := do(); err != nil { 244 | log.Warn(err) 245 | os.Exit(1) 246 | } 247 | elapsed := time.Since(start) 248 | log.Info("Scanning time to complete: ", elapsed) 249 | os.Exit(0) 250 | } 251 | 252 | // Output output result to writable io device 253 | func (*Scanner) Output(writer io.Writer, res []Result) error { 254 | bytes, err := json.Marshal(res) 255 | if err != nil { 256 | return err 257 | } 258 | if _, err := writer.Write(bytes); err != nil { 259 | return err 260 | } 261 | return nil 262 | } 263 | 264 | func (*Scanner) SaveAs(writer io.Writer, res []ResultElement) error { 265 | bytes, err := json.Marshal(res) 266 | if err != nil { 267 | return err 268 | } 269 | if _, err := writer.Write(bytes); err != nil { 270 | return err 271 | } 272 | return nil 273 | } 274 | 275 | // Output send the specified content to the console or file 276 | func Output(out string, scanner *Scanner, res []Result) { 277 | if out != "" { 278 | if file, err := os.OpenFile(out, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666); err != nil { 279 | log.Warn(err) 280 | file.Close() 281 | os.Exit(1) 282 | } else { 283 | defer file.Close() 284 | if err := scanner.Output(file, res); err != nil { 285 | log.Warn(err) 286 | os.Exit(1) 287 | } 288 | log.Info("The result has been redirected to: ", out) 289 | os.Exit(0) 290 | } 291 | } 292 | } 293 | 294 | func SaveFile(out string, scanner *Scanner, res []ResultElement) { 295 | if out != "" { 296 | if file, err := os.OpenFile(out, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666); err != nil { 297 | log.Warn(err) 298 | file.Close() 299 | os.Exit(1) 300 | } else { 301 | defer file.Close() 302 | if err := scanner.SaveAs(file, res); err != nil { 303 | log.Warn(err) 304 | os.Exit(1) 305 | } 306 | log.Info("The result has been redirected to: ", out) 307 | os.Exit(0) 308 | } 309 | } 310 | } 311 | 312 | // OutFileString output content to the specified file 313 | func OutFileString(out string, _ *Scanner, hexStr string) { 314 | if out != "" { 315 | if file, err := os.OpenFile(out, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666); err != nil { 316 | log.Warn(err) 317 | file.Close() 318 | os.Exit(1) 319 | } else { 320 | defer file.Close() 321 | if _, err := file.WriteString(hexStr); err != nil { 322 | log.Warn(err) 323 | os.Exit(1) 324 | } 325 | log.Info("The result has been redirected to: ", out) 326 | os.Exit(0) 327 | } 328 | } 329 | } 330 | 331 | type ResultElement struct { 332 | Path string `json:"path"` 333 | Line string `json:"line"` 334 | Column string `json:"column"` 335 | Msg string `json:"msg"` 336 | Rule string `json:"rule"` 337 | Refs []Ref `json:"refs"` 338 | } 339 | 340 | type Ref struct { 341 | Line string `json:"line"` 342 | Msg string `json:"msg"` 343 | Tag string `json:"tag"` 344 | Path string `json:"path"` 345 | } 346 | --------------------------------------------------------------------------------