From 891d477c585320b39d2612daadd5d305ba6cad04 Mon Sep 17 00:00:00 2001 From: Yasmin Zhamborova Date: Mon, 7 Apr 2025 12:21:58 +0200 Subject: [PATCH 1/7] move pmd files --- cmd/analyze.go | 7 ++++--- cmd/init.go | 5 +++-- tools/{ => pmd}/pmdConfigCreator.go | 7 ++++--- tools/{ => pmd}/pmdRunner.go | 3 +-- 4 files changed, 12 insertions(+), 10 deletions(-) rename tools/{ => pmd}/pmdConfigCreator.go (97%) rename tools/{ => pmd}/pmdRunner.go (93%) diff --git a/cmd/analyze.go b/cmd/analyze.go index 56108e0e..ab62e72b 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -3,6 +3,7 @@ package cmd import ( "codacy/cli-v2/config" "codacy/cli-v2/tools" + "codacy/cli-v2/tools/pmd" "encoding/json" "fmt" "io" @@ -206,10 +207,10 @@ func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile st } func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) { - pmd := config.Config.Tools()["pmd"] - pmdBinary := pmd.Binaries["pmd"] + tool := config.Config.Tools()["pmd"] + pmdBinary := tool.Binaries["pmd"] - err := tools.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat, pmdRulesetFile) + err := pmd.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat, pmdRulesetFile) if err != nil { log.Fatalf("Error running PMD: %v", err) } diff --git a/cmd/init.go b/cmd/init.go index 203b799d..743f961d 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,6 +3,7 @@ package cmd import ( "codacy/cli-v2/config" "codacy/cli-v2/tools" + "codacy/cli-v2/tools/pmd" "encoding/json" "errors" "fmt" @@ -295,13 +296,13 @@ func extractPMDConfiguration(toolConfigurations []CodacyToolConfiguration) *Coda func createPMDConfigFile(config CodacyToolConfiguration) error { pmdDomainConfiguration := convertAPIToolConfigurationToDomain(config) - pmdConfigurationString := tools.CreatePmdConfig(pmdDomainConfiguration) + pmdConfigurationString := pmd.CreatePmdConfig(pmdDomainConfiguration) return os.WriteFile("pmd-ruleset.xml", []byte(pmdConfigurationString), 0644) } func createDefaultPMDConfigFile() error { emptyConfig := tools.ToolConfiguration{} - content := tools.CreatePmdConfig(emptyConfig) + content := pmd.CreatePmdConfig(emptyConfig) return os.WriteFile("pmd-ruleset.xml", []byte(content), 0644) } diff --git a/tools/pmdConfigCreator.go b/tools/pmd/pmdConfigCreator.go similarity index 97% rename from tools/pmdConfigCreator.go rename to tools/pmd/pmdConfigCreator.go index a32dc8d6..c3da6f63 100644 --- a/tools/pmdConfigCreator.go +++ b/tools/pmd/pmdConfigCreator.go @@ -1,13 +1,14 @@ -package tools +package pmd import ( + "codacy/cli-v2/tools" _ "embed" "encoding/xml" "fmt" "strings" ) -//go:embed pmd/default-ruleset.xml +//go:embed default-ruleset.xml var defaultPMDRuleset string // Parameter represents a rule parameter @@ -171,7 +172,7 @@ func ConvertToPMDRuleset(rules []Rule) (string, error) { } // CreatePmdConfig creates a PMD configuration from the provided tool configuration -func CreatePmdConfig(configuration ToolConfiguration) string { +func CreatePmdConfig(configuration tools.ToolConfiguration) string { // If no patterns provided, return the default ruleset if len(configuration.PatternsConfiguration) == 0 { return defaultPMDRuleset diff --git a/tools/pmdRunner.go b/tools/pmd/pmdRunner.go similarity index 93% rename from tools/pmdRunner.go rename to tools/pmd/pmdRunner.go index 996d30b9..088be432 100644 --- a/tools/pmdRunner.go +++ b/tools/pmd/pmdRunner.go @@ -1,4 +1,4 @@ -package tools +package pmd import ( "os" @@ -6,7 +6,6 @@ import ( "strings" ) -// RunPmd executes PMD static code analyzer with the specified options func RunPmd(repositoryToAnalyseDirectory string, pmdBinary string, pathsToCheck []string, outputFile string, outputFormat string, rulesetFile string) error { cmdArgs := []string{"pmd"} From 98a6c9cd4639407702a805c7642be4ed77e8cdb4 Mon Sep 17 00:00:00 2001 From: Yasmin Zhamborova Date: Mon, 7 Apr 2025 12:26:45 +0200 Subject: [PATCH 2/7] move pmd test files --- tools/{ => test/pmd}/pmdConfigCreator_test.go | 32 +-- tools/{ => test/pmd}/pmdRunner_test.go | 10 +- .../repositories/pmd/RulesBreaker.java | 20 -- .../repositories/pmd/expected-ruleset.xml | 15 -- .../testdata/repositories/pmd/expected.sarif | 203 ------------------ .../testdata/repositories/pmd/pmd-ruleset.xml | 24 --- 6 files changed, 26 insertions(+), 278 deletions(-) rename tools/{ => test/pmd}/pmdConfigCreator_test.go (84%) rename tools/{ => test/pmd}/pmdRunner_test.go (92%) delete mode 100644 tools/testdata/repositories/pmd/RulesBreaker.java delete mode 100644 tools/testdata/repositories/pmd/expected-ruleset.xml delete mode 100644 tools/testdata/repositories/pmd/expected.sarif delete mode 100644 tools/testdata/repositories/pmd/pmd-ruleset.xml diff --git a/tools/pmdConfigCreator_test.go b/tools/test/pmd/pmdConfigCreator_test.go similarity index 84% rename from tools/pmdConfigCreator_test.go rename to tools/test/pmd/pmdConfigCreator_test.go index b53175b0..3b4f257f 100644 --- a/tools/pmdConfigCreator_test.go +++ b/tools/test/pmd/pmdConfigCreator_test.go @@ -1,4 +1,4 @@ -package tools +package pmd import ( "encoding/xml" @@ -7,6 +7,9 @@ import ( "strings" "testing" + "codacy/cli-v2/tools" + "codacy/cli-v2/tools/pmd" + "github.com/stretchr/testify/assert" ) @@ -35,13 +38,18 @@ type PMDProperty struct { Value string `xml:"value,attr"` } +// CreatePmdConfig is a wrapper around the actual implementation to make it testable +func CreatePmdConfig(config tools.ToolConfiguration) string { + return pmd.CreatePmdConfig(config) +} + func TestCreatePmdConfig(t *testing.T) { // Setup test configuration with patterns - config := ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ + config := tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ { PatternId: "java/codestyle/AtLeastOneConstructor", - ParameterConfigurations: []PatternParameterConfiguration{ + ParameterConfigurations: []tools.PatternParameterConfiguration{ { Name: "enabled", Value: "true", @@ -50,7 +58,7 @@ func TestCreatePmdConfig(t *testing.T) { }, { PatternId: "java/design/UnusedPrivateField", - ParameterConfigurations: []PatternParameterConfiguration{ + ParameterConfigurations: []tools.PatternParameterConfiguration{ { Name: "enabled", Value: "true", @@ -59,7 +67,7 @@ func TestCreatePmdConfig(t *testing.T) { }, { PatternId: "java/design/LoosePackageCoupling", - ParameterConfigurations: []PatternParameterConfiguration{ + ParameterConfigurations: []tools.PatternParameterConfiguration{ { Name: "enabled", Value: "true", @@ -77,7 +85,7 @@ func TestCreatePmdConfig(t *testing.T) { generatedConfig := CreatePmdConfig(config) // Read expected ruleset - expectedRulesetPath := filepath.Join("testdata", "repositories", "pmd", "expected-ruleset.xml") + expectedRulesetPath := "expected-ruleset.xml" expectedRulesetBytes, err := os.ReadFile(expectedRulesetPath) if err != nil { t.Fatalf("Failed to read expected ruleset: %v", err) @@ -105,11 +113,11 @@ func TestCreatePmdConfig(t *testing.T) { } func TestCreatePmdConfigWithDisabledRules(t *testing.T) { - config := ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ + config := tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ { PatternId: "java/codestyle/AtLeastOneConstructor", - ParameterConfigurations: []PatternParameterConfiguration{ + ParameterConfigurations: []tools.PatternParameterConfiguration{ { Name: "enabled", Value: "false", @@ -163,8 +171,8 @@ func TestCreatePmdConfigEmpty(t *testing.T) { } defer os.Chdir(cwd) - config := ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{}, + config := tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{}, } obtainedConfig := CreatePmdConfig(config) diff --git a/tools/pmdRunner_test.go b/tools/test/pmd/pmdRunner_test.go similarity index 92% rename from tools/pmdRunner_test.go rename to tools/test/pmd/pmdRunner_test.go index 453bfe11..d5e255d8 100644 --- a/tools/pmdRunner_test.go +++ b/tools/test/pmd/pmdRunner_test.go @@ -1,4 +1,4 @@ -package tools +package pmd import ( "encoding/json" @@ -8,6 +8,8 @@ import ( "strings" "testing" + "codacy/cli-v2/tools/pmd" + "github.com/stretchr/testify/assert" ) @@ -50,20 +52,20 @@ func TestRunPmdToFile(t *testing.T) { } // Use the correct path relative to tools directory - testDirectory := filepath.Join(currentDirectory, "testdata", "repositories", "pmd") + testDirectory := "." tempResultFile := filepath.Join(os.TempDir(), "pmd.sarif") defer os.Remove(tempResultFile) // Use absolute paths repositoryToAnalyze := testDirectory // Use the standard ruleset file for testing the PMD runner functionality - rulesetFile := filepath.Join(testDirectory, "pmd-ruleset.xml") + rulesetFile := "pmd-ruleset.xml" // Use the same path as defined in plugin.yaml pmdBinary := filepath.Join(homeDirectory, ".cache/codacy/tools/pmd@6.55.0/pmd-bin-6.55.0/bin/run.sh") // Run PMD - err = RunPmd(repositoryToAnalyze, pmdBinary, nil, tempResultFile, "sarif", rulesetFile) + err = pmd.RunPmd(repositoryToAnalyze, pmdBinary, nil, tempResultFile, "sarif", rulesetFile) if err != nil { t.Fatalf("Failed to run pmd: %v", err) } diff --git a/tools/testdata/repositories/pmd/RulesBreaker.java b/tools/testdata/repositories/pmd/RulesBreaker.java deleted file mode 100644 index fcb86a61..00000000 --- a/tools/testdata/repositories/pmd/RulesBreaker.java +++ /dev/null @@ -1,20 +0,0 @@ - -/** - * This class demonstrates various PMD rule violations. - * It is used for testing PMD's rule detection capabilities. - * - * @author Codacy Test Team - * @version 1.0 - */ - -package tools.testdata.repositories.pmd; - -/** - * A class that demonstrates PMD rule violations. - */ -public class RulesBreaker { - - // Breaking naming convention rules - private int x; - -} diff --git a/tools/testdata/repositories/pmd/expected-ruleset.xml b/tools/testdata/repositories/pmd/expected-ruleset.xml deleted file mode 100644 index d08d8bfc..00000000 --- a/tools/testdata/repositories/pmd/expected-ruleset.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - Codacy PMD Ruleset - - - - - - - - - \ No newline at end of file diff --git a/tools/testdata/repositories/pmd/expected.sarif b/tools/testdata/repositories/pmd/expected.sarif deleted file mode 100644 index fb011bc4..00000000 --- a/tools/testdata/repositories/pmd/expected.sarif +++ /dev/null @@ -1,203 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", - "version": "2.1.0", - "runs": [ - { - "tool": { - "driver": { - "name": "PMD", - "version": "6.55.0", - "informationUri": "https://pmd.github.io/pmd/", - "rules": [ - { - "id": "AtLeastOneConstructor", - "shortDescription": { - "text": "Each class should declare at least one constructor" - }, - "fullDescription": { - "text": "\n\nEach non-static class should declare at least one constructor.\nClasses with solely static members are ignored, refer to [UseUtilityClassRule](pmd_rules_java_design.html#useutilityclass) to detect those.\n\n " - }, - "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_codestyle.html#atleastoneconstructor", - "help": { - "text": "\n\nEach non-static class should declare at least one constructor.\nClasses with solely static members are ignored, refer to [UseUtilityClassRule](pmd_rules_java_design.html#useutilityclass) to detect those.\n\n " - }, - "properties": { - "ruleset": "Code Style", - "priority": 3, - "tags": [ - "Code Style" - ] - } - }, - { - "id": "UnusedPrivateField", - "shortDescription": { - "text": "Avoid unused private fields such as 'x'." - }, - "fullDescription": { - "text": "\nDetects when a private field is declared and/or assigned a value, but not used.\n\nSince PMD 6.50.0 private fields are ignored, if the fields are annotated with any annotation or the\nenclosing class has any annotation. Annotations often enable a framework (such as dependency injection, mocking\nor e.g. Lombok) which use the fields by reflection or other means. This usage can't be detected by static code analysis.\nPreviously these frameworks where explicitly allowed by listing their annotations in the property\n\"ignoredAnnotations\", but that turned out to be prone of false positive for any not explicitly considered framework.\n " - }, - "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_bestpractices.html#unusedprivatefield", - "help": { - "text": "\nDetects when a private field is declared and/or assigned a value, but not used.\n\nSince PMD 6.50.0 private fields are ignored, if the fields are annotated with any annotation or the\nenclosing class has any annotation. Annotations often enable a framework (such as dependency injection, mocking\nor e.g. Lombok) which use the fields by reflection or other means. This usage can't be detected by static code analysis.\nPreviously these frameworks where explicitly allowed by listing their annotations in the property\n\"ignoredAnnotations\", but that turned out to be prone of false positive for any not explicitly considered framework.\n " - }, - "properties": { - "ruleset": "Best Practices", - "priority": 3, - "tags": [ - "Best Practices" - ] - } - }, - { - "id": "ShortVariable", - "shortDescription": { - "text": "Avoid variables with short names like x" - }, - "fullDescription": { - "text": "\nFields, local variables, or parameter names that are very short are not helpful to the reader.\n " - }, - "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_codestyle.html#shortvariable", - "help": { - "text": "\nFields, local variables, or parameter names that are very short are not helpful to the reader.\n " - }, - "properties": { - "ruleset": "Code Style", - "priority": 3, - "tags": [ - "Code Style" - ] - } - }, - { - "id": "CommentRequired", - "shortDescription": { - "text": "Field comments are required" - }, - "fullDescription": { - "text": "\nDenotes whether javadoc (formal) comments are required (or unwanted) for specific language elements.\n " - }, - "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_documentation.html#commentrequired", - "help": { - "text": "\nDenotes whether javadoc (formal) comments are required (or unwanted) for specific language elements.\n " - }, - "properties": { - "ruleset": "Documentation", - "priority": 3, - "tags": [ - "Documentation" - ] - } - } - ] - } - }, - "results": [ - { - "ruleId": "AtLeastOneConstructor", - "ruleIndex": 0, - "message": { - "text": "Each class should declare at least one constructor" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "testdata/repositories/pmd/RulesBreaker.java" - }, - "region": { - "startLine": 15, - "startColumn": 8, - "endLine": 20, - "endColumn": 1 - } - } - } - ] - }, - { - "ruleId": "UnusedPrivateField", - "ruleIndex": 1, - "message": { - "text": "Avoid unused private fields such as 'x'." - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "testdata/repositories/pmd/RulesBreaker.java" - }, - "region": { - "startLine": 18, - "startColumn": 17, - "endLine": 18, - "endColumn": 17 - } - } - } - ] - }, - { - "ruleId": "ShortVariable", - "ruleIndex": 2, - "message": { - "text": "Avoid variables with short names like x" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "testdata/repositories/pmd/RulesBreaker.java" - }, - "region": { - "startLine": 18, - "startColumn": 17, - "endLine": 18, - "endColumn": 17 - } - } - } - ] - }, - { - "ruleId": "CommentRequired", - "ruleIndex": 3, - "message": { - "text": "Field comments are required" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "testdata/repositories/pmd/RulesBreaker.java" - }, - "region": { - "startLine": 18, - "startColumn": 13, - "endLine": 18, - "endColumn": 18 - } - } - } - ] - } - ], - "invocations": [ - { - "executionSuccessful": false, - "toolConfigurationNotifications": [ - { - "associatedRule": { - "id": "LoosePackageCoupling" - }, - "message": { - "text": "No packages or classes specified" - } - } - ], - "toolExecutionNotifications": [] - } - ] - } - ] -} diff --git a/tools/testdata/repositories/pmd/pmd-ruleset.xml b/tools/testdata/repositories/pmd/pmd-ruleset.xml deleted file mode 100644 index 11377f9e..00000000 --- a/tools/testdata/repositories/pmd/pmd-ruleset.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Basic PMD ruleset for Codacy analysis - - - - - - - - - - - - - - - \ No newline at end of file From dbadf360319ee1ae3349636b267d36578a41b42e Mon Sep 17 00:00:00 2001 From: Yasmin Zhamborova Date: Mon, 7 Apr 2025 13:10:14 +0200 Subject: [PATCH 3/7] move eslint files --- cmd/analyze.go | 8 +- cmd/init.go | 5 +- tools/{ => eslint}/eslintConfigCreator.go | 6 +- tools/{ => eslint}/eslintRunner.go | 12 +- tools/eslintRunner_test.go | 49 ------- .../eslint}/eslintConfigCreator_test.go | 37 ++--- tools/test/eslint/eslintRunner_test.go | 130 ++++++++++++++++++ .../repositories/test1/expected.sarif | 29 ---- .../repositories/test1/src/eslint.config.mjs | 7 - tools/testdata/repositories/test1/src/test.js | 1 - .../testdata/repositories/test1/eslint.sarif | 29 ---- .../repositories/test1/expected.sarif | 29 ---- .../testdata/repositories/test1/sarif.json | 29 ---- 13 files changed, 171 insertions(+), 200 deletions(-) rename tools/{ => eslint}/eslintConfigCreator.go (94%) rename tools/{ => eslint}/eslintRunner.go (79%) delete mode 100644 tools/eslintRunner_test.go rename tools/{ => test/eslint}/eslintConfigCreator_test.go (68%) create mode 100644 tools/test/eslint/eslintRunner_test.go delete mode 100644 tools/testdata/repositories/test1/expected.sarif delete mode 100644 tools/testdata/repositories/test1/src/eslint.config.mjs delete mode 100644 tools/testdata/repositories/test1/src/test.js delete mode 100644 tools/testdata/repositories/test1/src/testdata/repositories/test1/eslint.sarif delete mode 100644 tools/testdata/repositories/test1/src/testdata/repositories/test1/expected.sarif delete mode 100644 tools/testdata/repositories/test1/src/testdata/repositories/test1/sarif.json diff --git a/cmd/analyze.go b/cmd/analyze.go index ab62e72b..cf3223e2 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -3,6 +3,7 @@ package cmd import ( "codacy/cli-v2/config" "codacy/cli-v2/tools" + "codacy/cli-v2/tools/eslint" "codacy/cli-v2/tools/pmd" "encoding/json" "fmt" @@ -188,12 +189,13 @@ func getToolName(toolName string, version string) string { } func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string) { - eslint := config.Config.Tools()["eslint"] - eslintInstallationDirectory := eslint.InstallDir + tool := config.Config.Tools()["eslint"] + eslintInstallationDirectory := tool.InstallDir nodeRuntime := config.Config.Runtimes()["node"] nodeBinary := nodeRuntime.Binaries["node"] - tools.RunEslint(workDirectory, eslintInstallationDirectory, nodeBinary, pathsToCheck, autoFix, outputFile, outputFormat) + //todo pass config file + eslint.RunEslint(workDirectory, eslintInstallationDirectory, nodeBinary, pathsToCheck, autoFix, outputFile, outputFormat, "") } func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) { diff --git a/cmd/init.go b/cmd/init.go index 743f961d..b2aa4f59 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,6 +3,7 @@ package cmd import ( "codacy/cli-v2/config" "codacy/cli-v2/tools" + "codacy/cli-v2/tools/eslint" "codacy/cli-v2/tools/pmd" "encoding/json" "errors" @@ -172,7 +173,7 @@ func buildRepositoryConfigurationFiles(token string) error { eslintApiConfiguration := extractESLintConfiguration(apiToolConfigurations) if eslintApiConfiguration != nil { eslintDomainConfiguration := convertAPIToolConfigurationToDomain(*eslintApiConfiguration) - eslintConfigurationString := tools.CreateEslintConfig(eslintDomainConfiguration) + eslintConfigurationString := eslint.CreateEslintConfig(eslintDomainConfiguration) eslintConfigFile, err := os.Create("eslint.config.mjs") if err != nil { @@ -388,7 +389,7 @@ func createDefaultTrivyConfigFile() error { func createDefaultEslintConfigFile() error { // Use empty tool configuration to get default settings emptyConfig := tools.ToolConfiguration{} - content := tools.CreateEslintConfig(emptyConfig) + content := eslint.CreateEslintConfig(emptyConfig) // Write to file return os.WriteFile("eslint.config.mjs", []byte(content), 0644) diff --git a/tools/eslintConfigCreator.go b/tools/eslint/eslintConfigCreator.go similarity index 94% rename from tools/eslintConfigCreator.go rename to tools/eslint/eslintConfigCreator.go index f52bf776..866611f7 100644 --- a/tools/eslintConfigCreator.go +++ b/tools/eslint/eslintConfigCreator.go @@ -1,9 +1,11 @@ -package tools +package eslint import ( "encoding/json" "fmt" "strings" + + "codacy/cli-v2/tools" ) func quoteWhenIsNotJson(value string) string { @@ -18,7 +20,7 @@ func quoteWhenIsNotJson(value string) string { } } -func CreateEslintConfig(configuration ToolConfiguration) string { +func CreateEslintConfig(configuration tools.ToolConfiguration) string { result := `export default [ { rules: { diff --git a/tools/eslintRunner.go b/tools/eslint/eslintRunner.go similarity index 79% rename from tools/eslintRunner.go rename to tools/eslint/eslintRunner.go index 798b87f9..8c4fcd89 100644 --- a/tools/eslintRunner.go +++ b/tools/eslint/eslintRunner.go @@ -1,4 +1,4 @@ -package tools +package eslint import ( "os" @@ -9,7 +9,7 @@ import ( // * Run from the root of the repo we want to analyse // * NODE_PATH="/node_modules" // * The local installed ESLint should have the @microsoft/eslint-formatter-sarif installed -func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory string, nodeBinary string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string) { +func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory string, nodeBinary string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string, configFile string) { eslintInstallationNodeModules := filepath.Join(eslintInstallationDirectory, "node_modules") eslintJsPath := filepath.Join(eslintInstallationNodeModules, ".bin", "eslint") @@ -19,13 +19,19 @@ func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory } if outputFormat == "sarif" { //When outputting in SARIF format - cmd.Args = append(cmd.Args, "-f", "@microsoft/eslint-formatter-sarif") + cmd.Args = append(cmd.Args, "-f", filepath.Join(eslintInstallationNodeModules, "@microsoft", "eslint-formatter-sarif", "sarif.js")) } if outputFile != "" { //When writing to file, use the output file option cmd.Args = append(cmd.Args, "-o", outputFile) } + + // Add config file flag if provided + if configFile != "" { + cmd.Args = append(cmd.Args, "-c", configFile) + } + if len(pathsToCheck) > 0 { cmd.Args = append(cmd.Args, pathsToCheck...) } else { diff --git a/tools/eslintRunner_test.go b/tools/eslintRunner_test.go deleted file mode 100644 index 48e88b7b..00000000 --- a/tools/eslintRunner_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package tools - -import ( - "log" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestRunEslintToFile(t *testing.T) { - homeDirectory, err := os.UserHomeDir() - if err != nil { - log.Fatal(err.Error()) - } - currentDirectory, err := os.Getwd() - if err != nil { - log.Fatal(err.Error()) - } - testDirectory := "testdata/repositories/test1" - tempResultFile := filepath.Join(os.TempDir(), "eslint.sarif") - defer os.Remove(tempResultFile) - - repositoryToAnalyze := filepath.Join(testDirectory, "src") - expectedSarifFile := filepath.Join(testDirectory, "expected.sarif") - eslintInstallationDirectory := filepath.Join(homeDirectory, ".cache/codacy/tools/eslint@9.3.0") - nodeBinary := "node" - - RunEslint(repositoryToAnalyze, eslintInstallationDirectory, nodeBinary, nil, false, tempResultFile, "sarif") - - expectedSarifBytes, err := os.ReadFile(expectedSarifFile) - if err != nil { - log.Fatal(err) - } - - obtainedSarifBytes, err := os.ReadFile(tempResultFile) - if err != nil { - log.Fatal(err.Error()) - } - obtainedSarif := string(obtainedSarifBytes) - filePrefix := "file://" + currentDirectory + "/" - actualSarif := strings.ReplaceAll(obtainedSarif, filePrefix, "") - - expectedSarif := strings.TrimSpace(string(expectedSarifBytes)) - - assert.Equal(t, expectedSarif, actualSarif, "output did not match expected") -} diff --git a/tools/eslintConfigCreator_test.go b/tools/test/eslint/eslintConfigCreator_test.go similarity index 68% rename from tools/eslintConfigCreator_test.go rename to tools/test/eslint/eslintConfigCreator_test.go index 809d50ce..f647f89b 100644 --- a/tools/eslintConfigCreator_test.go +++ b/tools/test/eslint/eslintConfigCreator_test.go @@ -1,19 +1,22 @@ -package tools +package eslint import ( "testing" + "codacy/cli-v2/tools" + "codacy/cli-v2/tools/eslint" + "github.com/stretchr/testify/assert" ) -func testConfig(t *testing.T, configuration ToolConfiguration, expected string) { - actual := CreateEslintConfig(configuration) +func testConfig(t *testing.T, configuration tools.ToolConfiguration, expected string) { + actual := eslint.CreateEslintConfig(configuration) assert.Equal(t, expected, actual) } func TestCreateEslintConfigEmptyConfig(t *testing.T) { testConfig(t, - ToolConfiguration{}, + tools.ToolConfiguration{}, `export default [ { rules: { @@ -24,8 +27,8 @@ func TestCreateEslintConfigEmptyConfig(t *testing.T) { func TestCreateEslintConfigConfig1(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ { PatternId: "ESLint8_semi", }, @@ -42,11 +45,11 @@ func TestCreateEslintConfigConfig1(t *testing.T) { func TestCreateEslintConfigUnnamedParam(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ { PatternId: "ESLint8_semi", - ParameterConfigurations: []PatternParameterConfiguration{ + ParameterConfigurations: []tools.PatternParameterConfiguration{ { Name: "unnamedParam", Value: "never", @@ -66,11 +69,11 @@ func TestCreateEslintConfigUnnamedParam(t *testing.T) { func TestCreateEslintConfigNamedParam(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ { PatternId: "consistent-return", - ParameterConfigurations: []PatternParameterConfiguration{ + ParameterConfigurations: []tools.PatternParameterConfiguration{ { Name: "treatUndefinedAsUnspecified", Value: "false", @@ -90,11 +93,11 @@ func TestCreateEslintConfigNamedParam(t *testing.T) { func TestCreateEslintConfigUnnamedAndNamedParam(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ { PatternId: "consistent-return", - ParameterConfigurations: []PatternParameterConfiguration{ + ParameterConfigurations: []tools.PatternParameterConfiguration{ { Name: "treatUndefinedAsUnspecified", Value: "false", @@ -118,8 +121,8 @@ func TestCreateEslintConfigUnnamedAndNamedParam(t *testing.T) { func TestCreateEslintConfigSupportPlugins(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ { PatternId: "plugin/consistent-return", }, diff --git a/tools/test/eslint/eslintRunner_test.go b/tools/test/eslint/eslintRunner_test.go new file mode 100644 index 00000000..6c5c989a --- /dev/null +++ b/tools/test/eslint/eslintRunner_test.go @@ -0,0 +1,130 @@ +package eslint + +import ( + "encoding/json" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "codacy/cli-v2/tools/eslint" + + "github.com/stretchr/testify/assert" +) + +// SarifResult represents a single result in a SARIF report +type SarifResult struct { + RuleID string `json:"ruleId"` + Message struct { + Text string `json:"text"` + } `json:"message"` + Locations []struct { + PhysicalLocation struct { + ArtifactLocation struct { + URI string `json:"uri"` + } `json:"artifactLocation"` + Region struct { + StartLine int `json:"startLine"` + StartColumn int `json:"startColumn"` + EndLine int `json:"endLine"` + EndColumn int `json:"endColumn"` + } `json:"region"` + } `json:"physicalLocation"` + } `json:"locations"` +} + +// SarifReport represents the structure of a SARIF report +type SarifReport struct { + Runs []struct { + Results []SarifResult `json:"results"` + } `json:"runs"` +} + +func TestRunEslintToFile(t *testing.T) { + homeDirectory, err := os.UserHomeDir() + if err != nil { + log.Fatal(err.Error()) + } + currentDirectory, err := os.Getwd() + if err != nil { + log.Fatal(err.Error()) + } + + // Find node binary + nodeBinary, err := exec.LookPath("node") + if err != nil { + t.Fatalf("Node.js is not installed: %v", err) + } + + // Use the current directory since files are in flat structure + testDirectory := currentDirectory + tempResultFile := filepath.Join(os.TempDir(), "eslint.sarif") + defer os.Remove(tempResultFile) + + // Use absolute paths + repositoryToAnalyze := testDirectory + eslintInstallDir := filepath.Join(homeDirectory, ".cache/codacy/tools/eslint@8.38.0") + + // Debug logging + log.Printf("Test directory: %s", testDirectory) + log.Printf("ESLint install dir: %s", eslintInstallDir) + log.Printf("Node binary: %s", nodeBinary) + log.Printf("Temp result file: %s", tempResultFile) + + // Check if ESLint is installed + if _, err := os.Stat(eslintInstallDir); os.IsNotExist(err) { + t.Fatalf("ESLint is not installed at %s", eslintInstallDir) + } + + // Run ESLint on test.js + eslint.RunEslint(repositoryToAnalyze, eslintInstallDir, nodeBinary, []string{"test.js"}, false, tempResultFile, "sarif", "") + + // Check if the output file was created + obtainedSarifBytes, err := os.ReadFile(tempResultFile) + if err != nil { + t.Fatalf("Failed to read output file: %v", err) + } + + // Normalize paths in the obtained SARIF output + obtainedSarif := string(obtainedSarifBytes) + obtainedSarif = strings.ReplaceAll(obtainedSarif, currentDirectory+"/", "") + + // Parse the normalized SARIF output + var sarifReport SarifReport + err = json.Unmarshal([]byte(obtainedSarif), &sarifReport) + if err != nil { + t.Fatalf("Failed to parse SARIF output: %v", err) + } + + // Verify we have results + assert.NotEmpty(t, sarifReport.Runs, "SARIF report should have at least one run") + assert.NotEmpty(t, sarifReport.Runs[0].Results, "SARIF report should have at least one result") + + // Define expected violations + expectedViolations := map[string]bool{ + "semi": false, + } + + // Check each result + for _, result := range sarifReport.Runs[0].Results { + // Mark this rule as found + expectedViolations[result.RuleID] = true + + // Verify the file path is correct + assert.Contains(t, result.Locations[0].PhysicalLocation.ArtifactLocation.URI, "test.js", + "Violation should be in test.js") + + // Verify line numbers are reasonable + assert.Greater(t, result.Locations[0].PhysicalLocation.Region.StartLine, 0, + "Start line should be positive") + assert.Less(t, result.Locations[0].PhysicalLocation.Region.StartLine, 10, + "Start line should be within the file") + } + + // Verify all expected violations were found + for ruleID, found := range expectedViolations { + assert.True(t, found, "Expected violation %s was not found", ruleID) + } +} diff --git a/tools/testdata/repositories/test1/expected.sarif b/tools/testdata/repositories/test1/expected.sarif deleted file mode 100644 index 742893c1..00000000 --- a/tools/testdata/repositories/test1/expected.sarif +++ /dev/null @@ -1,29 +0,0 @@ -{ - "version": "2.1.0", - "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", - "runs": [ - { - "tool": { - "driver": { - "name": "ESLint", - "informationUri": "https://eslint.org", - "rules": [], - "version": "9.3.0" - } - }, - "artifacts": [ - { - "location": { - "uri": "testdata/repositories/test1/src/eslint.config.mjs" - } - }, - { - "location": { - "uri": "testdata/repositories/test1/src/test.js" - } - } - ], - "results": [] - } - ] -} diff --git a/tools/testdata/repositories/test1/src/eslint.config.mjs b/tools/testdata/repositories/test1/src/eslint.config.mjs deleted file mode 100644 index 22188e19..00000000 --- a/tools/testdata/repositories/test1/src/eslint.config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -export default [ - { - rules: { - "prefer-const": "error" - } - } -]; diff --git a/tools/testdata/repositories/test1/src/test.js b/tools/testdata/repositories/test1/src/test.js deleted file mode 100644 index 4ec1fa47..00000000 --- a/tools/testdata/repositories/test1/src/test.js +++ /dev/null @@ -1 +0,0 @@ -var foo = "bar"; diff --git a/tools/testdata/repositories/test1/src/testdata/repositories/test1/eslint.sarif b/tools/testdata/repositories/test1/src/testdata/repositories/test1/eslint.sarif deleted file mode 100644 index 2285ac09..00000000 --- a/tools/testdata/repositories/test1/src/testdata/repositories/test1/eslint.sarif +++ /dev/null @@ -1,29 +0,0 @@ -{ - "version": "2.1.0", - "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", - "runs": [ - { - "tool": { - "driver": { - "name": "ESLint", - "informationUri": "https://eslint.org", - "rules": [], - "version": "9.3.0" - } - }, - "artifacts": [ - { - "location": { - "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/eslint.config.mjs" - } - }, - { - "location": { - "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/test.js" - } - } - ], - "results": [] - } - ] -} \ No newline at end of file diff --git a/tools/testdata/repositories/test1/src/testdata/repositories/test1/expected.sarif b/tools/testdata/repositories/test1/src/testdata/repositories/test1/expected.sarif deleted file mode 100644 index 2285ac09..00000000 --- a/tools/testdata/repositories/test1/src/testdata/repositories/test1/expected.sarif +++ /dev/null @@ -1,29 +0,0 @@ -{ - "version": "2.1.0", - "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", - "runs": [ - { - "tool": { - "driver": { - "name": "ESLint", - "informationUri": "https://eslint.org", - "rules": [], - "version": "9.3.0" - } - }, - "artifacts": [ - { - "location": { - "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/eslint.config.mjs" - } - }, - { - "location": { - "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/test.js" - } - } - ], - "results": [] - } - ] -} \ No newline at end of file diff --git a/tools/testdata/repositories/test1/src/testdata/repositories/test1/sarif.json b/tools/testdata/repositories/test1/src/testdata/repositories/test1/sarif.json deleted file mode 100644 index 2285ac09..00000000 --- a/tools/testdata/repositories/test1/src/testdata/repositories/test1/sarif.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "version": "2.1.0", - "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", - "runs": [ - { - "tool": { - "driver": { - "name": "ESLint", - "informationUri": "https://eslint.org", - "rules": [], - "version": "9.3.0" - } - }, - "artifacts": [ - { - "location": { - "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/eslint.config.mjs" - } - }, - { - "location": { - "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/test.js" - } - } - ], - "results": [] - } - ] -} \ No newline at end of file From f24ac3feb0c486838e14260f75305b93c5da15e6 Mon Sep 17 00:00:00 2001 From: Yasmin Zhamborova Date: Mon, 7 Apr 2025 13:17:10 +0200 Subject: [PATCH 4/7] move trivy files --- cmd/analyze.go | 7 +- cmd/init.go | 5 +- tools/test/eslint/.eslintrc.json | 1 + tools/test/eslint/config.json | 18 ++ tools/test/eslint/eslint.sarif | 29 +++ tools/test/eslint/expected.sarif | 29 +++ tools/test/eslint/sarif.json | 29 +++ tools/test/eslint/test.js | 2 + tools/test/pmd/RulesBreaker.java | 20 ++ tools/test/pmd/expected-ruleset.xml | 15 ++ tools/test/pmd/expected.sarif | 203 +++++++++++++++ tools/test/pmd/pmd-ruleset.xml | 24 ++ .../trivy/expected.sarif | 0 .../trivy/src/package-lock.json | 0 .../trivy/src/trivy.yaml | 0 tools/{ => trivy}/trivyConfigCreator.go | 5 +- tools/{ => trivy}/trivyRunner.go | 2 +- tools/trivyConfigCreator_test.go | 238 ------------------ tools/trivyRunner_test.go | 56 ----- 19 files changed, 381 insertions(+), 302 deletions(-) create mode 100644 tools/test/eslint/.eslintrc.json create mode 100644 tools/test/eslint/config.json create mode 100644 tools/test/eslint/eslint.sarif create mode 100644 tools/test/eslint/expected.sarif create mode 100644 tools/test/eslint/sarif.json create mode 100644 tools/test/eslint/test.js create mode 100644 tools/test/pmd/RulesBreaker.java create mode 100644 tools/test/pmd/expected-ruleset.xml create mode 100644 tools/test/pmd/expected.sarif create mode 100644 tools/test/pmd/pmd-ruleset.xml rename tools/{testdata/repositories => test}/trivy/expected.sarif (100%) rename tools/{testdata/repositories => test}/trivy/src/package-lock.json (100%) rename tools/{testdata/repositories => test}/trivy/src/trivy.yaml (100%) rename tools/{ => trivy}/trivyConfigCreator.go (95%) rename tools/{ => trivy}/trivyRunner.go (98%) delete mode 100644 tools/trivyConfigCreator_test.go delete mode 100644 tools/trivyRunner_test.go diff --git a/cmd/analyze.go b/cmd/analyze.go index cf3223e2..e4c33225 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -5,6 +5,7 @@ import ( "codacy/cli-v2/tools" "codacy/cli-v2/tools/eslint" "codacy/cli-v2/tools/pmd" + "codacy/cli-v2/tools/trivy" "encoding/json" "fmt" "io" @@ -199,10 +200,10 @@ func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool } func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) { - trivy := config.Config.Tools()["trivy"] - trivyBinary := trivy.Binaries["trivy"] + tool := config.Config.Tools()["trivy"] + trivyBinary := tool.Binaries["trivy"] - err := tools.RunTrivy(workDirectory, trivyBinary, pathsToCheck, outputFile, outputFormat) + err := trivy.RunTrivy(workDirectory, trivyBinary, pathsToCheck, outputFile, outputFormat) if err != nil { log.Fatalf("Error running Trivy: %v", err) } diff --git a/cmd/init.go b/cmd/init.go index b2aa4f59..c63c4175 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -5,6 +5,7 @@ import ( "codacy/cli-v2/tools" "codacy/cli-v2/tools/eslint" "codacy/cli-v2/tools/pmd" + "codacy/cli-v2/tools/trivy" "encoding/json" "errors" "fmt" @@ -329,7 +330,7 @@ func createTrivyConfigFile(config CodacyToolConfiguration) error { trivyDomainConfiguration := convertAPIToolConfigurationForTrivy(config) // Use the shared CreateTrivyConfig function to generate the config content - trivyConfigurationString := tools.CreateTrivyConfig(trivyDomainConfiguration) + trivyConfigurationString := trivy.CreateTrivyConfig(trivyDomainConfiguration) // Write to file return os.WriteFile("trivy.yaml", []byte(trivyConfigurationString), 0644) @@ -379,7 +380,7 @@ func convertAPIToolConfigurationForTrivy(config CodacyToolConfiguration) tools.T func createDefaultTrivyConfigFile() error { // Use empty tool configuration to get default settings emptyConfig := tools.ToolConfiguration{} - content := tools.CreateTrivyConfig(emptyConfig) + content := trivy.CreateTrivyConfig(emptyConfig) // Write to file return os.WriteFile("trivy.yaml", []byte(content), 0644) diff --git a/tools/test/eslint/.eslintrc.json b/tools/test/eslint/.eslintrc.json new file mode 100644 index 00000000..2935b313 --- /dev/null +++ b/tools/test/eslint/.eslintrc.json @@ -0,0 +1 @@ +{ "parser": "espree", "rules": { "semi": ["error", "always"] } } diff --git a/tools/test/eslint/config.json b/tools/test/eslint/config.json new file mode 100644 index 00000000..f2459053 --- /dev/null +++ b/tools/test/eslint/config.json @@ -0,0 +1,18 @@ +{ + "env": {}, + "globals": {}, + "parser": null, + "parserOptions": {}, + "plugins": [], + "rules": { + "prefer-const": [ + "error" + ], + "semi": [ + "error", + "always" + ] + }, + "settings": {}, + "ignorePatterns": [] +} diff --git a/tools/test/eslint/eslint.sarif b/tools/test/eslint/eslint.sarif new file mode 100644 index 00000000..2285ac09 --- /dev/null +++ b/tools/test/eslint/eslint.sarif @@ -0,0 +1,29 @@ +{ + "version": "2.1.0", + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": [ + { + "tool": { + "driver": { + "name": "ESLint", + "informationUri": "https://eslint.org", + "rules": [], + "version": "9.3.0" + } + }, + "artifacts": [ + { + "location": { + "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/eslint.config.mjs" + } + }, + { + "location": { + "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/test.js" + } + } + ], + "results": [] + } + ] +} \ No newline at end of file diff --git a/tools/test/eslint/expected.sarif b/tools/test/eslint/expected.sarif new file mode 100644 index 00000000..2285ac09 --- /dev/null +++ b/tools/test/eslint/expected.sarif @@ -0,0 +1,29 @@ +{ + "version": "2.1.0", + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": [ + { + "tool": { + "driver": { + "name": "ESLint", + "informationUri": "https://eslint.org", + "rules": [], + "version": "9.3.0" + } + }, + "artifacts": [ + { + "location": { + "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/eslint.config.mjs" + } + }, + { + "location": { + "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/test.js" + } + } + ], + "results": [] + } + ] +} \ No newline at end of file diff --git a/tools/test/eslint/sarif.json b/tools/test/eslint/sarif.json new file mode 100644 index 00000000..2285ac09 --- /dev/null +++ b/tools/test/eslint/sarif.json @@ -0,0 +1,29 @@ +{ + "version": "2.1.0", + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": [ + { + "tool": { + "driver": { + "name": "ESLint", + "informationUri": "https://eslint.org", + "rules": [], + "version": "9.3.0" + } + }, + "artifacts": [ + { + "location": { + "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/eslint.config.mjs" + } + }, + { + "location": { + "uri": "file:///Users/lorenzo/codacy/github/codacy-cli-v2/tools/testdata/repositories/test1/src/test.js" + } + } + ], + "results": [] + } + ] +} \ No newline at end of file diff --git a/tools/test/eslint/test.js b/tools/test/eslint/test.js new file mode 100644 index 00000000..be581f65 --- /dev/null +++ b/tools/test/eslint/test.js @@ -0,0 +1,2 @@ +var foo = "bar" +foo = "baz" diff --git a/tools/test/pmd/RulesBreaker.java b/tools/test/pmd/RulesBreaker.java new file mode 100644 index 00000000..fcb86a61 --- /dev/null +++ b/tools/test/pmd/RulesBreaker.java @@ -0,0 +1,20 @@ + +/** + * This class demonstrates various PMD rule violations. + * It is used for testing PMD's rule detection capabilities. + * + * @author Codacy Test Team + * @version 1.0 + */ + +package tools.testdata.repositories.pmd; + +/** + * A class that demonstrates PMD rule violations. + */ +public class RulesBreaker { + + // Breaking naming convention rules + private int x; + +} diff --git a/tools/test/pmd/expected-ruleset.xml b/tools/test/pmd/expected-ruleset.xml new file mode 100644 index 00000000..d08d8bfc --- /dev/null +++ b/tools/test/pmd/expected-ruleset.xml @@ -0,0 +1,15 @@ + + + Codacy PMD Ruleset + + + + + + + + + \ No newline at end of file diff --git a/tools/test/pmd/expected.sarif b/tools/test/pmd/expected.sarif new file mode 100644 index 00000000..fb011bc4 --- /dev/null +++ b/tools/test/pmd/expected.sarif @@ -0,0 +1,203 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "6.55.0", + "informationUri": "https://pmd.github.io/pmd/", + "rules": [ + { + "id": "AtLeastOneConstructor", + "shortDescription": { + "text": "Each class should declare at least one constructor" + }, + "fullDescription": { + "text": "\n\nEach non-static class should declare at least one constructor.\nClasses with solely static members are ignored, refer to [UseUtilityClassRule](pmd_rules_java_design.html#useutilityclass) to detect those.\n\n " + }, + "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_codestyle.html#atleastoneconstructor", + "help": { + "text": "\n\nEach non-static class should declare at least one constructor.\nClasses with solely static members are ignored, refer to [UseUtilityClassRule](pmd_rules_java_design.html#useutilityclass) to detect those.\n\n " + }, + "properties": { + "ruleset": "Code Style", + "priority": 3, + "tags": [ + "Code Style" + ] + } + }, + { + "id": "UnusedPrivateField", + "shortDescription": { + "text": "Avoid unused private fields such as 'x'." + }, + "fullDescription": { + "text": "\nDetects when a private field is declared and/or assigned a value, but not used.\n\nSince PMD 6.50.0 private fields are ignored, if the fields are annotated with any annotation or the\nenclosing class has any annotation. Annotations often enable a framework (such as dependency injection, mocking\nor e.g. Lombok) which use the fields by reflection or other means. This usage can't be detected by static code analysis.\nPreviously these frameworks where explicitly allowed by listing their annotations in the property\n\"ignoredAnnotations\", but that turned out to be prone of false positive for any not explicitly considered framework.\n " + }, + "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_bestpractices.html#unusedprivatefield", + "help": { + "text": "\nDetects when a private field is declared and/or assigned a value, but not used.\n\nSince PMD 6.50.0 private fields are ignored, if the fields are annotated with any annotation or the\nenclosing class has any annotation. Annotations often enable a framework (such as dependency injection, mocking\nor e.g. Lombok) which use the fields by reflection or other means. This usage can't be detected by static code analysis.\nPreviously these frameworks where explicitly allowed by listing their annotations in the property\n\"ignoredAnnotations\", but that turned out to be prone of false positive for any not explicitly considered framework.\n " + }, + "properties": { + "ruleset": "Best Practices", + "priority": 3, + "tags": [ + "Best Practices" + ] + } + }, + { + "id": "ShortVariable", + "shortDescription": { + "text": "Avoid variables with short names like x" + }, + "fullDescription": { + "text": "\nFields, local variables, or parameter names that are very short are not helpful to the reader.\n " + }, + "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_codestyle.html#shortvariable", + "help": { + "text": "\nFields, local variables, or parameter names that are very short are not helpful to the reader.\n " + }, + "properties": { + "ruleset": "Code Style", + "priority": 3, + "tags": [ + "Code Style" + ] + } + }, + { + "id": "CommentRequired", + "shortDescription": { + "text": "Field comments are required" + }, + "fullDescription": { + "text": "\nDenotes whether javadoc (formal) comments are required (or unwanted) for specific language elements.\n " + }, + "helpUri": "https://pmd.github.io/pmd-6.55.0/pmd_rules_java_documentation.html#commentrequired", + "help": { + "text": "\nDenotes whether javadoc (formal) comments are required (or unwanted) for specific language elements.\n " + }, + "properties": { + "ruleset": "Documentation", + "priority": 3, + "tags": [ + "Documentation" + ] + } + } + ] + } + }, + "results": [ + { + "ruleId": "AtLeastOneConstructor", + "ruleIndex": 0, + "message": { + "text": "Each class should declare at least one constructor" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "testdata/repositories/pmd/RulesBreaker.java" + }, + "region": { + "startLine": 15, + "startColumn": 8, + "endLine": 20, + "endColumn": 1 + } + } + } + ] + }, + { + "ruleId": "UnusedPrivateField", + "ruleIndex": 1, + "message": { + "text": "Avoid unused private fields such as 'x'." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "testdata/repositories/pmd/RulesBreaker.java" + }, + "region": { + "startLine": 18, + "startColumn": 17, + "endLine": 18, + "endColumn": 17 + } + } + } + ] + }, + { + "ruleId": "ShortVariable", + "ruleIndex": 2, + "message": { + "text": "Avoid variables with short names like x" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "testdata/repositories/pmd/RulesBreaker.java" + }, + "region": { + "startLine": 18, + "startColumn": 17, + "endLine": 18, + "endColumn": 17 + } + } + } + ] + }, + { + "ruleId": "CommentRequired", + "ruleIndex": 3, + "message": { + "text": "Field comments are required" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "testdata/repositories/pmd/RulesBreaker.java" + }, + "region": { + "startLine": 18, + "startColumn": 13, + "endLine": 18, + "endColumn": 18 + } + } + } + ] + } + ], + "invocations": [ + { + "executionSuccessful": false, + "toolConfigurationNotifications": [ + { + "associatedRule": { + "id": "LoosePackageCoupling" + }, + "message": { + "text": "No packages or classes specified" + } + } + ], + "toolExecutionNotifications": [] + } + ] + } + ] +} diff --git a/tools/test/pmd/pmd-ruleset.xml b/tools/test/pmd/pmd-ruleset.xml new file mode 100644 index 00000000..11377f9e --- /dev/null +++ b/tools/test/pmd/pmd-ruleset.xml @@ -0,0 +1,24 @@ + + + + + Basic PMD ruleset for Codacy analysis + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/testdata/repositories/trivy/expected.sarif b/tools/test/trivy/expected.sarif similarity index 100% rename from tools/testdata/repositories/trivy/expected.sarif rename to tools/test/trivy/expected.sarif diff --git a/tools/testdata/repositories/trivy/src/package-lock.json b/tools/test/trivy/src/package-lock.json similarity index 100% rename from tools/testdata/repositories/trivy/src/package-lock.json rename to tools/test/trivy/src/package-lock.json diff --git a/tools/testdata/repositories/trivy/src/trivy.yaml b/tools/test/trivy/src/trivy.yaml similarity index 100% rename from tools/testdata/repositories/trivy/src/trivy.yaml rename to tools/test/trivy/src/trivy.yaml diff --git a/tools/trivyConfigCreator.go b/tools/trivy/trivyConfigCreator.go similarity index 95% rename from tools/trivyConfigCreator.go rename to tools/trivy/trivyConfigCreator.go index 4d50bb80..86ac0d5d 100644 --- a/tools/trivyConfigCreator.go +++ b/tools/trivy/trivyConfigCreator.go @@ -1,12 +1,13 @@ -package tools +package trivy import ( + "codacy/cli-v2/tools" "fmt" "strings" ) // CreateTrivyConfig generates a Trivy configuration based on the tool configuration -func CreateTrivyConfig(config ToolConfiguration) string { +func CreateTrivyConfig(config tools.ToolConfiguration) string { // Default settings - include all severities and scanners includeLow := true includeMedium := true diff --git a/tools/trivyRunner.go b/tools/trivy/trivyRunner.go similarity index 98% rename from tools/trivyRunner.go rename to tools/trivy/trivyRunner.go index 784a65bb..749b6420 100644 --- a/tools/trivyRunner.go +++ b/tools/trivy/trivyRunner.go @@ -1,4 +1,4 @@ -package tools +package trivy import ( "os" diff --git a/tools/trivyConfigCreator_test.go b/tools/trivyConfigCreator_test.go deleted file mode 100644 index f238056e..00000000 --- a/tools/trivyConfigCreator_test.go +++ /dev/null @@ -1,238 +0,0 @@ -package tools - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func testTrivyConfig(t *testing.T, configuration ToolConfiguration, expected string) { - actual := CreateTrivyConfig(configuration) - assert.Equal(t, expected, actual) -} - -func TestCreateTrivyConfigEmptyConfig(t *testing.T) { - testTrivyConfig(t, - ToolConfiguration{}, - `severity: - - LOW - - MEDIUM - - HIGH - - CRITICAL - -scan: - scanners: - - vuln - - secret -`) -} - -func TestCreateTrivyConfigAllEnabled(t *testing.T) { - testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, - }, - }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, - }, - }, - { - PatternId: "Trivy_vulnerability", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, - }, - }, - { - PatternId: "Trivy_secret", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, - }, - }, - }, - }, - `severity: - - LOW - - MEDIUM - - HIGH - - CRITICAL - -scan: - scanners: - - vuln - - secret -`) -} - -func TestCreateTrivyConfigNoLow(t *testing.T) { - testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - }, - }, - `severity: - - MEDIUM - - HIGH - - CRITICAL - -scan: - scanners: - - vuln - - secret -`) -} - -func TestCreateTrivyConfigOnlyHigh(t *testing.T) { - testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - { - PatternId: "Trivy_secret", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - }, - }, - `severity: - - HIGH - - CRITICAL - -scan: - scanners: - - vuln -`) -} - -func TestCreateTrivyConfigNoVulnerabilities(t *testing.T) { - testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - { - PatternId: "Trivy_vulnerability", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - }, - }, - `severity: - -scan: - scanners: - - vuln - - secret -`) -} - -func TestCreateTrivyConfigOnlySecretsLow(t *testing.T) { - testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, - }, - }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - { - PatternId: "Trivy_vulnerability", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, - }, - }, - }, - }, - `severity: - - LOW - -scan: - scanners: - - vuln - - secret -`) -} diff --git a/tools/trivyRunner_test.go b/tools/trivyRunner_test.go deleted file mode 100644 index c7d1dd66..00000000 --- a/tools/trivyRunner_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package tools - -import ( - "fmt" - "log" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestRunTrivyToFile(t *testing.T) { - homeDirectory, err := os.UserHomeDir() - if err != nil { - log.Fatal(err.Error()) - } - currentDirectory, err := os.Getwd() - if err != nil { - log.Fatal(err.Error()) - } - - testDirectory := "testdata/repositories/trivy" - tempResultFile := filepath.Join(os.TempDir(), "trivy.sarif") - defer os.Remove(tempResultFile) - - repositoryToAnalyze := filepath.Join(testDirectory, "src") - - trivyBinary := filepath.Join(homeDirectory, ".cache/codacy/tools/trivy@0.59.1/trivy") - - err = RunTrivy(repositoryToAnalyze, trivyBinary, nil, tempResultFile, "sarif") - if err != nil { - t.Fatalf("Failed to run trivy: %v", err) - } - - // Check if the output file was created - obtainedSarifBytes, err := os.ReadFile(tempResultFile) - if err != nil { - t.Fatalf("Failed to read output file: %v", err) - } - obtainedSarif := string(obtainedSarifBytes) - filePrefix := "file://" + currentDirectory + "/" - fmt.Println(filePrefix) - actualSarif := strings.ReplaceAll(obtainedSarif, filePrefix, "") - - // Read the expected SARIF - expectedSarifFile := filepath.Join(testDirectory, "expected.sarif") - expectedSarifBytes, err := os.ReadFile(expectedSarifFile) - if err != nil { - log.Fatal(err) - } - expectedSarif := strings.TrimSpace(string(expectedSarifBytes)) - - assert.Equal(t, expectedSarif, actualSarif, "output did not match expected") -} From 09c21adbeecf555db9dc2afad0f4bd28a9846975 Mon Sep 17 00:00:00 2001 From: Yasmin Zhamborova Date: Mon, 7 Apr 2025 13:39:28 +0200 Subject: [PATCH 5/7] fx eslint --- tools/eslint/eslintRunner.go | 3 ++- tools/test/eslint/.eslintrc.json | 1 - tools/test/eslint/eslint.config.js | 5 +++++ tools/test/eslint/eslintRunner_test.go | 6 ++++-- 4 files changed, 11 insertions(+), 4 deletions(-) delete mode 100644 tools/test/eslint/.eslintrc.json create mode 100644 tools/test/eslint/eslint.config.js diff --git a/tools/eslint/eslintRunner.go b/tools/eslint/eslintRunner.go index 8c4fcd89..40e590f9 100644 --- a/tools/eslint/eslintRunner.go +++ b/tools/eslint/eslintRunner.go @@ -1,6 +1,7 @@ package eslint import ( + "log" "os" "os/exec" "path/filepath" @@ -29,7 +30,7 @@ func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory // Add config file flag if provided if configFile != "" { - cmd.Args = append(cmd.Args, "-c", configFile) + cmd.Args = append(cmd.Args, "--config", configFile) } if len(pathsToCheck) > 0 { diff --git a/tools/test/eslint/.eslintrc.json b/tools/test/eslint/.eslintrc.json deleted file mode 100644 index 2935b313..00000000 --- a/tools/test/eslint/.eslintrc.json +++ /dev/null @@ -1 +0,0 @@ -{ "parser": "espree", "rules": { "semi": ["error", "always"] } } diff --git a/tools/test/eslint/eslint.config.js b/tools/test/eslint/eslint.config.js new file mode 100644 index 00000000..2691022c --- /dev/null +++ b/tools/test/eslint/eslint.config.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + semi: ["error", "always"] + } +}; \ No newline at end of file diff --git a/tools/test/eslint/eslintRunner_test.go b/tools/test/eslint/eslintRunner_test.go index 6c5c989a..4b16d150 100644 --- a/tools/test/eslint/eslintRunner_test.go +++ b/tools/test/eslint/eslintRunner_test.go @@ -65,7 +65,7 @@ func TestRunEslintToFile(t *testing.T) { // Use absolute paths repositoryToAnalyze := testDirectory - eslintInstallDir := filepath.Join(homeDirectory, ".cache/codacy/tools/eslint@8.38.0") + eslintInstallDir := filepath.Join(homeDirectory, ".cache/codacy/tools/eslint@9.3.0") // Debug logging log.Printf("Test directory: %s", testDirectory) @@ -79,7 +79,9 @@ func TestRunEslintToFile(t *testing.T) { } // Run ESLint on test.js - eslint.RunEslint(repositoryToAnalyze, eslintInstallDir, nodeBinary, []string{"test.js"}, false, tempResultFile, "sarif", "") + configFile := filepath.Join(testDirectory, "eslint.config.js") + log.Printf("Using ESLint config file: %s", configFile) + eslint.RunEslint(repositoryToAnalyze, eslintInstallDir, nodeBinary, []string{"test.js"}, false, tempResultFile, "sarif", configFile) // Check if the output file was created obtainedSarifBytes, err := os.ReadFile(tempResultFile) From b96fe38255554bc3d77d34f7ebf6c607b1ea6486 Mon Sep 17 00:00:00 2001 From: Yasmin Zhamborova Date: Mon, 7 Apr 2025 13:40:34 +0200 Subject: [PATCH 6/7] fx eslint --- tools/eslint/eslintRunner.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/eslint/eslintRunner.go b/tools/eslint/eslintRunner.go index 40e590f9..75be1496 100644 --- a/tools/eslint/eslintRunner.go +++ b/tools/eslint/eslintRunner.go @@ -1,7 +1,6 @@ package eslint import ( - "log" "os" "os/exec" "path/filepath" From d6678cf8e7c908d4f10f681464c79de6c118b19d Mon Sep 17 00:00:00 2001 From: Yasmin Zhamborova Date: Mon, 7 Apr 2025 13:48:20 +0200 Subject: [PATCH 7/7] fx trivy tests --- tools/test/trivy/trivyConfigCreator_test.go | 241 ++++++++++++++++++++ tools/test/trivy/trivyRunner_test.go | 61 +++++ 2 files changed, 302 insertions(+) create mode 100644 tools/test/trivy/trivyConfigCreator_test.go create mode 100644 tools/test/trivy/trivyRunner_test.go diff --git a/tools/test/trivy/trivyConfigCreator_test.go b/tools/test/trivy/trivyConfigCreator_test.go new file mode 100644 index 00000000..20d20465 --- /dev/null +++ b/tools/test/trivy/trivyConfigCreator_test.go @@ -0,0 +1,241 @@ +package tools + +import ( + "testing" + + "codacy/cli-v2/tools" + "codacy/cli-v2/tools/trivy" + + "github.com/stretchr/testify/assert" +) + +func testTrivyConfig(t *testing.T, configuration tools.ToolConfiguration, expected string) { + actual := trivy.CreateTrivyConfig(configuration) + assert.Equal(t, expected, actual) +} + +func TestCreateTrivyConfigEmptyConfig(t *testing.T) { + testTrivyConfig(t, + tools.ToolConfiguration{}, + `severity: + - LOW + - MEDIUM + - HIGH + - CRITICAL + +scan: + scanners: + - vuln + - secret +`) +} + +func TestCreateTrivyConfigAllEnabled(t *testing.T) { + testTrivyConfig(t, + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ + { + PatternId: "Trivy_vulnerability_minor", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "true", + }, + }, + }, + { + PatternId: "Trivy_vulnerability_medium", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "true", + }, + }, + }, + { + PatternId: "Trivy_vulnerability", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "true", + }, + }, + }, + { + PatternId: "Trivy_secret", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "true", + }, + }, + }, + }, + }, + `severity: + - LOW + - MEDIUM + - HIGH + - CRITICAL + +scan: + scanners: + - vuln + - secret +`) +} + +func TestCreateTrivyConfigNoLow(t *testing.T) { + testTrivyConfig(t, + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ + { + PatternId: "Trivy_vulnerability_minor", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + }, + }, + `severity: + - MEDIUM + - HIGH + - CRITICAL + +scan: + scanners: + - vuln + - secret +`) +} + +func TestCreateTrivyConfigOnlyHigh(t *testing.T) { + testTrivyConfig(t, + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ + { + PatternId: "Trivy_vulnerability_minor", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + { + PatternId: "Trivy_vulnerability_medium", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + { + PatternId: "Trivy_secret", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + }, + }, + `severity: + - HIGH + - CRITICAL + +scan: + scanners: + - vuln +`) +} + +func TestCreateTrivyConfigNoVulnerabilities(t *testing.T) { + testTrivyConfig(t, + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ + { + PatternId: "Trivy_vulnerability_minor", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + { + PatternId: "Trivy_vulnerability_medium", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + { + PatternId: "Trivy_vulnerability", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + }, + }, + `severity: + +scan: + scanners: + - vuln + - secret +`) +} + +func TestCreateTrivyConfigOnlySecretsLow(t *testing.T) { + testTrivyConfig(t, + tools.ToolConfiguration{ + PatternsConfiguration: []tools.PatternConfiguration{ + { + PatternId: "Trivy_vulnerability_minor", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "true", + }, + }, + }, + { + PatternId: "Trivy_vulnerability_medium", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + { + PatternId: "Trivy_vulnerability", + ParameterConfigurations: []tools.PatternParameterConfiguration{ + { + Name: "enabled", + Value: "false", + }, + }, + }, + }, + }, + `severity: + - LOW + +scan: + scanners: + - vuln + - secret +`) +} diff --git a/tools/test/trivy/trivyRunner_test.go b/tools/test/trivy/trivyRunner_test.go new file mode 100644 index 00000000..b151df18 --- /dev/null +++ b/tools/test/trivy/trivyRunner_test.go @@ -0,0 +1,61 @@ +package tools + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + "testing" + + "codacy/cli-v2/tools/trivy" + + "github.com/stretchr/testify/assert" +) + +func TestRunTrivyToFile(t *testing.T) { + homeDirectory, err := os.UserHomeDir() + if err != nil { + log.Fatal(err.Error()) + } + currentDirectory, err := os.Getwd() + if err != nil { + log.Fatal(err.Error()) + } + + testDirectory := currentDirectory + tempResultFile := filepath.Join(os.TempDir(), "trivy.sarif") + defer os.Remove(tempResultFile) + + repositoryToAnalyze := filepath.Join(testDirectory, "src") + + trivyBinary := filepath.Join(homeDirectory, ".cache/codacy/tools/trivy@0.59.1/trivy") + + err = trivy.RunTrivy(repositoryToAnalyze, trivyBinary, nil, tempResultFile, "sarif") + if err != nil { + t.Fatalf("Failed to run trivy: %v", err) + } + + // Check if the output file was created + obtainedSarifBytes, err := os.ReadFile(tempResultFile) + if err != nil { + t.Fatalf("Failed to read output file: %v", err) + } + obtainedSarif := string(obtainedSarifBytes) + filePrefix := "file://" + currentDirectory + "/" + fmt.Println(filePrefix) + actualSarif := strings.ReplaceAll(obtainedSarif, filePrefix, "") + + // Normalize paths in the SARIF output + actualSarif = strings.ReplaceAll(actualSarif, `"uri": "src/"`, `"uri": "testdata/repositories/trivy/src/"`) + + // Read the expected SARIF + expectedSarifFile := filepath.Join(testDirectory, "expected.sarif") + expectedSarifBytes, err := os.ReadFile(expectedSarifFile) + if err != nil { + log.Fatal(err) + } + expectedSarif := strings.TrimSpace(string(expectedSarifBytes)) + + assert.Equal(t, expectedSarif, actualSarif, "output did not match expected") +}