diff --git a/lua/null-ls/builtins/diagnostics/gitleaks.lua b/lua/null-ls/builtins/diagnostics/gitleaks.lua new file mode 100644 index 00000000..d4e4b151 --- /dev/null +++ b/lua/null-ls/builtins/diagnostics/gitleaks.lua @@ -0,0 +1,62 @@ +local h = require("null-ls.helpers") +local methods = require("null-ls.methods") + +local DIAGNOSTICS = methods.internal.DIAGNOSTICS + +local handle_gitleaks_output = function(params) + local parser = h.diagnostics.from_json({ + attributes = { + code = "code", + }, + diagnostic = { + source = "gitleaks", + }, + }) + + local offenses = {} + for _, finding in ipairs(params.output or {}) do + table.insert(offenses, { + message = finding.Description, + ruleId = finding.RuleID, + code = finding.RuleID, + line = finding.StartLine, + column = finding.StartColumn, + endLine = finding.EndLine, + endColumn = finding.EndColumn, + }) + end + + return parser({ output = offenses }) +end + +return h.make_builtin({ + name = "gitleaks", + meta = { + url = "https://github.com/gitleaks/gitleaks", + description = "Gitleaks is a SAST tool for detecting and preventing hardcoded secrets like passwords, API keys, and tokens in git repos.", + }, + method = DIAGNOSTICS, + filetypes = {}, + generator_opts = { + command = "gitleaks", + args = { + "stdin", + "--report-format", + "json", + "--report-path", + "-", + "--exit-code", + "0", + "--no-banner", + }, + format = "json", + to_stdin = true, + from_stderr = true, + ignore_stderr = true, + check_exit_code = function(code) + return code == 0 + end, + on_output = handle_gitleaks_output, + }, + factory = h.generator_factory, +}) diff --git a/test/spec/builtins/diagnostics/gitleaks_spec.lua b/test/spec/builtins/diagnostics/gitleaks_spec.lua new file mode 100644 index 00000000..bc9abc6f --- /dev/null +++ b/test/spec/builtins/diagnostics/gitleaks_spec.lua @@ -0,0 +1,107 @@ +local diagnostics = require("null-ls.builtins").diagnostics + +describe("diagnostics gitleaks", function() + local parser = diagnostics.gitleaks._opts.on_output + + it("should create a diagnostic from gitleaks output", function() + local output = vim.json.decode([[ + [ + { + "RuleID": "generic-api-key", + "Description": "Detected a Generic API Key, potentially exposing access to various services and sensitive operations.", + "StartLine": 192, + "EndLine": 192, + "StartColumn": 8, + "EndColumn": 67, + "Match": "ocp-apim-subscription-key: 5ccb5b137e7444d885be752eda7f767a'", + "Secret": "5ccb5b137e7444d885be752eda7f767a", + "File": "zsh/zsh.d/functions.zsh", + "SymlinkFile": "", + "Commit": "", + "Entropy": 3.5695488, + "Author": "", + "Email": "", + "Date": "", + "Message": "", + "Tags": [], + "Fingerprint": "zsh/zsh.d/functions.zsh:generic-api-key:192" + } + ] + ]]) + local diagnostic = parser({ output = output }) + assert.same({ + { + row = 192, + col = 8, + end_row = 192, + end_col = 67, + message = "Detected a Generic API Key, potentially exposing access to various services and sensitive operations.", + source = "gitleaks", + code = "generic-api-key", + }, + }, diagnostic) + end) + + it("should handle multiple findings", function() + local output = vim.json.decode([[ + [ + { + "RuleID": "generic-api-key", + "Description": "Detected a Generic API Key, potentially exposing access to various services and sensitive operations.", + "StartLine": 10, + "EndLine": 10, + "StartColumn": 5, + "EndColumn": 30, + "Match": "api_key = 'abc123'", + "Secret": "abc123", + "File": "config.py", + "Fingerprint": "config.py:generic-api-key:10" + }, + { + "RuleID": "aws-access-token", + "Description": "Detected AWS Access Token, risking unauthorized cloud resource access and data breaches.", + "StartLine": 25, + "EndLine": 25, + "StartColumn": 12, + "EndColumn": 50, + "Match": "AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI", + "Secret": "wJalrXUtnFEMI", + "File": "env.sh", + "Fingerprint": "env.sh:aws-access-token:25" + } + ] + ]]) + local diagnostic = parser({ output = output }) + assert.same({ + { + row = 10, + col = 5, + end_row = 10, + end_col = 30, + message = "Detected a Generic API Key, potentially exposing access to various services and sensitive operations.", + source = "gitleaks", + code = "generic-api-key", + }, + { + row = 25, + col = 12, + end_row = 25, + end_col = 50, + message = "Detected AWS Access Token, risking unauthorized cloud resource access and data breaches.", + source = "gitleaks", + code = "aws-access-token", + }, + }, diagnostic) + end) + + it("should handle empty output", function() + local output = vim.json.decode("[]") + local diagnostic = parser({ output = output }) + assert.same({}, diagnostic) + end) + + it("should handle nil output", function() + local diagnostic = parser({ output = nil }) + assert.same({}, diagnostic) + end) +end)