-
-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic structure without any output generation available (very basic C…
…omponent definition).
- Loading branch information
Showing
16 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from typing import List | ||
from .cyclonedx import Component | ||
from ..parser import BaseParser | ||
|
||
|
||
class Bom: | ||
""" | ||
This is our internal representation of the BOM. | ||
We can pass a BOM instance to a Generator to produce CycloneDX in the required format and according | ||
to the requested schema version. | ||
""" | ||
|
||
_components: List[Component] = [] | ||
|
||
@staticmethod | ||
def from_parser(parser: BaseParser): | ||
bom = Bom() | ||
bom.add_components(parser.get_components()) | ||
return bom | ||
|
||
def __init__(self): | ||
self._components.clear() | ||
|
||
def add_component(self, component: Component): | ||
self._components.add(component) | ||
|
||
def add_components(self, components: List[Component]): | ||
self._components = self._components + components | ||
|
||
def component_count(self) -> int: | ||
return len(self._components) | ||
|
||
def has_component(self, component: Component) -> bool: | ||
print("Checking if {} is contained within {}".format(component, self._components)) | ||
return component in self._components |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from enum import Enum | ||
|
||
PURL_TYPE_PREFIX = 'pypi' | ||
|
||
|
||
class ComponentType(Enum): | ||
""" | ||
Enum object that defines the permissable 'types' for a Component according to the CycloneDX | ||
schemas. | ||
""" | ||
APPLICATION = 'application' | ||
CONTAINER = 'container' | ||
DEVICE = 'device' | ||
FILE = 'file' | ||
FIRMWARE = 'firmware' | ||
FRAMEWORK = 'framework' | ||
LIBRARY = 'library' | ||
OPERATING_SYSTEM = 'operating-system' | ||
|
||
|
||
class Component: | ||
""" | ||
An object that mirrors the Component type in the CycloneDX schema. | ||
""" | ||
_type: ComponentType | ||
_name: str | ||
_version: str | ||
_qualifiers: str | ||
|
||
def __init__(self, name: str, version: str, qualifiers: str = None, type: ComponentType = ComponentType.LIBRARY): | ||
self._name = name | ||
self._version = version | ||
self._type = type | ||
self._qualifiers = qualifiers | ||
|
||
def get_purl(self) -> str: | ||
base_purl = 'pkg:{}/{}@{}'.format(PURL_TYPE_PREFIX, self._name, self._version) | ||
if self._qualifiers: | ||
base_purl = '{}?{}'.format(base_purl, self._qualifiers) | ||
return base_purl | ||
|
||
def __eq__(self, other): | ||
return other.get_purl() == self.get_purl() | ||
|
||
def __repr__(self): | ||
return '<Component {}={}>'.format(self._name, self._version) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from cyclonedx.model.bom import Bom | ||
|
||
|
||
class Xml: | ||
|
||
_bom: Bom | ||
|
||
def __init__(self, bom: Bom): | ||
self._bom = bom |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from abc import ABC | ||
from typing import List | ||
|
||
from ..model.cyclonedx import Component | ||
|
||
|
||
class BaseParser(ABC): | ||
_components: List[Component] = [] | ||
|
||
def component_count(self) -> int: | ||
return len(self._components) | ||
|
||
def get_components(self) -> List[Component]: | ||
return self._components |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from . import BaseParser | ||
|
||
from ..model.cyclonedx import Component | ||
|
||
|
||
class EnvironmentParser(BaseParser): | ||
""" | ||
This will look at the current Python environment and list out all installed packages. | ||
Best used when you have virtual Python environments per project. | ||
""" | ||
|
||
def __init__(self): | ||
import pkg_resources | ||
for i in iter(pkg_resources.working_set): | ||
self._components.append(Component(name=i.project_name, version=i.version, type='pypi')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import pkg_resources | ||
|
||
from . import BaseParser | ||
|
||
from ..model.cyclonedx import Component | ||
|
||
|
||
class RequirementsParser(BaseParser): | ||
|
||
def __init__(self, requirements_content: str): | ||
requirements = pkg_resources.parse_requirements(requirements_content) | ||
for requirement in requirements: | ||
""" | ||
@todo | ||
Note that the below line will get the first (lowest) version specified in the Requirement and | ||
ignore the operator (it might not be ==). This is passed to the Component. | ||
For example if a requirement was listed as: "PickyThing>1.6,<=1.9,!=1.8.6", we'll be interpretting this | ||
as if it were written "PickyThing==1.6" | ||
""" | ||
(op, version) = requirement.specs[0] | ||
self._components.append(Component( | ||
name=requirement.project_name, version=version | ||
)) | ||
|
||
|
||
class RequirementsFileParser(RequirementsParser): | ||
|
||
def __init__(self, requirements_file: str): | ||
with open(requirements_file) as r: | ||
super(RequirementsFileParser, self).__init__(requirements_content=r.read()) | ||
r.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
packageurl-python==0.9.4 | ||
requirements_parser==0.2.0 | ||
setuptools>=50.3.2 | ||
tox==3.24.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
packageurl-python>=0.9.4 | ||
requirements_parser>=0.2.0 | ||
setuptools>=50.3.2 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
packageurl-python>=0.9.4 | ||
requirements_parser>=0.2.0 | ||
setuptools>=50.3.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
setuptools==50.3.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from unittest import TestCase | ||
|
||
import os | ||
|
||
from cyclonedx.model.bom import Bom | ||
from cyclonedx.model.cyclonedx import Component | ||
from cyclonedx.parser.requirements import RequirementsFileParser | ||
|
||
|
||
class TestBom(TestCase): | ||
|
||
def test_bom_simple(self): | ||
parser = RequirementsFileParser( | ||
requirements_file=os.path.join(os.path.dirname(__file__), 'fixtures/requirements-simple.txt') | ||
) | ||
bom = Bom.from_parser(parser=parser) | ||
|
||
self.assertEqual(bom.component_count(), 1) | ||
self.assertTrue(bom.has_component( | ||
Component(name='setuptools', version='50.3.2') | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
from unittest import TestCase | ||
|
||
from cyclonedx.model.cyclonedx import Component | ||
from packageurl import PackageURL | ||
|
||
|
||
class TestComponent(TestCase): | ||
_component: Component | ||
|
||
@classmethod | ||
def setUpClass(cls) -> None: | ||
cls._component = Component(name='setuptools', version='50.3.2').get_purl() | ||
cls._component_with_qualifiers = Component(name='setuptools', version='50.3.2', | ||
qualifiers='extension=tar.gz').get_purl() | ||
|
||
def test_purl_correct(self): | ||
self.assertEqual( | ||
str(PackageURL( | ||
type='pypi', name='setuptools', version='50.3.2' | ||
)), | ||
TestComponent._component | ||
) | ||
|
||
def test_purl_incorrect_version(self): | ||
purl = PackageURL( | ||
type='pypi', name='setuptools', version='50.3.1' | ||
) | ||
self.assertNotEqual( | ||
str(purl), | ||
TestComponent._component | ||
) | ||
self.assertEqual(purl.type, 'pypi') | ||
self.assertEqual(purl.name, 'setuptools') | ||
self.assertEqual(purl.version, '50.3.1') | ||
|
||
def test_purl_incorrect_name(self): | ||
purl = PackageURL( | ||
type='pypi', name='setuptoolz', version='50.3.2' | ||
) | ||
self.assertNotEqual( | ||
str(purl), | ||
TestComponent._component | ||
) | ||
self.assertEqual(purl.type, 'pypi') | ||
self.assertEqual(purl.name, 'setuptoolz') | ||
self.assertEqual(purl.version, '50.3.2') | ||
|
||
def test_purl_with_qualifiers(self): | ||
purl = PackageURL( | ||
type='pypi', name='setuptools', version='50.3.2', qualifiers='extension=tar.gz' | ||
) | ||
self.assertEqual( | ||
str(purl), | ||
TestComponent._component_with_qualifiers | ||
) | ||
self.assertNotEqual( | ||
str(purl), | ||
TestComponent._component | ||
) | ||
self.assertEqual(purl.qualifiers, {'extension': 'tar.gz'}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from unittest import TestCase | ||
|
||
from cyclonedx.parser.environment import EnvironmentParser | ||
|
||
|
||
class TestRequirementsParser(TestCase): | ||
|
||
def test_simple(self): | ||
""" | ||
@todo This test is a vague as it will detect the unique environment where tests are being executed - | ||
so is this valid? | ||
:return: | ||
""" | ||
parser = EnvironmentParser() | ||
self.assertGreater(parser.component_count(), 1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import os | ||
from unittest import TestCase | ||
|
||
from cyclonedx.parser.requirements import RequirementsParser | ||
|
||
|
||
class TestRequirementsParser(TestCase): | ||
|
||
def test_simple(self): | ||
with open(os.path.join(os.path.dirname(__file__), 'fixtures/requirements-simple.txt')) as r: | ||
parser = RequirementsParser( | ||
requirements_content=r.read() | ||
) | ||
r.close() | ||
self.assertTrue(1, parser.component_count()) | ||
|
||
def test_example_1(self): | ||
with open(os.path.join(os.path.dirname(__file__), 'fixtures/requirements-example-1.txt')) as r: | ||
parser = RequirementsParser( | ||
requirements_content=r.read() | ||
) | ||
r.close() | ||
self.assertTrue(3, parser.component_count()) |