diff --git a/cmd/analyze.go b/cmd/analyze.go index dfc256a0..d651a209 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -41,6 +41,7 @@ type LanguagesConfig struct { Name string `yaml:"name" json:"name"` Languages []string `yaml:"languages" json:"languages"` Extensions []string `yaml:"extensions" json:"extensions"` + Files []string `yaml:"files" json:"files"` } `yaml:"tools" json:"tools"` } @@ -90,7 +91,7 @@ func GetFileExtension(filePath string) string { return strings.ToLower(filepath.Ext(filePath)) } -// IsToolSupportedForFile checks if a tool supports a given file based on its extension +// IsToolSupportedForFile checks if a tool supports a given file based on its extension or filename func IsToolSupportedForFile(toolName string, filePath string, langConfig *LanguagesConfig) bool { if langConfig == nil { // If no language config is available, assume all tools are supported @@ -98,11 +99,14 @@ func IsToolSupportedForFile(toolName string, filePath string, langConfig *Langua } fileExt := GetFileExtension(filePath) + if fileExt == "" { // If file has no extension, assume tool is supported return true } + fileName := filepath.Base(filePath) + for _, tool := range langConfig.Tools { if tool.Name == toolName { // If tool has no extensions defined, assume it supports all files @@ -117,6 +121,13 @@ func IsToolSupportedForFile(toolName string, filePath string, langConfig *Langua } } + // Check if filename is supported by this tool (exact match) + for _, file := range tool.Files { + if strings.EqualFold(file, fileName) { + return true + } + } + // Extension not found in tool's supported extensions return false } diff --git a/cmd/analyze_test.go b/cmd/analyze_test.go index e8de72ed..75ab3020 100644 --- a/cmd/analyze_test.go +++ b/cmd/analyze_test.go @@ -62,21 +62,31 @@ func TestIsToolSupportedForFile(t *testing.T) { Name string `yaml:"name" json:"name"` Languages []string `yaml:"languages" json:"languages"` Extensions []string `yaml:"extensions" json:"extensions"` + Files []string `yaml:"files" json:"files"` }{ { Name: "pylint", Languages: []string{"Python"}, Extensions: []string{".py"}, + Files: []string{}, + }, + { + Name: "eslint", + Languages: []string{"JavaScript", "TypeScript"}, + Extensions: []string{}, + Files: []string{}, }, { Name: "cppcheck", Languages: []string{"C", "CPP"}, Extensions: []string{".c", ".cpp", ".h", ".hpp"}, + Files: []string{}, }, { Name: "trivy", Languages: []string{"Multiple"}, - Extensions: []string{}, + Extensions: []string{".yaml", ".yml"}, + Files: []string{"requirements.txt"}, }, }, } @@ -111,7 +121,7 @@ func TestIsToolSupportedForFile(t *testing.T) { }, { name: "Tool with no extensions specified", - toolName: "trivy", + toolName: "eslint", filePath: "any.file", config: langConfig, want: true, @@ -130,6 +140,13 @@ func TestIsToolSupportedForFile(t *testing.T) { config: nil, want: true, }, + { + name: "Trivy with requirements.txt", + toolName: "trivy", + filePath: "requirements.txt", + config: langConfig, + want: true, + }, } for _, tt := range tests { diff --git a/codacy-client/client.go b/codacy-client/client.go index 5f7c7b56..f2934994 100644 --- a/codacy-client/client.go +++ b/codacy-client/client.go @@ -253,7 +253,7 @@ func GetToolsVersions() ([]domain.Tool, error) { } // GetRepositoryLanguages fetches the languages for a repository -func GetRepositoryLanguages(initFlags domain.InitFlags) ([]domain.Language, error) { +func GetRepositoryLanguages(initFlags domain.InitFlags) ([]domain.RepositoryLanguage, error) { baseURL := fmt.Sprintf("%s/api/v3/organizations/%s/%s/repositories/%s/settings/languages", CodacyApiBase, initFlags.Provider, diff --git a/domain/language.go b/domain/language.go index 6f9da01a..ee2a559b 100644 --- a/domain/language.go +++ b/domain/language.go @@ -1,23 +1,32 @@ package domain -// Language represents a language in the Codacy API -type Language struct { +// RepositoryLanguage represents a language in the Codacy API +type RepositoryLanguage struct { Name string `json:"name"` CodacyDefaults []string `json:"codacyDefaults"` Extensions []string `json:"extensions"` + DefaultFiles []string `json:"defaultFiles"` Enabled bool `json:"enabled"` Detected bool `json:"detected"` } // LanguagesResponse represents the structure of the languages response type LanguagesResponse struct { - Languages []Language `json:"languages"` + Languages []RepositoryLanguage `json:"languages"` +} + +// Language represents a processed language with combined extensions and files +type Language struct { + Name string + Extensions []string + Files []string } // LanguageTool represents a language tool with its file extensions from the API type LanguageTool struct { Name string `json:"name"` FileExtensions []string `json:"fileExtensions"` + Files []string `json:"files"` } // LanguageToolsResponse represents the structure of the language tools API response @@ -30,6 +39,7 @@ type ToolLanguageInfo struct { Name string `yaml:"name"` Languages []string `yaml:"languages,flow"` Extensions []string `yaml:"extensions,flow"` + Files []string `yaml:"files,flow"` } // LanguagesConfig represents the structure of the languages configuration file diff --git a/integration-tests/config-discover/expected/codacy.yaml b/integration-tests/config-discover/expected/codacy.yaml index 2bc78526..558d375b 100644 --- a/integration-tests/config-discover/expected/codacy.yaml +++ b/integration-tests/config-discover/expected/codacy.yaml @@ -10,4 +10,4 @@ tools: - pmd@7.11.0 - pylint@3.3.6 - semgrep@1.78.0 - - trivy@0.59.1 \ No newline at end of file + - trivy@0.59.1 diff --git a/integration-tests/config-discover/expected/tools-configs/languages-config.yaml b/integration-tests/config-discover/expected/tools-configs/languages-config.yaml index 5d54c143..6e686c11 100644 --- a/integration-tests/config-discover/expected/tools-configs/languages-config.yaml +++ b/integration-tests/config-discover/expected/tools-configs/languages-config.yaml @@ -2,24 +2,32 @@ tools: - name: dartanalyzer languages: [Dart] extensions: [.dart] + files: [] - name: eslint languages: [Javascript, TypeScript] extensions: [.js, .jsm, .jsx, .mjs, .ts, .tsx, .vue] + files: [] - name: lizard languages: [C, CPP, CSharp, Erlang, Fortran, Go, Java, Javascript, Kotlin, Lua, Objective C, PHP, Python, Ruby, Rust, Scala, Solidity, Swift, TypeScript] extensions: [.c, .cc, .cpp, .cs, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .m, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .ts, .tsx, .vue] + files: [] - name: pmd languages: [Apex, JSP, Java, Javascript, PLSQL, SQL, Velocity, VisualForce, XML] extensions: [.cls, .component, .fnc, .java, .js, .jsm, .jsp, .jsx, .mjs, .page, .pck, .pkb, .pkh, .pks, .plb, .pld, .plh, .pls, .pom, .prc, .sql, .tpb, .tps, .trg, .trigger, .tyb, .typ, .vm, .vue, .wsdl, .xml, .xsl] + files: [] - name: pylint languages: [Python] extensions: [.py] + files: [] - name: revive languages: [Go] extensions: [.go] + files: [] - name: semgrep languages: [Apex, C, CPP, CSharp, Dockerfile, Go, Java, Javascript, Kotlin, PHP, PLSQL, Python, Ruby, Rust, SQL, Scala, Shell, Swift, Terraform, TypeScript, YAML] extensions: [.bash, .c, .cc, .cls, .cpp, .cs, .cxx, .dockerfile, .fnc, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .mjs, .opal, .pck, .php, .pkb, .pkh, .pks, .plb, .pld, .plh, .pls, .podspec, .prc, .py, .rake, .rb, .rlib, .rs, .scala, .sh, .sql, .swift, .tf, .tpb, .tps, .trg, .trigger, .ts, .tsx, .tyb, .typ, .vue, .yaml, .yml] + files: [] - name: trivy languages: [C, CPP, CSharp, Dart, Dockerfile, Elixir, Go, JSON, Java, Javascript, PHP, Python, Ruby, Rust, Scala, Swift, Terraform, TypeScript, XML, YAML] extensions: [.c, .cc, .cpp, .cs, .cxx, .dart, .dockerfile, .ex, .exs, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .json, .jsx, .mjs, .opal, .php, .podspec, .pom, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .tf, .ts, .tsx, .vue, .wsdl, .xml, .xsl, .yaml, .yml] + files: [.deps.json, Berksfile, Capfile, Cargo.lock, Cheffile, Directory.Packages.props, Dockerfile, Fastfile, Gemfile, Gemfile.lock, Guardfile, Package.resolved, Packages.props, Pipfile.lock, Podfile, Podfile.lock, Rakefile, Thorfile, Vagabondfile, Vagrantfile, build.sbt.lock, composer.lock, conan.lock, config.ru, go.mod, gradle.lockfile, mix.lock, package-lock.json, package.json, packages.config, packages.lock.json, pnpm-lock.yaml, poetry.lock, pom.xml, pubspec.lock, requirements.txt, uv.lock, yarn.lock] diff --git a/integration-tests/init-with-token/expected/tools-configs/languages-config.yaml b/integration-tests/init-with-token/expected/tools-configs/languages-config.yaml index 8c8d5243..71f6d25f 100644 --- a/integration-tests/init-with-token/expected/tools-configs/languages-config.yaml +++ b/integration-tests/init-with-token/expected/tools-configs/languages-config.yaml @@ -2,18 +2,24 @@ tools: - name: eslint languages: [Javascript] extensions: [.js, .jsm, .jsx, .mjs, .vue] + files: [] - name: lizard languages: [Java, Javascript, Python] extensions: [.java, .js, .jsm, .jsx, .mjs, .py, .vue] + files: [] - name: pmd languages: [Java, Javascript] extensions: [.java, .js, .jsm, .jsx, .mjs, .vue] + files: [] - name: pylint languages: [Python] extensions: [.py] + files: [] - name: semgrep languages: [Java, Javascript, Python] extensions: [.java, .js, .jsm, .jsx, .mjs, .py, .vue] + files: [] - name: trivy languages: [JSON, Java, Javascript, Python] - extensions: [.java, .js, .jsm, .json, .jsx, .mjs, .py, .vue] \ No newline at end of file + extensions: [.java, .js, .jsm, .json, .jsx, .mjs, .py, .vue] + files: [Pipfile.lock, gradle.lockfile, package-lock.json, package.json, pnpm-lock.yaml, poetry.lock, pom.xml, requirements.txt, uv.lock, yarn.lock] diff --git a/integration-tests/init-without-token/expected/tools-configs/languages-config.yaml b/integration-tests/init-without-token/expected/tools-configs/languages-config.yaml index 385510f8..0998dce0 100644 --- a/integration-tests/init-without-token/expected/tools-configs/languages-config.yaml +++ b/integration-tests/init-without-token/expected/tools-configs/languages-config.yaml @@ -2,24 +2,32 @@ tools: - name: dartanalyzer languages: [Dart] extensions: [.dart] + files: [] - name: eslint languages: [Javascript, TypeScript] extensions: [.js, .jsm, .jsx, .mjs, .ts, .tsx, .vue] + files: [] - name: lizard languages: [C, CPP, CSharp, Erlang, Fortran, Go, Java, Javascript, Kotlin, Lua, Objective C, PHP, Python, Ruby, Rust, Scala, Solidity, Swift, TypeScript] extensions: [.c, .cc, .cpp, .cs, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .m, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .ts, .tsx, .vue] + files: [] - name: pmd languages: [Apex, JSP, Java, Javascript, PLSQL, SQL, Velocity, VisualForce, XML] extensions: [.cls, .component, .fnc, .java, .js, .jsm, .jsp, .jsx, .mjs, .page, .pck, .pkb, .pkh, .pks, .plb, .pld, .plh, .pls, .pom, .prc, .sql, .tpb, .tps, .trg, .trigger, .tyb, .typ, .vm, .vue, .wsdl, .xml, .xsl] + files: [] - name: pylint languages: [Python] extensions: [.py] + files: [] - name: revive languages: [Go] extensions: [.go] + files: [] - name: semgrep languages: [Apex, C, CPP, CSharp, Dockerfile, Go, Java, Javascript, Kotlin, PHP, PLSQL, Python, Ruby, Rust, SQL, Scala, Shell, Swift, Terraform, TypeScript, YAML] extensions: [.bash, .c, .cc, .cls, .cpp, .cs, .cxx, .dockerfile, .fnc, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .mjs, .opal, .pck, .php, .pkb, .pkh, .pks, .plb, .pld, .plh, .pls, .podspec, .prc, .py, .rake, .rb, .rlib, .rs, .scala, .sh, .sql, .swift, .tf, .tpb, .tps, .trg, .trigger, .ts, .tsx, .tyb, .typ, .vue, .yaml, .yml] + files: [] - name: trivy languages: [C, CPP, CSharp, Dart, Dockerfile, Elixir, Go, JSON, Java, Javascript, PHP, Python, Ruby, Rust, Scala, Swift, Terraform, TypeScript, XML, YAML] - extensions: [.c, .cc, .cpp, .cs, .cxx, .dart, .dockerfile, .ex, .exs, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .json, .jsx, .mjs, .opal, .php, .podspec, .pom, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .tf, .ts, .tsx, .vue, .wsdl, .xml, .xsl, .yaml, .yml] \ No newline at end of file + extensions: [.c, .cc, .cpp, .cs, .cxx, .dart, .dockerfile, .ex, .exs, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .json, .jsx, .mjs, .opal, .php, .podspec, .pom, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .tf, .ts, .tsx, .vue, .wsdl, .xml, .xsl, .yaml, .yml] + files: [.deps.json, Berksfile, Capfile, Cargo.lock, Cheffile, Directory.Packages.props, Dockerfile, Fastfile, Gemfile, Gemfile.lock, Guardfile, Package.resolved, Packages.props, Pipfile.lock, Podfile, Podfile.lock, Rakefile, Thorfile, Vagabondfile, Vagrantfile, build.sbt.lock, composer.lock, conan.lock, config.ru, go.mod, gradle.lockfile, mix.lock, package-lock.json, package.json, packages.config, packages.lock.json, pnpm-lock.yaml, poetry.lock, pom.xml, pubspec.lock, requirements.txt, uv.lock, yarn.lock] \ No newline at end of file diff --git a/plugins/tools/eslint/test/src/.codacy/tools-configs/eslint.config.mjs b/plugins/tools/eslint/test/src/.codacy/tools-configs/eslint.config.mjs new file mode 100644 index 00000000..92f3fc1b --- /dev/null +++ b/plugins/tools/eslint/test/src/.codacy/tools-configs/eslint.config.mjs @@ -0,0 +1,67 @@ +export default [ + { + rules: { + "constructor-super": ["error"], + "for-direction": ["error"], + "getter-return": ["error", {"allowImplicit": false}], + "no-async-promise-executor": ["error"], + "no-case-declarations": ["error"], + "no-class-assign": ["error"], + "no-compare-neg-zero": ["error"], + "no-cond-assign": ["error", "except-parens"], + "no-constant-condition": ["error", {"checkLoops": true}], + "no-const-assign": ["error"], + "no-control-regex": ["error"], + "no-debugger": ["error"], + "no-delete-var": ["error"], + "no-dupe-args": ["error"], + "no-dupe-class-members": ["error"], + "no-dupe-else-if": ["error"], + "no-dupe-keys": ["error"], + "no-duplicate-case": ["error"], + "no-empty": ["error", {"allowEmptyCatch": false}], + "no-empty-character-class": ["error"], + "no-empty-pattern": ["error", {"allowObjectPatternsAsParameters": false}], + "no-ex-assign": ["error"], + "no-extra-boolean-cast": ["error", {"enforceForLogicalOperands": false}], + "no-extra-semi": ["error"], + "no-fallthrough": ["error", {"allowEmptyCase": false}], + "no-func-assign": ["error"], + "no-global-assign": ["error"], + "no-import-assign": ["error"], + "no-inner-declarations": ["error", "functions"], + "no-invalid-regexp": ["error"], + "no-irregular-whitespace": ["error", {"skipComments": false, "skipJSXText": false, "skipRegExps": false, "skipStrings": true, "skipTemplates": false}], + "no-loss-of-precision": ["error"], + "no-misleading-character-class": ["error"], + "no-mixed-spaces-and-tabs": ["error"], + "no-new-symbol": ["error"], + "no-nonoctal-decimal-escape": ["error"], + "no-obj-calls": ["error"], + "no-octal": ["error"], + "no-prototype-builtins": ["error"], + "no-redeclare": ["error", {"builtinGlobals": true}], + "no-regex-spaces": ["error"], + "no-self-assign": ["error", {"props": true}], + "no-setter-return": ["error"], + "no-shadow-restricted-names": ["error"], + "no-sparse-arrays": ["error"], + "no-this-before-super": ["error"], + "no-undef": ["error", {"typeof": false}], + "no-unexpected-multiline": ["error"], + "no-unreachable": ["error"], + "no-unsafe-finally": ["error"], + "no-unsafe-negation": ["error", {"enforceForOrderingRelations": false}], + "no-unsafe-optional-chaining": ["error", {"disallowArithmeticOperators": false}], + "no-unused-labels": ["error"], + "no-unused-vars": ["error"], + "no-useless-backreference": ["error"], + "no-useless-catch": ["error"], + "no-useless-escape": ["error"], + "no-with": ["error"], + "require-yield": ["error"], + "use-isnan": ["error", {"enforceForIndexOf": false, "enforceForSwitchCase": true}], + "valid-typeof": ["error", {"requireStringLiterals": false}], + } + } +]; \ No newline at end of file diff --git a/plugins/tools/eslint/test/src/.codacy/tools-configs/languages-config.yaml b/plugins/tools/eslint/test/src/.codacy/tools-configs/languages-config.yaml new file mode 100644 index 00000000..0026c53f --- /dev/null +++ b/plugins/tools/eslint/test/src/.codacy/tools-configs/languages-config.yaml @@ -0,0 +1,4 @@ +tools: + - name: eslint + languages: [Javascript, TypeScript] + extensions: [.js, .jsm, .jsx, .mjs, .ts, .tsx, .vue] diff --git a/plugins/tools/pylint/test/src/.codacy/tools-configs/languages-config.yaml b/plugins/tools/pylint/test/src/.codacy/tools-configs/languages-config.yaml new file mode 100644 index 00000000..8b270b2d --- /dev/null +++ b/plugins/tools/pylint/test/src/.codacy/tools-configs/languages-config.yaml @@ -0,0 +1,5 @@ +tools: + - name: pylint + languages: [Python] + extensions: [.py] + files: [] \ No newline at end of file diff --git a/plugins/tools/pylint/test/src/.codacy/tools-configs/pylint.rc b/plugins/tools/pylint/test/src/.codacy/tools-configs/pylint.rc new file mode 100644 index 00000000..92f4aea4 --- /dev/null +++ b/plugins/tools/pylint/test/src/.codacy/tools-configs/pylint.rc @@ -0,0 +1,9 @@ +[MASTER] +ignore=CVS +persistent=yes +load-plugins= + +[MESSAGES CONTROL] +disable=all +enable=C0123,C0200,C0303,E0100,E0101,E0102,E0103,E0104,E0105,E0106,E0107,E0108,E0110,E0112,E0113,E0114,E0115,E0116,E0117,E0202,E0203,E0211,E0236,E0238,E0239,E0240,E0241,E0301,E0302,E0601,E0603,E0604,E0701,E0702,E0704,E0710,E0711,E0712,E1003,E1102,E1111,E1120,E1121,E1123,E1124,E1125,E1126,E1127,E1132,E1200,E1201,E1205,E1206,E1300,E1301,E1302,E1303,E1304,E1305,E1306,R0202,R0203,W0101,W0102,W0104,W0105,W0106,W0107,W0108,W0109,W0120,W0122,W0124,W0150,W0199,W0221,W0222,W0233,W0404,W0410,W0601,W0602,W0604,W0611,W0612,W0622,W0702,W0705,W0711,W1300,W1301,W1302,W1303,W1305,W1306,W1307 + diff --git a/plugins/tools/pylint/test/src/requirements.txt b/plugins/tools/pylint/test/src/requirements.txt new file mode 100644 index 00000000..fbde3519 --- /dev/null +++ b/plugins/tools/pylint/test/src/requirements.txt @@ -0,0 +1,2 @@ +# This file should be successfully ignored by Codacy CLI as pylint does not support it. +django==1.11.29 \ No newline at end of file diff --git a/plugins/tools/trivy/test/expected.sarif b/plugins/tools/trivy/test/expected.sarif index 9507b051..562e72b3 100644 --- a/plugins/tools/trivy/test/expected.sarif +++ b/plugins/tools/trivy/test/expected.sarif @@ -9,6 +9,33 @@ } }, "results": [ + { + "level": "note", + "locations": [ + { + "message": { + "text": "package-lock.json: brace-expansion@1.1.11" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "package-lock.json", + "uriBaseId": "ROOTPATH" + }, + "region": { + "endColumn": 1, + "endLine": 357, + "startColumn": 1, + "startLine": 349 + } + } + } + ], + "message": { + "text": "Package: brace-expansion\nInstalled Version: 1.1.11\nVulnerability CVE-2025-5889\nSeverity: LOW\nFixed Version: 2.0.2, 1.1.12, 3.0.1, 4.0.1\nLink: [CVE-2025-5889](https://avd.aquasec.com/nvd/cve-2025-5889)" + }, + "ruleId": "CVE-2025-5889", + "ruleIndex": 0 + }, { "level": "error", "locations": [ @@ -37,31 +64,112 @@ "ruleIndex": 1 }, { - "level": "note", + "level": "error", "locations": [ { "message": { - "text": "package-lock.json: brace-expansion@1.1.11" + "text": "requirements.txt: django@1.11.29" }, "physicalLocation": { "artifactLocation": { - "uri": "package-lock.json", + "uri": "requirements.txt", "uriBaseId": "ROOTPATH" }, "region": { "endColumn": 1, - "endLine": 357, + "endLine": 1, "startColumn": 1, - "startLine": 349 + "startLine": 1 } } } ], "message": { - "text": "Package: brace-expansion\nInstalled Version: 1.1.11\nVulnerability CVE-2025-5889\nSeverity: LOW\nFixed Version: 2.0.2, 1.1.12, 3.0.1, 4.0.1\nLink: [CVE-2025-5889](https://avd.aquasec.com/nvd/cve-2025-5889)" + "text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2022-36359\nSeverity: HIGH\nFixed Version: 3.2.15, 4.0.7\nLink: [CVE-2022-36359](https://avd.aquasec.com/nvd/cve-2022-36359)" }, - "ruleId": "CVE-2025-5889", - "ruleIndex": 0 + "ruleId": "CVE-2022-36359", + "ruleIndex": 2 + }, + { + "level": "warning", + "locations": [ + { + "message": { + "text": "requirements.txt: django@1.11.29" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "requirements.txt", + "uriBaseId": "ROOTPATH" + }, + "region": { + "endColumn": 1, + "endLine": 1, + "startColumn": 1, + "startLine": 1 + } + } + } + ], + "message": { + "text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2021-33203\nSeverity: MEDIUM\nFixed Version: 2.2.24, 3.1.12, 3.2.4\nLink: [CVE-2021-33203](https://avd.aquasec.com/nvd/cve-2021-33203)" + }, + "ruleId": "CVE-2021-33203", + "ruleIndex": 3 + }, + { + "level": "warning", + "locations": [ + { + "message": { + "text": "requirements.txt: django@1.11.29" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "requirements.txt", + "uriBaseId": "ROOTPATH" + }, + "region": { + "endColumn": 1, + "endLine": 1, + "startColumn": 1, + "startLine": 1 + } + } + } + ], + "message": { + "text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2024-45231\nSeverity: MEDIUM\nFixed Version: 5.1.1, 5.0.9, 4.2.16\nLink: [CVE-2024-45231](https://avd.aquasec.com/nvd/cve-2024-45231)" + }, + "ruleId": "CVE-2024-45231", + "ruleIndex": 4 + }, + { + "level": "warning", + "locations": [ + { + "message": { + "text": "requirements.txt: django@1.11.29" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "requirements.txt", + "uriBaseId": "ROOTPATH" + }, + "region": { + "endColumn": 1, + "endLine": 1, + "startColumn": 1, + "startLine": 1 + } + } + } + ], + "message": { + "text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2025-48432\nSeverity: MEDIUM\nFixed Version: 5.2.2, 5.1.10, 4.2.22\nLink: [CVE-2025-48432](https://avd.aquasec.com/nvd/cve-2025-48432)" + }, + "ruleId": "CVE-2025-48432", + "ruleIndex": 5 } ], "tool": { diff --git a/plugins/tools/trivy/test/src/.codacy/tools-configs/languages-config.yaml b/plugins/tools/trivy/test/src/.codacy/tools-configs/languages-config.yaml new file mode 100644 index 00000000..6e686c11 --- /dev/null +++ b/plugins/tools/trivy/test/src/.codacy/tools-configs/languages-config.yaml @@ -0,0 +1,33 @@ +tools: + - name: dartanalyzer + languages: [Dart] + extensions: [.dart] + files: [] + - name: eslint + languages: [Javascript, TypeScript] + extensions: [.js, .jsm, .jsx, .mjs, .ts, .tsx, .vue] + files: [] + - name: lizard + languages: [C, CPP, CSharp, Erlang, Fortran, Go, Java, Javascript, Kotlin, Lua, Objective C, PHP, Python, Ruby, Rust, Scala, Solidity, Swift, TypeScript] + extensions: [.c, .cc, .cpp, .cs, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .m, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .ts, .tsx, .vue] + files: [] + - name: pmd + languages: [Apex, JSP, Java, Javascript, PLSQL, SQL, Velocity, VisualForce, XML] + extensions: [.cls, .component, .fnc, .java, .js, .jsm, .jsp, .jsx, .mjs, .page, .pck, .pkb, .pkh, .pks, .plb, .pld, .plh, .pls, .pom, .prc, .sql, .tpb, .tps, .trg, .trigger, .tyb, .typ, .vm, .vue, .wsdl, .xml, .xsl] + files: [] + - name: pylint + languages: [Python] + extensions: [.py] + files: [] + - name: revive + languages: [Go] + extensions: [.go] + files: [] + - name: semgrep + languages: [Apex, C, CPP, CSharp, Dockerfile, Go, Java, Javascript, Kotlin, PHP, PLSQL, Python, Ruby, Rust, SQL, Scala, Shell, Swift, Terraform, TypeScript, YAML] + extensions: [.bash, .c, .cc, .cls, .cpp, .cs, .cxx, .dockerfile, .fnc, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .mjs, .opal, .pck, .php, .pkb, .pkh, .pks, .plb, .pld, .plh, .pls, .podspec, .prc, .py, .rake, .rb, .rlib, .rs, .scala, .sh, .sql, .swift, .tf, .tpb, .tps, .trg, .trigger, .ts, .tsx, .tyb, .typ, .vue, .yaml, .yml] + files: [] + - name: trivy + languages: [C, CPP, CSharp, Dart, Dockerfile, Elixir, Go, JSON, Java, Javascript, PHP, Python, Ruby, Rust, Scala, Swift, Terraform, TypeScript, XML, YAML] + extensions: [.c, .cc, .cpp, .cs, .cxx, .dart, .dockerfile, .ex, .exs, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .json, .jsx, .mjs, .opal, .php, .podspec, .pom, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .tf, .ts, .tsx, .vue, .wsdl, .xml, .xsl, .yaml, .yml] + files: [.deps.json, Berksfile, Capfile, Cargo.lock, Cheffile, Directory.Packages.props, Dockerfile, Fastfile, Gemfile, Gemfile.lock, Guardfile, Package.resolved, Packages.props, Pipfile.lock, Podfile, Podfile.lock, Rakefile, Thorfile, Vagabondfile, Vagrantfile, build.sbt.lock, composer.lock, conan.lock, config.ru, go.mod, gradle.lockfile, mix.lock, package-lock.json, package.json, packages.config, packages.lock.json, pnpm-lock.yaml, poetry.lock, pom.xml, pubspec.lock, requirements.txt, uv.lock, yarn.lock] diff --git a/plugins/tools/trivy/test/src/.codacy/tools-configs/trivy.yaml b/plugins/tools/trivy/test/src/.codacy/tools-configs/trivy.yaml new file mode 100644 index 00000000..c785541c --- /dev/null +++ b/plugins/tools/trivy/test/src/.codacy/tools-configs/trivy.yaml @@ -0,0 +1,10 @@ +severity: + - LOW + - MEDIUM + - HIGH + - CRITICAL + +scan: + scanners: + - vuln + - secret diff --git a/plugins/tools/trivy/test/src/requirements.txt b/plugins/tools/trivy/test/src/requirements.txt new file mode 100644 index 00000000..fd925c78 --- /dev/null +++ b/plugins/tools/trivy/test/src/requirements.txt @@ -0,0 +1 @@ +django==1.11.29 \ No newline at end of file diff --git a/tools/language_config.go b/tools/language_config.go index d1aa10ef..0afb7d9f 100644 --- a/tools/language_config.go +++ b/tools/language_config.go @@ -23,6 +23,12 @@ import ( // This file is responsible for building the languages-config.yaml file. // +// Tools that support specific files (hardcoded list) +// We want to move this to the API in the future +var supportSpecificFiles = map[string]bool{ + "trivy": true, +} + // buildToolLanguageInfoFromAPI builds tool language information from API data // This is the core shared logic used by both GetToolLanguageMappingFromAPI and buildToolLanguageConfigFromAPI func buildToolLanguageInfoFromAPI() (map[string]domain.ToolLanguageInfo, error) { @@ -44,6 +50,12 @@ func buildToolLanguageInfoFromAPI() (map[string]domain.ToolLanguageInfo, error) languageExtensionsMap[strings.ToLower(langTool.Name)] = langTool.FileExtensions } + // Create map of language name to files + languageFilesMap := make(map[string][]string) + for _, langTool := range languageTools { + languageFilesMap[strings.ToLower(langTool.Name)] = langTool.Files + } + // Build tool language configurations from API data result := make(map[string]domain.ToolLanguageInfo) supportedToolNames := make(map[string]bool) @@ -70,10 +82,13 @@ func buildToolLanguageInfoFromAPI() (map[string]domain.ToolLanguageInfo, error) Name: toolName, Languages: tool.Languages, Extensions: []string{}, + Files: []string{}, } - // Build extensions from API language data + // Build extensions and files from API language data extensionsSet := make(map[string]struct{}) + filesSet := make(map[string]struct{}) + for _, apiLang := range tool.Languages { lowerLang := strings.ToLower(apiLang) if extensions, exists := languageExtensionsMap[lowerLang]; exists { @@ -81,13 +96,25 @@ func buildToolLanguageInfoFromAPI() (map[string]domain.ToolLanguageInfo, error) extensionsSet[ext] = struct{}{} } } + // Only populate files if the tool supports specific files + if supportSpecificFiles[toolName] { + if files, exists := languageFilesMap[lowerLang]; exists { + for _, file := range files { + filesSet[file] = struct{}{} + } + } + } } - // Convert set to sorted slice + // Convert sets to sorted slices for ext := range extensionsSet { configTool.Extensions = append(configTool.Extensions, ext) } slices.Sort(configTool.Extensions) + for file := range filesSet { + configTool.Files = append(configTool.Files, file) + } + slices.Sort(configTool.Files) // Sort languages alphabetically slices.Sort(configTool.Languages) @@ -201,18 +228,6 @@ func CreateLanguagesConfigFile(apiTools []domain.Tool, toolsConfigDir string, to // buildRemoteModeLanguagesConfig builds the languages config for remote mode using repository languages as source of truth func buildRemoteModeLanguagesConfig(apiTools []domain.Tool, toolIDMap map[string]string, initFlags domain.InitFlags) ([]domain.ToolLanguageInfo, error) { - // Get language file extensions from API - languageTools, err := codacyclient.GetLanguageTools() - if err != nil { - return nil, fmt.Errorf("failed to get language tools from API: %w", err) - } - - // Create map of language name to file extensions - languageExtensionsMap := make(map[string][]string) - for _, langTool := range languageTools { - languageExtensionsMap[strings.ToLower(langTool.Name)] = langTool.FileExtensions - } - // Get repository languages - this is the single source of truth for remote mode repositoryLanguages, err := getRepositoryLanguages(initFlags) if err != nil { @@ -232,18 +247,36 @@ func buildRemoteModeLanguagesConfig(apiTools []domain.Tool, toolIDMap map[string Name: shortName, Languages: []string{}, Extensions: []string{}, + Files: []string{}, } // Use only languages that exist in the repository extensionsSet := make(map[string]struct{}) + filesSet := make(map[string]struct{}) for _, lang := range tool.Languages { lowerLang := strings.ToLower(lang) - if repoExts, exists := repositoryLanguages[lowerLang]; exists && len(repoExts) > 0 { - configTool.Languages = append(configTool.Languages, lang) - // Add repository-specific extensions - for _, ext := range repoExts { - extensionsSet[ext] = struct{}{} + if repoLang, exists := repositoryLanguages[lowerLang]; exists { + // Check if this language has either extensions or files + hasExtensions := len(repoLang.Extensions) > 0 + hasFiles := len(repoLang.Files) > 0 + + if hasExtensions || hasFiles { + configTool.Languages = append(configTool.Languages, lang) + + // Add repository-specific extensions if they exist + if hasExtensions { + for _, ext := range repoLang.Extensions { + extensionsSet[ext] = struct{}{} + } + } + + // Add repository-specific files if they exist and tool supports specific files + if hasFiles && supportSpecificFiles[shortName] { + for _, file := range repoLang.Files { + filesSet[file] = struct{}{} + } + } } } } @@ -254,6 +287,12 @@ func buildRemoteModeLanguagesConfig(apiTools []domain.Tool, toolIDMap map[string } slices.Sort(configTool.Extensions) + // Convert files set to sorted slice + for file := range filesSet { + configTool.Files = append(configTool.Files, file) + } + slices.Sort(configTool.Files) + // Sort languages alphabetically slices.Sort(configTool.Languages) @@ -264,14 +303,14 @@ func buildRemoteModeLanguagesConfig(apiTools []domain.Tool, toolIDMap map[string return configTools, nil } -func getRepositoryLanguages(initFlags domain.InitFlags) (map[string][]string, error) { +func getRepositoryLanguages(initFlags domain.InitFlags) (map[string]domain.Language, error) { response, err := codacyclient.GetRepositoryLanguages(initFlags) if err != nil { return nil, fmt.Errorf("failed to get repository languages: %w", err) } - // Create map to store language name -> combined extensions - result := make(map[string][]string) + // Create map to store language name -> Language struct + result := make(map[string]domain.Language) // Filter and process languages for _, lang := range response { @@ -285,17 +324,32 @@ func getRepositoryLanguages(initFlags domain.InitFlags) (map[string][]string, er extensions[ext] = struct{}{} } - // Convert map to slice + // Combine and deduplicate files + files := make(map[string]struct{}) + for _, file := range lang.DefaultFiles { + files[file] = struct{}{} + } + + // Convert extension map to slice extSlice := make([]string, 0, len(extensions)) for ext := range extensions { extSlice = append(extSlice, ext) } - - // Sort extensions for consistent ordering in the config file slices.Sort(extSlice) + // Convert files map to slice + fileSlice := make([]string, 0, len(files)) + for file := range files { + fileSlice = append(fileSlice, file) + } + slices.Sort(fileSlice) + // Add to result map with lowercase key for case-insensitive matching - result[strings.ToLower(lang.Name)] = extSlice + result[strings.ToLower(lang.Name)] = domain.Language{ + Name: lang.Name, + Extensions: extSlice, + Files: fileSlice, + } } }