Skip to content

Commit bb2a724

Browse files
committed
Add package supplier info to package objects
This commit adds package supplier information as an attribute in the package class for package objects. For some package managers (like PyPI), there is only one feasible supplier (PyPI) and this value is set as a constant string (PyPI). For others, like rpm, the string is determined using the /etc/os-release file based on the Linux Distro providing the packages. While this is not a perfect way to determine the distro/distributor, it is satisfactory to satisfty the NTIA minimum elements for the upcoming EO 14028. We decided to use the distro as the supplier based on conversations had on the SPDX mailing list[1]. [1] https://lists.spdx.org/g/Spdx-tech/message/4942 Works towards #1205 Signed-off-by: Rose Judge <[email protected]>
1 parent 2e51f67 commit bb2a724

File tree

5 files changed

+68
-4
lines changed

5 files changed

+68
-4
lines changed

tern/analyze/default/bundle.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ def convert_to_pkg_dicts(attr_lists):
5252
'pkg_licenses': 'pkg_licenses',
5353
'files': 'files',
5454
'src_name': 'source_names',
55-
'src_version': 'source_versions'}
55+
'src_version': 'source_versions',
56+
'pkg_supplier': 'pkg_suppliers'}
5657
pkg_list = []
5758
len_names = len(attr_lists['names'])
5859
# make a list of keys that correspond with package property names

tern/analyze/default/command_lib/base.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ dpkg:
8888
container:
8989
- "dpkg-query -W -f '${Version}\n'"
9090
delimiter: "\n"
91+
pkg_suppliers:
92+
invoke:
93+
1:
94+
container:
95+
- "distro=`/bin/cat /etc/os-release | grep NAME | sed -n '2p' | cut -f 2 -d '=' | cut -d '\"' -f2`"
96+
- "pkgs=`dpkg-query -W -f '${Package}\n'`"
97+
- "for p in $pkgs; do echo $distro; done"
98+
delimiter: "\n"
9199
source_names:
92100
invoke:
93101
1:
@@ -148,6 +156,13 @@ apk:
148156
- "pkgs=`apk info 2>/dev/null`"
149157
- "for p in $pkgs; do lic=`apk info $p 2>/dev/null | head -1 | awk '{print $1}'`; echo $lic | sed -e \"s/^$p-//\"; done"
150158
delimiter: "\n"
159+
pkg_suppliers:
160+
invoke:
161+
1:
162+
container:
163+
- "pkgs=`apk info 2>/dev/null`"
164+
- "for p in $pkgs; do echo 'Alpine Linux'; done"
165+
delimiter: "\n"
151166
licenses:
152167
invoke:
153168
1:
@@ -192,6 +207,13 @@ pacman:
192207
container:
193208
- 'pacman -Q 2>/dev/null | cut -f 2 -d " "'
194209
delimiter: "\n"
210+
pkg_suppliers:
211+
invoke:
212+
1:
213+
container:
214+
- 'pkgs=`pacman -Qq 2>/dev/null`'
215+
- 'for p in $pkgs; do echo "Arch Linux"; done'
216+
delimiter: "\n"
195217
licenses:
196218
invoke:
197219
1:
@@ -238,6 +260,14 @@ rpm:
238260
container:
239261
- 'rpm --query --all --queryformat "%{version}\n" 2>/dev/null'
240262
delimiter: "\n"
263+
pkg_suppliers:
264+
invoke:
265+
1:
266+
container:
267+
- "distro=`/bin/cat /etc/os-release | grep NAME | sed -n '1p' | cut -f 2 -d '=' | cut -d '\"' -f2`"
268+
- "pkgs=`rpm --query --all --queryformat \"%{name}\n\" 2>/dev/null`"
269+
- "for p in $pkgs; do echo $distro; done"
270+
delimiter: "\n"
241271
licenses:
242272
invoke:
243273
1:
@@ -302,6 +332,13 @@ pip:
302332
- "pkgs=`pip list --format=freeze 2> /dev/null | cut -f1 -d'='`"
303333
- "for p in $pkgs; do pip show $p 2> /dev/null | head -2 | tail -1 | cut -f2 -d' '; done"
304334
delimiter: "\n"
335+
pkg_suppliers:
336+
invoke:
337+
1:
338+
container:
339+
- "pkgs=`pip list --format=freeze 2> /dev/null | cut -f1 -d'='`"
340+
- "for p in $pkgs; do echo 'PyPI'; done"
341+
delimiter: "\n"
305342
licenses:
306343
invoke:
307344
1:
@@ -345,6 +382,13 @@ pip3:
345382
- "pkgs=`pip3 list --format=freeze 2> /dev/null | cut -f1 -d'='`"
346383
- "for p in $pkgs; do pip3 show $p 2> /dev/null | head -2 | tail -1 | cut -f2 -d' '; done"
347384
delimiter: "\n"
385+
pkg_suppliers:
386+
invoke:
387+
1:
388+
container:
389+
- "pkgs=`pip3 list --format=freeze 2> /dev/null | cut -f1 -d'='`"
390+
- "for p in $pkgs; do echo 'PyPI'; done"
391+
delimiter: "\n"
348392
licenses:
349393
invoke:
350394
1:

tern/analyze/default/command_lib/command_lib.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@
3737
command_lib['snippets'] = yaml.safe_load(f)
3838
# list of package information keys that the command library can accomodate
3939
base_keys = {'names', 'versions', 'licenses', 'source_names',
40-
'source_versions', 'copyrights', 'proj_urls', 'srcs', 'files'}
40+
'source_versions', 'pkg_suppliers', 'copyrights',
41+
'proj_urls', 'srcs', 'files'}
4142
package_keys = {'name', 'version', 'license', 'src_name', 'src_version',
42-
'copyright', 'proj_url', 'src', 'files'}
43+
'pkg_supplier', 'copyright', 'proj_url', 'src', 'files'}
4344

4445
# global logger
4546
logger = logging.getLogger(constants.logger_name)

tern/classes/package.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class Package:
2424
pkg_licenses: all licenses found within a package
2525
src_name: the source package associated with the binary package
2626
src_version: the source package version
27+
pkg_supplier: the package distributor according to SPDX 7.5;
28+
required to create NTIA compliant SPDX docs
2729
2830
methods:
2931
to_dict: returns a dict representation of the instance
@@ -47,6 +49,7 @@ def __init__(self, name):
4749
self.__pkg_format = ''
4850
self.__src_name = ''
4951
self.__src_version = ''
52+
self.__pkg_supplier = ''
5053

5154
@property
5255
def name(self):
@@ -144,6 +147,14 @@ def src_version(self):
144147
def src_version(self, src_version):
145148
self.__src_version = src_version
146149

150+
@property
151+
def pkg_supplier(self):
152+
return self.__pkg_supplier
153+
154+
@pkg_supplier.setter
155+
def pkg_supplier(self, pkg_supplier):
156+
self.__pkg_supplier = pkg_supplier
157+
147158
def get_file_paths(self):
148159
"""Return a list of paths of all the files in a package"""
149160
return [f.path for f in self.__files]
@@ -217,6 +228,7 @@ def fill(self, package_dict):
217228
files: <package files>
218229
src_name: <source package>
219230
src_ver: <source package version>
231+
pkg_supplier: <package distributor/supplier>
220232
the way to use this method is to instantiate the class with the
221233
name and then give it a package dictionary to fill in the rest
222234
return true if package name is the same as the one used to instantiate

tests/test_class_package.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def setUp(self):
2525
self.p1.pkg_format = 'deb'
2626
self.p1.src_name = 'p1src'
2727
self.p1.src_version = '1.0'
28+
self.p1.pkg_supplier = 'VMware'
2829

2930
self.p2 = Package('p2')
3031

@@ -65,6 +66,8 @@ def testSetters(self):
6566
('a', '/usr/abc/a'))
6667
self.assertEqual((self.p2.files[1].name, self.p2.files[1].path),
6768
('b', '/usr/abc/b'))
69+
self.p2.pkg_supplier = 'Linux Foundation'
70+
self.assertEqual(self.p2.pkg_supplier, 'Linux Foundation')
6871

6972
def testGetters(self):
7073
self.assertEqual(self.p1.name, 'p1')
@@ -78,6 +81,7 @@ def testGetters(self):
7881
self.assertEqual(self.p1.pkg_format, 'deb')
7982
self.assertEqual(self.p1.src_name, 'p1src')
8083
self.assertEqual(self.p1.src_version, '1.0')
84+
self.assertEqual(self.p1.pkg_supplier, 'VMware')
8185

8286
def testAddFile(self):
8387
p1 = Package('package')
@@ -130,6 +134,7 @@ def testToDict(self):
130134
self.assertEqual(a_dict['pkg_format'], 'deb')
131135
self.assertEqual(a_dict['src_name'], 'p1src')
132136
self.assertEqual(a_dict['src_version'], '1.0')
137+
self.assertEqual(a_dict['pkg_supplier'], 'VMware')
133138

134139
def testToDictTemplate(self):
135140
template1 = TestTemplate1()
@@ -175,7 +180,8 @@ def testFill(self):
175180
{'name': 'b.txt', 'path': '/lib/b.txt'}],
176181
'pkg_format': 'rpm',
177182
'src_name': 'p1src',
178-
'src_version': '1.0'
183+
'src_version': '1.0',
184+
'pkg_supplier': 'VMware'
179185
}
180186
p = Package('p1')
181187
p.fill(p_dict)

0 commit comments

Comments
 (0)