Skip to content

Commit 68ffbf3

Browse files
works with semgrep all definitions file
wip: semgrep patterns empty
1 parent 9a3ae32 commit 68ffbf3

File tree

16 files changed

+116567
-25
lines changed

16 files changed

+116567
-25
lines changed

.codacy/codacy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ tools:
66
77
88
9+

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ go.work.sum
2727
# Codacy CLI
2828
cli-v2
2929
codacy-cli
30+
31+
#Macos
32+
.DS_Store

cmd/analyze.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,18 @@ func runPylintAnalysis(workDirectory string, pathsToCheck []string, outputFile s
226226
}
227227
}
228228

229+
func runSemgrepAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) {
230+
semgrep := config.Config.Tools()["semgrep"]
231+
if semgrep == nil {
232+
log.Fatal("Semgrep tool configuration not found")
233+
}
234+
235+
err := tools.RunSemgrep(workDirectory, semgrep, pathsToCheck, outputFile, outputFormat)
236+
if err != nil {
237+
log.Fatalf("Failed to run Semgrep analysis: %v", err)
238+
}
239+
}
240+
229241
var analyzeCmd = &cobra.Command{
230242
Use: "analyze",
231243
Short: "Runs all configured linters.",
@@ -312,6 +324,8 @@ func runTool(workDirectory string, toolName string, args []string, outputFile st
312324
runPmdAnalysis(workDirectory, args, outputFile, outputFormat)
313325
case "pylint":
314326
runPylintAnalysis(workDirectory, args, outputFile, outputFormat)
327+
case "semgrep":
328+
runSemgrepAnalysis(workDirectory, args, outputFile, outputFormat)
315329
default:
316330
log.Printf("Warning: Unsupported tool: %s\n", toolName)
317331
}

cmd/init.go

Lines changed: 154 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"time"
1919

2020
"github.com/spf13/cobra"
21+
"gopkg.in/yaml.v3"
2122
)
2223

2324
const CodacyApiBase = "https://app.codacy.com"
@@ -141,10 +142,11 @@ func configFileTemplate(tools []tools.Tool) string {
141142

142143
// Default versions
143144
defaultVersions := map[string]string{
144-
ESLint: "9.3.0",
145-
Trivy: "0.59.1",
146-
PyLint: "3.3.6",
147-
PMD: "6.55.0",
145+
ESLint: "9.3.0",
146+
Trivy: "0.59.1",
147+
PyLint: "3.3.6",
148+
PMD: "6.55.0",
149+
Semgrep: "1.78.0",
148150
}
149151

150152
// Build map of enabled tools with their versions
@@ -188,10 +190,11 @@ func configFileTemplate(tools []tools.Tool) string {
188190
if len(tools) > 0 {
189191
// Add only the tools that are in the API response (enabled tools)
190192
uuidToName := map[string]string{
191-
ESLint: "eslint",
192-
Trivy: "trivy",
193-
PyLint: "pylint",
194-
PMD: "pmd",
193+
ESLint: "eslint",
194+
Trivy: "trivy",
195+
PyLint: "pylint",
196+
PMD: "pmd",
197+
Semgrep: "semgrep",
195198
}
196199

197200
for uuid, name := range uuidToName {
@@ -205,6 +208,7 @@ func configFileTemplate(tools []tools.Tool) string {
205208
sb.WriteString(fmt.Sprintf(" - trivy@%s\n", defaultVersions[Trivy]))
206209
sb.WriteString(fmt.Sprintf(" - pylint@%s\n", defaultVersions[PyLint]))
207210
sb.WriteString(fmt.Sprintf(" - pmd@%s\n", defaultVersions[PMD]))
211+
sb.WriteString(fmt.Sprintf(" - semgrep@%s\n", defaultVersions[Semgrep]))
208212
}
209213

210214
return sb.String()
@@ -257,7 +261,8 @@ func buildRepositoryConfigurationFiles(token string) error {
257261

258262
// Only generate config files for tools not using their own config file
259263
for _, tool := range configuredToolsWithUI {
260-
url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns?enabled=true",
264+
265+
url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns?enabled=true&limit=1000",
261266
CodacyApiBase,
262267
initFlags.provider,
263268
initFlags.organization,
@@ -380,6 +385,19 @@ func createToolFileConfigurations(tool tools.Tool, patternConfiguration []domain
380385
}
381386
}
382387
fmt.Println("Pylint configuration created based on Codacy settings")
388+
case Semgrep:
389+
if len(patternConfiguration) > 0 {
390+
err := createSemgrepConfigFile(patternConfiguration, toolsConfigDir)
391+
if err != nil {
392+
return fmt.Errorf("failed to create Semgrep config: %v", err)
393+
}
394+
} else {
395+
err := createDefaultSemgrepConfigFile(toolsConfigDir)
396+
if err != nil {
397+
return fmt.Errorf("failed to create default Semgrep config: %v", err)
398+
}
399+
}
400+
fmt.Println("Semgrep configuration created based on Codacy settings")
383401
}
384402
return nil
385403
}
@@ -434,6 +452,128 @@ func createDefaultEslintConfigFile(toolsConfigDir string) error {
434452
return os.WriteFile(filepath.Join(toolsConfigDir, "eslint.config.mjs"), []byte(content), utils.DefaultFilePerms)
435453
}
436454

455+
// SemgrepRulesFile represents the structure of the rules.yaml file
456+
type SemgrepRulesFile struct {
457+
Rules []map[string]interface{} `yaml:"rules"`
458+
}
459+
460+
// createSemgrepConfigFile creates a semgrep.yaml configuration file based on the API configuration
461+
func createSemgrepConfigFile(config []domain.PatternConfiguration, toolsConfigDir string) error {
462+
// When specific patterns are configured, filter rules from rules.yaml
463+
if len(config) > 0 {
464+
// First try to read the rules.yaml file
465+
rulesFile := filepath.Join("plugins", "tools", "semgrep", "rules.yaml")
466+
if _, err := os.Stat(rulesFile); err == nil {
467+
// Read and parse the rules.yaml file
468+
data, err := os.ReadFile(rulesFile)
469+
if err != nil {
470+
fmt.Printf("Warning: Failed to read rules.yaml: %v\n", err)
471+
// Fall back to the old method
472+
semgrepConfigurationString := tools.CreateSemgrepConfig(config)
473+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(semgrepConfigurationString), utils.DefaultFilePerms)
474+
}
475+
476+
// Parse the YAML file just enough to get the rules array
477+
var allRules SemgrepRulesFile
478+
if err := yaml.Unmarshal(data, &allRules); err != nil {
479+
fmt.Printf("Warning: Failed to parse rules.yaml: %v\n", err)
480+
// Fall back to the old method
481+
semgrepConfigurationString := tools.CreateSemgrepConfig(config)
482+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(semgrepConfigurationString), utils.DefaultFilePerms)
483+
}
484+
485+
// Create a map of enabled pattern IDs for faster lookup
486+
enabledPatterns := make(map[string]bool)
487+
for _, pattern := range config {
488+
if pattern.Enabled && pattern.PatternDefinition.Enabled {
489+
// Extract rule ID from pattern ID
490+
parts := strings.SplitN(pattern.PatternDefinition.Id, "_", 2)
491+
if len(parts) == 2 {
492+
ruleID := parts[1]
493+
enabledPatterns[ruleID] = true
494+
}
495+
}
496+
}
497+
498+
// Filter the rules based on enabled patterns
499+
var filteredRules SemgrepRulesFile
500+
filteredRules.Rules = []map[string]interface{}{}
501+
502+
for _, rule := range allRules.Rules {
503+
// Get the rule ID
504+
if ruleID, ok := rule["id"].(string); ok && enabledPatterns[ruleID] {
505+
// If this rule is enabled, include it
506+
filteredRules.Rules = append(filteredRules.Rules, rule)
507+
}
508+
}
509+
510+
// If no rules match, use the old method
511+
if len(filteredRules.Rules) == 0 {
512+
fmt.Println("Warning: No matching rules found in rules.yaml")
513+
semgrepConfigurationString := tools.CreateSemgrepConfig(config)
514+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(semgrepConfigurationString), utils.DefaultFilePerms)
515+
}
516+
517+
// Marshal the filtered rules back to YAML
518+
filteredData, err := yaml.Marshal(filteredRules)
519+
if err != nil {
520+
fmt.Printf("Warning: Failed to marshal filtered rules: %v\n", err)
521+
// Fall back to the old method
522+
semgrepConfigurationString := tools.CreateSemgrepConfig(config)
523+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(semgrepConfigurationString), utils.DefaultFilePerms)
524+
}
525+
526+
// Write the filtered rules to semgrep.yaml
527+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), filteredData, utils.DefaultFilePerms)
528+
}
529+
530+
// If rules.yaml doesn't exist, fall back to the old method
531+
semgrepConfigurationString := tools.CreateSemgrepConfig(config)
532+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(semgrepConfigurationString), utils.DefaultFilePerms)
533+
}
534+
535+
// For default case with no specific patterns, use the entire rules.yaml
536+
rulesFile := filepath.Join("plugins", "tools", "semgrep", "rules.yaml")
537+
if _, err := os.Stat(rulesFile); err == nil {
538+
data, err := os.ReadFile(rulesFile)
539+
if err != nil {
540+
fmt.Printf("Warning: Failed to read rules.yaml: %v\n", err)
541+
// Fall back to the old method for default config
542+
emptyConfig := []domain.PatternConfiguration{}
543+
content := tools.CreateSemgrepConfig(emptyConfig)
544+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(content), utils.DefaultFilePerms)
545+
}
546+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), data, utils.DefaultFilePerms)
547+
}
548+
549+
// Fall back to default config
550+
emptyConfig := []domain.PatternConfiguration{}
551+
content := tools.CreateSemgrepConfig(emptyConfig)
552+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(content), utils.DefaultFilePerms)
553+
}
554+
555+
// createDefaultSemgrepConfigFile creates a default semgrep.yaml configuration file
556+
func createDefaultSemgrepConfigFile(toolsConfigDir string) error {
557+
// Use rules.yaml as the default
558+
rulesFile := filepath.Join("plugins", "tools", "semgrep", "rules.yaml")
559+
if _, err := os.Stat(rulesFile); err == nil {
560+
data, err := os.ReadFile(rulesFile)
561+
if err != nil {
562+
fmt.Printf("Warning: Failed to read rules.yaml: %v\n", err)
563+
// Fall back to the old method
564+
emptyConfig := []domain.PatternConfiguration{}
565+
content := tools.CreateSemgrepConfig(emptyConfig)
566+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(content), utils.DefaultFilePerms)
567+
}
568+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), data, utils.DefaultFilePerms)
569+
}
570+
571+
// Fall back to the old method if rules.yaml doesn't exist
572+
emptyConfig := []domain.PatternConfiguration{}
573+
content := tools.CreateSemgrepConfig(emptyConfig)
574+
return os.WriteFile(filepath.Join(toolsConfigDir, "semgrep.yaml"), []byte(content), utils.DefaultFilePerms)
575+
}
576+
437577
// cleanConfigDirectory removes all previous configuration files in the tools-configs directory
438578
func cleanConfigDirectory(toolsConfigDir string) error {
439579
// Check if directory exists
@@ -462,8 +602,9 @@ func cleanConfigDirectory(toolsConfigDir string) error {
462602
}
463603

464604
const (
465-
ESLint string = "f8b29663-2cb2-498d-b923-a10c6a8c05cd"
466-
Trivy string = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb"
467-
PMD string = "9ed24812-b6ee-4a58-9004-0ed183c45b8f"
468-
PyLint string = "31677b6d-4ae0-4f56-8041-606a8d7a8e61"
605+
ESLint string = "f8b29663-2cb2-498d-b923-a10c6a8c05cd"
606+
Trivy string = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb"
607+
PMD string = "9ed24812-b6ee-4a58-9004-0ed183c45b8f"
608+
PyLint string = "31677b6d-4ae0-4f56-8041-606a8d7a8e61"
609+
Semgrep string = "6792c561-236d-41b7-ba5e-9d6bee0d548b"
469610
)

domain/patternConfiguration.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,22 @@ type ParameterConfiguration struct {
66
}
77

88
type PatternDefinition struct {
9-
Id string `json:"id"`
9+
Id string `json:"id"`
10+
Category string `json:"category"`
11+
Level string `json:"level"`
12+
SeverityLevel string `json:"severityLevel"`
13+
Enabled bool `json:"enabled"`
14+
Parameters []ParameterConfiguration `json:"parameters"`
15+
Title string `json:"title"`
16+
Description string `json:"description"`
17+
Explanation string `json:"explanation"`
18+
Languages []string `json:"languages"`
19+
TimeToFix int `json:"timeToFix"`
1020
}
1121

1222
type PatternConfiguration struct {
1323
PatternDefinition PatternDefinition `json:"patternDefinition"`
1424
Parameters []ParameterConfiguration
25+
Enabled bool `json:"enabled"`
26+
IsCustom bool `json:"isCustom"`
1527
}

plugins/tool-utils_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func TestGetSupportedTools(t *testing.T) {
166166
"pmd",
167167
"pylint",
168168
"trivy",
169+
"semgrep",
169170
},
170171
expectedError: false,
171172
},

plugins/tools/semgrep/plugin.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: semgrep
2+
description: Static Analysis Security Testing (SAST) tool
3+
runtime: python
4+
runtime_binaries:
5+
package_manager: python3
6+
execution: python3
7+
binaries:
8+
- name: python
9+
path: "venv/bin/python3"
10+
formatters:
11+
- name: json
12+
flag: "--json"
13+
output_options:
14+
file_flag: "--output"
15+
analysis_options:
16+
default_path: "."

0 commit comments

Comments
 (0)