Skip to content

Commit aff6c6a

Browse files
committed
new
1 parent 37a19c0 commit aff6c6a

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed

examples/client/upload_sbom.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#!/usr/bin/env python
2+
3+
'''
4+
Created on Friday, January 13th, 2023
5+
@author: kumykov
6+
7+
Copyright (C) 2021 Synopsys, Inc.
8+
http://www.blackducksoftware.com/
9+
10+
Licensed to the Apache Software Foundation (ASF) under one
11+
or more contributor license agreements. See the NOTICE file
12+
distributed with this work for additional information
13+
regarding copyright ownership. The ASF licenses this file
14+
to you under the Apache License, Version 2.0 (the
15+
"License"); you may not use this file except in compliance
16+
with the License. You may obtain a copy of the License at
17+
18+
http://www.apache.org/licenses/LICENSE-2.0
19+
20+
Unless required by applicable law or agreed to in writing,
21+
software distributed under the License is distributed on an
22+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23+
KIND, either express or implied. See the License for the
24+
specific language governing permissions and limitations
25+
under the License.
26+
27+
usage: upload_sbom [-h] [-pg PROJECT_GROUP] -u BASE_URL -t TOKEN_FILE [-nv] filename project version
28+
29+
Uploads SBOM file to a Blackduck server
30+
31+
positional arguments:
32+
filename SBOM file to upload
33+
project Project to associate SBOM with
34+
version Project Version to associate SBOM with
35+
36+
options:
37+
-h, --help show this help message and exit
38+
-pg PROJECT_GROUP, --project_group PROJECT_GROUP
39+
Project Group to be used
40+
-u BASE_URL, --base-url BASE_URL
41+
Hub server URL e.g. https://your.blackduck.url
42+
-t TOKEN_FILE, --token-file TOKEN_FILE
43+
File containing access token
44+
-nv, --no-verify Disable TLS certificate verification
45+
46+
Blackduck examples collection
47+
48+
'''
49+
50+
51+
import sys
52+
import argparse
53+
import logging
54+
55+
from blackduck import Client
56+
57+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
58+
logging.getLogger("requests").setLevel(logging.WARNING)
59+
logging.getLogger("urllib3").setLevel(logging.WARNING)
60+
logging.getLogger("blackduck").setLevel(logging.WARNING)
61+
62+
def find_project_by_name(project_name):
63+
params = {
64+
'q': [f"name:{project_name}"]
65+
}
66+
projects = [p for p in bd.get_resource('projects', params=params) if p['name'] == project_name]
67+
if len(projects) == 1:
68+
return projects[0]
69+
else:
70+
return None
71+
72+
def find_project_version_by_name(project, version_name):
73+
params = {
74+
'q': [f"versionName:{version_name}"]
75+
}
76+
versions = [v for v in bd.get_resource('versions', project, params=params) if v['versionName'] == version_name]
77+
if len(versions) == 1:
78+
return versions[0]
79+
else:
80+
return None
81+
82+
def find_or_create_project_group(group_name):
83+
url = '/api/project-groups'
84+
params = {
85+
'q': [f"name:{group_name}"]
86+
}
87+
groups = [p for p in bd.get_items(url, params=params) if p['name'] == group_name]
88+
if len(groups) == 0:
89+
headers = {
90+
'Accept': 'application/vnd.blackducksoftware.project-detail-5+json',
91+
'Content-Type': 'application/vnd.blackducksoftware.project-detail-5+json'
92+
}
93+
data = {
94+
'name': group_name
95+
}
96+
response = bd.session.post(url, headers=headers, json=data)
97+
return response.headers['Location']
98+
else:
99+
return groups[0]['_meta']['href']
100+
101+
def create_project_version(project_name,version_name,project_group, nickname = None):
102+
version_data = {"distribution": "EXTERNAL", "phase": "DEVELOPMENT", "versionName": version_name}
103+
if nickname:
104+
version_data['nickname'] = nickname
105+
url = '/api/projects'
106+
project = find_project_by_name(project_name)
107+
if project:
108+
data = version_data
109+
url = project['_meta']['href'] + '/versions'
110+
else:
111+
data = {"name": project_name,
112+
"projectGroup": find_or_create_project_group(project_group),
113+
"versionRequest": version_data}
114+
return bd.session.post(url, json=data)
115+
116+
def find_or_create_project_version(project_name, version_name, project_group):
117+
project = find_project_by_name(project_name)
118+
if project:
119+
version = find_project_version_by_name(project, version_name)
120+
if version:
121+
pass
122+
else:
123+
version = create_project_version(project_name, version_name)
124+
else:
125+
version = create_project_version(project_name, version_name, project_group)
126+
project = find_project_by_name(project_name)
127+
version = find_project_version_by_name(project, version_name)
128+
if not version:
129+
logging.info(f"Project {project_name} with Version {version_name} could not be located or created.")
130+
sys.exit(1)
131+
132+
def get_sbom_mime_type(filename):
133+
with open(filename, 'r') as f:
134+
data = f.readlines()
135+
content = " ".join(data)
136+
if 'CycloneDX' in content:
137+
return 'application/vnd.cyclonedx'
138+
if 'SPDX' in content:
139+
return 'application/spdx'
140+
return None
141+
142+
def upload_sbom_file(filename, project, version, project_group):
143+
find_or_create_project_version(project, version, project_group)
144+
mime_type = get_sbom_mime_type(filename)
145+
print (mime_type)
146+
if not mime_type:
147+
logging.error(f"Could not identify file content for {filename}")
148+
sys.exit(1)
149+
files = {"file": (filename, open(filename,"rb"), mime_type)}
150+
fields = {"projectName": project, "versionName": version}
151+
response = bd.session.post("/api/scan/data", files = files, data=fields)
152+
logging.info(response)
153+
if response.status_code == 409:
154+
logging.info(f"File {filename} is already mapped to a different project version")
155+
156+
def main():
157+
args = parse_command_args()
158+
with open(args.token_file, 'r') as tf:
159+
access_token = tf.readline().strip()
160+
global bd
161+
bd = Client(base_url=args.base_url, token=access_token, verify=args.no_verify, timeout=60.0, retries=4)
162+
upload_sbom_file(args.filename, args.project, args.version, args.project_group)
163+
164+
def parse_command_args():
165+
parser = argparse.ArgumentParser(prog = "upload_sbom", description="Uploads SBOM file to a Blackduck server", epilog="Blackduck examples collection")
166+
parser.add_argument("filename", help="SBOM file to upload")
167+
parser.add_argument("project", help="Project to associate SBOM with")
168+
parser.add_argument("version", help="Project Version to associate SBOM with")
169+
parser.add_argument("-pg", "--project_group", required=False, default='SBOM-Import', help="Project Group to be used")
170+
parser.add_argument("-u", "--base-url", required=True, help="Hub server URL e.g. https://your.blackduck.url")
171+
parser.add_argument("-t", "--token-file", required=True, help="File containing access token")
172+
parser.add_argument("-nv", "--no-verify", action='store_false', help="Disable TLS certificate verification")
173+
return parser.parse_args()
174+
175+
176+
177+
if __name__ == "__main__":
178+
sys.exit(main())

0 commit comments

Comments
 (0)