Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codacy/codacy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ runtimes:
- [email protected]
tools:
- [email protected]
- [email protected]
48 changes: 30 additions & 18 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,25 @@ func getToolName(toolName string, version string) string {
return toolName
}

func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string) {
eslint := config.Config.Tools()["eslint"]
eslintInstallationDirectory := eslint.InstallDir
nodeRuntime := config.Config.Runtimes()["node"]
nodeBinary := nodeRuntime.Binaries["node"]

tools.RunEslint(workDirectory, eslintInstallationDirectory, nodeBinary, pathsToCheck, autoFix, outputFile, outputFormat)
}

func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) {
trivy := config.Config.Tools()["trivy"]
trivyBinary := trivy.Binaries["trivy"]

err := tools.RunTrivy(workDirectory, trivyBinary, pathsToCheck, outputFile, outputFormat)
if err != nil {
log.Fatalf("Error running Trivy: %v", err)
}
}

var analyzeCmd = &cobra.Command{
Use: "analyze",
Short: "Runs all linters.",
Expand All @@ -194,30 +213,23 @@ var analyzeCmd = &cobra.Command{
log.Fatal(err)
}

// TODO add more tools here
switch toolToAnalyze {
case "eslint":
// nothing
case "":
log.Fatal("You need to specify a tool to run analysis with, e.g., '--tool eslint'", toolToAnalyze)
default:
log.Fatal("Trying to run unsupported tool: ", toolToAnalyze)
}

eslint := config.Config.Tools()["eslint"]
eslintInstallationDirectory := eslint.InstallDir
nodeRuntime := config.Config.Runtimes()["node"]
nodeBinary := nodeRuntime.Binaries["node"]

log.Printf("Running %s...\n", toolToAnalyze)
if outputFormat == "sarif" {
log.Println("Output will be in SARIF format")
}

if outputFile != "" {
log.Println("Output will be available at", outputFile)
}

tools.RunEslint(workDirectory, eslintInstallationDirectory, nodeBinary, args, autoFix, outputFile, outputFormat)
switch toolToAnalyze {
case "eslint":
runEslintAnalysis(workDirectory, args, autoFix, outputFile, outputFormat)
case "trivy":
runTrivyAnalysis(workDirectory, args, outputFile, outputFormat)
case "":
log.Fatal("You need to specify a tool to run analysis with, e.g., '--tool eslint'")
default:
log.Fatal("Trying to run unsupported tool: ", toolToAnalyze)
}
},
}
}
102 changes: 100 additions & 2 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,25 @@

func configFileTemplate(tools []tools.Tool) string {

// Default version
// Default versions
eslintVersion := "9.3.0"
trivyVersion := "0.59.1" // Latest stable version

for _, tool := range tools {
if tool.Uuid == "f8b29663-2cb2-498d-b923-a10c6a8c05cd" {
eslintVersion = tool.Version
}
if tool.Uuid == "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb" {
trivyVersion = tool.Version
}
}

return fmt.Sprintf(`runtimes:
- [email protected]
tools:
- eslint@%s
`, eslintVersion)
- trivy@%s
`, eslintVersion, trivyVersion)
}

func buildRepositoryConfigurationFiles(token string) error {
Expand Down Expand Up @@ -150,7 +155,24 @@
_, err = eslintConfigFile.WriteString(eslintConfigurationString)
if err != nil {
log.Fatal(err)
}

// Create Trivy configuration after processing ESLint
trivyApiConfiguration := extractTrivyConfiguration(apiToolConfigurations)

Check notice on line 161 in cmd/init.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

cmd/init.go#L161

var trivyApiConfiguration should be trivyAPIConfiguration
if trivyApiConfiguration != nil {
// Create trivy.yaml file based on API configuration
err = createTrivyConfigFile(*trivyApiConfiguration)
if err != nil {
log.Fatal(err)
}
fmt.Println("Trivy configuration created based on Codacy settings")
} else {
// Create default trivy.yaml if no configuration from API
err = createDefaultTrivyConfigFile()
if err != nil {
log.Fatal(err)
}
fmt.Println("Default Trivy configuration created")
}

return nil
Expand Down Expand Up @@ -197,6 +219,20 @@
return nil
}

// extractTrivyConfiguration extracts Trivy configuration from the Codacy API response
func extractTrivyConfiguration(toolConfigurations []CodacyToolConfiguration) *CodacyToolConfiguration {
// Trivy internal codacy uuid
const TrivyUUID = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb"

for _, toolConfiguration := range toolConfigurations {
if toolConfiguration.Uuid == TrivyUUID {
return &toolConfiguration
}
}

return nil
}

type CodacyToolConfiguration struct {
Uuid string `json:"uuid"`
IsEnabled bool `json:"isEnabled"`
Expand All @@ -212,3 +248,65 @@
name string `json:"name"`
value string `json:"value"`
}

// createTrivyConfigFile creates a trivy.yaml configuration file based on the API configuration
func createTrivyConfigFile(config CodacyToolConfiguration) 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)

Check warning on line 261 in cmd/init.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

cmd/init.go#L261

The application was found setting file permissions to overly permissive values.
}

// convertAPIToolConfigurationForTrivy converts API tool configuration to domain model for Trivy
func convertAPIToolConfigurationForTrivy(config CodacyToolConfiguration) tools.ToolConfiguration {
var patterns []tools.PatternConfiguration

// Only process if tool is enabled
if config.IsEnabled {
for _, pattern := range config.Patterns {
var parameters []tools.PatternParameterConfiguration

// By default patterns are enabled
patternEnabled := true

// Check if there's an explicit enabled parameter
for _, param := range pattern.Parameters {
if param.name == "enabled" && param.value == "false" {
patternEnabled = false
}
}

// Add enabled parameter
parameters = append(parameters, tools.PatternParameterConfiguration{
Name: "enabled",
Value: fmt.Sprintf("%t", patternEnabled),
})

patterns = append(
patterns,
tools.PatternConfiguration{
PatternId: pattern.InternalId,
ParamenterConfigurations: parameters,
},
)
}
}

return tools.ToolConfiguration{
PatternsConfiguration: patterns,
}
}

// createDefaultTrivyConfigFile creates a default trivy.yaml configuration file
func createDefaultTrivyConfigFile() 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)

Check warning on line 311 in cmd/init.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

cmd/init.go#L311

The application was found setting file permissions to overly permissive values.
}
86 changes: 77 additions & 9 deletions config/tools-installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import (
"bytes"
"codacy/cli-v2/plugins"
"codacy/cli-v2/utils"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"text/template"
)
Expand All @@ -30,18 +32,26 @@
return nil
}

// Get the runtime for this tool
runtimeInfo, ok := Config.Runtimes()[toolInfo.Runtime]
if !ok {
return fmt.Errorf("required runtime %s not found for tool %s", toolInfo.Runtime, name)
}

// Make sure the installation directory exists
err := os.MkdirAll(toolInfo.InstallDir, 0755)
if err != nil {
return fmt.Errorf("failed to create installation directory: %w", err)
}

// Check if this is a download-based tool (like trivy) or a runtime-based tool (like eslint)
if toolInfo.DownloadURL != "" {
// This is a download-based tool
return installDownloadBasedTool(toolInfo)
Copy link
Contributor Author

@andrzej-janczak andrzej-janczak Mar 31, 2025

Choose a reason for hiding this comment

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

return ❤️ 🥲

}

// This is a runtime-based tool, proceed with regular installation

// Get the runtime for this tool
runtimeInfo, ok := Config.Runtimes()[toolInfo.Runtime]
if !ok {
return fmt.Errorf("required runtime %s not found for tool %s", toolInfo.Runtime, name)
}

// Prepare template data
templateData := map[string]string{
"InstallDir": toolInfo.InstallDir,
Expand Down Expand Up @@ -80,7 +90,7 @@

// Execute the installation command using the package manager
cmd := exec.Command(packageManagerBinary, strings.Split(installCmd, " ")...)

log.Printf("Installing %s v%s...\n", toolInfo.Name, toolInfo.Version)
output, err := cmd.CombinedOutput()
if err != nil {
Expand All @@ -91,6 +101,64 @@
return nil
}

// installDownloadBasedTool installs a tool by downloading and extracting it
func installDownloadBasedTool(toolInfo *plugins.ToolInfo) error {

Check failure on line 105 in config/tools-installer.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/tools-installer.go#L105

Method installDownloadBasedTool has a cyclomatic complexity of 18 (limit is 10)
// Create a file name for the downloaded archive
fileName := filepath.Base(toolInfo.DownloadURL)
downloadPath := filepath.Join(Config.ToolsDirectory(), fileName)

// Check if the file already exists
_, err := os.Stat(downloadPath)
if os.IsNotExist(err) {
// Download the file
log.Printf("Downloading %s v%s...\n", toolInfo.Name, toolInfo.Version)
downloadPath, err = utils.DownloadFile(toolInfo.DownloadURL, Config.ToolsDirectory())
if err != nil {
return fmt.Errorf("failed to download tool: %w", err)
}
} else if err != nil {
return fmt.Errorf("error checking for existing download: %w", err)
} else {
log.Printf("Using existing download for %s v%s\n", toolInfo.Name, toolInfo.Version)
}

// Open the downloaded file
file, err := os.Open(downloadPath)
if err != nil {
return fmt.Errorf("failed to open downloaded file: %w", err)
}
defer file.Close()

// Create the installation directory
err = os.MkdirAll(toolInfo.InstallDir, 0755)

Check warning on line 133 in config/tools-installer.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/tools-installer.go#L133

Detected file permissions that are set to more than `0600` (user/owner can read and write). Setting file permissions to higher than `0600` is most likely unnecessary and violates the principle of least privilege.

Check warning on line 133 in config/tools-installer.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/tools-installer.go#L133

The application was found setting directory permissions to overly permissive values.
if err != nil {
return fmt.Errorf("failed to create installation directory: %w", err)
}

// Extract directly to the installation directory
log.Printf("Extracting %s v%s...\n", toolInfo.Name, toolInfo.Version)
if strings.HasSuffix(fileName, ".zip") {
err = utils.ExtractZip(file.Name(), toolInfo.InstallDir)
} else {
err = utils.ExtractTarGz(file, toolInfo.InstallDir)
}

if err != nil {
return fmt.Errorf("failed to extract tool: %w", err)
}

// Make sure all binaries are executable
for _, binaryPath := range toolInfo.Binaries {
err = os.Chmod(filepath.Join(toolInfo.InstallDir, filepath.Base(binaryPath)), 0755)

Check warning on line 152 in config/tools-installer.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/tools-installer.go#L152

Detected file permissions that are set to more than `0600` (user/owner can read and write). Setting file permissions to higher than `0600` is most likely unnecessary and violates the principle of least privilege.

Check warning on line 152 in config/tools-installer.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/tools-installer.go#L152

The application was found setting file permissions to overly permissive values.
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to make binary executable: %w", err)
}
}

log.Printf("Successfully installed %s v%s\n", toolInfo.Name, toolInfo.Version)
return nil
}

// isToolInstalled checks if a tool is already installed by checking for the binary
func isToolInstalled(toolInfo *plugins.ToolInfo) bool {
// If there are no binaries, check the install directory
Expand All @@ -116,12 +184,12 @@
if err != nil {
return "", err
}

var buf bytes.Buffer
err = tmpl.Execute(&buf, data)
if err != nil {
return "", err
}

return buf.String(), nil
}
Loading