Description
Description
SARIF format allows specifying related locations to the root defect
. GitHub is able to parse this location data and show them in a semi-usable way (examples below).
relatedLocations[]
- A set of locations relevant to this result. Code scanning will link to related locations when they are embedded in the result message. For more information, see the location object.
Note: This feature requires the support for region
data to be implemented first: #136
Example of relatedLocations
used in SARIF
I'm using ShellCheck as an example, but I believe that this might be useful also for other static analyzers.
The shell script used in the example:
#!/bin/bash
echo "I'm innocent script, just pass the name of the directory as parameter and I'll remove it for you..."
DIR_SUFFIX="*"
UNUSED_VAR=""
echo "I'm going to remove directory $1/$DIR_SUFFIX"
#! FIXME - Call rm -rf $1/$DIR_SUFFIX
echo "<img src="foo.png" />" > file.html
files='file1 file2'
combined_file=`cat ${files}`
echo "${combined_file}"
rm $1
echo $1 # Unquoted variables
find . -name *.ogg # Unquoted find/grep patterns
rm "~/my file.txt" # Quoted tilde expansion
v='--verbose="true"'; cmd $v # Literal quotes in variables
{
"ruleId": "SHELLCHECK_WARNING: style[SC2006]",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 15
}
}
}
],
"message": {
"text": "Use $(...) notation instead of legacy backticks `...`."
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 15
}
},
"message": {
"text": "Use $(...) notation instead of legacy backticks `...`. Suggestions: use [`$(`](1), use [`)`](2)"
}
},
"nestingLevel": 0,
"kinds": [
"style[SC2006]"
]
}
]
}
]
}
],
"relatedLocations": [
{
"id": 1,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"endColumn": 16,
"endLine": 15,
"startColumn": 15,
"startLine": 15
}
},
"message": {
"text": "$("
}
},
{
"id": 2,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"endColumn": 29,
"endLine": 15,
"startColumn": 28,
"startLine": 15
}
},
"message": {
"text": ")"
}
}
]
},
The GitHub requires links torelatedLocations
directly in the message.text
string. Otherwise, it doesn't show them in UI.
{
"text": "Use $(...) notation instead of legacy backticks `...`. Suggestions: use [`$(`](1), use [`)`](2)"
}
Data for relatedLocations
can be gathered when using ShellCheck JSON1 format.
{
"file": "innocent-script.sh",
"line": 15,
"endLine": 15,
"column": 15,
"endColumn": 29,
"level": "style",
"code": 2006,
"message": "Use $(...) notation instead of legacy backticks `...`.",
"fix": {
"replacements": [
{
"column": 15,
"endColumn": 16,
"endLine": 15,
"insertionPoint": "afterEnd",
"line": 15,
"precedence": 8,
"replacement": "$("
},
{
"column": 28,
"endColumn": 29,
"endLine": 15,
"insertionPoint": "beforeStart",
"line": 15,
"precedence": 8,
"replacement": ")"
}
]
}
}
Full ShellCheck JSON1
{
"comments": [
{
"file": "innocent-script.sh",
"line": 6,
"endLine": 6,
"column": 1,
"endColumn": 11,
"level": "warning",
"code": 2034,
"message": "UNUSED_VAR appears unused. Verify use (or export if used externally).",
"fix": null
},
{
"file": "innocent-script.sh",
"line": 12,
"endLine": 12,
"column": 17,
"endColumn": 24,
"level": "warning",
"code": 2140,
"message": "Word is of the form \"A\"B\"C\" (B indicated). Did you mean \"ABC\" or \"A\\\"B\\\"C\"?",
"fix": null
},
{
"file": "innocent-script.sh",
"line": 15,
"endLine": 15,
"column": 15,
"endColumn": 29,
"level": "style",
"code": 2006,
"message": "Use $(...) notation instead of legacy backticks `...`.",
"fix": {
"replacements": [
{
"column": 15,
"endColumn": 16,
"endLine": 15,
"insertionPoint": "afterEnd",
"line": 15,
"precedence": 8,
"replacement": "$("
},
{
"column": 28,
"endColumn": 29,
"endLine": 15,
"insertionPoint": "beforeStart",
"line": 15,
"precedence": 8,
"replacement": ")"
}
]
}
},
{
"file": "innocent-script.sh",
"line": 21,
"endLine": 21,
"column": 14,
"endColumn": 19,
"level": "warning",
"code": 2061,
"message": "Quote the parameter to -name so the shell won't interpret it.",
"fix": null
},
{
"file": "innocent-script.sh",
"line": 21,
"endLine": 21,
"column": 14,
"endColumn": 15,
"level": "info",
"code": 2035,
"message": "Use ./*glob* or -- *glob* so names with dashes won't become options.",
"fix": null
},
{
"file": "innocent-script.sh",
"line": 22,
"endLine": 22,
"column": 5,
"endColumn": 18,
"level": "warning",
"code": 2088,
"message": "Tilde does not expand in quotes. Use $HOME.",
"fix": null
},
{
"file": "innocent-script.sh",
"line": 23,
"endLine": 23,
"column": 3,
"endColumn": 21,
"level": "warning",
"code": 2089,
"message": "Quotes/backslashes will be treated literally. Use an array.",
"fix": null
},
{
"file": "innocent-script.sh",
"line": 23,
"endLine": 23,
"column": 27,
"endColumn": 29,
"level": "warning",
"code": 2090,
"message": "Quotes/backslashes in this variable will not be respected.",
"fix": null
}
]
}
Full edited csgrep SARIF
{
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "csdiff",
"version": "3.0.3",
"informationUri": "https://github.com/csutils/csdiff",
"rules": [
{
"id": "SHELLCHECK_WARNING: info[SC2035]",
"name": "SC2035",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2035",
"markdown": "Defect reference: [SC2035](https://github.com/koalaman/shellcheck/wiki/SC2035)"
}
},
{
"id": "SHELLCHECK_WARNING: style[SC2006]",
"name": "SC2006",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2006",
"markdown": "Defect reference: [SC2006](https://github.com/koalaman/shellcheck/wiki/SC2006)"
}
},
{
"id": "SHELLCHECK_WARNING: warning[SC2034]",
"name": "SC2034",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2034",
"markdown": "Defect reference: [SC2034](https://github.com/koalaman/shellcheck/wiki/SC2034)"
}
},
{
"id": "SHELLCHECK_WARNING: warning[SC2061]",
"name": "SC2061",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2061",
"markdown": "Defect reference: [SC2061](https://github.com/koalaman/shellcheck/wiki/SC2061)"
}
},
{
"id": "SHELLCHECK_WARNING: warning[SC2088]",
"name": "SC2088",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2088",
"markdown": "Defect reference: [SC2088](https://github.com/koalaman/shellcheck/wiki/SC2088)"
}
},
{
"id": "SHELLCHECK_WARNING: warning[SC2089]",
"name": "SC2089",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2089",
"markdown": "Defect reference: [SC2089](https://github.com/koalaman/shellcheck/wiki/SC2089)"
}
},
{
"id": "SHELLCHECK_WARNING: warning[SC2090]",
"name": "SC2090",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2090",
"markdown": "Defect reference: [SC2090](https://github.com/koalaman/shellcheck/wiki/SC2090)"
}
},
{
"id": "SHELLCHECK_WARNING: warning[SC2140]",
"name": "SC2140",
"properties": {
"tags": [
"ShellCheck"
]
},
"help": {
"text": "Defect reference: https://github.com/koalaman/shellcheck/wiki/SC2140",
"markdown": "Defect reference: [SC2140](https://github.com/koalaman/shellcheck/wiki/SC2140)"
}
}
]
}
},
"results": [
{
"ruleId": "SHELLCHECK_WARNING: warning[SC2034]",
"level": "warning",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 6
}
}
}
],
"message": {
"text": "UNUSED_VAR appears unused. Verify use (or export if used externally)."
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 6
}
},
"message": {
"text": "UNUSED_VAR appears unused. Verify use (or export if used externally)."
}
},
"nestingLevel": 0,
"kinds": [
"warning[SC2034]"
]
}
]
}
]
}
]
},
{
"ruleId": "SHELLCHECK_WARNING: warning[SC2140]",
"level": "warning",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 12
}
}
}
],
"message": {
"text": "Word is of the form \"A\"B\"C\" (B indicated). Did you mean \"ABC\" or \"A\\\"B\\\"C\"?"
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 12
}
},
"message": {
"text": "Word is of the form \"A\"B\"C\" (B indicated). Did you mean \"ABC\" or \"A\\\"B\\\"C\"?"
}
},
"nestingLevel": 0,
"kinds": [
"warning[SC2140]"
]
}
]
}
]
}
]
},
{
"ruleId": "SHELLCHECK_WARNING: style[SC2006]",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 15
}
}
}
],
"message": {
"text": "Use $(...) notation instead of legacy backticks `...`. Suggestions: use [`$(`](1), use [`)`](2)"
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 15
}
},
"message": {
"text": "Use $(...) notation instead of legacy backticks `...`."
}
},
"nestingLevel": 0,
"kinds": [
"style[SC2006]"
]
}
]
}
]
}
],
"relatedLocations": [
{
"id": 1,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"endColumn": 16,
"endLine": 15,
"startColumn": 15,
"startLine": 15
}
},
"message": {
"text": "$("
}
},
{
"id": 2,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"endColumn": 29,
"endLine": 15,
"startColumn": 28,
"startLine": 15
}
},
"message": {
"text": ")"
}
}
]
},
{
"ruleId": "SHELLCHECK_WARNING: warning[SC2061]",
"level": "warning",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 21
}
}
}
],
"message": {
"text": "Quote the parameter to -name so the shell won't interpret it."
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 21
}
},
"message": {
"text": "Quote the parameter to -name so the shell won't interpret it."
}
},
"nestingLevel": 0,
"kinds": [
"warning[SC2061]"
]
}
]
}
]
}
]
},
{
"ruleId": "SHELLCHECK_WARNING: info[SC2035]",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 21
}
}
}
],
"message": {
"text": "Use ./*glob* or -- *glob* so names with dashes won't become options."
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 21
}
},
"message": {
"text": "Use ./*glob* or -- *glob* so names with dashes won't become options."
}
},
"nestingLevel": 0,
"kinds": [
"info[SC2035]"
]
}
]
}
]
}
]
},
{
"ruleId": "SHELLCHECK_WARNING: warning[SC2088]",
"level": "warning",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 22
}
}
}
],
"message": {
"text": "Tilde does not expand in quotes. Use $HOME."
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 22
}
},
"message": {
"text": "Tilde does not expand in quotes. Use $HOME."
}
},
"nestingLevel": 0,
"kinds": [
"warning[SC2088]"
]
}
]
}
]
}
]
},
{
"ruleId": "SHELLCHECK_WARNING: warning[SC2089]",
"level": "warning",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 23
}
}
}
],
"message": {
"text": "Quotes/backslashes will be treated literally. Use an array."
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 23
}
},
"message": {
"text": "Quotes/backslashes will be treated literally. Use an array."
}
},
"nestingLevel": 0,
"kinds": [
"warning[SC2089]"
]
}
]
}
]
}
]
},
{
"ruleId": "SHELLCHECK_WARNING: warning[SC2090]",
"level": "warning",
"locations": [
{
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 23
}
}
}
],
"message": {
"text": "Quotes/backslashes in this variable will not be respected."
},
"codeFlows": [
{
"threadFlows": [
{
"locations": [
{
"location": {
"id": 0,
"physicalLocation": {
"artifactLocation": {
"uri": "innocent-script.sh"
},
"region": {
"startLine": 23
}
},
"message": {
"text": "Quotes/backslashes in this variable will not be respected."
}
},
"nestingLevel": 0,
"kinds": [
"warning[SC2090]"
]
}
]
}
]
}
]
}
]
}
]
}
GitHub UI
Upon clicking on the link with suggestions, the pop-up box shows the location (not visible enough IMHO):
The data can be used to show suggestions in console output as well. For example sarif-fmt
tool shows:
Related to: