Skip to content

Commit 4b0ae1c

Browse files
author
Glenn Snyder
committed
2 parents 089d8ed + 8e4a1ef commit 4b0ae1c

6 files changed

+313
-5
lines changed

examples/create_fix_it_message.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
from datetime import datetime
5+
import logging
6+
import json
7+
import sys
8+
9+
parser = argparse.ArgumentParser("Process the JSON output from get_bom_component_policy_violations.py to create a FIX IT message that guides the project team how to resolve the issues identified through policy rule violations")
10+
parser.add_argument("-f", "--policy_violations_file", help="By default, program reads JSON doc from stdin, but you can alternatively give a file name")
11+
parser.add_argument("-o", "--output_file", help="By default, the fix it message is written to stdout. Use this option to instead write to a file")
12+
13+
args = parser.parse_args()
14+
15+
logging.basicConfig(format='%(asctime)s%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
16+
logging.getLogger("requests").setLevel(logging.WARNING)
17+
logging.getLogger("urllib3").setLevel(logging.WARNING)
18+
19+
if args.policy_violations_file:
20+
with open(args.policy_violations_file, 'r') as pvf:
21+
policy_violations = json.load(pvf)
22+
else:
23+
policy_violations = json.load(sys.stdin)
24+
25+
project_name = policy_violations['project']['name']
26+
version_label = policy_violations['version']['versionName']
27+
version_url = policy_violations['version']['_meta']['href']
28+
version_license = policy_violations['version']['license']['licenseDisplay']
29+
if version_license.lower() == "unknown license":
30+
license_statement = """This project version does not appear to have a declared license.
31+
You should probably declare one.. You can find more info on how to choose the appropriate license
32+
for your project <a href=https://our-company/choosing-license>here</a>"""
33+
else:
34+
license_statement = "This project version is governed by the {} license.".format(version_license)
35+
36+
html = """
37+
<html>
38+
<body>
39+
<p>This is a summary of the policy violations found in project {}, version {}. It was created {}.
40+
This summary provides guidance on what to do to resolve these violations. </p>
41+
<p>{}</p>
42+
<p>You can view the project version and more details regarding it
43+
on Black Duck <a href={}>here</a></p>
44+
<p>For more information on SCA (Software Composition Analysis) and
45+
how it fits into our SDLC go to <a href=https://our-company/sca-info>https://our-company/sca-info</a></p>
46+
47+
<table border=1>
48+
<tr>
49+
<th>Component</th>
50+
<th>Version</th>
51+
<th>Component License</th>
52+
<th>URL</th>
53+
<th>Violation</th>
54+
<th>Guidance to Fix</th>
55+
<th>Overrideable</th>
56+
<th>Severity</th>
57+
</tr>
58+
""".format(project_name, version_label, datetime.now(), license_statement, version_url)
59+
60+
del policy_violations['project']
61+
del policy_violations['version']
62+
63+
for violation_info in policy_violations.values():
64+
component_name = violation_info['bom_component']['componentName']
65+
component_version = violation_info['bom_component'].get('componentVersionName')
66+
component_url = violation_info['bom_component']['component']
67+
# if component-version URL is available use it, but if not revert to the component URL
68+
component_version_url = violation_info['bom_component'].get('componentVersion', component_url)
69+
component_license = ",".join([l['licenseDisplay'] for l in violation_info['bom_component']['licenses']])
70+
policies_in_violation = violation_info['policies_in_violation']['totalCount']
71+
for policy_violation in violation_info['policies_in_violation']['items']:
72+
pv_name = policy_violation['name']
73+
pv_description = policy_violation['description']
74+
overridable = policy_violation['overridable']
75+
severity = policy_violation['severity']
76+
html += """
77+
<tr>
78+
<td>{}</td>
79+
<td>{}</td>
80+
<td>{}</td>
81+
<td><a href={}>click here to view more info on this component from Black Duck</a></td>
82+
<td>{}</td>
83+
<td>{}</td>
84+
<td>{}</td>
85+
<td>{}</td>
86+
</tr>
87+
""".format(component_name, component_version, component_license, component_version_url, pv_name, pv_description, overridable, severity)
88+
89+
html += """
90+
</table>
91+
</body>
92+
</html>
93+
"""
94+
95+
if args.output_file:
96+
output_file = open(args.output_file, 'w')
97+
else:
98+
output_file = sys.stdout
99+
100+
output_file.write(html)
101+
102+
103+

examples/get_bom_component_policy_violations.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
from blackduck.HubRestApi import HubInstance
1010

1111

12-
parser = argparse.ArgumentParser("Retreive BOM components for the given project and version")
13-
parser.add_argument("project_name")
14-
parser.add_argument("version")
12+
parser = argparse.ArgumentParser("Retreive BOM components for the given project and version by giving project/version names OR using a version URL")
13+
mutually_xgroup = parser.add_mutually_exclusive_group()
14+
mutually_xgroup.add_argument("-p", "--project_name")
15+
mutually_xgroup.add_argument("-u", "--url")
16+
parser.add_argument("-v", "--version", required="-p" in sys.argv or "--project_name" in sys.argv)
1517

1618
args = parser.parse_args()
1719

@@ -21,13 +23,20 @@
2123

2224
hub = HubInstance()
2325

24-
project = hub.get_project_by_name(args.project_name)
25-
version = hub.get_version_by_name(project, args.version)
26+
if args.url:
27+
project_url = "/".join(args.url.split("/")[:-2])
28+
project = hub.execute_get(project_url).json()
29+
version = hub.execute_get(args.url).json()
30+
else:
31+
project = hub.get_project_by_name(args.project_name)
32+
version = hub.get_version_by_name(project, args.version)
2633

2734
bom_components = hub.get_version_components(version)
2835

2936
all_policy_violations = dict()
3037

38+
all_policy_violations.update({'project': project, 'version': version})
39+
3140
for bom_component in bom_components.get('items'):
3241
if bom_component.get('policyStatus') == "IN_VIOLATION":
3342
policy_rules_url = hub.get_link(bom_component, "policy-rules")
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env python
2+
3+
'''
4+
Created on Feb 21, 2020
5+
6+
@author: gsnyder
7+
8+
Retrieve BOM computed notifications
9+
10+
Note: The user account you run this under will determine the scope (i.e. projects, versions) of
11+
the notifications that can be received.
12+
13+
'''
14+
15+
# TODO: Use startDate filter on /api/notifications to limit the notifications retrieved when using -n
16+
17+
import argparse
18+
from datetime import datetime
19+
import json
20+
import logging
21+
import pytz
22+
import sys
23+
import timestring
24+
25+
from terminaltables import AsciiTable
26+
27+
from blackduck.HubRestApi import HubInstance, object_id
28+
29+
#
30+
# Example usage:
31+
#
32+
# To get all the vulnerability notices,
33+
# python examples/get_vulnerability_notifications.py > all_vuln_notifications.json
34+
#
35+
# To get all the vulnerability notices and save the date/time of the last run,
36+
# python examples/get_vulnerability_notifications.py -s > all_vuln_notifications.json
37+
#
38+
# To get all the vulnerability notices since the last run,
39+
# python examples/get_vulnerability_notifications.py -n `cat .last_run` > all_vuln_notifications.json
40+
#
41+
# To get all the vulnerability notices since a date/time,
42+
# python examples/get_vulnerability_notifications.py -n "March 29, 2019 12:00" > since_mar_29_at_noon_vuln_notifications.json
43+
#
44+
# To get all the vulnerability notices for a given project,
45+
# python examples/get_vulnerability_notifications.py -p my-project > all_vuln_notifications_for_my_project.json
46+
#
47+
# To get all the vulnerability notices for a given project and version,
48+
# python examples/get_vulnerability_notifications.py -p my-project -v 1.0 > all_vuln_notifications_for_my_project_v1.0.json
49+
#
50+
#
51+
52+
53+
parser = argparse.ArgumentParser("Retreive BOM computed notifications")
54+
parser.add_argument("-p", "--project", help="If supplied, filter the notifications to this project")
55+
parser.add_argument("-v", "--version", help="If supplied, filter the notifications to this version (requires a project)")
56+
parser.add_argument("-n", "--newer_than",
57+
default=None,
58+
type=str,
59+
help="Set this option to see all vulnerability notifications published since the given date/time.")
60+
parser.add_argument("-d", "--save_dt",
61+
action='store_true',
62+
help="If set, the date/time will be saved to a file named '.last_run' in the current directory which can be used later with the -n option to see vulnerabilities published since the last run.")
63+
parser.add_argument("-l", "--limit", default=100000, help="To change the limit on the number of notifications to retrieve")
64+
parser.add_argument("-s", "--system", action='store_true', help="Pull notifications from the system as opposed to the user's account")
65+
args = parser.parse_args()
66+
67+
if args.newer_than:
68+
newer_than = timestring.Date(args.newer_than).date
69+
# adjust to UTC so the comparison is normalized
70+
newer_than = newer_than.astimezone(pytz.utc)
71+
else:
72+
newer_than = None
73+
74+
if args.save_dt:
75+
with open(".last_run", "w") as f:
76+
f.write(datetime.now().isoformat())
77+
78+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
79+
logging.getLogger("requests").setLevel(logging.WARNING)
80+
logging.getLogger("urllib3").setLevel(logging.WARNING)
81+
82+
hub = HubInstance()
83+
current_user = hub.get_current_user()
84+
85+
# Construct the URL to either pull from the system or user account scope,
86+
# and then narrow the search to only include BOM computed
87+
if args.system:
88+
notifications_url = "{}/api/notifications".format(hub.get_urlbase())
89+
else:
90+
notifications_url = hub.get_link(current_user, "notifications")
91+
92+
notifications_url = "{}?limit={}&filter=notificationType:VERSION_BOM_CODE_LOCATION_BOM_COMPUTED".format(
93+
notifications_url, args.limit)
94+
95+
if newer_than:
96+
start_date = newer_than.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
97+
notifications_url += "&startDate=" + start_date
98+
99+
bom_computed_notifications = hub.execute_get(notifications_url).json().get('items', [])
100+
101+
# if newer_than:
102+
# bom_computed_notifications = list(
103+
# filter(lambda n: timestring.Date(n['createdAt']) > newer_than, bom_computed_notifications))
104+
if args.project:
105+
bom_computed_notifications = list(
106+
filter(lambda n: args.project in [apv['projectName'] for apv in n['content']['affectedProjectVersions']],
107+
bom_computed_notifications))
108+
if args.version:
109+
bom_computed_notifications = list(
110+
filter(lambda n: args.version in [apv['projectVersionName'] for apv in n['content']['affectedProjectVersions']],
111+
bom_computed_notifications))
112+
113+
print(json.dumps(bom_computed_notifications))

examples/get_snippet_match_counts.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
import json
5+
import logging
6+
import sys
7+
from pprint import pprint
8+
9+
from blackduck.HubRestApi import HubInstance, object_id
10+
11+
parser = argparse.ArgumentParser("Retrieve snippet match counts for a given project-version")
12+
parser.add_argument("project_name")
13+
parser.add_argument("version")
14+
15+
args = parser.parse_args()
16+
17+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
18+
logging.getLogger("requests").setLevel(logging.WARNING)
19+
logging.getLogger("urllib3").setLevel(logging.WARNING)
20+
21+
hub = HubInstance()
22+
23+
version = hub.get_project_version_by_name(args.project_name, args.version)
24+
25+
snippet_counts_url = version['_meta']['href'] + "/snippet-counts"
26+
27+
snippet_counts = hub.execute_get(snippet_counts_url).json()
28+
29+
del snippet_counts['_meta']
30+
31+
print("Snippet count info for project {}, version {}:".format(args.project_name, args.version))
32+
pprint(snippet_counts)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
# set -x
3+
# invoke from root of repository, e.g.
4+
# cd repo_dir
5+
# ./examples/poll_for_bom_computed_notifications.bash
6+
#
7+
8+
while true
9+
do
10+
echo "polling for bom computed notifications to create fix it messages for the project-versions"
11+
if [ -f ".last_run" ]
12+
then
13+
OPTIONS="-n $(cat .last_run) -d"
14+
else
15+
OPTIONS="-n $(date +"%Y-%m-%dT%H:%M:%SZ") -d"
16+
fi
17+
python examples/get_bom_computed_notifications.py ${OPTIONS} |
18+
python examples/project_version_urls_from_bom_computed_notifications.py |
19+
while read url
20+
do
21+
echo "Processing BOM to produce a fix it message for url $url" >&2
22+
python examples/get_bom_component_policy_violations.py -u $url |
23+
python examples/create_fix_it_message.py -o $(basename $url).html
24+
echo "Wrote fix it message to $(basename $url).html"
25+
done
26+
sleep 5
27+
done
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
import logging
5+
import json
6+
import sys
7+
8+
parser = argparse.ArgumentParser("Process the JSON output from get_bom_component_policy_violations.py to create a FIX IT message that guides the project team how to resolve the issues identified through policy rule violations")
9+
parser.add_argument("-f", "--policy_violations_file", help="By default, program reads JSON doc from stdin, but you can alternatively give a file name")
10+
parser.add_argument("-o", "--output_file", help="By default, the fix it message is written to stdout. Use this option to instead write to a file")
11+
12+
args = parser.parse_args()
13+
14+
logging.basicConfig(format='%(asctime)s%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
15+
logging.getLogger("requests").setLevel(logging.WARNING)
16+
logging.getLogger("urllib3").setLevel(logging.WARNING)
17+
18+
bom_computed_notifications = json.load(sys.stdin)
19+
bom_computed_notifications = sorted(bom_computed_notifications, key=lambda n: n['createdAt'])
20+
21+
for bom_computed_notification in bom_computed_notifications:
22+
project_version_url = bom_computed_notification['content']['projectVersion']
23+
# created_at = bom_computed_notification['createdAt']
24+
print(project_version_url)

0 commit comments

Comments
 (0)