18
18
# Copyright (c) OWASP Foundation. All Rights Reserved.
19
19
20
20
from enum import Enum
21
+ from os .path import exists
21
22
from packageurl import PackageURL
22
23
from typing import List
23
24
25
+ from . import HashAlgorithm , HashType , sha1sum
24
26
from .vulnerability import Vulnerability
25
27
26
- PURL_TYPE_PREFIX = 'pypi'
27
-
28
28
29
29
class ComponentType (Enum ):
30
30
"""
@@ -51,6 +51,7 @@ class Component:
51
51
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.3/#type_component
52
52
"""
53
53
_type : ComponentType
54
+ _package_url_type : str
54
55
_name : str
55
56
_version : str
56
57
_qualifiers : str
@@ -59,15 +60,57 @@ class Component:
59
60
_description : str = None
60
61
_license : str = None
61
62
63
+ _hashes : List [HashType ] = []
62
64
_vulnerabilites : List [Vulnerability ] = []
63
65
64
- def __init__ (self , name : str , version : str , qualifiers : str = None ,
65
- component_type : ComponentType = ComponentType .LIBRARY ):
66
+ @staticmethod
67
+ def for_file (absolute_file_path : str , path_for_bom : str = None ):
68
+ """
69
+ Helper method to create a Component that represents the provided local file as a Component.
70
+
71
+ Args:
72
+ absolute_file_path:
73
+ Absolute path to the file you wish to represent
74
+ path_for_bom:
75
+ Optionally, if supplied this is the path that will be used to identify the file in the BOM
76
+
77
+ Returns:
78
+ `Component` representing the supplied file
79
+ """
80
+ if not exists (absolute_file_path ):
81
+ raise FileExistsError ('Supplied file path \' {}\' does not exist' .format (absolute_file_path ))
82
+
83
+ sha1_hash : str = sha1sum (filename = absolute_file_path )
84
+
85
+ return Component (
86
+ name = path_for_bom if path_for_bom else absolute_file_path ,
87
+ version = '0.0.0-{}' .format (sha1_hash [0 :12 ]),
88
+ hashes = [
89
+ HashType (algorithm = HashAlgorithm .SHA_1 , hash_value = sha1_hash )
90
+ ],
91
+ component_type = ComponentType .FILE ,
92
+ package_url_type = 'generic'
93
+ )
94
+
95
+ def __init__ (self , name : str , version : str , qualifiers : str = None , hashes : List [HashType ] = [],
96
+ component_type : ComponentType = ComponentType .LIBRARY , package_url_type : str = 'pypi' ):
66
97
self ._name = name
67
98
self ._version = version
68
99
self ._type = component_type
69
100
self ._qualifiers = qualifiers
101
+ self ._hashes = hashes
70
102
self ._vulnerabilites = []
103
+ self ._package_url_type = package_url_type
104
+
105
+ def add_hash (self , hash : HashType ):
106
+ """
107
+ Adds a hash that pins/identifies this Component.
108
+
109
+ Args:
110
+ hash:
111
+ `HashType` instance
112
+ """
113
+ self ._hashes .append (hash )
71
114
72
115
def add_vulnerability (self , vulnerability : Vulnerability ):
73
116
"""
@@ -100,6 +143,15 @@ def get_description(self) -> str:
100
143
"""
101
144
return self ._description
102
145
146
+ def get_hashes (self ) -> List [HashType ]:
147
+ """
148
+ List of cryptographic hashes that identify this Component.
149
+
150
+ Returns:
151
+ `List` of `HashType` objects where there are any hashes, else an empty `List`.
152
+ """
153
+ return self ._hashes
154
+
103
155
def get_license (self ) -> str :
104
156
"""
105
157
Get the license of this Component.
@@ -125,7 +177,7 @@ def get_purl(self) -> str:
125
177
Returns:
126
178
PackageURL that reflects this Component as `str`.
127
179
"""
128
- base_purl = 'pkg:{}/{}@{}' .format (PURL_TYPE_PREFIX , self ._name , self ._version )
180
+ base_purl = 'pkg:{}/{}@{}' .format (self . _package_url_type , self ._name , self ._version )
129
181
if self ._qualifiers :
130
182
base_purl = '{}?{}' .format (base_purl , self ._qualifiers )
131
183
return base_purl
@@ -213,7 +265,7 @@ def to_package_url(self) -> PackageURL:
213
265
`packageurl.PackageURL` instance which represents this Component.
214
266
""" ""
215
267
return PackageURL (
216
- type = PURL_TYPE_PREFIX ,
268
+ type = self . _package_url_type ,
217
269
name = self ._name ,
218
270
version = self ._version ,
219
271
qualifiers = self ._qualifiers
0 commit comments