├── .github
├── dependabot.yml
└── workflows
│ ├── ci.yml
│ ├── release-please.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .pylintrc
├── CHANGELOG.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── action.yml
├── entrypoint.sh
├── launch.json
├── requirements-dev.txt
├── requirements.txt
├── setup.py
├── spdxmerge
├── SPDXMerge.py
├── SPDXMergeLib.py
├── SPDX_DeepMerge.py
├── SPDX_ShallowMerge.py
├── __init__.py
├── checksum.py
└── utils.py
└── test
├── __init__.py
├── json
├── input
│ ├── test1.json
│ └── test2.json
└── output
│ └── result.json
├── spdx
├── input
│ ├── test1.spdx
│ └── test2.spdx
└── output
│ └── result.spdx
├── test_SPDX_DeepMerge.py
└── test_SPDX_ShallowMerge.py
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "pip" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 | - package-ecosystem: "github-actions"
13 | directory: "/"
14 | schedule:
15 | interval: "weekly"
16 | - package-ecosystem: "docker"
17 | directory: "/"
18 | schedule:
19 | interval: "weekly"
20 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [push]
3 | jobs:
4 | build:
5 | runs-on: ubuntu-latest
6 | strategy:
7 | matrix:
8 | python-version: [3.10.9]
9 | steps:
10 | - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # ratchet:actions/checkout@v4.1.3
11 | - name: Set up Python ${{ matrix.python-version }}
12 | uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # ratchet:actions/setup-python@v4
13 | with:
14 | python-version: ${{ matrix.python-version }}
15 | - name: Install dependencies
16 | run: |
17 | python -m pip install --upgrade pip
18 | pip install -r requirements.txt
19 | - name: Show help
20 | run: |
21 | python spdxmerge/SPDXMerge.py --help
22 | - name: Run pylint
23 | run: |
24 | pip install -r requirements-dev.txt
25 | pylint **/*.py
26 | - name: Run pytest
27 | run: |
28 | pytest .
29 |
--------------------------------------------------------------------------------
/.github/workflows/release-please.yml:
--------------------------------------------------------------------------------
1 | name: Create Release
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches: [main]
6 | permissions:
7 | contents: read
8 | jobs:
9 | release-please:
10 | name: Create Release
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: google-github-actions/release-please-action@a37ac6e4f6449ce8b3f7607e4d97d0146028dc0b # ratchet:google-github-actions/release-please-action@v4.1.0
14 | with:
15 | release-type: python
16 | token: ${{ secrets.SPDXMERGE_GITHUB_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create and publish signed docker image with SLSA provenance and SBOM
2 | on:
3 | push:
4 | tags:
5 | - v[0-9]+.[0-9]+.[0-9]+*
6 | env:
7 | REGISTRY_USERNAME: ${{ secrets.DOCKER_USERNAME }}
8 | REGISTRY_TOKEN: '${{ secrets.DOCKER_PASSWORD }}'
9 | DOCKER_ORGANIZATION: philipssoftware
10 | GITHUB_ORGANIZATION: philips-software
11 | KEYLESS: true
12 | jobs:
13 | get-version:
14 | name: Get version from tag
15 | runs-on: ubuntu-latest
16 | outputs:
17 | version: ${{ steps.version.outputs.version }}
18 | steps:
19 | - name: Get version
20 | id: version
21 | run: |
22 | echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
23 |
24 | docker-image:
25 | needs: [get-version]
26 | name: Docker build from dockerfile
27 | runs-on: ubuntu-latest
28 | permissions:
29 | packages: write
30 | id-token: write
31 | container: python:3.10
32 | steps:
33 | - name: Checkout code
34 | uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # ratchet:actions/checkout@v3
35 | - name: Show version
36 | run: echo ${{ needs.get-version.outputs.version }}
37 | - name: Build docker image
38 | uses: philips-software/docker-ci-scripts@d0045b844f08b0dcb4c62bd4acf6c36877404dae # ratchet:philips-software/docker-ci-scripts@v5.1.0
39 | with:
40 | dockerfile: .
41 | image-name: spdxmerge
42 | tags: ${{ needs.get-version.outputs.version }} latest
43 | push-branches: main
44 | push-on-git-tag: true
45 | slsa-provenance: true
46 | sign: true
47 | sbom: true
48 |
49 | pypi-executable:
50 | name: "Build and publish Python \U0001F40D distributions \U0001F4E6 to PyPI and TestPyPI"
51 | runs-on: ubuntu-latest
52 | permissions:
53 | packages: write
54 | id-token: write
55 | container: python:3.10
56 | steps:
57 | - name: Checkout code
58 | uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # ratchet:actions/checkout@v3
59 | - name: Install pypa/build
60 | run: >-
61 | python3 -m pip install build
62 | - name: Build a binary wheel and a source tarball
63 | run: >-
64 | python -m build --sdist --wheel --outdir dist/ .
65 | - name: "Publish distribution \U0001F4E6 to PyPI"
66 | if: startsWith(github.ref, 'refs/tags')
67 | uses: pypa/gh-action-pypi-publish@81e9d935c883d0b210363ab89cf05f3894778450 # ratchet:pypa/gh-action-pypi-publish@release/v1
68 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test github action for merging SPDX
2 | on:
3 | push:
4 | jobs:
5 | test-action:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # ratchet:actions/checkout@v4.1.3
9 | - name: Test merging SPDX JSON format files
10 | uses: ./
11 | with:
12 | docpath: ${{github.workspace}}/test/json/input
13 | name: sample-sbom-json
14 | version: "1"
15 | mergetype: "1" # 0 shallow merge, 1 deep merge
16 | author: "ci/cd build pipeline"
17 | email: "test@mail.com"
18 | docnamespace: "https://philips.example.com"
19 | filetype: "J" # J JSON, T for SPDX tag value format
20 | - name: Check result for JSON format
21 | run: |
22 | # Ignore field created since it contains a created at timestamp
23 | diff -I"created" merged-SBoM-deep.json ${{github.workspace}}/test/json/output/result.json
24 | - name: Test merging SPDX Tag value format files
25 | uses: ./
26 | with:
27 | docpath: ${{github.workspace}}/test/spdx/input
28 | name: sample-sbom-spdx
29 | version: "1"
30 | mergetype: "0" # 0 shallow merge, 1 deep merge
31 | author: "ci/cd build pipeline"
32 | email: "test@mail.com"
33 | docnamespace: "https://philips.example.com"
34 | filetype: "T" # J JSON, T for SPDX tag value format
35 | - name: Check result for SPDX tag value format
36 | run: |
37 | # Ignore field created since it contains a created at timestamp
38 | diff -I"Created" -I"SHA[0-9]+:" merged-SBoM-shallow.spdx ${{github.workspace}}/test/spdx/output/result.spdx
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/launch.json
2 | __pycache__/
3 | SPDXMerge.code-workspace
4 |
5 | merged-SBoM*.json
6 |
7 | .DS_Store
8 |
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 |
4 | disable=line-too-long,missing-module-docstring,invalid-name,too-many-arguments,
5 | wrong-import-order,missing-function-docstring,duplicate-code,missing-class-docstring,
6 | redefined-builtin,fixme,attribute-defined-outside-init,import-error
7 |
8 | [REPORTS]
9 |
10 | # Set the output format. Available formats are text, parseable, colorized, msvs
11 | # (visual studio) and html. You can also give a reporter class, eg
12 | # mypackage.mymodule.MyReporterClass.
13 | output-format=colorized
14 |
15 | [FORMAT]
16 |
17 | # Maximum number of characters on a single line.
18 | max-line-length=120
19 |
20 | [TYPECHECK]
21 | signature-mutators=click.decorators.option
22 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [0.2.0](https://github.com/philips-software/SPDXMerge/compare/v0.1.3...v0.2.0) (2023-05-25)
4 |
5 | ### Features
6 |
7 | * Output path ([#42](https://github.com/philips-software/SPDXMerge/pull/42) - Thanks to @electricgull
8 |
9 | ### Bug Fixes
10 |
11 | * pylint error ([5305124](https://github.com/philips-software/SPDXMerge/commit/530512479c6741f69b6e5e500f032dcdb6e40d58))
12 |
13 |
14 | ### Documentation
15 |
16 | * update docs ([51515d6](https://github.com/philips-software/SPDXMerge/commit/51515d6cb70f286ab68d25e359a742005097292a))
17 |
18 | ## [0.1.3](https://github.com/philips-software/SPDXMerge/compare/v0.1.2...v0.1.3) (2023-04-26)
19 |
20 |
21 | ### Bug Fixes
22 |
23 | * remove pypi test environment ([cf37e22](https://github.com/philips-software/SPDXMerge/commit/cf37e22d43a1b7a8e076138346c5446bc1cde707))
24 |
25 | ## [0.1.2](https://github.com/philips-software/SPDXMerge/compare/v0.1.1...v0.1.2) (2023-04-26)
26 |
27 |
28 | ### Bug Fixes
29 |
30 | * Use Trusted Publisher ([cf7d5f6](https://github.com/philips-software/SPDXMerge/commit/cf7d5f6cdfa93295b8cd0c59acb1acfa58a8692e))
31 |
32 | ## [0.1.1](https://github.com/philips-software/SPDXMerge/compare/v0.1.0...v0.1.1) (2023-04-26)
33 |
34 |
35 | ### Bug Fixes
36 |
37 | * Also tag the latest docker-image ([17e0deb](https://github.com/philips-software/SPDXMerge/commit/17e0deb57f14ddc30d6f57bd44ce6ecc096ac556))
38 |
39 | ## [0.1.0](https://github.com/philips-software/SPDXMerge/compare/v0.1.0...v0.1.0) (2023-04-03)
40 |
41 |
42 | ### Bug Fixes
43 |
44 | * push on tags ([8cf0f11](https://github.com/philips-software/SPDXMerge/commit/8cf0f1127243a2129d563ad7cc06ace25f87f4a9))
45 | * Remove changelog ([7070746](https://github.com/philips-software/SPDXMerge/commit/707074687b53ac627eb3af2ca5771d3e11da324e))
46 | * Update CHANGELOG ([97cc859](https://github.com/philips-software/SPDXMerge/commit/97cc859e7a3d36a08fd5b665097dd28806c8f639))
47 | * update docker image name ([1e71092](https://github.com/philips-software/SPDXMerge/commit/1e71092b372bc0b558836997809e27eb12c07cfb))
48 |
49 |
50 | ### Documentation
51 |
52 | * Add CONTRIBUTING and LICENSE ([9f7cd39](https://github.com/philips-software/SPDXMerge/commit/9f7cd39e5327b2857c00d1f1af8804e205507c7f))
53 |
54 |
55 | ### Miscellaneous Chores
56 |
57 | * release 0.1.1 ([c75459a](https://github.com/philips-software/SPDXMerge/commit/c75459a0540f1ad479fe66ad8550fa3037b5d498))
58 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in
2 | # the repo. Unless a later match takes precedence,
3 | # they will be requested for review when someone opens a
4 | # pull request.
5 | * @philips-software/spdx-merge
6 |
7 | # See CODEOWNERS syntax here: https://help.github.com/articles/about-codeowners/#codeowners-syntax
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to SPDXMerge
2 |
3 | - [Question or Problem?](#question)
4 | - [Issues and Bugs](#issue)
5 |
6 | ## Got a Question or Problem?
7 | Please raise an issue for now. We don't have other official communication channels in place right now. If you really want to know more, you can contact the contributors through the standard Philips communication channel.
8 |
9 | ## Found an Issue?
10 | If you find a bug in the source code or a mistake in the documentation, you can help us by submitting an issue to our [Github Repository][github]. Even better you can submit a Pull Request with a fix.
11 |
12 | [github]: https://github.com/philips-software/SPDXMerge/issues
13 |
14 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # For more information, please refer to https://aka.ms/vscode-docker-python
2 | FROM python:3.12.3
3 |
4 | # Assign work directory
5 | WORKDIR /app
6 | COPY . /app
7 |
8 | # Install pip requirements
9 | RUN pip install -r requirements.txt
10 |
11 | # Execute
12 | RUN chmod +x /app/entrypoint.sh
13 | ENTRYPOINT ["/app/entrypoint.sh"]
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2023 Koninklijke Philips N.V.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tool for merging SPDX files
2 |
3 | This tool integrates multiple SPDX JSON formatted Software Bill of Materials (SBOMs) into a parent SBOM, either by consolidating all the contents into a single file or by creating references to multiple files.
4 |
5 | The tool works with SPDX 2.2 and SPDX 2.3 versions.
6 |
7 | ## Features
8 |
9 | Combine multiple SPDX JSON/Tag value files into a single parent Software Bill of Materials (SBOM) in one of two ways:
10 |
11 | - **Deep Merge** - Combines the contents of all SBOM files into a single comprehensive parent file, incorporating all the information about the package dependencies and their relationships.
12 | - **Shallow Merge** - Generates a parent SBOM that references multiple SBOM files in the `externalDocumentRefs` section.
13 |
14 | ## How to use
15 |
16 | ### Manual Installation
17 |
18 | SPDX Tools (`spdx-tools`) needs to be installed as a prerequisite for this application to work. It is listed in the `requirements.txt` file.
19 | Run the following command to install all necessary dependencies:
20 |
21 | ```shell
22 | pip install -r requirements.txt
23 | ```
24 |
25 | Execute the command with the required inputs:
26 |
27 | ```shell
28 | python spdxmerge/SPDXMerge --docpath
29 | --outpath (optional)
30 | --name
31 | --version
32 | --mergetype <1 for deep merge/0 for shallow merge>
33 | --author
34 | --email
35 | --docnamespace
36 | --filetype
37 | --rootdocpath (optional)
38 | ```
39 | ---
40 |
41 | ### 🔹 **New Update: Version Input**
42 | To establish a unique **"DESCRIBES"** relationship in the SPDX document, a **root package** must be created.
43 | For this, we now require both **name** and **version** as input parameters.
44 |
45 | ---
46 |
47 | ### 🔹 **New Update: Roothpath input**
48 | To establish a unique **"DESCRIBES"** relationship in the SPDX document, a **root document** can be specified.
49 | For this, we now introduce the **`--rootdocpath`** option to define the root document, ensuring proper validation and relationship mapping.
50 |
51 | #### Options
52 | - `--rootdocpath` (optional): Specifies the root SBoM document.
53 |
54 | #### Implementation Details
55 | - The tool scans the root document for a `DESCRIBES` relationship.
56 | - If found, the related SPDX element ID is used to establish the relationship.
57 | - If no such relationship is found, an error is raised.
58 | - The relationship is added to the master document.
59 |
60 | #### Error Handling
61 | - Raises an error if `--rootdocpath` is defined but the file is not found in the path.
62 | - Raises an error if the root document lacks a `DESCRIBES` relationship.
63 | ---
64 |
65 | ### GitHub Action
66 |
67 | ```yml
68 | - name: Checkout project
69 | uses: actions/checkout@v3
70 | - name: Run SPDX Merge tool to merge spdx files
71 | uses: philips-software/SPDXMerge@v0.2.0
72 | with:
73 | docpath: ${{github.workspace}}/Test
74 | name: sample-sbom
75 | version: 1.0.0
76 | mergetype: 1
77 | author: "Kung Fury"
78 | email: "kfury@example.com"
79 | filetype: J
80 | docnamespace: "https://mycompany.example.com"
81 | - name: Check result
82 | run: cat merged-SBoM.json
83 | ```
84 |
85 | ---
86 |
87 | ### Docker Image
88 |
89 | ```shell
90 | docker run -it --rm \
91 | -v $(PWD):/code \
92 | -v $(PWD)/output/:/output \
93 | -e DOCPATH='/code' \
94 | -e OUTPATH='/output' \
95 | -e NAME='' \
96 | -e VERSION='' \
97 | -e MERGETYPE='' \
98 | -e AUTHOR='' \
99 | -e EMAIL='' \
100 | -e DOCNAMESPACE='' \
101 | -e FILETYPE='' \
102 | -e ROOTPATH='' \
103 | docker.io/philipssoftware/spdxmerge:v0.2.0
104 | ```
105 |
106 | ---
107 |
108 | ## TODOs
109 |
110 | - Option for Organization, Author tag in document creation
111 |
112 | ---
113 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: 'SPDX Merge'
2 | author: 'Philips'
3 | description: 'Merge one or more SBOMs into one parent SBOM'
4 | inputs:
5 | docpath:
6 | description: 'The path to the folder that contains the files to be merged'
7 | mandatory: true
8 | outpath:
9 | description: 'The path to the folder where the merged file will be saved'
10 | mandatory: false
11 | name:
12 | description: "Name of product for which SBoM is created"
13 | mandatory: true
14 | version:
15 | description: "Version of product for which SBoM is created"
16 | mandatory: true
17 | mergetype:
18 | description: "Type of merge: 0 for shallow merge, 1 for deep merge"
19 | mandatory: false
20 | default: "1"
21 | author:
22 | description: "SBoM Author name"
23 | mandatory: false
24 | email:
25 | description: "SBoM Author email address"
26 | mandatory: false
27 | docnamespace:
28 | description: "Document namespace; URL where document is stored or organzation URL"
29 | default: "https://spdx.organization.name"
30 | filetype:
31 | description: "Resulting merge file format: J for JSON, T for SPDX tag value format"
32 | default: "J"
33 | runs:
34 | using: 'docker'
35 | image: 'Dockerfile'
36 | env:
37 | DOCPATH: ${{ inputs.docpath }}
38 | NAME: ${{ inputs.name }}
39 | VERSION: ${{ inputs.version }}
40 | MERGETYPE: ${{ inputs.mergetype }}
41 | AUTHOR: ${{ inputs.author }}
42 | EMAIL: ${{ inputs.email }}
43 | DOCNAMESPACE: ${{ inputs.docnamespace }}
44 | FILETYPE: ${{ inputs.filetype }}
45 | branding:
46 | icon: 'book'
47 | color: 'blue'
48 |
--------------------------------------------------------------------------------
/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo "arguments:"
3 | echo " - DOCPATH : $DOCPATH"
4 | echo " - OUTPATH : $OUTPATH"
5 | echo " - NAME : $NAME"
6 | echo " - VERSION : $VERSION"
7 | echo " - MERGETYPE : $MERGETYPE"
8 | echo " - AUTHOR : $AUTHOR"
9 | echo " - EMAIL : $EMAIL"
10 | echo " - DOCNAMESPACE : $DOCNAMESPACE"
11 | echo " - FILETYPE : $FILETYPE"
12 | echo " - ROOTPATH : $ROOTPATH"
13 |
14 | # TODO: add check for missing arguments
15 | python3 /app/spdxmerge/SPDXMerge.py \
16 | --docpath "$DOCPATH" \
17 | --outpath "$OUTPATH" \
18 | --name "$NAME" \
19 | --version "$VERSION" \
20 | --mergetype $MERGETYPE \
21 | --author "$AUTHOR" \
22 | --email "$EMAIL" \
23 | --docnamespace "$DOCNAMESPACE" \
24 | --filetype "$FILETYPE" \
25 | --rootdocpath "$ROOTPATH"
--------------------------------------------------------------------------------
/launch.json:
--------------------------------------------------------------------------------
1 | python3 -u SPDXMerge.py --docpath=test/json/input
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | # To ensure app dependencies are ported from your virtual environment/host machine into your container, run 'pip freeze > requirements.txt' in the terminal to overwrite this file
2 | pylint==3.1.0
3 | pytest==8.1.1
4 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | astroid==3.1.0
2 | beartype==0.20.0
3 | boolean.py==4.0
4 | click==8.1.8
5 | colorama==0.4.6
6 | dill==0.3.9
7 | iniconfig==2.0.0
8 | isort==5.13.2
9 | license-expression==30.4.1
10 | mccabe==0.7.0
11 | packaging==24.2
12 | platformdirs==4.3.6
13 | pluggy==1.5.0
14 | ply==3.11
15 | pylint==3.1.0
16 | pyparsing==3.2.1
17 | pytest==8.1.1
18 | PyYAML==6.0.2
19 | rdflib==7.1.3
20 | semantic-version==2.10.0
21 | spdx-tools==0.8.3
22 | tomlkit==0.13.2
23 | uritools==4.0.3
24 | xmltodict==0.14.2
25 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from setuptools import find_packages, setup
4 |
5 | requirements_path = os.path.join(os.path.dirname(__file__), 'requirements.txt')
6 |
7 | with open(requirements_path, "r", encoding="utf8") as f:
8 | required = f.read().splitlines()
9 |
10 | setup(
11 | name='spdxmerge',
12 | version='0.2.0',
13 | description="merges content of two/more spdx sboms",
14 | long_description_content_type="text/markdown",
15 | url="https://github.com/philips-software/SPDXMerge",
16 | packages=find_packages(include=['spdxmerge'], exclude=['test', '*.test', '*.test.*']),
17 | classifiers=[
18 | "Programming Language :: Python :: 3",
19 | "License :: OSI Approved :: MIT License",
20 | "Operating System :: OS Independent",
21 | ],
22 | install_requires=required,
23 | python_requires='>=3.8',
24 | entry_points='''
25 | [console_scripts]
26 | spdxmerge=spdxmerge.SPDXMerge:main
27 | '''
28 | )
29 |
--------------------------------------------------------------------------------
/spdxmerge/SPDXMerge.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=wrong-import-position
2 | import os
3 | import sys
4 | import click
5 |
6 | sys.path.insert(0, "/".join(os.path.dirname(__file__).split('/')[:-1]))
7 |
8 | from spdxmerge.SPDXMergeLib import create_merged_spdx_document, write_file
9 | from spdxmerge.utils import read_docs
10 |
11 |
12 | @click.command()
13 | @click.option("--docpath", prompt="Directory path", required=True,
14 | help="Directory path with SPDX files to be merged")
15 | @click.option("--outpath", prompt="Output directory path", required=False, prompt_required=False,
16 | help="Output directory path where merged file should be saved")
17 | @click.option("--name", prompt="Product Name", required=True,
18 | help="Name of product for which SBoM is created")
19 | @click.option("--version", prompt="Product Version", required=True,
20 | help="Version of product for which SBoM is created")
21 | @click.option("--mergetype", prompt="Shallow Merge -0 or Deep Merge-1",
22 | help="Enter 0 for shallow merge , 1 for deep merge", type=click.Choice(['0', '1']), default='1')
23 | @click.option("--author", prompt="SBoM Author name", required=True,
24 | help="Author who is writing SBoM")
25 | @click.option("--email", prompt="SBoM Author email", required=False, prompt_required=False,
26 | help="Author email address")
27 | @click.option("--docnamespace", prompt="Document namespace",
28 | help="URL where document is stored or organization URL", default="https://spdx.organization.name")
29 | @click.option("--filetype", prompt="SBoM output file type SPDX tag value format - T or JSON - J",
30 | help="Enter T for SPDX tag value format, J for JSON",
31 | type=click.Choice(['T', 't', 'J', 'j']), default='J')
32 | @click.option("--rootdocpath", prompt="SBoM root author", required=False, prompt_required=False,
33 | default="",
34 | help="SBoM that should be used as root author")
35 | def main(docpath, name, version, mergetype, author, email, docnamespace, filetype, rootdocpath=None, outpath=None):
36 | """Tool provides option to merge SPDX SBoM files. Provides two options for merging,
37 | Shallow Merge: New SBoM is created only with external ref links to SBoM files to be merged
38 | Deep Merge: New SBoM file is created by appending package, relationship, license information
39 | """
40 | doc_list, root_doc = read_docs(docpath, rootdocpath)
41 | merge_type = "shallow" if mergetype == '0' else "deep"
42 | doc = create_merged_spdx_document(doc_list, docnamespace, name, version, author, email, merge_type, root_doc)
43 | write_file(doc, filetype, merge_type, outpath)
44 |
45 |
46 | if __name__ == "__main__":
47 | main()
48 |
--------------------------------------------------------------------------------
/spdxmerge/SPDXMergeLib.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | from spdxmerge.SPDX_DeepMerge import SPDX_DeepMerger
4 | from spdxmerge.SPDX_ShallowMerge import SPDX_ShallowMerger
5 | from spdx_tools.spdx.writer.json.json_writer import (
6 | write_document_to_file as write_json_document,
7 | )
8 | from spdx_tools.spdx.writer.tagvalue.tagvalue_writer import (
9 | write_document_to_file as write_tagvalue_document
10 | )
11 |
12 | def create_merged_spdx_document(doc_list, docnamespace, name, version, author, email, merge_type, root_doc):
13 | if merge_type == "deep":
14 | merger = SPDX_DeepMerger(doc_list, docnamespace, name, version, author, email, root_doc)
15 | merger.doc_packageinfo()
16 | merger.doc_fileinfo()
17 | merger.doc_snippetinfo()
18 | merger.doc_other_license_info()
19 | merger.doc_relationship_info()
20 | elif merge_type == "shallow":
21 | merger = SPDX_ShallowMerger(doc_list, docnamespace, name, version, author, email)
22 | merger.doc_externalDocumentRef()
23 |
24 | return merger.get_document()
25 |
26 | def write_file(doc, filetype, merge_type, outpath=None):
27 | result_filetype = "spdx" if filetype.lower() == "t" else "json"
28 | file = f"merged-SBoM-{merge_type}.{result_filetype}"
29 | if outpath:
30 | file = os.path.join(outpath, file)
31 | try:
32 | if result_filetype == "spdx":
33 | write_tagvalue_document(doc, file, validate=False)
34 | else:
35 | write_json_document(doc, file, validate=False)
36 | with open(file, 'r', encoding='utf-8') as f:
37 | json_content = json.load(f)
38 | print(json.dumps(json_content, indent=2))
39 | except (ValueError) as e:
40 | print("Document is Invalid:", end="")
41 | print((e.args[0]))
42 | print("File "+file+" is generated")
43 |
--------------------------------------------------------------------------------
/spdxmerge/SPDX_DeepMerge.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from spdx_tools.spdx.model import (
3 | Document,
4 | Relationship,
5 | RelationshipType,
6 | CreationInfo,
7 | Actor,
8 | ActorType,
9 | Package,
10 | SpdxNoAssertion,
11 | )
12 |
13 |
14 | class SPDX_DeepMerger:
15 | def __init__(
16 | self,
17 | doc_list=None,
18 | docnamespace=None,
19 | name=None,
20 | version=None,
21 | author=None,
22 | email=None,
23 | root_doc=None,
24 | ):
25 | self.doc_list = doc_list
26 | self.version = version
27 | self.root_doc = root_doc
28 | # data_license is "CC0-1.0" by default
29 | self.master_doc = Document(
30 | CreationInfo(
31 | spdx_version="SPDX-2.3",
32 | spdx_id="SPDXRef-DOCUMENT",
33 | name=name,
34 | document_namespace=docnamespace,
35 | creators=[
36 | Actor(actor_type=ActorType.ORGANIZATION, name=author, email=email)
37 | ],
38 | created=datetime.utcnow().replace(microsecond=0),
39 | )
40 | )
41 |
42 | def get_document(self):
43 | return self.master_doc
44 |
45 | def doc_packageinfo(self):
46 | """
47 | Append packages from document list
48 | """
49 | if self.root_doc is None:
50 | Main_Package = Package(
51 | name=self.master_doc.creation_info.name,
52 | version=self.version,
53 | spdx_id="SPDXRef-" + str(0),
54 | download_location=SpdxNoAssertion(),
55 | )
56 |
57 | self.master_doc.packages.append(Main_Package)
58 | for doc in self.doc_list:
59 | self.master_doc.packages += doc.packages
60 |
61 | def doc_fileinfo(self):
62 | for doc in self.doc_list:
63 | self.master_doc.files += doc.files
64 |
65 | def doc_snippetinfo(self):
66 | for doc in self.doc_list:
67 | self.master_doc.snippets += doc.snippets
68 |
69 | def doc_other_license_info(self):
70 | """
71 | Append unique licenses to hasExtractedLicensingInfo
72 | """
73 | master_doc_eli_ids = []
74 | for doc in self.doc_list:
75 | doc_eli = [
76 | eli
77 | for eli in doc.extracted_licensing_info
78 | if eli.license_id not in master_doc_eli_ids
79 | ]
80 | master_doc_eli_ids += [eli.license_id for eli in doc_eli]
81 | self.master_doc.extracted_licensing_info += doc_eli
82 |
83 | def doc_relationship_info(self):
84 | if self.root_doc is None:
85 | Main_Package = self.master_doc.packages[0]
86 | # The document should DESCRIBE the root package with name as input name and version as input version
87 | relationship = Relationship(
88 | spdx_element_id=self.master_doc.creation_info.spdx_id,
89 | relationship_type=RelationshipType.DESCRIBES,
90 | related_spdx_element_id=Main_Package.spdx_id,
91 | )
92 |
93 | else:
94 | found = False
95 | # Find the SPDX element ID with the DESCRIBES relationship
96 | for rel in self.root_doc.relationships:
97 | if rel.relationship_type == RelationshipType.DESCRIBES:
98 | related_spdx_element_id = rel.related_spdx_element_id
99 | found = True
100 | break
101 |
102 | # If no DESCRIBES relationship is found, raise an error
103 | if not found:
104 | raise ValueError("Root document has no relationship of type DESCRIBES")
105 |
106 | relationship = Relationship(
107 | spdx_element_id=self.master_doc.creation_info.spdx_id,
108 | relationship_type=RelationshipType.DESCRIBES,
109 | related_spdx_element_id=related_spdx_element_id,
110 | )
111 |
112 | self.master_doc.relationships.append(relationship)
113 |
114 | # Also add relationships from the imported documents
115 | valid_relationships = []
116 | for doc in self.doc_list:
117 | for rel in doc.relationships:
118 | # Since we need a unique DESCIRBES, skip the other DESCRIBES relationships
119 | if rel.relationship_type == RelationshipType.DESCRIBES:
120 | continue
121 |
122 | valid_relationships.append(rel)
123 |
124 | # Append only valid relationships
125 | self.master_doc.relationships += valid_relationships
126 |
127 | # Since Reviews have been deprecated in SPDX 2.3, we will not include this and instead use Annotations
128 | def doc_annotation_info(self):
129 | for doc in self.doc_list:
130 | self.master_doc.annotations += doc.annotations
131 |
--------------------------------------------------------------------------------
/spdxmerge/SPDX_ShallowMerge.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from spdx_tools.spdx.model import (
3 | Checksum,
4 | ChecksumAlgorithm,
5 | Actor,
6 | ActorType,
7 | Document,
8 | ExternalDocumentRef,
9 | Package,
10 | SpdxNoAssertion,
11 | CreationInfo
12 | )
13 | class SPDX_ShallowMerger():
14 | def __init__(self,
15 | doc_list=None,
16 | docnamespace=None,
17 | name=None,
18 | version=None,
19 | author=None,
20 | email=None):
21 | self.doc_list = doc_list
22 | self.version = version
23 | # data_license is "CC0-1.0" by default
24 | self.master_doc = Document(CreationInfo(
25 | spdx_version="SPDX-2.3",
26 | spdx_id="SPDXRef-DOCUMENT",
27 | name=name,
28 | document_namespace=docnamespace,
29 | creators=[Actor(
30 | actor_type=ActorType.ORGANIZATION,
31 | name=author,
32 | email=email
33 | )],
34 | created=datetime.utcnow().replace(microsecond=0)
35 | ))
36 |
37 | def get_document(self):
38 | return self.master_doc
39 |
40 | def doc_externalDocumentRef(self):
41 | package = Package(
42 | name=self.master_doc.creation_info.name,
43 | version=self.version,
44 | spdx_id="SPDXRef-" + str(0),
45 | download_location=SpdxNoAssertion()
46 | )
47 |
48 | self.master_doc.packages.append(package)
49 |
50 | # for doc in self.doc_list:
51 | # check_sum = Checksum(ChecksumAlgorithm.SHA1, doc.creation_info.document_comment)
52 | # doc_ref_id = "DocumentRef-" + doc.creation_info.spdx_id #is this how we want it?
53 | # extDoc = ExternalDocumentRef(doc_ref_id, doc.creation_info.document_namespace, check_sum)
54 | # self.master_doc.creation_info.external_document_refs.append(extDoc)
55 | for doc in self.doc_list:
56 | print(doc.comment)
57 | check_sum = Checksum(ChecksumAlgorithm.SHA1, doc.comment)
58 | doc_ref_id = "DocumentRef-" + doc.creation_info.spdx_id
59 | extDoc = ExternalDocumentRef(doc_ref_id, doc.creation_info.document_namespace, check_sum)
60 | self.master_doc.creation_info.external_document_refs.append(extDoc)
61 |
--------------------------------------------------------------------------------
/spdxmerge/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/philips-software/SPDXMerge/488954bc4d7793d57ac594f929f9be6769e820cd/spdxmerge/__init__.py
--------------------------------------------------------------------------------
/spdxmerge/checksum.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | def sha256sum(filename):
4 | h = hashlib.sha256()
5 | b = bytearray(128*1024)
6 | mv = memoryview(b)
7 | with open(filename, 'rb', buffering=0) as f:
8 | while n := f.readinto(mv):
9 | h.update(mv[:n])
10 | return h.hexdigest()
11 |
12 |
13 | def sha1sum(filename):
14 | h = hashlib.sha1()
15 | b = bytearray(128*1024)
16 | mv = memoryview(b)
17 | with open(filename, 'rb', buffering=0) as f:
18 | while n := f.readinto(mv):
19 | h.update(mv[:n])
20 | return h.hexdigest()
21 |
--------------------------------------------------------------------------------
/spdxmerge/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | from spdx_tools.spdx.parser import parse_anything
3 | from spdxmerge.checksum import sha1sum
4 |
5 |
6 | def read_docs(dir, root_doc_path=None):
7 | doc_list = []
8 | root_doc = None
9 | doc_files = [f for f in os.listdir(dir) if f.endswith((".json", ".spdx"))]
10 |
11 | # Check if root document is present in the directory
12 | if root_doc_path and root_doc_path in doc_files:
13 | root_doc = parse_anything.parse_file(dir + "/" + root_doc_path)
14 | root_doc.comment = sha1sum(dir + "/" + root_doc_path)
15 | else:
16 | if root_doc_path != "":
17 | # Raise error if --rootdocpath is defined but given root document is not found
18 | raise FileNotFoundError(
19 | f"Root document {root_doc_path} not found in directory {dir}."
20 | )
21 | for file in doc_files:
22 | doc = parse_anything.parse_file(dir + "/" + file)
23 | check_sum = sha1sum(dir + "/" + file)
24 | doc.comment = check_sum
25 | doc_list.append(doc)
26 |
27 | return doc_list, root_doc
28 |
--------------------------------------------------------------------------------
/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/philips-software/SPDXMerge/488954bc4d7793d57ac594f929f9be6769e820cd/test/__init__.py
--------------------------------------------------------------------------------
/test/json/input/test1.json:
--------------------------------------------------------------------------------
1 | {
2 | "SPDXID" : "SPDXRef-DOCUMENT",
3 | "spdxVersion" : "SPDX-2.3",
4 | "creationInfo" : {
5 | "created" : "2023-05-12T18:30:22Z",
6 | "creators" : [ "Person: Gary O'Neall (gary@sourceauditor.com)" ]
7 | },
8 | "name" : "SPDX Tools v1.1.5 Java SBOM",
9 | "dataLicense" : "CC0-1.0",
10 | "documentNamespace" : "http://spdx.org/spdxdocs/tools-java/v1.1.5-444504E0-4F89-41D3-9A0C-0305E82C3301",
11 | "packages" : [ {
12 | "SPDXID" : "SPDXRef-Package",
13 | "name" : "tools-java",
14 | "versionInfo" : "1.5.1",
15 | "supplier" : "Organization: SPDX (Spdx-tech@lists.spdx.org)",
16 | "packageFileName" : "tools-java-1.1.5.zip",
17 | "checksums" : [ {
18 | "algorithm" : "SHA1",
19 | "checksumValue" : "e01bcba3c55fa7f0c82d08d0cdce9061b21bf32f"
20 | } ],
21 | "downloadLocation" : "https://github.com/spdx/tools-java/releases/download/v1.1.5/tools-java-1.1.5.zip",
22 | "filesAnalyzed" : false,
23 | "externalRefs" : [ {
24 | "referenceCategory" : "PACKAGE-MANAGER",
25 | "referenceLocator" : "pkg:github/spdx/tools-java@2235d5d7f7fe46ce1e0d54b7831c5681633b25cc",
26 | "referenceType" : "purl"
27 | } ]
28 | } ],
29 | "relationships" : [ {
30 | "spdxElementId" : "SPDXRef-DOCUMENT",
31 | "relationshipType" : "DESCRIBES",
32 | "relatedSpdxElement" : "SPDXRef-Package"
33 | } ]
34 | }
--------------------------------------------------------------------------------
/test/json/input/test2.json:
--------------------------------------------------------------------------------
1 | {
2 | "SPDXID" : "SPDXRef-DOCUMENT",
3 | "spdxVersion" : "SPDX-2.3",
4 | "creationInfo" : {
5 | "created" : "2022-10-23T15:44:16Z",
6 | "creators" : [ "Person: Gary O'Neall", "Tool: spdx-maven-plugin" ],
7 | "licenseListVersion" : "3.18"
8 | },
9 | "name" : "examplemaven",
10 | "dataLicense" : "CC0-1.0",
11 | "documentDescribes" : [ "SPDXRef-example" ],
12 | "documentNamespace" : "http://spdx.org/documents/examplemaven-0.0.1",
13 | "packages" : [ {
14 | "SPDXID" : "SPDXRef-junit",
15 | "copyrightText" : "UNSPECIFIED",
16 | "description" : "JUnit is a regression testing framework written by Erich Gamma and Kent Beck. It is used by the developer who implements unit tests in Java.",
17 | "downloadLocation" : "NOASSERTION",
18 | "filesAnalyzed" : false,
19 | "homepage" : "http://junit.org",
20 | "licenseConcluded" : "NOASSERTION",
21 | "licenseDeclared" : "CPL-1.0",
22 | "name" : "JUnit",
23 | "originator" : "Organization: JUnit",
24 | "summary" : "JUnit is a regression testing framework written by Erich Gamma and Kent Beck. It is used by the developer who implements unit tests in Java.",
25 | "versionInfo" : "3.8.1"
26 | }, {
27 | "SPDXID" : "SPDXRef-log4jslf4jbinding",
28 | "copyrightText" : "UNSPECIFIED",
29 | "description" : "The Apache Log4j SLF4J API binding to Log4j 2 Core",
30 | "downloadLocation" : "NOASSERTION",
31 | "filesAnalyzed" : false,
32 | "licenseConcluded" : "NOASSERTION",
33 | "licenseDeclared" : "NOASSERTION",
34 | "name" : "Apache Log4j SLF4J Binding",
35 | "summary" : "The Apache Log4j SLF4J API binding to Log4j 2 Core"
36 | }, {
37 | "SPDXID" : "SPDXRef-log4jslf4jApi",
38 | "copyrightText" : "UNSPECIFIED",
39 | "description" : "The slf4j API",
40 | "downloadLocation" : "NOASSERTION",
41 | "filesAnalyzed" : false,
42 | "homepage" : "http://www.slf4j.org",
43 | "licenseConcluded" : "NOASSERTION",
44 | "licenseDeclared" : "NOASSERTION",
45 | "name" : "SLF4J API Module",
46 | "summary" : "The slf4j API"
47 | }, {
48 | "SPDXID" : "SPDXRef-log4jApi",
49 | "copyrightText" : "UNSPECIFIED",
50 | "description" : "The Apache Log4j API",
51 | "downloadLocation" : "NOASSERTION",
52 | "filesAnalyzed" : false,
53 | "licenseConcluded" : "NOASSERTION",
54 | "licenseDeclared" : "NOASSERTION",
55 | "name" : "Apache Log4j API",
56 | "summary" : "The Apache Log4j API"
57 | }, {
58 | "SPDXID" : "SPDXRef-log4jImpl",
59 | "copyrightText" : "UNSPECIFIED",
60 | "description" : "The Apache Log4j Implementation",
61 | "downloadLocation" : "NOASSERTION",
62 | "filesAnalyzed" : false,
63 | "licenseConcluded" : "NOASSERTION",
64 | "licenseDeclared" : "NOASSERTION",
65 | "name" : "Apache Log4j Core",
66 | "summary" : "The Apache Log4j Implementation"
67 | }, {
68 | "SPDXID" : "SPDXRef-example",
69 | "checksums" : [ {
70 | "algorithm" : "SHA1",
71 | "checksumValue" : "b8a7e6c75001e6d78625cfc9a3103bf121abf8b4"
72 | } ],
73 | "copyrightText" : "Copyright (c) 2022 Source Auditor Inc.",
74 | "description" : "This is a simple example Maven project created using the Maven quickstart archetype with one dependency added.",
75 | "downloadLocation" : "NOASSERTION",
76 | "filesAnalyzed" : true,
77 | "homepage" : "https://github.com/spdx/spdx-examples",
78 | "licenseConcluded" : "Apache-2.0",
79 | "licenseDeclared" : "Apache-2.0",
80 | "licenseInfoFromFiles" : [ "Apache-2.0" ],
81 | "name" : "examplemaven",
82 | "originator" : "Organization: Linux Foundation",
83 | "packageFileName" : "examplemaven-0.0.1.jar",
84 | "packageVerificationCode" : {
85 | "packageVerificationCodeValue" : "c12417def36d7804096521de4280721e5863e68b"
86 | },
87 | "primaryPackagePurpose" : "LIBRARY",
88 | "hasFiles" : [ "SPDXRef-appsource", "SPDXRef-apptest" ],
89 | "summary" : "This is a simple example Maven project created using the Maven quickstart archetype with one dependency added.",
90 | "supplier" : "Organization: SPDX",
91 | "versionInfo" : "0.0.1"
92 | } ],
93 | "files" : [ {
94 | "SPDXID" : "SPDXRef-appsource",
95 | "checksums" : [ {
96 | "algorithm" : "SHA1",
97 | "checksumValue" : "a6f47dbc7e4615058490055172fe0065c55f8fc5"
98 | } ],
99 | "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.",
100 | "fileContributors" : [ "Gary O'Neall" ],
101 | "fileName" : "./src/main/java/org/spdx/examplemaven/App.java",
102 | "fileTypes" : [ "SOURCE" ],
103 | "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0",
104 | "licenseConcluded" : "Apache-2.0",
105 | "licenseInfoInFiles" : [ "Apache-2.0" ],
106 | "noticeText" : "SPDX-License-Identifier: Apache-2.0\nCopyright (c) 2022 Source Auditor Inc."
107 | }, {
108 | "SPDXID" : "SPDXRef-apptest",
109 | "checksums" : [ {
110 | "algorithm" : "SHA1",
111 | "checksumValue" : "4b4df52d36588c8e9482d56eebc42336447f3dad"
112 | } ],
113 | "copyrightText" : "Copyright (c) 2020 Source Auditor Inc.",
114 | "fileContributors" : [ "Gary O'Neall" ],
115 | "fileName" : "./src/test/java/org/spdx/examplemaven/AppTest.java",
116 | "fileTypes" : [ "SOURCE" ],
117 | "licenseComments" : "This file contains SPDX-License-Identifiers for Apache-2.0",
118 | "licenseConcluded" : "Apache-2.0",
119 | "licenseInfoInFiles" : [ "Apache-2.0" ],
120 | "noticeText" : "SPDX-License-Identifier: Apache-2.0\nCopyright (c) 2022 Source Auditor Inc."
121 | } ],
122 | "relationships" : [ {
123 | "spdxElementId" : "SPDXRef-junit",
124 | "relationshipType" : "TEST_DEPENDENCY_OF",
125 | "relatedSpdxElement" : "SPDXRef-example",
126 | "comment" : "Relationship created based on Maven POM information"
127 | }, {
128 | "spdxElementId" : "SPDXRef-example",
129 | "relationshipType" : "DYNAMIC_LINK",
130 | "relatedSpdxElement" : "SPDXRef-log4jslf4jbinding",
131 | "comment" : "Relationship based on Maven POM file dependency information"
132 | }, {
133 | "spdxElementId" : "SPDXRef-example",
134 | "relationshipType" : "DYNAMIC_LINK",
135 | "relatedSpdxElement" : "SPDXRef-log4jslf4jApi",
136 | "comment" : "Relationship based on Maven POM file dependency information"
137 | }, {
138 | "spdxElementId" : "SPDXRef-example",
139 | "relationshipType" : "DYNAMIC_LINK",
140 | "relatedSpdxElement" : "SPDXRef-log4jApi",
141 | "comment" : "Relationship based on Maven POM file dependency information"
142 | }, {
143 | "spdxElementId" : "SPDXRef-example",
144 | "relationshipType" : "DYNAMIC_LINK",
145 | "relatedSpdxElement" : "SPDXRef-log4jImpl",
146 | "comment" : "Relationship based on Maven POM file dependency information"
147 | }, {
148 | "spdxElementId" : "SPDXRef-appsource",
149 | "relationshipType" : "GENERATES",
150 | "relatedSpdxElement" : "SPDXRef-example",
151 | "comment" : ""
152 | }, {
153 | "spdxElementId" : "SPDXRef-apptest",
154 | "relationshipType" : "TEST_CASE_OF",
155 | "relatedSpdxElement" : "SPDXRef-example",
156 | "comment" : ""
157 | } ]
158 | }
159 |
--------------------------------------------------------------------------------
/test/json/output/result.json:
--------------------------------------------------------------------------------
1 | {
2 | "SPDXID": "SPDXRef-DOCUMENT",
3 | "creationInfo": {
4 | "created": "2025-02-25T12:37:58Z",
5 | "creators": [
6 | "Organization: ci/cd build pipeline (test@mail.com)"
7 | ]
8 | },
9 | "dataLicense": "CC0-1.0",
10 | "name": "sample-sbom-json",
11 | "spdxVersion": "SPDX-2.3",
12 | "documentNamespace": "https://philips.example.com",
13 | "packages": [
14 | {
15 | "SPDXID": "SPDXRef-0",
16 | "downloadLocation": "NOASSERTION",
17 | "filesAnalyzed": true,
18 | "name": "sample-sbom-json",
19 | "versionInfo": "1"
20 | },
21 | {
22 | "SPDXID": "SPDXRef-Package",
23 | "checksums": [
24 | {
25 | "algorithm": "SHA1",
26 | "checksumValue": "e01bcba3c55fa7f0c82d08d0cdce9061b21bf32f"
27 | }
28 | ],
29 | "downloadLocation": "https://github.com/spdx/tools-java/releases/download/v1.1.5/tools-java-1.1.5.zip",
30 | "externalRefs": [
31 | {
32 | "referenceCategory": "PACKAGE_MANAGER",
33 | "referenceLocator": "pkg:github/spdx/tools-java@2235d5d7f7fe46ce1e0d54b7831c5681633b25cc",
34 | "referenceType": "purl"
35 | }
36 | ],
37 | "filesAnalyzed": false,
38 | "name": "tools-java",
39 | "packageFileName": "tools-java-1.1.5.zip",
40 | "supplier": "Organization: SPDX (Spdx-tech@lists.spdx.org)",
41 | "versionInfo": "1.5.1"
42 | },
43 | {
44 | "SPDXID": "SPDXRef-junit",
45 | "copyrightText": "UNSPECIFIED",
46 | "description": "JUnit is a regression testing framework written by Erich Gamma and Kent Beck. It is used by the developer who implements unit tests in Java.",
47 | "downloadLocation": "NOASSERTION",
48 | "filesAnalyzed": false,
49 | "homepage": "http://junit.org",
50 | "licenseConcluded": "NOASSERTION",
51 | "licenseDeclared": "CPL-1.0",
52 | "name": "JUnit",
53 | "originator": "Organization: JUnit",
54 | "summary": "JUnit is a regression testing framework written by Erich Gamma and Kent Beck. It is used by the developer who implements unit tests in Java.",
55 | "versionInfo": "3.8.1"
56 | },
57 | {
58 | "SPDXID": "SPDXRef-log4jslf4jbinding",
59 | "copyrightText": "UNSPECIFIED",
60 | "description": "The Apache Log4j SLF4J API binding to Log4j 2 Core",
61 | "downloadLocation": "NOASSERTION",
62 | "filesAnalyzed": false,
63 | "licenseConcluded": "NOASSERTION",
64 | "licenseDeclared": "NOASSERTION",
65 | "name": "Apache Log4j SLF4J Binding",
66 | "summary": "The Apache Log4j SLF4J API binding to Log4j 2 Core"
67 | },
68 | {
69 | "SPDXID": "SPDXRef-log4jslf4jApi",
70 | "copyrightText": "UNSPECIFIED",
71 | "description": "The slf4j API",
72 | "downloadLocation": "NOASSERTION",
73 | "filesAnalyzed": false,
74 | "homepage": "http://www.slf4j.org",
75 | "licenseConcluded": "NOASSERTION",
76 | "licenseDeclared": "NOASSERTION",
77 | "name": "SLF4J API Module",
78 | "summary": "The slf4j API"
79 | },
80 | {
81 | "SPDXID": "SPDXRef-log4jApi",
82 | "copyrightText": "UNSPECIFIED",
83 | "description": "The Apache Log4j API",
84 | "downloadLocation": "NOASSERTION",
85 | "filesAnalyzed": false,
86 | "licenseConcluded": "NOASSERTION",
87 | "licenseDeclared": "NOASSERTION",
88 | "name": "Apache Log4j API",
89 | "summary": "The Apache Log4j API"
90 | },
91 | {
92 | "SPDXID": "SPDXRef-log4jImpl",
93 | "copyrightText": "UNSPECIFIED",
94 | "description": "The Apache Log4j Implementation",
95 | "downloadLocation": "NOASSERTION",
96 | "filesAnalyzed": false,
97 | "licenseConcluded": "NOASSERTION",
98 | "licenseDeclared": "NOASSERTION",
99 | "name": "Apache Log4j Core",
100 | "summary": "The Apache Log4j Implementation"
101 | },
102 | {
103 | "SPDXID": "SPDXRef-example",
104 | "checksums": [
105 | {
106 | "algorithm": "SHA1",
107 | "checksumValue": "b8a7e6c75001e6d78625cfc9a3103bf121abf8b4"
108 | }
109 | ],
110 | "copyrightText": "Copyright (c) 2022 Source Auditor Inc.",
111 | "description": "This is a simple example Maven project created using the Maven quickstart archetype with one dependency added.",
112 | "downloadLocation": "NOASSERTION",
113 | "filesAnalyzed": true,
114 | "homepage": "https://github.com/spdx/spdx-examples",
115 | "licenseConcluded": "Apache-2.0",
116 | "licenseDeclared": "Apache-2.0",
117 | "licenseInfoFromFiles": [
118 | "Apache-2.0"
119 | ],
120 | "name": "examplemaven",
121 | "originator": "Organization: Linux Foundation",
122 | "packageFileName": "examplemaven-0.0.1.jar",
123 | "packageVerificationCode": {
124 | "packageVerificationCodeValue": "c12417def36d7804096521de4280721e5863e68b"
125 | },
126 | "primaryPackagePurpose": "LIBRARY",
127 | "summary": "This is a simple example Maven project created using the Maven quickstart archetype with one dependency added.",
128 | "supplier": "Organization: SPDX",
129 | "versionInfo": "0.0.1"
130 | }
131 | ],
132 | "files": [
133 | {
134 | "SPDXID": "SPDXRef-appsource",
135 | "checksums": [
136 | {
137 | "algorithm": "SHA1",
138 | "checksumValue": "a6f47dbc7e4615058490055172fe0065c55f8fc5"
139 | }
140 | ],
141 | "copyrightText": "Copyright (c) 2020 Source Auditor Inc.",
142 | "fileContributors": [
143 | "Gary O'Neall"
144 | ],
145 | "fileName": "./src/main/java/org/spdx/examplemaven/App.java",
146 | "fileTypes": [
147 | "SOURCE"
148 | ],
149 | "licenseComments": "This file contains SPDX-License-Identifiers for Apache-2.0",
150 | "licenseConcluded": "Apache-2.0",
151 | "licenseInfoInFiles": [
152 | "Apache-2.0"
153 | ],
154 | "noticeText": "SPDX-License-Identifier: Apache-2.0\nCopyright (c) 2022 Source Auditor Inc."
155 | },
156 | {
157 | "SPDXID": "SPDXRef-apptest",
158 | "checksums": [
159 | {
160 | "algorithm": "SHA1",
161 | "checksumValue": "4b4df52d36588c8e9482d56eebc42336447f3dad"
162 | }
163 | ],
164 | "copyrightText": "Copyright (c) 2020 Source Auditor Inc.",
165 | "fileContributors": [
166 | "Gary O'Neall"
167 | ],
168 | "fileName": "./src/test/java/org/spdx/examplemaven/AppTest.java",
169 | "fileTypes": [
170 | "SOURCE"
171 | ],
172 | "licenseComments": "This file contains SPDX-License-Identifiers for Apache-2.0",
173 | "licenseConcluded": "Apache-2.0",
174 | "licenseInfoInFiles": [
175 | "Apache-2.0"
176 | ],
177 | "noticeText": "SPDX-License-Identifier: Apache-2.0\nCopyright (c) 2022 Source Auditor Inc."
178 | }
179 | ],
180 | "relationships": [
181 | {
182 | "spdxElementId": "SPDXRef-DOCUMENT",
183 | "relatedSpdxElement": "SPDXRef-0",
184 | "relationshipType": "DESCRIBES"
185 | },
186 | {
187 | "spdxElementId": "SPDXRef-junit",
188 | "comment": "Relationship created based on Maven POM information",
189 | "relatedSpdxElement": "SPDXRef-example",
190 | "relationshipType": "TEST_DEPENDENCY_OF"
191 | },
192 | {
193 | "spdxElementId": "SPDXRef-example",
194 | "comment": "Relationship based on Maven POM file dependency information",
195 | "relatedSpdxElement": "SPDXRef-log4jslf4jbinding",
196 | "relationshipType": "DYNAMIC_LINK"
197 | },
198 | {
199 | "spdxElementId": "SPDXRef-example",
200 | "comment": "Relationship based on Maven POM file dependency information",
201 | "relatedSpdxElement": "SPDXRef-log4jslf4jApi",
202 | "relationshipType": "DYNAMIC_LINK"
203 | },
204 | {
205 | "spdxElementId": "SPDXRef-example",
206 | "comment": "Relationship based on Maven POM file dependency information",
207 | "relatedSpdxElement": "SPDXRef-log4jApi",
208 | "relationshipType": "DYNAMIC_LINK"
209 | },
210 | {
211 | "spdxElementId": "SPDXRef-example",
212 | "comment": "Relationship based on Maven POM file dependency information",
213 | "relatedSpdxElement": "SPDXRef-log4jImpl",
214 | "relationshipType": "DYNAMIC_LINK"
215 | },
216 | {
217 | "spdxElementId": "SPDXRef-appsource",
218 | "comment": "",
219 | "relatedSpdxElement": "SPDXRef-example",
220 | "relationshipType": "GENERATES"
221 | },
222 | {
223 | "spdxElementId": "SPDXRef-apptest",
224 | "comment": "",
225 | "relatedSpdxElement": "SPDXRef-example",
226 | "relationshipType": "TEST_CASE_OF"
227 | },
228 | {
229 | "spdxElementId": "SPDXRef-example",
230 | "relatedSpdxElement": "SPDXRef-appsource",
231 | "relationshipType": "CONTAINS"
232 | },
233 | {
234 | "spdxElementId": "SPDXRef-example",
235 | "relatedSpdxElement": "SPDXRef-apptest",
236 | "relationshipType": "CONTAINS"
237 | }
238 | ]
239 | }
--------------------------------------------------------------------------------
/test/spdx/input/test1.spdx:
--------------------------------------------------------------------------------
1 | SPDXVersion: SPDX-2.2
2 | DataLicense: CC0-1.0
3 | SPDXID: SPDXRef-DOCUMENT
4 | DocumentName: Test document for SPDX tag value format
5 | DocumentNamespace: https://spdx.org/510f728d-05b2-4e32-8501-550587c39a2a
6 | LicenseListVersion: 3.8
7 |
8 | ## Creation information
9 | Creator: Organization: Test
10 | Creator: Tool: null
11 | Created: 2023-05-12T18:30:22Z
12 | CreatorComment: This SPDX file was generated by null.
13 | DocumentComment: Test
14 |
15 | ## Start of pub package 'glob' version 1.2.0
16 | PackageName: glob
17 | SPDXID: SPDXRef-A9
18 | PackageVersion: 1.2.0
19 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/glob/glob@1.2.0
20 | PackageSupplier: Organization: glob
21 | PackageOriginator: NOASSERTION
22 | PackageDownloadLocation: git+https://github.com/dart-lang/glob.git@1.2.0
23 | FilesAnalyzed: true
24 | PackageHomePage: https://github.com/dart-lang/glob
25 | PackageLicenseConcluded: BSD-3-Clause
26 | PackageLicenseDeclared: NOASSERTION
27 | PackageLicenseInfoFromFiles: BSD-3-Clause
28 | PackageCopyrightText: NOASSERTION
29 | PackageDescription: Bash-style filename globbing.
30 | Relationship: SPDXRef-A9 DYNAMIC_LINK SPDXRef-A4
31 | Relationship: SPDXRef-A9 DYNAMIC_LINK SPDXRef-A15
32 | Relationship: SPDXRef-A9 DYNAMIC_LINK SPDXRef-A13
33 | Relationship: SPDXRef-A9 DYNAMIC_LINK SPDXRef-A17
34 |
35 | ## Start of pub package 'archive' version 2.0.13
36 | PackageName: archive
37 | SPDXID: SPDXRef-A2
38 | PackageVersion: 2.0.13
39 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/archive/archive@2.0.13
40 | PackageSupplier: Organization: archive
41 | PackageOriginator: NOASSERTION
42 | PackageDownloadLocation: git+https://github.com/brendan-duncan/archive.git@2.0.13
43 | FilesAnalyzed: true
44 | PackageHomePage: https://github.com/brendan-duncan/archive
45 | PackageLicenseConcluded: Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND MIT
46 | PackageLicenseDeclared: NOASSERTION
47 | PackageLicenseInfoFromFiles: Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND MIT
48 | PackageCopyrightText: NOASSERTION
49 | PackageDescription: Provides encoders and decoders for various archive and compression formats such as zip, tar, bzip2, gzip, and zlib.
50 | Relationship: SPDXRef-A2 DYNAMIC_LINK SPDXRef-A8
51 |
52 | ## Start of pub package 'args' version 1.6.0
53 | PackageName: args
54 | SPDXID: SPDXRef-A3
55 | PackageVersion: 1.6.0
56 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/args/args@1.6.0
57 | PackageSupplier: Organization: args
58 | PackageOriginator: NOASSERTION
59 | PackageDownloadLocation: git+https://github.com/dart-lang/args.git@1.6.0
60 | FilesAnalyzed: true
61 | PackageHomePage: https://github.com/dart-lang/args
62 | PackageLicenseConcluded: BSD-3-Clause
63 | PackageLicenseDeclared: NOASSERTION
64 | PackageLicenseInfoFromFiles: BSD-3-Clause
65 | PackageCopyrightText: NOASSERTION
66 | PackageDescription: Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.
67 |
68 | ## Start of pub package 'meta' version 1.1.8
69 | PackageName: meta
70 | SPDXID: SPDXRef-A11
71 | PackageVersion: 1.1.8
72 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/meta/meta@1.1.8
73 | PackageSupplier: Organization: meta
74 | PackageOriginator: NOASSERTION
75 | PackageDownloadLocation: git+https://github.com/dart-lang/sdk.git@master#pkg%2Fmeta
76 | FilesAnalyzed: true
77 | PackageHomePage: https://github.com/dart-lang/sdk/tree/master/pkg/meta
78 | PackageLicenseConcluded: BSD-3-Clause
79 | PackageLicenseDeclared: NOASSERTION
80 | PackageLicenseInfoFromFiles: BSD-3-Clause
81 | PackageCopyrightText: NOASSERTION
82 | PackageDescription: This library contains the definitions of annotations that provide additional semantic information about the program being annotated. These annotations are intended to be used by tools to provide a better user experience.
83 |
84 |
85 | ## Start of pub package 'string_scanner' version 1.0.5
86 | PackageName: string_scanner
87 | SPDXID: SPDXRef-A17
88 | PackageVersion: 1.0.5
89 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/string_scanner/string_scanner@1.0.5
90 | PackageSupplier: Organization: string_scanner
91 | PackageOriginator: NOASSERTION
92 | PackageDownloadLocation: git+https://github.com/dart-lang/string_scanner.git@1.0.5
93 | FilesAnalyzed: true
94 | PackageHomePage: https://github.com/dart-lang/string_scanner
95 | PackageLicenseConcluded: BSD-3-Clause
96 | PackageLicenseDeclared: NOASSERTION
97 | PackageLicenseInfoFromFiles: BSD-3-Clause
98 | PackageCopyrightText: NOASSERTION
99 | PackageDescription: A class for parsing strings using a sequence of patterns.
100 | Relationship: SPDXRef-A17 DYNAMIC_LINK SPDXRef-A11
101 | Relationship: SPDXRef-A17 DYNAMIC_LINK SPDXRef-A16
102 | Relationship: SPDXRef-A17 DYNAMIC_LINK SPDXRef-A5
103 |
104 | ## Start of pub package 'node_interop' version 1.1.1
105 | PackageName: node_interop
106 | SPDXID: SPDXRef-A12
107 | PackageVersion: 1.1.1
108 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/node_interop/node_interop@1.1.1
109 | PackageSupplier: Organization: node_interop
110 | PackageOriginator: NOASSERTION
111 | PackageDownloadLocation: git+https://github.com/pulyaevskiy/node-interop.git@1.1.1
112 | FilesAnalyzed: true
113 | PackageHomePage: https://github.com/pulyaevskiy/node-interop
114 | PackageLicenseConcluded: BSD-3-Clause
115 | PackageLicenseDeclared: NOASSERTION
116 | PackageLicenseInfoFromFiles: BSD-3-Clause
117 | PackageCopyrightText: NOASSERTION
118 | PackageDescription: Provides Dart bindings and utility functions for core Node.js modules.
119 | Relationship: SPDXRef-A12 DYNAMIC_LINK SPDXRef-A10
120 |
121 | ## Start of pub package 'charcode' version 1.1.3
122 | PackageName: charcode
123 | SPDXID: SPDXRef-A5
124 | PackageVersion: 1.1.3
125 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/charcode/charcode@1.1.3
126 | PackageSupplier: Organization: charcode
127 | PackageOriginator: NOASSERTION
128 | PackageDownloadLocation: git+https://github.com/dart-lang/charcode.git@1.1.3
129 | FilesAnalyzed: true
130 | PackageHomePage: https://github.com/dart-lang/charcode
131 | PackageLicenseConcluded: BSD-3-Clause
132 | PackageLicenseDeclared: NOASSERTION
133 | PackageLicenseInfoFromFiles: BSD-3-Clause
134 | PackageCopyrightText: NOASSERTION
135 | PackageDescription: Constants for ASCII and common non-ASCII character codes represented by top-level constants.
136 |
137 | ## Start of pub package 'pedantic' version 1.9.0
138 | PackageName: pedantic
139 | SPDXID: SPDXRef-A15
140 | PackageVersion: 1.9.0
141 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/pedantic/pedantic@1.9.0
142 | PackageSupplier: Organization: pedantic
143 | PackageOriginator: NOASSERTION
144 | PackageDownloadLocation: git+https://github.com/dart-lang/pedantic.git@1.9.0
145 | FilesAnalyzed: true
146 | PackageHomePage: https://github.com/dart-lang/pedantic
147 | PackageLicenseConcluded: BSD-3-Clause
148 | PackageLicenseDeclared: NOASSERTION
149 | PackageLicenseInfoFromFiles: BSD-3-Clause
150 | PackageCopyrightText: NOASSERTION
151 | PackageDescription: How to get the most value from Dart static analysis.
152 |
153 | ## Start of pub package 'convert' version 2.1.1
154 | PackageName: convert
155 | SPDXID: SPDXRef-A7
156 | PackageVersion: 2.1.1
157 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/convert/convert@2.1.1
158 | PackageSupplier: Organization: convert
159 | PackageOriginator: NOASSERTION
160 | PackageDownloadLocation: git+https://github.com/dart-lang/convert.git@2.1.1
161 | FilesAnalyzed: true
162 | PackageHomePage: https://github.com/dart-lang/convert
163 | PackageLicenseConcluded: BSD-3-Clause
164 | PackageLicenseDeclared: NOASSERTION
165 | PackageLicenseInfoFromFiles: BSD-3-Clause
166 | PackageCopyrightText: NOASSERTION
167 | PackageDescription: Utilities for converting between data representations.
168 | Relationship: SPDXRef-A7 DYNAMIC_LINK SPDXRef-A19
169 |
170 | ## Start of pub package 'source_span' version 1.7.0
171 | PackageName: source_span
172 | SPDXID: SPDXRef-A16
173 | PackageVersion: 1.7.0
174 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/source_span/source_span@1.7.0
175 | PackageSupplier: Organization: source_span
176 | PackageOriginator: NOASSERTION
177 | PackageDownloadLocation: git+https://github.com/dart-lang/source_span.git@1.7.0
178 | FilesAnalyzed: true
179 | PackageHomePage: https://github.com/dart-lang/source_span
180 | PackageLicenseConcluded: BSD-3-Clause
181 | PackageLicenseDeclared: NOASSERTION
182 | PackageLicenseInfoFromFiles: BSD-3-Clause
183 | PackageCopyrightText: NOASSERTION
184 | PackageDescription: A library for identifying source spans and locations.
185 | Relationship: SPDXRef-A16 DYNAMIC_LINK SPDXRef-A18
186 |
187 | ## Start of pub package 'js' version 0.6.1+1
188 | PackageName: js
189 | SPDXID: SPDXRef-A10
190 | PackageVersion: 0.6.1+1
191 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/js/js@0.6.1%2B1
192 | PackageSupplier: Organization: js
193 | PackageOriginator: NOASSERTION
194 | PackageDownloadLocation: git+https://github.com/dart-lang/sdk.git@master#pkg%2Fjs
195 | FilesAnalyzed: true
196 | PackageHomePage: https://github.com/dart-lang/sdk/tree/master/pkg/js
197 | PackageLicenseConcluded: BSD-3-Clause
198 | PackageLicenseDeclared: NOASSERTION
199 | PackageLicenseInfoFromFiles: BSD-3-Clause
200 | PackageCopyrightText: NOASSERTION
201 | PackageDescription: Access JavaScript from Dart.
202 |
203 | ## Start of pub package 'test' version
204 | PackageName: Test
205 | SPDXID: SPDXRef-A1
206 | PackageVersion:
207 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/test/test@
208 | PackageSupplier: Organization: test
209 | PackageOriginator: NOASSERTION
210 | PackageDownloadLocation: git+https://github.com/dart-lang/sdk.git@master#pkg%2Fjs
211 | FilesAnalyzed: true
212 | PackageHomePage: NOASSERTION
213 | PackageLicenseConcluded: NOASSERTION
214 | PackageLicenseDeclared: NOASSERTION
215 | PackageLicenseInfoFromFiles: NOASSERTION
216 | PackageCopyrightText: NOASSERTION
217 | Relationship: SPDXRef-A1 DYNAMIC_LINK SPDXRef-A9
218 | Relationship: SPDXRef-A1 DYNAMIC_LINK SPDXRef-A3
219 | Relationship: SPDXRef-A1 DYNAMIC_LINK SPDXRef-A2
220 | Relationship: SPDXRef-A1 DYNAMIC_LINK SPDXRef-A14
221 |
222 | ## Start of pub package 'term_glyph' version 1.1.0
223 | PackageName: term_glyph
224 | SPDXID: SPDXRef-A18
225 | PackageVersion: 1.1.0
226 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/term_glyph/term_glyph@1.1.0
227 | PackageSupplier: Organization: term_glyph
228 | PackageOriginator: NOASSERTION
229 | PackageDownloadLocation: git+https://github.com/dart-lang/term_glyph.git@1.1.0
230 | FilesAnalyzed: true
231 | PackageHomePage: https://github.com/dart-lang/term_glyph
232 | PackageLicenseConcluded: BSD-3-Clause
233 | PackageLicenseDeclared: NOASSERTION
234 | PackageLicenseInfoFromFiles: BSD-3-Clause
235 | PackageCopyrightText: NOASSERTION
236 | PackageDescription: Useful Unicode glyphs and ASCII substitutes.
237 |
238 | ## Start of pub package 'async' version 2.4.1
239 | PackageName: async
240 | SPDXID: SPDXRef-A4
241 | PackageVersion: 2.4.1
242 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/async/async@2.4.1
243 | PackageSupplier: Organization: async
244 | PackageOriginator: NOASSERTION
245 | PackageDownloadLocation: git+https://github.com/dart-lang/async.git@2.4.1
246 | FilesAnalyzed: true
247 | PackageHomePage: https://www.github.com/dart-lang/async
248 | PackageLicenseConcluded: BSD-3-Clause
249 | PackageLicenseDeclared: NOASSERTION
250 | PackageLicenseInfoFromFiles: BSD-3-Clause
251 | PackageCopyrightText: NOASSERTION
252 | PackageDescription: Utility functions and classes related to the 'dart:async' library.
253 | Relationship: SPDXRef-A4 DYNAMIC_LINK SPDXRef-A6
254 |
255 | ## Start of pub package 'path' version 1.7.0
256 | PackageName: path
257 | SPDXID: SPDXRef-A14
258 | PackageVersion: 1.7.0
259 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/path/path@1.7.0
260 | PackageSupplier: Organization: path
261 | PackageOriginator: NOASSERTION
262 | PackageDownloadLocation: git+https://github.com/dart-lang/path.git@1.7.0
263 | FilesAnalyzed: true
264 | PackageHomePage: https://github.com/dart-lang/path
265 | PackageLicenseConcluded: BSD-3-Clause
266 | PackageLicenseDeclared: NOASSERTION
267 | PackageLicenseInfoFromFiles: BSD-3-Clause
268 | PackageCopyrightText: NOASSERTION
269 | PackageDescription: A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.
270 |
271 | ## Start of pub package 'crypto' version 2.1.4
272 | PackageName: crypto
273 | SPDXID: SPDXRef-A8
274 | PackageVersion: 2.1.4
275 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/crypto/crypto@2.1.4
276 | PackageSupplier: Organization: crypto
277 | PackageOriginator: NOASSERTION
278 | PackageDownloadLocation: git+https://github.com/dart-lang/crypto.git@2.1.4
279 | FilesAnalyzed: true
280 | PackageHomePage: https://www.github.com/dart-lang/crypto
281 | PackageLicenseConcluded: BSD-3-Clause
282 | PackageLicenseDeclared: NOASSERTION
283 | PackageLicenseInfoFromFiles: BSD-3-Clause
284 | PackageCopyrightText: NOASSERTION
285 | PackageDescription: Library of cryptographic functions.
286 | Relationship: SPDXRef-A8 DYNAMIC_LINK SPDXRef-A7
287 |
288 | ## Start of pub package 'collection' version 1.14.12
289 | PackageName: collection
290 | SPDXID: SPDXRef-A6
291 | PackageVersion: 1.14.12
292 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/collection/collection@1.14.12
293 | PackageSupplier: Organization: collection
294 | PackageOriginator: NOASSERTION
295 | PackageDownloadLocation: git+https://github.com/dart-lang/collection.git@1.14.12
296 | FilesAnalyzed: true
297 | PackageHomePage: https://www.github.com/dart-lang/collection
298 | PackageLicenseConcluded: BSD-3-Clause
299 | PackageLicenseDeclared: NOASSERTION
300 | PackageLicenseInfoFromFiles: BSD-3-Clause
301 | PackageCopyrightText: NOASSERTION
302 | PackageDescription: Collections and utilities functions and classes related to collections.
303 |
304 | ## Start of pub package 'typed_data' version 1.1.6
305 | PackageName: typed_data
306 | SPDXID: SPDXRef-A19
307 | PackageVersion: 1.1.6
308 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/typed_data/typed_data@1.1.6
309 | PackageSupplier: Organization: typed_data
310 | PackageOriginator: NOASSERTION
311 | PackageDownloadLocation: git+https://github.com/dart-lang/typed_data.git@1.1.6
312 | FilesAnalyzed: true
313 | PackageHomePage: https://github.com/dart-lang/typed_data
314 | PackageLicenseConcluded: BSD-3-Clause
315 | PackageLicenseDeclared: NOASSERTION
316 | PackageLicenseInfoFromFiles: BSD-3-Clause
317 | PackageCopyrightText: NOASSERTION
318 | PackageDescription: Utility functions and classes related to the 'dart:typed_data' library.
319 |
320 | ## Start of pub package 'node_io' version 1.1.1
321 | PackageName: node_io
322 | SPDXID: SPDXRef-A13
323 | PackageVersion: 1.1.1
324 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/node_io/node_io@1.1.1
325 | PackageSupplier: Organization: node_io
326 | PackageOriginator: NOASSERTION
327 | PackageDownloadLocation: git+https://github.com/pulyaevskiy/node-interop.git@1.1.1
328 | FilesAnalyzed: true
329 | PackageHomePage: https://github.com/pulyaevskiy/node-interop
330 | PackageLicenseConcluded: BSD-3-Clause
331 | PackageLicenseDeclared: NOASSERTION
332 | PackageLicenseInfoFromFiles: BSD-3-Clause
333 | PackageCopyrightText: NOASSERTION
334 | PackageDescription: Like dart:io but with Node.js.
335 | Relationship: SPDXRef-A13 DYNAMIC_LINK SPDXRef-A12
336 |
337 |
--------------------------------------------------------------------------------
/test/spdx/input/test2.spdx:
--------------------------------------------------------------------------------
1 | SPDXVersion: SPDX-2.2
2 | DataLicense: CC0-1.0
3 | SPDXID: SPDXRef-DOCUMENT
4 | DocumentName: Test document for SPDX tag value format
5 | DocumentNamespace: https://spdx.org/510f728d-05b2-4e32-8501-550587c39a2a
6 | LicenseListVersion: 3.8
7 |
8 | ## Creation information
9 | Creator: Organization: Test
10 | Creator: Tool: null
11 | Created: 2023-05-12T18:30:22Z
12 | CreatorComment: This SPDX file was generated by null.
13 | DocumentComment: Test
14 |
15 | ## Start of pub package 'glob' version 1.2.0
16 | PackageName: glob
17 | SPDXID: SPDXRef-9
18 | PackageVersion: 1.2.0
19 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/glob/glob@1.2.0
20 | PackageSupplier: Organization: glob
21 | PackageOriginator: NOASSERTION
22 | PackageDownloadLocation: git+https://github.com/dart-lang/glob.git@1.2.0
23 | FilesAnalyzed: true
24 | PackageHomePage: https://github.com/dart-lang/glob
25 | PackageLicenseConcluded: BSD-3-Clause
26 | PackageLicenseDeclared: NOASSERTION
27 | PackageLicenseInfoFromFiles: BSD-3-Clause
28 | PackageCopyrightText: NOASSERTION
29 | PackageDescription: Bash-style filename globbing.
30 | Relationship: SPDXRef-9 DYNAMIC_LINK SPDXRef-4
31 | Relationship: SPDXRef-9 DYNAMIC_LINK SPDXRef-15
32 | Relationship: SPDXRef-9 DYNAMIC_LINK SPDXRef-13
33 | Relationship: SPDXRef-9 DYNAMIC_LINK SPDXRef-17
34 |
35 | ## Start of pub package 'archive' version 2.0.13
36 | PackageName: archive
37 | SPDXID: SPDXRef-2
38 | PackageVersion: 2.0.13
39 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/archive/archive@2.0.13
40 | PackageSupplier: Organization: archive
41 | PackageOriginator: NOASSERTION
42 | PackageDownloadLocation: git+https://github.com/brendan-duncan/archive.git@2.0.13
43 | FilesAnalyzed: true
44 | PackageHomePage: https://github.com/brendan-duncan/archive
45 | PackageLicenseConcluded: Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND MIT
46 | PackageLicenseDeclared: NOASSERTION
47 | PackageLicenseInfoFromFiles: Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND MIT
48 | PackageCopyrightText: NOASSERTION
49 | PackageDescription: Provides encoders and decoders for various archive and compression formats such as zip, tar, bzip2, gzip, and zlib.
50 | Relationship: SPDXRef-2 DYNAMIC_LINK SPDXRef-8
51 |
52 | ## Start of pub package 'args' version 1.6.0
53 | PackageName: args
54 | SPDXID: SPDXRef-3
55 | PackageVersion: 1.6.0
56 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/args/args@1.6.0
57 | PackageSupplier: Organization: args
58 | PackageOriginator: NOASSERTION
59 | PackageDownloadLocation: git+https://github.com/dart-lang/args.git@1.6.0
60 | FilesAnalyzed: true
61 | PackageHomePage: https://github.com/dart-lang/args
62 | PackageLicenseConcluded: BSD-3-Clause
63 | PackageLicenseDeclared: NOASSERTION
64 | PackageLicenseInfoFromFiles: BSD-3-Clause
65 | PackageCopyrightText: NOASSERTION
66 | PackageDescription: Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.
67 |
68 | ## Start of pub package 'meta' version 1.1.8
69 | PackageName: meta
70 | SPDXID: SPDXRef-11
71 | PackageVersion: 1.1.8
72 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/meta/meta@1.1.8
73 | PackageSupplier: Organization: meta
74 | PackageOriginator: NOASSERTION
75 | PackageDownloadLocation: git+https://github.com/dart-lang/sdk.git@master#pkg%2Fmeta
76 | FilesAnalyzed: true
77 | PackageHomePage: https://github.com/dart-lang/sdk/tree/master/pkg/meta
78 | PackageLicenseConcluded: BSD-3-Clause
79 | PackageLicenseDeclared: NOASSERTION
80 | PackageLicenseInfoFromFiles: BSD-3-Clause
81 | PackageCopyrightText: NOASSERTION
82 | PackageDescription: This library contains the definitions of annotations that provide additional semantic information about the program being annotated. These annotations are intended to be used by tools to provide a better user experience.
83 |
84 |
85 | ## Start of pub package 'string_scanner' version 1.0.5
86 | PackageName: string_scanner
87 | SPDXID: SPDXRef-17
88 | PackageVersion: 1.0.5
89 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/string_scanner/string_scanner@1.0.5
90 | PackageSupplier: Organization: string_scanner
91 | PackageOriginator: NOASSERTION
92 | PackageDownloadLocation: git+https://github.com/dart-lang/string_scanner.git@1.0.5
93 | FilesAnalyzed: true
94 | PackageHomePage: https://github.com/dart-lang/string_scanner
95 | PackageLicenseConcluded: BSD-3-Clause
96 | PackageLicenseDeclared: NOASSERTION
97 | PackageLicenseInfoFromFiles: BSD-3-Clause
98 | PackageCopyrightText: NOASSERTION
99 | PackageDescription: A class for parsing strings using a sequence of patterns.
100 | Relationship: SPDXRef-17 DYNAMIC_LINK SPDXRef-11
101 | Relationship: SPDXRef-17 DYNAMIC_LINK SPDXRef-16
102 | Relationship: SPDXRef-17 DYNAMIC_LINK SPDXRef-5
103 |
104 | ## Start of pub package 'node_interop' version 1.1.1
105 | PackageName: node_interop
106 | SPDXID: SPDXRef-12
107 | PackageVersion: 1.1.1
108 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/node_interop/node_interop@1.1.1
109 | PackageSupplier: Organization: node_interop
110 | PackageOriginator: NOASSERTION
111 | PackageDownloadLocation: git+https://github.com/pulyaevskiy/node-interop.git@1.1.1
112 | FilesAnalyzed: true
113 | PackageHomePage: https://github.com/pulyaevskiy/node-interop
114 | PackageLicenseConcluded: BSD-3-Clause
115 | PackageLicenseDeclared: NOASSERTION
116 | PackageLicenseInfoFromFiles: BSD-3-Clause
117 | PackageCopyrightText: NOASSERTION
118 | PackageDescription: Provides Dart bindings and utility functions for core Node.js modules.
119 | Relationship: SPDXRef-12 DYNAMIC_LINK SPDXRef-10
120 |
121 | ## Start of pub package 'charcode' version 1.1.3
122 | PackageName: charcode
123 | SPDXID: SPDXRef-5
124 | PackageVersion: 1.1.3
125 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/charcode/charcode@1.1.3
126 | PackageSupplier: Organization: charcode
127 | PackageOriginator: NOASSERTION
128 | PackageDownloadLocation: git+https://github.com/dart-lang/charcode.git@1.1.3
129 | FilesAnalyzed: true
130 | PackageHomePage: https://github.com/dart-lang/charcode
131 | PackageLicenseConcluded: BSD-3-Clause
132 | PackageLicenseDeclared: NOASSERTION
133 | PackageLicenseInfoFromFiles: BSD-3-Clause
134 | PackageCopyrightText: NOASSERTION
135 | PackageDescription: Constants for ASCII and common non-ASCII character codes represented by top-level constants.
136 |
137 | ## Start of pub package 'pedantic' version 1.9.0
138 | PackageName: pedantic
139 | SPDXID: SPDXRef-15
140 | PackageVersion: 1.9.0
141 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/pedantic/pedantic@1.9.0
142 | PackageSupplier: Organization: pedantic
143 | PackageOriginator: NOASSERTION
144 | PackageDownloadLocation: git+https://github.com/dart-lang/pedantic.git@1.9.0
145 | FilesAnalyzed: true
146 | PackageHomePage: https://github.com/dart-lang/pedantic
147 | PackageLicenseConcluded: BSD-3-Clause
148 | PackageLicenseDeclared: NOASSERTION
149 | PackageLicenseInfoFromFiles: BSD-3-Clause
150 | PackageCopyrightText: NOASSERTION
151 | PackageDescription: How to get the most value from Dart static analysis.
152 |
153 | ## Start of pub package 'convert' version 2.1.1
154 | PackageName: convert
155 | SPDXID: SPDXRef-7
156 | PackageVersion: 2.1.1
157 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/convert/convert@2.1.1
158 | PackageSupplier: Organization: convert
159 | PackageOriginator: NOASSERTION
160 | PackageDownloadLocation: git+https://github.com/dart-lang/convert.git@2.1.1
161 | FilesAnalyzed: true
162 | PackageHomePage: https://github.com/dart-lang/convert
163 | PackageLicenseConcluded: BSD-3-Clause
164 | PackageLicenseDeclared: NOASSERTION
165 | PackageLicenseInfoFromFiles: BSD-3-Clause
166 | PackageCopyrightText: NOASSERTION
167 | PackageDescription: Utilities for converting between data representations.
168 | Relationship: SPDXRef-7 DYNAMIC_LINK SPDXRef-19
169 |
170 | ## Start of pub package 'source_span' version 1.7.0
171 | PackageName: source_span
172 | SPDXID: SPDXRef-16
173 | PackageVersion: 1.7.0
174 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/source_span/source_span@1.7.0
175 | PackageSupplier: Organization: source_span
176 | PackageOriginator: NOASSERTION
177 | PackageDownloadLocation: git+https://github.com/dart-lang/source_span.git@1.7.0
178 | FilesAnalyzed: true
179 | PackageHomePage: https://github.com/dart-lang/source_span
180 | PackageLicenseConcluded: BSD-3-Clause
181 | PackageLicenseDeclared: NOASSERTION
182 | PackageLicenseInfoFromFiles: BSD-3-Clause
183 | PackageCopyrightText: NOASSERTION
184 | PackageDescription: A library for identifying source spans and locations.
185 | Relationship: SPDXRef-16 DYNAMIC_LINK SPDXRef-18
186 |
187 | ## Start of pub package 'js' version 0.6.1+1
188 | PackageName: js
189 | SPDXID: SPDXRef-10
190 | PackageVersion: 0.6.1+1
191 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/js/js@0.6.1%2B1
192 | PackageSupplier: Organization: js
193 | PackageOriginator: NOASSERTION
194 | PackageDownloadLocation: git+https://github.com/dart-lang/sdk.git@master#pkg%2Fjs
195 | FilesAnalyzed: true
196 | PackageHomePage: https://github.com/dart-lang/sdk/tree/master/pkg/js
197 | PackageLicenseConcluded: BSD-3-Clause
198 | PackageLicenseDeclared: NOASSERTION
199 | PackageLicenseInfoFromFiles: BSD-3-Clause
200 | PackageCopyrightText: NOASSERTION
201 | PackageDescription: Access JavaScript from Dart.
202 |
203 | ## Start of pub package 'test' version
204 | PackageName: Test
205 | SPDXID: SPDXRef-1
206 | PackageVersion:
207 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/test/test@
208 | PackageSupplier: Organization: test
209 | PackageOriginator: NOASSERTION
210 | PackageDownloadLocation: git+https://github.com/dart-lang/sdk.git@master#pkg%2Fjs
211 | FilesAnalyzed: true
212 | PackageHomePage: NOASSERTION
213 | PackageLicenseConcluded: NOASSERTION
214 | PackageLicenseDeclared: NOASSERTION
215 | PackageLicenseInfoFromFiles: NOASSERTION
216 | PackageCopyrightText: NOASSERTION
217 | Relationship: SPDXRef-1 DYNAMIC_LINK SPDXRef-9
218 | Relationship: SPDXRef-1 DYNAMIC_LINK SPDXRef-3
219 | Relationship: SPDXRef-1 DYNAMIC_LINK SPDXRef-2
220 | Relationship: SPDXRef-1 DYNAMIC_LINK SPDXRef-14
221 |
222 | ## Start of pub package 'term_glyph' version 1.1.0
223 | PackageName: term_glyph
224 | SPDXID: SPDXRef-18
225 | PackageVersion: 1.1.0
226 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/term_glyph/term_glyph@1.1.0
227 | PackageSupplier: Organization: term_glyph
228 | PackageOriginator: NOASSERTION
229 | PackageDownloadLocation: git+https://github.com/dart-lang/term_glyph.git@1.1.0
230 | FilesAnalyzed: true
231 | PackageHomePage: https://github.com/dart-lang/term_glyph
232 | PackageLicenseConcluded: BSD-3-Clause
233 | PackageLicenseDeclared: NOASSERTION
234 | PackageLicenseInfoFromFiles: BSD-3-Clause
235 | PackageCopyrightText: NOASSERTION
236 | PackageDescription: Useful Unicode glyphs and ASCII substitutes.
237 |
238 | ## Start of pub package 'async' version 2.4.1
239 | PackageName: async
240 | SPDXID: SPDXRef-4
241 | PackageVersion: 2.4.1
242 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/async/async@2.4.1
243 | PackageSupplier: Organization: async
244 | PackageOriginator: NOASSERTION
245 | PackageDownloadLocation: git+https://github.com/dart-lang/async.git@2.4.1
246 | FilesAnalyzed: true
247 | PackageHomePage: https://www.github.com/dart-lang/async
248 | PackageLicenseConcluded: BSD-3-Clause
249 | PackageLicenseDeclared: NOASSERTION
250 | PackageLicenseInfoFromFiles: BSD-3-Clause
251 | PackageCopyrightText: NOASSERTION
252 | PackageDescription: Utility functions and classes related to the 'dart:async' library.
253 | Relationship: SPDXRef-4 DYNAMIC_LINK SPDXRef-6
254 |
255 | ## Start of pub package 'path' version 1.7.0
256 | PackageName: path
257 | SPDXID: SPDXRef-14
258 | PackageVersion: 1.7.0
259 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/path/path@1.7.0
260 | PackageSupplier: Organization: path
261 | PackageOriginator: NOASSERTION
262 | PackageDownloadLocation: git+https://github.com/dart-lang/path.git@1.7.0
263 | FilesAnalyzed: true
264 | PackageHomePage: https://github.com/dart-lang/path
265 | PackageLicenseConcluded: BSD-3-Clause
266 | PackageLicenseDeclared: NOASSERTION
267 | PackageLicenseInfoFromFiles: BSD-3-Clause
268 | PackageCopyrightText: NOASSERTION
269 | PackageDescription: A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.
270 |
271 | ## Start of pub package 'crypto' version 2.1.4
272 | PackageName: crypto
273 | SPDXID: SPDXRef-8
274 | PackageVersion: 2.1.4
275 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/crypto/crypto@2.1.4
276 | PackageSupplier: Organization: crypto
277 | PackageOriginator: NOASSERTION
278 | PackageDownloadLocation: git+https://github.com/dart-lang/crypto.git@2.1.4
279 | FilesAnalyzed: true
280 | PackageHomePage: https://www.github.com/dart-lang/crypto
281 | PackageLicenseConcluded: BSD-3-Clause
282 | PackageLicenseDeclared: NOASSERTION
283 | PackageLicenseInfoFromFiles: BSD-3-Clause
284 | PackageCopyrightText: NOASSERTION
285 | PackageDescription: Library of cryptographic functions.
286 | Relationship: SPDXRef-8 DYNAMIC_LINK SPDXRef-7
287 |
288 | ## Start of pub package 'collection' version 1.14.12
289 | PackageName: collection
290 | SPDXID: SPDXRef-6
291 | PackageVersion: 1.14.12
292 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/collection/collection@1.14.12
293 | PackageSupplier: Organization: collection
294 | PackageOriginator: NOASSERTION
295 | PackageDownloadLocation: git+https://github.com/dart-lang/collection.git@1.14.12
296 | FilesAnalyzed: true
297 | PackageHomePage: https://www.github.com/dart-lang/collection
298 | PackageLicenseConcluded: BSD-3-Clause
299 | PackageLicenseDeclared: NOASSERTION
300 | PackageLicenseInfoFromFiles: BSD-3-Clause
301 | PackageCopyrightText: NOASSERTION
302 | PackageDescription: Collections and utilities functions and classes related to collections.
303 |
304 | ## Start of pub package 'typed_data' version 1.1.6
305 | PackageName: typed_data
306 | SPDXID: SPDXRef-19
307 | PackageVersion: 1.1.6
308 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/typed_data/typed_data@1.1.6
309 | PackageSupplier: Organization: typed_data
310 | PackageOriginator: NOASSERTION
311 | PackageDownloadLocation: git+https://github.com/dart-lang/typed_data.git@1.1.6
312 | FilesAnalyzed: true
313 | PackageHomePage: https://github.com/dart-lang/typed_data
314 | PackageLicenseConcluded: BSD-3-Clause
315 | PackageLicenseDeclared: NOASSERTION
316 | PackageLicenseInfoFromFiles: BSD-3-Clause
317 | PackageCopyrightText: NOASSERTION
318 | PackageDescription: Utility functions and classes related to the 'dart:typed_data' library.
319 |
320 | ## Start of pub package 'node_io' version 1.1.1
321 | PackageName: node_io
322 | SPDXID: SPDXRef-13
323 | PackageVersion: 1.1.1
324 | ExternalRef: PACKAGE-MANAGER purl pkg:pub/node_io/node_io@1.1.1
325 | PackageSupplier: Organization: node_io
326 | PackageOriginator: NOASSERTION
327 | PackageDownloadLocation: git+https://github.com/pulyaevskiy/node-interop.git@1.1.1
328 | FilesAnalyzed: true
329 | PackageHomePage: https://github.com/pulyaevskiy/node-interop
330 | PackageLicenseConcluded: BSD-3-Clause
331 | PackageLicenseDeclared: NOASSERTION
332 | PackageLicenseInfoFromFiles: BSD-3-Clause
333 | PackageCopyrightText: NOASSERTION
334 | PackageDescription: Like dart:io but with Node.js.
335 | Relationship: SPDXRef-13 DYNAMIC_LINK SPDXRef-12
336 |
337 |
--------------------------------------------------------------------------------
/test/spdx/output/result.spdx:
--------------------------------------------------------------------------------
1 | ## Document Information
2 | SPDXVersion: SPDX-2.3
3 | DataLicense: CC0-1.0
4 | SPDXID: SPDXRef-DOCUMENT
5 | DocumentName: sample-sbom-spdx
6 | DocumentNamespace: https://philips.example.com
7 |
8 | ## External Document References
9 | ExternalDocumentRef: DocumentRef-SPDXRef-DOCUMENT https://spdx.org/510f728d-05b2-4e32-8501-550587c39a2a SHA1: 2bda6fc16f7e9428ad775e313ea4ac33cf4e4bfe
10 | ExternalDocumentRef: DocumentRef-SPDXRef-DOCUMENT https://spdx.org/510f728d-05b2-4e32-8501-550587c39a2a SHA1: b993cf2e89576a0d60e4ecb2a21fa1f1f72cdf94
11 |
12 | ## Creation Information
13 | Creator: Organization: ci/cd build pipeline (test@mail.com)
14 | Created: 2025-02-25T13:05:35Z
15 |
16 | ## Package Information
17 | PackageName: sample-sbom-spdx
18 | SPDXID: SPDXRef-0
19 | PackageVersion: 1
20 | PackageDownloadLocation: NOASSERTION
21 | FilesAnalyzed: true
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/test_SPDX_DeepMerge.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from spdx_tools.spdx.model import (
3 | Document,
4 | Relationship,
5 | RelationshipType,
6 | CreationInfo,
7 | Actor,
8 | ActorType,
9 | Package,
10 | SpdxNoAssertion,
11 | Annotation,
12 | AnnotationType,
13 | )
14 | from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
15 | from spdxmerge.SPDX_DeepMerge import SPDX_DeepMerger
16 |
17 |
18 | def test_document_creation():
19 | doc1 = Document(
20 | CreationInfo(
21 | spdx_version="SPDX-2.3",
22 | spdx_id="SPDXRef-DOCUMENT1",
23 | name="Test Document 1",
24 | document_namespace="https://example.com/spdx1",
25 | creators=[Actor(name="John Doe", actor_type=ActorType.ORGANIZATION)],
26 | created=datetime.utcnow().replace(microsecond=0),
27 | )
28 | )
29 |
30 | doc2 = Document(
31 | CreationInfo(
32 | spdx_version="SPDX-2.3",
33 | spdx_id="SPDXRef-DOCUMENT2",
34 | name="Test Document 2",
35 | document_namespace="https://example.com/spdx2",
36 | creators=[Actor(name="Jane Doe", actor_type=ActorType.ORGANIZATION)],
37 | created=datetime.utcnow().replace(microsecond=0),
38 | )
39 | )
40 |
41 | merger = SPDX_DeepMerger(
42 | doc_list=[doc1, doc2],
43 | docnamespace="https://example.com/spdx",
44 | name="Merged Document",
45 | version="1.0",
46 | author="Yazat Mishra",
47 | email="yazat@example.com",
48 | )
49 | merged_doc = merger.get_document()
50 |
51 | assert merged_doc.creation_info.name == "Merged Document"
52 | assert merged_doc.creation_info.creators[0].name == "Yazat Mishra"
53 | assert merged_doc.creation_info.creators[0].email == "yazat@example.com"
54 | assert merged_doc.creation_info.document_namespace == "https://example.com/spdx"
55 |
56 |
57 | def test_package_info():
58 | doc1 = Document(
59 | CreationInfo(
60 | spdx_version="SPDX-2.3",
61 | spdx_id="SPDXRef-DOCUMENT1",
62 | name="Test Document 1",
63 | document_namespace="https://example.com/spdx1",
64 | creators=[Actor(name="John Doe", actor_type=ActorType.ORGANIZATION)],
65 | created=datetime.utcnow().replace(microsecond=0),
66 | )
67 | )
68 |
69 | doc2 = Document(
70 | CreationInfo(
71 | spdx_version="SPDX-2.3",
72 | spdx_id="SPDXRef-DOCUMENT2",
73 | name="Test Document 2",
74 | document_namespace="https://example.com/spdx2",
75 | creators=[Actor(name="Jane Doe", actor_type=ActorType.ORGANIZATION)],
76 | created=datetime.utcnow().replace(microsecond=0),
77 | )
78 | )
79 |
80 | package1 = Package(
81 | name="Test Package 1",
82 | version="1.0",
83 | spdx_id="SPDXRef-PACKAGE1",
84 | download_location=SpdxNoAssertion(),
85 | )
86 | package2 = Package(
87 | name="Test Package 2",
88 | version="2.0",
89 | spdx_id="SPDXRef-PACKAGE2",
90 | download_location=SpdxNoAssertion(),
91 | )
92 |
93 | doc1.packages.append(package1)
94 | doc2.packages.append(package2)
95 |
96 | merger = SPDX_DeepMerger(
97 | doc_list=[doc1, doc2],
98 | docnamespace="https://example.com/spdx",
99 | name="Merged Document",
100 | version="1.0",
101 | author="Yazat Mishra",
102 | email="yazat@example.com",
103 | )
104 | merger.doc_packageinfo()
105 | merged_doc = merger.get_document()
106 |
107 | assert len(merged_doc.packages) == 3 # Includes root package
108 | assert merged_doc.packages[1].name == "Test Package 1"
109 | assert merged_doc.packages[2].name == "Test Package 2"
110 |
111 |
112 | def test_relationships():
113 | doc1 = Document(
114 | CreationInfo(
115 | spdx_version="SPDX-2.3",
116 | spdx_id="SPDXRef-DOCUMENT1",
117 | name="Test Document 1",
118 | document_namespace="https://example.com/spdx1",
119 | creators=[Actor(name="John Doe", actor_type=ActorType.ORGANIZATION)],
120 | created=datetime.utcnow().replace(microsecond=0),
121 | )
122 | )
123 |
124 | doc2 = Document(
125 | CreationInfo(
126 | spdx_version="SPDX-2.3",
127 | spdx_id="SPDXRef-DOCUMENT2",
128 | name="Test Document 2",
129 | document_namespace="https://example.com/spdx2",
130 | creators=[Actor(name="Jane Doe", actor_type=ActorType.ORGANIZATION)],
131 | created=datetime.utcnow().replace(microsecond=0),
132 | )
133 | )
134 |
135 | rel1 = Relationship(
136 | spdx_element_id="SPDXRef-PACKAGE1",
137 | relationship_type=RelationshipType.CONTAINS,
138 | related_spdx_element_id="SPDXRef-DOCUMENT1",
139 | )
140 |
141 | rel2 = Relationship(
142 | spdx_element_id="SPDXRef-PACKAGE2",
143 | relationship_type=RelationshipType.DEPENDS_ON,
144 | related_spdx_element_id="SPDXRef-DOCUMENT2",
145 | )
146 |
147 | doc1.relationships.append(rel1)
148 | doc2.relationships.append(rel2)
149 |
150 | merger = SPDX_DeepMerger(
151 | doc_list=[doc1, doc2],
152 | docnamespace="https://example.com/spdx",
153 | name="Merged Document",
154 | version="1.0",
155 | author="Yazat Mishra",
156 | email="yazat@example.com",
157 | )
158 | merger.doc_packageinfo()
159 | merger.doc_relationship_info()
160 | merged_doc = merger.get_document()
161 |
162 | assert len(merged_doc.relationships) == 3 # Includes the DESCRIBES relationship
163 | assert merged_doc.relationships[1].relationship_type == RelationshipType.CONTAINS
164 | assert merged_doc.relationships[2].relationship_type == RelationshipType.DEPENDS_ON
165 |
166 |
167 | def test_annotations():
168 | doc1 = Document(
169 | CreationInfo(
170 | spdx_version="SPDX-2.3",
171 | spdx_id="SPDXRef-DOCUMENT1",
172 | name="Test Document 1",
173 | document_namespace="https://example.com/spdx1",
174 | creators=[Actor(name="John Doe", actor_type=ActorType.ORGANIZATION)],
175 | created=datetime.utcnow().replace(microsecond=0),
176 | )
177 | )
178 |
179 | doc2 = Document(
180 | CreationInfo(
181 | spdx_version="SPDX-2.3",
182 | spdx_id="SPDXRef-DOCUMENT2",
183 | name="Test Document 2",
184 | document_namespace="https://example.com/spdx2",
185 | creators=[Actor(name="Jane Doe", actor_type=ActorType.ORGANIZATION)],
186 | created=datetime.utcnow().replace(microsecond=0),
187 | )
188 | )
189 |
190 | annotation1 = Annotation(
191 | spdx_id="SPDXRef-PACKAGE1",
192 | annotation_type=AnnotationType.OTHER,
193 | annotator=Actor(name="John Doe", actor_type=ActorType.ORGANIZATION),
194 | annotation_date=datetime.utcnow(),
195 | annotation_comment="Test annotation 1",
196 | )
197 |
198 | annotation2 = Annotation(
199 | spdx_id="SPDXRef-PACKAGE2",
200 | annotation_type=AnnotationType.REVIEW,
201 | annotator=Actor(name="Jane Doe", actor_type=ActorType.ORGANIZATION),
202 | annotation_date=datetime.utcnow(),
203 | annotation_comment="Test annotation 2",
204 | )
205 |
206 | doc1.annotations.append(annotation1)
207 | doc2.annotations.append(annotation2)
208 |
209 | merger = SPDX_DeepMerger(
210 | doc_list=[doc1, doc2],
211 | docnamespace="https://example.com/spdx",
212 | name="Merged Document",
213 | version="1.0",
214 | author="Yazat Mishra",
215 | email="yazat@example.com",
216 | )
217 | merger.doc_annotation_info()
218 | merged_doc = merger.get_document()
219 |
220 | assert merged_doc.annotations[0].annotation_comment == "Test annotation 1"
221 | assert merged_doc.annotations[1].annotation_comment == "Test annotation 2"
222 | assert len(merged_doc.annotations) == 2
223 |
224 |
225 | def test_spdx_document_validation():
226 | doc1 = Document(
227 | CreationInfo(
228 | spdx_version="SPDX-2.3",
229 | spdx_id="SPDXRef-DOCUMENT1",
230 | name="Test Document 1",
231 | document_namespace="https://example.com/spdx1",
232 | creators=[Actor(name="John Doe", actor_type=ActorType.ORGANIZATION)],
233 | created=datetime.utcnow().replace(microsecond=0),
234 | )
235 | )
236 |
237 | doc2 = Document(
238 | CreationInfo(
239 | spdx_version="SPDX-2.3",
240 | spdx_id="SPDXRef-DOCUMENT2",
241 | name="Test Document 2",
242 | document_namespace="https://example.com/spdx2",
243 | creators=[Actor(name="Jane Doe", actor_type=ActorType.ORGANIZATION)],
244 | created=datetime.utcnow().replace(microsecond=0),
245 | )
246 | )
247 |
248 | merger = SPDX_DeepMerger(
249 | doc_list=[doc1, doc2],
250 | docnamespace="https://example.com/spdx",
251 | name="Merged Document",
252 | version="1.0",
253 | author="Yazat Mishra",
254 | email="yazat@example.com",
255 | )
256 | merger.doc_packageinfo()
257 | merger.doc_fileinfo()
258 | merger.doc_snippetinfo()
259 | merger.doc_other_license_info()
260 | merger.doc_relationship_info()
261 | merged_doc = merger.get_document()
262 | validation_errors = validate_full_spdx_document(merged_doc)
263 |
264 | assert len(validation_errors) == 0 # Ensure document is valid SPDX
265 |
--------------------------------------------------------------------------------
/test/test_SPDX_ShallowMerge.py:
--------------------------------------------------------------------------------
1 | from spdxmerge.SPDX_ShallowMerge import SPDX_ShallowMerger
2 |
3 | from spdx_tools.spdx.model import Document, CreationInfo, Actor, ActorType
4 | from spdx_tools.spdx.model.checksum import ChecksumAlgorithm
5 | from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
6 | from datetime import datetime
7 |
8 |
9 | class Test_SPDX_ShallowMerger:
10 | def setup_method(self):
11 | # Create test documents with comments for checksums
12 | creation_info1 = CreationInfo(
13 | spdx_version="SPDX-2.3",
14 | spdx_id="DOCUMENT1", # Changed to match expected format
15 | name="Test document 1",
16 | document_namespace="http://example.com/spdx",
17 | creators=[Actor(ActorType.PERSON, "John Smith", "john@example.com")],
18 | created=datetime.utcnow().replace(microsecond=0),
19 | )
20 | doc1 = Document(creation_info=creation_info1)
21 | doc1.comment = "comment1" # Add comment for checksum value
22 |
23 | creation_info2 = CreationInfo(
24 | spdx_version="SPDX-2.3",
25 | spdx_id="DOCUMENT2", # Changed to match expected format
26 | name="Test document 2",
27 | document_namespace="http://example.com/spdx",
28 | creators=[Actor(ActorType.PERSON, "John Smith", "john@example.com")],
29 | created=datetime.utcnow().replace(microsecond=0),
30 | )
31 | doc2 = Document(creation_info=creation_info2)
32 | doc2.comment = "comment2" # Add comment for checksum value
33 |
34 | # Create merger with email parameter
35 | self.merger = SPDX_ShallowMerger(
36 | doc_list=[doc1, doc2],
37 | docnamespace="http://example.com/spdx",
38 | name="Test document",
39 | version="1.0", # Fixed to match expected test value
40 | author="Yazat",
41 | email="yazat@example.com" # Added email parameter
42 | )
43 | self.merger.doc_externalDocumentRef()
44 | self.master_doc = self.merger.get_document()
45 |
46 | def test_document_creationInfo(self):
47 | doc = self.master_doc
48 |
49 | assert doc.creation_info.name == "Test document"
50 | assert doc.creation_info.spdx_version == "SPDX-2.3"
51 | assert doc.creation_info.spdx_id == "SPDXRef-DOCUMENT"
52 | assert doc.creation_info.document_namespace == "http://example.com/spdx"
53 | assert len(doc.creation_info.creators) == 1
54 | assert doc.creation_info.creators[0].name == "Yazat"
55 | assert doc.creation_info.creators[0].email == "yazat@example.com" # Added email check
56 | assert doc.creation_info.creators[0].actor_type == ActorType.ORGANIZATION # Check organization type
57 |
58 | def test_document_externalReference(self):
59 | doc = self.master_doc
60 |
61 | assert len(doc.packages) == 1
62 | package = doc.packages[0]
63 | assert package.name == "Test document"
64 | assert package.version == "1.0"
65 | assert package.spdx_id == "SPDXRef-0" # Fixed to match implementation
66 | assert isinstance(package.download_location, SpdxNoAssertion)
67 | assert len(doc.creation_info.external_document_refs) == 2
68 |
69 | # Check external document references
70 | assert doc.creation_info.external_document_refs[0].document_ref_id == "DocumentRef-DOCUMENT1" # Fixed prefix
71 | assert doc.creation_info.external_document_refs[0].document_uri == "http://example.com/spdx"
72 | assert doc.creation_info.external_document_refs[0].checksum.algorithm == ChecksumAlgorithm.SHA1
73 | assert doc.creation_info.external_document_refs[0].checksum.value == "comment1"
74 |
75 | assert doc.creation_info.external_document_refs[1].document_ref_id == "DocumentRef-DOCUMENT2" # Fixed prefix
76 | assert doc.creation_info.external_document_refs[1].document_uri == "http://example.com/spdx"
77 | assert doc.creation_info.external_document_refs[1].checksum.algorithm == ChecksumAlgorithm.SHA1
78 | assert doc.creation_info.external_document_refs[1].checksum.value == "comment2"
79 |
--------------------------------------------------------------------------------