mirror of
https://github.com/hohn/sarif-cli.git
synced 2025-12-15 17:03:04 +01:00
pre-alpha versions of bin/sarif-{digest,labeled,list-files,results-summary
This commit is contained in:
committed by
=Michael Hohn
parent
d180a079b0
commit
3032fe3fcd
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.sarif filter=lfs diff=lfs merge=lfs -text
|
||||
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Compiled python modules.
|
||||
*.pyc
|
||||
|
||||
# Setuptools distribution folder.
|
||||
/dist/
|
||||
|
||||
# Python egg metadata, regenerated from source files by setuptools.
|
||||
/*.egg-info
|
||||
|
||||
# virtual environment
|
||||
/.venv/
|
||||
|
||||
# Backup files
|
||||
*~
|
||||
|
||||
1
MANIFEST.in
Normal file
1
MANIFEST.in
Normal file
@@ -0,0 +1 @@
|
||||
include README.org
|
||||
55
README.org
Normal file
55
README.org
Normal file
@@ -0,0 +1,55 @@
|
||||
* Collection of cli tools for SARIF processing
|
||||
This is a work in progress; the plan is as follows:
|
||||
|
||||
Each of these tools present a high-level command-line interface to extract a
|
||||
specific subset of information from a SARIF file. The format of each tool's
|
||||
/output/ is versioned and, as much as possible, independent of the input.
|
||||
|
||||
It is the intent of these tools to
|
||||
- hide the internals of sarif when /used/
|
||||
- provide examples of extracting information from sarif files while writing your
|
||||
own or extending the tools
|
||||
|
||||
* Setup for development
|
||||
Set up the virtual environment and install the packages:
|
||||
# pip freeze > requirements.txt
|
||||
#+BEGIN_SRC sh
|
||||
python3 -m venv .venv
|
||||
. .venv/bin/activate
|
||||
python3 -m pip install -r requirements.txt
|
||||
# Or separately:
|
||||
pip install --upgrade pip
|
||||
pip install ipython pyyaml
|
||||
#+END_SRC
|
||||
|
||||
"Install" for local development:
|
||||
#+BEGIN_SRC sh
|
||||
pip install -e .
|
||||
#+END_SRC
|
||||
|
||||
* Sample Data
|
||||
The query results in =data/= are taken from lgtm.com, which ran the
|
||||
: ql/$LANG/ql/src/codeql-suites/$LANG-lgtm.qls
|
||||
queries.
|
||||
|
||||
The linux kernel has both single-location results (="kind": "problem"=) and path
|
||||
results (="kind": "path-problem"=). It also has results for multiple source
|
||||
languages.
|
||||
|
||||
The subset of files referenced by the sarif results is in =data/linux-small/=
|
||||
and is taken from
|
||||
#+begin_src javascript
|
||||
"versionControlProvenance": [
|
||||
{
|
||||
"repositoryUri": "https://github.com/torvalds/linux.git",
|
||||
"revisionId": "d9abdee5fd5abffd0e763e52fbfa3116de167822"
|
||||
}
|
||||
]
|
||||
#+end_src
|
||||
|
||||
* Commands
|
||||
|
||||
|
||||
|
||||
#+OPTIONS: ^:{}
|
||||
|
||||
6
bin/json-to-yaml
Executable file
6
bin/json-to-yaml
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import yaml
|
||||
import sys
|
||||
|
||||
yaml.dump(json.load(sys.stdin), stream=sys.stdout)
|
||||
38
bin/sarif-digest
Executable file
38
bin/sarif-digest
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import sarif_cli as S
|
||||
import sys
|
||||
|
||||
# TODO command-line: sarif-digest [<file>]
|
||||
#
|
||||
# reduce size by listing only first/last elements
|
||||
fpath = sys.argv[1]
|
||||
with open(fpath, 'r') as fp:
|
||||
sarif_struct = json.load(fp)
|
||||
|
||||
def _show_dict(elem, context):
|
||||
return {key : _compact(val, key) for key, val in elem.items()}
|
||||
|
||||
def _show_list(elem, context):
|
||||
if len(elem) > 2:
|
||||
# first and last
|
||||
return ["------------%d items, showing first and last ----------" % len(elem),
|
||||
_compact(elem[0], 0),
|
||||
_compact(elem[-1], -1)]
|
||||
if len(elem) > 0:
|
||||
return [_compact(elem[i], i) for i in range(0, len(elem))]
|
||||
else:
|
||||
return elem
|
||||
|
||||
def _compact(elem, context):
|
||||
t = type(elem)
|
||||
if t == dict:
|
||||
return _show_dict(elem, context)
|
||||
elif t == list:
|
||||
return _show_list(elem, context)
|
||||
else:
|
||||
return elem
|
||||
|
||||
json.dump(_compact(sarif_struct, "starting"), sys.stdout, indent=2)
|
||||
|
||||
|
||||
50
bin/sarif-labeled
Normal file
50
bin/sarif-labeled
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import json
|
||||
import sarif_cli as S
|
||||
import sys
|
||||
import collections
|
||||
|
||||
# TODO
|
||||
# require python 3.7+ for ordered dictionaries?
|
||||
|
||||
parser = argparse.ArgumentParser(description='Output a sarif file with labeled paths preceeding arrays and objects')
|
||||
parser.add_argument('file', metavar='file', type=str, help='input file, - for stdin')
|
||||
|
||||
args = parser.parse_args()
|
||||
with open(args.file, 'r') if args.file != '-' else sys.stdin as fp:
|
||||
sarif_struct = json.load(fp)
|
||||
|
||||
def _label_dict(elem, path):
|
||||
d = collections.OrderedDict()
|
||||
for key, val in elem.items():
|
||||
subpath = path + "['%s']" % key
|
||||
if type(val) in [dict, list]:
|
||||
d[subpath] = "----path----"
|
||||
d[key] = _label(val, subpath)
|
||||
return d
|
||||
|
||||
def _label_list(elem, path):
|
||||
if len(elem) > 0:
|
||||
l = []
|
||||
for i in range(0, len(elem)):
|
||||
subpath = path + "[%d]" % i
|
||||
if i % 4 == 0:
|
||||
l.append("---- %s ----" % subpath)
|
||||
l.append(_label(elem[i], subpath))
|
||||
return l
|
||||
else:
|
||||
return elem
|
||||
|
||||
def _label(elem, path):
|
||||
t = type(elem)
|
||||
if t == dict:
|
||||
return _label_dict(elem, path)
|
||||
elif t == list:
|
||||
return _label_list(elem, path)
|
||||
else:
|
||||
return elem
|
||||
|
||||
json.dump(_label(sarif_struct, "sarif_struct"), sys.stdout, indent=2)
|
||||
|
||||
|
||||
46
bin/sarif-list-files
Executable file
46
bin/sarif-list-files
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import json
|
||||
import sarif_cli as S
|
||||
import sys
|
||||
import collections
|
||||
|
||||
parser = argparse.ArgumentParser(description='list source files referenced by sarif file')
|
||||
parser.add_argument('file', metavar='sarif-file', type=str,
|
||||
help='input file, - for stdin')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Grab the file
|
||||
with open(args.file, 'r') if args.file != '-' else sys.stdin as fp:
|
||||
sarif_struct = json.load(fp)
|
||||
|
||||
# Make sure there are some results
|
||||
num_results = len(S.get(sarif_struct, 'runs', 0, 'results'))
|
||||
if num_results == 0:
|
||||
S.exit(0)
|
||||
|
||||
# Collect the file names
|
||||
uris = set()
|
||||
|
||||
# Locations for @kind problem
|
||||
# e.g.,
|
||||
# sarif_struct['runs'][0]['results'][5]['locations'][0]['physicalLocation']['artifactLocation']
|
||||
for resi in range(0, len(S.get(sarif_struct, 'runs', 0, 'results'))):
|
||||
uri = S.get(sarif_struct, 'runs', 0, 'results', resi, 'locations', 0,
|
||||
'physicalLocation', 'artifactLocation', 'uri')
|
||||
uris.add(uri)
|
||||
|
||||
# Locations for @kind path-problem
|
||||
# e.g. sarif_struct['runs'][0]['results'][22]['codeFlows'][0]['threadFlows'][0]['locations'][1]['location']
|
||||
for resi in range(0, len(S.get(sarif_struct, 'runs', 0, 'results'))):
|
||||
if 'codeFlows' in S.get(sarif_struct, 'runs', 0, 'results', resi).keys():
|
||||
locations = S.get(sarif_struct, 'runs', 0, 'results', resi, 'codeFlows', 0,
|
||||
'threadFlows', 0, 'locations')
|
||||
for loci in range(0, len(locations)):
|
||||
uri = S.get(locations, loci, 'location', 'physicalLocation',
|
||||
'artifactLocation', 'uri')
|
||||
uris.add(uri)
|
||||
uris = list(uris)
|
||||
uris.sort()
|
||||
for u in uris:
|
||||
print(u)
|
||||
32
bin/sarif-results-summary
Normal file
32
bin/sarif-results-summary
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import json
|
||||
import sarif_cli as S
|
||||
import sys
|
||||
import collections
|
||||
|
||||
parser = argparse.ArgumentParser(description='summary of results')
|
||||
parser.add_argument('file', metavar='sarif-file', type=str, help='input file, - for stdin')
|
||||
|
||||
args = parser.parse_args()
|
||||
with open(args.file, 'r') if args.file != '-' else sys.stdin as fp:
|
||||
sarif_struct = json.load(fp)
|
||||
|
||||
num_results = len(S.get(sarif_struct, 'runs', 0, 'results'))
|
||||
S.msg("Found %d results\n\n" % num_results)
|
||||
if num_results == 0:
|
||||
S.exit(0)
|
||||
|
||||
for resi in range(0, len(S.get(sarif_struct, 'runs', 0, 'results'))):
|
||||
message = S.get(sarif_struct, 'runs', 0, 'results', resi, 'message', 'text')
|
||||
artifact = S.get(sarif_struct, 'runs', 0, 'results', resi, 'locations', 0,
|
||||
'physicalLocation', 'artifactLocation')
|
||||
region = S.get(sarif_struct, 'runs', 0, 'results', resi, 'locations', 0,
|
||||
'physicalLocation', 'region')
|
||||
filepath = "%s:%d:%d" % (artifact['uri'], region['startLine'],
|
||||
region.get('startColumn', -1))
|
||||
S.msg("%s: %s\n" % (filepath, message))
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
data/torvalds_linux__2021-10-21_10_07_00__export.sarif
(Stored with Git LFS)
Normal file
BIN
data/torvalds_linux__2021-10-21_10_07_00__export.sarif
(Stored with Git LFS)
Normal file
Binary file not shown.
18
requirements.txt
Normal file
18
requirements.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
appnope==0.1.2
|
||||
attrs==21.2.0
|
||||
backcall==0.2.0
|
||||
decorator==5.1.0
|
||||
ipython==7.28.0
|
||||
jedi==0.18.0
|
||||
matplotlib-inline==0.1.3
|
||||
parso==0.8.2
|
||||
pbr==5.6.0
|
||||
pexpect==4.8.0
|
||||
pickleshare==0.7.5
|
||||
prompt-toolkit==3.0.20
|
||||
ptyprocess==0.7.0
|
||||
Pygments==2.10.0
|
||||
PyYAML==6.0
|
||||
sarif-om==1.0.4
|
||||
traitlets==5.1.0
|
||||
wcwidth==0.2.5
|
||||
18
sarif_cli/__init__.py
Normal file
18
sarif_cli/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import sys
|
||||
|
||||
MIN_PYTHON = (3, 7)
|
||||
if sys.version_info < MIN_PYTHON:
|
||||
sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON)
|
||||
|
||||
def get(sarif_struct, *path):
|
||||
""" Get the sarif entry at PATH """
|
||||
res = sarif_struct
|
||||
for p in path:
|
||||
res = res[p]
|
||||
return res
|
||||
|
||||
def msg(message):
|
||||
""" Print message to stdout """
|
||||
sys.stdout.write(message)
|
||||
sys.stdout.write('\n')
|
||||
|
||||
1
sarif_cli/tests/__init__.py
Normal file
1
sarif_cli/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
17
setup.py
Normal file
17
setup.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from setuptools import setup
|
||||
import glob
|
||||
|
||||
setup(name='sarif_cli',
|
||||
version='0.1',
|
||||
description='Collection of command line tools for sarif files',
|
||||
url='https://github.com/hohn/sarif-cli',
|
||||
author='Michael Hohn',
|
||||
author_email='hohn@github.com',
|
||||
license='MIT',
|
||||
packages=['sarif_cli'],
|
||||
install_requires=[],
|
||||
include_package_data=True,
|
||||
scripts=glob.glob("bin/sarif-*"),
|
||||
zip_safe=False,
|
||||
python_requires='>=3.7'
|
||||
)
|
||||
Reference in New Issue
Block a user