-
Notifications
You must be signed in to change notification settings - Fork 10
replace semgrep with opengrep #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -273,7 +273,7 @@ var versionedToolNames = map[string]map[int]string{ | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| var simpleToolAliases = map[string]string{ | ||||||||||||||||||||||
| "lizard": "Lizard", | ||||||||||||||||||||||
| "semgrep": "Semgrep", | ||||||||||||||||||||||
| "opengrep": "Opengrep", | ||||||||||||||||||||||
| "pylint": "pylintpython3", | ||||||||||||||||||||||
| "trivy": "Trivy", | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
274
to
279
|
||||||||||||||||||||||
|
|
@@ -405,9 +405,9 @@ func runToolByName(toolName string, workDirectory string, pathsToCheck []string, | |||||||||||||||||||||
| case "dartanalyzer": | ||||||||||||||||||||||
| binaryPath := tool.Binaries[tool.Runtime] | ||||||||||||||||||||||
| return tools.RunDartAnalyzer(workDirectory, tool.InstallDir, binaryPath, pathsToCheck, outputFile, outputFormat) | ||||||||||||||||||||||
| case "semgrep": | ||||||||||||||||||||||
| case "opengrep": | ||||||||||||||||||||||
|
||||||||||||||||||||||
| case "opengrep": | |
| case "opengrep", "semgrep": |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetSupportedTools() will still report semgrep as supported because plugins/tools/semgrep/plugin.yaml is still embedded, but runToolByName no longer has a case "semgrep". This leads to a confusing flow where validateToolName("semgrep") succeeds and then execution fails with “unsupported tool: semgrep”. Either keep a semgrep case (possibly delegating to opengrep for backwards compatibility) or remove/stop embedding the semgrep plugin so it’s not advertised as supported.
| return tools.RunOpengrep(workDirectory, binaryPath, pathsToCheck, outputFile, outputFormat) | |
| return tools.RunOpengrep(workDirectory, binaryPath, pathsToCheck, outputFile, outputFormat) | |
| case "semgrep": | |
| // Backwards compatibility: delegate semgrep to opengrep if available, | |
| // otherwise fall back to a semgrep binary if defined. | |
| binaryPath, ok := tool.Binaries["opengrep"] | |
| if !ok { | |
| binaryPath = tool.Binaries["semgrep"] | |
| } | |
| return tools.RunOpengrep(workDirectory, binaryPath, pathsToCheck, outputFile, outputFormat) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -24,7 +24,7 @@ var toolConfigRegistry = map[string]ToolConfigCreator{ | |||||
| domain.PMD7: &pmd7ConfigCreator{}, | ||||||
| domain.PyLint: &pylintConfigCreator{}, | ||||||
| domain.DartAnalyzer: &dartAnalyzerConfigCreator{}, | ||||||
| domain.Semgrep: &semgrepConfigCreator{}, | ||||||
| domain.Opengrep: &opengrepConfigCreator{}, | ||||||
| domain.Lizard: &lizardConfigCreator{}, | ||||||
| domain.Revive: &reviveConfigCreator{}, | ||||||
| } | ||||||
|
|
@@ -121,23 +121,23 @@ func (d *dartAnalyzerConfigCreator) GetConfigFileName() string { | |||||
| } | ||||||
| func (d *dartAnalyzerConfigCreator) GetToolName() string { return "Dart Analyzer" } | ||||||
|
|
||||||
| // semgrepConfigCreator implements ToolConfigCreator for Semgrep | ||||||
| type semgrepConfigCreator struct{} | ||||||
| // opengrepConfigCreator implements ToolConfigCreator for Opengrep | ||||||
| type opengrepConfigCreator struct{} | ||||||
|
|
||||||
| func (s *semgrepConfigCreator) CreateConfig(toolsConfigDir string, patterns []domain.PatternConfiguration) error { | ||||||
| configData, err := tools.GetSemgrepConfig(patterns) | ||||||
| func (s *opengrepConfigCreator) CreateConfig(toolsConfigDir string, patterns []domain.PatternConfiguration) error { | ||||||
| configData, err := tools.GetOpengrepConfig(patterns) | ||||||
| if err != nil { | ||||||
| return fmt.Errorf("failed to create Semgrep config: %v", err) | ||||||
| return fmt.Errorf("failed to create Opengrep config: %v", err) | ||||||
|
||||||
| return fmt.Errorf("failed to create Opengrep config: %v", err) | |
| return fmt.Errorf("failed to create Opengrep config: %w", err) |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -55,7 +55,7 @@ var sarifShortNameMap = map[string]string{ | |||||||
| "Trivy": "trivy", | ||||||||
| "Pylint": "pylintpython3", | ||||||||
| "dartanalyzer": "dartanalyzer", | ||||||||
| "Semgrep": "semgrep", | ||||||||
| "Opengrep": "opengrep", | ||||||||
| "Lizard": "lizard", | ||||||||
| "revive": "revive", | ||||||||
|
||||||||
| "revive": "revive", | |
| "revive": "revive", | |
| "Semgrep": "semgrep", |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||
| "codacy/cli-v2/utils" | ||||||||||||||||||||||||||||||||||||||||||||||
| "codacy/cli-v2/utils/logger" | ||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||
| "io" | ||||||||||||||||||||||||||||||||||||||||||||||
| "log" | ||||||||||||||||||||||||||||||||||||||||||||||
| "os" | ||||||||||||||||||||||||||||||||||||||||||||||
| "os/exec" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -297,29 +298,43 @@ func installDownloadBasedTool(toolInfo *plugins.ToolInfo) error { | |||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to create installation directory: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Extract based on file extension | ||||||||||||||||||||||||||||||||||||||||||||||
| logger.Debug("Extracting tool", logrus.Fields{ | ||||||||||||||||||||||||||||||||||||||||||||||
| "tool": toolInfo.Name, | ||||||||||||||||||||||||||||||||||||||||||||||
| "version": toolInfo.Version, | ||||||||||||||||||||||||||||||||||||||||||||||
| "fileName": fileName, | ||||||||||||||||||||||||||||||||||||||||||||||
| "extractDirectory": toolInfo.InstallDir, | ||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||
| isArchive := strings.HasSuffix(fileName, ".zip") || strings.HasSuffix(fileName, ".tar.gz") || strings.HasSuffix(fileName, ".tgz") | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if strings.HasSuffix(fileName, ".zip") { | ||||||||||||||||||||||||||||||||||||||||||||||
| err = utils.ExtractZip(file.Name(), toolInfo.InstallDir) | ||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||
| err = utils.ExtractTarGz(file, toolInfo.InstallDir) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if isArchive { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Extract based on file extension | ||||||||||||||||||||||||||||||||||||||||||||||
| logger.Debug("Extracting tool", logrus.Fields{ | ||||||||||||||||||||||||||||||||||||||||||||||
| "tool": toolInfo.Name, | ||||||||||||||||||||||||||||||||||||||||||||||
| "version": toolInfo.Version, | ||||||||||||||||||||||||||||||||||||||||||||||
| "fileName": fileName, | ||||||||||||||||||||||||||||||||||||||||||||||
| "extractDirectory": toolInfo.InstallDir, | ||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to extract tool: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if strings.HasSuffix(fileName, ".zip") { | ||||||||||||||||||||||||||||||||||||||||||||||
| err = utils.ExtractZip(file.Name(), toolInfo.InstallDir) | ||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||
| err = utils.ExtractTarGz(file, toolInfo.InstallDir) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to extract tool: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Make sure all binaries are executable | ||||||||||||||||||||||||||||||||||||||||||||||
| for _, binaryPath := range toolInfo.Binaries { | ||||||||||||||||||||||||||||||||||||||||||||||
| err = os.Chmod(filepath.Join(toolInfo.InstallDir, filepath.Base(binaryPath)), constants.DefaultDirPerms) | ||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil && !os.IsNotExist(err) { | ||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to make binary executable: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
| // Make sure all binaries are executable | ||||||||||||||||||||||||||||||||||||||||||||||
| for _, binaryPath := range toolInfo.Binaries { | ||||||||||||||||||||||||||||||||||||||||||||||
| err = os.Chmod(filepath.Join(toolInfo.InstallDir, filepath.Base(binaryPath)), constants.DefaultDirPerms) | ||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil && !os.IsNotExist(err) { | ||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to make binary executable: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Bare binary — copy directly to the binary destination path | ||||||||||||||||||||||||||||||||||||||||||||||
| logger.Debug("Installing bare binary", logrus.Fields{ | ||||||||||||||||||||||||||||||||||||||||||||||
| "tool": toolInfo.Name, | ||||||||||||||||||||||||||||||||||||||||||||||
| "version": toolInfo.Version, | ||||||||||||||||||||||||||||||||||||||||||||||
| "downloadPath": downloadPath, | ||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||
| if err = installBareBinary(downloadPath, toolInfo); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("failed to install binary: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+329
to
+337
|
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -330,6 +345,44 @@ func installDownloadBasedTool(toolInfo *plugins.ToolInfo) error { | |||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // installBareBinary copies a downloaded bare binary to its destination path and makes it executable. | ||||||||||||||||||||||||||||||||||||||||||||||
| func installBareBinary(downloadPath string, toolInfo *plugins.ToolInfo) error { | ||||||||||||||||||||||||||||||||||||||||||||||
| var destPath string | ||||||||||||||||||||||||||||||||||||||||||||||
| for _, p := range toolInfo.Binaries { | ||||||||||||||||||||||||||||||||||||||||||||||
| destPath = p | ||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+351
to
+354
|
||||||||||||||||||||||||||||||||||||||||||||||
| for _, p := range toolInfo.Binaries { | |
| destPath = p | |
| break | |
| } | |
| // Prefer a binary whose key matches the tool name, if present. | |
| if p, ok := toolInfo.Binaries[toolInfo.Name]; ok && p != "" { | |
| destPath = p | |
| } else { | |
| // Fallback: if there is exactly one binary entry, use that. | |
| switch len(toolInfo.Binaries) { | |
| case 0: | |
| // Handled below as "no binary destination defined". | |
| case 1: | |
| for _, p := range toolInfo.Binaries { | |
| destPath = p | |
| break | |
| } | |
| default: | |
| return fmt.Errorf("multiple binary destinations defined for tool %s and none matches its name", toolInfo.Name) | |
| } | |
| } |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
installBareBinary selects the destination by taking the first value from toolInfo.Binaries, which is a map (iteration order is random). This can become nondeterministic/incorrect if a download-based tool ever defines multiple binaries. Consider selecting a specific key (e.g., tool name) or explicitly erroring unless exactly one binary path is defined.
| var destPath string | |
| for _, p := range toolInfo.Binaries { | |
| destPath = p | |
| break | |
| } | |
| if destPath == "" { | |
| return fmt.Errorf("no binary destination defined for tool %s", toolInfo.Name) | |
| } | |
| if len(toolInfo.Binaries) == 0 { | |
| return fmt.Errorf("no binary destination defined for tool %s", toolInfo.Name) | |
| } | |
| if len(toolInfo.Binaries) > 1 { | |
| return fmt.Errorf("multiple binary destinations defined for tool %s; bare binary install supports exactly one", toolInfo.Name) | |
| } | |
| var destPath string | |
| for _, p := range toolInfo.Binaries { | |
| destPath = p | |
| } |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
destPath comes from toolInfo.Binaries, which is built using path.Join (forward slashes). Using filepath.Dir(destPath)/os.Create(destPath) here can produce inconsistent results on Windows when paths contain mixed separators. Consider normalizing destPath with filepath.FromSlash/filepath.Clean (or constructing binary paths with filepath.Join consistently) before creating directories/files.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Package embedded provides access to embedded Opengrep rules. | ||
| package embedded | ||
|
|
||
| import "embed" | ||
|
|
||
| //go:embed rules.yaml | ||
| var rulesFS embed.FS | ||
|
|
||
| // GetOpengrepRules returns the embedded Opengrep rules. | ||
| // Opengrep is compatible with the semgrep rule format, so the same rules are used. | ||
| func GetOpengrepRules() []byte { | ||
| data, err := rulesFS.ReadFile("rules.yaml") | ||
| if err != nil { | ||
| panic(err) // This should never happen as the file is embedded | ||
| } | ||
| return data | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getToolNameis called withstrings.ToLower(run.Tool.Driver.Name)when processing SARIF uploads, but Opengrep's SARIF driver name is typically "Opengrep OSS". With the current alias map, "opengrep oss" won't be normalized to "Opengrep", soloadsToolAndPatternswon't find the tool and uploads can silently contain no issues. Consider normalizing SARIF tool names (e.g., strip a trailing " oss" / take the first token) or adding an explicit alias for "opengrep oss" -> "Opengrep".