mirror of
https://github.com/koalaman/shellcheck
synced 2025-08-19 13:00:54 -07:00
Support for SARIF output format
This commit is contained in:
parent
4806719035
commit
f6cd40d4f4
6 changed files with 150 additions and 2 deletions
|
@ -7,6 +7,7 @@
|
|||
- SC2321: Suggest removing $((..)) in array[$((idx))]=val
|
||||
- SC2322: Suggest collapsing double parentheses in arithmetic contexts
|
||||
- SC2323: Suggest removing wrapping parentheses in a[(x+1)]=val
|
||||
- support Static Analysis Results Interchange Format (SARIF) output
|
||||
|
||||
### Fixed
|
||||
- SC2086: Now uses DFA to make more accurate predictions about values
|
||||
|
|
|
@ -83,6 +83,7 @@ library
|
|||
ShellCheck.Formatter.GCC
|
||||
ShellCheck.Formatter.JSON
|
||||
ShellCheck.Formatter.JSON1
|
||||
ShellCheck.Formatter.SARIF
|
||||
ShellCheck.Formatter.TTY
|
||||
ShellCheck.Formatter.Quiet
|
||||
ShellCheck.Interface
|
||||
|
|
|
@ -197,6 +197,10 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts.
|
|||
: This is a legacy version of the **json1** format. It's a raw array of
|
||||
comments, and all offsets have a tab stop of 8.
|
||||
|
||||
**sarif**
|
||||
|
||||
: Static Analysis Results Interchange Format (SARIF) output.
|
||||
|
||||
**quiet**
|
||||
|
||||
: Suppress all normal output. Exit with zero if no issues are found,
|
||||
|
|
|
@ -29,6 +29,7 @@ import qualified ShellCheck.Formatter.Diff
|
|||
import qualified ShellCheck.Formatter.GCC
|
||||
import qualified ShellCheck.Formatter.JSON
|
||||
import qualified ShellCheck.Formatter.JSON1
|
||||
import qualified ShellCheck.Formatter.SARIF
|
||||
import qualified ShellCheck.Formatter.TTY
|
||||
import qualified ShellCheck.Formatter.Quiet
|
||||
|
||||
|
@ -146,6 +147,7 @@ formats options = Map.fromList [
|
|||
("gcc", ShellCheck.Formatter.GCC.format),
|
||||
("json", ShellCheck.Formatter.JSON.format),
|
||||
("json1", ShellCheck.Formatter.JSON1.format),
|
||||
("sarif", ShellCheck.Formatter.SARIF.format),
|
||||
("tty", ShellCheck.Formatter.TTY.format options),
|
||||
("quiet", ShellCheck.Formatter.Quiet.format options)
|
||||
]
|
||||
|
@ -576,7 +578,7 @@ printVersion = do
|
|||
putStrLn "ShellCheck - shell script analysis tool"
|
||||
putStrLn $ "version: " ++ shellcheckVersion
|
||||
putStrLn "license: GNU General Public License, version 3"
|
||||
putStrLn "website: https://www.shellcheck.net"
|
||||
putStrLn $ "website: " ++ shellcheckWebsite
|
||||
|
||||
printOptional = do
|
||||
mapM f list
|
||||
|
|
|
@ -22,7 +22,7 @@ Use:
|
|||
|
||||
import Paths_ShellCheck (version)
|
||||
shellcheckVersion = showVersion version -- VERSIONSTRING
|
||||
|
||||
shellcheckWebsite = "https://www.shellcheck.net"
|
||||
|
||||
internalVariables = [
|
||||
-- Generic
|
||||
|
|
140
src/ShellCheck/Formatter/SARIF.hs
Normal file
140
src/ShellCheck/Formatter/SARIF.hs
Normal file
|
@ -0,0 +1,140 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-
|
||||
Copyright 2012-2019 Vidar Holen
|
||||
|
||||
This file is part of ShellCheck.
|
||||
https://www.shellcheck.net
|
||||
|
||||
ShellCheck is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ShellCheck is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-}
|
||||
module ShellCheck.Formatter.SARIF (format) where
|
||||
|
||||
import ShellCheck.Data
|
||||
import ShellCheck.Interface
|
||||
import ShellCheck.Formatter.Format
|
||||
|
||||
import Control.DeepSeq
|
||||
import Data.Aeson
|
||||
import Data.IORef
|
||||
import Data.Monoid
|
||||
import GHC.Exts
|
||||
import System.IO
|
||||
import qualified Data.ByteString.Lazy.Char8 as BL
|
||||
|
||||
format :: IO Formatter
|
||||
format = do
|
||||
ref <- newIORef []
|
||||
return Formatter {
|
||||
header = return (),
|
||||
onResult = collectResult ref,
|
||||
onFailure = outputError,
|
||||
footer = finish ref
|
||||
}
|
||||
|
||||
data SarifOutput = SarifOutput {
|
||||
comments :: [PositionedComment]
|
||||
}
|
||||
|
||||
instance ToJSON SarifOutput where
|
||||
toJSON result = object [
|
||||
"version" .= ("2.1.0" :: String),
|
||||
"$schema" .= ("http://json.schemastore.org/sarif-2.1.0" :: String),
|
||||
"runs" .= [object [
|
||||
"tool" .= object [
|
||||
"driver" .= object [
|
||||
"name" .= ("ShellCheck" :: String),
|
||||
"semanticVersion" .= shellcheckVersion,
|
||||
"informationUri" .= shellcheckWebsite
|
||||
]
|
||||
],
|
||||
"results" .= comments result
|
||||
]]
|
||||
]
|
||||
|
||||
instance ToJSON Replacement where
|
||||
toJSON replacement =
|
||||
let start = repStartPos replacement
|
||||
end = repEndPos replacement in
|
||||
object [
|
||||
"artifactLocation" .= object [
|
||||
"uri" .= posFile start
|
||||
],
|
||||
"replacements" .= [object [
|
||||
"deletedRegion" .= object [
|
||||
"startLine" .= posLine start,
|
||||
"startColumn" .= posColumn start,
|
||||
"endLine" .= posLine end,
|
||||
"endColumn" .= posColumn end
|
||||
],
|
||||
"insertedContent" .= object [
|
||||
"text" .= repString replacement
|
||||
]
|
||||
]]
|
||||
]
|
||||
|
||||
instance ToJSON PositionedComment where
|
||||
toJSON comment =
|
||||
let start = pcStartPos comment
|
||||
end = pcEndPos comment
|
||||
c = pcComment comment
|
||||
replacements = maybe [] fixReplacements (pcFix comment) in
|
||||
object [
|
||||
"ruleId" .= code (cCode c),
|
||||
"level" .= level (cSeverity c),
|
||||
"message" .= object [
|
||||
"text" .= cMessage c
|
||||
],
|
||||
"locations" .= [object [
|
||||
"physicalLocation" .= object [
|
||||
"artifactLocation" .= object [
|
||||
"uri" .= posFile start
|
||||
],
|
||||
"region" .= object [
|
||||
"startLine" .= posLine start,
|
||||
"startColumn" .= posColumn start,
|
||||
"endLine" .= posLine end,
|
||||
"endColumn" .= posColumn end
|
||||
]
|
||||
]
|
||||
]],
|
||||
"fixes" .= [object [
|
||||
"artifactChanges" .= replacements
|
||||
] | not (null replacements)]
|
||||
]
|
||||
|
||||
code num = "SC" ++ show num
|
||||
|
||||
level severity = case severity of
|
||||
ErrorC -> "error" :: String
|
||||
WarningC -> "warning"
|
||||
InfoC -> "note"
|
||||
StyleC -> "none"
|
||||
|
||||
outputError file msg = hPutStrLn stderr $ file ++ ": " ++ msg
|
||||
|
||||
collectResult ref cr sys = mapM_ f groups
|
||||
where
|
||||
comments = crComments cr
|
||||
groups = groupWith sourceFile comments
|
||||
f :: [PositionedComment] -> IO ()
|
||||
f group = do
|
||||
let filename = sourceFile (head group)
|
||||
result <- siReadFile sys (Just True) filename
|
||||
let contents = either (const "") id result
|
||||
let comments' = makeNonVirtual comments contents
|
||||
deepseq comments' $ modifyIORef ref (\x -> comments' ++ x)
|
||||
|
||||
finish ref = do
|
||||
list <- readIORef ref
|
||||
BL.putStrLn $ encode $ SarifOutput { comments = list }
|
Loading…
Add table
Add a link
Reference in a new issue