From 50b2697abcf2c197eaea5496ac39c5f017d38550 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Tue, 17 Dec 2024 22:19:38 +0800 Subject: [PATCH 1/4] feat: support sarif formatter --- luacheck-dev-1.rockspec | 3 +- src/luacheck/format.lua | 75 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/luacheck-dev-1.rockspec b/luacheck-dev-1.rockspec index d95a5d4d..13379528 100644 --- a/luacheck-dev-1.rockspec +++ b/luacheck-dev-1.rockspec @@ -28,7 +28,8 @@ description = { dependencies = { "lua >= 5.1", "argparse >= 0.6.0", - "luafilesystem >= 1.6.3" + "luafilesystem >= 1.6.3", + "dkjson >= 2.1.0" } test_dependencies = { diff --git a/src/luacheck/format.lua b/src/luacheck/format.lua index 18f95c83..3cdfa150 100644 --- a/src/luacheck/format.lua +++ b/src/luacheck/format.lua @@ -1,5 +1,6 @@ +local json = require "dkjson" local stages = require "luacheck.stages" -local utils = require "luacheck.utils" +local utils = require "luacheck.utils" local format = {} @@ -317,6 +318,78 @@ function format.builtin_formatters.plain(report, file_names, opts) return table.concat(buf, "\n") end +function format.builtin_formatters.Sarif(report, file_names, opts) + opts.color = false + local get_level = function(event) + return event.code:sub(1, 1) == "0" and "error" or "warning" + end + + -- SARIF support for code scanning: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning + local sarif_output = { + ["$schema"] = "https://json.schemastore.org/sarif-2.1.0.json", + version = "2.1.0", + runs = { + { + tool = { + driver = { + informationUri = "https://github.com/lunarmodules/luacheck", + name = "luacheck", + version = "1.0.0" + } + }, + results = {} + } + } + } + + for file_i, file_report in ipairs(report) do + if file_report.fatal then + table.insert(sarif_output.runs[1].results, { + level = "error", + ruleId = fatal_type(file_report), + message = { + text = ("luacheck : fatal error %s: couldn't check %s: %s"):format( + fatal_error_codes[file_report.fatal], file_names[file_i], file_report.msg) + }, + locations = { + { + physicalLocation = { + artifactLocation = { + uri = file_names[file_i] + } + } + } + } + }) + else + for _, event in ipairs(file_report) do + table.insert(sarif_output.runs[1].results, { + level = get_level(event), + ruleId = event_code(event), + message = { + text = format_event(file_names[file_i], event, opts) + }, + locations = { + { + physicalLocation = { + artifactLocation = { + uri = file_names[file_i] + }, + region = { + startLine = event.line, + startColumn = event.column + } + } + } + } + }) + end + end + end + + return json.encode(sarif_output) +end + --- Formats a report. -- Recognized options: -- `options.formatter`: name of used formatter. Default: "default". From 525ffc8378bea360934e41c589fdc3d1ccb33f9b Mon Sep 17 00:00:00 2001 From: "yeshan.ye" Date: Wed, 18 Dec 2024 20:00:58 +0800 Subject: [PATCH 2/4] chore: sarif test & docs --- .github/workflows/busted.yml | 1 + README.md | 2 +- docsrc/cli.rst | 1 + spec/cli_spec.lua | 9 +++++++++ src/luacheck/main.lua | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/busted.yml b/.github/workflows/busted.yml index 6b4dd747..64331fc7 100644 --- a/.github/workflows/busted.yml +++ b/.github/workflows/busted.yml @@ -28,6 +28,7 @@ jobs: luarocks install busted luarocks install lanes # required for parallel execution luarocks install luautf8 # required for decoder unit test + luarocks install dkjson # required for decoder unit test luarocks install luasocket # required for profiler unit test luarocks install luacov-coveralls diff --git a/README.md b/README.md index 2244d9e1..710cf511 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ Use the Luacheck issue tracker on GitHub to submit bugs, suggestions and questio ## Building and testing -After the Luacheck repo is cloned and changes are made, run `luarocks make` (using `sudo` if necessary) from its root directory to install dev version of Luacheck. To run Luacheck using sources in current directory without installing it, run `lua -e 'package.path="./src/?.lua;./src/?/init.lua;"..package.path' bin/luacheck.lua ...`. To test Luacheck, ensure that you have [busted](http://olivinelabs.com/busted/), [luautf8](https://github.com/starwing/luautf8), and [luasocket](https://github.com/lunarmodules/luasocket) installed and run `busted`. +After the Luacheck repo is cloned and changes are made, run `luarocks make` (using `sudo` if necessary) from its root directory to install dev version of Luacheck. To run Luacheck using sources in current directory without installing it, run `lua -e 'package.path="./src/?.lua;./src/?/init.lua;"..package.path' bin/luacheck.lua ...`. To test Luacheck, ensure that you have [busted](http://olivinelabs.com/busted/), [luautf8](https://github.com/starwing/luautf8), [dkjson](https://luarocks.org/modules/dhkolf/dkjson) and [luasocket](https://github.com/lunarmodules/luasocket) installed and run `busted`. ## Docker diff --git a/docsrc/cli.rst b/docsrc/cli.rst index be222004..e85fd8b4 100644 --- a/docsrc/cli.rst +++ b/docsrc/cli.rst @@ -142,6 +142,7 @@ Option Meaning * ``TAP`` - Test Anything Protocol formatter; * ``JUnit`` - JUnit XML formatter; + * ``Sarif`` - Static Analysis Results Interchange Format formatter; * ``visual_studio`` - MSBuild/Visual Studio aware formatter; * ``plain`` - simple warning-per-line formatter; * ``default`` - standard formatter. diff --git a/spec/cli_spec.lua b/spec/cli_spec.lua index a099ba61..aafbe370 100644 --- a/spec/cli_spec.lua +++ b/spec/cli_spec.lua @@ -1,3 +1,4 @@ +local json = require "dkjson" local utils = require "luacheck.utils" local multithreading = require "luacheck.multithreading" local helper = require "spec.helper" @@ -979,6 +980,14 @@ not ok 8 spec/samples/python_code.lua:1:6: (E011) expected '=' near '__future__' ]], get_output "bad_file spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter JUnit --no-config") end) + it("has built-in Sarif formatter", function() + local expect = [[ +{"runs":[{"tool":{"driver":{"version":"1.0.0","name":"luacheck","informationUri":"https://github.com/lunarmodules/luacheck"}},"results":[{"locations":[{"physicalLocation":{"artifactLocation":{"uri":"bad_file"}}}],"ruleId":"I/O error","level":"error","message":{"text":"luacheck : fatal error F1: couldn't check bad_file: couldn't read: No such file or directory"}},{"locations":[{"physicalLocation":{"region":{"startLine":3,"startColumn":16},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W211","level":"warning","message":{"text":"spec/samples/bad_code.lua:3:16: unused function 'helper'"}},{"locations":[{"physicalLocation":{"region":{"startLine":3,"startColumn":23},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W212","level":"warning","message":{"text":"spec/samples/bad_code.lua:3:23: unused variable length argument"}},{"locations":[{"physicalLocation":{"region":{"startLine":7,"startColumn":10},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W111","level":"warning","message":{"text":"spec/samples/bad_code.lua:7:10: setting non-standard global variable 'embrace'"}},{"locations":[{"physicalLocation":{"region":{"startLine":8,"startColumn":10},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W412","level":"warning","message":{"text":"spec/samples/bad_code.lua:8:10: variable 'opt' was previously defined as an argument on line 7"}},{"locations":[{"physicalLocation":{"region":{"startLine":9,"startColumn":11},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W113","level":"warning","message":{"text":"spec/samples/bad_code.lua:9:11: accessing undefined variable 'hepler'"}},{"locations":[{"physicalLocation":{"region":{"startLine":1,"startColumn":6},"artifactLocation":{"uri":"spec/samples/python_code.lua"}}}],"ruleId":"E011","level":"error","message":{"text":"spec/samples/python_code.lua:1:6: expected '=' near '__future__'"}}]}],"version":"2.1.0","$schema":"https://json.schemastore.org/sarif-2.1.0.json"} +]] + local actual = get_output "bad_file spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter Sarif --no-config" + assert.same(json.decode(expect), json.decode(actual)) + end) + it("has built-in Visual Studio aware formatter", function() assert.equal([[ luacheck : fatal error F1: couldn't check bad_file: couldn't read: No such file or directory diff --git a/src/luacheck/main.lua b/src/luacheck/main.lua index cb8646af..a217a67b 100644 --- a/src/luacheck/main.lua +++ b/src/luacheck/main.lua @@ -244,6 +244,7 @@ Links: parser:option("--formatter" , "Use custom formatter. must be a module name or one of:\n" .. " TAP - Test Anything Protocol formatter;\n" .. " JUnit - JUnit XML formatter;\n" .. + " Sarif - Static Analysis Results Interchange Format formatter;\n" .. " visual_studio - MSBuild/Visual Studio aware formatter;\n" .. " plain - simple warning-per-line formatter;\n" .. " default - standard formatter."), From 8c5cef18a8dd4b93086d9b30f45180a69cb1b26d Mon Sep 17 00:00:00 2001 From: "yeshan.ye" Date: Wed, 18 Dec 2024 20:06:46 +0800 Subject: [PATCH 3/4] fix: luacheck --- spec/cli_spec.lua | 1 + src/luacheck/format.lua | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/cli_spec.lua b/spec/cli_spec.lua index aafbe370..31895d8e 100644 --- a/spec/cli_spec.lua +++ b/spec/cli_spec.lua @@ -981,6 +981,7 @@ not ok 8 spec/samples/python_code.lua:1:6: (E011) expected '=' near '__future__' end) it("has built-in Sarif formatter", function() + -- luacheck: ignore 631 local expect = [[ {"runs":[{"tool":{"driver":{"version":"1.0.0","name":"luacheck","informationUri":"https://github.com/lunarmodules/luacheck"}},"results":[{"locations":[{"physicalLocation":{"artifactLocation":{"uri":"bad_file"}}}],"ruleId":"I/O error","level":"error","message":{"text":"luacheck : fatal error F1: couldn't check bad_file: couldn't read: No such file or directory"}},{"locations":[{"physicalLocation":{"region":{"startLine":3,"startColumn":16},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W211","level":"warning","message":{"text":"spec/samples/bad_code.lua:3:16: unused function 'helper'"}},{"locations":[{"physicalLocation":{"region":{"startLine":3,"startColumn":23},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W212","level":"warning","message":{"text":"spec/samples/bad_code.lua:3:23: unused variable length argument"}},{"locations":[{"physicalLocation":{"region":{"startLine":7,"startColumn":10},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W111","level":"warning","message":{"text":"spec/samples/bad_code.lua:7:10: setting non-standard global variable 'embrace'"}},{"locations":[{"physicalLocation":{"region":{"startLine":8,"startColumn":10},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W412","level":"warning","message":{"text":"spec/samples/bad_code.lua:8:10: variable 'opt' was previously defined as an argument on line 7"}},{"locations":[{"physicalLocation":{"region":{"startLine":9,"startColumn":11},"artifactLocation":{"uri":"spec/samples/bad_code.lua"}}}],"ruleId":"W113","level":"warning","message":{"text":"spec/samples/bad_code.lua:9:11: accessing undefined variable 'hepler'"}},{"locations":[{"physicalLocation":{"region":{"startLine":1,"startColumn":6},"artifactLocation":{"uri":"spec/samples/python_code.lua"}}}],"ruleId":"E011","level":"error","message":{"text":"spec/samples/python_code.lua:1:6: expected '=' near '__future__'"}}]}],"version":"2.1.0","$schema":"https://json.schemastore.org/sarif-2.1.0.json"} ]] diff --git a/src/luacheck/format.lua b/src/luacheck/format.lua index 3cdfa150..a97f7d51 100644 --- a/src/luacheck/format.lua +++ b/src/luacheck/format.lua @@ -318,13 +318,14 @@ function format.builtin_formatters.plain(report, file_names, opts) return table.concat(buf, "\n") end +-- https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning function format.builtin_formatters.Sarif(report, file_names, opts) opts.color = false local get_level = function(event) return event.code:sub(1, 1) == "0" and "error" or "warning" end - -- SARIF support for code scanning: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning + -- SARIF support for GitHub code scanning local sarif_output = { ["$schema"] = "https://json.schemastore.org/sarif-2.1.0.json", version = "2.1.0", From 52f908e53fd9940207ccaf327e83d7f192622b77 Mon Sep 17 00:00:00 2001 From: "yeshan.ye" Date: Thu, 19 Dec 2024 19:04:48 +0800 Subject: [PATCH 4/4] fix: code style --- .github/workflows/busted.yml | 2 +- src/luacheck/format.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/busted.yml b/.github/workflows/busted.yml index 64331fc7..960757ab 100644 --- a/.github/workflows/busted.yml +++ b/.github/workflows/busted.yml @@ -28,7 +28,7 @@ jobs: luarocks install busted luarocks install lanes # required for parallel execution luarocks install luautf8 # required for decoder unit test - luarocks install dkjson # required for decoder unit test + luarocks install dkjson # required for decoder unit test luarocks install luasocket # required for profiler unit test luarocks install luacov-coveralls diff --git a/src/luacheck/format.lua b/src/luacheck/format.lua index a97f7d51..7a587b22 100644 --- a/src/luacheck/format.lua +++ b/src/luacheck/format.lua @@ -1,6 +1,6 @@ local json = require "dkjson" local stages = require "luacheck.stages" -local utils = require "luacheck.utils" +local utils = require "luacheck.utils" local format = {}