Skip to content

Commit

Permalink
feat: update to latest stable cyclonedx-python-lib
Browse files Browse the repository at this point in the history
- Enables PipEnv support natively
- Vast improvements to quality and information contained in the genereated CycloneDX BOM documents - see `cyclonedx-python-lib` for details
- Various old files removes

Signed-off-by: Paul Horton <phorton@sonatype.com>
  • Loading branch information
madpah committed Oct 12, 2021
1 parent ca992f2 commit 6145bd5
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 2,205 deletions.
5 changes: 0 additions & 5 deletions create-virtualenv.sh

This file was deleted.

42 changes: 38 additions & 4 deletions cyclonedx_py/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@

import argparse
import os
import sys
from datetime import datetime

from cyclonedx.model.bom import Bom
from cyclonedx.model.bom import Bom, Tool
from cyclonedx.output import BaseOutput, get_instance, OutputFormat, SchemaVersion
from cyclonedx.parser import BaseParser
from cyclonedx.parser.environment import EnvironmentParser
from cyclonedx.parser.pipenv import PipEnvFileParser
from cyclonedx.parser.poetry import PoetryFileParser
from cyclonedx.parser.requirements import RequirementsFileParser

Expand Down Expand Up @@ -62,8 +64,19 @@ def get_output(self) -> BaseOutput:
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
print('')

bom = Bom.from_parser(parser=parser)

# Add cyclonedx_bom as a Tool to record it being part of the CycloneDX SBOM generation process
if sys.version_info >= (3, 8, 0):
from importlib.metadata import version
else:
from importlib_metadata import version
bom.get_metadata().add_tool(tool=Tool(
vendor='CycloneDX', name='cyclonedx-bom', version=version('cyclonedx-bom')
))

return get_instance(
bom=Bom.from_parser(parser=parser),
bom=bom,
output_format=OutputFormat[str(self._arguments.output_format).upper()],
schema_version=SchemaVersion['V{}'.format(
str(self._arguments.output_schema_version).replace('.', '_')
Expand Down Expand Up @@ -98,6 +111,12 @@ def get_arg_parser() -> argparse.ArgumentParser:
'to a `poetry.lock` you wish to use, else we\'ll look for one in the current working directory.',
dest='input_from_poetry'
)
input_group.add_argument(
'-pip', '--pip', action='store_true',
help='Build a SBOM based on a PipEnv Pipfile.lock\'s contents. Use with --pip-file to specify absolute path'
'to a `Pipefile.lock` you wish to use, else we\'ll look for one in the current working directory.',
dest='input_from_pip'
)
input_group.add_argument(
'-r', '--r', '--requirements', action='store_true',
help='Build a SBOM based on a requirements.txt\'s contents. Use with -rf to specify absolute path'
Expand All @@ -115,6 +134,16 @@ def get_arg_parser() -> argparse.ArgumentParser:
dest='input_poetry_file', required=False
)

req_input_group = arg_parser.add_argument_group(
title='PipEnv',
description='Additional optional arguments if you are setting the input type to `pipenv`'
)
req_input_group.add_argument(
'--pip-file', action='store', metavar='FILE_PATH', default='Pipfile.lock',
help='Path to a the `Pipfile.lock` file you wish to parse',
dest='input_pipenv_file', required=False
)

req_input_group = arg_parser.add_argument_group(
title='Requirements',
description='Additional optional arguments if you are setting the input type to `requirements`.'
Expand Down Expand Up @@ -164,6 +193,13 @@ def _error_and_exit(message: str, exit_code: int = 1):
def _get_input_parser(self) -> BaseParser:
if self._arguments.input_from_environment:
return EnvironmentParser()
elif self._arguments.input_from_pip:
pipfile_lock_file = os.path.realpath(self._arguments.input_pipenv_file)
if CycloneDxCmd._validate_file_exists(self._arguments.input_pipenv_file):
# A Pipfile.lock path was provided
return PipEnvFileParser(pipenv_lock_filename=pipfile_lock_file)
else:
self._error_and_exit(f'The provided file \'{pipfile_lock_file}\' does not exist')
elif self._arguments.input_from_poetry:
poetry_lock_file = os.path.realpath(self._arguments.input_poetry_file)
if CycloneDxCmd._validate_file_exists(self._arguments.input_poetry_file):
Expand All @@ -174,9 +210,7 @@ def _get_input_parser(self) -> BaseParser:
poetry_lock_file
))
elif self._arguments.input_from_requirements:
# if self._arguments.input_requirements_file:
requirements_file = os.path.realpath(self._arguments.input_requirements_file)
# requirements_file = self._arguments.input_requirements_file
if CycloneDxCmd._validate_file_exists(self._arguments.input_requirements_file):
# A requirements.txt path was provided
return RequirementsFileParser(requirements_file=requirements_file)
Expand Down
57 changes: 32 additions & 25 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ packages = [
{ include = "cyclonedx_py" }
]
include = [
"LICENSE"
"LICENSE", "NOTICE"
]

[tool.poetry.dependencies]
python = "^3.6"
cyclonedx-python-lib = "0.4.1"
cyclonedx-python-lib = "^0.8.1"

[tool.poetry.dev-dependencies]
tox = "^3.24.3"
Expand Down
39 changes: 39 additions & 0 deletions tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,22 @@
# Copyright (c) OWASP Foundation. All Rights Reserved.

import json
import sys
import xml.etree.ElementTree
from datetime import datetime, timezone
from unittest import TestCase
from uuid import uuid4
from xml.dom import minidom

if sys.version_info >= (3, 8, 0):
from importlib.metadata import version
else:
from importlib_metadata import version

cyclonedx_bom_name: str = 'cyclonedx-bom'
cyclonedx_bom_version: str = version(cyclonedx_bom_name)
cyclonedx_lib_name: str = 'cyclonedx-python-lib'
cyclonedx_lib_version: str = version(cyclonedx_lib_name)
single_uuid: str = 'urn:uuid:{}'.format(uuid4())


Expand All @@ -50,6 +60,21 @@ def assertEqualJsonBom(self, a: str, b: str):
ab['metadata']['timestamp'] = now.isoformat()
bb['metadata']['timestamp'] = now.isoformat()

# Align 'this' Tool Version
if 'tools' in ab['metadata'].keys():
for i, tool in enumerate(ab['metadata']['tools']):
if tool['name'] == cyclonedx_lib_name:
ab['metadata']['tools'][i]['version'] = cyclonedx_lib_version
elif tool['name'] == cyclonedx_bom_name:
ab['metadata']['tools'][i]['version'] = cyclonedx_bom_version

if 'tools' in bb['metadata'].keys():
for i, tool in enumerate(bb['metadata']['tools']):
if tool['name'] == cyclonedx_lib_name:
bb['metadata']['tools'][i]['version'] = cyclonedx_lib_version
elif tool['name'] == cyclonedx_bom_name:
bb['metadata']['tools'][i]['version'] = cyclonedx_bom_version

self.assertEqualJson(json.dumps(ab), json.dumps(bb))


Expand Down Expand Up @@ -80,6 +105,20 @@ def assertEqualXmlBom(self, a: str, b: str, namespace: str):
if metadata_ts_b is not None:
metadata_ts_b.text = now.isoformat()

# Align 'this' Tool Version
lib_tool = ba.find('.//*/{{{}}}tool[{{{}}}name="cyclonedx-python-lib"]'.format(namespace, namespace))
if lib_tool:
lib_tool.find('./{{{}}}version'.format(namespace)).text = cyclonedx_lib_version
lib_tool = bb.find('.//*/{{{}}}tool[{{{}}}name="cyclonedx-python-lib"]'.format(namespace, namespace))
if lib_tool:
lib_tool.find('./{{{}}}version'.format(namespace)).text = cyclonedx_lib_version
this_tool = ba.find('.//*/{{{}}}tool[{{{}}}name="cyclonedx-bom"]'.format(namespace, namespace))
if this_tool:
this_tool.find('./{{{}}}version'.format(namespace)).text = cyclonedx_bom_version
this_tool = bb.find('.//*/{{{}}}tool[{{{}}}name="cyclonedx-bom"]'.format(namespace, namespace))
if this_tool:
this_tool.find('./{{{}}}version'.format(namespace)).text = cyclonedx_bom_version

self.assertEqualXml(
xml.etree.ElementTree.tostring(ba, 'unicode'),
xml.etree.ElementTree.tostring(bb, 'unicode')
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/bom_v1.2_setuptools.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" version="1">
<metadata>
<timestamp>2021-09-01T10:50:42.051979+00:00</timestamp>
<tools>
<tool>
<vendor>CycloneDX</vendor>
<name>cyclonedx-python-lib</name>
<version>VERSION</version>
</tool>
<tool>
<vendor>CycloneDX</vendor>
<name>cyclonedx-bom</name>
<version>VERSION</version>
</tool>
</tools>
</metadata>
<components>
<component type="library" bom-ref="pkg:pypi/setuptools@50.3.2">
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/bom_v1.3_setuptools.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" version="1">
<metadata>
<timestamp>2021-09-01T10:50:42.051979+00:00</timestamp>
<tools>
<tool>
<vendor>CycloneDX</vendor>
<name>cyclonedx-python-lib</name>
<version>VERSION</version>
</tool>
<tool>
<vendor>CycloneDX</vendor>
<name>cyclonedx-bom</name>
<version>VERSION</version>
</tool>
</tools>
</metadata>
<components>
<component type="library" bom-ref="pkg:pypi/setuptools@50.3.2">
Expand Down
Loading

0 comments on commit 6145bd5

Please sign in to comment.