From 481a128ba372f9d44b69b25602933e0353ad7c00 Mon Sep 17 00:00:00 2001 From: "andrzej.janczak" Date: Mon, 26 May 2025 13:16:05 +0200 Subject: [PATCH] fix: semgrep init --- .../tools/semgrep/{ => embedded}/rules.yaml | 0 plugins/tools/semgrep/embedded/semgrep.go | 16 +++ tools/semgrepConfigCreator.go | 60 ++------ tools/semgrepConfigCreator_test.go | 135 +++--------------- 4 files changed, 50 insertions(+), 161 deletions(-) rename plugins/tools/semgrep/{ => embedded}/rules.yaml (100%) create mode 100644 plugins/tools/semgrep/embedded/semgrep.go diff --git a/plugins/tools/semgrep/rules.yaml b/plugins/tools/semgrep/embedded/rules.yaml similarity index 100% rename from plugins/tools/semgrep/rules.yaml rename to plugins/tools/semgrep/embedded/rules.yaml diff --git a/plugins/tools/semgrep/embedded/semgrep.go b/plugins/tools/semgrep/embedded/semgrep.go new file mode 100644 index 00000000..ad25f336 --- /dev/null +++ b/plugins/tools/semgrep/embedded/semgrep.go @@ -0,0 +1,16 @@ +// Package embedded contains embedded files used by the tools package +package embedded + +import "embed" + +//go:embed rules.yaml +var rulesFS embed.FS + +// GetSemgrepRules returns the embedded Semgrep rules +func GetSemgrepRules() []byte { + data, err := rulesFS.ReadFile("rules.yaml") + if err != nil { + panic(err) // This should never happen as the file is embedded + } + return data +} diff --git a/tools/semgrepConfigCreator.go b/tools/semgrepConfigCreator.go index 44c051b6..a4e6d5e4 100644 --- a/tools/semgrepConfigCreator.go +++ b/tools/semgrepConfigCreator.go @@ -2,9 +2,9 @@ package tools import ( "codacy/cli-v2/domain" + "codacy/cli-v2/plugins/tools/semgrep/embedded" "fmt" "os" - "path/filepath" "strings" "gopkg.in/yaml.v3" @@ -20,19 +20,18 @@ type semgrepRulesFile struct { var getExecutablePath = os.Executable // FilterRulesFromFile extracts enabled rules from a rules.yaml file based on configuration -func FilterRulesFromFile(rulesFilePath string, config []domain.PatternConfiguration) ([]byte, error) { - // Read the rules.yaml file - data, err := os.ReadFile(rulesFilePath) - if err != nil { - return nil, fmt.Errorf("failed to read rules file: %w", err) - } - - // Parse the YAML file +func FilterRulesFromFile(rulesData []byte, config []domain.PatternConfiguration) ([]byte, error) { + // Parse the YAML data var allRules semgrepRulesFile - if err := yaml.Unmarshal(data, &allRules); err != nil { + if err := yaml.Unmarshal(rulesData, &allRules); err != nil { return nil, fmt.Errorf("failed to parse rules file: %w", err) } + // If no configuration provided, return all rules + if len(config) == 0 { + return rulesData, nil + } + // Create a map of enabled pattern IDs for faster lookup enabledPatterns := make(map[string]bool) for _, pattern := range config { @@ -67,45 +66,14 @@ func FilterRulesFromFile(rulesFilePath string, config []domain.PatternConfigurat return yaml.Marshal(filteredRules) } -// GetSemgrepConfig gets the Semgrep configuration based on the pattern configuration +// GetSemgrepConfig gets the Semgrep configuration based on the pattern configuration. +// If no configuration is provided, returns all default rules. func GetSemgrepConfig(config []domain.PatternConfiguration) ([]byte, error) { - // Get the executable's directory - execPath, err := getExecutablePath() - if err != nil { - return nil, fmt.Errorf("failed to get executable path: %w", err) - } - execDir := filepath.Dir(execPath) - - // Get the default rules file location relative to the executable - rulesFile := filepath.Join(execDir, "plugins", "tools", "semgrep", "rules.yaml") - - // Check if it exists and config is not empty - if _, err := os.Stat(rulesFile); err == nil && len(config) > 0 { - // Try to filter rules from the file - return FilterRulesFromFile(rulesFile, config) - } - - // If rules.yaml doesn't exist or config is empty, return an error - return nil, fmt.Errorf("rules.yaml not found or empty configuration") + return FilterRulesFromFile(embedded.GetSemgrepRules(), config) } // GetDefaultSemgrepConfig gets the default Semgrep configuration func GetDefaultSemgrepConfig() ([]byte, error) { - // Get the executable's directory - execPath, err := getExecutablePath() - if err != nil { - return nil, fmt.Errorf("failed to get executable path: %w", err) - } - execDir := filepath.Dir(execPath) - - // Get the default rules file location relative to the executable - rulesFile := filepath.Join(execDir, "plugins", "tools", "semgrep", "rules.yaml") - - // If the file exists, return its contents - if _, err := os.Stat(rulesFile); err == nil { - return os.ReadFile(rulesFile) - } - - // Return an error if rules.yaml doesn't exist - return nil, fmt.Errorf("rules.yaml not found") + // Return the embedded rules + return embedded.GetSemgrepRules(), nil } diff --git a/tools/semgrepConfigCreator_test.go b/tools/semgrepConfigCreator_test.go index 992fc413..d43ea033 100644 --- a/tools/semgrepConfigCreator_test.go +++ b/tools/semgrepConfigCreator_test.go @@ -2,135 +2,67 @@ package tools import ( "codacy/cli-v2/domain" - "os" - "path/filepath" + "codacy/cli-v2/plugins/tools/semgrep/embedded" "testing" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" ) -// Sample rules YAML content for testing -const sampleRulesYAML = `rules: - - id: rule1 - pattern: | - $X - message: "Test rule 1" - languages: [go] - severity: INFO - - id: rule2 - pattern: | - $Y - message: "Test rule 2" - languages: [javascript] - severity: WARNING - - id: rule3 - pattern-either: - - pattern: "foo()" - - pattern: "bar()" - message: "Test rule 3" - languages: [python] - severity: ERROR -` - // TestFilterRulesFromFile tests the FilterRulesFromFile function func TestFilterRulesFromFile(t *testing.T) { - // Create a temporary rules file - tempDir := t.TempDir() - rulesFile := filepath.Join(tempDir, "rules.yaml") - err := os.WriteFile(rulesFile, []byte(sampleRulesYAML), 0644) - assert.NoError(t, err) + // Get the actual rules file content + rulesData := embedded.GetSemgrepRules() // Test case 1: Filter with enabled rules config := []domain.PatternConfiguration{ { Enabled: true, PatternDefinition: domain.PatternDefinition{ - Id: "Semgrep_rule1", - Enabled: true, - }, - }, - { - Enabled: true, - PatternDefinition: domain.PatternDefinition{ - Id: "Semgrep_rule3", + Id: "Semgrep_ai.csharp.detect-openai.detect-openai", Enabled: true, }, }, } - result, err := FilterRulesFromFile(rulesFile, config) + result, err := FilterRulesFromFile(rulesData, config) assert.NoError(t, err) - // Parse the result and verify the rules + // Parse the result and verify we got filtered rules var parsedRules semgrepRulesFile err = yaml.Unmarshal(result, &parsedRules) assert.NoError(t, err) - assert.Equal(t, 2, len(parsedRules.Rules)) - - // Check that it contains rule1 and rule3 but not rule2 - ruleIDs := map[string]bool{} - for _, rule := range parsedRules.Rules { - id, _ := rule["id"].(string) - ruleIDs[id] = true - } - assert.True(t, ruleIDs["rule1"]) - assert.False(t, ruleIDs["rule2"]) - assert.True(t, ruleIDs["rule3"]) + assert.Equal(t, 1, len(parsedRules.Rules)) // Test case 2: No enabled rules should return an error noEnabledConfig := []domain.PatternConfiguration{ { Enabled: false, PatternDefinition: domain.PatternDefinition{ - Id: "Semgrep_rule1", + Id: "Semgrep_nonexistent", Enabled: false, }, }, } - _, err = FilterRulesFromFile(rulesFile, noEnabledConfig) + _, err = FilterRulesFromFile(rulesData, noEnabledConfig) assert.Error(t, err) assert.Contains(t, err.Error(), "no matching rules found") - // Test case 3: Non-existent rules file should return an error - _, err = FilterRulesFromFile(filepath.Join(tempDir, "nonexistent.yaml"), config) + // Test case 3: Invalid YAML should return an error + _, err = FilterRulesFromFile([]byte("invalid yaml"), config) assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to read rules file") + assert.Contains(t, err.Error(), "failed to parse rules file") } // TestGetSemgrepConfig tests the GetSemgrepConfig function func TestGetSemgrepConfig(t *testing.T) { - // Create a temporary rules file - tempDir := t.TempDir() - testRulesFile := filepath.Join(tempDir, "rules.yaml") - err := os.WriteFile(testRulesFile, []byte(sampleRulesYAML), 0644) - assert.NoError(t, err) - - // Create a mock executable path that points to our temp directory - originalGetExecutablePath := getExecutablePath - getExecutablePath = func() (string, error) { - return filepath.Join(tempDir, "test-executable"), nil - } - defer func() { - getExecutablePath = originalGetExecutablePath - }() - - // Create the plugins directory structure - pluginsDir := filepath.Join(tempDir, "plugins", "tools", "semgrep") - err = os.MkdirAll(pluginsDir, 0755) - assert.NoError(t, err) - - // Copy our test file to the plugins directory - err = os.WriteFile(filepath.Join(pluginsDir, "rules.yaml"), []byte(sampleRulesYAML), 0644) - assert.NoError(t, err) - // Test with valid configuration config := []domain.PatternConfiguration{ { Enabled: true, PatternDefinition: domain.PatternDefinition{ - Id: "Semgrep_rule1", + Id: "Semgrep_ai.csharp.detect-openai.detect-openai", Enabled: true, }, }, @@ -144,37 +76,16 @@ func TestGetSemgrepConfig(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 1, len(parsedRules.Rules)) - // Test with empty configuration - _, err = GetSemgrepConfig([]domain.PatternConfiguration{}) - assert.Error(t, err) + // Test with empty configuration (should return all rules) + result, err = GetSemgrepConfig([]domain.PatternConfiguration{}) + assert.NoError(t, err) + err = yaml.Unmarshal(result, &parsedRules) + assert.NoError(t, err) + assert.True(t, len(parsedRules.Rules) > 0) } // TestGetDefaultSemgrepConfig tests the GetDefaultSemgrepConfig function func TestGetDefaultSemgrepConfig(t *testing.T) { - // Create a temporary rules file - tempDir := t.TempDir() - testRulesFile := filepath.Join(tempDir, "rules.yaml") - err := os.WriteFile(testRulesFile, []byte(sampleRulesYAML), 0644) - assert.NoError(t, err) - - // Create a mock executable path that points to our temp directory - originalGetExecutablePath := getExecutablePath - getExecutablePath = func() (string, error) { - return filepath.Join(tempDir, "test-executable"), nil - } - defer func() { - getExecutablePath = originalGetExecutablePath - }() - - // Create the plugins directory structure - pluginsDir := filepath.Join(tempDir, "plugins", "tools", "semgrep") - err = os.MkdirAll(pluginsDir, 0755) - assert.NoError(t, err) - - // Copy our test file to the plugins directory - err = os.WriteFile(filepath.Join(pluginsDir, "rules.yaml"), []byte(sampleRulesYAML), 0644) - assert.NoError(t, err) - // Test getting default config result, err := GetDefaultSemgrepConfig() assert.NoError(t, err) @@ -182,11 +93,5 @@ func TestGetDefaultSemgrepConfig(t *testing.T) { var parsedRules semgrepRulesFile err = yaml.Unmarshal(result, &parsedRules) assert.NoError(t, err) - assert.Equal(t, 3, len(parsedRules.Rules)) - - // Test when rules.yaml doesn't exist - os.Remove(filepath.Join(pluginsDir, "rules.yaml")) - _, err = GetDefaultSemgrepConfig() - assert.Error(t, err) - assert.Contains(t, err.Error(), "rules.yaml not found") + assert.True(t, len(parsedRules.Rules) > 0) }