From 001eb1e3de19c5c14e4d160e583798d16a1fe283 Mon Sep 17 00:00:00 2001 From: Oskar Grunning Date: Sat, 14 Oct 2023 07:49:56 +0200 Subject: [PATCH 1/2] Add end_col to results of eslint/eslintd (#409) --- lua/lint/linters/eslint.lua | 34 ++++++++++++++++++----- lua/lint/linters/eslint_d.lua | 34 ++++++++++++++++++----- tests/eslint_d_spec.lua | 51 ++++++++++++++++++++--------------- 3 files changed, 83 insertions(+), 36 deletions(-) diff --git a/lua/lint/linters/eslint.lua b/lua/lint/linters/eslint.lua index 9cb328e2..4c796dcf 100644 --- a/lua/lint/linters/eslint.lua +++ b/lua/lint/linters/eslint.lua @@ -1,9 +1,7 @@ -local pattern = [[%s*(%d+):(%d+)%s+(%w+)%s+(.+%S)%s+(%S+)]] -local groups = { 'lnum', 'col', 'severity', 'message', 'code' } -local severity_map = { - ['error'] = vim.diagnostic.severity.ERROR, - ['warn'] = vim.diagnostic.severity.WARN, - ['warning'] = vim.diagnostic.severity.WARN, +local severities = { + nil, + vim.diagnostic.severity.ERROR, + vim.diagnostic.severity.WARN, } return require('lint.util').inject_cmd_exe({ @@ -16,6 +14,8 @@ return require('lint.util').inject_cmd_exe({ return 'eslint' end, args = { + '--format', + 'json', '--stdin', '--stdin-filename', function() return vim.api.nvim_buf_get_name(0) end, @@ -23,5 +23,25 @@ return require('lint.util').inject_cmd_exe({ stdin = true, stream = 'stdout', ignore_exitcode = true, - parser = require('lint.parser').from_pattern(pattern, groups, severity_map, { ['source'] = 'eslint' }), + parser = function(output) + local success, decodedData = pcall(vim.json.decode, output) + local diagnostics = {} + + if success and decodedData ~= nil then + for _, diagnostic in ipairs(decodedData.messages or {}) do + table.insert(diagnostics, { + source = "eslint", + lnum = diagnostic.line - 1, + col = diagnostic.column - 1, + end_lnum = diagnostic.endLine - 1, + end_col = diagnostic.endColumn - 1, + severity = severities[diagnostic.severity], + message = diagnostic.message, + code = diagnostic.ruleId + }) + end + end + + return diagnostics + end }) diff --git a/lua/lint/linters/eslint_d.lua b/lua/lint/linters/eslint_d.lua index 378f956a..18724199 100644 --- a/lua/lint/linters/eslint_d.lua +++ b/lua/lint/linters/eslint_d.lua @@ -1,9 +1,7 @@ -local pattern = [[%s*(%d+):(%d+)%s+(%w+)%s+(.+%S)%s+(%S+)]] -local groups = { 'lnum', 'col', 'severity', 'message', 'code' } -local severity_map = { - ['error'] = vim.diagnostic.severity.ERROR, - ['warn'] = vim.diagnostic.severity.WARN, - ['warning'] = vim.diagnostic.severity.WARN, +local severities = { + nil, + vim.diagnostic.severity.ERROR, + vim.diagnostic.severity.WARN, } return require('lint.util').inject_cmd_exe({ @@ -16,6 +14,8 @@ return require('lint.util').inject_cmd_exe({ return 'eslint_d' end, args = { + '--format', + 'json', '--stdin', '--stdin-filename', function() return vim.api.nvim_buf_get_name(0) end, @@ -23,5 +23,25 @@ return require('lint.util').inject_cmd_exe({ stdin = true, stream = 'stdout', ignore_exitcode = true, - parser = require('lint.parser').from_pattern(pattern, groups, severity_map, { ['source'] = 'eslint_d' }), + parser = function(output) + local success, decodedData = pcall(vim.json.decode, output) + local diagnostics = {} + + if success and decodedData ~= nil then + for _, diagnostic in ipairs(decodedData.messages or {}) do + table.insert(diagnostics, { + source = "eslint_d", + lnum = diagnostic.line - 1, + col = diagnostic.column - 1, + end_lnum = diagnostic.endLine - 1, + end_col = diagnostic.endColumn - 1, + severity = severities[diagnostic.severity], + message = diagnostic.message, + code = diagnostic.ruleId + }) + end + end + + return diagnostics + end }) diff --git a/tests/eslint_d_spec.lua b/tests/eslint_d_spec.lua index d9bf668f..a1f6cabb 100644 --- a/tests/eslint_d_spec.lua +++ b/tests/eslint_d_spec.lua @@ -7,48 +7,55 @@ describe("linter.eslint_d", function() it("can parse output", function() local parser = require("lint.linters.eslint_d").parser - local result = parser( - [[ -/directory/file.js - 1:10 error 'testFunc' is defined but never used no-unused-vars - 4:16 error This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain no-dupe-else-if -✖ 2 problems (2 errors, 0 warnings) - ]], - vim.api.nvim_get_current_buf() - ) + local result = parser([[ +{ + "messages": [ + { + "column": 10, + "endColumn": 18, + "endLine": 1, + "line": 1, + "message": "'testFunc' is defined but never used", + "ruleId": "no-unused-vars", + "severity": 2 + }, + { + "column": 16, + "endColumn": 22, + "endLine": 4, + "line": 4, + "message": "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain", + "ruleId": "no-dupe-else-if", + "severity": 2 + } + ] +}]]) + assert.are.same(2, #result) + local expected_1 = { code = "no-unused-vars", col = 9, - end_col = 9, + end_col = 17, end_lnum = 0, lnum = 0, message = "'testFunc' is defined but never used", severity = 1, source = "eslint_d", - user_data = { - lsp = { - code = "no-unused-vars", - }, - }, } assert.are.same(expected_1, result[1]) local expected_2 = { code = "no-dupe-else-if", col = 15, - end_col = 15, + end_col = 21, end_lnum = 3, lnum = 3, - message = "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain", + message = + "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain", severity = 1, source = "eslint_d", - user_data = { - lsp = { - code = "no-dupe-else-if", - }, - }, } assert.are.same(expected_2, result[2]) end) From 6d596b87862909370f4d1e6535cc9fad80c89fe6 Mon Sep 17 00:00:00 2001 From: Peter Benjamin Date: Fri, 13 Oct 2023 22:57:30 -0700 Subject: [PATCH 2/2] Add trivy (#406) --- README.md | 2 + lua/lint/linters/trivy.lua | 40 +++++++++++++++ tests/trivy_spec.lua | 101 +++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 lua/lint/linters/trivy.lua create mode 100644 tests/trivy_spec.lua diff --git a/README.md b/README.md index 726de6ff..e92db3cd 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ Other dedicated linters that are built-in are: | [vulture][vulture] | `vulture` | | [yamllint][yamllint] | `yamllint` | | [tfsec][tfsec] | `tfsec` | +| [trivy][trivy] | `trivy` | ## Custom Linters @@ -388,6 +389,7 @@ busted tests/ [buf_lint]: https://github.com/bufbuild/buf [erb-lint]: https://github.com/shopify/erb-lint [tfsec]: https://github.com/aquasecurity/tfsec +[trivy]: https://github.com/aquasecurity/trivy [djlint]: https://djlint.com/ [buildifier]: https://github.com/bazelbuild/buildtools/tree/master/buildifier [solhint]: https://protofire.github.io/solhint/ diff --git a/lua/lint/linters/trivy.lua b/lua/lint/linters/trivy.lua new file mode 100644 index 00000000..57be886c --- /dev/null +++ b/lua/lint/linters/trivy.lua @@ -0,0 +1,40 @@ +local severity_map = { + ["LOW"] = vim.diagnostic.severity.INFO, + ["MEDIUM"] = vim.diagnostic.severity.WARN, + ["HIGH"] = vim.diagnostic.severity.ERROR, +} + +return { + cmd = "trivy", + stdin = false, + append_fname = true, + args = { "--scanners", "config", "--format", "json", "fs" }, + stream = "stdout", + ignore_exitcode = false, + parser = function(output, bufnr) + local diagnostics = {} + local ok, decoded = pcall(vim.json.decode, output) + if not ok then + return diagnostics + end + local fpath = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(bufnr), ":t") + for _, result in ipairs(decoded and decoded.Results or {}) do + if result.Target == fpath then + for _, misconfig in ipairs(result.Misconfigurations) do + local err = { + source = "trivy", + message = string.format("%s %s", misconfig.Title, misconfig.Description), + col = misconfig.CauseMetadata.StartLine, + end_col = misconfig.CauseMetadata.EndLine, + lnum = misconfig.CauseMetadata.StartLine - 1, + end_lnum = misconfig.CauseMetadata.EndLine - 1, + code = misconfig.ID, + severity = severity_map[misconfig.Severity], + } + table.insert(diagnostics, err) + end + end + end + return diagnostics + end, +} diff --git a/tests/trivy_spec.lua b/tests/trivy_spec.lua new file mode 100644 index 00000000..8b440bd6 --- /dev/null +++ b/tests/trivy_spec.lua @@ -0,0 +1,101 @@ +describe("linter.trivy", function() + it("Parses output sample", function() + local parser = require("lint.linters.trivy").parser + local bufnr = vim.uri_to_bufnr("file:///main.tf") + local output = [[ +{ + "SchemaVersion": 2, + "ArtifactName": "main.tf", + "ArtifactType": "filesystem", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + }, + "Results": [ + { + "Target": ".", + "Class": "config", + "Type": "terraform", + "MisconfSummary": { + "Successes": 1, + "Failures": 0, + "Exceptions": 0 + } + }, + { + "Target": "main.tf", + "Class": "config", + "Type": "terraform", + "MisconfSummary": { + "Successes": 0, + "Failures": 1, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "Terraform Security Check", + "ID": "AVD-AWS-0065", + "AVDID": "AVD-AWS-0065", + "Title": "A KMS key is not configured to auto-rotate.", + "Description": "You should configure your KMS keys to auto rotate to maintain security and defend against compromise.", + "Message": "Key does not have rotation enabled.", + "Query": "data..", + "Resolution": "Configure KMS key to auto rotate", + "Severity": "MEDIUM", + "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0065", + "References": [ + "https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html", + "https://avd.aquasec.com/misconfig/avd-aws-0065" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Resource": "aws_kms_key.foo", + "Provider": "AWS", + "Service": "kms", + "StartLine": 15, + "EndLine": 15, + "Code": { + "Lines": [ + { + "Number": 15, + "Content": "resource \"aws_kms_key\" \"foo\" {}", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": true + } + ] + } + } + } + ] + } + ] +} + ]] + local result = parser(output, bufnr) + local expected = { + { + source = "trivy", + message = "A KMS key is not configured to auto-rotate. You should configure your KMS keys to auto rotate to maintain security and defend against compromise.", + lnum = 14, + end_lnum = 14, + col = 15, + end_col = 15, + severity = vim.diagnostic.severity.WARN, + code = "AVD-AWS-0065", + }, + } + assert.are.same(expected, result) + end) +end)