├── .gitignore ├── Common ├── CommandExecutor.go ├── Config.go ├── Environment.go ├── Flag.go ├── Start.go └── Utils.go ├── Database ├── Builder.go ├── Decompile.go ├── Decompiler.go ├── Initializer.go └── Utils.go ├── Install ├── AntDownload.go ├── CodeqlDownload.go ├── DecompileDownload.go ├── JDKDownload.go ├── TomcatDownload.go └── Utils.go ├── LICENSE ├── README.md ├── Scanner ├── Scanner.go ├── cleanup.go ├── file_extractor.go └── hints.go ├── application.ico ├── go.mod ├── go.sum ├── main.go ├── qlLibs ├── codeql-pack.lock.yml ├── qlpack.yml └── queries │ └── main.ql ├── results.sarif ├── rsrc.syso └── scan_report.html /.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 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # Go workspace file 18 | go.work 19 | 20 | # IDE files 21 | .vscode/ 22 | .idea/ 23 | *.swp 24 | *.swo 25 | *~ 26 | 27 | # OS generated files 28 | .DS_Store 29 | .DS_Store? 30 | ._* 31 | .Spotlight-V100 32 | .Trashes 33 | ehthumbs.db 34 | Thumbs.db 35 | 36 | # Project specific 37 | /tools/ 38 | /temp/ 39 | /output/ 40 | /createdatabase/ 41 | *.jar 42 | *.war 43 | *.zip 44 | *.tar.gz 45 | 46 | # Build artifacts 47 | /dist/ 48 | /build/ 49 | 50 | # Logs 51 | *.log 52 | 53 | 54 | # Compiled executable 55 | codeql_n1ght_with_icon.exe 56 | codeql_n1ght 57 | codeql_n1ght.exe 58 | 59 | # Temporary files 60 | *.tmp 61 | *.temp 62 | 63 | # Database files 64 | *.db 65 | *.sqlite 66 | *.sqlite3 67 | 68 | # Configuration files with sensitive data 69 | config.json 70 | secrets.json 71 | .env -------------------------------------------------------------------------------- /Common/CommandExecutor.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | // CommandExecutor 命令执行器结构体 12 | type CommandExecutor struct { 13 | ToolsPath string // tools目录路径 14 | } 15 | 16 | // NewCommandExecutor 创建新的命令执行器 17 | func NewCommandExecutor(toolsPath string) *CommandExecutor { 18 | return &CommandExecutor{ 19 | ToolsPath: toolsPath, 20 | } 21 | } 22 | 23 | // GetExecutablePath 从环境变量或tools目录获取可执行文件路径 24 | func (ce *CommandExecutor) GetExecutablePath(envVar, toolSubPath, executableName string) (string, error) { 25 | // 首先尝试从环境变量获取 26 | if envPath := os.Getenv(envVar); envPath != "" { 27 | execPath := filepath.Join(envPath, "bin", executableName) 28 | if FileExists(execPath) { 29 | return execPath, nil 30 | } 31 | // 如果bin目录下没有,直接在环境变量路径下查找 32 | execPath = filepath.Join(envPath, executableName) 33 | if FileExists(execPath) { 34 | return execPath, nil 35 | } 36 | } 37 | 38 | // 如果环境变量不存在或找不到可执行文件,从tools目录查找 39 | toolPath := filepath.Join(ce.ToolsPath, toolSubPath, "bin", executableName) 40 | if FileExists(toolPath) { 41 | return toolPath, nil 42 | } 43 | 44 | // 直接在工具子目录下查找 45 | toolPath = filepath.Join(ce.ToolsPath, toolSubPath, executableName) 46 | if FileExists(toolPath) { 47 | return toolPath, nil 48 | } 49 | 50 | return "", fmt.Errorf("无法找到 %s,请检查环境变量 %s 或确保工具已安装在 %s", executableName, envVar, toolSubPath) 51 | } 52 | 53 | // ExecuteCommand 执行命令并返回结果 54 | func (ce *CommandExecutor) ExecuteCommand(executablePath string, args ...string) (string, error) { 55 | cmd := exec.Command(executablePath, args...) 56 | output, err := cmd.CombinedOutput() 57 | if err != nil { 58 | return string(output), fmt.Errorf("命令执行失败: %v\n输出: %s", err, string(output)) 59 | } 60 | return string(output), nil 61 | } 62 | 63 | // ExecuteJavaCommand 执行Java命令 64 | func (ce *CommandExecutor) ExecuteJavaCommand(args ...string) (string, error) { 65 | javaPath, err := ce.GetExecutablePath("JAVA_HOME", "jdk", "java.exe") 66 | if err != nil { 67 | return "", err 68 | } 69 | return ce.ExecuteCommand(javaPath, args...) 70 | } 71 | 72 | // ExecuteCodeQLCommand 执行CodeQL命令 73 | func (ce *CommandExecutor) ExecuteCodeQLCommand(args ...string) (string, error) { 74 | codeqlPath, err := ce.GetExecutablePath("CODEQL_HOME", "codeql", "codeql.exe") 75 | if err != nil { 76 | return "", err 77 | } 78 | return ce.ExecuteCommand(codeqlPath, args...) 79 | } 80 | 81 | // ExecuteAntCommand 执行Ant命令 82 | func (ce *CommandExecutor) ExecuteAntCommand(args ...string) (string, error) { 83 | antPath, err := ce.GetExecutablePath("ANT_HOME", "ant", "ant.bat") 84 | if err != nil { 85 | return "", err 86 | } 87 | return ce.ExecuteCommand(antPath, args...) 88 | } 89 | 90 | // GetJavaVersion 获取Java版本信息 91 | func (ce *CommandExecutor) GetJavaVersion() (string, error) { 92 | output, err := ce.ExecuteJavaCommand("-version") 93 | if err != nil { 94 | return "", err 95 | } 96 | // Java版本信息通常在stderr中,但CombinedOutput会合并stdout和stderr 97 | lines := strings.Split(output, "\n") 98 | for _, line := range lines { 99 | if strings.Contains(line, "version") { 100 | return strings.TrimSpace(line), nil 101 | } 102 | } 103 | return output, nil 104 | } 105 | 106 | // GetCodeQLVersion 获取CodeQL版本信息 107 | func (ce *CommandExecutor) GetCodeQLVersion() (string, error) { 108 | output, err := ce.ExecuteCodeQLCommand("version") 109 | if err != nil { 110 | return "", err 111 | } 112 | return strings.TrimSpace(output), nil 113 | } 114 | 115 | // GetAntVersion 获取Ant版本信息 116 | func (ce *CommandExecutor) GetAntVersion() (string, error) { 117 | output, err := ce.ExecuteAntCommand("-version") 118 | if err != nil { 119 | return "", err 120 | } 121 | lines := strings.Split(output, "\n") 122 | for _, line := range lines { 123 | if strings.Contains(line, "Apache Ant") { 124 | return strings.TrimSpace(line), nil 125 | } 126 | } 127 | return output, nil 128 | } 129 | func (ce *CommandExecutor) GetProcyonVersion() (string, error) { 130 | output, err := ce.ExecuteJavaCommand("-jar", "./tools/procyon-decompiler-0.6.0.jar", "--version") 131 | if err != nil { 132 | return "", err 133 | } 134 | lines := strings.Split(output, "\n") 135 | for _, line := range lines { 136 | return strings.TrimSpace(line), nil 137 | } 138 | return output, nil 139 | } 140 | 141 | // GetTomcatVersion 获取Tomcat版本信息 142 | func (ce *CommandExecutor) GetTomcatVersion() (string, error) { 143 | // 首先检查CATALINA_HOME环境变量 144 | catalinaHome := os.Getenv("CATALINA_HOME") 145 | if catalinaHome != "" { 146 | versionScript := filepath.Join(catalinaHome, "bin", "version.bat") 147 | if FileExists(versionScript) { 148 | output, err := ce.ExecuteCommand(versionScript) 149 | if err == nil { 150 | lines := strings.Split(output, "\n") 151 | for _, line := range lines { 152 | if strings.Contains(line, "Server version:") { 153 | return strings.TrimSpace(strings.Split(line, ":")[1]), nil 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | // 如果CATALINA_HOME不存在,检查tools目录 161 | tomcatPath := filepath.Join(ce.ToolsPath, "tomcat") 162 | 163 | // 检查apache-tomcat-9.0.27目录 164 | tomcatVersionPath := filepath.Join(tomcatPath, "apache-tomcat-9.0.27") 165 | if _, err := os.Stat(tomcatVersionPath); err == nil { 166 | versionScript := filepath.Join(tomcatVersionPath, "bin", "version.bat") 167 | if FileExists(versionScript) { 168 | output, err := ce.ExecuteCommand(versionScript) 169 | if err == nil { 170 | lines := strings.Split(output, "\n") 171 | for _, line := range lines { 172 | if strings.Contains(line, "Server version:") { 173 | return strings.TrimSpace(strings.Split(line, ":")[1]), nil 174 | } 175 | } 176 | } 177 | } 178 | return "9.0.27", nil // 已知版本 179 | } 180 | 181 | // 检查通用tomcat目录 182 | versionScript := filepath.Join(tomcatPath, "bin", "version.bat") 183 | if FileExists(versionScript) { 184 | output, err := ce.ExecuteCommand(versionScript) 185 | if err == nil { 186 | lines := strings.Split(output, "\n") 187 | for _, line := range lines { 188 | if strings.Contains(line, "Server version:") { 189 | return strings.TrimSpace(strings.Split(line, ":")[1]), nil 190 | } 191 | } 192 | } 193 | } 194 | 195 | return "", fmt.Errorf("无法获取Tomcat版本信息") 196 | } 197 | 198 | // CheckToolAvailability 检查工具是否可用 199 | func (ce *CommandExecutor) CheckToolAvailability(envVar, toolSubPath, executableName string) bool { 200 | _, err := ce.GetExecutablePath(envVar, toolSubPath, executableName) 201 | return err == nil 202 | } 203 | -------------------------------------------------------------------------------- /Common/Config.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | var IsInstall bool 4 | var CreateJar string 5 | var DecompilerType string 6 | var UseGoroutine bool 7 | var MaxGoroutines int 8 | var KeepTempFiles bool 9 | var CodeQLThreads int 10 | 11 | // 用户指定的下载URL 12 | var JDKDownloadURL string 13 | var AntDownloadURL string 14 | var CodeQLDownloadURL string 15 | 16 | // 扫描相关配置 17 | var ScanMode bool 18 | var DatabasePath string 19 | var QLLibsPath string 20 | 21 | // 保持向后兼容的旧变量名 22 | var ScanDirectory string 23 | 24 | // 额外源码目录配置 25 | var ExtraSourceDir string 26 | 27 | // 清理缓存配置 28 | var CleanCache bool 29 | 30 | // 新增:依赖选择模式(none|all;为空表示交互选择) 31 | var DependencySelection string 32 | -------------------------------------------------------------------------------- /Common/Environment.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "runtime" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/fatih/color" 12 | ) 13 | 14 | // SetupEnvironment 设置所有工具的环境变量 15 | func SetupEnvironment() error { 16 | fmt.Println("正在设置环境变量...") 17 | 18 | // 获取当前工作目录 19 | cwd, err := os.Getwd() 20 | if err != nil { 21 | return fmt.Errorf("获取当前目录失败: %v", err) 22 | } 23 | 24 | toolsDir := filepath.Join(cwd, "tools") 25 | 26 | // 设置JDK环境变量 27 | if err := setupJDKEnvironment(toolsDir); err != nil { 28 | fmt.Printf("设置JDK环境变量失败: %v\n", err) 29 | } 30 | 31 | // 设置CodeQL环境变量 32 | if err := setupCodeQLEnvironment(toolsDir); err != nil { 33 | fmt.Printf("设置CodeQL环境变量失败: %v\n", err) 34 | } 35 | 36 | // 设置Ant环境变量 37 | if err := setupAntEnvironment(toolsDir); err != nil { 38 | fmt.Printf("设置Ant环境变量失败: %v\n", err) 39 | } 40 | 41 | // 设置Tomcat环境变量 42 | if err := setupTomcatEnvironment(toolsDir); err != nil { 43 | fmt.Printf("设置Tomcat环境变量失败: %v\n", err) 44 | } 45 | 46 | fmt.Println("环境变量设置完成") 47 | return nil 48 | } 49 | 50 | // setupJDKEnvironment 设置JDK环境变量 51 | func setupJDKEnvironment(toolsDir string) error { 52 | jdkPath := filepath.Join(toolsDir, "jdk") 53 | if _, err := os.Stat(jdkPath); err != nil { 54 | return fmt.Errorf("JDK未安装") 55 | } 56 | 57 | // 设置JAVA_HOME 58 | if err := os.Setenv("JAVA_HOME", jdkPath); err != nil { 59 | return fmt.Errorf("设置JAVA_HOME失败: %v", err) 60 | } 61 | // 设置JRE_HOME 62 | if err := os.Setenv("JRE_HOME", jdkPath); err != nil { 63 | return fmt.Errorf("设置JRE_HOME失败: %v", err) 64 | } 65 | // 添加到PATH 66 | jdkBinPath := filepath.Join(jdkPath, "bin") 67 | if err := addToPath(jdkBinPath); err != nil { 68 | return fmt.Errorf("添加JDK到PATH失败: %v", err) 69 | } 70 | 71 | fmt.Printf("JDK环境变量设置完成: JAVA_HOME=%s\n", jdkPath) 72 | return nil 73 | } 74 | 75 | // setupCodeQLEnvironment 设置CodeQL环境变量 76 | func setupCodeQLEnvironment(toolsDir string) error { 77 | codeqlPath := filepath.Join(toolsDir, "codeql") 78 | if _, err := os.Stat(codeqlPath); err != nil { 79 | return fmt.Errorf("CodeQL未安装") 80 | } 81 | 82 | // 添加到PATH 83 | if err := addToPath(codeqlPath); err != nil { 84 | return fmt.Errorf("添加CodeQL到PATH失败: %v", err) 85 | } 86 | 87 | // 获取系统内存并计算一半 88 | memoryGB := getHalfSystemMemoryGB() 89 | color.Green("获取内存", memoryGB, "\n") 90 | jvmArgs := fmt.Sprintf("-Xmx%dg -Xms%dg", memoryGB, memoryGB) 91 | 92 | // 设置SEMMLE_JAVA_EXTRACTOR_JVM_ARGS 93 | if err := os.Setenv("SEMMLE_JAVA_EXTRACTOR_JVM_ARGS", jvmArgs); err != nil { 94 | return fmt.Errorf("设置SEMMLE_JAVA_EXTRACTOR_JVM_ARGS失败: %v", err) 95 | } 96 | 97 | fmt.Printf("CodeQL环境变量设置完成: PATH中已添加 %s\n", codeqlPath) 98 | fmt.Printf("SEMMLE_JAVA_EXTRACTOR_JVM_ARGS已设置为: %s\n", jvmArgs) 99 | return nil 100 | } 101 | 102 | // setupAntEnvironment 设置Ant环境变量 103 | func setupAntEnvironment(toolsDir string) error { 104 | antPath := filepath.Join(toolsDir, "ant") 105 | if _, err := os.Stat(antPath); err != nil { 106 | return fmt.Errorf("Apache Ant未安装") 107 | } 108 | 109 | // 设置ANT_HOME 110 | if err := os.Setenv("ANT_HOME", antPath); err != nil { 111 | return fmt.Errorf("设置ANT_HOME失败: %v", err) 112 | } 113 | 114 | // 添加到PATH 115 | antBinPath := filepath.Join(antPath, "bin") 116 | if err := addToPath(antBinPath); err != nil { 117 | return fmt.Errorf("添加Ant到PATH失败: %v", err) 118 | } 119 | 120 | fmt.Printf("Ant环境变量设置完成: ANT_HOME=%s\n", antPath) 121 | return nil 122 | } 123 | 124 | // setupTomcatEnvironment 设置Tomcat环境变量 125 | func setupTomcatEnvironment(toolsDir string) error { 126 | tomcatPath := filepath.Join(toolsDir, "tomcat") 127 | if _, err := os.Stat(tomcatPath); err != nil { 128 | return fmt.Errorf("Apache Tomcat未安装") 129 | } 130 | 131 | // 检查是否有apache-tomcat-9.0.27目录 132 | tomcatVersionPath := filepath.Join(tomcatPath, "apache-tomcat-9.0.27") 133 | if _, err := os.Stat(tomcatVersionPath); err == nil { 134 | // 使用具体版本目录作为CATALINA_HOME 135 | if err := os.Setenv("CATALINA_HOME", tomcatVersionPath); err != nil { 136 | return fmt.Errorf("设置CATALINA_HOME失败: %v", err) 137 | } 138 | fmt.Printf("Tomcat环境变量设置完成: CATALINA_HOME=%s\n", tomcatVersionPath) 139 | } else { 140 | // 使用tomcat目录作为CATALINA_HOME 141 | if err := os.Setenv("CATALINA_HOME", tomcatPath); err != nil { 142 | return fmt.Errorf("设置CATALINA_HOME失败: %v", err) 143 | } 144 | fmt.Printf("Tomcat环境变量设置完成: CATALINA_HOME=%s\n", tomcatPath) 145 | } 146 | 147 | // 添加Tomcat bin目录到PATH 148 | catalinaHome := os.Getenv("CATALINA_HOME") 149 | tomcatBinPath := filepath.Join(catalinaHome, "bin") 150 | if _, err := os.Stat(tomcatBinPath); err == nil { 151 | if err := addToPath(tomcatBinPath); err != nil { 152 | return fmt.Errorf("添加Tomcat到PATH失败: %v", err) 153 | } 154 | } 155 | 156 | return nil 157 | } 158 | 159 | // addToPath 将路径添加到PATH环境变量 160 | func addToPath(newPath string) error { 161 | currentPath := os.Getenv("PATH") 162 | 163 | // 检查路径是否已存在 164 | var pathSeparator string 165 | if runtime.GOOS == "windows" { 166 | pathSeparator = ";" 167 | } else { 168 | pathSeparator = ":" 169 | } 170 | 171 | paths := strings.Split(currentPath, pathSeparator) 172 | for _, path := range paths { 173 | if strings.EqualFold(strings.TrimSpace(path), newPath) { 174 | // 路径已存在 175 | return nil 176 | } 177 | } 178 | 179 | // 添加新路径到PATH前面 180 | newPathValue := newPath + pathSeparator + currentPath 181 | return os.Setenv("PATH", newPathValue) 182 | } 183 | 184 | // GetToolVersions 获取工具版本信息 185 | func GetToolVersions() map[string]string { 186 | versions := make(map[string]string) 187 | 188 | cwd, err := os.Getwd() 189 | if err != nil { 190 | return versions 191 | } 192 | 193 | toolsDir := filepath.Join(cwd, "tools") 194 | executor := NewCommandExecutor(toolsDir) 195 | 196 | // 检查JDK并获取版本 197 | if executor.CheckToolAvailability("JAVA_HOME", "jdk", "java.exe") { 198 | if version, err := executor.GetJavaVersion(); err == nil { 199 | versions["JDK"] = version 200 | } else { 201 | versions["JDK"] = "已安装 (版本获取失败)" 202 | } 203 | } else { 204 | versions["JDK"] = "未安装" 205 | } 206 | 207 | // 检查CodeQL并获取版本 208 | if executor.CheckToolAvailability("CODEQL_HOME", "codeql", "codeql.exe") { 209 | if version, err := executor.GetCodeQLVersion(); err == nil { 210 | versions["CodeQL"] = version 211 | } else { 212 | versions["CodeQL"] = "已安装 (版本获取失败)" 213 | } 214 | } else { 215 | versions["CodeQL"] = "未安装" 216 | } 217 | 218 | // 检查Ant并获取版本 219 | if executor.CheckToolAvailability("ANT_HOME", "ant", "ant.bat") { 220 | if version, err := executor.GetAntVersion(); err == nil { 221 | versions["Ant"] = version 222 | } else { 223 | versions["Ant"] = "已安装 (版本获取失败)" 224 | } 225 | } else { 226 | versions["Ant"] = "未安装" 227 | } 228 | 229 | // 检查Procyon并获取版本 230 | if version, err := executor.GetProcyonVersion(); err == nil { 231 | versions["Procyon"] = version 232 | } else { 233 | versions["Procyon"] = "未安装" 234 | } 235 | 236 | // 检查Tomcat并获取版本 237 | if version, err := executor.GetTomcatVersion(); err == nil { 238 | versions["Tomcat"] = version 239 | } else { 240 | versions["Tomcat"] = "未安装" 241 | } 242 | 243 | return versions 244 | } 245 | 246 | // PrintToolVersions 打印所有工具的版本信息 247 | func PrintToolVersions() { 248 | fmt.Println("\n=== 工具版本信息 ===") 249 | versions := GetToolVersions() 250 | 251 | for tool, version := range versions { 252 | fmt.Printf("%s: %s\n", tool, version) 253 | } 254 | fmt.Println("===================") 255 | } 256 | 257 | // getHalfSystemMemoryGB 获取系统内存的一半(以GB为单位) 258 | func getHalfSystemMemoryGB() int { 259 | // 获取系统总内存(字节) 260 | var m runtime.MemStats 261 | runtime.ReadMemStats(&m) 262 | 263 | // 使用runtime包获取系统内存信息 264 | // 注意:runtime.MemStats主要用于Go程序的内存统计,这里我们使用一个更直接的方法 265 | // 在Windows上,我们可以通过执行系统命令来获取内存信息 266 | if runtime.GOOS == "windows" { 267 | return getWindowsMemoryGB() / 2 268 | } 269 | 270 | // 对于其他系统,使用默认值 271 | return 16 // 默认16GB 272 | } 273 | 274 | // getWindowsMemoryGB 获取Windows系统的总内存(GB) 275 | func getWindowsMemoryGB() int { 276 | // 使用wmic命令获取总物理内存 277 | cmd := fmt.Sprintf("wmic computersystem get TotalPhysicalMemory /value") 278 | exec := NewCommandExecutor(".") 279 | output, err := exec.ExecuteCommand("cmd", "/c", cmd) 280 | if err != nil { 281 | fmt.Printf("获取系统内存失败,使用默认值16GB: %v\n", err) 282 | return 16 283 | } 284 | 285 | // 解析输出 286 | lines := strings.Split(output, "\n") 287 | for _, line := range lines { 288 | if strings.Contains(line, "TotalPhysicalMemory=") { 289 | parts := strings.Split(line, "=") 290 | if len(parts) == 2 { 291 | memoryBytes := strings.TrimSpace(parts[1]) 292 | if memoryInt, err := strconv.ParseInt(memoryBytes, 10, 64); err == nil { 293 | // 转换为GB(1GB = 1024^3 bytes) 294 | memoryGB := int(memoryInt / (1024 * 1024 * 1024)) 295 | if memoryGB > 0 { 296 | return memoryGB 297 | } 298 | } 299 | } 300 | } 301 | } 302 | 303 | // 如果解析失败,返回默认值 304 | fmt.Println("解析系统内存失败,使用默认值32GB") 305 | return 32 306 | } 307 | -------------------------------------------------------------------------------- /Common/Flag.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func InitFlag() { 10 | // 主要功能参数 11 | flag.BoolVar(&IsInstall, "install", false, "一键安装环境") 12 | flag.StringVar(&CreateJar, "database", "", "通过jar包一键生成数据库") 13 | flag.BoolVar(&ScanMode, "scan", false, "启用扫描模式") 14 | 15 | // 安装模式专用参数(只能与-install一起使用) 16 | flag.StringVar(&JDKDownloadURL, "jdk", "", "指定JDK下载地址(仅限-install模式)") 17 | flag.StringVar(&AntDownloadURL, "ant", "", "指定Apache Ant下载地址(仅限-install模式)") 18 | flag.StringVar(&CodeQLDownloadURL, "codeql", "", "指定CodeQL下载地址(仅限-install模式)") 19 | 20 | // 扫描模式专用参数(只能与-scan一起使用) 21 | flag.StringVar(&DatabasePath, "db", "./lib", "指定CodeQL数据库路径(仅限-scan模式)") 22 | flag.StringVar(&QLLibsPath, "ql", "./qlLibs", "指定QL查询库路径(仅限-scan模式)") 23 | flag.BoolVar(&CleanCache, "clean-cache", false, "扫描前清理缓存,确保修改的QL文件生效(仅限-scan模式)") 24 | 25 | // 保持向后兼容 26 | flag.StringVar(&ScanDirectory, "d", "", "【已弃用】指定要扫描的目录,请使用-db和-ql参数") 27 | 28 | // 数据库模式专用参数(只能与-database一起使用) 29 | flag.StringVar(&ExtraSourceDir, "dir", "", "指定额外的源码目录,将复制到src1中一起生成数据库(仅限-database模式)") 30 | // 新增:控制依赖选择模式(none=空依赖, all=全依赖;不指定则进入交互选择) 31 | flag.StringVar(&DependencySelection, "deps", "", "数据库生成时依赖选择:none=空依赖, all=全依赖;不指定进入交互选择(仅限-database模式)") 32 | 33 | // 通用配置参数 34 | flag.StringVar(&DecompilerType, "decompiler", "procyon", "选择反编译器类型 (procyon|fernflower)") 35 | flag.BoolVar(&UseGoroutine, "goroutine", false, "启用goroutine并发处理") 36 | flag.IntVar(&MaxGoroutines, "max-goroutines", 4, "最大goroutine数量(需要-goroutine)") 37 | flag.BoolVar(&KeepTempFiles, "keep-temp", false, "保留临时文件和目录") 38 | flag.IntVar(&CodeQLThreads, "threads", 20, "CodeQL处理时的线程数") 39 | 40 | // 自定义help信息 41 | flag.Usage = printUsage 42 | 43 | flag.Parse() 44 | } 45 | 46 | // printUsage 自定义使用说明 47 | func printUsage() { 48 | fmt.Println("Usage: codeql_n1ght [options]") 49 | fmt.Println("\n主要功能:") 50 | fmt.Println(" -install 一键安装环境") 51 | fmt.Println(" -database 通过jar包一键生成数据库") 52 | fmt.Println(" -scan 启用扫描模式") 53 | 54 | fmt.Println("\n数据库模式参数(仅与 -database 一起使用):") 55 | fmt.Println(" -dir 指定额外源码目录,复制到src1中一起生成数据库") 56 | fmt.Println(" -deps 依赖选择:none=空依赖, all=全依赖;不指定进入交互选择") 57 | 58 | fmt.Println("\n扫描模式参数(仅与 -scan 一起使用):") 59 | fmt.Println(" -db 指定CodeQL数据库路径") 60 | fmt.Println(" -ql 指定QL查询库路径") 61 | fmt.Println(" -clean-cache 扫描前清理缓存,确保修改的QL文件生效") 62 | 63 | fmt.Println("\n安装模式参数(仅与 -install 一起使用):") 64 | fmt.Println(" -jdk 指定JDK下载地址") 65 | fmt.Println(" -ant 指定Apache Ant下载地址") 66 | fmt.Println(" -codeql 指定CodeQL下载地址") 67 | 68 | fmt.Println("\n通用配置:") 69 | fmt.Println(" -decompiler 选择反编译器类型 (procyon|fernflower)") 70 | fmt.Println(" -goroutine 启用goroutine并发处理") 71 | fmt.Println(" -max-goroutines 最大goroutine数量(需要-goroutine)") 72 | fmt.Println(" -keep-temp 保留临时文件和目录") 73 | fmt.Println(" -threads CodeQL处理时的线程数") 74 | 75 | fmt.Println("\n示例:") 76 | fmt.Println(" codeql_n1ght -database app.jar -deps none") 77 | fmt.Println(" codeql_n1ght -database app.jar -deps all") 78 | os.Exit(0) 79 | } 80 | -------------------------------------------------------------------------------- /Common/Start.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/common-nighthawk/go-figure" 8 | "github.com/fatih/color" 9 | ) 10 | 11 | func Start() { 12 | // 1. 生成艺术字 13 | myFigure := figure.NewFigure("codeql_n1ght", "", true) 14 | asciiArt := myFigure.String() 15 | 16 | // 2. 拆分行 17 | lines := strings.Split(asciiArt, "\n") 18 | 19 | // 3. 计算最长行长度 20 | maxLen := 0 21 | for _, line := range lines { 22 | if len(line) > maxLen { 23 | maxLen = len(line) 24 | } 25 | } 26 | 27 | // 4. 定义颜色(标准蓝色) 28 | blue := color.New(color.FgBlue).SprintFunc() 29 | 30 | // 5. 打印上边框 31 | fmt.Println(strings.Repeat("-", maxLen+4)) 32 | 33 | // 6. 打印正文带边框和颜色 34 | for _, line := range lines { 35 | fmt.Printf("| %s%s |\n", blue(line), strings.Repeat(" ", maxLen-len(line))) 36 | } 37 | 38 | // 7. 打印下边框 39 | fmt.Println(strings.Repeat("-", maxLen+4)) 40 | } 41 | -------------------------------------------------------------------------------- /Common/Utils.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | import ( 4 | "archive/zip" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net/http" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/fatih/color" 14 | "github.com/schollz/progressbar/v3" 15 | ) 16 | 17 | // LogLevel 日志级别 18 | type LogLevel int 19 | 20 | const ( 21 | LogLevelInfo LogLevel = iota 22 | LogLevelWarn 23 | LogLevelError 24 | ) 25 | 26 | // LogMessage 统一的日志输出函数 27 | func LogMessage(level LogLevel, format string, args ...interface{}) { 28 | switch level { 29 | case LogLevelInfo: 30 | color.White(format, args...) 31 | case LogLevelWarn: 32 | color.Yellow(format, args...) 33 | case LogLevelError: 34 | color.Red(format, args...) 35 | } 36 | } 37 | 38 | // LogError 记录错误信息 39 | func LogError(format string, args ...interface{}) { 40 | LogMessage(LogLevelError, format, args...) 41 | } 42 | 43 | // LogWarn 记录警告信息 44 | func LogWarn(format string, args ...interface{}) { 45 | LogMessage(LogLevelWarn, format, args...) 46 | } 47 | 48 | // LogInfo 记录信息 49 | func LogInfo(format string, args ...interface{}) { 50 | LogMessage(LogLevelInfo, format, args...) 51 | } 52 | 53 | // ValidateFile 验证文件是否存在且可读 54 | func ValidateFile(filePath string) error { 55 | if !FileExists(filePath) { 56 | return fmt.Errorf("文件不存在: %s", filePath) 57 | } 58 | 59 | file, err := os.Open(filePath) 60 | if err != nil { 61 | return fmt.Errorf("无法打开文件: %v", err) 62 | } 63 | defer file.Close() 64 | 65 | return nil 66 | } 67 | 68 | // IsDirectory 检查路径是否为目录 69 | func IsDirectory(path string) bool { 70 | info, err := os.Stat(path) 71 | if err != nil { 72 | return false 73 | } 74 | return info.IsDir() 75 | } 76 | 77 | // DownloadFile 下载文件的通用函数(带进度条) 78 | func DownloadFile(url, filepath string) error { 79 | resp, err := http.Get(url) 80 | if err != nil { 81 | return err 82 | } 83 | defer resp.Body.Close() 84 | 85 | if resp.StatusCode != http.StatusOK { 86 | return fmt.Errorf("下载失败,状态码: %d", resp.StatusCode) 87 | } 88 | 89 | out, err := os.Create(filepath) 90 | if err != nil { 91 | return err 92 | } 93 | defer out.Close() 94 | 95 | // 获取文件大小和文件名 96 | fileSize := resp.ContentLength 97 | filename := filepath[strings.LastIndex(filepath, "\\")+1:] 98 | 99 | if fileSize > 0 { 100 | // 使用第三方进度条库显示下载进度 101 | bar := progressbar.DefaultBytes( 102 | fileSize, 103 | fmt.Sprintf("下载 %s", filename), 104 | ) 105 | _, err = io.Copy(io.MultiWriter(out, bar), resp.Body) 106 | } else { 107 | // 如果无法获取文件大小,使用spinner模式 108 | bar := progressbar.DefaultBytes(-1, fmt.Sprintf("下载 %s", filename)) 109 | _, err = io.Copy(io.MultiWriter(out, bar), resp.Body) 110 | } 111 | 112 | return err 113 | } 114 | 115 | // ExtractZip 解压ZIP文件的通用函数,完全还原原始结构 116 | func ExtractZip(src, dest string) error { 117 | r, err := zip.OpenReader(src) 118 | if err != nil { 119 | return err 120 | } 121 | defer r.Close() 122 | 123 | os.MkdirAll(dest, 0755) 124 | 125 | for _, f := range r.File { 126 | rc, err := f.Open() 127 | if err != nil { 128 | return err 129 | } 130 | defer rc.Close() 131 | 132 | // 完全保留原始路径结构,不移除顶层目录 133 | path := f.Name 134 | fpath := filepath.Join(dest, path) 135 | 136 | if f.FileInfo().IsDir() { 137 | os.MkdirAll(fpath, f.FileInfo().Mode()) 138 | continue 139 | } 140 | 141 | if err := os.MkdirAll(filepath.Dir(fpath), 0755); err != nil { 142 | return err 143 | } 144 | 145 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.FileInfo().Mode()) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | _, err = io.Copy(outFile, rc) 151 | outFile.Close() 152 | 153 | if err != nil { 154 | return err 155 | } 156 | } 157 | return nil 158 | } 159 | 160 | // FileExists 检查文件或目录是否存在 161 | func FileExists(path string) bool { 162 | _, err := os.Stat(path) 163 | return err == nil 164 | } 165 | 166 | // CreateDirIfNotExists 如果目录不存在则创建 167 | func CreateDirIfNotExists(dir string) error { 168 | if !FileExists(dir) { 169 | return os.MkdirAll(dir, 0755) 170 | } 171 | return nil 172 | } 173 | 174 | // RemoveFile 安全删除文件 175 | func RemoveFile(path string) error { 176 | if !FileExists(path) { 177 | return nil 178 | } 179 | 180 | fileInfo, err := os.Stat(path) 181 | if err != nil { 182 | return err 183 | } 184 | 185 | if fileInfo.IsDir() { 186 | // 如果是目录,递归删除整个目录 187 | return os.RemoveAll(path) 188 | } else { 189 | // 如果是文件,删除文件 190 | return os.Remove(path) 191 | } 192 | } 193 | 194 | // SafeExecute 安全执行函数,捕获panic 195 | func SafeExecute(fn func() error, errorMsg string) error { 196 | defer func() { 197 | if r := recover(); r != nil { 198 | log.Printf("发生panic: %v", r) 199 | } 200 | }() 201 | 202 | if err := fn(); err != nil { 203 | LogError("%s: %v", errorMsg, err) 204 | return err 205 | } 206 | return nil 207 | } 208 | 209 | // CopyFile 复制单个文件 210 | func CopyFile(src, dst string) error { 211 | srcFile, err := os.Open(src) 212 | if err != nil { 213 | return err 214 | } 215 | defer srcFile.Close() 216 | 217 | // 确保目标目录存在 218 | if err := CreateDirIfNotExists(filepath.Dir(dst)); err != nil { 219 | return err 220 | } 221 | 222 | dstFile, err := os.Create(dst) 223 | if err != nil { 224 | return err 225 | } 226 | defer dstFile.Close() 227 | 228 | _, err = io.Copy(dstFile, srcFile) 229 | if err != nil { 230 | return err 231 | } 232 | 233 | // 复制文件权限 234 | srcInfo, err := os.Stat(src) 235 | if err != nil { 236 | return err 237 | } 238 | return os.Chmod(dst, srcInfo.Mode()) 239 | } 240 | 241 | // CopyDirectory 递归复制整个目录 242 | func CopyDirectory(src, dst string) error { 243 | srcInfo, err := os.Stat(src) 244 | if err != nil { 245 | return err 246 | } 247 | 248 | if !srcInfo.IsDir() { 249 | return fmt.Errorf("源路径不是目录: %s", src) 250 | } 251 | 252 | // 创建目标目录 253 | if err := CreateDirIfNotExists(dst); err != nil { 254 | return err 255 | } 256 | 257 | entries, err := os.ReadDir(src) 258 | if err != nil { 259 | return err 260 | } 261 | 262 | for _, entry := range entries { 263 | srcPath := filepath.Join(src, entry.Name()) 264 | dstPath := filepath.Join(dst, entry.Name()) 265 | 266 | if entry.IsDir() { 267 | // 递归复制子目录 268 | if err := CopyDirectory(srcPath, dstPath); err != nil { 269 | return err 270 | } 271 | } else { 272 | // 复制文件 273 | if err := CopyFile(srcPath, dstPath); err != nil { 274 | return err 275 | } 276 | } 277 | } 278 | 279 | return nil 280 | } 281 | 282 | // CopyExtraSourceToSrc1 将额外源码目录复制到src1目录 283 | func CopyExtraSourceToSrc1(extraSourceDir, src1Dir string) error { 284 | if extraSourceDir == "" { 285 | return nil // 没有指定额外源码目录 286 | } 287 | 288 | LogInfo("开始复制额外源码目录: %s -> %s", extraSourceDir, src1Dir) 289 | 290 | // 复制所有文件和子目录到src1 291 | err := CopyDirectory(extraSourceDir, src1Dir) 292 | if err != nil { 293 | return fmt.Errorf("复制额外源码失败: %v", err) 294 | } 295 | 296 | LogInfo("额外源码复制完成") 297 | return nil 298 | } 299 | -------------------------------------------------------------------------------- /Database/Builder.go: -------------------------------------------------------------------------------- 1 | package Database 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strconv" 9 | 10 | "codeql_n1ght/Common" 11 | ) 12 | 13 | // Createdatabase 创建CodeQL数据库 14 | func Createdatabase(location string) { 15 | Common.SetupEnvironment() 16 | cmd := exec.Command( 17 | "codeql", 18 | "database", "create", "temp", 19 | "--language=java", 20 | "--command=ant -f build.xml", 21 | "--source-root", "./", 22 | "--overwrite", 23 | "--ram=51200", 24 | "--threads="+strconv.Itoa(Common.CodeQLThreads), 25 | ) 26 | cmd.Dir = location 27 | // 获取标准输出管道 28 | stdout, err := cmd.StdoutPipe() 29 | if err != nil { 30 | fmt.Println("获取 StdoutPipe 失败:", err) 31 | return 32 | } 33 | // 获取标准错误管道(可选) 34 | stderr, err := cmd.StderrPipe() 35 | if err != nil { 36 | fmt.Println("获取 StderrPipe 失败:", err) 37 | return 38 | } 39 | // 启动命令 40 | if err := cmd.Start(); err != nil { 41 | fmt.Println("启动命令失败:", err) 42 | return 43 | } 44 | // 创建协程并发读取标准输出 45 | go streamOutput(stdout, "STDOUT") 46 | // 创建协程并发读取标准错误 47 | go streamOutput(stderr, "STDERR") 48 | // 等待命令结束 49 | if err := cmd.Wait(); err != nil { 50 | fmt.Println("命令执行异常:", err) 51 | } 52 | } 53 | 54 | // GenerateBuildXML 生成Ant构建文件 55 | func GenerateBuildXML(location string) error { 56 | buildxml := fmt.Sprintf(` 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | `, os.Getenv("CATALINA_HOME")) 80 | 81 | f, err := os.OpenFile(filepath.Join(location, "build.xml"), os.O_CREATE|os.O_WRONLY, 0644) 82 | if err != nil { 83 | return fmt.Errorf("create build.xml failed: %v", err) 84 | } 85 | defer f.Close() 86 | 87 | _, err = f.Write([]byte(buildxml)) 88 | return err 89 | } 90 | -------------------------------------------------------------------------------- /Database/Decompile.go: -------------------------------------------------------------------------------- 1 | package Database 2 | 3 | import ( 4 | "archive/zip" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | // UnzipJar 解压JAR文件到指定目录 12 | func UnzipJar(src, dest string) error { 13 | r, err := zip.OpenReader(src) 14 | if err != nil { 15 | return err 16 | } 17 | defer r.Close() 18 | 19 | // 创建目标目录 20 | os.MkdirAll(dest, 0755) 21 | 22 | // 解压文件 23 | for _, f := range r.File { 24 | path := filepath.Join(dest, f.Name) 25 | 26 | if f.FileInfo().IsDir() { 27 | os.MkdirAll(path, f.FileInfo().Mode()) 28 | continue 29 | } 30 | 31 | // 创建文件目录 32 | if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { 33 | return err 34 | } 35 | 36 | // 打开压缩文件中的文件 37 | rc, err := f.Open() 38 | if err != nil { 39 | return err 40 | } 41 | 42 | // 创建目标文件 43 | outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.FileInfo().Mode()) 44 | if err != nil { 45 | rc.Close() 46 | return err 47 | } 48 | 49 | // 复制文件内容 50 | _, err = io.Copy(outFile, rc) 51 | outFile.Close() 52 | rc.Close() 53 | 54 | if err != nil { 55 | return err 56 | } 57 | } 58 | 59 | fmt.Printf("Successfully extracted %s to %s\n", src, dest) 60 | return nil 61 | } -------------------------------------------------------------------------------- /Database/Decompiler.go: -------------------------------------------------------------------------------- 1 | package Database 2 | 3 | import ( 4 | "archive/zip" 5 | "bufio" 6 | "codeql_n1ght/Common" 7 | "fmt" 8 | "io" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "runtime" 13 | "strings" 14 | "sync" 15 | 16 | "github.com/AlecAivazis/survey/v2" 17 | "github.com/fatih/color" 18 | ) 19 | 20 | // DecompileJava 反编译Java文件 21 | func DecompileJava(args ...string) error { 22 | cmd := exec.Command("java", args...) 23 | // 获取标准输出管道 24 | stdout, err := cmd.StdoutPipe() 25 | if err != nil { 26 | return fmt.Errorf("获取 StdoutPipe 失败: %v", err) 27 | } 28 | // 获取标准错误管道(可选) 29 | stderr, err := cmd.StderrPipe() 30 | if err != nil { 31 | return fmt.Errorf("获取 StderrPipe 失败: %v", err) 32 | } 33 | // 启动命令 34 | if err := cmd.Start(); err != nil { 35 | return fmt.Errorf("启动命令失败: %v", err) 36 | } 37 | // 创建协程并发读取标准输出 38 | go streamOutput(stdout, "STDOUT") 39 | // 创建协程并发读取标准错误 40 | go streamOutput(stderr, "STDERR") 41 | // 等待命令结束 42 | if err := cmd.Wait(); err != nil { 43 | return fmt.Errorf("命令执行异常: %v", err) 44 | } 45 | return nil 46 | } 47 | 48 | // copyDir 复制目录 49 | func copyDir(src, dst string) error { 50 | return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { 51 | if err != nil { 52 | return err 53 | } 54 | 55 | // 计算目标路径 56 | relPath, err := filepath.Rel(src, path) 57 | if err != nil { 58 | return err 59 | } 60 | dstPath := filepath.Join(dst, relPath) 61 | 62 | if info.IsDir() { 63 | // 创建目录 64 | return os.MkdirAll(dstPath, info.Mode()) 65 | } else { 66 | // 复制文件 67 | return copyFile(path, dstPath) 68 | } 69 | }) 70 | } 71 | 72 | // copyFile 复制文件 73 | func copyFile(src, dst string) error { 74 | // 确保目标目录存在 75 | err := os.MkdirAll(filepath.Dir(dst), 0755) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | // 打开源文件 81 | srcFile, err := os.Open(src) 82 | if err != nil { 83 | return err 84 | } 85 | defer srcFile.Close() 86 | 87 | // 创建目标文件 88 | dstFile, err := os.Create(dst) 89 | if err != nil { 90 | return err 91 | } 92 | defer dstFile.Close() 93 | 94 | // 复制文件内容 95 | _, err = io.Copy(dstFile, srcFile) 96 | return err 97 | } 98 | 99 | 100 | 101 | // DecompileLibraries 反编译依赖库,允许用户选择 102 | func DecompileLibraries(location string) { 103 | // 优先检查BOOT-INF/lib目录(Spring Boot结构) 104 | libDir := filepath.Join(location, "output", "BOOT-INF", "lib") 105 | 106 | // 如果BOOT-INF/lib不存在,检查传统的WEB-INF/lib目录 107 | if _, err := os.Stat(libDir); os.IsNotExist(err) { 108 | libDir = filepath.Join(location, "output", "WEB-INF", "lib") 109 | if _, err := os.Stat(libDir); os.IsNotExist(err) { 110 | // 最后检查根目录下的lib目录 111 | libDir = filepath.Join(location, "output", "lib") 112 | if _, err := os.Stat(libDir); os.IsNotExist(err) { 113 | fmt.Println("No lib directory found (checked BOOT-INF/lib, WEB-INF/lib, and lib), skipping jar decompilation.") 114 | return 115 | } 116 | } 117 | } 118 | 119 | fmt.Printf("Using lib directory: %s\n", libDir) 120 | 121 | // 查找所有.jar文件 122 | jarFiles, err := filepath.Glob(filepath.Join(libDir, "*.jar")) 123 | if err != nil { 124 | fmt.Printf("Error searching for jar files: %v\n", err) 125 | return 126 | } 127 | 128 | if len(jarFiles) == 0 { 129 | fmt.Println("No jar files found in lib directory.") 130 | return 131 | } 132 | 133 | // 准备选项列表(只显示文件名) 134 | options := make([]string, len(jarFiles)) 135 | for i, jarFile := range jarFiles { 136 | options[i] = filepath.Base(jarFile) 137 | } 138 | 139 | // 清空当前输出 140 | // clearScreen() 141 | 142 | // 使用survey的MultiSelect进行交互式选择或根据 -deps 自动选择 143 | var selectedFiles []string 144 | mode := strings.ToLower(Common.DependencySelection) 145 | if mode == "none" { 146 | fmt.Println("Dependency selection set to 'none'; skipping jar decompilation.") 147 | return 148 | } else if mode == "all" { 149 | selectedFiles = options 150 | fmt.Printf("Auto-selected all %d jar files for decompilation.\n", len(selectedFiles)) 151 | } else { 152 | prompt := &survey.MultiSelect{ 153 | Message: "Select jar files to decompile (use arrow keys to navigate, space to select/deselect, enter to confirm):", 154 | Options: options, 155 | PageSize: 40, // 每页显示40个选项 156 | } 157 | 158 | err = survey.AskOne(prompt, &selectedFiles) 159 | if err != nil { 160 | fmt.Printf("Error during selection: %v\n", err) 161 | return 162 | } 163 | 164 | if len(selectedFiles) == 0 { 165 | fmt.Println("No files selected, skipping jar decompilation.") 166 | return 167 | } 168 | } 169 | 170 | // 反编译选中的文件 171 | fmt.Printf("\nDecompiling %d selected jar files...\n", len(selectedFiles)) 172 | 173 | if Common.UseGoroutine { 174 | // 使用goroutine并发反编译 175 | decompileWithGoroutines(selectedFiles, jarFiles, location) 176 | } else { 177 | // 串行反编译 178 | for _, selectedFile := range selectedFiles { 179 | // 找到完整路径 180 | for _, jarFile := range jarFiles { 181 | if filepath.Base(jarFile) == selectedFile { 182 | fmt.Printf("Decompiling %s...\n", selectedFile) 183 | outputDir := filepath.Join(location, "createdabase", "src1") 184 | decompileJarFile(jarFile, outputDir, selectedFile) 185 | break 186 | } 187 | } 188 | } 189 | } 190 | fmt.Println("Jar decompilation completed.") 191 | } 192 | 193 | // 实时流式打印输出 194 | func streamOutput(reader io.ReadCloser, prefix string) { 195 | scanner := bufio.NewScanner(reader) 196 | for scanner.Scan() { 197 | fmt.Printf("[%s] %s\n", prefix, scanner.Text()) 198 | } 199 | if err := scanner.Err(); err != nil { 200 | fmt.Printf("[%s] 读取出错: %v\n", prefix, err) 201 | } 202 | } 203 | 204 | // clearScreen 清空控制台屏幕 205 | func clearScreen() { 206 | var cmd *exec.Cmd 207 | switch runtime.GOOS { 208 | case "windows": 209 | cmd = exec.Command("cmd", "/c", "cls") 210 | default: // linux, darwin, etc. 211 | cmd = exec.Command("clear") 212 | } 213 | cmd.Stdout = os.Stdout 214 | cmd.Run() 215 | } 216 | 217 | // decompileWithProcyon 使用Procyon反编译器 218 | func decompileWithProcyon(jarFile, outputDir string) error { 219 | return DecompileJava("-jar", "tools/procyon-decompiler-0.6.0.jar", jarFile, "-o", outputDir) 220 | } 221 | 222 | // decompileWithFernflower 使用Fernflower反编译器 223 | func decompileWithFernflower(jarFile, outputDir string) error { 224 | // 使用fernflower反编译jar文件 225 | err := DecompileJava("-cp", "tools/java-decompiler.jar", 226 | "org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler", 227 | "-dgs=true", 228 | jarFile, outputDir) 229 | if err != nil { 230 | return err 231 | } 232 | 233 | // 反编译完成后,解压生成的jar文件 234 | decompiledJar := filepath.Join(outputDir, filepath.Base(jarFile)) 235 | if _, err := os.Stat(decompiledJar); err == nil { 236 | extractDir := filepath.Join(outputDir) 237 | if err := extractJar(decompiledJar, extractDir); err != nil { 238 | return fmt.Errorf("解压反编译后的jar文件失败: %v", err) 239 | } else { 240 | // 删除反编译生成的jar文件 241 | os.Remove(decompiledJar) 242 | fmt.Printf("反编译和解压完成,源码位于: %s\n", extractDir) 243 | } 244 | } 245 | return nil 246 | } 247 | 248 | // extractJar 解压jar文件 249 | func extractJar(jarFile, destDir string) error { 250 | // 创建目标目录 251 | if err := os.MkdirAll(destDir, 0755); err != nil { 252 | return fmt.Errorf("创建目录失败: %v", err) 253 | } 254 | 255 | // 打开zip文件 256 | r, err := zip.OpenReader(jarFile) 257 | if err != nil { 258 | return fmt.Errorf("打开jar文件失败: %v", err) 259 | } 260 | defer r.Close() 261 | 262 | // 解压文件 263 | for _, f := range r.File { 264 | // 构建完整的文件路径 265 | path := filepath.Join(destDir, f.Name) 266 | 267 | // 确保路径安全 268 | if !strings.HasPrefix(path, filepath.Clean(destDir)+string(os.PathSeparator)) { 269 | continue 270 | } 271 | 272 | if f.FileInfo().IsDir() { 273 | // 创建目录 274 | os.MkdirAll(path, f.FileInfo().Mode()) 275 | continue 276 | } 277 | 278 | // 创建文件的父目录 279 | if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { 280 | return fmt.Errorf("创建父目录失败: %v", err) 281 | } 282 | 283 | // 打开zip文件中的文件 284 | rc, err := f.Open() 285 | if err != nil { 286 | return fmt.Errorf("打开zip文件中的文件失败: %v", err) 287 | } 288 | 289 | // 创建目标文件 290 | outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.FileInfo().Mode()) 291 | if err != nil { 292 | rc.Close() 293 | return fmt.Errorf("创建目标文件失败: %v", err) 294 | } 295 | 296 | // 复制文件内容 297 | _, err = io.Copy(outFile, rc) 298 | rc.Close() 299 | outFile.Close() 300 | 301 | if err != nil { 302 | return fmt.Errorf("复制文件内容失败: %v", err) 303 | } 304 | } 305 | 306 | fmt.Printf("jar文件解压完成: %s\n", destDir) 307 | return nil 308 | } 309 | 310 | // decompileJarFile 反编译单个jar文件 311 | func decompileJarFile(jarFile, outputDir, selectedFile string) { 312 | var err error 313 | // 根据反编译器类型选择不同的反编译方式 314 | switch Common.DecompilerType { 315 | case "fernflower": 316 | err = decompileWithFernflower(jarFile, outputDir) 317 | if err != nil { 318 | color.Red("Fernflower反编译失败: %v,切换到Procyon反编译器\n", err) 319 | err = decompileWithProcyon(jarFile, outputDir) 320 | if err != nil { 321 | color.Red("Procyon反编译也失败: %v\n", err) 322 | } else { 323 | fmt.Printf("使用Procyon反编译器成功完成 %s\n", selectedFile) 324 | } 325 | } 326 | default: // procyon 327 | err = decompileWithProcyon(jarFile, outputDir) 328 | if err != nil { 329 | color.Red("Procyon反编译失败: %v,切换到Fernflower反编译器\n", err) 330 | err = decompileWithFernflower(jarFile, outputDir) 331 | if err != nil { 332 | color.Red("Fernflower反编译也失败: %v\n", err) 333 | } else { 334 | fmt.Printf("使用Fernflower反编译器成功完成 %s\n", selectedFile) 335 | } 336 | } 337 | } 338 | } 339 | 340 | // decompileWithGoroutines 使用goroutine并发反编译 341 | func decompileWithGoroutines(selectedFiles, jarFiles []string, location string) { 342 | // 创建工作队列 343 | type DecompileTask struct { 344 | jarFile string 345 | selectedFile string 346 | outputDir string 347 | } 348 | 349 | tasks := make(chan DecompileTask, len(selectedFiles)) 350 | var wg sync.WaitGroup 351 | 352 | // 启动worker goroutines 353 | maxWorkers := Common.MaxGoroutines 354 | if maxWorkers <= 0 { 355 | maxWorkers = 4 // 默认值 356 | } 357 | 358 | fmt.Printf("启动 %d 个goroutine进行并发反编译...\n", maxWorkers) 359 | 360 | // 启动worker 361 | for i := 0; i < maxWorkers; i++ { 362 | wg.Add(1) 363 | go func(workerID int) { 364 | defer wg.Done() 365 | for task := range tasks { 366 | fmt.Printf("[Worker %d] Decompiling %s...\n", workerID, task.selectedFile) 367 | decompileJarFile(task.jarFile, task.outputDir, task.selectedFile) 368 | fmt.Printf("[Worker %d] Completed %s\n", workerID, task.selectedFile) 369 | } 370 | }(i) 371 | } 372 | 373 | // 发送任务到队列 374 | for _, selectedFile := range selectedFiles { 375 | // 找到完整路径 376 | for _, jarFile := range jarFiles { 377 | if filepath.Base(jarFile) == selectedFile { 378 | outputDir := filepath.Join(location, "createdabase", "src1") 379 | tasks <- DecompileTask{ 380 | jarFile: jarFile, 381 | selectedFile: selectedFile, 382 | outputDir: outputDir, 383 | } 384 | break 385 | } 386 | } 387 | } 388 | 389 | // 关闭任务队列 390 | close(tasks) 391 | 392 | // 等待所有goroutine完成 393 | wg.Wait() 394 | fmt.Println("所有goroutine反编译任务完成") 395 | } 396 | -------------------------------------------------------------------------------- /Database/Initializer.go: -------------------------------------------------------------------------------- 1 | package Database 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/fatih/color" 9 | ) 10 | 11 | // Init 初始化数据库创建流程 12 | func Init(jar string) { 13 | pwd, _ := os.Getwd() 14 | jar = filepath.Join(pwd, jar) 15 | if !Common.FileExists(jar) { 16 | color.Red("Jar file not found") 17 | return 18 | } 19 | location := filepath.Dir(jar) 20 | color.Green("Jar file found") 21 | 22 | // 清理旧文件 23 | cleanupOldFiles(location) 24 | 25 | // 解压jar包 26 | Common.ExtractZip(jar, filepath.Join(location, "output")) 27 | Common.SetupEnvironment() 28 | color.Green("解压完成") 29 | 30 | // 设置创建数据库目录 31 | setupDatabaseDirectory(location) 32 | 33 | // 生成构建文件 34 | err := GenerateBuildXML(filepath.Join(location, "createdabase")) 35 | if err != nil { 36 | color.Red("Generate build.xml failed: %v", err) 37 | return 38 | } 39 | 40 | // 创建必要的目录 41 | createDirectories(location) 42 | 43 | // 检查是否为war包,如果是则特殊处理 44 | if filepath.Ext(jar) == ".war" { 45 | // 对于war包,直接反编译classes目录和JSP文件 46 | outputDir := filepath.Join(location, "output") 47 | src1Dir := filepath.Join(location, "createdabase", "src1") 48 | 49 | // 反编译BOOT-INF/classes目录 50 | classesDir := filepath.Join(outputDir, "BOOT-INF", "classes") 51 | if _, err := os.Stat(classesDir); err == nil { 52 | color.Green("开始反编译BOOT-INF/classes目录") 53 | err := DecompileJava("-cp", "tools/java-decompiler.jar", 54 | "org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler", 55 | "-dgs=true", "-hdc=0", "-dgs=1", "-rsy=1", "-rbr=1", "-lit=1", "-nls=1", "-mpm=60", 56 | classesDir, src1Dir) 57 | if err != nil { 58 | color.Red("BOOT-INF/classes目录反编译失败: %v", err) 59 | return 60 | } 61 | color.Green("BOOT-INF/classes目录反编译完成") 62 | } 63 | 64 | // 检查传统WAR包的WEB-INF/classes目录 65 | webInfClassesDir := filepath.Join(outputDir, "WEB-INF", "classes") 66 | if _, err := os.Stat(webInfClassesDir); err == nil { 67 | color.Green("开始反编译WEB-INF/classes目录") 68 | err := DecompileJava("-cp", "tools/java-decompiler.jar", 69 | "org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler", 70 | "-dgs=true", "-hdc=0", "-dgs=1", "-rsy=1", "-rbr=1", "-lit=1", "-nls=1", "-mpm=60", 71 | webInfClassesDir, src1Dir) 72 | if err != nil { 73 | color.Red("WEB-INF/classes目录反编译失败: %v", err) 74 | return 75 | } 76 | color.Green("WEB-INF/classes目录反编译完成") 77 | } 78 | 79 | // 反编译JSP文件 80 | color.Green("反编译JSP文件: ") 81 | err := DecompileJava("-jar", "tools/jsp2class.jar", outputDir, src1Dir) 82 | if err != nil { 83 | color.Red("JSP文件反编译失败 %s: %v", err) 84 | // JSP反编译失败不影响整体流程,继续执行 85 | } 86 | color.Green("反编译JSP文件: 完成") 87 | } else { 88 | // 对于普通jar包,使用原有逻辑 89 | DecompileJava("-jar", "tools/procyon-decompiler-0.6.0.jar", jar, "-o", filepath.Join(location, "createdabase", "src1")) 90 | } 91 | 92 | // 反编译依赖到src1 93 | DecompileLibraries(location) 94 | 95 | // 复制额外源码目录到src1(如果指定了的话) 96 | src1Dir := filepath.Join(location, "createdabase", "src1") 97 | if err := Common.CopyExtraSourceToSrc1(Common.ExtraSourceDir, src1Dir); err != nil { 98 | color.Red("复制额外源码失败: %v", err) 99 | return 100 | } 101 | 102 | // 清理可能导致编译失败的文件 103 | cleanupProblematicFiles(location) 104 | 105 | // 创建数据库 106 | Createdatabase(filepath.Join(location, "createdabase")) 107 | 108 | // 移动和清理文件 109 | finalizeDatabaseCreation(location) 110 | } 111 | 112 | // cleanupOldFiles 清理旧文件 113 | func cleanupOldFiles(location string) { 114 | color.Red("删除output") 115 | color.Red("删除src") 116 | Common.RemoveFile(filepath.Join(location, "output")) 117 | Common.RemoveFile(filepath.Join(location, "src")) 118 | } 119 | 120 | // setupDatabaseDirectory 设置数据库目录 121 | func setupDatabaseDirectory(location string) { 122 | if Common.FileExists(filepath.Join(location, "createdabase")) { 123 | color.Red("已删除存在的createdatabase") 124 | os.Remove(filepath.Join(location, "createdabase")) 125 | } 126 | os.Mkdir(filepath.Join(location, "createdabase"), 0755) 127 | } 128 | 129 | // createDirectories 创建必要的目录 130 | func createDirectories(location string) { 131 | os.Mkdir(filepath.Join(location, "createdabase", "src1"), 0755) 132 | os.Mkdir(filepath.Join(location, "createdabase", "src2"), 0755) 133 | os.Mkdir(filepath.Join(location, "createdabase", "build_classes"), 0755) 134 | } 135 | 136 | // cleanupProblematicFiles 清理可能导致编译失败的文件 137 | func cleanupProblematicFiles(location string) { 138 | src1Dir := filepath.Join(location, "createdabase", "src1") 139 | 140 | // 删除所有.kt文件 141 | ktFiles, err := filepath.Glob(filepath.Join(src1Dir, "**", "*.kt")) 142 | if err == nil { 143 | for _, ktFile := range ktFiles { 144 | os.Remove(ktFile) 145 | color.Yellow("删除Kotlin文件: %s", filepath.Base(ktFile)) 146 | } 147 | } 148 | 149 | // // 递归遍历src1目录,删除除.java文件之外的所有文件 150 | // filepath.Walk(src1Dir, func(path string, info os.FileInfo, err error) error { 151 | // if err != nil { 152 | // return nil 153 | // } 154 | // // 跳过目录 155 | // if info.IsDir() { 156 | // return nil 157 | // } 158 | // // 保留.java文件,删除其他所有文件 159 | // if filepath.Ext(path) != ".java" { 160 | // os.Remove(path) 161 | // color.Yellow("删除非Java文件: %s", path) 162 | // } 163 | // return nil 164 | // }) 165 | 166 | // 递归删除所有module-info.java文件,防止报错 167 | filepath.Walk(src1Dir, func(path string, info os.FileInfo, err error) error { 168 | if err != nil { 169 | return nil 170 | } 171 | if !info.IsDir() && info.Name() == "module-info.java" { 172 | os.Remove(path) 173 | color.Yellow("删除模块信息文件: %s", path) 174 | } 175 | return nil 176 | }) 177 | 178 | color.Green("清理问题文件完成") 179 | } 180 | 181 | // finalizeDatabaseCreation 完成数据库创建的最后步骤 182 | func finalizeDatabaseCreation(location string) { 183 | Common.RemoveFile(filepath.Join(location, "temp")) 184 | 185 | err := os.Rename(filepath.Join(location, "createdabase", "temp"), filepath.Join(location, "temp")) 186 | if err != nil { 187 | color.Red("移动失败:", err) 188 | } else { 189 | color.Green("数据库移动成功") 190 | } 191 | 192 | if Common.KeepTempFiles { 193 | color.Yellow("保留临时文件模式:跳过清理output和createdatabase目录") 194 | color.Green("数据库生成完成") 195 | } else { 196 | color.Green("数据库生成完成,删除output和createdatabase") 197 | Common.RemoveFile(filepath.Join(location, "createdabase")) 198 | Common.RemoveFile(filepath.Join(location, "output")) 199 | color.Green("删除成功") 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /Database/Utils.go: -------------------------------------------------------------------------------- 1 | package Database 2 | 3 | // Utils.go 现在作为数据库模块的入口文件 4 | // 主要功能已拆分到以下文件: 5 | // - Initializer.go: 数据库初始化流程 6 | // - Decompiler.go: 反编译相关功能 7 | // - Builder.go: 构建和CodeQL数据库创建 8 | // - Decompile.go: 其他反编译工具函数 9 | 10 | // 如果需要添加通用的工具函数,可以在这里添加 11 | -------------------------------------------------------------------------------- /Install/AntDownload.go: -------------------------------------------------------------------------------- 1 | package Install 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | // CheckAntInstalled 检查Apache Ant是否已安装在tools目录下 11 | func CheckAntInstalled() bool { 12 | toolsDir := "./tools" 13 | antPath := filepath.Join(toolsDir, "ant") 14 | 15 | if _, err := os.Stat(antPath); err == nil { 16 | fmt.Println("Apache Ant 已经安装在 ./tools/ant 目录下") 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | // DownloadAnt 下载并安装Apache Ant到tools目录 23 | func DownloadAnt() error { 24 | if CheckAntInstalled() { 25 | return nil 26 | } 27 | 28 | fmt.Println("开始下载Apache Ant...") 29 | 30 | // 创建tools目录 31 | toolsDir := "./tools" 32 | if err := os.MkdirAll(toolsDir, 0755); err != nil { 33 | return fmt.Errorf("创建tools目录失败: %v", err) 34 | } 35 | 36 | // Apache Ant下载链接(跨平台通用)// 下载Apache Ant 37 | var downloadURL string 38 | if Common.AntDownloadURL != "" { 39 | downloadURL = Common.AntDownloadURL 40 | fmt.Printf("使用用户指定的Apache Ant下载地址: %s\n", downloadURL) 41 | } else { 42 | downloadURL = "https://archive.apache.org/dist/ant/binaries/apache-ant-1.10.14-bin.zip" 43 | fmt.Printf("使用默认Apache Ant下载地址: %s\n", downloadURL) 44 | } 45 | 46 | fileName := "apache-ant-1.10.14-bin.zip" 47 | filePath := filepath.Join(toolsDir, fileName) 48 | if err := Common.DownloadFile(downloadURL, filePath); err != nil { 49 | return fmt.Errorf("下载Apache Ant失败: %v", err) 50 | } 51 | 52 | fmt.Printf("Apache Ant下载完成: %s\n", filePath) 53 | 54 | // 自动解压 55 | antDir := filepath.Join(toolsDir, "ant") 56 | if err := ExtractInstallZip(filePath, antDir); err != nil { 57 | return fmt.Errorf("解压Apache Ant失败: %v", err) 58 | } 59 | 60 | fmt.Println("Apache Ant解压完成") 61 | 62 | // 删除下载的压缩包 63 | Common.RemoveFile(filePath) 64 | 65 | return nil 66 | } 67 | 68 | // InstallAllTools 安装所有工具的便捷函数 69 | func InstallAllTools() error { 70 | fmt.Println("=== 开始安装开发工具 ===") 71 | 72 | // 检查并安装JDK8 73 | fmt.Println("\n1. 检查JDK8...") 74 | if err := DownloadJDK(); err != nil { 75 | fmt.Printf("JDK安装失败: %v\n", err) 76 | } 77 | 78 | // 检查并安装CodeQL 79 | fmt.Println("\n2. 检查CodeQL...") 80 | if err := DownloadCodeQL(); err != nil { 81 | fmt.Printf("CodeQL安装失败: %v\n", err) 82 | } 83 | 84 | // 检查并安装Apache Ant 85 | fmt.Println("\n3. 检查Apache Ant...") 86 | if err := DownloadAnt(); err != nil { 87 | fmt.Printf("Apache Ant安装失败: %v\n", err) 88 | } 89 | 90 | // 检查并安装Procyon 91 | fmt.Println("\n4. 检查Procyon...") 92 | if err := DownloadProcyon(); err != nil { 93 | fmt.Printf("Procyon安装失败: %v\n", err) 94 | } 95 | 96 | // 检查并安装Apache Tomcat 97 | fmt.Println("\n5. 检查Apache Tomcat...") 98 | if err := DownloadTomcat(); err != nil { 99 | fmt.Printf("Apache Tomcat安装失败: %v\n", err) 100 | } 101 | 102 | fmt.Println("\n=== 工具安装检查完成 ===") 103 | return nil 104 | } 105 | -------------------------------------------------------------------------------- /Install/CodeqlDownload.go: -------------------------------------------------------------------------------- 1 | package Install 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "runtime" 9 | ) 10 | 11 | // CheckCodeQLInstalled 检查CodeQL是否已安装在tools目录下 12 | func CheckCodeQLInstalled() bool { 13 | toolsDir := "./tools" 14 | codeqlPath := filepath.Join(toolsDir, "codeql") 15 | 16 | if _, err := os.Stat(codeqlPath); err == nil { 17 | fmt.Println("CodeQL 已经安装在 ./tools/codeql 目录下") 18 | return true 19 | } 20 | return false 21 | } 22 | 23 | // DownloadCodeQL 下载并安装CodeQL到tools目录 24 | func DownloadCodeQL() error { 25 | if CheckCodeQLInstalled() { 26 | return nil 27 | } 28 | 29 | fmt.Println("开始下载CodeQL...") 30 | 31 | // 创建tools目录 32 | toolsDir := "./tools" 33 | if err := os.MkdirAll(toolsDir, 0755); err != nil { 34 | return fmt.Errorf("创建tools目录失败: %v", err) 35 | } 36 | 37 | // 根据操作系统选择下载链接 38 | var downloadURL string 39 | var fileName string 40 | 41 | // 优先使用用户指定的URL 42 | if Common.CodeQLDownloadURL != "" { 43 | downloadURL = Common.CodeQLDownloadURL 44 | fmt.Printf("使用用户指定的CodeQL下载地址: %s\n", downloadURL) 45 | // 从URL中提取文件名 46 | fileName = filepath.Base(downloadURL) 47 | } else { 48 | // 使用默认URL 49 | switch runtime.GOOS { 50 | case "windows": 51 | downloadURL = "https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql-win64.zip" 52 | fileName = "codeql-win64.zip" 53 | case "linux": 54 | downloadURL = "https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql-linux64.zip" 55 | fileName = "codeql-linux64.zip" 56 | case "darwin": 57 | downloadURL = "https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql-osx64.zip" 58 | fileName = "codeql-osx64.zip" 59 | default: 60 | downloadURL = "https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql-osx64.zip" 61 | fileName = "codeql-osx64.zip" 62 | } 63 | fmt.Printf("使用默认CodeQL下载地址: %s\n", downloadURL) 64 | } 65 | 66 | // 下载文件 67 | filePath := filepath.Join(toolsDir, fileName) 68 | if err := Common.DownloadFile(downloadURL, filePath); err != nil { 69 | return fmt.Errorf("下载CodeQL失败: %v", err) 70 | } 71 | 72 | fmt.Printf("CodeQL下载完成: %s\n", filePath) 73 | 74 | // 自动解压 75 | codeqlDir := filepath.Join(toolsDir, "codeql") 76 | if err := ExtractInstallZip(filePath, codeqlDir); err != nil { 77 | return fmt.Errorf("解压CodeQL失败: %v", err) 78 | } 79 | 80 | fmt.Println("CodeQL解压完成") 81 | 82 | // 删除下载的压缩包 83 | Common.RemoveFile(filePath) 84 | 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /Install/DecompileDownload.go: -------------------------------------------------------------------------------- 1 | package Install 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | // CheckDecompileInstalled 检查反编译器是否已安装在tools目录下 11 | func CheckDecompileInstalled() bool { 12 | toolsDir := "./tools" 13 | procyonPath := filepath.Join(toolsDir, "procyon-decompiler-0.6.0.jar") 14 | fernflowerPath := filepath.Join(toolsDir, "java-decompiler.jar") 15 | jsp2classPath := filepath.Join(toolsDir, "jsp2class.jar") 16 | 17 | procyonExists := false 18 | fernflowerExists := false 19 | jsp2classExists := false 20 | 21 | if _, err := os.Stat(procyonPath); err == nil { 22 | fmt.Println("procyon-decompiler-0.6.0.jar 已经安装在 ./tools 目录下") 23 | procyonExists = true 24 | } 25 | 26 | if _, err := os.Stat(fernflowerPath); err == nil { 27 | fmt.Println("java-decompiler.jar 已经安装在 ./tools 目录下") 28 | fernflowerExists = true 29 | } 30 | 31 | if _, err := os.Stat(jsp2classPath); err == nil { 32 | fmt.Println("jsp2class.jar 已经安装在 ./tools 目录下") 33 | jsp2classExists = true 34 | } 35 | 36 | return procyonExists && fernflowerExists && jsp2classExists 37 | } 38 | 39 | // DownloadDecompilers 下载反编译器到tools目录 40 | func DownloadDecompilers() error { 41 | if CheckDecompileInstalled() { 42 | return nil 43 | } 44 | 45 | // 创建tools目录 46 | toolsDir := "./tools" 47 | if err := os.MkdirAll(toolsDir, 0755); err != nil { 48 | return fmt.Errorf("创建tools目录失败: %v", err) 49 | } 50 | 51 | // 下载procyon-decompiler 52 | procyonPath := filepath.Join(toolsDir, "procyon-decompiler-0.6.0.jar") 53 | if _, err := os.Stat(procyonPath); os.IsNotExist(err) { 54 | fmt.Println("开始下载procyon-decompiler-0.6.0.jar...") 55 | procyonURL := "https://raw.githubusercontent.com/yezere/codeql_n1ght_dp/refs/heads/main/procyon-decompiler-0.6.0.jar" 56 | if err := Common.DownloadFile(procyonURL, procyonPath); err != nil { 57 | return fmt.Errorf("下载procyon-decompiler-0.6.0.jar失败: %v", err) 58 | } 59 | fmt.Printf("procyon-decompiler-0.6.0.jar下载完成: %s\n", procyonPath) 60 | } 61 | 62 | // 下载java-decompiler (fernflower) 63 | fernflowerPath := filepath.Join(toolsDir, "java-decompiler.jar") 64 | if _, err := os.Stat(fernflowerPath); os.IsNotExist(err) { 65 | fmt.Println("开始下载java-decompiler.jar...") 66 | fernflowerURL := "https://raw.githubusercontent.com/yezere/codeql_n1ght_dp/refs/heads/main/java-decompiler.jar" 67 | if err := Common.DownloadFile(fernflowerURL, fernflowerPath); err != nil { 68 | return fmt.Errorf("下载java-decompiler.jar失败: %v", err) 69 | } 70 | fmt.Printf("java-decompiler.jar下载完成: %s\n", fernflowerPath) 71 | } 72 | 73 | // 下载jsp2class.jar 74 | jsp2classPath := filepath.Join(toolsDir, "jsp2class.jar") 75 | if _, err := os.Stat(jsp2classPath); os.IsNotExist(err) { 76 | fmt.Println("开始下载jsp2class.jar...") 77 | jsp2classURL := "https://raw.githubusercontent.com/yezere/codeql_n1ght_dp/refs/heads/main/jsp2class.jar" 78 | if err := Common.DownloadFile(jsp2classURL, jsp2classPath); err != nil { 79 | return fmt.Errorf("下载jsp2class.jar失败: %v", err) 80 | } 81 | fmt.Printf("jsp2class.jar下载完成: %s\n", jsp2classPath) 82 | } 83 | 84 | return nil 85 | } 86 | 87 | // DownloadProcyon 保持向后兼容性 88 | func DownloadProcyon() error { 89 | return DownloadDecompilers() 90 | } 91 | -------------------------------------------------------------------------------- /Install/JDKDownload.go: -------------------------------------------------------------------------------- 1 | package Install 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "runtime" 9 | ) 10 | 11 | // CheckJDKInstalled 检查JDK是否已安装在tools目录下 12 | func CheckJDKInstalled() bool { 13 | toolsDir := "./tools" 14 | jdkPath := filepath.Join(toolsDir, "jdk") 15 | 16 | if _, err := os.Stat(jdkPath); err == nil { 17 | fmt.Println("JDK 已经安装在 ./tools/jdk 目录下") 18 | return true 19 | } 20 | return false 21 | } 22 | 23 | // DownloadJDK 下载并安装JDK8到tools目录 24 | func DownloadJDK() error { 25 | if CheckJDKInstalled() { 26 | return nil 27 | } 28 | 29 | fmt.Println("开始下载JDK8...") 30 | 31 | // 创建tools目录 32 | toolsDir := "./tools" 33 | if err := os.MkdirAll(toolsDir, 0755); err != nil { 34 | return fmt.Errorf("创建tools目录失败: %v", err) 35 | } 36 | 37 | // 根据操作系统选择下载链接 38 | var downloadURL string 39 | var fileName string 40 | 41 | // 优先使用用户指定的URL 42 | if Common.JDKDownloadURL != "" { 43 | downloadURL = Common.JDKDownloadURL 44 | fmt.Printf("使用用户指定的JDK下载地址: %s\n", downloadURL) 45 | } else { 46 | // 使用默认URL 47 | switch runtime.GOOS { 48 | case "windows": 49 | // Windows JDK17 50 | downloadURL = "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jdk_x64_windows_hotspot_8u392b08.zip" 51 | case "linux": 52 | downloadURL = "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u392b08.tar.gz" 53 | case "darwin": 54 | downloadURL = "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jdk_x64_mac_hotspot_8u392b08.tar.gz" 55 | default: 56 | return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS) 57 | } 58 | fmt.Printf("使用默认JDK下载地址: %s\n", downloadURL) 59 | } 60 | 61 | switch runtime.GOOS { 62 | case "windows": 63 | fileName = "OpenJDK8U-jdk_x64_windows_hotspot_8u392b08.zip" 64 | case "linux": 65 | fileName = "OpenJDK8U-jdk_x64_linux_hotspot_8u392b08.tar.gz" 66 | case "darwin": 67 | fileName = "OpenJDK8U-jdk_x64_mac_hotspot_8u392b08.tar.gz" 68 | } 69 | 70 | // 下载文件 71 | filePath := filepath.Join(toolsDir, fileName) 72 | if err := Common.DownloadFile(downloadURL, filePath); err != nil { 73 | return fmt.Errorf("下载JDK失败: %v", err) 74 | } 75 | 76 | fmt.Printf("JDK下载完成: %s\n", filePath) 77 | 78 | // 自动解压 79 | jdkDir := filepath.Join(toolsDir, "jdk") 80 | if err := ExtractInstallZip(filePath, jdkDir); err != nil { 81 | return fmt.Errorf("解压JDK失败: %v", err) 82 | } 83 | 84 | fmt.Println("JDK解压完成") 85 | 86 | // 删除下载的压缩包 87 | Common.RemoveFile(filePath) 88 | 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /Install/TomcatDownload.go: -------------------------------------------------------------------------------- 1 | package Install 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | // CheckTomcatInstalled 检查Apache Tomcat是否已安装在tools目录下 11 | func CheckTomcatInstalled() bool { 12 | toolsDir := "./tools" 13 | tomcatPath := filepath.Join(toolsDir, "tomcat") 14 | 15 | if _, err := os.Stat(tomcatPath); err == nil { 16 | fmt.Println("Apache Tomcat 已经安装在 ./tools/tomcat 目录下") 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | // DownloadTomcat 下载并安装Apache Tomcat到tools目录 23 | func DownloadTomcat() error { 24 | if CheckTomcatInstalled() { 25 | return nil 26 | } 27 | 28 | fmt.Println("开始下载Apache Tomcat...") 29 | 30 | // 创建tools目录 31 | toolsDir := "./tools" 32 | if err := os.MkdirAll(toolsDir, 0755); err != nil { 33 | return fmt.Errorf("创建tools目录失败: %v", err) 34 | } 35 | 36 | // Apache Tomcat 9.0.27下载链接 37 | downloadURL := "https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.27/bin/apache-tomcat-9.0.27.zip" 38 | fileName := "apache-tomcat-9.0.27.zip" 39 | 40 | // 下载文件 41 | filePath := filepath.Join(toolsDir, fileName) 42 | if err := Common.DownloadFile(downloadURL, filePath); err != nil { 43 | return fmt.Errorf("下载Apache Tomcat失败: %v", err) 44 | } 45 | 46 | fmt.Printf("Apache Tomcat下载完成: %s\n", filePath) 47 | 48 | // 自动解压 49 | tomcatDir := filepath.Join(toolsDir, "tomcat") 50 | if err := ExtractInstallZip(filePath, tomcatDir); err != nil { 51 | return fmt.Errorf("解压Apache Tomcat失败: %v", err) 52 | } 53 | 54 | fmt.Printf("Apache Tomcat解压完成: %s\n", tomcatDir) 55 | 56 | // 删除下载的zip文件 57 | if err := os.Remove(filePath); err != nil { 58 | fmt.Printf("警告: 删除下载文件失败: %v\n", err) 59 | } else { 60 | fmt.Println("已删除下载的zip文件") 61 | } 62 | 63 | fmt.Println("Apache Tomcat安装完成") 64 | return nil 65 | } 66 | 67 | // InstallTomcat 安装Apache Tomcat的便捷函数 68 | func InstallTomcat() error { 69 | fmt.Println("=== 安装Apache Tomcat ===") 70 | return DownloadTomcat() 71 | } 72 | 73 | // CheckTomcatAvailability 检查Tomcat可用性 74 | func CheckTomcatAvailability() bool { 75 | toolsDir := "./tools" 76 | tomcatPath := filepath.Join(toolsDir, "tomcat") 77 | 78 | // 检查tomcat目录是否存在 79 | if _, err := os.Stat(tomcatPath); err != nil { 80 | return false 81 | } 82 | 83 | // 检查关键文件是否存在 84 | tomcatVersionPath := filepath.Join(tomcatPath, "apache-tomcat-9.0.27") 85 | if _, err := os.Stat(tomcatVersionPath); err == nil { 86 | // 检查bin目录和startup脚本 87 | binPath := filepath.Join(tomcatVersionPath, "bin") 88 | if _, err := os.Stat(binPath); err == nil { 89 | return true 90 | } 91 | } 92 | 93 | return false 94 | } 95 | 96 | // GetTomcatPath 获取Tomcat安装路径 97 | func GetTomcatPath() string { 98 | toolsDir := "./tools" 99 | tomcatVersionPath := filepath.Join(toolsDir, "tomcat", "apache-tomcat-9.0.27") 100 | 101 | if _, err := os.Stat(tomcatVersionPath); err == nil { 102 | absPath, _ := filepath.Abs(tomcatVersionPath) 103 | return absPath 104 | } 105 | 106 | return "" 107 | } 108 | -------------------------------------------------------------------------------- /Install/Utils.go: -------------------------------------------------------------------------------- 1 | package Install 2 | 3 | import ( 4 | "archive/zip" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // ExtractInstallZip 解压ZIP文件并移除顶层路径,专门用于Install模块 13 | // 这个函数会自动移除ZIP文件中的顶层目录,将内容直接解压到目标目录 14 | func ExtractInstallZip(src, dest string) error { 15 | r, err := zip.OpenReader(src) 16 | if err != nil { 17 | return err 18 | } 19 | defer r.Close() 20 | 21 | os.MkdirAll(dest, 0755) 22 | 23 | // 找到顶层目录名 24 | var topLevelDir string 25 | for _, f := range r.File { 26 | if f.FileInfo().IsDir() && !strings.Contains(f.Name, "/") { 27 | topLevelDir = f.Name 28 | break 29 | } 30 | } 31 | 32 | // 如果没有找到顶层目录,尝试从第一个文件路径中提取 33 | if topLevelDir == "" && len(r.File) > 0 { 34 | firstPath := r.File[0].Name 35 | if idx := strings.Index(firstPath, "/"); idx != -1 { 36 | topLevelDir = firstPath[:idx+1] 37 | } 38 | } 39 | 40 | for _, f := range r.File { 41 | rc, err := f.Open() 42 | if err != nil { 43 | return err 44 | } 45 | defer rc.Close() 46 | 47 | // 移除顶层目录路径 48 | path := f.Name 49 | if topLevelDir != "" && strings.HasPrefix(path, topLevelDir) { 50 | path = strings.TrimPrefix(path, topLevelDir) 51 | // 如果移除顶层目录后路径为空,跳过 52 | if path == "" { 53 | continue 54 | } 55 | } 56 | 57 | fpath := filepath.Join(dest, path) 58 | 59 | if f.FileInfo().IsDir() { 60 | os.MkdirAll(fpath, f.FileInfo().Mode()) 61 | continue 62 | } 63 | 64 | if err := os.MkdirAll(filepath.Dir(fpath), 0755); err != nil { 65 | return err 66 | } 67 | 68 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.FileInfo().Mode()) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | _, err = io.Copy(outFile, rc) 74 | outFile.Close() 75 | 76 | if err != nil { 77 | return err 78 | } 79 | } 80 | return nil 81 | } 82 | 83 | // ExtractInstallZipWithProgress 带进度显示的解压函数 84 | func ExtractInstallZipWithProgress(src, dest string) error { 85 | fmt.Printf("正在解压 %s 到 %s...\n", filepath.Base(src), dest) 86 | err := ExtractInstallZip(src, dest) 87 | if err != nil { 88 | return fmt.Errorf("解压失败: %v", err) 89 | } 90 | fmt.Println("解压完成") 91 | return nil 92 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeQL N1ght 2 | 3 | 一个自用的 CodeQL 数据库自动化创建工具,支持 JAR/WAR 包的反编译和数据库生成。 4 | 没用使用maven去创建codeql数据库,使用了ant去编译这样,就不会因为报错而终止数据库生成。 5 | 可以手动在exe所在目录下,修改tools里面的工具。(~~其中基本所有代码都是trea ai写的,包括readme.md,为了不浪费我首月花费的3美元才写的~~) 6 | ~~可以加入我的qq群玩一玩:1027627836~~ 7 | 8 | ## 🚀 功能特性 9 | 10 | - **一键环境安装**:自动下载并配置 JDK、Apache Ant、CodeQL 等必要工具 11 | - **智能反编译**:支持 JAR 和 WAR 包的自动反编译 12 | - **多反编译器支持**:支持 Procyon 和 Fernflower 反编译器 13 | - **WAR 包特殊处理**:针对 Spring Boot 和传统 WAR 包的智能路径处理 14 | - **自动数据库创建**:一键生成 CodeQL 数据库用于安全分析 15 | - **安全扫描功能**:集成 CodeQL 扫描引擎,支持并发扫描和报告生成 16 | - **多格式报告**:生成 SARIF 和 HTML 格式的扫描报告 17 | - **并发处理**:支持 Goroutine 并发反编译和扫描,提升处理效率 18 | 19 | ## 📋 系统要求 20 | 21 | - Go 1.22.0 或更高版本 22 | - 网络连接(用于下载工具) 23 | - 足够的磁盘空间(建议至少 2GB) 24 | 25 | ## 🛠️ 安装 26 | 27 | ### 方法一:直接下载可执行文件 28 | 29 | 从 [Releases](https://github.com/yezere/codeql_n1ght/releases) 页面下载对应平台的可执行文件。 30 | 31 | ### 方法二:从源码编译 32 | 33 | ```bash 34 | git clone https://github.com/yezere/codeql_n1ght.git 35 | cd codeql_n1ght 36 | go build -o codeql_n1ght 37 | ``` 38 | 39 | ## 🎯 快速开始 40 | 41 | ### 1. 一键安装环境 42 | 43 | ```bash 44 | # 安装所有必要工具(JDK、Apache Ant、CodeQL) 45 | ./codeql_n1ght -install 46 | 47 | # 使用自定义下载地址安装 48 | ./codeql_n1ght -install -jdk https://your-jdk-url.zip -codeql https://your-codeql-url.zip 49 | ``` 50 | 51 | ### 2. 创建 CodeQL 数据库 52 | 53 | ```bash 54 | # 从 JAR 包创建数据库 55 | ./codeql_n1ght -database your-app.jar 56 | 57 | # 从 WAR 包创建数据库 58 | ./codeql_n1ght -database your-webapp.war 59 | 60 | # 指定反编译器类型 61 | ./codeql_n1ght -database your-app.jar -decompiler fernflower 62 | 63 | # 反编译自己想要的lib,将jar包放入lib文件夹下,打包成zip 64 | ./codeql_n1ght -database your-zip.zip 65 | 66 | # 使用 -deps 控制依赖选择(跳过 TUI) 67 | ./codeql_n1ght -database your-app.jar -deps none # 空依赖(跳过依赖反编译) 68 | ./codeql_n1ght -database your-app.jar -deps all # 全依赖(自动反编译所有依赖) 69 | ``` 70 | 71 | ### 3. 执行安全扫描 72 | 73 | ```bash 74 | # 扫描数据库(使用默认路径) 75 | ./codeql_n1ght -scan 76 | 77 | # 扫描数据库(指定路径) 78 | ./codeql_n1ght -scan -db ./mydb -ql ./myqueries 79 | 80 | # 并发扫描(提升扫描速度) 81 | ./codeql_n1ght -scan -db ./mydb -ql ./myqueries -goroutine -max-goroutines 8 82 | 83 | # 清理缓存后扫描(确保修改的QL文件生效) 84 | ./codeql_n1ght -scan -clean-cache 85 | ``` 86 | 87 | ## 📖 详细用法 88 | 89 | ### 命令行参数 90 | 91 | #### 基础功能参数 92 | 93 | | 参数 | 说明 | 示例 | 94 | |------|------|------| 95 | | `-install` | 一键安装环境 | `./codeql_n1ght -install` | 96 | | `-database` | 指定要分析的 JAR/WAR/ZIP 文件 | `./codeql_n1ght -database app.jar` | 97 | | `-scan` | 执行 CodeQL 安全扫描 | `./codeql_n1ght -scan` | 98 | | `-decompiler` | 选择反编译器 (procyon\|fernflower) | `./codeql_n1ght -database app.jar -decompiler fernflower` | 99 | 100 | #### 数据库模式参数(仅与 `-database` 一起使用) 101 | 102 | | 参数 | 说明 | 示例 | 103 | |------|------|------| 104 | | `-dir` | 指定额外源码目录(复制到 src1 一起生成数据库) | `./codeql_n1ght -database app.jar -dir ./extra_src` | 105 | | `-deps` | 依赖选择:`none`=空依赖,`all`=全依赖;不指定进入交互选择(TUI) | `./codeql_n1ght -database app.jar -deps all` | 106 | 107 | #### 扫描功能参数 108 | 109 | | 参数 | 说明 | 示例 | 110 | |------|------|------| 111 | | `-db` | 指定 CodeQL 数据库路径 | `./codeql_n1ght -scan -db ./mydb` | 112 | | `-ql` | 指定 QL 查询文件或目录路径 | `./codeql_n1ght -scan -ql ./myqueries` | 113 | | `-goroutine` | 启用并发扫描模式 | `./codeql_n1ght -scan -goroutine` | 114 | | `-max-goroutines` | 设置最大并发数 | `./codeql_n1ght -scan -goroutine -max-goroutines 8` | 115 | | `-threads` | 设置 CodeQL 扫描线程数 | `./codeql_n1ght -scan -threads 4` | 116 | | `-clean-cache` | 清理 CodeQL 缓存 | `./codeql_n1ght -scan -clean-cache` | 117 | 118 | #### 自定义下载参数 119 | 120 | | 参数 | 说明 | 示例 | 121 | |------|------|------| 122 | | `-jdk` | 自定义 JDK 下载地址 | `./codeql_n1ght -install -jdk https://example.com/jdk.zip` | 123 | | `-ant` | 自定义 Apache Ant 下载地址 | `./codeql_n1ght -install -ant https://example.com/ant.zip` | 124 | | `-codeql` | 自定义 CodeQL 下载地址 | `./codeql_n1ght -install -codeql https://example.com/codeql.zip` | 125 | 126 | ### 工作流程 127 | 128 | #### 数据库创建流程 129 | 130 | 1. **环境检查**:检查必要工具是否已安装 131 | 2. **文件解压**:解压 JAR/WAR 包到临时目录 132 | 3. **智能反编译**: 133 | - JAR 包:反编译所有 class 文件 134 | - WAR 包:分别处理 `BOOT-INF/classes`、`WEB-INF/classes` 和 JSP 文件 135 | 4. **构建配置**:生成 Apache Ant 构建文件 136 | 5. **数据库创建**:使用 CodeQL 创建分析数据库 137 | 138 | #### 安全扫描流程 139 | 140 | 1. **扫描准备**:验证数据库和查询文件路径 141 | 2. **清理环境**:清理之前的扫描结果和缓存 142 | 3. **源码提取**:从数据库中提取源码文件(如果需要) 143 | 4. **查询执行**: 144 | - 顺序模式:逐个执行 QL 查询文件 145 | - 并发模式:使用 Goroutine 并发执行查询 146 | 5. **结果生成**:生成 SARIF 和 HTML 格式的扫描报告 147 | 6. **报告展示**:显示扫描摘要和结果统计 148 | 149 | ### WAR 包特殊处理 150 | 151 | 本工具针对 WAR 包进行了特殊优化: 152 | 153 | - **Spring Boot JAR/WAR**:自动处理 `BOOT-INF/classes` 和 `BOOT-INF/lib` 目录 154 | - **传统 WAR**:兼容处理 `WEB-INF/classes` 和 `WEB-INF/lib` 目录 155 | - **JSP 文件**:使用专用的 `jsp2class.jar` 进行反编译 156 | - **智能路径检测**:自动识别不同的 WAR 包结构 157 | 158 | ## 📁 项目结构 159 | 160 | ``` 161 | codeql_n1ght/ 162 | ├── Common/ # 公共工具模块 163 | │ ├── CommandExecutor.go # 命令执行器 164 | │ ├── Config.go # 配置管理 165 | │ ├── Environment.go # 环境变量设置 166 | │ ├── Flag.go # 命令行参数解析 167 | │ ├── Start.go # 启动界面 168 | │ └── Utils.go # 工具函数 169 | ├── Database/ # 数据库创建模块 170 | │ ├── Builder.go # CodeQL 数据库构建 171 | │ ├── Decompile.go # 反编译入口 172 | │ ├── Decompiler.go # 反编译器实现 173 | │ ├── Initializer.go # 初始化流程 174 | │ └── Utils.go # 数据库工具函数 175 | ├── Install/ # 工具安装模块 176 | │ ├── AntDownload.go # Apache Ant 下载 177 | │ ├── CodeqlDownload.go # CodeQL 下载 178 | │ ├── DecompileDownload.go # 反编译器下载 179 | │ ├── JDKDownload.go # JDK 下载 180 | │ ├── TomcatDownload.go # Tomcat 下载 181 | │ └── Utils.go # 安装工具函数 182 | ├── Scanner/ # 安全扫描模块 183 | │ ├── Scanner.go # 扫描引擎核心 184 | │ ├── cleanup.go # 清理工具 185 | │ ├── file_extractor.go # 文件提取器 186 | │ ├── hints.go # 扫描提示 187 | │ └── html_report.go # HTML 报告生成 188 | ├── qlLibs/ # CodeQL 查询库(自动创建) 189 | ├── tools/ # 工具目录(自动创建) 190 | │ ├── ant/ # Apache Ant 191 | │ ├── codeql/ # CodeQL CLI 192 | │ └── jdk/ # JDK 193 | ├── results.sarif # SARIF 格式扫描结果 194 | ├── scan_report.html # HTML 格式扫描报告 195 | └── main.go # 主程序入口 196 | ``` 197 | 198 | ## 🔧 配置说明 199 | 200 | ### 反编译器选择 201 | 202 | - **Procyon**(默认):Java 反编译效果较好,推荐用于大多数场景 203 | - **Fernflower**:IntelliJ IDEA 内置反编译器,在某些复杂场景下表现更好 204 | 205 | ### 自定义工具版本 206 | 207 | 如果默认的工具版本与你的 Java 版本不兼容,可以手动替换: 208 | 209 | ```bash 210 | # 替换 Java 反编译器 211 | cp your-java-decompiler.jar tools/java-decompiler.jar 212 | 213 | # 替换 JSP 反编译器 214 | cp your-jsp2class.jar tools/jsp2class.jar 215 | ``` 216 | 217 | ## 🐛 故障排除 218 | 219 | ### 常见问题 220 | 221 | 1. **下载失败** 222 | - 检查网络连接 223 | - 尝试使用自定义下载地址 224 | - 检查防火墙设置 225 | 226 | 2. **反编译失败** 227 | - 尝试切换反编译器:`-decompiler fernflower` 228 | - 检查 JAR/WAR 文件是否损坏 229 | - 确保有足够的磁盘空间 230 | 231 | 3. **数据库创建失败** 232 | - 检查 CodeQL 是否正确安装 233 | - 确保 Apache Ant 在 PATH 中 234 | - 检查内存设置(默认 51200MB) 235 | - 可能存在一些特殊问题 236 | 237 | 4. **扫描失败** 238 | - 确保数据库路径正确且数据库完整 239 | - 检查 QL 查询文件是否存在 240 | - 尝试运行 `codeql pack install` 安装依赖包 241 | - 使用 `-clean-cache` 参数清理缓存 242 | 243 | 5. **并发扫描问题** 244 | - 降低并发数:`-max-goroutines 4` 245 | - 检查系统内存是否充足 246 | - 尝试使用顺序扫描模式(不使用 `-goroutine` 参数) 247 | 248 | 6. **报告生成失败** 249 | - 检查当前目录的写入权限 250 | - 确保没有其他程序占用结果文件 251 | - 检查磁盘空间是否充足 252 | 253 | --- 254 | 255 | ⭐ 如果这个项目对你有帮助,请给它一个 Star! -------------------------------------------------------------------------------- /Scanner/Scanner.go: -------------------------------------------------------------------------------- 1 | package Scanner 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "strings" 10 | "sync" 11 | "time" 12 | 13 | "github.com/common-nighthawk/go-figure" 14 | "github.com/fatih/color" 15 | ) 16 | 17 | // ScanResult 扫描结果结构 18 | type ScanResult struct { 19 | QueryFile string 20 | Success bool 21 | Output string 22 | Error error 23 | Duration time.Duration 24 | } 25 | 26 | // RunScan 执行CodeQL扫描 27 | func RunScan() error { 28 | // 显示带边框的扫描开始提示 29 | displayScanHeader() 30 | 31 | // 清理之前的结果文件 32 | if err := cleanupPreviousResults(); err != nil { 33 | Common.LogWarn("清理之前的结果文件失败: %v", err) 34 | } 35 | 36 | // 检查并解压源码文件 37 | if err := extractSourceFiles(); err != nil { 38 | Common.LogWarn("解压源码文件失败: %v", err) 39 | } 40 | 41 | // 显示扫描配置信息 42 | Common.LogInfo("数据库路径: %s", Common.DatabasePath) 43 | Common.LogInfo("QL库路径: %s", Common.QLLibsPath) 44 | 45 | // 验证扫描相关目录 46 | if err := validateScanDirectory(); err != nil { 47 | return err 48 | } 49 | 50 | // 获取目录下的所有.ql文件 51 | qlFiles, err := findQLFiles() 52 | if err != nil { 53 | return err 54 | } 55 | 56 | if len(qlFiles) == 0 { 57 | Common.LogWarn("未找到任何.ql文件") 58 | return fmt.Errorf("QL库目录 %s 中未找到.ql文件", Common.QLLibsPath) 59 | } 60 | 61 | Common.LogInfo("找到 %d 个查询文件", len(qlFiles)) 62 | 63 | // 执行查询 64 | results := make([]ScanResult, 0, len(qlFiles)) 65 | if Common.UseGoroutine { 66 | results = executeConcurrentQueries(qlFiles) 67 | } else { 68 | results = executeSequentialQueries(qlFiles) 69 | } 70 | 71 | // 显示扫描总结 72 | displayScanSummary(results) 73 | 74 | 75 | 76 | return nil 77 | } 78 | 79 | // displayScanHeader 显示扫描开始的界面 80 | func displayScanHeader() { 81 | scannerFigure := figure.NewFigure("Starting Scan", "", true) 82 | asciiArt := scannerFigure.String() 83 | lines := strings.Split(asciiArt, "\n") 84 | 85 | // 计算最长行长度 86 | maxLen := 0 87 | for _, line := range lines { 88 | if len(line) > maxLen { 89 | maxLen = len(line) 90 | } 91 | } 92 | 93 | // 打印带边框的绿色提示 94 | green := color.New(color.FgGreen).SprintFunc() 95 | fmt.Println(strings.Repeat("-", maxLen+4)) 96 | for _, line := range lines { 97 | fmt.Printf("| %s%s |\n", green(line), strings.Repeat(" ", maxLen-len(line))) 98 | } 99 | fmt.Println(strings.Repeat("-", maxLen+4)) 100 | fmt.Println() 101 | } 102 | 103 | // validateScanDirectory 验证扫描相关目录 104 | func validateScanDirectory() error { 105 | // 验证数据库路径 106 | if !Common.IsDirectory(Common.DatabasePath) { 107 | return fmt.Errorf("指定的数据库路径不是有效目录: %s", Common.DatabasePath) 108 | } 109 | 110 | // 验证QL库路径 111 | if !Common.IsDirectory(Common.QLLibsPath) { 112 | return fmt.Errorf("指定的QL库路径不是有效目录: %s", Common.QLLibsPath) 113 | } 114 | 115 | return nil 116 | } 117 | 118 | // findQLFiles 查找所有.ql文件 119 | func findQLFiles() ([]string, error) { 120 | var qlFiles []string 121 | err := filepath.Walk(Common.QLLibsPath, func(path string, info os.FileInfo, err error) error { 122 | if err != nil { 123 | return err 124 | } 125 | if !info.IsDir() && strings.HasSuffix(info.Name(), ".ql") { 126 | qlFiles = append(qlFiles, path) 127 | } 128 | return nil 129 | }) 130 | return qlFiles, err 131 | } 132 | 133 | // executeConcurrentQueries 并发执行查询 134 | func executeConcurrentQueries(qlFiles []string) []ScanResult { 135 | var wg sync.WaitGroup 136 | semaphore := make(chan struct{}, Common.MaxGoroutines) 137 | results := make(chan ScanResult, len(qlFiles)) 138 | 139 | Common.LogInfo("使用并发模式执行查询 (最大并发数: %d)", Common.MaxGoroutines) 140 | 141 | for _, qlFile := range qlFiles { 142 | wg.Add(1) 143 | semaphore <- struct{}{} // 获取信号量 144 | go executeQuery(qlFile, &wg, semaphore, results) 145 | } 146 | 147 | // 等待所有查询完成 148 | wg.Wait() 149 | close(results) 150 | 151 | // 收集结果 152 | var scanResults []ScanResult 153 | for result := range results { 154 | scanResults = append(scanResults, result) 155 | } 156 | 157 | return scanResults 158 | } 159 | 160 | // executeSequentialQueries 顺序执行查询 161 | func executeSequentialQueries(qlFiles []string) []ScanResult { 162 | var results []ScanResult 163 | 164 | Common.LogInfo("使用顺序模式执行查询") 165 | 166 | for _, qlFile := range qlFiles { 167 | startTime := time.Now() 168 | result := ScanResult{ 169 | QueryFile: qlFile, 170 | Success: false, 171 | } 172 | 173 | Common.LogInfo("正在执行查询: %s", filepath.Base(qlFile)) 174 | Common.SetupEnvironment() 175 | 176 | // 构建CodeQL命令 177 | cmd := exec.Command("codeql", "database", "analyze", 178 | Common.DatabasePath, // 数据库路径 179 | qlFile, // 查询文件 180 | fmt.Sprintf("--threads=%d", Common.CodeQLThreads), 181 | "--format=sarifv2.1.0", 182 | "--output=results.sarif", 183 | ) 184 | 185 | // 执行命令并获取输出 186 | output, err := cmd.CombinedOutput() 187 | result.Duration = time.Since(startTime) 188 | result.Output = string(output) 189 | 190 | if err != nil { 191 | result.Error = err 192 | Common.LogError("执行查询 %s 失败 (耗时: %v): %v", filepath.Base(qlFile), result.Duration, err) 193 | if len(result.Output) > 0 { 194 | Common.LogError("错误输出: %s", result.Output) 195 | } 196 | showPackInstallHint() 197 | } else { 198 | result.Success = true 199 | Common.LogInfo("查询 %s 完成 (耗时: %v)", filepath.Base(qlFile), result.Duration) 200 | if len(result.Output) > 0 { 201 | color.White("查询结果:\n%s", result.Output) 202 | } 203 | } 204 | 205 | results = append(results, result) 206 | } 207 | 208 | return results 209 | } 210 | 211 | // executeQuery 执行单个查询 212 | func executeQuery(qlFile string, wg *sync.WaitGroup, semaphore chan struct{}, results chan<- ScanResult) { 213 | defer wg.Done() 214 | defer func() { <-semaphore }() // 释放信号量 215 | 216 | startTime := time.Now() 217 | result := ScanResult{ 218 | QueryFile: qlFile, 219 | Success: false, 220 | } 221 | 222 | Common.LogInfo("正在执行查询: %s", filepath.Base(qlFile)) 223 | Common.SetupEnvironment() 224 | // 构建CodeQL命令 225 | cmd := exec.Command("codeql", "database", "analyze", 226 | Common.DatabasePath, // 数据库路径 227 | qlFile, // 查询文件 228 | fmt.Sprintf("--threads=%d", Common.CodeQLThreads), 229 | "--format=sarifv2.1.0", 230 | "--output=results.sarif", 231 | ) 232 | 233 | // 执行命令并获取输出 234 | output, err := cmd.CombinedOutput() 235 | result.Duration = time.Since(startTime) 236 | result.Output = string(output) 237 | 238 | if err != nil { 239 | result.Error = err 240 | Common.LogError("执行查询 %s 失败 (耗时: %v): %v", filepath.Base(qlFile), result.Duration, err) 241 | if len(result.Output) > 0 { 242 | Common.LogError("错误输出: %s", result.Output) 243 | } 244 | showPackInstallHint() 245 | } else { 246 | result.Success = true 247 | Common.LogInfo("查询 %s 完成 (耗时: %v)", filepath.Base(qlFile), result.Duration) 248 | if len(result.Output) > 0 { 249 | color.White("查询结果:\n%s", result.Output) 250 | } 251 | } 252 | 253 | results <- result 254 | } 255 | 256 | // displayScanSummary 显示扫描总结 257 | func displayScanSummary(results []ScanResult) { 258 | successCount := 0 259 | totalDuration := time.Duration(0) 260 | 261 | for _, result := range results { 262 | if result.Success { 263 | successCount++ 264 | } 265 | totalDuration += result.Duration 266 | } 267 | 268 | fmt.Println("\n" + strings.Repeat("=", 60)) 269 | Common.LogInfo("扫描总结:") 270 | Common.LogInfo("总查询数: %d", len(results)) 271 | color.Green("成功: %d", successCount) 272 | color.Red("失败: %d", len(results)-successCount) 273 | Common.LogInfo("总耗时: %v", totalDuration) 274 | fmt.Println(strings.Repeat("=", 60)) 275 | } 276 | -------------------------------------------------------------------------------- /Scanner/cleanup.go: -------------------------------------------------------------------------------- 1 | package Scanner 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | // cleanupPreviousResults 清理之前的结果文件 10 | func cleanupPreviousResults() error { 11 | // 定义需要清理的文件列表 12 | filesToClean := []string{ 13 | "results.sarif", 14 | "scan_report.html", 15 | } 16 | 17 | for _, file := range filesToClean { 18 | if _, err := os.Stat(file); err == nil { 19 | // 文件存在,尝试删除 20 | if err := os.Remove(file); err != nil { 21 | Common.LogWarn("无法删除文件 %s: %v", file, err) 22 | } else { 23 | Common.LogInfo("已删除之前的结果文件: %s", file) 24 | } 25 | } 26 | } 27 | 28 | // 清理CodeQL缓存,确保修改的QL文件能生效 29 | cleanupCodeQLCache() 30 | 31 | // 可选:清理之前解压的src目录(如果用户想要重新解压) 32 | // 注释掉下面的代码以保留之前解压的文件,加快后续扫描速度 33 | /* 34 | srcDir := filepath.Join(Common.DatabasePath, "src") 35 | if _, err := os.Stat(srcDir); err == nil { 36 | if err := os.RemoveAll(srcDir); err != nil { 37 | Common.LogWarn("无法删除src目录 %s: %v", srcDir, err) 38 | } else { 39 | Common.LogInfo("已清理之前的src目录: %s", srcDir) 40 | } 41 | } 42 | */ 43 | 44 | return nil 45 | } 46 | 47 | // cleanupCodeQLCache 清理CodeQL缓存文件 48 | func cleanupCodeQLCache() { 49 | // 只有在用户明确指定时才清理缓存 50 | if !Common.CleanCache { 51 | return 52 | } 53 | 54 | Common.LogInfo("开始清理CodeQL缓存...") 55 | 56 | // 清理数据库缓存目录 57 | cacheDir := filepath.Join(Common.DatabasePath, "cache") 58 | if _, err := os.Stat(cacheDir); err == nil { 59 | if err := os.RemoveAll(cacheDir); err != nil { 60 | Common.LogWarn("无法删除缓存目录 %s: %v", cacheDir, err) 61 | } else { 62 | Common.LogInfo("已清理CodeQL缓存目录: %s", cacheDir) 63 | } 64 | } 65 | 66 | // 清理查询结果缓存目录(完全删除以确保重新运行) 67 | resultsDir := filepath.Join(Common.DatabasePath, "results") 68 | if _, err := os.Stat(resultsDir); err == nil { 69 | if err := os.RemoveAll(resultsDir); err != nil { 70 | Common.LogWarn("无法删除results目录 %s: %v", resultsDir, err) 71 | } else { 72 | Common.LogInfo("已清理查询结果缓存目录: %s", resultsDir) 73 | } 74 | } 75 | 76 | // 清理全局CodeQL缓存(如果存在) 77 | homeDir, err := os.UserHomeDir() 78 | if err == nil { 79 | globalCacheDir := filepath.Join(homeDir, ".codeql", "cache") 80 | if _, err := os.Stat(globalCacheDir); err == nil { 81 | Common.LogInfo("检测到全局CodeQL缓存目录: %s,建议手动清理以获得最佳效果", globalCacheDir) 82 | } 83 | } 84 | 85 | Common.LogInfo("缓存清理完成,这将确保修改的QL文件生效") 86 | } 87 | -------------------------------------------------------------------------------- /Scanner/file_extractor.go: -------------------------------------------------------------------------------- 1 | package Scanner 2 | 3 | import ( 4 | "archive/zip" 5 | "codeql_n1ght/Common" 6 | "fmt" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | // 全局变量存储源码根目录路径 14 | var sourceRootPath string 15 | 16 | // extractSourceFiles 检查并解压源码文件 17 | func extractSourceFiles() error { 18 | srcZipPath := filepath.Join(Common.DatabasePath, "src.zip") 19 | srcDir := filepath.Join(Common.DatabasePath, "src") 20 | 21 | // 检查src.zip是否存在 22 | if _, err := os.Stat(srcZipPath); os.IsNotExist(err) { 23 | Common.LogInfo("未找到src.zip文件,跳过源码解压") 24 | return nil 25 | } 26 | 27 | // 检查src目录是否已存在 28 | if _, err := os.Stat(srcDir); err == nil { 29 | Common.LogInfo("src目录已存在,跳过解压") 30 | // 探测并缓存源码根目录路径 31 | detectSourceRootPath() 32 | return nil 33 | } 34 | 35 | Common.LogInfo("正在解压源码文件: %s", srcZipPath) 36 | 37 | // 打开zip文件 38 | reader, err := zip.OpenReader(srcZipPath) 39 | if err != nil { 40 | return fmt.Errorf("无法打开zip文件: %v", err) 41 | } 42 | defer reader.Close() 43 | 44 | // 创建目标目录 45 | if err := os.MkdirAll(srcDir, 0755); err != nil { 46 | return fmt.Errorf("无法创建目录: %v", err) 47 | } 48 | 49 | // 解压文件 50 | for _, file := range reader.File { 51 | if err := extractFile(file, srcDir); err != nil { 52 | return fmt.Errorf("解压文件 %s 失败: %v", file.Name, err) 53 | } 54 | } 55 | 56 | Common.LogInfo("源码解压完成到: %s", srcDir) 57 | 58 | // 探测并缓存源码根目录路径 59 | detectSourceRootPath() 60 | return nil 61 | } 62 | 63 | // detectSourceRootPath 探测源码根目录路径 64 | func detectSourceRootPath() { 65 | srcDir := filepath.Join(Common.DatabasePath, "src") 66 | 67 | // 递归查找包含src1目录的路径 68 | var findSrc1 func(string) string 69 | findSrc1 = func(dir string) string { 70 | entries, err := os.ReadDir(dir) 71 | if err != nil { 72 | return "" 73 | } 74 | 75 | for _, entry := range entries { 76 | if entry.IsDir() { 77 | entryPath := filepath.Join(dir, entry.Name()) 78 | if entry.Name() == "src1" { 79 | // 找到src1目录,返回其父目录 80 | return filepath.Dir(entryPath) 81 | } 82 | // 递归搜索子目录 83 | if result := findSrc1(entryPath); result != "" { 84 | return result 85 | } 86 | } 87 | } 88 | return "" 89 | } 90 | 91 | if rootPath := findSrc1(srcDir); rootPath != "" { 92 | // 计算相对于src目录的路径 93 | if relPath, err := filepath.Rel(srcDir, rootPath); err == nil { 94 | sourceRootPath = relPath 95 | Common.LogInfo("检测到源码根目录: %s", filepath.Join(srcDir, sourceRootPath)) 96 | } 97 | } else { 98 | Common.LogWarn("未能检测到源码根目录,使用默认路径") 99 | sourceRootPath = "" 100 | } 101 | } 102 | 103 | // extractFile 解压单个文件 104 | func extractFile(file *zip.File, destDir string) error { 105 | // 构建目标路径 106 | destPath := filepath.Join(destDir, file.Name) 107 | 108 | // 确保目标路径在目标目录内(安全检查) 109 | if !strings.HasPrefix(destPath, filepath.Clean(destDir)+string(os.PathSeparator)) { 110 | return fmt.Errorf("无效的文件路径: %s", file.Name) 111 | } 112 | 113 | // 如果是目录,创建目录 114 | if file.FileInfo().IsDir() { 115 | return os.MkdirAll(destPath, file.FileInfo().Mode()) 116 | } 117 | 118 | // 创建文件的父目录 119 | if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil { 120 | return err 121 | } 122 | 123 | // 打开zip中的文件 124 | srcFile, err := file.Open() 125 | if err != nil { 126 | return err 127 | } 128 | defer srcFile.Close() 129 | 130 | // 创建目标文件 131 | destFile, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.FileInfo().Mode()) 132 | if err != nil { 133 | return err 134 | } 135 | defer destFile.Close() 136 | 137 | // 复制文件内容 138 | _, err = io.Copy(destFile, srcFile) 139 | return err 140 | } 141 | 142 | // GetSourceRootPath 获取源码根目录路径(供其他模块使用) 143 | func GetSourceRootPath() string { 144 | return sourceRootPath 145 | } 146 | -------------------------------------------------------------------------------- /Scanner/hints.go: -------------------------------------------------------------------------------- 1 | package Scanner 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "fmt" 6 | 7 | "github.com/fatih/color" 8 | ) 9 | 10 | // showPackInstallHint 显示pack install提示 11 | func showPackInstallHint() { 12 | yellow := color.New(color.FgYellow).SprintFunc() 13 | Common.LogWarn("如果遇到package相关错误,请尝试以下解决方案:") 14 | fmt.Printf("%s\n", yellow("1. 进入QL库目录: cd "+Common.QLLibsPath)) 15 | fmt.Printf("%s\n", yellow("2. 运行命令: ../tools/codeql/codeql pack install")) 16 | fmt.Printf("%s\n", yellow("3. 或者运行: codeql pack install")) 17 | fmt.Println() 18 | } 19 | -------------------------------------------------------------------------------- /application.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yezere/codeql_n1ght/11359b67ad9a3b9e2c2678c4ce4bd260568f8005/application.ico -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module codeql_n1ght 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/AlecAivazis/survey/v2 v2.3.7 7 | github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be 8 | github.com/fatih/color v1.18.0 9 | github.com/schollz/progressbar/v3 v3.18.0 10 | ) 11 | 12 | require ( 13 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 14 | github.com/mattn/go-colorable v0.1.13 // indirect 15 | github.com/mattn/go-isatty v0.0.20 // indirect 16 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect 17 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 18 | github.com/rivo/uniseg v0.4.7 // indirect 19 | golang.org/x/net v0.17.0 // indirect 20 | golang.org/x/sys v0.29.0 // indirect 21 | golang.org/x/term v0.28.0 // indirect 22 | golang.org/x/text v0.13.0 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= 2 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= 3 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= 4 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= 5 | github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= 6 | github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= 7 | github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= 8 | github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= 9 | github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= 10 | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 11 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 15 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 16 | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= 17 | github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 18 | github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= 19 | github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 20 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= 21 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= 22 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 23 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 24 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 25 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 26 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 27 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 28 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 29 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 30 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 31 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 32 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 33 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= 34 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 35 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 36 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 37 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 38 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 39 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 40 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 41 | github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA= 42 | github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= 43 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 44 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 45 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 46 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 47 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 48 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 49 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 50 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 51 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 52 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 53 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 54 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 55 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 56 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 57 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 58 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 59 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 60 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 61 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 62 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 63 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 64 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 65 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 66 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 67 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 68 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 69 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 70 | golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= 71 | golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 72 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 73 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 74 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 75 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 76 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 77 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 78 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 79 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 80 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 81 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 83 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 84 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 85 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "codeql_n1ght/Common" 5 | "codeql_n1ght/Database" 6 | "codeql_n1ght/Install" 7 | "codeql_n1ght/Scanner" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | func main() { 13 | // 显示启动界面 14 | Common.Start() 15 | 16 | // 解析命令行参数 17 | Common.InitFlag() 18 | 19 | // 检查参数合法性 20 | if err := validateArguments(); err != nil { 21 | Common.LogError("参数验证失败: %v", err) 22 | os.Exit(1) 23 | } 24 | 25 | // 执行相应的功能 26 | if err := executeCommand(); err != nil { 27 | Common.LogError("执行失败: %v", err) 28 | os.Exit(1) 29 | } 30 | 31 | Common.LogInfo("程序执行完成") 32 | } 33 | 34 | // validateArguments 验证命令行参数 35 | func validateArguments() error { 36 | // 检查是否指定了操作 37 | if !Common.IsInstall && Common.CreateJar == "" && !Common.ScanMode { 38 | return fmt.Errorf("请指定要执行的操作: -install, -database 或 -scan") 39 | } 40 | 41 | // 验证下载URL参数只能在install模式下使用 42 | if !Common.IsInstall { 43 | if Common.JDKDownloadURL != "" { 44 | return fmt.Errorf("-jdk 参数只能在 -install 模式下使用") 45 | } 46 | if Common.AntDownloadURL != "" { 47 | return fmt.Errorf("-ant 参数只能在 -install 模式下使用") 48 | } 49 | if Common.CodeQLDownloadURL != "" { 50 | return fmt.Errorf("-codeql 参数只能在 -install 模式下使用") 51 | } 52 | } 53 | 54 | // 验证额外源码目录参数只能在database模式下使用 55 | if Common.ExtraSourceDir != "" && Common.CreateJar == "" { 56 | return fmt.Errorf("-dir 参数只能在 -database 模式下使用") 57 | } 58 | 59 | // 验证扫描模式参数 60 | if Common.ScanMode { 61 | // 向后兼容处理:如果使用了旧的-d参数,给出提示 62 | if Common.ScanDirectory != "" { 63 | Common.LogWarn("-d 参数已弃用,请使用 -db 指定数据库路径,-ql 指定查询库路径") 64 | // 为了向后兼容,将-d参数的值作为数据库路径 65 | if Common.DatabasePath == "./lib" { // 如果是默认值 66 | Common.DatabasePath = Common.ScanDirectory 67 | } 68 | } 69 | 70 | // 验证数据库路径 71 | if !Common.IsDirectory(Common.DatabasePath) { 72 | return fmt.Errorf("指定的数据库路径不是有效目录: %s", Common.DatabasePath) 73 | } 74 | 75 | // 验证QL库路径 76 | if !Common.IsDirectory(Common.QLLibsPath) { 77 | return fmt.Errorf("指定的QL库路径不是有效目录: %s", Common.QLLibsPath) 78 | } 79 | 80 | // 扫描模式下不能同时使用install或database 81 | if Common.IsInstall { 82 | return fmt.Errorf("扫描模式不能与安装模式同时使用") 83 | } 84 | if Common.CreateJar != "" { 85 | return fmt.Errorf("扫描模式不能与数据库创建模式同时使用") 86 | } 87 | } 88 | 89 | // 验证数据库模式参数 90 | if Common.CreateJar != "" { 91 | if err := Common.ValidateFile(Common.CreateJar); err != nil { 92 | return fmt.Errorf("JAR文件验证失败: %v", err) 93 | } 94 | 95 | // 验证额外源码目录 96 | if Common.ExtraSourceDir != "" { 97 | if !Common.IsDirectory(Common.ExtraSourceDir) { 98 | return fmt.Errorf("指定的额外源码路径不是有效目录: %s", Common.ExtraSourceDir) 99 | } 100 | } 101 | 102 | // 数据库模式下不能同时使用install 103 | if Common.IsInstall { 104 | return fmt.Errorf("数据库创建模式不能与安装模式同时使用") 105 | } 106 | } 107 | 108 | // 验证并发参数 109 | if Common.UseGoroutine && Common.MaxGoroutines <= 0 { 110 | return fmt.Errorf("最大goroutine数量必须大于0") 111 | } 112 | 113 | // 验证线程数参数 114 | if Common.CodeQLThreads <= 0 { 115 | return fmt.Errorf("线程数必须大于0") 116 | } 117 | 118 | return nil 119 | } 120 | 121 | // executeCommand 执行相应的命令 122 | func executeCommand() error { 123 | // 安装工具 124 | if Common.IsInstall { 125 | if err := installTools(); err != nil { 126 | return err 127 | } 128 | } 129 | 130 | // 创建数据库 131 | if Common.CreateJar != "" { 132 | if err := createDatabase(); err != nil { 133 | return err 134 | } 135 | } 136 | 137 | // 执行扫描 138 | if Common.ScanMode { 139 | if err := runScan(); err != nil { 140 | return err 141 | } 142 | } 143 | 144 | return nil 145 | } 146 | 147 | // installTools 安装工具 148 | func installTools() error { 149 | return Common.SafeExecute(func() error { 150 | Common.LogInfo("开始安装工具...") 151 | 152 | // 安装必要的工具 153 | if err := Install.InstallAllTools(); err != nil { 154 | return err 155 | } 156 | 157 | // 设置环境变量 158 | if err := Common.SetupEnvironment(); err != nil { 159 | return err 160 | } 161 | 162 | // 显示工具版本信息 163 | Common.PrintToolVersions() 164 | 165 | Common.LogInfo("工具安装完成") 166 | return nil 167 | }, "工具安装失败") 168 | } 169 | 170 | // createDatabase 创建数据库 171 | func createDatabase() error { 172 | return Common.SafeExecute(func() error { 173 | Common.LogInfo("开始创建数据库: %s", Common.CreateJar) 174 | Database.Init(Common.CreateJar) 175 | Common.LogInfo("数据库创建完成") 176 | return nil 177 | }, "数据库创建失败") 178 | } 179 | 180 | // runScan 执行扫描 181 | func runScan() error { 182 | return Common.SafeExecute(func() error { 183 | Common.LogInfo("开始扫描 - 数据库: %s, QL库: %s", Common.DatabasePath, Common.QLLibsPath) 184 | if err := Scanner.RunScan(); err != nil { 185 | return err 186 | } 187 | Common.LogInfo("扫描完成") 188 | return nil 189 | }, "扫描执行失败") 190 | } -------------------------------------------------------------------------------- /qlLibs/codeql-pack.lock.yml: -------------------------------------------------------------------------------- 1 | --- 2 | lockVersion: 1.0.0 3 | dependencies: 4 | codeql/controlflow: 5 | version: 2.0.10 6 | codeql/dataflow: 7 | version: 2.0.10 8 | codeql/java-all: 9 | version: 7.3.2 10 | codeql/mad: 11 | version: 1.0.26 12 | codeql/quantum: 13 | version: 0.0.4 14 | codeql/rangeanalysis: 15 | version: 1.0.26 16 | codeql/regex: 17 | version: 1.0.26 18 | codeql/ssa: 19 | version: 2.0.2 20 | codeql/threat-models: 21 | version: 1.0.26 22 | codeql/tutorial: 23 | version: 1.0.26 24 | codeql/typeflow: 25 | version: 1.0.26 26 | codeql/typetracking: 27 | version: 2.0.10 28 | codeql/util: 29 | version: 2.0.13 30 | codeql/xml: 31 | version: 1.0.26 32 | compiled: false 33 | -------------------------------------------------------------------------------- /qlLibs/qlpack.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: n1ght/qlinspector 3 | version: 1.0.0 4 | library: false 5 | authors: n1ght 6 | dependencies: 7 | codeql/java-all: "*" 8 | defaultSuite: 9 | query: queries/main.ql 10 | 11 | -------------------------------------------------------------------------------- /qlLibs/queries/main.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name N1ght QL Inspector 3 | * @kind problem 4 | * @description 查找所有Java方法的测试查询 5 | * @problem.severity warning 6 | * @precision high 7 | * @id java/n1ght-ql-inspector 8 | * @tags security 9 | * maintainability 10 | */ 11 | import java 12 | 13 | from Method m 14 | where m.getName() = "exec" 15 | select m, "N1ght QL Inspector: Found method " + m.getName() -------------------------------------------------------------------------------- /rsrc.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yezere/codeql_n1ght/11359b67ad9a3b9e2c2678c4ce4bd260568f8005/rsrc.syso --------------------------------------------------------------------------------