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
|
- SC2321: Suggest removing $((..)) in array[$((idx))]=val
|
||||||
- SC2322: Suggest collapsing double parentheses in arithmetic contexts
|
- SC2322: Suggest collapsing double parentheses in arithmetic contexts
|
||||||
- SC2323: Suggest removing wrapping parentheses in a[(x+1)]=val
|
- SC2323: Suggest removing wrapping parentheses in a[(x+1)]=val
|
||||||
|
- support Static Analysis Results Interchange Format (SARIF) output
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- SC2086: Now uses DFA to make more accurate predictions about values
|
- SC2086: Now uses DFA to make more accurate predictions about values
|
||||||
|
|
|
@ -83,6 +83,7 @@ library
|
||||||
ShellCheck.Formatter.GCC
|
ShellCheck.Formatter.GCC
|
||||||
ShellCheck.Formatter.JSON
|
ShellCheck.Formatter.JSON
|
||||||
ShellCheck.Formatter.JSON1
|
ShellCheck.Formatter.JSON1
|
||||||
|
ShellCheck.Formatter.SARIF
|
||||||
ShellCheck.Formatter.TTY
|
ShellCheck.Formatter.TTY
|
||||||
ShellCheck.Formatter.Quiet
|
ShellCheck.Formatter.Quiet
|
||||||
ShellCheck.Interface
|
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
|
: 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.
|
comments, and all offsets have a tab stop of 8.
|
||||||
|
|
||||||
|
**sarif**
|
||||||
|
|
||||||
|
: Static Analysis Results Interchange Format (SARIF) output.
|
||||||
|
|
||||||
**quiet**
|
**quiet**
|
||||||
|
|
||||||
: Suppress all normal output. Exit with zero if no issues are found,
|
: 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.GCC
|
||||||
import qualified ShellCheck.Formatter.JSON
|
import qualified ShellCheck.Formatter.JSON
|
||||||
import qualified ShellCheck.Formatter.JSON1
|
import qualified ShellCheck.Formatter.JSON1
|
||||||
|
import qualified ShellCheck.Formatter.SARIF
|
||||||
import qualified ShellCheck.Formatter.TTY
|
import qualified ShellCheck.Formatter.TTY
|
||||||
import qualified ShellCheck.Formatter.Quiet
|
import qualified ShellCheck.Formatter.Quiet
|
||||||
|
|
||||||
|
@ -146,6 +147,7 @@ formats options = Map.fromList [
|
||||||
("gcc", ShellCheck.Formatter.GCC.format),
|
("gcc", ShellCheck.Formatter.GCC.format),
|
||||||
("json", ShellCheck.Formatter.JSON.format),
|
("json", ShellCheck.Formatter.JSON.format),
|
||||||
("json1", ShellCheck.Formatter.JSON1.format),
|
("json1", ShellCheck.Formatter.JSON1.format),
|
||||||
|
("sarif", ShellCheck.Formatter.SARIF.format),
|
||||||
("tty", ShellCheck.Formatter.TTY.format options),
|
("tty", ShellCheck.Formatter.TTY.format options),
|
||||||
("quiet", ShellCheck.Formatter.Quiet.format options)
|
("quiet", ShellCheck.Formatter.Quiet.format options)
|
||||||
]
|
]
|
||||||
|
@ -576,7 +578,7 @@ printVersion = do
|
||||||
putStrLn "ShellCheck - shell script analysis tool"
|
putStrLn "ShellCheck - shell script analysis tool"
|
||||||
putStrLn $ "version: " ++ shellcheckVersion
|
putStrLn $ "version: " ++ shellcheckVersion
|
||||||
putStrLn "license: GNU General Public License, version 3"
|
putStrLn "license: GNU General Public License, version 3"
|
||||||
putStrLn "website: https://www.shellcheck.net"
|
putStrLn $ "website: " ++ shellcheckWebsite
|
||||||
|
|
||||||
printOptional = do
|
printOptional = do
|
||||||
mapM f list
|
mapM f list
|
||||||
|
|
|
@ -22,7 +22,7 @@ Use:
|
||||||
|
|
||||||
import Paths_ShellCheck (version)
|
import Paths_ShellCheck (version)
|
||||||
shellcheckVersion = showVersion version -- VERSIONSTRING
|
shellcheckVersion = showVersion version -- VERSIONSTRING
|
||||||
|
shellcheckWebsite = "https://www.shellcheck.net"
|
||||||
|
|
||||||
internalVariables = [
|
internalVariables = [
|
||||||
-- Generic
|
-- 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