Skip to content

Add support for relatedLocations in SARIF format #140

Open
@jamacku

Description

@jamacku

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

Screenshot from 2023-08-24 08-57-26

Upon clicking on the link with suggestions, the pop-up box shows the location (not visible enough IMHO):

Screenshot from 2023-08-24 08-57-39

Screenshot from 2023-08-24 08-57-47

The data can be used to show suggestions in console output as well. For example sarif-fmt tool shows:

Screenshot from 2023-08-24 08-58-35

Related to:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions