Skip to content
Merged
1 change: 0 additions & 1 deletion .codacy/cli-config.yaml

This file was deleted.

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ go.work.sum
.idea/
.vscode/

cli-v2
# Codacy CLI
cli-v2
4 changes: 1 addition & 3 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ var outputFormat string
var sarifPath string
var commitUuid string
var projectToken string
var pmdRulesetFile string

type Sarif struct {
Runs []struct {
Expand Down Expand Up @@ -96,7 +95,6 @@ func init() {
analyzeCmd.Flags().StringVarP(&toolToAnalyze, "tool", "t", "", "Which tool to run analysis with")
analyzeCmd.Flags().StringVar(&outputFormat, "format", "", "Output format (use 'sarif' for SARIF format)")
analyzeCmd.Flags().BoolVar(&autoFix, "fix", false, "Apply auto fix to your issues when available")
analyzeCmd.Flags().StringVar(&pmdRulesetFile, "rulesets", "", "Path to PMD ruleset file")
rootCmd.AddCommand(analyzeCmd)
}

Expand Down Expand Up @@ -209,7 +207,7 @@ func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile stri
pmd := config.Config.Tools()["pmd"]
pmdBinary := pmd.Binaries["pmd"]

err := tools.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat, pmdRulesetFile)
err := tools.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat, "")
if err != nil {
log.Fatalf("Error running PMD: %v", err)
}
Expand Down
62 changes: 46 additions & 16 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package cmd
import (
"codacy/cli-v2/config"
"codacy/cli-v2/tools"
"codacy/cli-v2/utils"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -55,6 +57,7 @@ var initCmd = &cobra.Command{
if err != nil {
log.Fatal(err)
}
createGitIgnoreFile()
}
fmt.Println()
fmt.Println("✅ Successfully initialized Codacy configuration!")
Expand All @@ -66,6 +69,26 @@ var initCmd = &cobra.Command{
},
}

func createGitIgnoreFile() error {
gitIgnorePath := filepath.Join(config.Config.LocalCodacyDirectory(), ".gitignore")
gitIgnoreFile, err := os.Create(gitIgnorePath)
if err != nil {
return fmt.Errorf("failed to create .gitignore file: %w", err)
}
defer gitIgnoreFile.Close()

content := `# Codacy CLI
tools-configs/
.gitignore
cli-config.yaml
`
if _, err := gitIgnoreFile.WriteString(content); err != nil {
return fmt.Errorf("failed to write to .gitignore file: %w", err)
}

return nil
}

func createConfigurationFiles(tools []tools.Tool, cliLocalMode bool) error {
configFile, err := os.Create(config.Config.ProjectConfigFile())
defer configFile.Close()
Expand Down Expand Up @@ -142,6 +165,13 @@ func buildRepositoryConfigurationFiles(token string) error {
fmt.Println("Building repository configuration files ...")
fmt.Println("Fetching repository configuration from codacy ...")

toolsConfigDir := config.Config.ToolsConfigDirectory()

// Create tools-configs directory if it doesn't exist
if err := os.MkdirAll(toolsConfigDir, utils.DefaultDirPerms); err != nil {
return fmt.Errorf("failed to create tools-configs directory: %w", err)
}

// API call to fetch settings
url := CodacyApiBase + "/2.0/project/analysis/configuration"

Expand Down Expand Up @@ -198,7 +228,7 @@ func buildRepositoryConfigurationFiles(token string) error {
eslintDomainConfiguration := convertAPIToolConfigurationToDomain(*eslintApiConfiguration)
eslintConfigurationString := tools.CreateEslintConfig(eslintDomainConfiguration)

eslintConfigFile, err := os.Create("eslint.config.mjs")
eslintConfigFile, err := os.Create(filepath.Join(toolsConfigDir, "eslint.config.mjs"))
if err != nil {
return fmt.Errorf("failed to create eslint config file: %v", err)
}
Expand All @@ -210,7 +240,7 @@ func buildRepositoryConfigurationFiles(token string) error {
}
fmt.Println("ESLint configuration created based on Codacy settings")
} else {
err = createDefaultEslintConfigFile()
err = createDefaultEslintConfigFile(toolsConfigDir)
if err != nil {
return fmt.Errorf("failed to create default ESLint config: %v", err)
}
Expand All @@ -220,13 +250,13 @@ func buildRepositoryConfigurationFiles(token string) error {
// Trivy configuration
trivyApiConfiguration := extractTrivyConfiguration(apiToolConfigurations)
if trivyApiConfiguration != nil {
err = createTrivyConfigFile(*trivyApiConfiguration)
err = createTrivyConfigFile(*trivyApiConfiguration, toolsConfigDir)
if err != nil {
return fmt.Errorf("failed to create Trivy config: %v", err)
}
fmt.Println("Trivy configuration created based on Codacy settings")
} else {
err = createDefaultTrivyConfigFile()
err = createDefaultTrivyConfigFile(toolsConfigDir)
if err != nil {
return fmt.Errorf("failed to create default Trivy config: %v", err)
}
Expand All @@ -236,13 +266,13 @@ func buildRepositoryConfigurationFiles(token string) error {
// PMD configuration
pmdApiConfiguration := extractPMDConfiguration(apiToolConfigurations)
if pmdApiConfiguration != nil {
err = createPMDConfigFile(*pmdApiConfiguration)
err = createPMDConfigFile(*pmdApiConfiguration, toolsConfigDir)
if err != nil {
return fmt.Errorf("failed to create PMD config: %v", err)
}
fmt.Println("PMD configuration created based on Codacy settings")
} else {
err = createDefaultPMDConfigFile()
err = createDefaultPMDConfigFile(toolsConfigDir)
if err != nil {
return fmt.Errorf("failed to create default PMD config: %v", err)
}
Expand Down Expand Up @@ -318,16 +348,16 @@ func extractPMDConfiguration(toolConfigurations []CodacyToolConfiguration) *Coda
return nil
}

func createPMDConfigFile(config CodacyToolConfiguration) error {
func createPMDConfigFile(config CodacyToolConfiguration, toolsConfigDir string) error {
pmdDomainConfiguration := convertAPIToolConfigurationToDomain(config)
pmdConfigurationString := tools.CreatePmdConfig(pmdDomainConfiguration)
return os.WriteFile("pmd-ruleset.xml", []byte(pmdConfigurationString), 0644)
return os.WriteFile(filepath.Join(toolsConfigDir, "pmd-ruleset.xml"), []byte(pmdConfigurationString), utils.DefaultRW)
}

func createDefaultPMDConfigFile() error {
func createDefaultPMDConfigFile(toolsConfigDir string) error {
emptyConfig := tools.ToolConfiguration{}
content := tools.CreatePmdConfig(emptyConfig)
return os.WriteFile("pmd-ruleset.xml", []byte(content), 0644)
return os.WriteFile(filepath.Join(toolsConfigDir, "pmd-ruleset.xml"), []byte(content), utils.DefaultRW)
}

type CodacyToolConfiguration struct {
Expand All @@ -347,15 +377,15 @@ type ParameterConfiguration struct {
}

// createTrivyConfigFile creates a trivy.yaml configuration file based on the API configuration
func createTrivyConfigFile(config CodacyToolConfiguration) error {
func createTrivyConfigFile(config CodacyToolConfiguration, toolsConfigDir string) error {
// Convert CodacyToolConfiguration to tools.ToolConfiguration
trivyDomainConfiguration := convertAPIToolConfigurationForTrivy(config)

// Use the shared CreateTrivyConfig function to generate the config content
trivyConfigurationString := tools.CreateTrivyConfig(trivyDomainConfiguration)

// Write to file
return os.WriteFile("trivy.yaml", []byte(trivyConfigurationString), 0644)
return os.WriteFile(filepath.Join(toolsConfigDir, "trivy.yaml"), []byte(trivyConfigurationString), utils.DefaultRW)
}

// convertAPIToolConfigurationForTrivy converts API tool configuration to domain model for Trivy
Expand Down Expand Up @@ -399,21 +429,21 @@ func convertAPIToolConfigurationForTrivy(config CodacyToolConfiguration) tools.T
}

// createDefaultTrivyConfigFile creates a default trivy.yaml configuration file
func createDefaultTrivyConfigFile() error {
func createDefaultTrivyConfigFile(toolsConfigDir string) error {
// Use empty tool configuration to get default settings
emptyConfig := tools.ToolConfiguration{}
content := tools.CreateTrivyConfig(emptyConfig)

// Write to file
return os.WriteFile("trivy.yaml", []byte(content), 0644)
return os.WriteFile(filepath.Join(toolsConfigDir, "trivy.yaml"), []byte(content), utils.DefaultRW)
}

// createDefaultEslintConfigFile creates a default eslint.config.mjs configuration file
func createDefaultEslintConfigFile() error {
func createDefaultEslintConfigFile(toolsConfigDir string) error {
// Use empty tool configuration to get default settings
emptyConfig := tools.ToolConfiguration{}
content := tools.CreateEslintConfig(emptyConfig)

// Write to file
return os.WriteFile("eslint.config.mjs", []byte(content), 0644)
return os.WriteFile(filepath.Join(toolsConfigDir, "eslint.config.mjs"), []byte(content), utils.DefaultRW)
}
10 changes: 10 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
runtimesDirectory string
toolsDirectory string
localCodacyDirectory string
toolsConfigDirectory string
projectConfigFile string
cliConfigFile string

Expand Down Expand Up @@ -42,6 +43,10 @@
return c.localCodacyDirectory
}

func (c *ConfigType) ToolsConfigsDirectory() string {

Check notice on line 46 in config/config.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/config.go#L46

exported method ConfigType.ToolsConfigsDirectory should have comment or be unexported
return c.toolsConfigDirectory
}

func (c *ConfigType) ProjectConfigFile() string {
return c.projectConfigFile
}
Expand Down Expand Up @@ -88,11 +93,16 @@
return nil
}

func (c *ConfigType) ToolsConfigDirectory() string {

Check notice on line 96 in config/config.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/config.go#L96

exported method ConfigType.ToolsConfigDirectory should have comment or be unexported
return c.toolsConfigDirectory
}

func (c *ConfigType) setupCodacyPaths() {
c.globalCacheDirectory = filepath.Join(c.homePath, ".cache", "codacy")
c.runtimesDirectory = filepath.Join(c.globalCacheDirectory, "runtimes")
c.toolsDirectory = filepath.Join(c.globalCacheDirectory, "tools")
c.localCodacyDirectory = ".codacy"
c.toolsConfigDirectory = filepath.Join(c.localCodacyDirectory, "tools-configs")

c.projectConfigFile = filepath.Join(c.localCodacyDirectory, "codacy.yaml")
c.cliConfigFile = filepath.Join(c.localCodacyDirectory, "cli-config.yaml")
Expand Down
1 change: 0 additions & 1 deletion tools/eslintConfigCreator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func CreateEslintConfig(configuration ToolConfiguration) string {

for _, patternConfiguration := range configuration.PatternsConfiguration {
rule := strings.TrimPrefix(patternConfiguration.PatternId, "ESLint8_")
fmt.Println("Rule:", rule)

const tempstring = "TEMPORARYSTRING"
rule = strings.ReplaceAll(rule, "__", tempstring)
Expand Down
16 changes: 13 additions & 3 deletions tools/eslintRunner.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tools

import (
"codacy/cli-v2/config"
"os"
"os/exec"
"path/filepath"
Expand All @@ -14,6 +15,15 @@ func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory
eslintJsPath := filepath.Join(eslintInstallationNodeModules, ".bin", "eslint")

cmd := exec.Command(nodeBinary, eslintJsPath)

// For Eslint compatibility with version 8.
// https://eslint.org/docs/v8.x/use/configure/configuration-files-new
cmd.Env = append(cmd.Env, "ESLINT_USE_FLAT_CONFIG=true")

// Add config file from tools-configs directory
configFile := filepath.Join(config.Config.ToolsConfigDirectory(), "eslint.config.mjs")
cmd.Args = append(cmd.Args, "-c", configFile)

if autoFix {
cmd.Args = append(cmd.Args, "--fix")
}
Expand All @@ -39,9 +49,9 @@ func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory
nodePathEnv := "NODE_PATH=" + eslintInstallationNodeModules
cmd.Env = append(cmd.Env, nodePathEnv)

//DEBUG
//fmt.Println(cmd.Env)
//fmt.Println(cmd)
// DEBUG
// fmt.Println(cmd.Env)
// fmt.Println(cmd)

// TODO eslint returns 1 when it finds errors, so we're not propagating it
cmd.Run()
Expand Down
38 changes: 26 additions & 12 deletions tools/pmdRunner.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,55 @@
package tools

import (
"codacy/cli-v2/config"
"os"
"os/exec"
"path/filepath"
"strings"
)

// RunPmd executes PMD static code analyzer with the specified options
//
// Parameters:
// - repositoryToAnalyseDirectory: The root directory of the repository to analyze
// - pmdBinary: Path to the PMD executable
// - pathsToCheck: List of specific paths to analyze, if empty analyzes whole repository
// - outputFile: Path where analysis results should be written
// - outputFormat: Format for the output (e.g. "sarif")
// - rulesetFile: Path to custom ruleset XML file, if empty uses default ruleset
//
// Returns:
// - error: nil if analysis succeeds or violations found, error otherwise
func RunPmd(repositoryToAnalyseDirectory string, pmdBinary string, pathsToCheck []string, outputFile string, outputFormat string, rulesetFile string) error {
cmdArgs := []string{"pmd"}
cmd := exec.Command(pmdBinary, "pmd")

Check failure on line 24 in tools/pmdRunner.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/pmdRunner.go#L24

Detected non-static command inside Command.

Check failure on line 24 in tools/pmdRunner.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/pmdRunner.go#L24

OS command injection is a critical vulnerability that can lead to a full system compromise as it may allow an adversary to pass in arbitrary commands or arguments to be executed.

// Add config file from tools-configs directory if not specified
if rulesetFile == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that we pass always "" is this a WIP, or suppose to be like this on the scope of this PR? I see you removed a setting witch was PMD only (which makes sense for me to be removed). So maybe we don't even want this argument if this if is just a fake?

Meaning, instead of deciding this here, assuming that we were passing some kind of default, is to generate a file with the defaults as needed?

Copy link
Contributor Author

@andrzej-janczak andrzej-janczak Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not always, in test I use a specific file and In this case it's ok.

Generally, If in future we want to be able to pass smth like analyze --config xyz.file - like ovveride, then it can stay.
If not we can delete 🤷‍♂️

configFile := filepath.Join(config.Config.ToolsConfigDirectory(), "pmd-ruleset.xml")
cmd.Args = append(cmd.Args, "-R", configFile)
} else {
cmd.Args = append(cmd.Args, "-R", rulesetFile)
}

// Add source directories (comma-separated list for PMD)
if len(pathsToCheck) > 0 {
dirArg := strings.Join(pathsToCheck, ",")
cmdArgs = append(cmdArgs, "-d", dirArg)
cmd.Args = append(cmd.Args, "-d", dirArg)
} else {
// Fall back to whole repo if no specific paths given
cmdArgs = append(cmdArgs, "-d", repositoryToAnalyseDirectory)
}

// Add ruleset
if rulesetFile != "" {
cmdArgs = append(cmdArgs, "-R", rulesetFile)
cmd.Args = append(cmd.Args, "-d", repositoryToAnalyseDirectory)
}

// Format
if outputFormat != "" {
cmdArgs = append(cmdArgs, "-f", outputFormat)
cmd.Args = append(cmd.Args, "-f", outputFormat)
}

// Output file
if outputFile != "" {
cmdArgs = append(cmdArgs, "-r", outputFile)
cmd.Args = append(cmd.Args, "-r", outputFile)
}

cmd := exec.Command(pmdBinary, cmdArgs...)

cmd.Dir = repositoryToAnalyseDirectory
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
Expand Down
6 changes: 6 additions & 0 deletions tools/trivyRunner.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package tools

import (
"codacy/cli-v2/config"
"os"
"os/exec"
"path/filepath"
)

// RunTrivy executes Trivy vulnerability scanner with the specified options
func RunTrivy(repositoryToAnalyseDirectory string, trivyBinary string, pathsToCheck []string, outputFile string, outputFormat string) error {
cmd := exec.Command(trivyBinary, "fs")

// Add config file from tools-configs directory
configFile := filepath.Join(config.Config.ToolsConfigDirectory(), "trivy.yaml")
cmd.Args = append(cmd.Args, "--config", configFile)

// Add format options
if outputFile != "" {
cmd.Args = append(cmd.Args, "--output", outputFile)
Expand Down
Loading