diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 1cbf9811e..000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,129 +0,0 @@
-version: 2.1
-
-commands:
- mac_install_python:
- parameters:
- python_version:
- description: "version of python to install"
- type: string
- default: 3.7.10
- steps:
- - run: |
- brew update
- python --version
- sudo -H pip install --upgrade virtualenv
- brew install pyenv
- echo 'eval "$(pyenv init --path)"' >> ~/.bash_profile
- echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
- source ~/.bash_profile
- pyenv install --list
- pyenv install << parameters.python_version >>
- pyenv versions
- pyenv global << parameters.python_version >>
- python --version
-
- update_packaging_tools:
- steps:
- - run: |
- python -m pip install --upgrade pip
- - run: |
- python -m pip install --upgrade setuptools wheel setuptools_scm build
-
- install_run_tests:
- steps:
- - run: |
- python -m build -nwx .
- python -m pip install --upgrade ./dist/*.whl
- python -m pip install pytest
- - run: pytest
-
-jobs:
- mac_python_3_7:
- shell: /bin/bash --login
- macos:
- xcode: '13.0.0'
- steps:
- - checkout
- - mac_install_python:
- python_version: "3.7.10"
- - update_packaging_tools
- - install_run_tests
-
-
- mac_python_3_8:
- shell: /bin/bash --login
- macos:
- xcode: '13.0.0'
- steps:
- - checkout
- - mac_install_python:
- python_version: "3.8.10"
- - update_packaging_tools
- - install_run_tests
-
- mac_python_3_9:
- shell: /bin/bash --login
- macos:
- xcode: '13.0.0'
- steps:
- - checkout
- - mac_install_python:
- python_version: "3.9.5"
- - update_packaging_tools
- - install_run_tests
-
- mac_python_3_10:
- shell: /bin/bash --login
- macos:
- xcode: '13.0.0'
- steps:
- - checkout
- - mac_install_python:
- python_version: "3.10.6"
- - update_packaging_tools
- - install_run_tests
-
- linux_python_3_7:
- docker:
- - image: python:3.7
- steps:
- - checkout
- - update_packaging_tools
- - install_run_tests
-
- linux_python_3_8:
- docker:
- - image: python:3.8
- steps:
- - checkout
- - update_packaging_tools
- - install_run_tests
-
- linux_python_3_9:
- docker:
- - image: python:3.9
- steps:
- - checkout
- - update_packaging_tools
- - install_run_tests
-
- linux_python_3_10:
- docker:
- - image: python:3.10
- steps:
- - checkout
- - update_packaging_tools
- - install_run_tests
-
-workflows:
- version: 2
- python_matrix_build:
- jobs:
- - mac_python_3_7
- - mac_python_3_8
- - mac_python_3_9
- - mac_python_3_10
- - linux_python_3_7
- - linux_python_3_8
- - linux_python_3_9
- - linux_python_3_10
diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index 9c27e202e..000000000
--- a/.coveragerc
+++ /dev/null
@@ -1,3 +0,0 @@
-[run]
-branch = True
-source = spdx
diff --git a/.flake8 b/.flake8
new file mode 100644
index 000000000..e2c6e5910
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+max-line-length = 119
+exclude = src/spdx_tools/spdx/parser/tagvalue/parsetab.py, __init__.py
+extend-ignore = E203
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..91abb11fd
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "pip" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/check_codestyle.yml b/.github/workflows/check_codestyle.yml
new file mode 100644
index 000000000..327f2f54a
--- /dev/null
+++ b/.github/workflows/check_codestyle.yml
@@ -0,0 +1,37 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+name: Run Linter
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ check_code_style:
+ runs-on: ${{ matrix.os }}
+ defaults:
+ run:
+ shell: bash
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
+ python-version: [ "3.14" ]
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Installation
+ run: pip install ".[code_style]"
+ - name: Check code with isort
+ run: |
+ isort src tests --check
+ black src tests --check
+ flake8 src tests
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 000000000..46dc4157a
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,48 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+name: Generate API docs
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.14'
+ - name: Install dependencies
+ run: |
+ sudo apt-get install graphviz-dev
+ pip install -e ".[test,graph_generation]"
+ pip install pdoc
+ - name: Generate docs
+ run: pdoc spdx_tools -o docs/
+ - name: Upload docs as artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: docs/
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - id: deployment
+ name: Deploy docs to GitHub pages
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml
new file mode 100644
index 000000000..d661745be
--- /dev/null
+++ b/.github/workflows/install_and_test.yml
@@ -0,0 +1,42 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+name: Install and Test
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ install_and_test:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
+ python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Installation
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install --upgrade setuptools wheel setuptools_scm build
+ python -m build -nwx .
+ python -m pip install --upgrade ./dist/*.whl
+ python -m pip install pytest
+ python -m pip install pyshacl
+ python -m pip install tzdata
+ python -m pip install networkx
+ shell: bash
+ - name: Run tests
+ run: pytest
+ - name: Run CLI
+ run: pyspdxtools -i ./tests/spdx/data/SPDXJSONExample-v2.3.spdx.json
diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml
new file mode 100644
index 000000000..3a749a3f0
--- /dev/null
+++ b/.github/workflows/integration_test.yml
@@ -0,0 +1,43 @@
+name: Circle conversion test
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ install_and_test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python 3.14
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.14
+ - name: Installation
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install --upgrade setuptools wheel setuptools_scm build
+ python -m build -nwx .
+ python -m pip install --upgrade ./dist/*.whl
+ shell: bash
+ - name: Install jd
+ uses: jaxxstorm/action-install-gh-release@v1.10.0
+ with: # Grab the latest version
+ repo: josephburnett/jd
+ platform: linux
+ extension-matching: disable
+ rename-to: jd
+ chmod: 0755
+ - name: Run CLI
+ run: |
+ pyspdxtools -i ./tests/spdx/data/circleConversionTestInitialDocument.json -o circleTest.yaml
+ pyspdxtools -i circleTest.yaml -o circleTest.xml
+ pyspdxtools -i circleTest.xml -o circleTest.rdf
+ pyspdxtools -i circleTest.rdf -o circleTest.spdx
+ pyspdxtools -i circleTest.spdx -o circleTest.json
+ - name: Compare initial and final json document of the circle conversion test
+ run: jd -set ./tests/spdx/data/circleConversionTestInitialDocument.json circleTest.json
diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml
new file mode 100644
index 000000000..15439f41f
--- /dev/null
+++ b/.github/workflows/prepare_release.yml
@@ -0,0 +1,44 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+name: Prepare release
+
+on:
+ push:
+ tags: [ 'v*.*.*']
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.14'
+ - name: Set up dependencies
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install --upgrade setuptools wheel setuptools_scm build twine
+ python -m pip install -e .
+ shell: bash
+ - name: Build wheel
+ run: python -m build
+ - name: Verify build
+ run: twine check dist/*
+ - name: Create build archive
+ uses: a7ul/tar-action@v1.1.0
+ with:
+ command: c
+ files: dist
+ outPath: spdx_tools_dist.tar.gz
+ - name: Create GitHub release
+ uses: softprops/action-gh-release@v1
+ with:
+ files: spdx_tools_dist.tar.gz
+ generate_release_notes: true
+ draft: true
diff --git a/.gitignore b/.gitignore
index cc02c2461..d82f5bc0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,9 +2,9 @@ __pycache__/
*.py[cod]
*.out
/build/
-/dist/
+/dist*/
/tmp/
-spdx/parsers/parsetab.py
+src/spdx_tools/spdx/parser/tagvalue/parsetab.py
/.cache/
.tox
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee72fb020..808f5e134 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,160 @@
# Changelog
+## v0.8.4 (2026-01-16)
+
+### New features and changes
+
+* change supported Python versions:
+ * add support for Python 3.14
+ * dropped support for Python 3.7, 3.8, 3.9 (which all have reached their end-of-life)
+* fixes
+ * download location URIs are not case sensitive anymore
+ * removed some control characters from JSON SPDX
+ * remove usages of `__file__` to make the project zip-safe
+* fixes to `README.md` and `process.md`
+
+### Contributors
+
+This release was made possible by the following contributors. Thank you very much!
+
+* @zbleness
+* @clabbenius
+* Oliver Benjamin @oli-ben
+* Arthit Suriyawongkul @bact
+* Armin Tänzer @armintaenzertng
+
+## v0.8.3 (2024-09-27)
+
+### New features and changes
+
+* tag-value parser:
+ * fixed license expression error handling
+ * fixed parsing Tool or Organization as annotator
+ * allow "NONE" and "NOASSERTION" as strings for fields requiring text
+* CLI tool:
+ * shortened the output of the FileNotFoundError
+ * now catches decoding errors while parsing
+* fixed tag-value output when the ID of a related SPDX element is "NONE" or "NOASSERTION"
+* spdx3:
+ * added REQUIREMENT type to software_purpose
+ * unindent creation information in element_writer
+* internal:
+ * replaced remaining occurrences of Licensing() with spdx_licensing
+ * fixed CI not working for Python 3.7 on latest MacOS
+
+### Contributors
+
+This release was made possible by the following contributors. Thank you very much!
+
+* Stanislav Pankevich @stanislaw
+* Meret Behrens @meretp
+* Maximilian Huber @maxhbr
+* Armin Tänzer @armintaenzertng
+
+## v0.8.2 (2023-10-12)
+
+### New features and changes
+
+* added optional encoding parameter for parsing files
+* fixed handling of the FilesAnalyzed field in Tag-Value format
+* fixed the validation of the DownloadLocation field
+* fixed the error handling while parsing license expressions
+* fixed output of timezone-sensitive datetimes
+* added code architecture documentation
+
+### Contributors
+
+This release was made possible by the following contributors. Thank you very much!
+
+* Christian Decker @chrisdecker1201
+* Jeff Licquia @licquia
+* Maximilian Huber @maxhbr
+* Armin Tänzer @armintaenzertng
+
+
+## v0.8.1 (2023-08-24)
+
+### New features and changes
+
+* massive speed-up in the validation process of large SBOMs
+* validation now detects and checks license references from external documents
+* allow for userinfo in git+ssh download location
+* more efficient relationship parsing in JSON/YAML/XML
+
+### Contributors
+
+This release was made possible by the following contributors. Thank you very much!
+
+* Brian DeHamer @bdehamer
+* Brandon Lum @lumjjb
+* Maximilian Huber @maxhbr
+* Armin Tänzer @armintaenzertng
+
+
+## v0.8.0 (2023-07-25)
+
+### New features and changes
+
+* major refactoring of the library
+ * new and improved data model
+ * type hints and type checks have been added to the model classes
+ * license expressions and SPDX license list are now handled by the `license-expression` package
+ * to update your existing code, refer to the [migration guide](https://github.com/spdx/tools-python/wiki/How-to-migrate-from-0.7-to-0.8)
+* experimental support for the upcoming SPDX v3 specification (note, however, that support is neither complete nor
+ stable at this point, as the spec is still evolving)
+* full validation of SPDX documents against the v2.2 and v2.3 specification
+* support for SPDX's RDF format with all v2.3 features
+* unified `pysdpxtools` CLI tool replaces separate `pyspdxtools_parser` and `pyspdxtools_convertor`
+* [online API documentation](https://spdx.github.io/tools-python)
+* replaced CircleCI with GitHub Actions
+
+### Contributors
+
+This release was made possible by the following contributors. Thank you very much!
+
+* Armin Tänzer @armintaenzertng
+* Gary O'Neall @goneall
+* Gaurav Mishra @GMishx
+* HarshvMahawar @HarshvMahawar
+* Holger Frydrych @fholger
+* Jeff Licquia @licquia
+* Kate Stewart @kestewart
+* Maximilian Huber @maxhbr
+* Meret Behrens @meretp
+* Nicolaus Weidner @nicoweidner
+* William Armiros @willarmiros
+
+
+## v0.7.1 (2023-03-14)
+
+### New features and changes
+
+* added GitHub Actions workflow
+* added requirements.txt
+* added uritools for URI validation
+* Python >= 3.7 is now required
+* json/yaml/xml: added support for empty arrays for hasFiles and licenseInfoFromFiles
+* rdf: fixed writing of multiple packages
+* tag-value: enhanced parsing of snippet ranges to not mix it up with package version
+* tag-value: fixed parsing of whitespaces
+* tag-value: duplicates in LicenseInfoInFile are now removed during writing
+* account for supplier and originator to be NOASSERTION
+* checksum validation now requires lowercase values
+* during writing of a file, the encoding can be set (default is utf-8)
+* license list updated to version 3.20
+
+### Contributors
+
+This release was made possible by the following contributors. Thank you very much!
+
+* Christian Decker @chrisdecker1201
+* Marc-Etienne Vargenau @vargenau
+* John Vandenberg @jayvdb
+* Nicolaus Weidner @nicoweidner
+* Meret Behrens @meretp
+* Armin Tänzer @armintaenzertng
+* Maximilian Huber @maxhbr
+
## v0.7.0 (2022-12-08)
@@ -9,12 +164,12 @@ Starting a Changelog.
* Dropped Python 2 support. Python >= 3.6 is now required.
* Added `pyspdxtools_convertor` and `pyspdxtools_parser` CLI scripts. See [the readme](README.md) for usage instructions.
-* Updated the tools to support SPDX versions up to 2.3 and to conform with the specification. Apart from many bugfixes
+* Updated the tools to support SPDX versions up to 2.3 and to conform with the specification. Apart from many bugfixes
and new properties, some of the more significant changes include:
- * Support for multiple packages per document
- * Support for multiple checksums for packages and files
- * Support for files outside a package
-* **Note**: Validation was updated to follow the 2.3 specification. Since there is currently no support for
+ * Support for multiple packages per document
+ * Support for multiple checksums for packages and files
+ * Support for files outside a package
+* **Note**: Validation was updated to follow the 2.3 specification. Since there is currently no support for
version-specific handling, some details may be handled incorrectly for documents using lower
versions. The changes are mostly restricted to properties becoming optional and new property values becoming
available, and should be of limited impact. See https://spdx.github.io/spdx-spec/v2.3/diffs-from-previous-editions/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 03e43d245..88638e70e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -15,7 +15,7 @@ intention prior to creating a patch.
## Development process
-We use the GitHub flow that is described here: https://guides.github.com/introduction/flow/
+We use the GitHub flow that is described here:
Here's the process to make changes to the codebase:
@@ -26,11 +26,23 @@ Here's the process to make changes to the codebase:
2. Review [open pull requests](https://github.com/spdx/tools-python/pulls) before committing time to a substantial
revision. Work along similar lines may already be in progress.
-3. Create a new branch:
+3. Fork the repository as described [here](https://docs.github.com/en/get-started/quickstart/fork-a-repo#forking-a-repository)
+ and optionally follow the further steps described to sync your fork and the original repository.
+
+4. Create a new branch in your fork and set up environment:
+
```sh
git checkout -b fix-or-improve-something
+ python -m venv ./venv
+ ./venv/bin/activate
+ pip install -e ".[development]"
```
-4. Make some changes and commit them to the branch:
+
+ Note: By using the group `[development]` for the installation, all dependencies (including optional ones) will be
+ installed. This way we make sure that all tests are executed.
+
+5. Make some changes and commit them to the branch:
+
```sh
git commit --signoff -m 'description of my changes'
```
@@ -42,22 +54,40 @@ Here's the process to make changes to the codebase:
commits: `git commit -s` or `--signoff` signs a current commit, and `git rebase --signoff `
retroactively signs a range of past commits.
-5. Test your changes:
+6. Test your changes:
+
```sh
- python setup.py test # in the repo root
+ pytest -vvs # in the repo root
+ ```
+
+7. Check your code style. When opening a pull request, your changes will automatically be checked with `isort`, `black`
+ and `flake8` to make sure your changes fit with the rest of the code style.
+
+ ```sh
+ # run the following commands in the repo root
+ isort src tests
+ black src tests
+ flake8 src tests
```
- You may use other test runners, such as `pytest` or `nose` at your preference.
-6. Push the branch to your fork on GitHub:
+
+ `black` and `isort` will automatically format the code and sort the imports. The configuration for these linters
+ can be found in the `pyproject.toml`. `flake8` lists all problems found which then need to be resolved manually.
+ The configuration for the linter can be found in the `.flake8` file.
+
+8. Push the branch to your fork on GitHub:
+
```sh
git push origin fix-or-improve-something
```
-7. Make a pull request on GitHub.
-8. Continue making more changes and commits on the branch, with `git commit --signoff` and `git push`.
-9. When done, write a comment on the PR asking for a code review.
-10. Some other developer will review your changes and accept your PR. The merge should be done with `rebase`, if
+
+9. Make a pull request on GitHub.
+10. Continue making more changes and commits on the branch, with `git commit --signoff` and `git push`.
+11. When done, write a comment on the PR asking for a code review.
+12. Some other developer will review your changes and accept your PR. The merge should be done with `rebase`, if
possible, or with `squash`.
-11. The temporary branch on GitHub should be deleted (there is a button for deleting it).
-12. Delete the local branch as well:
+13. The temporary branch on GitHub should be deleted (there is a button for deleting it).
+14. Delete the local branch as well:
+
```sh
git checkout master
git pull -p
@@ -65,11 +95,11 @@ Here's the process to make changes to the codebase:
git branch -d fix-or-improve-something
```
-# How to run tests
+## How to run tests
The tests framework is using pytest:
-```
+```sh
pip install pytest
pytest -vvs
```
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
new file mode 100644
index 000000000..6a9bcb2a5
--- /dev/null
+++ b/DOCUMENTATION.md
@@ -0,0 +1,125 @@
+# Code architecture documentation
+
+## Package Overview
+
+Beneath the top-level package `spdx_tools` you will find three sub-packages:
+
+- `spdx`, which contains the code to create, parse, write and validate SPDX documents of versions 2.2 and 2.3
+- `spdx3`, which will contain the same feature set for versions 3.x
+- `common`, which contains code that is shared between the different versions, such as type-checking and `spdx_licensing`.
+
+## `spdx`
+
+The `spdx` package contains the code dealing with SPDX-2 documents.
+The subpackages serve the purpose to divide the code into logically independent chunks. Shared code can be found in the top-level modules here.
+`model`, `parser`, `validation` and `writer` constitute the four main components of this library and are further described below.
+`clitools` serves as the entrypoint for the command `pyspdxtools`.
+`jsonschema` and `rdfschema` contain code specific to the corresponding serialization format.
+
+### `model`
+
+The internal data model closely follows the [official SPDX-2.3 specification](https://spdx.github.io/spdx-spec/v2.3/).
+
+Entrypoint to the model is the `Document` class, which has the following attributes:
+
+- `creation_info`: a single instance of the `CreationInfo` class
+- `packages`: a list of `Package` objects
+- `files`: a list of `File` objects
+- `snippets`: a list of `Snippet` objects
+- `relationships`: a list of `Relationship` objects
+- `annotations`: a list of `Annotation` objects
+- `extracted_licensing_info`: a list of `ExtractedLicensingInfo` objects
+
+For a complete overview of the model classes and their respective attributes, please refer to [the API documentation](https://spdx.github.io/tools-python/spdx_tools/spdx/model.html).
+
+For licensing attributes, i.e. those of type `LicenseExpression`, the `license-expression` library is used.
+The function mainly used here is `get_spdx_licensing().parse(some_license_expression_string)`.
+While `get_spdx_licensing()` takes very long to call, its return value can be reused across the code, which is why it is centrally provided by the `spdx_licensing` module in the `common` package.
+
+A custom extension of the `@dataclass` annotation is used that is called `@dataclass_with_properties`.
+Apart from all the usual `dataclass` functionality, this implements fields of a class as properties with their own getter and setter methods.
+This is used in particular to implement type checking when properties are set.
+Source of truth for these checks are the attribute definitions at the start of the respective class that must specify the correct type hint.
+
+The `beartype` library is used to check type conformity (`typeguard` was used in the past but has been replaced since due to performance issues).
+In case of a type mismatch a `TypeError` is raised. To ensure that all possible type errors are found during the construction of an object,
+a custom `__init__()` that calls `check_types_and_set_values()` is part of every class.
+This function tries to set all values provided by the constructor and collects all occurrences of `TypeError` into a single error of type `ConstructorTypeErrors`.
+
+For the SPDX values `NONE` and `NOASSERTION` the classes `SpdxNone` and `SpdxNoAssertion` are used, respectively. Both can be instantiated without any arguments.
+
+### `parser`
+
+The parsing and writing modules are split into subpackages according to the serialization formats: `json`, `yaml`, `xml`, `tagvalue` and `rdf`.
+As the first three share the same tree structure that can be parsed into a dictionary, their shared logic is contained in the `jsonlikedict` package.
+One overarching concept of all parsers is the goal of dealing with parsing errors (like faulty types or missing mandatory fields) as long as possible before failing.
+Thus, the `SPDXParsingError` that is finally raised collects as much information as possible about all parsing errors that occurred.
+
+#### `tagvalue`
+
+Since Tag-Value is an SPDX-specific format, there exist no readily available parsers for it.
+This library implements its own deserialization code using the `ply` library's `lex` module for lexing and the `yacc` module for parsing.
+
+#### `rdf`
+
+The `rdflib` library is used to deserialize RDF graphs from XML format.
+The graph is then being parsed and translated into the internal data model.
+
+#### `json`, `yaml`, `xml`
+
+In a first step, all three of JSON, YAML and XML formats are deserialized into a dictionary representing their tree structure.
+This is achieved via the `json`, `yaml` and `xmltodict` packages, respectively.
+Special note has to be taken in the XML case which does not support lists and numbers.
+The logic concerning the translation from these dicts to the internal data model can be found in the `jsonlikedict` package.
+
+### `writer`
+
+For serialization purposes, only non-null fields are written out.
+All writers expect a valid SPDX document from the internal model as input.
+To ensure this is actually the case, the standard behaviour of every writer function is to call validation before the writing process.
+This can be disabled by setting the `validate` boolean to false.
+Also by default, all list properties in the model are scanned for duplicates which are being removed.
+This can be disabled by setting the `drop_duplicates` boolean to false.
+
+#### `tagvalue`
+
+The ordering of the tags follows the [example in the official specification](https://github.com/spdx/spdx-spec/blob/development/v2.3.1/examples/SPDXTagExample-v2.3.spdx).
+
+#### `rdf`
+
+The RDF graph is constructed from the internal data model and serialized to XML format afterward, using the `rdflib` library.
+
+#### `json`, `yaml`, `xml`
+
+As all three of JSON, YAML and XML formats share the same tree structure, the first step is to generate the dictionary representing that tree.
+This is achieved by the `DocumentConverter` class in the `jsonschema` package.
+Subsequently, the dictionary is serialized using the `json`, `yaml` and `xmltodict` packages, respectively.
+
+### `validation`
+
+The `validation` package takes care of all nonconformities with the SPDX specification that are not due to incorrect typing.
+This mainly includes checks for correctly formatted strings or the actual existence of references SPDXIDs.
+Entrypoint is the `document_validator` module with the `validate_full_spdx_document()` function.
+This library supports SPDX versions "SPDX-2.2" and "SPDX-2.3", which differ slightly in the validation process so that the version has to be specified here.
+This main validator calls subvalidators for all packages, files etc. that are contained in the document.
+Validators are split into two parts, where applicable: The first part validates the object on its own while the second validates it in the context of the whole document.
+Validation and reference checking of SPDXIDs (and possibly external document references) is done in the `spdx_id_validators` module.
+For the validation of license expressions we utilise the `license-expression` library's `validate` and `parse` functions, which take care of checking license symbols against the [SPDX license list](https://spdx.org/licenses/).
+
+Invalidities are captured in instances of a custom `ValidationMessage` class. This has two attributes:
+
+- `validation_message` is a string that describes the actual problem
+- `validation_context` is a `ValidationContext` object that helps to pinpoint the source of the problem by providing the faulty element's SPDXID (if it has one), the parent SPDXID (if that is known), the element's type and finally the full element itself.
+It is left open to the implementer which of this information to use in the following evaluation of the validation process.
+
+Every validation function returns a list of `ValidationMessage` objects, which are gradually concatenated until the final list is returned.
+That is, if an empty list is returned, the document is valid.
+
+## `spdx3`
+
+This package is still a work in progress.
+However, as the basic building blocks of parsing, writing, creation and validation are still important in the new version,
+the `spdx3` package is planned to be structured similarly to the `spdx` package.
+
+Additionally, the `bump_from_spdx2` package takes care of converting SPDX-2 documents to SPDX-3.
+Guideline for this is the [migration guide](https://docs.google.com/document/d/1-olHRnX1CssUS67Psv_sAq9Vd-pc81HF8MM0hA7M0hg).
diff --git a/README.md b/README.md
index 04a1431b4..a8e5cafd8 100644
--- a/README.md
+++ b/README.md
@@ -1,109 +1,233 @@
# Python library to parse, validate and create SPDX documents
-| Linux | macOS | Windows |
-|:-------------------------------|:------------------------------|:--------------------------------|
-| [ ![Linux build status][1]][2] | [![macOS build status][3]][4] | [![Windows build status][5]][6] |
+CI status (Linux, macOS and Windows): [![Install and Test][1]][2]
-[1]: https://travis-ci.org/spdx/tools-python.svg?branch=master
-[2]: https://travis-ci.org/spdx/tools-python
-[3]: https://circleci.com/gh/spdx/tools-python/tree/master.svg?style=shield&circle-token=36cca2dfa3639886fc34e22d92495a6773bdae6d
-[4]: https://circleci.com/gh/spdx/tools-python/tree/master
-[5]: https://ci.appveyor.com/api/projects/status/0bf9glha2yg9x8ef/branch/master?svg=true
-[6]: https://ci.appveyor.com/project/spdx/tools-python/branch/master
+[1]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml/badge.svg
+[2]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml
+## Breaking changes v0.7 -> v0.8
-# Information
+Please be aware that the upcoming 0.8 release has undergone a significant refactoring in preparation for the upcoming
+SPDX v3.0 release, leading to breaking changes in the API.
+Please refer to the [migration guide](https://github.com/spdx/tools-python/wiki/How-to-migrate-from-0.7-to-0.8)
+to update your existing code.
-This library implements SPDX parsers, convertors, validators and handlers in Python.
+The main features of v0.8 are:
-- Home: https://github.com/spdx/tools-python
-- Issues: https://github.com/spdx/tools-python/issues
-- PyPI: https://pypi.python.org/pypi/spdx-tools
+- full validation of SPDX documents against the v2.2 and v2.3 specification
+- support for SPDX's RDF format with all v2.3 features
+- experimental support for the upcoming SPDX v3 specification. Note, however, that support is neither complete nor
+ stable at this point, as the spec is still evolving. SPDX3-related code is contained in a separate subpackage "spdx3"
+ and its use is optional. We do not recommend using it in production code yet.
+Note that v0.8 only supports **writing**, not **reading** SPDX 3.0 documents.
+See [#760](https://github.com/spdx/tools-python/issues/760) for details.
-# History
+## Information
-This is the result of an initial GSoC contribution by @[ah450](https://github.com/ah450)
-(or https://github.com/a-h-i) and is maintained by a community of SPDX adopters and enthusiasts.
+This library implements SPDX parsers, convertors, validators and handlers in Python.
+
+- Home:
+- Issues:
+- PyPI:
+- Browse the API:
+Important updates regarding this library are shared via
+the SPDX tech mailing list: .
-# License
+## License
[Apache-2.0](LICENSE)
+## Features
-# Features
+- API to create and manipulate SPDX v2.2 and v2.3 documents
+- Parse, convert, create and validate SPDX files
+- Supported formats: Tag/Value, RDF, JSON, YAML, XML
+- Visualize the structure of a SPDX document by creating an `AGraph`.
+ Note: This is an optional feature and requires
+ additional installation of optional dependencies
-* API to create and manipulate SPDX v2.3 documents.
-* Parse, convert, create and validate Tag/Value, RDF, JSON, YAML, XML format SPDX files
+## Experimental support for SPDX 3.0
-### Known Limitations
+- Create v3.0 elements and payloads
+- Convert v2.2/v2.3 documents to v3.0
+- Serialize to JSON-LD
-* No full 2.3 support for RDF format [#295](https://github.com/spdx/tools-python/issues/295)
-* No full license expression support [#10](https://github.com/spdx/tools-python/issues/10)
-* Output of the CLI parser is incomplete [#268](https://github.com/spdx/tools-python/issues/268)
+See [Quickstart to SPDX 3.0](#quickstart-to-spdx-30) below.
+The implementation is based on the descriptive Markdown files in the repository
+
+(commit: a5372a3c145dbdfc1381fc1f791c68889aafc7ff).
+The latest SPDX 3.0 model is available at
+ .
-# TODOs
+## Installation
-* Include specialized validation for SPDX v2.2.1(ISO 5962:2021)
+As always you should work in a virtualenv (venv). You can install a local clone
+of this repo with `yourenv/bin/pip install .` or install it from PyPI
+(check for the [newest release](https://pypi.org/project/spdx-tools/#history) and install it like
+`yourenv/bin/pip install spdx-tools==0.8.3`). Note that on Windows it would be `Scripts`
+instead of `bin`.
+## How to use
-# How to use
+### Command-line usage
-## Command-line usage:
+1. **PARSING/VALIDATING** (for parsing any format):
-1. **PARSER** (for parsing any format):
-* Use `pyspdxtools_parser --file ` where `` is the location of the file.
-If you are using a source distribution, try running: `pyspdxtools_parser --file tests/data/formats/SPDXRdfExample.rdf`.
+ - Use `pyspdxtools -i ` where `` is the location of the file. The input format is inferred automatically from the file ending.
-* Or you can use `pyspdxtools_parser` only, and it will automatically prompt/ask for `filename`.
+ - If you are using a source distribution, try running:
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json`
-* For help use `pyspdxtools_parser --help`
+2. **CONVERTING** (for converting one format to another):
+ - Use `pyspdxtools -i -o ` where `` is the location of the file to be converted
+ and `` is the location of the output file. The input and output formats are inferred automatically from the file endings.
-2. **CONVERTOR** (for converting one format to another):
-* If I/O formats are known:
+ - If you are using a source distribution, try running:
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o output.tag`
- * Use `pyspdxtools_convertor --infile/-i --outfile/-o ` where `` is the location of the file to be converted
- and `` is the location of the output file.
- If you are using a source distribution, try running : `pyspdxtools_convertor --infile tests/data/formats/SPDXRdfExample.rdf --outfile output.json`
+ - If you want to skip the validation process, provide the `--novalidation` flag, like so:
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation`
+ (use this with caution: note that undetected invalid documents may lead to unexpected behavior of the tool)
-* If I/O formats are not known:
+ - For help use `pyspdxtools --help`
- * Use `pyspdxtools_convertor --from/-f --to/-t ` where `` is the manually entered format of the input file
- and `` is the manually entered format of the output file.
- If you are using a source distribution, try running : `pyspdxtools_convertor --from tag tests/data/formats/SPDXTagExample.in --to yaml output.out`
+3. **GRAPH GENERATION** (optional feature)
-* If one of the formats is known and the other is not, you can use a mixture of the above two points.
-Example (if you are using a source distribution): `pyspdxtools_convertor -f rdf tests/data/formats/SPDXRdfExample.xyz -o output.xml`
+ - This feature generates a graph representing all elements in the SPDX document and their connections based on the provided
+ relationships. The graph can be rendered to a picture. Below is an example for the file `tests/spdx/data/SPDXJSONExample-v2.3.spdx.json`:
+ 
-* For help use `pyspdxtools_convertor --help`
+ - Make sure you install the optional dependencies `networkx` and `pygraphviz`. To do so run `pip install ".[graph_generation]"`.
+ - Use `pyspdxtools -i --graph -o ` where `` is an output file name with valid format for `pygraphviz` (check
+ the documentation [here](https://pygraphviz.github.io/documentation/stable/reference/agraph.html#pygraphviz.AGraph.draw)).
+ - If you are using a source distribution, try running
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json --graph -o SPDXJSONExample-v2.3.spdx.png` to generate
+ a png with an overview of the structure of the example file.
+### Library usage
-# Installation
+1. **DATA MODEL**
-As always you should work in a virtualenv (venv). You can install a local clone
-of this repo with `yourenv/bin/pip install .` or install it from PyPI with
-`yourenv/bin/pip install spdx-tools`. Note that on Windows it would be `Scripts`
-instead of `bin`.
+ - The `spdx_tools.spdx.model` package constitutes the internal SPDX v2.3 data model (v2.2 is simply a subset of this). All relevant classes for SPDX document creation are exposed in the `__init__.py` found [here](src%2Fspdx_tools%2Fspdx%2Fmodel%2F__init__.py).
+ - SPDX objects are implemented via `@dataclass_with_properties`, a custom extension of `@dataclass`.
+ - Each class starts with a list of its properties and their possible types. When no default value is provided, the property is mandatory and must be set during initialization.
+ - Using the type hints, type checking is enforced when initializing a new instance or setting/getting a property on an instance
+ (wrong types will raise `ConstructorTypeError` or `TypeError`, respectively). This makes it easy to catch invalid properties early and only construct valid documents.
+ - Note: in-place manipulations like `list.append(item)` will circumvent the type checking (a `TypeError` will still be raised when reading `list` again). We recommend using `list = list + [item]` instead.
+ - The main entry point of an SPDX document is the `Document` class from the [document.py](src%2Fspdx_tools%2Fspdx%2Fmodel%2Fdocument.py) module, which links to all other classes.
+ - For license handling, the [license_expression](https://github.com/nexB/license-expression) library is used.
+ - Note on `documentDescribes` and `hasFiles`: These fields will be converted to relationships in the internal data model. As they are deprecated, these fields will not be written in the output.
+
+2. **PARSING**
+
+ - Use `parse_file(file_name)` from the `parse_anything.py` module to parse an arbitrary file with one of the supported file endings.
+ - Successful parsing will return a `Document` instance. Unsuccessful parsing will raise `SPDXParsingError` with a list of all encountered problems.
+
+3. **VALIDATING**
+
+ - Use `validate_full_spdx_document(document)` to validate an instance of the `Document` class.
+ - This will return a list of `ValidationMessage` objects, each consisting of a String describing the invalidity and a `ValidationContext` to pinpoint the source of the validation error.
+ - Validation depends on the SPDX version of the document. Note that only versions `SPDX-2.2` and `SPDX-2.3` are supported by this tool.
+
+4. **WRITING**
+
+ - Use `write_file(document, file_name)` from the `write_anything.py` module to write a `Document` instance to the specified file.
+ The serialization format is determined from the filename ending.
+ - Validation is performed per default prior to the writing process, which is cancelled if the document is invalid. You can skip the validation via `write_file(document, file_name, validate=False)`.
+ Caution: Only valid documents can be serialized reliably; serialization of invalid documents is not supported.
+
+### Example
+
+Here are some examples of possible use cases to quickly get you started with the spdx-tools.
+If you want more examples, like how to create an SPDX document from scratch, have a look [at the examples folder](examples).
+```python
+import logging
-# Dependencies
+from license_expression import get_spdx_licensing
-* PLY: https://pypi.python.org/pypi/ply/ used for parsing.
-* rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF.
-* PyYAML: https://pypi.org/project/PyYAML/ for handling YAML.
-* xmltodict: https://pypi.org/project/xmltodict/ for handling XML.
-* click: https://pypi.org/project/click/ for creating the CLI interface.
+from spdx_tools.spdx.model import (Checksum, ChecksumAlgorithm, File,
+ FileType, Relationship, RelationshipType)
+from spdx_tools.spdx.parser.parse_anything import parse_file
+from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
+from spdx_tools.spdx.writer.write_anything import write_file
+# read in an SPDX document from a file
+document = parse_file("spdx_document.json")
-# Support
+# change the document's name
+document.creation_info.name = "new document name"
-* Submit issues, questions or feedback at https://github.com/spdx/tools-python/issues
-* Join the chat at https://gitter.im/spdx-org/Lobby
-* Join the discussion on https://lists.spdx.org/g/spdx-tech and
- https://spdx.dev/participate/tech/
+# define a file and a DESCRIBES relationship between the file and the document
+checksum = Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c")
-# Contributing
+file = File(name="./fileName.py", spdx_id="SPDXRef-File", checksums=[checksum],
+ file_types=[FileType.TEXT],
+ license_concluded=get_spdx_licensing().parse("MIT and GPL-2.0"),
+ license_comment="licenseComment", copyright_text="copyrightText")
-Contributions are very welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to contribute to the codebase.
+relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File")
+
+# add the file and the relationship to the document
+# (note that we do not use "document.files.append(file)" as that would circumvent the type checking)
+document.files = document.files + [file]
+document.relationships = document.relationships + [relationship]
+
+# validate the edited document and log the validation messages
+# (depending on your use case, you might also want to utilize the validation_message.context)
+validation_messages = validate_full_spdx_document(document)
+for validation_message in validation_messages:
+ logging.warning(validation_message.validation_message)
+
+# if there are no validation messages, the document is valid
+# and we can safely serialize it without validating again
+if not validation_messages:
+ write_file(document, "new_spdx_document.rdf", validate=False)
+```
+
+## Quickstart to SPDX 3.0
+
+In contrast to SPDX v2, all elements are now subclasses of the central `Element` class.
+This includes packages, files, snippets, relationships, annotations, but also SBOMs, SpdxDocuments, and more.
+For serialization purposes, all Elements that are to be serialized into the same file are collected in a `Payload`.
+This is just a dictionary that maps each Element's SpdxId to itself.
+Use the `write_payload()` functions to serialize a payload.
+There currently are two options:
+
+- The `spdx_tools.spdx3.writer.json_ld.json_ld_writer` module generates a JSON-LD file of the payload.
+- The `spdx_tools.spdx3.writer.console.payload_writer` module prints a debug output to console. Note that this is not an official part of the SPDX specification and will probably be dropped as soon as a better standard emerges.
+
+You can convert an SPDX v2 document to v3 via the `spdx_tools.spdx3.bump_from_spdx2.spdx_document` module.
+The `bump_spdx_document()` function will return a payload containing an `SpdxDocument` Element and one Element for each package, file, snippet, relationship, or annotation contained in the v2 document.
+
+## Dependencies
+
+- PyYAML: for handling YAML.
+- xmltodict: for handling XML.
+- rdflib: for handling RDF.
+- ply: for handling tag-value.
+- click: for creating the CLI interface.
+- beartype: for type checking.
+- uritools: for validation of URIs.
+- license-expression: for handling SPDX license expressions.
+
+## Support
+
+- Submit issues, questions or feedback at
+- Join the chat at
+- Join the discussion on and
+
+
+## Contributing
+
+Contributions are very welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to contribute to the
+codebase.
+
+## History
+
+This is the result of an initial GSoC contribution by @[ah450](https://github.com/ah450)
+(or ) and is maintained by a community of SPDX adopters and enthusiasts.
+In order to prepare for the release of SPDX v3.0, the repository has undergone a major refactoring during the time from 11/2022 to 07/2023.
diff --git a/assets/SPDXJSONExample-v2.3.spdx.png b/assets/SPDXJSONExample-v2.3.spdx.png
new file mode 100644
index 000000000..1d050a166
Binary files /dev/null and b/assets/SPDXJSONExample-v2.3.spdx.png differ
diff --git a/dev/publish_from_tag.sh b/dev/publish_from_tag.sh
new file mode 100755
index 000000000..eb367d781
--- /dev/null
+++ b/dev/publish_from_tag.sh
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+set -euo pipefail
+
+if [ $# -eq 0 ]; then
+ cat< /dev/null; then
+ echo "twine could not be found"
+ echo "maybe load venv with"
+ echo " . ./venv/bin/activate"
+ echo " . ./venv/bin/activate.fish"
+ echo
+
+ if [[ -d ./venv/bin/ ]]; then
+ echo "will try to activate ./venv ..."
+
+ source ./venv/bin/activate
+
+ if ! command -v twine &> /dev/null; then
+ echo "twine still could not be found"
+ exit 1
+ fi
+ else
+ exit 1
+ fi
+fi
+
+
+if [[ -d "$tag_dir" ]]; then
+ echo "the dir \"$tag_dir\" already exists, exiting for safety"
+ exit 1
+fi
+
+mkdir -p "$tag_dir"
+(cd "$tag_dir" && wget -c "$tar_gz" -O - | tar --strip-components=1 -xz)
+
+twine check "${tag_dir}/spdx-tools-${version}.tar.gz" "${tag_dir}/spdx_tools-${version}-py3-none-any.whl"
+read -r -p "Do you want to upload? [y/N] " response
+case "$response" in
+ [yY][eE][sS]|[yY])
+ twine upload -r pypi "${tag_dir}/spdx-tools-${version}.tar.gz" "${tag_dir}/spdx_tools-${version}-py3-none-any.whl"
+ ;;
+esac
diff --git a/examples/__init__.py b/examples/__init__.py
deleted file mode 100644
index 1f63eb496..000000000
--- a/examples/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2020 Yash Varshney
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
diff --git a/examples/pp_rdf.py b/examples/pp_rdf.py
deleted file mode 100755
index 4545b0fc1..000000000
--- a/examples/pp_rdf.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-
-# Parses an RDF file and writes it out pretty-printed.
-# Usage: pp_rdf
-
-if __name__ == "__main__":
- import sys
- from spdx.parsers.rdf import Parser
- from spdx.parsers.loggers import StandardLogger
- from spdx.parsers.rdfbuilders import Builder
- from spdx.writers.rdf import write_document
-
- infile = sys.argv[1]
- outfile = sys.argv[2]
- p = Parser(Builder(), StandardLogger())
- with open(infile) as f:
- doc, error = p.parse(f)
- if not error:
- with open(outfile, mode="wb") as out:
- write_document(doc, out)
-
- else:
- print("Errors while parsing")
diff --git a/examples/pp_tv.py b/examples/pp_tv.py
deleted file mode 100755
index e0de82159..000000000
--- a/examples/pp_tv.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-
-# Parses a tag/value file and writes it out pretty-printed.
-# Usage: pp_tv
-if __name__ == "__main__":
- import sys
- import codecs
- from spdx.writers.tagvalue import write_document, InvalidDocumentError
- from spdx.parsers.tagvalue import Parser
- from spdx.parsers.loggers import StandardLogger
- from spdx.parsers.loggers import ErrorMessages
- from spdx.parsers.tagvaluebuilders import Builder
-
- source = sys.argv[1]
- target = sys.argv[2]
- p = Parser(Builder(), StandardLogger())
- p.build()
- with open(source, "r") as f:
- data = f.read()
- document, error = p.parse(data)
- if not error:
- print("Parsing Successful")
- with codecs.open(target, mode="w", encoding="utf-8") as out:
- try:
- write_document(document, out)
- except InvalidDocumentError:
- print("Document is Invalid")
- messages = ErrorMessages()
- document.validate(messages)
- print("\n".join(messages.messages))
- else:
- print("Errors encountered while parsing")
diff --git a/examples/spdx2_convert_format.py b/examples/spdx2_convert_format.py
new file mode 100644
index 000000000..63cb4a67a
--- /dev/null
+++ b/examples/spdx2_convert_format.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from os import path
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.writer.write_anything import write_file
+from spdx_tools.spdx.parser.parse_anything import parse_file
+
+# This example demonstrates how to load an existing SPDX2 file and convert it to a different SPDX2 format
+
+# Provide a path to the input file in the originating format
+input_path = path.join(path.dirname(__file__), "..", "tests", "spdx", "data", "SPDXLite.spdx")
+# Parse the original input file (format is deduced automatically from the file extension)
+document: Document = parse_file(input_path)
+# Write to a different file format (e.g. XML, format is deduced automatically from the file extension)
+write_file(document, "converted_format.xml")
diff --git a/examples/spdx2_convert_to_spdx3.py b/examples/spdx2_convert_to_spdx3.py
new file mode 100644
index 000000000..ebbbbc7c7
--- /dev/null
+++ b/examples/spdx2_convert_to_spdx3.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from os import path
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx3.writer.json_ld.json_ld_writer import write_payload
+from spdx_tools.spdx3.bump_from_spdx2.spdx_document import bump_spdx_document
+from spdx_tools.spdx.parser.parse_anything import parse_file
+
+# This example demonstrates how to load an existing SPDX2 file and convert it to the SPDX3 format
+
+# Provide a path to the input file
+input_path = path.join(path.dirname(__file__), "..", "tests", "spdx", "data", "SPDXLite.spdx")
+# Parse the original SPDX2 input file
+spdx2_document: Document = parse_file(input_path)
+# Convert original document to an SPDX3 payload
+spdx3_payload: Payload = bump_spdx_document(spdx2_document)
+# Write SPDX3 payload in json-ld format
+write_payload(spdx3_payload, "spdx2_to_3")
diff --git a/examples/spdx2_document_from_scratch.py b/examples/spdx2_document_from_scratch.py
new file mode 100644
index 000000000..bc92175a8
--- /dev/null
+++ b/examples/spdx2_document_from_scratch.py
@@ -0,0 +1,145 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import logging
+from datetime import datetime
+from typing import List
+
+from spdx_tools.common.spdx_licensing import spdx_licensing
+from spdx_tools.spdx.model import (
+ Actor,
+ ActorType,
+ Checksum,
+ ChecksumAlgorithm,
+ CreationInfo,
+ Document,
+ ExternalPackageRef,
+ ExternalPackageRefCategory,
+ File,
+ FileType,
+ Package,
+ PackagePurpose,
+ PackageVerificationCode,
+ Relationship,
+ RelationshipType,
+)
+from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
+from spdx_tools.spdx.validation.validation_message import ValidationMessage
+from spdx_tools.spdx.writer.write_anything import write_file
+
+# This example shows how to use the spdx-tools to create an SPDX document from scratch,
+# validate it and write it to a file.
+
+# First up, we need general information about the creation of the document, summarised by the CreationInfo class.
+creation_info = CreationInfo(
+ spdx_version="SPDX-2.3",
+ spdx_id="SPDXRef-DOCUMENT",
+ name="document name",
+ data_license="CC0-1.0",
+ document_namespace="https://some.namespace",
+ creators=[Actor(ActorType.PERSON, "Jane Doe", "jane.doe@example.com")],
+ created=datetime(2022, 1, 1),
+)
+
+# creation_info is the only required property of the Document class (have a look there!), the rest are optional lists.
+# So, we are set up to create a new document instance.
+document = Document(creation_info)
+
+# The document currently does not describe anything. Let's create a package that we can add to it.
+# The Package class has quite a few properties (have a look there!),
+# but only name, spdx_id and download_location are mandatory in SPDX v2.3.
+package = Package(
+ name="package name",
+ spdx_id="SPDXRef-Package",
+ download_location="https://download.com",
+ version="2.2.1",
+ file_name="./foo.bar",
+ supplier=Actor(ActorType.PERSON, "Jane Doe", "jane.doe@example.com"),
+ originator=Actor(ActorType.ORGANIZATION, "some organization", "contact@example.com"),
+ files_analyzed=True,
+ verification_code=PackageVerificationCode(
+ value="d6a770ba38583ed4bb4525bd96e50461655d2758", excluded_files=["./some.file"]
+ ),
+ checksums=[
+ Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"),
+ Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24"),
+ ],
+ license_concluded=spdx_licensing.parse("GPL-2.0-only OR MIT"),
+ license_info_from_files=[spdx_licensing.parse("GPL-2.0-only"), spdx_licensing.parse("MIT")],
+ license_declared=spdx_licensing.parse("GPL-2.0-only AND MIT"),
+ license_comment="license comment",
+ copyright_text="Copyright 2022 Jane Doe",
+ description="package description",
+ attribution_texts=["package attribution"],
+ primary_package_purpose=PackagePurpose.LIBRARY,
+ release_date=datetime(2015, 1, 1),
+ external_references=[
+ ExternalPackageRef(
+ category=ExternalPackageRefCategory.OTHER,
+ reference_type="http://reference.type",
+ locator="reference/locator",
+ comment="external reference comment",
+ )
+ ],
+)
+
+# Now that we have a package defined, we can add it to the document's package property.
+document.packages = [package]
+
+# A DESCRIBES relationship asserts that the document indeed describes the package.
+describes_relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Package")
+document.relationships = [describes_relationship]
+
+# Let's add two files. Have a look at the file class for all possible properties a file can have.
+file1 = File(
+ name="./package/file1.py",
+ spdx_id="SPDXRef-File1",
+ file_types=[FileType.SOURCE],
+ checksums=[
+ Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"),
+ Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24"),
+ ],
+ license_concluded=spdx_licensing.parse("MIT"),
+ license_info_in_file=[spdx_licensing.parse("MIT")],
+ copyright_text="Copyright 2022 Jane Doe",
+)
+file2 = File(
+ name="./package/file2.py",
+ spdx_id="SPDXRef-File2",
+ checksums=[
+ Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2759"),
+ ],
+ license_concluded=spdx_licensing.parse("GPL-2.0-only"),
+)
+
+# Assuming the package contains those two files, we create two CONTAINS relationships.
+contains_relationship1 = Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File1")
+contains_relationship2 = Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File2")
+
+# This library uses run-time type checks when assigning properties.
+# Because in-place alterations like .append() circumvent these checks, we don't use them here.
+document.relationships += [contains_relationship1, contains_relationship2]
+document.files += [file1, file2]
+
+# We now have created a document with basic creation information, describing a package that contains two files.
+# You can also add Annotations, Snippets and ExtractedLicensingInfo to the document in an analogous manner to the above.
+# Have a look at their respective classes if you are unsure about their properties.
+
+
+# This library provides comprehensive validation against the SPDX specification.
+# Note that details of the validation depend on the SPDX version of the document.
+validation_messages: List[ValidationMessage] = validate_full_spdx_document(document)
+
+# You can have a look at each entry's message and context (like spdx_id, parent_id, full_element)
+# which will help you pinpoint the location of the invalidity.
+for message in validation_messages:
+ logging.warning(message.validation_message)
+ logging.warning(message.context)
+
+# If the document is valid, validation_messages will be empty.
+assert validation_messages == []
+
+# Finally, we can serialize the document to any of the five supported formats.
+# Using the write_file() method from the write_anything module,
+# the format will be determined by the file ending: .spdx (tag-value), .json, .xml, .yaml. or .rdf (or .rdf.xml)
+write_file(document, "my_spdx_document.spdx.json")
diff --git a/examples/spdx2_generate_graph.py b/examples/spdx2_generate_graph.py
new file mode 100644
index 000000000..dad7dcfce
--- /dev/null
+++ b/examples/spdx2_generate_graph.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from os import path
+
+from spdx_tools.spdx.graph_generation import export_graph_from_document
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.parse_anything import parse_file
+
+# This example demonstrates how to generate a relationship graph for an SPDX2 document
+
+# Provide a path to the input file
+input_path = path.join(path.dirname(__file__), "..", "tests", "spdx", "data", "SPDXJSONExample-v2.3.spdx.json")
+# Parse the file
+document: Document = parse_file(input_path)
+# Generate the graph (note: you need to have installed the optional dependencies networkx and pygraphviz)
+export_graph_from_document(document, "graph.png")
diff --git a/examples/spdx2_parse_file.py b/examples/spdx2_parse_file.py
new file mode 100644
index 000000000..a6564e127
--- /dev/null
+++ b/examples/spdx2_parse_file.py
@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import logging
+from os import path
+
+from spdx_tools.spdx.model.document import Document
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.parse_anything import parse_file
+
+# This example demonstrates how to parse an existing spdx file.
+
+# Provide a path to the input file
+input_path = path.join(path.dirname(__file__), "..", "tests", "spdx", "data", "SPDXLite.spdx")
+try:
+ # Try to parse the input file. If successful, returns a Document, otherwise raises an SPDXParsingError
+ document: Document = parse_file(input_path)
+except SPDXParsingError:
+ logging.exception("Failed to parse spdx file")
+
+# We can now access attributes from the parsed document
+print(f"Parsed document name: {document.creation_info.name}")
+creators_as_str = ", ".join([creator.to_serialized_string() for creator in document.creation_info.creators])
+print(f"Created on {document.creation_info.created} by {creators_as_str}")
diff --git a/examples/tv_to_rdf.py b/examples/tv_to_rdf.py
deleted file mode 100755
index db890961e..000000000
--- a/examples/tv_to_rdf.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2017 BMW AG
-# Author: Thomas Hafner
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import sys
-
-from spdx.parsers.loggers import StandardLogger
-from spdx.writers.rdf import write_document
-from spdx.parsers.tagvalue import Parser
-from spdx.parsers.tagvaluebuilders import Builder
-from spdx.parsers.loggers import ErrorMessages
-
-
-def tv_to_rdf(infile_name, outfile_name):
- """
- Convert a SPDX file from tag/value format to RDF format.
- Return True on success, False otherwise.
- """
- parser = Parser(Builder(), StandardLogger())
- parser.build()
- with open(infile_name) as infile:
- data = infile.read()
- document, error = parser.parse(data)
- if not error:
- with open(outfile_name, mode="wb") as outfile:
- write_document(document, outfile)
- return True
- else:
- print("Errors encountered while parsing RDF file.")
- messages = ErrorMessages()
- document.validate(messages)
- print("\n".join(messages.messages))
- return False
-
-
-def main():
- args = sys.argv[1:]
- if not args:
- print(
- "Usage: spdx-tv2rdf \n"
- "Convert an SPDX tag/value document to RDF."
- )
- sys.exit(1)
-
- tv_file = args[0]
- rdf_file = args[1]
- success = tv_to_rdf(tv_file, rdf_file)
- sys.exit(0 if success else 1)
-
-
-if __name__ == "__main__":
- main()
diff --git a/examples/write_tv.py b/examples/write_tv.py
deleted file mode 100755
index 0dec0f2f3..000000000
--- a/examples/write_tv.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env python
-from spdx.relationship import Relationship
-
-# Writes a new tag/value file from scratch.
-# Usage: write_tv
-if __name__ == "__main__":
- import sys
- import codecs
- from spdx.writers.tagvalue import write_document, InvalidDocumentError
- from spdx.parsers.loggers import ErrorMessages
- from spdx.document import Document
- from spdx.license import License, LicenseConjunction, ExtractedLicense
- from spdx.version import Version
- from spdx.creationinfo import Person
- from spdx.review import Review
- from spdx.package import Package
- from spdx.file import File, FileType
- from spdx.checksum import Checksum
- from spdx.utils import SPDXNone, NoAssert, UnKnown
-
- doc = Document()
- doc.version = Version(1, 2)
- doc.name = "Hello_SPDX"
- doc.spdx_id = "Test#SPDXRef-DOCUMENT"
- doc.comment = "Example Document"
- doc.namespace = "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301"
- doc.data_license = License.from_identifier("CC0-1.0")
- doc.creation_info.add_creator(Person("Alice", "alice@example.com"))
- doc.creation_info.set_created_now()
- review = Review(Person("Joe", None))
- review.set_review_date_now()
- review.comment = "Joe reviewed this document"
- doc.add_review(review)
- # File
- testfile1 = File("TestFile1")
- testfile1.type = FileType.BINARY
- testfile1.spdx_id = "TestFilet#SPDXRef-FILE"
- testfile1.comment = "This is a test file."
- testfile1.chksum = Checksum("SHA1", "c537c5d99eca5333f23491d47ededd083fefb7ad")
- testfile1.conc_lics = License.from_identifier("BSD-2-Clause")
- testfile1.add_lics(License.from_identifier("BSD-2-Clause"))
- testfile1.copyright = SPDXNone()
- testfile1.add_artifact("name", "TagWriteTest")
- testfile1.add_artifact("home", UnKnown())
- testfile1.add_artifact("uri", "http://tagwritetest.test")
-
- testfile2 = File("TestFile2")
- testfile2.type = FileType.SOURCE
- testfile2.spdx_id = "TestFile2#SPDXRef-FILE"
- testfile2.comment = "This is a test file."
- testfile2.chksum = Checksum("SHA1", "bb154f28d1cf0646ae21bb0bec6c669a2b90e113")
- testfile2.conc_lics = License.from_identifier("Apache-2.0")
- testfile2.add_lics(License.from_identifier("Apache-2.0"))
- testfile2.copyright = NoAssert()
- doc.add_file(testfile1)
- doc.add_file(testfile2)
-
- # Package
- package = Package()
- package.name = "TagWriteTest"
- package.version = "1.0"
- package.file_name = "twt.jar"
- package.spdx_id = 'TestPackage#SPDXRef-PACKAGE'
- package.download_location = "http://www.tagwritetest.test/download"
- package.checksum = Checksum("SHA1", "c537c5d99eca5333f23491d47ededd083fefb7ad")
- package.homepage = SPDXNone()
- package.verif_code = "4e3211c67a2d28fced849ee1bb76e7391b93feba"
- license_set = LicenseConjunction(
- License.from_identifier("Apache-2.0"), License.from_identifier("BSD-2-Clause")
- )
- package.conc_lics = license_set
- package.license_declared = license_set
- package.add_lics_from_file(License.from_identifier("Apache-2.0"))
- package.add_lics_from_file(License.from_identifier("BSD-2-Clause"))
- package.cr_text = NoAssert()
- package.summary = "Simple package."
- package.description = "Really simple package."
-
- doc.package = package
- relationship = Relationship("TestPackage#SPDXRef-PACKAGE CONTAINS TestFilet#SPDXRef-FILE")
- doc.add_relationship(relationship)
- relationship = Relationship("TestPackage#SPDXRef-PACKAGE CONTAINS TestFile2#SPDXRef-FILE")
- doc.add_relationship(relationship)
-
- # An extracted license
-
- lic = ExtractedLicense("LicenseRef-1")
- lic.text = "Some non legal legal text.."
- doc.add_extr_lic(lic)
-
- file = sys.argv[1]
- with codecs.open(file, mode="w", encoding="utf-8") as out:
- try:
- write_document(doc, out)
- except InvalidDocumentError as e:
- print("Document is Invalid:\n\t", end="")
- print("\n\t".join(e.args[0]))
- messages = ErrorMessages()
- doc.validate(messages)
- print("\n".join(messages.messages))
diff --git a/pyproject.toml b/pyproject.toml
index 6c44a244f..e773d841c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,45 +4,86 @@ build-backend = "setuptools.build_meta"
[project]
name = "spdx-tools"
-authors = [{name = "Ahmed H. Ismail", email = "ahm3d.hisham@gmail.com"}]
+authors = [{ name = "Ahmed H. Ismail", email = "ahm3d.hisham@gmail.com" }]
maintainers = [
- {name = "Philippe Ombredanne", email = "pombredanne@gmail.com"},
- {name = "SPDX group at the Linux Foundation and others"},
+ { name = "Philippe Ombredanne", email = "pombredanne@gmail.com" },
+ { name = "SPDX group at the Linux Foundation and others" },
]
-license = {text = "Apache-2.0"}
+license = "Apache-2.0"
description = "SPDX parser and tools."
readme = "README.md"
classifiers = [
+ "Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
- "License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+ "Topic :: Software Development :: Documentation",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Utilities",
+]
+requires-python = ">=3.10"
+dependencies = [
+ "beartype",
+ "click",
+ "license_expression",
+ "ply",
+ "pyyaml",
+ "rdflib",
+ "semantic_version",
+ "uritools",
+ "xmltodict",
]
-urls = {Homepage = "https://github.com/spdx/tools-python"}
-requires-python = ">=3.7"
-dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict"]
dynamic = ["version"]
[project.optional-dependencies]
-test = ["pytest"]
+test = ["pyshacl", "pytest", "tzdata"]
+code_style = ["black", "flake8", "isort"]
+graph_generation = ["networkx", "pygraphviz"]
+development = ["black", "flake8", "isort", "networkx", "pyshacl", "pytest"]
[project.scripts]
-pyspdxtools_convertor = "spdx.cli_tools.convertor:main"
-pyspdxtools_parser = "spdx.cli_tools.parser:main"
+pyspdxtools = "spdx_tools.spdx.clitools.pyspdxtools:main"
+pyspdxtools3 = "spdx_tools.spdx3.clitools.pyspdxtools3:main"
+
+[project.urls]
+Homepage = "https://github.com/spdx/tools-python"
+Documentation = "https://spdx.github.io/tools-python/"
+Repository = "https://github.com/spdx/tools-python.git"
+Issues = "https://github.com/spdx/tools-python/issues"
+Changelog = "https://github.com/spdx/tools-python/blob/main/CHANGELOG.md"
[tool.setuptools]
-zip-safe = false # because of the uses of __file__: https://github.com/spdx/tools-python/issues/257
include-package-data = true
[tool.setuptools.packages.find]
-include = ["spdx", "spdx.*"]
+where = ["src"]
+
+[tool.setuptools.package-data]
+"*" = ["py.typed"]
+# the default git describe resolves to the tag `python3.6` because the current release tag is not on main
+# by adding "v" the command resolves to the alpha release of 0.7.0 which leads to the desired name spdx-tools-0.7.0
[tool.setuptools_scm]
-git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"] # `python3.6` tag falsely matches to the default one, clrearly a bug in setuptools_scm
+git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"]
[tool.aliases]
release = "clean --all sdist --formats=gztar bdist_wheel"
+
+[tool.black]
+line-length = 119
+include = "(^/src/.*.py|^/tests/.*.py)"
+
+[tool.isort]
+profile = "black"
+line_length = 119
+skip = ["__init__.py"]
+
+[tool.pytest.ini_options]
+norecursedirs = [] # overwrite the default to not skip tests/build folder which is needed in spdx3
diff --git a/spdx/__init__.py b/spdx/__init__.py
deleted file mode 100644
index 5284146eb..000000000
--- a/spdx/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__import__("pkg_resources").declare_namespace(__name__)
diff --git a/spdx/annotation.py b/spdx/annotation.py
deleted file mode 100644
index 51cff82d9..000000000
--- a/spdx/annotation.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# Copyright (c) 2018 Yash M. Nisar
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from datetime import datetime
-from functools import total_ordering
-
-from spdx.utils import datetime_iso_format
-
-
-@total_ordering
-class Annotation(object):
-
- """
- Document annotation information.
- Fields:
- - annotator: Person, Organization or tool that has commented on a file,
- package, or the entire document. Conditional (Mandatory, one), if there
- is an Annotation.
- - annotation_date: To identify when the comment was made. Conditional
- (Mandatory, one), if there is an Annotation. Type: datetime.
- - comment: Annotation comment. Conditional (Mandatory, one), if there is
- an Annotation. Type: str.
- - annotation_type: Annotation type. Conditional (Mandatory, one), if there is an
- Annotation. Type: str.
- - spdx_id: Uniquely identify the element in an SPDX document which is being
- referenced. Conditional (Mandatory, one), if there is an Annotation.
- Type: str.
- """
-
- def __init__(
- self,
- annotator=None,
- annotation_date=None,
- comment=None,
- annotation_type=None,
- spdx_id=None,
- ):
- self.annotator = annotator
- self.annotation_date = annotation_date
- self.comment = comment
- self.annotation_type = annotation_type
- self.spdx_id = spdx_id
-
- def __eq__(self, other):
- return (
- isinstance(other, Annotation)
- and self.annotator == other.annotator
- and self.annotation_date == other.annotation_date
- and self.comment == other.comment
- )
-
- def __lt__(self, other):
- return (self.annotator, self.annotation_date, self.comment) < (
- other.annotator,
- other.annotation_date,
- other.comment,
- )
-
- def set_annotation_date_now(self):
- self.annotation_date = datetime.utcnow()
-
- @property
- def annotation_date_iso_format(self):
- return datetime_iso_format(self.annotation_date)
-
- @property
- def has_comment(self):
- return self.comment is not None
-
- def validate(self, messages):
- """
- Check that all the fields are valid.
- Appends any error messages to messages parameter shall be a ErrorMessages.
- """
- self.validate_annotator(messages)
- self.validate_annotation_date(messages)
- self.validate_annotation_type(messages)
- self.validate_spdx_id(messages)
-
- return messages
-
- def validate_annotator(self, messages):
- if self.annotator is None:
- messages.append("Annotation missing annotator.")
-
- def validate_annotation_date(self, messages):
- if self.annotation_date is None:
- messages.append("Annotation missing annotation date.")
-
- def validate_annotation_type(self, messages):
- if self.annotation_type is None:
- messages.append("Annotation missing annotation type.")
-
- def validate_spdx_id(self, messages):
- if self.spdx_id is None:
- messages.append("Annotation missing SPDX Identifier Reference.")
diff --git a/spdx/checksum.py b/spdx/checksum.py
deleted file mode 100644
index 315814269..000000000
--- a/spdx/checksum.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-import re
-from enum import Enum, auto
-
-
-class ChecksumAlgorithm(Enum):
- SHA1 = auto()
- SHA224 = auto()
- SHA256 = auto()
- SHA384 = auto()
- SHA512 = auto()
- SHA3_256 = auto()
- SHA3_384 = auto()
- SHA3_512 = auto()
- BLAKE2B_256 = auto()
- BLAKE2B_384 = auto()
- BLAKE2B_512 = auto()
- BLAKE3 = auto()
- MD2 = auto()
- MD4 = auto()
- MD5 = auto()
- MD6 = auto()
- ADLER32 = auto()
-
- def algorithm_to_rdf_representation(self) -> str:
- if self.name.startswith("BLAKE2B"):
- return "checksumAlgorithm_" + self.name.replace("_", "").lower()
- else:
- return "checksumAlgorithm_" + self.name.lower()
-
- @classmethod
- def checksum_from_rdf(cls, identifier: str) -> 'ChecksumAlgorithm':
- identifier = identifier.split('_', 1)[-1].upper()
- blake_checksum = re.compile(r"^(BLAKE2B)(256|384|512)$", re.UNICODE)
- match = blake_checksum.match(identifier)
- if match:
- identifier = match[1] + '_' + match[2]
- if identifier not in ChecksumAlgorithm.__members__:
- raise ValueError(f"Invalid algorithm for checksum: {identifier}")
- return ChecksumAlgorithm[identifier]
-
- @classmethod
- def checksum_algorithm_from_string(cls, identifier: str) -> 'ChecksumAlgorithm':
- identifier = identifier.replace("-", "_").upper()
- if identifier not in ChecksumAlgorithm.__members__:
- raise ValueError(f"Invalid algorithm for checksum: {identifier}")
- return ChecksumAlgorithm[identifier]
-
-
-class Checksum(object):
- """Generic checksum algorithm."""
-
- def __init__(self, identifier: ChecksumAlgorithm, value: str):
- self.identifier = identifier
- self.value = value
-
- def __eq__(self, other) -> bool:
- if not isinstance(other, Checksum):
- return False
- return self.identifier == other.identifier and self.value == other.value
-
- @classmethod
- def checksum_from_string(cls, value: str) -> 'Checksum':
- CHECKSUM_RE = re.compile("(ADLER32|BLAKE2b-256|BLAKE2b-384|BLAKE2b-512|BLAKE3|MD2|MD4|MD5|MD6|" \
- "SHA1|SHA224|SHA256|SHA384|SHA512|SHA3-256|SHA3-384|SHA3-512):\\s*([a-fA-F0-9]*)")
- match = CHECKSUM_RE.match(value)
- if match is None or match.group(1) is None or match.group(2) is None:
- raise ValueError(f"Invalid checksum: {value}")
- identifier = ChecksumAlgorithm.checksum_algorithm_from_string(match.group(1))
- return Checksum(identifier=identifier, value=match.group(2))
-
- def to_tv(self) -> str:
- algorithm_name: str = self.identifier.name
- # Convert underscores to dashes, and other Blake2b-specific casing rules
- if "_" in algorithm_name:
- algorithm_name = CHECKSUM_ALGORITHM_TO_TV.get(algorithm_name)
- if algorithm_name is None:
- raise ValueError(f"Missing conversion rule for converting {self.identifier.name} to tag-value string")
- return "{0}: {1}".format(algorithm_name, self.value)
-
-
-CHECKSUM_ALGORITHM_TO_TV = {
- ChecksumAlgorithm.BLAKE2B_256.name: "BLAKE2b-256",
- ChecksumAlgorithm.BLAKE2B_384.name: "BLAKE2b-384",
- ChecksumAlgorithm.BLAKE2B_512.name: "BLAKE2b-512",
- ChecksumAlgorithm.SHA3_256.name: "SHA3-256",
- ChecksumAlgorithm.SHA3_384.name: "SHA3-384",
- ChecksumAlgorithm.SHA3_512.name: "SHA3-512"
-}
diff --git a/spdx/cli_tools/__init__.py b/spdx/cli_tools/__init__.py
deleted file mode 100644
index 1f63eb496..000000000
--- a/spdx/cli_tools/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2020 Yash Varshney
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
diff --git a/spdx/cli_tools/convertor.py b/spdx/cli_tools/convertor.py
deleted file mode 100644
index 04e96af72..000000000
--- a/spdx/cli_tools/convertor.py
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (c) 2020 Yash Varshney
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-from spdx.parsers.builderexceptions import FileTypeError
-from spdx.parsers.parse_anything import parse_file
-from spdx.writers.write_anything import write_file
-
-import click
-
-def print_help_msg(command):
- with click.Context(command) as ctx:
- click.echo(command.get_help(ctx))
-
-def determine_infile_and_outfile(infile, outfile, src, from_, to):
- if infile is not None and outfile is not None:
- """
- when the CLI is of given format:
- ' pyspdxtools_convertor ---infile ---outfile .
- """
- return infile, outfile
-
- elif infile is None and outfile is None and len(src) == 2:
- """
- ' pyspdxtools_convertor -f/--from -t/--to .
- """
- infile = src[0]
- outfile = src[1]
- # infile = os.path.splitext(infile)[0]
- if from_ is not None:
- infile_path = os.path.splitext(infile)[0]
- infile = infile_path + "." + from_
- if to is not None:
- outfile_path = os.path.splitext(outfile)[0]
- outfile = outfile_path + "." + to
- return infile, outfile
-
- elif infile is None and outfile is not None:
- """
- ' pyspdxtools_convertor -f/--from --outfile '
- """
- infile = src[0]
- if from_ is not None:
- infile_path = os.path.splitext(infile)[0]
- infile = infile_path + "." + from_
- return infile, outfile
-
- elif infile is not None and outfile is None:
- """
- ' pyspdxtools_convertor --infile -t/--to '
- """
- outfile = src[0]
- if to is not None:
- outfile_path = os.path.splitext(outfile)[0]
- outfile = outfile_path + "." + to
- return infile, outfile
-
- else:
- raise ValueError("Given arguments for convertor are invalid.")
-
-
-@click.command()
-@click.argument("src", nargs=-1)
-@click.option("--infile", "-i", help="The file to be converted ")
-@click.option("--outfile", "-o", help="The file after converting")
-@click.option(
- "--to",
- "-t",
- type=click.Choice(["json", "rdf", "yaml", "xml", "tag"], case_sensitive=False)
-)
-@click.option(
- "--from",
- "-f",
- "from_",
- type=click.Choice(["tag", "rdf"], case_sensitive=False))
-@click.option("--force", is_flag=True, help="convert even if there are some parsing errors or inconsistencies")
-def main(infile, outfile, src, from_, to, force):
- """
- CLI-TOOL for converting a RDF or TAG file to RDF, JSON, YAML, TAG or XML format.
-
- To use : run 'pyspdxtools_convertor -f -t ' command on terminal or use ' pyspdxtools_convertor --infile --outfile '
-
- """
- try:
- infile, outfile = determine_infile_and_outfile(infile, outfile, src, from_, to)
- except ValueError as err:
- print(err)
- print_help_msg(main)
- return
-
- doc, errors = parse_file(infile)
- if errors:
- print("Errors while parsing: ", errors)
- if not force:
- return 1
-
- write_file(doc, outfile)
-
-
-if __name__ == "__main__":
- main()
diff --git a/spdx/cli_tools/parser.py b/spdx/cli_tools/parser.py
deleted file mode 100755
index aad96d1c2..000000000
--- a/spdx/cli_tools/parser.py
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (c) 2020 Yash Varshney
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-
-from spdx import utils
-from spdx.parsers.parse_anything import parse_file
-import spdx.file as spdxfile
-
-import click
-
-
-@click.command()
-@click.option("--file", prompt="File name", help="The file to be parsed")
-@click.option("--force", is_flag=True, help="print information even if there are some parsing errors")
-def main(file, force):
- """
- COMMAND-LINE TOOL for parsing file of RDF, XML, JSON, YAML and XML format.
-
- To use : run `pyspdxtools_parser` using terminal or run `pyspdxtools_parser --file `
-
- """
- doc, errors = parse_file(file)
- if errors:
- print("Errors while parsing: ", errors)
- if not force:
- return 1
-
- print("doc comment: {0}".format(doc.comment))
- print("Creators:")
- for c in doc.creation_info.creators:
- print("\t{0}".format(c))
- print("Document review information:")
- for review in doc.reviews:
- print("\tReviewer: {0}".format(review.reviewer))
- print("\tDate: {0}".format(review.review_date))
- print("\tComment: {0}".format(review.comment))
- print("Creation comment: {0}".format(doc.creation_info.comment))
- for package in doc.packages:
- print("Package Name: {0}".format(package.name))
- print("Package Version: {0}".format(package.version))
- print(
- "Package Download Location: {0}".format(package.download_location)
- )
- print("Package Homepage: {0}".format(package.homepage))
- for checksum in doc.package.checksums.values():
- print("Package Checksum: {0} {1}".format(checksum.identifier.name, checksum.value))
- print("Package Attribution Text: {0}".format(package.attribution_text))
- print("Package verification code: {0}".format(package.verif_code))
- print(
- "Package excluded from verif: {0}".format(
- ",".join(package.verif_exc_files)
- )
- )
- print("Package license concluded: {0}".format(package.conc_lics))
- print("Package license declared: {0}".format(package.license_declared))
- print("Package licenses from files:")
- for lics in package.licenses_from_files:
- print("\t{0}".format(lics))
- print("Package Copyright text: {0}".format(package.cr_text))
- print("Package summary: {0}".format(package.summary))
- print("Package description: {0}".format(package.description))
- if len(package.pkg_ext_refs) > 0:
- print("Package external references:")
- for ref in package.pkg_ext_refs:
- print(f"\tCategory: {ref.category}")
- print(f"\tType: {ref.pkg_ext_ref_type}")
- print(f"\tLocator: {ref.locator}")
- if ref.comment:
- print(f"\tComment: {ref.comment}")
- if doc.files:
- print("Files:")
- for f in doc.files:
- print("\tFile name: {0}".format(f.name))
- for file_type in f.file_types:
- print("\tFile type: {0}".format(file_type.name))
- for file_checksum in f.checksums.values():
- print("\tFile Checksum: {0} {1}".format(file_checksum.identifier.name, file_checksum.value))
- print("\tFile license concluded: {0}".format(f.conc_lics))
- print(
- "\tFile license info in file: {0}".format(
- ",".join(
- map(lambda l: l.identifier if not isinstance(l, (utils.SPDXNone, utils.NoAssert)) else l.to_value(),
- f.licenses_in_file))
- )
- )
- print(
- "\tFile artifact of project name: {0}".format(
- ",".join(f.artifact_of_project_name)
- )
- )
-
- if doc.extracted_licenses:
- print("Document Extracted licenses:")
- for lics in doc.extracted_licenses:
- print("\tIdentifier: {0}".format(lics.identifier))
- print("\tName: {0}".format(lics.full_name))
- print("\License Text: {0}".format(lics.text))
- if doc.annotations:
- print("Annotations:")
- for an in doc.annotations:
- print("\tAnnotator: {0}".format(an.annotator))
- print("\tAnnotation Date: {0}".format(an.annotation_date))
- print("\tAnnotation Comment: {0}".format(an.comment))
- print("\tAnnotation Type: {0}".format(an.annotation_type))
- print("\tAnnotation SPDX Identifier: {0}".format(an.spdx_id))
-
- if doc.relationships:
- print("Relationships: ")
- for relation in doc.relationships:
- print("\tRelationship: {0}".format(relation.relationship))
- try:
- print("\tRelationship: {0}".format(relation.comment))
- except:
- continue
-
-
-if __name__ == "__main__":
- main()
diff --git a/spdx/config.py b/spdx/config.py
deleted file mode 100644
index a10582043..000000000
--- a/spdx/config.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import codecs
-import json
-import os
-
-from spdx.version import Version
-
-
-_base_dir = os.path.dirname(__file__)
-_licenses = os.path.join(_base_dir, "licenses.json")
-_exceptions = os.path.join(_base_dir, "exceptions.json")
-
-
-def _load_list(file_name, object_type="licenses", id_attribute="licenseId"):
- """
- Return a list version tuple and a mapping of licenses
- name->id and id->name loaded from a JSON file
- from https://github.com/spdx/license-list-data
- """
- licenses_map = {}
- with codecs.open(file_name, "rb", encoding="utf-8") as lics:
- licenses = json.load(lics)
- version = tuple(licenses["licenseListVersion"].split("."))
- for lic in licenses[object_type]:
- if lic.get("isDeprecatedLicenseId"):
- continue
- name = lic["name"]
- identifier = lic[id_attribute]
- licenses_map[name] = identifier
- licenses_map[identifier] = name
- return version, licenses_map
-
-
-def load_license_list(file_name):
- """
- Return the licenses list version tuple and a mapping of licenses
- name->id and id->name loaded from a JSON file
- from https://github.com/spdx/license-list-data
- """
- return _load_list(file_name, object_type="licenses", id_attribute="licenseId")
-
-
-def load_exception_list(file_name):
- """
- Return the exceptions list version tuple and a mapping of exceptions
- name->id and id->name loaded from a JSON file
- from https://github.com/spdx/license-list-data
- """
- return _load_list(
- file_name, object_type="exceptions", id_attribute="licenseExceptionId"
- )
-
-
-(_lmajor, _lminor), LICENSE_MAP = load_license_list(_licenses)
-LICENSE_LIST_VERSION = Version(major=_lmajor, minor=_lminor)
-
-(_emajor, _eminor), EXCEPTION_MAP = load_exception_list(_exceptions)
-EXCEPTION_LIST_VERSION = Version(major=_emajor, minor=_eminor)
-
-assert LICENSE_LIST_VERSION == EXCEPTION_LIST_VERSION
-del _emajor, _eminor, EXCEPTION_LIST_VERSION
diff --git a/spdx/creationinfo.py b/spdx/creationinfo.py
deleted file mode 100644
index eca9cbe37..000000000
--- a/spdx/creationinfo.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from datetime import datetime
-from functools import total_ordering
-
-from spdx import config
-from spdx import utils
-from spdx.version import Version
-
-
-@total_ordering
-class Creator(object):
- """
- Creator entity.
- Fields:
- - name: creator's name/identifier
- """
-
- def __init__(self, name):
- self.name = name
-
- # FIXME: do not override eq and not hash
- def __eq__(self, other):
- return isinstance(other, Creator) and self.name == other.name
-
- def __lt__(self, other):
- return isinstance(other, Creator) and self.name < other.name
-
-
-@total_ordering
-class Organization(Creator):
- """
- Organization entity.
- Fields:
- - name: Org's name/identifier. Mandatory. Type: str.
- - email: Org's email address. Optional. Type: str.
- """
-
- def __init__(self, name, email=None):
- super(Organization, self).__init__(name)
- self.email = email
-
- # FIXME: do not override eq and not hash
- def __eq__(self, other):
- return isinstance(other, Organization) and (self.name, self.email) == (
- other.name,
- other.email,
- )
-
- def __lt__(self, other):
- return isinstance(other, Organization) and (self.name, self.email) < (
- other.name,
- other.email,
- )
-
- def to_value(self):
- if self.email:
- return "Organization: {0} ({1})".format(self.name, self.email)
- else:
- return "Organization: {0}".format(self.name)
-
- def __str__(self):
- return self.to_value()
-
-
-@total_ordering
-class Person(Creator):
- """
- Person entity.
- Fields:
- - name: person's name/identifier. Mandatory. Type: str.
- - email: person's email address. Optional. Type: str.
- """
-
- def __init__(self, name, email=None):
- super(Person, self).__init__(name)
- self.email = email
-
- # FIXME: do not override eq and not hash
- def __eq__(self, other):
- return isinstance(other, Person) and (self.name, self.email) == (
- other.name,
- other.email,
- )
-
- def __lt__(self, other):
- return isinstance(other, Person) and (self.name, self.email) < (
- other.name,
- other.email,
- )
-
- def to_value(self):
- if self.email is not None:
- return "Person: {0} ({1})".format(self.name, self.email)
- else:
- return "Person: {0}".format(self.name)
-
- def __str__(self):
- return self.to_value()
-
-
-class Tool(Creator):
- """
- Tool entity.
- Fields:
- - name: tool identifier, with version. Type: str.
- """
-
- def __init__(self, name):
- super(Tool, self).__init__(name)
-
- def to_value(self):
- return "Tool: {0}".format(self.name)
-
- def __str__(self):
- return self.to_value()
-
-
-class CreationInfo(object):
- """
- Represent a document creation info.
- Fields:
- - creators: List of creators. At least one required.
- Type: Creator.
- - comment: Creation comment, optional. Type: str.
- - license_list_version: version of SPDX license used in creation of SPDX
- document. One, optional. Type: spdx.version.Version
- - created: Creation date. Mandatory one. Type: datetime.
- """
-
- def __init__(
- self,
- created=None,
- comment=None,
- license_list_version=config.LICENSE_LIST_VERSION,
- ):
- self.creators = []
- self.created = created
- self.comment = comment
- self.license_list_version = license_list_version
-
- def add_creator(self, creator):
- self.creators.append(creator)
-
- def remove_creator(self, creator):
- self.creators.remove(creator)
-
- def set_created_now(self):
- self.created = datetime.utcnow().replace(microsecond=0)
-
- def set_license_list_version(self, license_list_version):
- self.license_list_version = Version.from_str(license_list_version)
-
- @property
- def created_iso_format(self):
- return utils.datetime_iso_format(self.created)
-
- @property
- def has_comment(self):
- return self.comment is not None
-
- def validate(self, messages):
- """
- Check that all the fields are valid.
- Appends any error messages to messages parameter shall be a ErrorMessages.
- """
- self.validate_creators(messages)
- self.validate_created(messages)
-
- def validate_creators(self, messages):
- if len(self.creators) == 0:
- messages.append("No creators defined, must have at least one.")
-
- def validate_created(self, messages):
- if self.created is None:
- messages.append("Creation info missing created date.")
diff --git a/spdx/document.py b/spdx/document.py
deleted file mode 100644
index 3175df40e..000000000
--- a/spdx/document.py
+++ /dev/null
@@ -1,298 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-from typing import List, Optional, TYPE_CHECKING
-
-if TYPE_CHECKING:
- from spdx.file import File
-from spdx.license import ExtractedLicense
-from spdx.parsers.loggers import ErrorMessages
-
-import warnings
-
-from functools import total_ordering
-from spdx.relationship import Relationship
-
-from spdx.relationship import Relationship
-
-
-@total_ordering
-class ExternalDocumentRef(object):
- """
- External Document References entity that contains the following fields :
- - external_document_id: A unique string containing letters, numbers, '.',
- '-' or '+'.
- - spdx_document_uri: The unique ID of the SPDX document being referenced.
- - check_sum: The checksum of the referenced SPDX document.
- """
-
- def __init__(
- self, external_document_id=None, spdx_document_uri=None, checksum=None
- ):
- self.external_document_id = external_document_id
- self.spdx_document_uri = spdx_document_uri
- self.checksum = checksum
-
- def __eq__(self, other):
- return (
- isinstance(other, ExternalDocumentRef)
- and self.external_document_id == other.external_document_id
- and self.spdx_document_uri == other.spdx_document_uri
- and self.checksum == other.checksum
- )
-
- def __lt__(self, other):
- return (self.external_document_id, self.spdx_document_uri, self.checksum) < (
- other.external_document_id,
- other.spdx_document_uri,
- other.checksum,
- )
-
- def validate(self, messages: ErrorMessages) -> ErrorMessages:
- """
- Check that all the fields are valid.
- Appends any error messages to messages parameter shall be a ErrorMessages.
- """
- self.validate_ext_doc_id(messages)
- self.validate_spdx_doc_uri(messages)
- self.validate_checksum(messages)
- return messages
-
- def validate_ext_doc_id(self, messages):
- if not self.external_document_id:
- messages.append("ExternalDocumentRef has no External Document ID.")
-
- def validate_spdx_doc_uri(self, messages):
- if not self.spdx_document_uri:
- messages.append("ExternalDocumentRef has no SPDX Document URI.")
-
- def validate_checksum(self, messages):
- if not self.checksum:
- messages.append("ExternalDocumentRef has no Checksum.")
-
-
-class Document(object):
- """
- Represent an SPDX document with these fields:
- - version: Spec version. Mandatory, one - Type: Version.
- - data_license: SPDX-Metadata license. Mandatory, one. Type: License.
- - name: Name of the document. Mandatory, one. Type: str.
- - spdx_id: SPDX Identifier for the document to refer to itself in
- relationship to other elements. Mandatory, one. Type: str.
- - ext_document_references: External SPDX documents referenced within the
- given SPDX document. Optional, one or many. Type: ExternalDocumentRef
- - comment: Comments on the SPDX file, optional one. Type: str
- - documentNamespace: SPDX document specific namespace. Mandatory, one. Type: str
- - creation_info: SPDX file creation info. Mandatory, one. Type: CreationInfo
- - package: Package described by this document. Mandatory, one. Type: Package
- - extracted_licenses: List of licenses extracted that are not part of the
- SPDX license list. Optional, many. Type: ExtractedLicense.
- - reviews: SPDX document review information, Optional zero or more.
- Type: Review.
- - annotations: SPDX document annotation information, Optional zero or more.
- Type: Annotation.
- - snippet: Snippet information. Optional zero or more. Type: Snippet.
- - relationships: Relationship between two SPDX elements. Optional zero or more.
- Type: Relationship.
- """
-
- def __init__(
- self,
- version=None,
- data_license=None,
- name=None,
- spdx_id=None,
- namespace=None,
- comment=None,
- package=None,
- license_list_version=None,
- ):
- # avoid recursive import
- from spdx.creationinfo import CreationInfo
-
- self.version = version
- self.data_license = data_license
- self.name = name
- self.spdx_id = spdx_id
- self.ext_document_references = []
- self.comment = comment
- self.namespace = namespace
- self.creation_info = CreationInfo()
- self.files: List['File'] = []
- self.packages = []
- if package is not None:
- self.packages.append(package)
- self.extracted_licenses = []
- self.reviews = []
- self.annotations = []
- self.relationships: List[Relationship] = []
- self.snippet = []
-
- # due to backwards compatibility write input argument for license list version to creation info
- if license_list_version:
- self.creation_info.set_license_list_version(license_list_version)
-
- def add_review(self, review):
- self.reviews.append(review)
-
- def add_annotation(self, annotation):
- self.annotations.append(annotation)
-
- def add_relationship(self, relationship):
- self.relationships.append(relationship)
-
- def add_extr_lic(self, lic):
- self.extracted_licenses.append(lic)
-
- def add_ext_document_reference(self, ext_doc_ref):
- self.ext_document_references.append(ext_doc_ref)
-
- def add_snippet(self, snip):
- self.snippet.append(snip)
-
- def add_package(self, package):
- self.packages.append(package)
-
- def add_file(self, file: 'File') -> None:
- self.files.append(file)
-
- # For backwards compatibility with older versions, we support a
- # mode where the first package in a document may be referred to as
- # the document's "package", and the files it contains may be
- # referred to as the document's files. This usage is deprecated.
-
- @property
- def package(self):
- warnings.warn('document.package and document.files are deprecated; '
- 'use document.packages instead',
- DeprecationWarning)
- if len(self.packages) == 0:
- return None
- else:
- return self.packages[0]
-
- @package.setter
- def package(self, value):
- warnings.warn('document.package and document.files are deprecated; '
- 'use document.packages instead',
- DeprecationWarning)
- if len(self.packages) == 0:
- self.packages.append(value)
- else:
- self.packages[0] = value
-
- @property
- def has_comment(self):
- return self.comment is not None
-
- def validate(self, messages=None):
- """
- Validate all fields of the document and update the
- messages list with user friendly error messages for display.
- """
- if isinstance(messages, list):
- raise TypeError("messages should be None or an instance of ErrorMessages")
- if messages is None:
- messages = ErrorMessages()
-
- messages.push_context(self.name)
- self.validate_version(messages)
- self.validate_data_lics(messages)
- self.validate_name(messages)
- self.validate_spdx_id(messages)
- self.validate_namespace(messages)
- self.validate_ext_document_references(messages)
- self.validate_creation_info(messages)
- self.validate_files(messages)
- self.validate_packages(messages)
- self.validate_extracted_licenses(messages)
- self.validate_reviews(messages)
- self.validate_snippet(messages)
- self.validate_annotations(messages)
- self.validate_relationships(messages)
- messages.pop_context()
- return messages
-
- def validate_version(self, messages):
- if self.version is None:
- messages.append("Document has no version.")
-
- def validate_data_lics(self, messages):
- if self.data_license is None:
- messages.append("Document has no data license.")
- else:
- # FIXME: REALLY? what if someone wants to use something else?
- if self.data_license.identifier != "CC0-1.0":
- messages.append("Document data license must be CC0-1.0.")
-
- def validate_name(self, messages):
- if self.name is None:
- messages.append("Document has no name.")
-
- def validate_namespace(self, messages):
- if self.namespace is None:
- messages.append("Document has no namespace.")
-
- def validate_spdx_id(self, messages):
- if self.spdx_id is None:
- messages.append("Document has no SPDX Identifier.")
- else:
- if not self.spdx_id.endswith("SPDXRef-DOCUMENT"):
- messages.append("Invalid Document SPDX Identifier value.")
-
- def validate_ext_document_references(self, messages):
- for doc in self.ext_document_references:
- if isinstance(doc, ExternalDocumentRef):
- messages = doc.validate(messages)
- else:
- messages = list(messages) + [
- "External document references must be of the type "
- "spdx.document.ExternalDocumentRef and not " + str(type(doc))
- ]
-
- def validate_reviews(self, messages):
- for review in self.reviews:
- messages = review.validate(messages)
-
- def validate_files(self, messages: ErrorMessages) -> None:
- for file in self.files:
- messages = file.validate(messages)
-
- def validate_annotations(self, messages):
- for annotation in self.annotations:
- messages = annotation.validate(messages)
-
- def validate_relationships(self, messages):
- for relationship in self.relationships:
- messages = relationship.validate(messages)
-
- def validate_snippet(self, messages=None):
- for snippet in self.snippet:
- snippet.validate(messages)
-
- def validate_creation_info(self, messages):
- if self.creation_info is not None:
- self.creation_info.validate(messages)
- else:
- messages.append("Document has no creation information.")
-
- def validate_packages(self, messages):
- for package in self.packages:
- messages = package.validate(messages)
-
- def validate_extracted_licenses(self, messages):
- for lic in self.extracted_licenses:
- if isinstance(lic, ExtractedLicense):
- messages = lic.validate(messages)
- else:
- messages.append(
- "Document extracted licenses must be of type "
- "spdx.document.ExtractedLicense and not " + type(lic)
- )
diff --git a/spdx/exceptions.json b/spdx/exceptions.json
deleted file mode 100644
index 0f77cd372..000000000
--- a/spdx/exceptions.json
+++ /dev/null
@@ -1,408 +0,0 @@
-{
- "licenseListVersion": "3.6",
- "releaseDate": "2019-07-10",
- "exceptions": [
- {
- "reference": "./Libtool-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Libtool-exception.json",
- "referenceNumber": "1",
- "name": "Libtool Exception",
- "seeAlso": [
- "http://git.savannah.gnu.org/cgit/libtool.git/tree/m4/libtool.m4"
- ],
- "licenseExceptionId": "Libtool-exception"
- },
- {
- "reference": "./Linux-syscall-note.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Linux-syscall-note.json",
- "referenceNumber": "2",
- "name": "Linux Syscall Note",
- "seeAlso": [
- "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/COPYING"
- ],
- "licenseExceptionId": "Linux-syscall-note"
- },
- {
- "reference": "./Autoconf-exception-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-3.0.json",
- "referenceNumber": "3",
- "name": "Autoconf exception 3.0",
- "seeAlso": [
- "http://www.gnu.org/licenses/autoconf-exception-3.0.html"
- ],
- "licenseExceptionId": "Autoconf-exception-3.0"
- },
- {
- "reference": "./OCCT-exception-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OCCT-exception-1.0.json",
- "referenceNumber": "4",
- "name": "Open CASCADE Exception 1.0",
- "seeAlso": [
- "http://www.opencascade.com/content/licensing"
- ],
- "licenseExceptionId": "OCCT-exception-1.0"
- },
- {
- "reference": "./openvpn-openssl-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/openvpn-openssl-exception.json",
- "referenceNumber": "5",
- "name": "OpenVPN OpenSSL Exception",
- "seeAlso": [
- "http://openvpn.net/index.php/license.html"
- ],
- "licenseExceptionId": "openvpn-openssl-exception"
- },
- {
- "reference": "./gnu-javamail-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/gnu-javamail-exception.json",
- "referenceNumber": "6",
- "name": "GNU JavaMail exception",
- "seeAlso": [
- "http://www.gnu.org/software/classpathx/javamail/javamail.html"
- ],
- "licenseExceptionId": "gnu-javamail-exception"
- },
- {
- "reference": "./OpenJDK-assembly-exception-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OpenJDK-assembly-exception-1.0.json",
- "referenceNumber": "7",
- "name": "OpenJDK Assembly exception 1.0",
- "seeAlso": [
- "http://openjdk.java.net/legal/assembly-exception.html"
- ],
- "licenseExceptionId": "OpenJDK-assembly-exception-1.0"
- },
- {
- "reference": "./Bison-exception-2.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Bison-exception-2.2.json",
- "referenceNumber": "8",
- "name": "Bison exception 2.2",
- "seeAlso": [
- "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141"
- ],
- "licenseExceptionId": "Bison-exception-2.2"
- },
- {
- "reference": "./i2p-gpl-java-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/i2p-gpl-java-exception.json",
- "referenceNumber": "9",
- "name": "i2p GPL+Java Exception",
- "seeAlso": [
- "http://geti2p.net/en/get-involved/develop/licenses#java_exception"
- ],
- "licenseExceptionId": "i2p-gpl-java-exception"
- },
- {
- "reference": "./Universal-FOSS-exception-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Universal-FOSS-exception-1.0.json",
- "referenceNumber": "10",
- "name": "Universal FOSS Exception, Version 1.0",
- "seeAlso": [
- "https://oss.oracle.com/licenses/universal-foss-exception/"
- ],
- "licenseExceptionId": "Universal-FOSS-exception-1.0"
- },
- {
- "reference": "./Qt-LGPL-exception-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Qt-LGPL-exception-1.1.json",
- "referenceNumber": "11",
- "name": "Qt LGPL exception 1.1",
- "seeAlso": [
- "http://code.qt.io/cgit/qt/qtbase.git/tree/LGPL_EXCEPTION.txt"
- ],
- "licenseExceptionId": "Qt-LGPL-exception-1.1"
- },
- {
- "reference": "./389-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/389-exception.json",
- "referenceNumber": "12",
- "name": "389 Directory Server Exception",
- "seeAlso": [
- "http://directory.fedoraproject.org/wiki/GPL_Exception_License_Text"
- ],
- "licenseExceptionId": "389-exception"
- },
- {
- "reference": "./Classpath-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Classpath-exception-2.0.json",
- "referenceNumber": "13",
- "name": "Classpath exception 2.0",
- "seeAlso": [
- "http://www.gnu.org/software/classpath/license.html",
- "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception"
- ],
- "licenseExceptionId": "Classpath-exception-2.0"
- },
- {
- "reference": "./Fawkes-Runtime-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Fawkes-Runtime-exception.json",
- "referenceNumber": "14",
- "name": "Fawkes Runtime Exception",
- "seeAlso": [
- "http://www.fawkesrobotics.org/about/license/"
- ],
- "licenseExceptionId": "Fawkes-Runtime-exception"
- },
- {
- "reference": "./PS-or-PDF-font-exception-20170817.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/PS-or-PDF-font-exception-20170817.json",
- "referenceNumber": "15",
- "name": "PS/PDF font exception (2017-08-17)",
- "seeAlso": [
- "https://github.com/ArtifexSoftware/urw-base35-fonts/blob/65962e27febc3883a17e651cdb23e783668c996f/LICENSE"
- ],
- "licenseExceptionId": "PS-or-PDF-font-exception-20170817"
- },
- {
- "reference": "./Qt-GPL-exception-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Qt-GPL-exception-1.0.json",
- "referenceNumber": "16",
- "name": "Qt GPL exception 1.0",
- "seeAlso": [
- "http://code.qt.io/cgit/qt/qtbase.git/tree/LICENSE.GPL3-EXCEPT"
- ],
- "licenseExceptionId": "Qt-GPL-exception-1.0"
- },
- {
- "reference": "./LZMA-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LZMA-exception.json",
- "referenceNumber": "17",
- "name": "LZMA exception",
- "seeAlso": [
- "http://nsis.sourceforge.net/Docs/AppendixI.html#I.6"
- ],
- "licenseExceptionId": "LZMA-exception"
- },
- {
- "reference": "./freertos-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/freertos-exception-2.0.json",
- "referenceNumber": "18",
- "name": "FreeRTOS Exception 2.0",
- "seeAlso": [
- "https://web.archive.org/web/20060809182744/http://www.freertos.org/a00114.html"
- ],
- "licenseExceptionId": "freertos-exception-2.0"
- },
- {
- "reference": "./Qwt-exception-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Qwt-exception-1.0.json",
- "referenceNumber": "19",
- "name": "Qwt exception 1.0",
- "seeAlso": [
- "http://qwt.sourceforge.net/qwtlicense.html"
- ],
- "licenseExceptionId": "Qwt-exception-1.0"
- },
- {
- "reference": "./CLISP-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CLISP-exception-2.0.json",
- "referenceNumber": "20",
- "name": "CLISP exception 2.0",
- "seeAlso": [
- "http://sourceforge.net/p/clisp/clisp/ci/default/tree/COPYRIGHT"
- ],
- "licenseExceptionId": "CLISP-exception-2.0"
- },
- {
- "reference": "./FLTK-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/FLTK-exception.json",
- "referenceNumber": "21",
- "name": "FLTK exception",
- "seeAlso": [
- "http://www.fltk.org/COPYING.php"
- ],
- "licenseExceptionId": "FLTK-exception"
- },
- {
- "reference": "./Bootloader-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Bootloader-exception.json",
- "referenceNumber": "22",
- "name": "Bootloader Distribution Exception",
- "seeAlso": [
- "https://github.com/pyinstaller/pyinstaller/blob/develop/COPYING.txt"
- ],
- "licenseExceptionId": "Bootloader-exception"
- },
- {
- "reference": "./Nokia-Qt-exception-1.1.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/Nokia-Qt-exception-1.1.json",
- "referenceNumber": "23",
- "name": "Nokia Qt LGPL exception 1.1",
- "seeAlso": [
- "https://www.keepassx.org/dev/projects/keepassx/repository/revisions/b8dfb9cc4d5133e0f09cd7533d15a4f1c19a40f2/entry/LICENSE.NOKIA-LGPL-EXCEPTION"
- ],
- "licenseExceptionId": "Nokia-Qt-exception-1.1"
- },
- {
- "reference": "./LLVM-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LLVM-exception.json",
- "referenceNumber": "24",
- "name": "LLVM Exception",
- "seeAlso": [
- "http://llvm.org/foundation/relicensing/LICENSE.txt"
- ],
- "licenseExceptionId": "LLVM-exception"
- },
- {
- "reference": "./WxWindows-exception-3.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/WxWindows-exception-3.1.json",
- "referenceNumber": "25",
- "name": "WxWindows Library Exception 3.1",
- "seeAlso": [
- "http://www.opensource.org/licenses/WXwindows"
- ],
- "licenseExceptionId": "WxWindows-exception-3.1"
- },
- {
- "reference": "./DigiRule-FOSS-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/DigiRule-FOSS-exception.json",
- "referenceNumber": "26",
- "name": "DigiRule FOSS License Exception",
- "seeAlso": [
- "http://www.digirulesolutions.com/drupal/foss"
- ],
- "licenseExceptionId": "DigiRule-FOSS-exception"
- },
- {
- "reference": "./Swift-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Swift-exception.json",
- "referenceNumber": "27",
- "name": "Swift Exception",
- "seeAlso": [
- "https://swift.org/LICENSE.txt",
- "https://github.com/apple/swift-package-manager/blob/7ab2275f447a5eb37497ed63a9340f8a6d1e488b/LICENSE.txt#L205"
- ],
- "licenseExceptionId": "Swift-exception"
- },
- {
- "reference": "./GCC-exception-3.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/GCC-exception-3.1.json",
- "referenceNumber": "28",
- "name": "GCC Runtime Library exception 3.1",
- "seeAlso": [
- "http://www.gnu.org/licenses/gcc-exception-3.1.html"
- ],
- "licenseExceptionId": "GCC-exception-3.1"
- },
- {
- "reference": "./eCos-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/eCos-exception-2.0.json",
- "referenceNumber": "29",
- "name": "eCos exception 2.0",
- "seeAlso": [
- "http://ecos.sourceware.org/license-overview.html"
- ],
- "licenseExceptionId": "eCos-exception-2.0"
- },
- {
- "reference": "./Autoconf-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-2.0.json",
- "referenceNumber": "30",
- "name": "Autoconf exception 2.0",
- "seeAlso": [
- "http://ac-archive.sourceforge.net/doc/copyright.html",
- "http://ftp.gnu.org/gnu/autoconf/autoconf-2.59.tar.gz"
- ],
- "licenseExceptionId": "Autoconf-exception-2.0"
- },
- {
- "reference": "./GPL-CC-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/GPL-CC-1.0.json",
- "referenceNumber": "31",
- "name": "GPL Cooperation Commitment 1.0",
- "seeAlso": [
- "https://github.com/gplcc/gplcc/blob/master/Project/COMMITMENT",
- "https://gplcc.github.io/gplcc/Project/README-PROJECT.html"
- ],
- "licenseExceptionId": "GPL-CC-1.0"
- },
- {
- "reference": "./Font-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Font-exception-2.0.json",
- "referenceNumber": "32",
- "name": "Font exception 2.0",
- "seeAlso": [
- "http://www.gnu.org/licenses/gpl-faq.html#FontException"
- ],
- "licenseExceptionId": "Font-exception-2.0"
- },
- {
- "reference": "./u-boot-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/u-boot-exception-2.0.json",
- "referenceNumber": "33",
- "name": "U-Boot exception 2.0",
- "seeAlso": [
- "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003dLicenses/Exceptions"
- ],
- "licenseExceptionId": "u-boot-exception-2.0"
- },
- {
- "reference": "./GCC-exception-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/GCC-exception-2.0.json",
- "referenceNumber": "34",
- "name": "GCC Runtime Library exception 2.0",
- "seeAlso": [
- "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10"
- ],
- "licenseExceptionId": "GCC-exception-2.0"
- },
- {
- "reference": "./mif-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/mif-exception.json",
- "referenceNumber": "35",
- "name": "Macros and Inline Functions Exception",
- "seeAlso": [
- "http://www.scs.stanford.edu/histar/src/lib/cppsup/exception",
- "http://dev.bertos.org/doxygen/",
- "https://www.threadingbuildingblocks.org/licensing"
- ],
- "licenseExceptionId": "mif-exception"
- },
- {
- "reference": "./OCaml-LGPL-linking-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OCaml-LGPL-linking-exception.json",
- "referenceNumber": "36",
- "name": "OCaml LGPL Linking Exception",
- "seeAlso": [
- "https://caml.inria.fr/ocaml/license.en.html"
- ],
- "licenseExceptionId": "OCaml-LGPL-linking-exception"
- }
- ]
-}
\ No newline at end of file
diff --git a/spdx/file.py b/spdx/file.py
deleted file mode 100644
index bab50e2ad..000000000
--- a/spdx/file.py
+++ /dev/null
@@ -1,249 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import hashlib
-import warnings
-from enum import Enum, auto
-from functools import total_ordering
-from typing import Optional
-
-from spdx import utils
-from spdx.checksum import Checksum, ChecksumAlgorithm
-from spdx.license import License
-from spdx.parsers.builderexceptions import SPDXValueError
-from spdx.parsers.loggers import ErrorMessages
-
-
-class FileType(Enum):
- SOURCE = auto()
- BINARY = auto()
- ARCHIVE = auto()
- OTHER = auto()
- APPLICATION = auto()
- AUDIO = auto()
- IMAGE = auto()
- TEXT = auto()
- DOCUMENTATION = auto()
- SPDX = auto()
- VIDEO = auto()
-
-
-def file_type_from_rdf(rdf_file_type: str) -> FileType:
- """e.g. convert fileType_source to FileType.SOURCE"""
- file_type_str = rdf_file_type.split("_")[1].upper()
-
- if file_type_str not in FileType.__members__:
- raise SPDXValueError("File:FileType")
-
- return FileType[file_type_str]
-
-
-def file_type_to_rdf(file_type: FileType) -> str:
- """e.g. convert SOURCE to fileType_source"""
- return f"fileType_{file_type.name.lower()}"
-
-
-@total_ordering
-class File(object):
- """
- Represent an SPDX file.
- Fields:
- - name: File name, str mandatory one.
- - spdx_id: Uniquely identify any element in an SPDX document which may be
- referenced by other elements. Mandatory, one. Type: str.
- - comment: File comment str, Optional zero or one.
- - file_types: list of file types. Cardinality 0..*
- - checksums: Dict with checksum.ChecksumAlgorithm as key and checksum.Checksum as value,
- there must be a SHA1 hash, at least.
- - conc_lics: Mandatory one. license.License or utils.NoAssert or utils.SPDXNone.
- - licenses_in_file: list of licenses found in file, mandatory one or more.
- document.License or utils.SPDXNone or utils.NoAssert.
- - document.license or utils.NoAssert or utils.SPDXNone.
- - license_comment: Optional.
- - copyright: Copyright text, Mandatory one. utils.NoAssert or utils.SPDXNone or str.
- - notice: optional One, str.
- - contributors: List of strings.
- - dependencies: list of file locations.
- - artifact_of_project_name: list of project names, possibly empty.
- - artifact_of_project_home: list of project home page, possibly empty.
- - artifact_of_project_uri: list of project uris, possibly empty.
- -attribution_text: optional string.
- """
-
- def __init__(self, name, spdx_id=None):
- self.name = name
- self.spdx_id = spdx_id
- self.comment = None
- self.file_types = []
- self.checksums = {}
- self.conc_lics = None
- self.licenses_in_file = []
- self.license_comment = None
- self.copyright = None
- self.notice = None
- self.attribution_text = None
- self.contributors = []
- self.dependencies = []
- self.artifact_of_project_name = []
- self.artifact_of_project_home = []
- self.artifact_of_project_uri = []
-
- def __eq__(self, other):
- return isinstance(other, File) and self.name == other.name
-
- def __lt__(self, other):
- return self.name < other.name
-
- @property
- def checksum(self):
- """
- Backwards compatibility, return SHA1 checksum.
- """
- warnings.warn("This property is deprecated. Use get_checksum instead.")
- return self.get_checksum(ChecksumAlgorithm.SHA1)
-
- @checksum.setter
- def checksum(self, value):
- """
- Backwards compatibility, set checksum.
- """
- warnings.warn("This property is deprecated. Use set_checksum instead.")
- if isinstance(value, str):
- self.set_checksum(Checksum("SHA1", value))
- elif isinstance(value, Checksum):
- self.set_checksum(value)
-
- def add_lics(self, lics):
- self.licenses_in_file.append(lics)
-
- def add_contrib(self, contrib):
- self.contributors.append(contrib)
-
- def add_depend(self, depend):
- self.dependencies.append(depend)
-
- def add_artifact(self, symbol, value):
- """
- Add value as artifact_of_project{symbol}.
- """
- symbol = "artifact_of_project_{}".format(symbol)
- artifact = getattr(self, symbol)
- artifact.append(value)
-
- def validate(self, messages):
- """
- Check that all the fields are valid.
- Appends any error messages to messages parameter shall be a ErrorMessages.
- """
- messages.push_context(self.name)
- self.validate_concluded_license(messages)
- self.validate_file_types(messages)
- self.validate_checksums(messages)
- self.validate_licenses_in_file(messages)
- self.validate_copyright(messages)
- self.validate_artifacts(messages)
- self.validate_spdx_id(messages)
- messages.pop_context()
- return messages
-
- def validate_spdx_id(self, messages):
- if self.spdx_id is None:
- messages.append("File has no SPDX Identifier.")
-
- return messages
-
- def validate_copyright(self, messages):
- if self.copyright and not isinstance(
- self.copyright,
- (str, utils.NoAssert, utils.SPDXNone),
- ):
- messages.append(
- "File copyright must be str or unicode or "
- "spdx.utils.NoAssert or spdx.utils.SPDXNone"
- )
-
- return messages
-
- def validate_artifacts(self, messages):
- if len(self.artifact_of_project_home) < max(
- len(self.artifact_of_project_uri), len(self.artifact_of_project_name)
- ):
- messages.append(
- "File must have as much artifact of project as uri or homepage"
- )
-
- return messages
-
- def validate_licenses_in_file(self, messages):
- for license_in_file in self.licenses_in_file:
- if not isinstance(
- license_in_file, (utils.SPDXNone, utils.NoAssert, License)
- ):
- messages.append(
- "License in file must be instance of "
- "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
- "spdx.license.License"
- )
-
- return messages
-
- def validate_concluded_license(self, messages):
- if self.conc_lics and not isinstance(
- self.conc_lics, (utils.SPDXNone, utils.NoAssert, License)
- ):
- messages.append(
- "File concluded license must be instance of "
- "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
- "spdx.license.License"
- )
-
- return messages
-
- def validate_file_types(self, messages):
- for file_type in self.file_types:
- if not isinstance(file_type, FileType):
- messages.append(f"{file_type} is not of type FileType.")
- return messages
-
- def validate_checksums(self, messages: ErrorMessages):
- for checksum in self.checksums.values():
- if not isinstance(checksum, Checksum):
- messages.append("File checksum must be instance of spdx.checksum.Checksum.")
-
- if self.get_checksum(ChecksumAlgorithm.SHA1) is None:
- messages.append("At least one file checksum algorithm must be SHA1")
-
- def calculate_checksum(self, hash_algorithm='SHA1'):
- if hash_algorithm not in ChecksumAlgorithm.__members__:
- raise ValueError
- BUFFER_SIZE = 65536
-
- file_hash = hashlib.new(hash_algorithm.lower())
- with open(self.name, "rb") as file_handle:
- while True:
- data = file_handle.read(BUFFER_SIZE)
- if not data:
- break
- file_hash.update(data)
-
- return file_hash.hexdigest()
-
- def get_checksum(self, hash_algorithm: ChecksumAlgorithm = ChecksumAlgorithm.SHA1) -> Optional[Checksum]:
- return self.checksums.get(hash_algorithm)
-
- def set_checksum(self, new_checksum: Checksum):
- if not isinstance(new_checksum, Checksum):
- raise SPDXValueError
-
- self.checksums[new_checksum.identifier] = new_checksum
-
- def has_optional_field(self, field):
- return bool(getattr(self, field, None))
diff --git a/spdx/license.py b/spdx/license.py
deleted file mode 100644
index ea973f5fd..000000000
--- a/spdx/license.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# Copyright (c) 2022 spdx contributors
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from functools import total_ordering
-
-from spdx import config
-
-
-@total_ordering
-class License(object):
- def __init__(self, full_name, identifier):
- """if one of the argument is None, we try to map as much as possible
- """
- self._full_name = None
- self._identifier = None
- self.set_full_name(full_name)
- self.set_identifier(identifier)
-
- @classmethod
- def from_identifier(cls, identifier):
- """If identifier exists in config.LICENSE_MAP
- the full_name is retrieved from it. Otherwise
- the full_name is the same as the identifier.
- """
- return cls(None, identifier)
-
- @classmethod
- def from_full_name(cls, full_name):
- """
- Return a new License for a full_name. If the full_name exists in
- config.LICENSE_MAP the identifier is retrieved from it.
- Otherwise the identifier is the same as the full_name.
- """
- return cls(full_name, None)
-
- @property
- def url(self):
- return "http://spdx.org/licenses/{0}".format(self.identifier)
-
- @property
- def full_name(self):
- return self._full_name
-
- @full_name.setter
- def full_name(self, value):
- self.set_full_name(value)
-
- def set_full_name(self, value):
-
- if value is None:
- return
- if self._identifier is None:
- if value in config.LICENSE_MAP:
- self._identifier = config.LICENSE_MAP[value]
- else:
- self._identifier = value
- self._full_name = value
-
- @property
- def identifier(self):
- return self._identifier
-
- @identifier.setter
- def identifier(self, value):
- self.set_identifier(value)
-
- def set_identifier(self, value):
- if value is None:
- return
- if self._full_name is None:
- if value in config.LICENSE_MAP:
- self._full_name = config.LICENSE_MAP[value]
- else:
- self._full_name = value
-
- self._identifier = value
-
-
- def __eq__(self, other):
- return (
- isinstance(other, License)
- and self.identifier == other.identifier
- and self.full_name == other.full_name
- )
-
- def __lt__(self, other):
- return isinstance(other, License) and self.identifier < other.identifier
-
- def __str__(self):
- return self.identifier
-
- def __hash__(self):
- return self.identifier.__hash__()
-
-
-class LicenseConjunction(License):
- """
- A conjunction of two licenses.
- """
-
- def __init__(self, license_1, license_2):
- self.license_1 = license_1
- self.license_2 = license_2
- super(LicenseConjunction, self).__init__(self.full_name, self.identifier)
-
- @property
- def full_name(self):
- license_1_complex = type(self.license_1) == LicenseDisjunction
- license_2_complex = type(self.license_2) == LicenseDisjunction
-
- return "{0} AND {1}".format(
- _add_parens(license_1_complex, self.license_1.full_name),
- _add_parens(license_2_complex, self.license_2.full_name),
- )
-
- @property
- def identifier(self):
- license_1_complex = type(self.license_1) == LicenseDisjunction
- license_2_complex = type(self.license_2) == LicenseDisjunction
-
- return "{0} AND {1}".format(
- _add_parens(license_1_complex, self.license_1.identifier),
- _add_parens(license_2_complex, self.license_2.identifier),
- )
-
-
-class LicenseDisjunction(License):
- """
- A disjunction of two licenses.
- """
-
- def __init__(self, license_1, license_2):
- self.license_1 = license_1
- self.license_2 = license_2
- super(LicenseDisjunction, self).__init__(self.full_name, self.identifier)
-
- @property
- def full_name(self):
- license_1_complex = type(self.license_1) == LicenseConjunction
- license_2_complex = type(self.license_2) == LicenseConjunction
-
- return "{0} OR {1}".format(
- _add_parens(license_1_complex, self.license_1.full_name),
- _add_parens(license_2_complex, self.license_2.full_name),
- )
-
- @property
- def identifier(self):
- license_1_complex = type(self.license_1) == LicenseConjunction
- license_2_complex = type(self.license_2) == LicenseConjunction
-
- return "{0} OR {1}".format(
- _add_parens(license_1_complex, self.license_1.identifier),
- _add_parens(license_2_complex, self.license_2.identifier),
- )
-
-
-@total_ordering
-class ExtractedLicense(License):
- """
- Represent an ExtractedLicense with its additional attributes:
- - text: Extracted text, str. Mandatory.
- - cross_ref: list of cross references.
- - comment: license comment, str.
- - full_name: license name. str or utils.NoAssert.
- """
-
- def __init__(self, identifier):
- super(ExtractedLicense, self).__init__(None, identifier)
- self.text = None
- self.cross_ref = []
- self.comment = None
-
- def __eq__(self, other):
- return (
- isinstance(other, ExtractedLicense)
- and self.identifier == other.identifier
- and self.full_name == other.full_name
- )
-
- def __lt__(self, other):
- return (
- isinstance(other, ExtractedLicense) and self.identifier < other.identifier
- )
-
- def add_xref(self, ref):
- self.cross_ref.append(ref)
-
- def validate(self, messages):
- if self.text is None:
- messages.append("ExtractedLicense text can not be None")
-
-
-def _add_parens(required, text):
- """
- Add parens around a license expression if `required` is True, otherwise
- return `text` unmodified.
- """
- return "({})".format(text) if required else text
diff --git a/spdx/licenses.json b/spdx/licenses.json
deleted file mode 100644
index 10550cbd4..000000000
--- a/spdx/licenses.json
+++ /dev/null
@@ -1,4974 +0,0 @@
-{
- "licenseListVersion": "3.6",
- "licenses": [
- {
- "reference": "./0BSD.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/0BSD.json",
- "referenceNumber": "319",
- "name": "BSD Zero Clause License",
- "licenseId": "0BSD",
- "seeAlso": [
- "http://landley.net/toybox/license.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AAL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/AAL.json",
- "referenceNumber": "21",
- "name": "Attribution Assurance License",
- "licenseId": "AAL",
- "seeAlso": [
- "https://opensource.org/licenses/attribution"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./ADSL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ADSL.json",
- "referenceNumber": "19",
- "name": "Amazon Digital Services License",
- "licenseId": "ADSL",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./AFL-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AFL-1.1.json",
- "referenceNumber": "118",
- "name": "Academic Free License v1.1",
- "licenseId": "AFL-1.1",
- "seeAlso": [
- "http://opensource.linux-mirror.org/licenses/afl-1.1.txt",
- "http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AFL-1.2.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AFL-1.2.json",
- "referenceNumber": "136",
- "name": "Academic Free License v1.2",
- "licenseId": "AFL-1.2",
- "seeAlso": [
- "http://opensource.linux-mirror.org/licenses/afl-1.2.txt",
- "http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AFL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AFL-2.0.json",
- "referenceNumber": "115",
- "name": "Academic Free License v2.0",
- "licenseId": "AFL-2.0",
- "seeAlso": [
- "http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AFL-2.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AFL-2.1.json",
- "referenceNumber": "251",
- "name": "Academic Free License v2.1",
- "licenseId": "AFL-2.1",
- "seeAlso": [
- "http://opensource.linux-mirror.org/licenses/afl-2.1.txt"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AFL-3.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AFL-3.0.json",
- "referenceNumber": "216",
- "name": "Academic Free License v3.0",
- "licenseId": "AFL-3.0",
- "seeAlso": [
- "http://www.rosenlaw.com/AFL3.0.htm",
- "https://opensource.org/licenses/afl-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AGPL-1.0.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AGPL-1.0.json",
- "referenceNumber": "335",
- "name": "Affero General Public License v1.0",
- "licenseId": "AGPL-1.0",
- "seeAlso": [
- "http://www.affero.org/oagpl.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./AGPL-1.0-only.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-only.json",
- "referenceNumber": "384",
- "name": "Affero General Public License v1.0 only",
- "licenseId": "AGPL-1.0-only",
- "seeAlso": [
- "http://www.affero.org/oagpl.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./AGPL-1.0-or-later.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-or-later.json",
- "referenceNumber": "332",
- "name": "Affero General Public License v1.0 or later",
- "licenseId": "AGPL-1.0-or-later",
- "seeAlso": [
- "http://www.affero.org/oagpl.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./AGPL-3.0.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AGPL-3.0.json",
- "referenceNumber": "229",
- "name": "GNU Affero General Public License v3.0",
- "licenseId": "AGPL-3.0",
- "seeAlso": [
- "https://www.gnu.org/licenses/agpl.txt",
- "https://opensource.org/licenses/AGPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AGPL-3.0-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-only.json",
- "referenceNumber": "95",
- "name": "GNU Affero General Public License v3.0 only",
- "licenseId": "AGPL-3.0-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/agpl.txt",
- "https://opensource.org/licenses/AGPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AGPL-3.0-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-or-later.json",
- "referenceNumber": "155",
- "name": "GNU Affero General Public License v3.0 or later",
- "licenseId": "AGPL-3.0-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/agpl.txt",
- "https://opensource.org/licenses/AGPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./AMDPLPA.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/AMDPLPA.json",
- "referenceNumber": "33",
- "name": "AMD\u0027s plpa_map.c License",
- "licenseId": "AMDPLPA",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./AML.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/AML.json",
- "referenceNumber": "148",
- "name": "Apple MIT License",
- "licenseId": "AML",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Apple_MIT_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./AMPAS.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/AMPAS.json",
- "referenceNumber": "191",
- "name": "Academy of Motion Picture Arts and Sciences BSD",
- "licenseId": "AMPAS",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./ANTLR-PD.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ANTLR-PD.json",
- "referenceNumber": "395",
- "name": "ANTLR Software Rights Notice",
- "licenseId": "ANTLR-PD",
- "seeAlso": [
- "http://www.antlr2.org/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./APAFML.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/APAFML.json",
- "referenceNumber": "195",
- "name": "Adobe Postscript AFM License",
- "licenseId": "APAFML",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./APL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/APL-1.0.json",
- "referenceNumber": "252",
- "name": "Adaptive Public License 1.0",
- "licenseId": "APL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/APL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./APSL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/APSL-1.0.json",
- "referenceNumber": "354",
- "name": "Apple Public Source License 1.0",
- "licenseId": "APSL-1.0",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./APSL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/APSL-1.1.json",
- "referenceNumber": "324",
- "name": "Apple Public Source License 1.1",
- "licenseId": "APSL-1.1",
- "seeAlso": [
- "http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./APSL-1.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/APSL-1.2.json",
- "referenceNumber": "34",
- "name": "Apple Public Source License 1.2",
- "licenseId": "APSL-1.2",
- "seeAlso": [
- "http://www.samurajdata.se/opensource/mirror/licenses/apsl.php"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./APSL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/APSL-2.0.json",
- "referenceNumber": "109",
- "name": "Apple Public Source License 2.0",
- "licenseId": "APSL-2.0",
- "seeAlso": [
- "http://www.opensource.apple.com/license/apsl/"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Abstyles.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Abstyles.json",
- "referenceNumber": "80",
- "name": "Abstyles License",
- "licenseId": "Abstyles",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Abstyles"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Adobe-2006.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Adobe-2006.json",
- "referenceNumber": "285",
- "name": "Adobe Systems Incorporated Source Code License Agreement",
- "licenseId": "Adobe-2006",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/AdobeLicense"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Adobe-Glyph.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Adobe-Glyph.json",
- "referenceNumber": "107",
- "name": "Adobe Glyph List License",
- "licenseId": "Adobe-Glyph",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Afmparse.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Afmparse.json",
- "referenceNumber": "42",
- "name": "Afmparse License",
- "licenseId": "Afmparse",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Afmparse"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Aladdin.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Aladdin.json",
- "referenceNumber": "258",
- "name": "Aladdin Free Public License",
- "licenseId": "Aladdin",
- "seeAlso": [
- "http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Apache-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Apache-1.0.json",
- "referenceNumber": "237",
- "name": "Apache License 1.0",
- "licenseId": "Apache-1.0",
- "seeAlso": [
- "http://www.apache.org/licenses/LICENSE-1.0"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Apache-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Apache-1.1.json",
- "referenceNumber": "84",
- "name": "Apache License 1.1",
- "licenseId": "Apache-1.1",
- "seeAlso": [
- "http://apache.org/licenses/LICENSE-1.1",
- "https://opensource.org/licenses/Apache-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Apache-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Apache-2.0.json",
- "referenceNumber": "26",
- "name": "Apache License 2.0",
- "licenseId": "Apache-2.0",
- "seeAlso": [
- "http://www.apache.org/licenses/LICENSE-2.0",
- "https://opensource.org/licenses/Apache-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Artistic-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Artistic-1.0.json",
- "referenceNumber": "165",
- "name": "Artistic License 1.0",
- "licenseId": "Artistic-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/Artistic-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Artistic-1.0-Perl.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-Perl.json",
- "referenceNumber": "377",
- "name": "Artistic License 1.0 (Perl)",
- "licenseId": "Artistic-1.0-Perl",
- "seeAlso": [
- "http://dev.perl.org/licenses/artistic.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Artistic-1.0-cl8.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-cl8.json",
- "referenceNumber": "13",
- "name": "Artistic License 1.0 w/clause 8",
- "licenseId": "Artistic-1.0-cl8",
- "seeAlso": [
- "https://opensource.org/licenses/Artistic-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Artistic-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Artistic-2.0.json",
- "referenceNumber": "189",
- "name": "Artistic License 2.0",
- "licenseId": "Artistic-2.0",
- "seeAlso": [
- "http://www.perlfoundation.org/artistic_license_2_0",
- "https://opensource.org/licenses/artistic-license-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./BSD-1-Clause.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-1-Clause.json",
- "referenceNumber": "358",
- "name": "BSD 1-Clause License",
- "licenseId": "BSD-1-Clause",
- "seeAlso": [
- "https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision\u003d326823"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-2-Clause.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause.json",
- "referenceNumber": "325",
- "name": "BSD 2-Clause \"Simplified\" License",
- "licenseId": "BSD-2-Clause",
- "seeAlso": [
- "https://opensource.org/licenses/BSD-2-Clause"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./BSD-2-Clause-FreeBSD.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-FreeBSD.json",
- "referenceNumber": "121",
- "name": "BSD 2-Clause FreeBSD License",
- "licenseId": "BSD-2-Clause-FreeBSD",
- "seeAlso": [
- "http://www.freebsd.org/copyright/freebsd-license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-2-Clause-NetBSD.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-NetBSD.json",
- "referenceNumber": "381",
- "name": "BSD 2-Clause NetBSD License",
- "licenseId": "BSD-2-Clause-NetBSD",
- "seeAlso": [
- "http://www.netbsd.org/about/redistribution.html#default"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-2-Clause-Patent.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-Patent.json",
- "referenceNumber": "169",
- "name": "BSD-2-Clause Plus Patent License",
- "licenseId": "BSD-2-Clause-Patent",
- "seeAlso": [
- "https://opensource.org/licenses/BSDplusPatent"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./BSD-3-Clause.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause.json",
- "referenceNumber": "270",
- "name": "BSD 3-Clause \"New\" or \"Revised\" License",
- "licenseId": "BSD-3-Clause",
- "seeAlso": [
- "https://opensource.org/licenses/BSD-3-Clause"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./BSD-3-Clause-Attribution.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Attribution.json",
- "referenceNumber": "39",
- "name": "BSD with attribution",
- "licenseId": "BSD-3-Clause-Attribution",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-3-Clause-Clear.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Clear.json",
- "referenceNumber": "212",
- "name": "BSD 3-Clause Clear License",
- "licenseId": "BSD-3-Clause-Clear",
- "seeAlso": [
- "http://labs.metacarta.com/license-explanation.html#license"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-3-Clause-LBNL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-LBNL.json",
- "referenceNumber": "337",
- "name": "Lawrence Berkeley National Labs BSD variant license",
- "licenseId": "BSD-3-Clause-LBNL",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/LBNLBSD"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./BSD-3-Clause-No-Nuclear-License.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json",
- "referenceNumber": "12",
- "name": "BSD 3-Clause No Nuclear License",
- "licenseId": "BSD-3-Clause-No-Nuclear-License",
- "seeAlso": [
- "http://download.oracle.com/otn-pub/java/licenses/bsd.txt?AuthParam\u003d1467140197_43d516ce1776bd08a58235a7785be1cc"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-3-Clause-No-Nuclear-License-2014.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json",
- "referenceNumber": "137",
- "name": "BSD 3-Clause No Nuclear License 2014",
- "licenseId": "BSD-3-Clause-No-Nuclear-License-2014",
- "seeAlso": [
- "https://java.net/projects/javaeetutorial/pages/BerkeleyLicense"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-3-Clause-No-Nuclear-Warranty.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json",
- "referenceNumber": "44",
- "name": "BSD 3-Clause No Nuclear Warranty",
- "licenseId": "BSD-3-Clause-No-Nuclear-Warranty",
- "seeAlso": [
- "https://jogamp.org/git/?p\u003dgluegen.git;a\u003dblob_plain;f\u003dLICENSE.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-3-Clause-Open-MPI.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Open-MPI.json",
- "referenceNumber": "349",
- "name": "BSD 3-Clause Open MPI variant",
- "licenseId": "BSD-3-Clause-Open-MPI",
- "seeAlso": [
- "https://www.open-mpi.org/community/license.php",
- "http://www.netlib.org/lapack/LICENSE.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-4-Clause.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause.json",
- "referenceNumber": "162",
- "name": "BSD 4-Clause \"Original\" or \"Old\" License",
- "licenseId": "BSD-4-Clause",
- "seeAlso": [
- "http://directory.fsf.org/wiki/License:BSD_4Clause"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-4-Clause-UC.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause-UC.json",
- "referenceNumber": "203",
- "name": "BSD-4-Clause (University of California-Specific)",
- "licenseId": "BSD-4-Clause-UC",
- "seeAlso": [
- "http://www.freebsd.org/copyright/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-Protection.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-Protection.json",
- "referenceNumber": "119",
- "name": "BSD Protection License",
- "licenseId": "BSD-Protection",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/BSD_Protection_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSD-Source-Code.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BSD-Source-Code.json",
- "referenceNumber": "308",
- "name": "BSD Source Code Attribution",
- "licenseId": "BSD-Source-Code",
- "seeAlso": [
- "https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BSL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/BSL-1.0.json",
- "referenceNumber": "224",
- "name": "Boost Software License 1.0",
- "licenseId": "BSL-1.0",
- "seeAlso": [
- "http://www.boost.org/LICENSE_1_0.txt",
- "https://opensource.org/licenses/BSL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Bahyph.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Bahyph.json",
- "referenceNumber": "366",
- "name": "Bahyph License",
- "licenseId": "Bahyph",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Bahyph"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Barr.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Barr.json",
- "referenceNumber": "333",
- "name": "Barr License",
- "licenseId": "Barr",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Barr"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Beerware.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Beerware.json",
- "referenceNumber": "17",
- "name": "Beerware License",
- "licenseId": "Beerware",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Beerware",
- "https://people.freebsd.org/~phk/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BitTorrent-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.0.json",
- "referenceNumber": "218",
- "name": "BitTorrent Open Source License v1.0",
- "licenseId": "BitTorrent-1.0",
- "seeAlso": [
- "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1\u003d1.1\u0026r2\u003d1.1.1.1\u0026diff_format\u003ds"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BitTorrent-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.1.json",
- "referenceNumber": "179",
- "name": "BitTorrent Open Source License v1.1",
- "licenseId": "BitTorrent-1.1",
- "seeAlso": [
- "http://directory.fsf.org/wiki/License:BitTorrentOSL1.1"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./BlueOak-1.0.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/BlueOak-1.0.0.json",
- "referenceNumber": "23",
- "name": "Blue Oak Model License 1.0.0",
- "licenseId": "BlueOak-1.0.0",
- "seeAlso": [
- "https://blueoakcouncil.org/license/1.0.0"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Borceux.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Borceux.json",
- "referenceNumber": "311",
- "name": "Borceux license",
- "licenseId": "Borceux",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Borceux"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CATOSL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CATOSL-1.1.json",
- "referenceNumber": "262",
- "name": "Computer Associates Trusted Open Source License 1.1",
- "licenseId": "CATOSL-1.1",
- "seeAlso": [
- "https://opensource.org/licenses/CATOSL-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./CC-BY-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-1.0.json",
- "referenceNumber": "128",
- "name": "Creative Commons Attribution 1.0 Generic",
- "licenseId": "CC-BY-1.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by/1.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-2.0.json",
- "referenceNumber": "232",
- "name": "Creative Commons Attribution 2.0 Generic",
- "licenseId": "CC-BY-2.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by/2.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-2.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-2.5.json",
- "referenceNumber": "129",
- "name": "Creative Commons Attribution 2.5 Generic",
- "licenseId": "CC-BY-2.5",
- "seeAlso": [
- "https://creativecommons.org/licenses/by/2.5/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-3.0.json",
- "referenceNumber": "256",
- "name": "Creative Commons Attribution 3.0 Unported",
- "licenseId": "CC-BY-3.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by/3.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-4.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-4.0.json",
- "referenceNumber": "330",
- "name": "Creative Commons Attribution 4.0 International",
- "licenseId": "CC-BY-4.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by/4.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-1.0.json",
- "referenceNumber": "130",
- "name": "Creative Commons Attribution Non Commercial 1.0 Generic",
- "licenseId": "CC-BY-NC-1.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc/1.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.0.json",
- "referenceNumber": "244",
- "name": "Creative Commons Attribution Non Commercial 2.0 Generic",
- "licenseId": "CC-BY-NC-2.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc/2.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-2.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.5.json",
- "referenceNumber": "1",
- "name": "Creative Commons Attribution Non Commercial 2.5 Generic",
- "licenseId": "CC-BY-NC-2.5",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc/2.5/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-3.0.json",
- "referenceNumber": "255",
- "name": "Creative Commons Attribution Non Commercial 3.0 Unported",
- "licenseId": "CC-BY-NC-3.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc/3.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-4.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-4.0.json",
- "referenceNumber": "186",
- "name": "Creative Commons Attribution Non Commercial 4.0 International",
- "licenseId": "CC-BY-NC-4.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc/4.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-ND-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-1.0.json",
- "referenceNumber": "59",
- "name": "Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic",
- "licenseId": "CC-BY-NC-ND-1.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-ND-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.0.json",
- "referenceNumber": "36",
- "name": "Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic",
- "licenseId": "CC-BY-NC-ND-2.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-ND-2.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.5.json",
- "referenceNumber": "158",
- "name": "Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic",
- "licenseId": "CC-BY-NC-ND-2.5",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-ND-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-3.0.json",
- "referenceNumber": "48",
- "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported",
- "licenseId": "CC-BY-NC-ND-3.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-ND-4.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-4.0.json",
- "referenceNumber": "281",
- "name": "Creative Commons Attribution Non Commercial No Derivatives 4.0 International",
- "licenseId": "CC-BY-NC-ND-4.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-SA-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-1.0.json",
- "referenceNumber": "178",
- "name": "Creative Commons Attribution Non Commercial Share Alike 1.0 Generic",
- "licenseId": "CC-BY-NC-SA-1.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-SA-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.0.json",
- "referenceNumber": "81",
- "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Generic",
- "licenseId": "CC-BY-NC-SA-2.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-SA-2.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.5.json",
- "referenceNumber": "62",
- "name": "Creative Commons Attribution Non Commercial Share Alike 2.5 Generic",
- "licenseId": "CC-BY-NC-SA-2.5",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-SA-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-3.0.json",
- "referenceNumber": "22",
- "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Unported",
- "licenseId": "CC-BY-NC-SA-3.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-NC-SA-4.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-4.0.json",
- "referenceNumber": "47",
- "name": "Creative Commons Attribution Non Commercial Share Alike 4.0 International",
- "licenseId": "CC-BY-NC-SA-4.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-ND-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-1.0.json",
- "referenceNumber": "50",
- "name": "Creative Commons Attribution No Derivatives 1.0 Generic",
- "licenseId": "CC-BY-ND-1.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nd/1.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-ND-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.0.json",
- "referenceNumber": "287",
- "name": "Creative Commons Attribution No Derivatives 2.0 Generic",
- "licenseId": "CC-BY-ND-2.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nd/2.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-ND-2.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.5.json",
- "referenceNumber": "68",
- "name": "Creative Commons Attribution No Derivatives 2.5 Generic",
- "licenseId": "CC-BY-ND-2.5",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nd/2.5/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-ND-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-3.0.json",
- "referenceNumber": "393",
- "name": "Creative Commons Attribution No Derivatives 3.0 Unported",
- "licenseId": "CC-BY-ND-3.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nd/3.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-ND-4.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-4.0.json",
- "referenceNumber": "132",
- "name": "Creative Commons Attribution No Derivatives 4.0 International",
- "licenseId": "CC-BY-ND-4.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-nd/4.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-SA-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-1.0.json",
- "referenceNumber": "322",
- "name": "Creative Commons Attribution Share Alike 1.0 Generic",
- "licenseId": "CC-BY-SA-1.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-sa/1.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-SA-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.0.json",
- "referenceNumber": "142",
- "name": "Creative Commons Attribution Share Alike 2.0 Generic",
- "licenseId": "CC-BY-SA-2.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-sa/2.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-SA-2.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.5.json",
- "referenceNumber": "306",
- "name": "Creative Commons Attribution Share Alike 2.5 Generic",
- "licenseId": "CC-BY-SA-2.5",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-sa/2.5/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-SA-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-3.0.json",
- "referenceNumber": "394",
- "name": "Creative Commons Attribution Share Alike 3.0 Unported",
- "licenseId": "CC-BY-SA-3.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-sa/3.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-BY-SA-4.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-4.0.json",
- "referenceNumber": "32",
- "name": "Creative Commons Attribution Share Alike 4.0 International",
- "licenseId": "CC-BY-SA-4.0",
- "seeAlso": [
- "https://creativecommons.org/licenses/by-sa/4.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC-PDDC.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CC-PDDC.json",
- "referenceNumber": "371",
- "name": "Creative Commons Public Domain Dedication and Certification",
- "licenseId": "CC-PDDC",
- "seeAlso": [
- "https://creativecommons.org/licenses/publicdomain/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CC0-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CC0-1.0.json",
- "referenceNumber": "213",
- "name": "Creative Commons Zero v1.0 Universal",
- "licenseId": "CC0-1.0",
- "seeAlso": [
- "https://creativecommons.org/publicdomain/zero/1.0/legalcode"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CDDL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CDDL-1.0.json",
- "referenceNumber": "138",
- "name": "Common Development and Distribution License 1.0",
- "licenseId": "CDDL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/cddl1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./CDDL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CDDL-1.1.json",
- "referenceNumber": "376",
- "name": "Common Development and Distribution License 1.1",
- "licenseId": "CDDL-1.1",
- "seeAlso": [
- "http://glassfish.java.net/public/CDDL+GPL_1_1.html",
- "https://javaee.github.io/glassfish/LICENSE"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CDLA-Permissive-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CDLA-Permissive-1.0.json",
- "referenceNumber": "250",
- "name": "Community Data License Agreement Permissive 1.0",
- "licenseId": "CDLA-Permissive-1.0",
- "seeAlso": [
- "https://cdla.io/permissive-1-0"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CDLA-Sharing-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CDLA-Sharing-1.0.json",
- "referenceNumber": "310",
- "name": "Community Data License Agreement Sharing 1.0",
- "licenseId": "CDLA-Sharing-1.0",
- "seeAlso": [
- "https://cdla.io/sharing-1-0"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CECILL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CECILL-1.0.json",
- "referenceNumber": "223",
- "name": "CeCILL Free Software License Agreement v1.0",
- "licenseId": "CECILL-1.0",
- "seeAlso": [
- "http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CECILL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CECILL-1.1.json",
- "referenceNumber": "300",
- "name": "CeCILL Free Software License Agreement v1.1",
- "licenseId": "CECILL-1.1",
- "seeAlso": [
- "http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CECILL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CECILL-2.0.json",
- "referenceNumber": "352",
- "name": "CeCILL Free Software License Agreement v2.0",
- "licenseId": "CECILL-2.0",
- "seeAlso": [
- "http://www.cecill.info/licences/Licence_CeCILL_V2-en.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CECILL-2.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CECILL-2.1.json",
- "referenceNumber": "120",
- "name": "CeCILL Free Software License Agreement v2.1",
- "licenseId": "CECILL-2.1",
- "seeAlso": [
- "http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./CECILL-B.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CECILL-B.json",
- "referenceNumber": "340",
- "name": "CeCILL-B Free Software License Agreement",
- "licenseId": "CECILL-B",
- "seeAlso": [
- "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CECILL-C.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CECILL-C.json",
- "referenceNumber": "77",
- "name": "CeCILL-C Free Software License Agreement",
- "licenseId": "CECILL-C",
- "seeAlso": [
- "http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CERN-OHL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.1.json",
- "referenceNumber": "341",
- "name": "CERN Open Hardware License v1.1",
- "licenseId": "CERN-OHL-1.1",
- "seeAlso": [
- "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CERN-OHL-1.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.2.json",
- "referenceNumber": "3",
- "name": "CERN Open Hardware Licence v1.2",
- "licenseId": "CERN-OHL-1.2",
- "seeAlso": [
- "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CNRI-Jython.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CNRI-Jython.json",
- "referenceNumber": "94",
- "name": "CNRI Jython License",
- "licenseId": "CNRI-Jython",
- "seeAlso": [
- "http://www.jython.org/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CNRI-Python.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CNRI-Python.json",
- "referenceNumber": "45",
- "name": "CNRI Python License",
- "licenseId": "CNRI-Python",
- "seeAlso": [
- "https://opensource.org/licenses/CNRI-Python"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./CNRI-Python-GPL-Compatible.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CNRI-Python-GPL-Compatible.json",
- "referenceNumber": "202",
- "name": "CNRI Python Open Source GPL Compatible License Agreement",
- "licenseId": "CNRI-Python-GPL-Compatible",
- "seeAlso": [
- "http://www.python.org/download/releases/1.6.1/download_win/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CPAL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CPAL-1.0.json",
- "referenceNumber": "170",
- "name": "Common Public Attribution License 1.0",
- "licenseId": "CPAL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/CPAL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./CPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/CPL-1.0.json",
- "referenceNumber": "172",
- "name": "Common Public License 1.0",
- "licenseId": "CPL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/CPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./CPOL-1.02.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CPOL-1.02.json",
- "referenceNumber": "28",
- "name": "Code Project Open License 1.02",
- "licenseId": "CPOL-1.02",
- "seeAlso": [
- "http://www.codeproject.com/info/cpol10.aspx"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CUA-OPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CUA-OPL-1.0.json",
- "referenceNumber": "365",
- "name": "CUA Office Public License v1.0",
- "licenseId": "CUA-OPL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/CUA-OPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Caldera.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Caldera.json",
- "referenceNumber": "108",
- "name": "Caldera License",
- "licenseId": "Caldera",
- "seeAlso": [
- "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./ClArtistic.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/ClArtistic.json",
- "referenceNumber": "271",
- "name": "Clarified Artistic License",
- "licenseId": "ClArtistic",
- "seeAlso": [
- "http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/",
- "http://www.ncftp.com/ncftp/doc/LICENSE.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Condor-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Condor-1.1.json",
- "referenceNumber": "307",
- "name": "Condor Public License v1.1",
- "licenseId": "Condor-1.1",
- "seeAlso": [
- "http://research.cs.wisc.edu/condor/license.html#condor",
- "http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Crossword.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Crossword.json",
- "referenceNumber": "363",
- "name": "Crossword License",
- "licenseId": "Crossword",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Crossword"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./CrystalStacker.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/CrystalStacker.json",
- "referenceNumber": "168",
- "name": "CrystalStacker License",
- "licenseId": "CrystalStacker",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd\u003dLicensing/CrystalStacker"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Cube.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Cube.json",
- "referenceNumber": "370",
- "name": "Cube License",
- "licenseId": "Cube",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Cube"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./D-FSL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/D-FSL-1.0.json",
- "referenceNumber": "182",
- "name": "Deutsche Freie Software Lizenz",
- "licenseId": "D-FSL-1.0",
- "seeAlso": [
- "http://www.dipp.nrw.de/d-fsl/lizenzen/",
- "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt",
- "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt",
- "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl",
- "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz",
- "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license",
- "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file",
- "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./DOC.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/DOC.json",
- "referenceNumber": "160",
- "name": "DOC License",
- "licenseId": "DOC",
- "seeAlso": [
- "http://www.cs.wustl.edu/~schmidt/ACE-copying.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./DSDP.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/DSDP.json",
- "referenceNumber": "141",
- "name": "DSDP License",
- "licenseId": "DSDP",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/DSDP"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Dotseqn.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Dotseqn.json",
- "referenceNumber": "390",
- "name": "Dotseqn License",
- "licenseId": "Dotseqn",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Dotseqn"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./ECL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ECL-1.0.json",
- "referenceNumber": "396",
- "name": "Educational Community License v1.0",
- "licenseId": "ECL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/ECL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./ECL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/ECL-2.0.json",
- "referenceNumber": "298",
- "name": "Educational Community License v2.0",
- "licenseId": "ECL-2.0",
- "seeAlso": [
- "https://opensource.org/licenses/ECL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./EFL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/EFL-1.0.json",
- "referenceNumber": "150",
- "name": "Eiffel Forum License v1.0",
- "licenseId": "EFL-1.0",
- "seeAlso": [
- "http://www.eiffel-nice.org/license/forum.txt",
- "https://opensource.org/licenses/EFL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./EFL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/EFL-2.0.json",
- "referenceNumber": "161",
- "name": "Eiffel Forum License v2.0",
- "licenseId": "EFL-2.0",
- "seeAlso": [
- "http://www.eiffel-nice.org/license/eiffel-forum-license-2.html",
- "https://opensource.org/licenses/EFL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./EPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/EPL-1.0.json",
- "referenceNumber": "214",
- "name": "Eclipse Public License 1.0",
- "licenseId": "EPL-1.0",
- "seeAlso": [
- "http://www.eclipse.org/legal/epl-v10.html",
- "https://opensource.org/licenses/EPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./EPL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/EPL-2.0.json",
- "referenceNumber": "134",
- "name": "Eclipse Public License 2.0",
- "licenseId": "EPL-2.0",
- "seeAlso": [
- "https://www.eclipse.org/legal/epl-2.0",
- "https://www.opensource.org/licenses/EPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./EUDatagrid.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/EUDatagrid.json",
- "referenceNumber": "192",
- "name": "EU DataGrid Software License",
- "licenseId": "EUDatagrid",
- "seeAlso": [
- "http://eu-datagrid.web.cern.ch/eu-datagrid/license.html",
- "https://opensource.org/licenses/EUDatagrid"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./EUPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/EUPL-1.0.json",
- "referenceNumber": "173",
- "name": "European Union Public License 1.0",
- "licenseId": "EUPL-1.0",
- "seeAlso": [
- "http://ec.europa.eu/idabc/en/document/7330.html",
- "http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id\u003d31096"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./EUPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/EUPL-1.1.json",
- "referenceNumber": "92",
- "name": "European Union Public License 1.1",
- "licenseId": "EUPL-1.1",
- "seeAlso": [
- "https://joinup.ec.europa.eu/software/page/eupl/licence-eupl",
- "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf",
- "https://opensource.org/licenses/EUPL-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./EUPL-1.2.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/EUPL-1.2.json",
- "referenceNumber": "387",
- "name": "European Union Public License 1.2",
- "licenseId": "EUPL-1.2",
- "seeAlso": [
- "https://joinup.ec.europa.eu/page/eupl-text-11-12",
- "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf",
- "https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt",
- "http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri\u003dCELEX:32017D0863",
- "https://opensource.org/licenses/EUPL-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Entessa.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Entessa.json",
- "referenceNumber": "99",
- "name": "Entessa Public License v1.0",
- "licenseId": "Entessa",
- "seeAlso": [
- "https://opensource.org/licenses/Entessa"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./ErlPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ErlPL-1.1.json",
- "referenceNumber": "157",
- "name": "Erlang Public License v1.1",
- "licenseId": "ErlPL-1.1",
- "seeAlso": [
- "http://www.erlang.org/EPLICENSE"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Eurosym.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Eurosym.json",
- "referenceNumber": "113",
- "name": "Eurosym License",
- "licenseId": "Eurosym",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Eurosym"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./FSFAP.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/FSFAP.json",
- "referenceNumber": "114",
- "name": "FSF All Permissive License",
- "licenseId": "FSFAP",
- "seeAlso": [
- "https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./FSFUL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/FSFUL.json",
- "referenceNumber": "193",
- "name": "FSF Unlimited License",
- "licenseId": "FSFUL",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./FSFULLR.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/FSFULLR.json",
- "referenceNumber": "43",
- "name": "FSF Unlimited License (with License Retention)",
- "licenseId": "FSFULLR",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./FTL.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/FTL.json",
- "referenceNumber": "240",
- "name": "Freetype Project License",
- "licenseId": "FTL",
- "seeAlso": [
- "http://freetype.fis.uniroma2.it/FTL.TXT",
- "http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Fair.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Fair.json",
- "referenceNumber": "297",
- "name": "Fair License",
- "licenseId": "Fair",
- "seeAlso": [
- "http://fairlicense.org/",
- "https://opensource.org/licenses/Fair"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Frameworx-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Frameworx-1.0.json",
- "referenceNumber": "389",
- "name": "Frameworx Open License 1.0",
- "licenseId": "Frameworx-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/Frameworx-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./FreeImage.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/FreeImage.json",
- "referenceNumber": "277",
- "name": "FreeImage Public License v1.0",
- "licenseId": "FreeImage",
- "seeAlso": [
- "http://freeimage.sourceforge.net/freeimage-license.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.1.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.1.json",
- "referenceNumber": "98",
- "name": "GNU Free Documentation License v1.1",
- "licenseId": "GFDL-1.1",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.1-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-only.json",
- "referenceNumber": "102",
- "name": "GNU Free Documentation License v1.1 only",
- "licenseId": "GFDL-1.1-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.1-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-or-later.json",
- "referenceNumber": "348",
- "name": "GNU Free Documentation License v1.1 or later",
- "licenseId": "GFDL-1.1-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.2.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.2.json",
- "referenceNumber": "197",
- "name": "GNU Free Documentation License v1.2",
- "licenseId": "GFDL-1.2",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.2-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-only.json",
- "referenceNumber": "236",
- "name": "GNU Free Documentation License v1.2 only",
- "licenseId": "GFDL-1.2-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.2-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-or-later.json",
- "referenceNumber": "215",
- "name": "GNU Free Documentation License v1.2 or later",
- "licenseId": "GFDL-1.2-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.3.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.3.json",
- "referenceNumber": "112",
- "name": "GNU Free Documentation License v1.3",
- "licenseId": "GFDL-1.3",
- "seeAlso": [
- "https://www.gnu.org/licenses/fdl-1.3.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.3-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-only.json",
- "referenceNumber": "69",
- "name": "GNU Free Documentation License v1.3 only",
- "licenseId": "GFDL-1.3-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/fdl-1.3.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GFDL-1.3-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-or-later.json",
- "referenceNumber": "4",
- "name": "GNU Free Documentation License v1.3 or later",
- "licenseId": "GFDL-1.3-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/fdl-1.3.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GL2PS.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/GL2PS.json",
- "referenceNumber": "124",
- "name": "GL2PS License",
- "licenseId": "GL2PS",
- "seeAlso": [
- "http://www.geuz.org/gl2ps/COPYING.GL2PS"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-1.0.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-1.0.json",
- "referenceNumber": "79",
- "name": "GNU General Public License v1.0 only",
- "licenseId": "GPL-1.0",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-1.0+.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-1.0+.json",
- "referenceNumber": "175",
- "name": "GNU General Public License v1.0 or later",
- "licenseId": "GPL-1.0+",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-1.0-only.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/GPL-1.0-only.json",
- "referenceNumber": "15",
- "name": "GNU General Public License v1.0 only",
- "licenseId": "GPL-1.0-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-1.0-or-later.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/GPL-1.0-or-later.json",
- "referenceNumber": "357",
- "name": "GNU General Public License v1.0 or later",
- "licenseId": "GPL-1.0-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-2.0.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0.json",
- "referenceNumber": "147",
- "name": "GNU General Public License v2.0 only",
- "licenseId": "GPL-2.0",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html",
- "https://opensource.org/licenses/GPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-2.0+.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0+.json",
- "referenceNumber": "75",
- "name": "GNU General Public License v2.0 or later",
- "licenseId": "GPL-2.0+",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html",
- "https://opensource.org/licenses/GPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-2.0-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0-only.json",
- "referenceNumber": "233",
- "name": "GNU General Public License v2.0 only",
- "licenseId": "GPL-2.0-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html",
- "https://opensource.org/licenses/GPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-2.0-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0-or-later.json",
- "referenceNumber": "56",
- "name": "GNU General Public License v2.0 or later",
- "licenseId": "GPL-2.0-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html",
- "https://opensource.org/licenses/GPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-2.0-with-GCC-exception.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-GCC-exception.json",
- "referenceNumber": "117",
- "name": "GNU General Public License v2.0 w/GCC Runtime Library exception",
- "licenseId": "GPL-2.0-with-GCC-exception",
- "seeAlso": [
- "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-2.0-with-autoconf-exception.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json",
- "referenceNumber": "355",
- "name": "GNU General Public License v2.0 w/Autoconf exception",
- "licenseId": "GPL-2.0-with-autoconf-exception",
- "seeAlso": [
- "http://ac-archive.sourceforge.net/doc/copyright.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-2.0-with-bison-exception.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-bison-exception.json",
- "referenceNumber": "378",
- "name": "GNU General Public License v2.0 w/Bison exception",
- "licenseId": "GPL-2.0-with-bison-exception",
- "seeAlso": [
- "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-2.0-with-classpath-exception.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-classpath-exception.json",
- "referenceNumber": "60",
- "name": "GNU General Public License v2.0 w/Classpath exception",
- "licenseId": "GPL-2.0-with-classpath-exception",
- "seeAlso": [
- "https://www.gnu.org/software/classpath/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-2.0-with-font-exception.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-font-exception.json",
- "referenceNumber": "375",
- "name": "GNU General Public License v2.0 w/Font exception",
- "licenseId": "GPL-2.0-with-font-exception",
- "seeAlso": [
- "https://www.gnu.org/licenses/gpl-faq.html#FontException"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./GPL-3.0.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-3.0.json",
- "referenceNumber": "242",
- "name": "GNU General Public License v3.0 only",
- "licenseId": "GPL-3.0",
- "seeAlso": [
- "https://www.gnu.org/licenses/gpl-3.0-standalone.html",
- "https://opensource.org/licenses/GPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-3.0+.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-3.0+.json",
- "referenceNumber": "73",
- "name": "GNU General Public License v3.0 or later",
- "licenseId": "GPL-3.0+",
- "seeAlso": [
- "https://www.gnu.org/licenses/gpl-3.0-standalone.html",
- "https://opensource.org/licenses/GPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-3.0-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-3.0-only.json",
- "referenceNumber": "206",
- "name": "GNU General Public License v3.0 only",
- "licenseId": "GPL-3.0-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/gpl-3.0-standalone.html",
- "https://opensource.org/licenses/GPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-3.0-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-3.0-or-later.json",
- "referenceNumber": "196",
- "name": "GNU General Public License v3.0 or later",
- "licenseId": "GPL-3.0-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/gpl-3.0-standalone.html",
- "https://opensource.org/licenses/GPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-3.0-with-GCC-exception.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-GCC-exception.json",
- "referenceNumber": "221",
- "name": "GNU General Public License v3.0 w/GCC Runtime Library exception",
- "licenseId": "GPL-3.0-with-GCC-exception",
- "seeAlso": [
- "https://www.gnu.org/licenses/gcc-exception-3.1.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./GPL-3.0-with-autoconf-exception.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json",
- "referenceNumber": "235",
- "name": "GNU General Public License v3.0 w/Autoconf exception",
- "licenseId": "GPL-3.0-with-autoconf-exception",
- "seeAlso": [
- "https://www.gnu.org/licenses/autoconf-exception-3.0.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Giftware.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Giftware.json",
- "referenceNumber": "369",
- "name": "Giftware License",
- "licenseId": "Giftware",
- "seeAlso": [
- "http://liballeg.org/license.html#allegro-4-the-giftware-license"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Glide.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Glide.json",
- "referenceNumber": "374",
- "name": "3dfx Glide License",
- "licenseId": "Glide",
- "seeAlso": [
- "http://www.users.on.net/~triforce/glidexp/COPYING.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Glulxe.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Glulxe.json",
- "referenceNumber": "93",
- "name": "Glulxe License",
- "licenseId": "Glulxe",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Glulxe"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./HPND.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/HPND.json",
- "referenceNumber": "264",
- "name": "Historical Permission Notice and Disclaimer",
- "licenseId": "HPND",
- "seeAlso": [
- "https://opensource.org/licenses/HPND"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./HPND-sell-variant.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/HPND-sell-variant.json",
- "referenceNumber": "145",
- "name": "Historical Permission Notice and Disclaimer - sell variant",
- "licenseId": "HPND-sell-variant",
- "seeAlso": [
- "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h\u003dv4.19"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./HaskellReport.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/HaskellReport.json",
- "referenceNumber": "122",
- "name": "Haskell Language Report License",
- "licenseId": "HaskellReport",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./IBM-pibs.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/IBM-pibs.json",
- "referenceNumber": "207",
- "name": "IBM PowerPC Initialization and Boot Software",
- "licenseId": "IBM-pibs",
- "seeAlso": [
- "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003darch/powerpc/cpu/ppc4xx/miiphy.c;h\u003d297155fdafa064b955e53e9832de93bfb0cfb85b;hb\u003d9fab4bf4cc077c21e43941866f3f2c196f28670d"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./ICU.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ICU.json",
- "referenceNumber": "194",
- "name": "ICU License",
- "licenseId": "ICU",
- "seeAlso": [
- "http://source.icu-project.org/repos/icu/icu/trunk/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./IJG.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/IJG.json",
- "referenceNumber": "55",
- "name": "Independent JPEG Group License",
- "licenseId": "IJG",
- "seeAlso": [
- "http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev\u003d1.2"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./IPA.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/IPA.json",
- "referenceNumber": "312",
- "name": "IPA Font License",
- "licenseId": "IPA",
- "seeAlso": [
- "https://opensource.org/licenses/IPA"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./IPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/IPL-1.0.json",
- "referenceNumber": "31",
- "name": "IBM Public License v1.0",
- "licenseId": "IPL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/IPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./ISC.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/ISC.json",
- "referenceNumber": "110",
- "name": "ISC License",
- "licenseId": "ISC",
- "seeAlso": [
- "https://www.isc.org/downloads/software-support-policy/isc-license/",
- "https://opensource.org/licenses/ISC"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./ImageMagick.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ImageMagick.json",
- "referenceNumber": "231",
- "name": "ImageMagick License",
- "licenseId": "ImageMagick",
- "seeAlso": [
- "http://www.imagemagick.org/script/license.php"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Imlib2.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Imlib2.json",
- "referenceNumber": "257",
- "name": "Imlib2 License",
- "licenseId": "Imlib2",
- "seeAlso": [
- "http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING",
- "https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Info-ZIP.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Info-ZIP.json",
- "referenceNumber": "104",
- "name": "Info-ZIP License",
- "licenseId": "Info-ZIP",
- "seeAlso": [
- "http://www.info-zip.org/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Intel.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Intel.json",
- "referenceNumber": "167",
- "name": "Intel Open Source License",
- "licenseId": "Intel",
- "seeAlso": [
- "https://opensource.org/licenses/Intel"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Intel-ACPI.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Intel-ACPI.json",
- "referenceNumber": "88",
- "name": "Intel ACPI Software License Agreement",
- "licenseId": "Intel-ACPI",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Interbase-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Interbase-1.0.json",
- "referenceNumber": "83",
- "name": "Interbase Public License v1.0",
- "licenseId": "Interbase-1.0",
- "seeAlso": [
- "https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./JPNIC.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/JPNIC.json",
- "referenceNumber": "105",
- "name": "Japan Network Information Center License",
- "licenseId": "JPNIC",
- "seeAlso": [
- "https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./JSON.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/JSON.json",
- "referenceNumber": "372",
- "name": "JSON License",
- "licenseId": "JSON",
- "seeAlso": [
- "http://www.json.org/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./JasPer-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/JasPer-2.0.json",
- "referenceNumber": "239",
- "name": "JasPer License",
- "licenseId": "JasPer-2.0",
- "seeAlso": [
- "http://www.ece.uvic.ca/~mdadams/jasper/LICENSE"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LAL-1.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LAL-1.2.json",
- "referenceNumber": "380",
- "name": "Licence Art Libre 1.2",
- "licenseId": "LAL-1.2",
- "seeAlso": [
- "http://artlibre.org/licence/lal/licence-art-libre-12/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LAL-1.3.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LAL-1.3.json",
- "referenceNumber": "156",
- "name": "Licence Art Libre 1.3",
- "licenseId": "LAL-1.3",
- "seeAlso": [
- "http://artlibre.org/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LGPL-2.0.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.0.json",
- "referenceNumber": "268",
- "name": "GNU Library General Public License v2 only",
- "licenseId": "LGPL-2.0",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-2.0+.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.0+.json",
- "referenceNumber": "52",
- "name": "GNU Library General Public License v2 or later",
- "licenseId": "LGPL-2.0+",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-2.0-only.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-only.json",
- "referenceNumber": "276",
- "name": "GNU Library General Public License v2 only",
- "licenseId": "LGPL-2.0-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-2.0-or-later.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-or-later.json",
- "referenceNumber": "217",
- "name": "GNU Library General Public License v2 or later",
- "licenseId": "LGPL-2.0-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-2.1.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.1.json",
- "referenceNumber": "166",
- "name": "GNU Lesser General Public License v2.1 only",
- "licenseId": "LGPL-2.1",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html",
- "https://opensource.org/licenses/LGPL-2.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-2.1+.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.1+.json",
- "referenceNumber": "64",
- "name": "GNU Library General Public License v2.1 or later",
- "licenseId": "LGPL-2.1+",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html",
- "https://opensource.org/licenses/LGPL-2.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-2.1-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-only.json",
- "referenceNumber": "2",
- "name": "GNU Lesser General Public License v2.1 only",
- "licenseId": "LGPL-2.1-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html",
- "https://opensource.org/licenses/LGPL-2.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-2.1-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-or-later.json",
- "referenceNumber": "338",
- "name": "GNU Lesser General Public License v2.1 or later",
- "licenseId": "LGPL-2.1-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html",
- "https://opensource.org/licenses/LGPL-2.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-3.0.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-3.0.json",
- "referenceNumber": "210",
- "name": "GNU Lesser General Public License v3.0 only",
- "licenseId": "LGPL-3.0",
- "seeAlso": [
- "https://www.gnu.org/licenses/lgpl-3.0-standalone.html",
- "https://opensource.org/licenses/LGPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-3.0+.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-3.0+.json",
- "referenceNumber": "152",
- "name": "GNU Lesser General Public License v3.0 or later",
- "licenseId": "LGPL-3.0+",
- "seeAlso": [
- "https://www.gnu.org/licenses/lgpl-3.0-standalone.html",
- "https://opensource.org/licenses/LGPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-3.0-only.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-only.json",
- "referenceNumber": "254",
- "name": "GNU Lesser General Public License v3.0 only",
- "licenseId": "LGPL-3.0-only",
- "seeAlso": [
- "https://www.gnu.org/licenses/lgpl-3.0-standalone.html",
- "https://opensource.org/licenses/LGPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPL-3.0-or-later.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-or-later.json",
- "referenceNumber": "301",
- "name": "GNU Lesser General Public License v3.0 or later",
- "licenseId": "LGPL-3.0-or-later",
- "seeAlso": [
- "https://www.gnu.org/licenses/lgpl-3.0-standalone.html",
- "https://opensource.org/licenses/LGPL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LGPLLR.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LGPLLR.json",
- "referenceNumber": "103",
- "name": "Lesser General Public License For Linguistic Resources",
- "licenseId": "LGPLLR",
- "seeAlso": [
- "http://www-igm.univ-mlv.fr/~unitex/lgpllr.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LPL-1.0.json",
- "referenceNumber": "89",
- "name": "Lucent Public License Version 1.0",
- "licenseId": "LPL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/LPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LPL-1.02.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LPL-1.02.json",
- "referenceNumber": "131",
- "name": "Lucent Public License v1.02",
- "licenseId": "LPL-1.02",
- "seeAlso": [
- "http://plan9.bell-labs.com/plan9/license.html",
- "https://opensource.org/licenses/LPL-1.02"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LPPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LPPL-1.0.json",
- "referenceNumber": "259",
- "name": "LaTeX Project Public License v1.0",
- "licenseId": "LPPL-1.0",
- "seeAlso": [
- "http://www.latex-project.org/lppl/lppl-1-0.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LPPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LPPL-1.1.json",
- "referenceNumber": "309",
- "name": "LaTeX Project Public License v1.1",
- "licenseId": "LPPL-1.1",
- "seeAlso": [
- "http://www.latex-project.org/lppl/lppl-1-1.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LPPL-1.2.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LPPL-1.2.json",
- "referenceNumber": "392",
- "name": "LaTeX Project Public License v1.2",
- "licenseId": "LPPL-1.2",
- "seeAlso": [
- "http://www.latex-project.org/lppl/lppl-1-2.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LPPL-1.3a.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/LPPL-1.3a.json",
- "referenceNumber": "305",
- "name": "LaTeX Project Public License v1.3a",
- "licenseId": "LPPL-1.3a",
- "seeAlso": [
- "http://www.latex-project.org/lppl/lppl-1-3a.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LPPL-1.3c.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LPPL-1.3c.json",
- "referenceNumber": "326",
- "name": "LaTeX Project Public License v1.3c",
- "licenseId": "LPPL-1.3c",
- "seeAlso": [
- "http://www.latex-project.org/lppl/lppl-1-3c.txt",
- "https://opensource.org/licenses/LPPL-1.3c"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Latex2e.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Latex2e.json",
- "referenceNumber": "283",
- "name": "Latex2e License",
- "licenseId": "Latex2e",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Latex2e"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Leptonica.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Leptonica.json",
- "referenceNumber": "159",
- "name": "Leptonica License",
- "licenseId": "Leptonica",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Leptonica"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./LiLiQ-P-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LiLiQ-P-1.1.json",
- "referenceNumber": "379",
- "name": "Licence Libre du Québec – Permissive version 1.1",
- "licenseId": "LiLiQ-P-1.1",
- "seeAlso": [
- "https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/",
- "http://opensource.org/licenses/LiLiQ-P-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LiLiQ-R-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LiLiQ-R-1.1.json",
- "referenceNumber": "286",
- "name": "Licence Libre du Québec – Réciprocité version 1.1",
- "licenseId": "LiLiQ-R-1.1",
- "seeAlso": [
- "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/",
- "http://opensource.org/licenses/LiLiQ-R-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./LiLiQ-Rplus-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/LiLiQ-Rplus-1.1.json",
- "referenceNumber": "139",
- "name": "Licence Libre du Québec – Réciprocité forte version 1.1",
- "licenseId": "LiLiQ-Rplus-1.1",
- "seeAlso": [
- "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/",
- "http://opensource.org/licenses/LiLiQ-Rplus-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Libpng.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Libpng.json",
- "referenceNumber": "101",
- "name": "libpng License",
- "licenseId": "Libpng",
- "seeAlso": [
- "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Linux-OpenIB.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Linux-OpenIB.json",
- "referenceNumber": "5",
- "name": "Linux Kernel Variant of OpenIB.org license",
- "licenseId": "Linux-OpenIB",
- "seeAlso": [
- "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MIT.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/MIT.json",
- "referenceNumber": "201",
- "name": "MIT License",
- "licenseId": "MIT",
- "seeAlso": [
- "https://opensource.org/licenses/MIT"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MIT-0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MIT-0.json",
- "referenceNumber": "6",
- "name": "MIT No Attribution",
- "licenseId": "MIT-0",
- "seeAlso": [
- "https://github.com/aws/mit-0",
- "https://romanrm.net/mit-zero",
- "https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MIT-CMU.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MIT-CMU.json",
- "referenceNumber": "9",
- "name": "CMU License",
- "licenseId": "MIT-CMU",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing:MIT?rd\u003dLicensing/MIT#CMU_Style",
- "https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MIT-advertising.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MIT-advertising.json",
- "referenceNumber": "8",
- "name": "Enlightenment License (e16)",
- "licenseId": "MIT-advertising",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MIT-enna.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MIT-enna.json",
- "referenceNumber": "25",
- "name": "enna License",
- "licenseId": "MIT-enna",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/MIT#enna"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MIT-feh.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MIT-feh.json",
- "referenceNumber": "38",
- "name": "feh License",
- "licenseId": "MIT-feh",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/MIT#feh"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MITNFA.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MITNFA.json",
- "referenceNumber": "294",
- "name": "MIT +no-false-attribs license",
- "licenseId": "MITNFA",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/MITNFA"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MPL-1.0.json",
- "referenceNumber": "49",
- "name": "Mozilla Public License 1.0",
- "licenseId": "MPL-1.0",
- "seeAlso": [
- "http://www.mozilla.org/MPL/MPL-1.0.html",
- "https://opensource.org/licenses/MPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/MPL-1.1.json",
- "referenceNumber": "304",
- "name": "Mozilla Public License 1.1",
- "licenseId": "MPL-1.1",
- "seeAlso": [
- "http://www.mozilla.org/MPL/MPL-1.1.html",
- "https://opensource.org/licenses/MPL-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MPL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/MPL-2.0.json",
- "referenceNumber": "234",
- "name": "Mozilla Public License 2.0",
- "licenseId": "MPL-2.0",
- "seeAlso": [
- "http://www.mozilla.org/MPL/2.0/",
- "https://opensource.org/licenses/MPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MPL-2.0-no-copyleft-exception.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json",
- "referenceNumber": "303",
- "name": "Mozilla Public License 2.0 (no copyleft exception)",
- "licenseId": "MPL-2.0-no-copyleft-exception",
- "seeAlso": [
- "http://www.mozilla.org/MPL/2.0/",
- "https://opensource.org/licenses/MPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MS-PL.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/MS-PL.json",
- "referenceNumber": "336",
- "name": "Microsoft Public License",
- "licenseId": "MS-PL",
- "seeAlso": [
- "http://www.microsoft.com/opensource/licenses.mspx",
- "https://opensource.org/licenses/MS-PL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MS-RL.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/MS-RL.json",
- "referenceNumber": "280",
- "name": "Microsoft Reciprocal License",
- "licenseId": "MS-RL",
- "seeAlso": [
- "http://www.microsoft.com/opensource/licenses.mspx",
- "https://opensource.org/licenses/MS-RL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./MTLL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MTLL.json",
- "referenceNumber": "181",
- "name": "Matrix Template Library License",
- "licenseId": "MTLL",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MakeIndex.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MakeIndex.json",
- "referenceNumber": "187",
- "name": "MakeIndex License",
- "licenseId": "MakeIndex",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/MakeIndex"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./MirOS.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/MirOS.json",
- "referenceNumber": "299",
- "name": "MirOS License",
- "licenseId": "MirOS",
- "seeAlso": [
- "https://opensource.org/licenses/MirOS"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Motosoto.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Motosoto.json",
- "referenceNumber": "317",
- "name": "Motosoto License",
- "licenseId": "Motosoto",
- "seeAlso": [
- "https://opensource.org/licenses/Motosoto"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Multics.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Multics.json",
- "referenceNumber": "63",
- "name": "Multics License",
- "licenseId": "Multics",
- "seeAlso": [
- "https://opensource.org/licenses/Multics"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Mup.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Mup.json",
- "referenceNumber": "353",
- "name": "Mup License",
- "licenseId": "Mup",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Mup"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NASA-1.3.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NASA-1.3.json",
- "referenceNumber": "87",
- "name": "NASA Open Source Agreement 1.3",
- "licenseId": "NASA-1.3",
- "seeAlso": [
- "http://ti.arc.nasa.gov/opensource/nosa/",
- "https://opensource.org/licenses/NASA-1.3"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./NBPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NBPL-1.0.json",
- "referenceNumber": "361",
- "name": "Net Boolean Public License v1",
- "licenseId": "NBPL-1.0",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d37b4b3f6cc4bf34e1d3dec61e69914b9819d8894"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NCSA.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/NCSA.json",
- "referenceNumber": "58",
- "name": "University of Illinois/NCSA Open Source License",
- "licenseId": "NCSA",
- "seeAlso": [
- "http://otm.illinois.edu/uiuc_openSource",
- "https://opensource.org/licenses/NCSA"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./NGPL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NGPL.json",
- "referenceNumber": "71",
- "name": "Nethack General Public License",
- "licenseId": "NGPL",
- "seeAlso": [
- "https://opensource.org/licenses/NGPL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./NLOD-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NLOD-1.0.json",
- "referenceNumber": "209",
- "name": "Norwegian Licence for Open Government Data",
- "licenseId": "NLOD-1.0",
- "seeAlso": [
- "http://data.norge.no/nlod/en/1.0"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NLPL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NLPL.json",
- "referenceNumber": "344",
- "name": "No Limit Public License",
- "licenseId": "NLPL",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/NLPL"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NOSL.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/NOSL.json",
- "referenceNumber": "383",
- "name": "Netizen Open Source License",
- "licenseId": "NOSL",
- "seeAlso": [
- "http://bits.netizen.com.au/licenses/NOSL/nosl.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/NPL-1.0.json",
- "referenceNumber": "328",
- "name": "Netscape Public License v1.0",
- "licenseId": "NPL-1.0",
- "seeAlso": [
- "http://www.mozilla.org/MPL/NPL/1.0/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/NPL-1.1.json",
- "referenceNumber": "185",
- "name": "Netscape Public License v1.1",
- "licenseId": "NPL-1.1",
- "seeAlso": [
- "http://www.mozilla.org/MPL/NPL/1.1/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NPOSL-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NPOSL-3.0.json",
- "referenceNumber": "222",
- "name": "Non-Profit Open Software License 3.0",
- "licenseId": "NPOSL-3.0",
- "seeAlso": [
- "https://opensource.org/licenses/NOSL3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./NRL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NRL.json",
- "referenceNumber": "53",
- "name": "NRL License",
- "licenseId": "NRL",
- "seeAlso": [
- "http://web.mit.edu/network/isakmp/nrllicense.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NTP.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NTP.json",
- "referenceNumber": "261",
- "name": "NTP License",
- "licenseId": "NTP",
- "seeAlso": [
- "https://opensource.org/licenses/NTP"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Naumen.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Naumen.json",
- "referenceNumber": "278",
- "name": "Naumen Public License",
- "licenseId": "Naumen",
- "seeAlso": [
- "https://opensource.org/licenses/Naumen"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Net-SNMP.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Net-SNMP.json",
- "referenceNumber": "284",
- "name": "Net-SNMP License",
- "licenseId": "Net-SNMP",
- "seeAlso": [
- "http://net-snmp.sourceforge.net/about/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./NetCDF.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/NetCDF.json",
- "referenceNumber": "46",
- "name": "NetCDF license",
- "licenseId": "NetCDF",
- "seeAlso": [
- "http://www.unidata.ucar.edu/software/netcdf/copyright.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Newsletr.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Newsletr.json",
- "referenceNumber": "279",
- "name": "Newsletr License",
- "licenseId": "Newsletr",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Newsletr"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Nokia.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Nokia.json",
- "referenceNumber": "327",
- "name": "Nokia Open Source License",
- "licenseId": "Nokia",
- "seeAlso": [
- "https://opensource.org/licenses/nokia"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Noweb.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Noweb.json",
- "referenceNumber": "364",
- "name": "Noweb License",
- "licenseId": "Noweb",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Noweb"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Nunit.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Nunit.json",
- "referenceNumber": "288",
- "name": "Nunit License",
- "licenseId": "Nunit",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Nunit"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OCCT-PL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OCCT-PL.json",
- "referenceNumber": "282",
- "name": "Open CASCADE Technology Public License",
- "licenseId": "OCCT-PL",
- "seeAlso": [
- "http://www.opencascade.com/content/occt-public-license"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OCLC-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OCLC-2.0.json",
- "referenceNumber": "111",
- "name": "OCLC Research Public License 2.0",
- "licenseId": "OCLC-2.0",
- "seeAlso": [
- "http://www.oclc.org/research/activities/software/license/v2final.htm",
- "https://opensource.org/licenses/OCLC-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./ODC-By-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ODC-By-1.0.json",
- "referenceNumber": "144",
- "name": "Open Data Commons Attribution License v1.0",
- "licenseId": "ODC-By-1.0",
- "seeAlso": [
- "https://opendatacommons.org/licenses/by/1.0/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./ODbL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/ODbL-1.0.json",
- "referenceNumber": "246",
- "name": "ODC Open Database License v1.0",
- "licenseId": "ODbL-1.0",
- "seeAlso": [
- "http://www.opendatacommons.org/licenses/odbl/1.0/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OFL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OFL-1.0.json",
- "referenceNumber": "153",
- "name": "SIL Open Font License 1.0",
- "licenseId": "OFL-1.0",
- "seeAlso": [
- "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OFL-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OFL-1.1.json",
- "referenceNumber": "315",
- "name": "SIL Open Font License 1.1",
- "licenseId": "OFL-1.1",
- "seeAlso": [
- "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web",
- "https://opensource.org/licenses/OFL-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./OGL-UK-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OGL-UK-1.0.json",
- "referenceNumber": "116",
- "name": "Open Government Licence v1.0",
- "licenseId": "OGL-UK-1.0",
- "seeAlso": [
- "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OGL-UK-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OGL-UK-2.0.json",
- "referenceNumber": "289",
- "name": "Open Government Licence v2.0",
- "licenseId": "OGL-UK-2.0",
- "seeAlso": [
- "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OGL-UK-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OGL-UK-3.0.json",
- "referenceNumber": "226",
- "name": "Open Government Licence v3.0",
- "licenseId": "OGL-UK-3.0",
- "seeAlso": [
- "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OGTSL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OGTSL.json",
- "referenceNumber": "125",
- "name": "Open Group Test Suite License",
- "licenseId": "OGTSL",
- "seeAlso": [
- "http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt",
- "https://opensource.org/licenses/OGTSL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./OLDAP-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-1.1.json",
- "referenceNumber": "97",
- "name": "Open LDAP Public License v1.1",
- "licenseId": "OLDAP-1.1",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d806557a5ad59804ef3a44d5abfbe91d706b0791f"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-1.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-1.2.json",
- "referenceNumber": "190",
- "name": "Open LDAP Public License v1.2",
- "licenseId": "OLDAP-1.2",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d42b0383c50c299977b5893ee695cf4e486fb0dc7"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-1.3.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-1.3.json",
- "referenceNumber": "106",
- "name": "Open LDAP Public License v1.3",
- "licenseId": "OLDAP-1.3",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003de5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-1.4.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-1.4.json",
- "referenceNumber": "30",
- "name": "Open LDAP Public License v1.4",
- "licenseId": "OLDAP-1.4",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dc9f95c2f3f2ffb5e0ae55fe7388af75547660941"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.json",
- "referenceNumber": "266",
- "name": "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)",
- "licenseId": "OLDAP-2.0",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcbf50f4e1185a21abd4c0a54d3f4341fe28f36ea"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.0.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.1.json",
- "referenceNumber": "350",
- "name": "Open LDAP Public License v2.0.1",
- "licenseId": "OLDAP-2.0.1",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db6d68acd14e51ca3aab4428bf26522aa74873f0e"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.1.json",
- "referenceNumber": "154",
- "name": "Open LDAP Public License v2.1",
- "licenseId": "OLDAP-2.1",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db0d176738e96a0d3b9f85cb51e140a86f21be715"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.json",
- "referenceNumber": "362",
- "name": "Open LDAP Public License v2.2",
- "licenseId": "OLDAP-2.2",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d470b0c18ec67621c85881b2733057fecf4a1acc3"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.2.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.1.json",
- "referenceNumber": "339",
- "name": "Open LDAP Public License v2.2.1",
- "licenseId": "OLDAP-2.2.1",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d4bc786f34b50aa301be6f5600f58a980070f481e"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.2.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.2.json",
- "referenceNumber": "199",
- "name": "Open LDAP Public License 2.2.2",
- "licenseId": "OLDAP-2.2.2",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003ddf2cc1e21eb7c160695f5b7cffd6296c151ba188"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.3.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.3.json",
- "referenceNumber": "164",
- "name": "Open LDAP Public License v2.3",
- "licenseId": "OLDAP-2.3",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dd32cf54a32d581ab475d23c810b0a7fbaf8d63c3"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.4.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.4.json",
- "referenceNumber": "66",
- "name": "Open LDAP Public License v2.4",
- "licenseId": "OLDAP-2.4",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcd1284c4a91a8a380d904eee68d1583f989ed386"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.5.json",
- "referenceNumber": "183",
- "name": "Open LDAP Public License v2.5",
- "licenseId": "OLDAP-2.5",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d6852b9d90022e8593c98205413380536b1b5a7cf"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.6.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.6.json",
- "referenceNumber": "61",
- "name": "Open LDAP Public License v2.6",
- "licenseId": "OLDAP-2.6",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d1cae062821881f41b73012ba816434897abf4205"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.7.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.7.json",
- "referenceNumber": "123",
- "name": "Open LDAP Public License v2.7",
- "licenseId": "OLDAP-2.7",
- "seeAlso": [
- "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d47c2415c1df81556eeb39be6cad458ef87c534a2"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OLDAP-2.8.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OLDAP-2.8.json",
- "referenceNumber": "37",
- "name": "Open LDAP Public License v2.8",
- "licenseId": "OLDAP-2.8",
- "seeAlso": [
- "http://www.openldap.org/software/release/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OML.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OML.json",
- "referenceNumber": "65",
- "name": "Open Market License",
- "licenseId": "OML",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Open_Market_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OPL-1.0.json",
- "referenceNumber": "343",
- "name": "Open Public License v1.0",
- "licenseId": "OPL-1.0",
- "seeAlso": [
- "http://old.koalateam.com/jackaroo/OPL_1_0.TXT",
- "https://fedoraproject.org/wiki/Licensing/Open_Public_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OSET-PL-2.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/OSET-PL-2.1.json",
- "referenceNumber": "291",
- "name": "OSET Public License version 2.1",
- "licenseId": "OSET-PL-2.1",
- "seeAlso": [
- "http://www.osetfoundation.org/public-license",
- "https://opensource.org/licenses/OPL-2.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./OSL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OSL-1.0.json",
- "referenceNumber": "85",
- "name": "Open Software License 1.0",
- "licenseId": "OSL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/OSL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./OSL-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OSL-1.1.json",
- "referenceNumber": "334",
- "name": "Open Software License 1.1",
- "licenseId": "OSL-1.1",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/OSL1.1"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./OSL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OSL-2.0.json",
- "referenceNumber": "20",
- "name": "Open Software License 2.0",
- "licenseId": "OSL-2.0",
- "seeAlso": [
- "http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./OSL-2.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OSL-2.1.json",
- "referenceNumber": "24",
- "name": "Open Software License 2.1",
- "licenseId": "OSL-2.1",
- "seeAlso": [
- "http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm",
- "https://opensource.org/licenses/OSL-2.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./OSL-3.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OSL-3.0.json",
- "referenceNumber": "100",
- "name": "Open Software License 3.0",
- "licenseId": "OSL-3.0",
- "seeAlso": [
- "https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm",
- "https://opensource.org/licenses/OSL-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./OpenSSL.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/OpenSSL.json",
- "referenceNumber": "249",
- "name": "OpenSSL License",
- "licenseId": "OpenSSL",
- "seeAlso": [
- "http://www.openssl.org/source/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./PDDL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/PDDL-1.0.json",
- "referenceNumber": "14",
- "name": "ODC Public Domain Dedication \u0026 License 1.0",
- "licenseId": "PDDL-1.0",
- "seeAlso": [
- "http://opendatacommons.org/licenses/pddl/1.0/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./PHP-3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/PHP-3.0.json",
- "referenceNumber": "385",
- "name": "PHP License v3.0",
- "licenseId": "PHP-3.0",
- "seeAlso": [
- "http://www.php.net/license/3_0.txt",
- "https://opensource.org/licenses/PHP-3.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./PHP-3.01.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/PHP-3.01.json",
- "referenceNumber": "316",
- "name": "PHP License v3.01",
- "licenseId": "PHP-3.01",
- "seeAlso": [
- "http://www.php.net/license/3_01.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Parity-6.0.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Parity-6.0.0.json",
- "referenceNumber": "91",
- "name": "The Parity Public License 6.0.0",
- "licenseId": "Parity-6.0.0",
- "seeAlso": [
- "https://paritylicense.com/versions/6.0.0.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Plexus.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Plexus.json",
- "referenceNumber": "225",
- "name": "Plexus Classworlds License",
- "licenseId": "Plexus",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./PostgreSQL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/PostgreSQL.json",
- "referenceNumber": "247",
- "name": "PostgreSQL License",
- "licenseId": "PostgreSQL",
- "seeAlso": [
- "http://www.postgresql.org/about/licence",
- "https://opensource.org/licenses/PostgreSQL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Python-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Python-2.0.json",
- "referenceNumber": "35",
- "name": "Python License 2.0",
- "licenseId": "Python-2.0",
- "seeAlso": [
- "https://opensource.org/licenses/Python-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./QPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/QPL-1.0.json",
- "referenceNumber": "27",
- "name": "Q Public License 1.0",
- "licenseId": "QPL-1.0",
- "seeAlso": [
- "http://doc.qt.nokia.com/3.3/license.html",
- "https://opensource.org/licenses/QPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Qhull.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Qhull.json",
- "referenceNumber": "67",
- "name": "Qhull License",
- "licenseId": "Qhull",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Qhull"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./RHeCos-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/RHeCos-1.1.json",
- "referenceNumber": "149",
- "name": "Red Hat eCos Public License v1.1",
- "licenseId": "RHeCos-1.1",
- "seeAlso": [
- "http://ecos.sourceware.org/old-license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./RPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/RPL-1.1.json",
- "referenceNumber": "269",
- "name": "Reciprocal Public License 1.1",
- "licenseId": "RPL-1.1",
- "seeAlso": [
- "https://opensource.org/licenses/RPL-1.1"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./RPL-1.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/RPL-1.5.json",
- "referenceNumber": "227",
- "name": "Reciprocal Public License 1.5",
- "licenseId": "RPL-1.5",
- "seeAlso": [
- "https://opensource.org/licenses/RPL-1.5"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./RPSL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/RPSL-1.0.json",
- "referenceNumber": "273",
- "name": "RealNetworks Public Source License v1.0",
- "licenseId": "RPSL-1.0",
- "seeAlso": [
- "https://helixcommunity.org/content/rpsl",
- "https://opensource.org/licenses/RPSL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./RSA-MD.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/RSA-MD.json",
- "referenceNumber": "82",
- "name": "RSA Message-Digest License ",
- "licenseId": "RSA-MD",
- "seeAlso": [
- "http://www.faqs.org/rfcs/rfc1321.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./RSCPL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/RSCPL.json",
- "referenceNumber": "211",
- "name": "Ricoh Source Code Public License",
- "licenseId": "RSCPL",
- "seeAlso": [
- "http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml",
- "https://opensource.org/licenses/RSCPL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Rdisc.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Rdisc.json",
- "referenceNumber": "295",
- "name": "Rdisc License",
- "licenseId": "Rdisc",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Rdisc_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Ruby.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Ruby.json",
- "referenceNumber": "263",
- "name": "Ruby License",
- "licenseId": "Ruby",
- "seeAlso": [
- "http://www.ruby-lang.org/en/LICENSE.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SAX-PD.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SAX-PD.json",
- "referenceNumber": "140",
- "name": "Sax Public Domain Notice",
- "licenseId": "SAX-PD",
- "seeAlso": [
- "http://www.saxproject.org/copying.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SCEA.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SCEA.json",
- "referenceNumber": "16",
- "name": "SCEA Shared Source License",
- "licenseId": "SCEA",
- "seeAlso": [
- "http://research.scea.com/scea_shared_source_license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SGI-B-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SGI-B-1.0.json",
- "referenceNumber": "90",
- "name": "SGI Free Software License B v1.0",
- "licenseId": "SGI-B-1.0",
- "seeAlso": [
- "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SGI-B-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SGI-B-1.1.json",
- "referenceNumber": "241",
- "name": "SGI Free Software License B v1.1",
- "licenseId": "SGI-B-1.1",
- "seeAlso": [
- "http://oss.sgi.com/projects/FreeB/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SGI-B-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/SGI-B-2.0.json",
- "referenceNumber": "272",
- "name": "SGI Free Software License B v2.0",
- "licenseId": "SGI-B-2.0",
- "seeAlso": [
- "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SHL-0.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SHL-0.5.json",
- "referenceNumber": "72",
- "name": "Solderpad Hardware License v0.5",
- "licenseId": "SHL-0.5",
- "seeAlso": [
- "https://solderpad.org/licenses/SHL-0.5/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SHL-0.51.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SHL-0.51.json",
- "referenceNumber": "314",
- "name": "Solderpad Hardware License, Version 0.51",
- "licenseId": "SHL-0.51",
- "seeAlso": [
- "https://solderpad.org/licenses/SHL-0.51/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SISSL.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/SISSL.json",
- "referenceNumber": "74",
- "name": "Sun Industry Standards Source License v1.1",
- "licenseId": "SISSL",
- "seeAlso": [
- "http://www.openoffice.org/licenses/sissl_license.html",
- "https://opensource.org/licenses/SISSL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./SISSL-1.2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SISSL-1.2.json",
- "referenceNumber": "7",
- "name": "Sun Industry Standards Source License v1.2",
- "licenseId": "SISSL-1.2",
- "seeAlso": [
- "http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SMLNJ.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/SMLNJ.json",
- "referenceNumber": "296",
- "name": "Standard ML of New Jersey License",
- "licenseId": "SMLNJ",
- "seeAlso": [
- "https://www.smlnj.org/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SMPPL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SMPPL.json",
- "referenceNumber": "127",
- "name": "Secure Messaging Protocol Public License",
- "licenseId": "SMPPL",
- "seeAlso": [
- "https://github.com/dcblake/SMP/blob/master/Documentation/License.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SNIA.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SNIA.json",
- "referenceNumber": "230",
- "name": "SNIA Public License 1.1",
- "licenseId": "SNIA",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/SNIA_Public_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/SPL-1.0.json",
- "referenceNumber": "54",
- "name": "Sun Public License v1.0",
- "licenseId": "SPL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/SPL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./SSPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SSPL-1.0.json",
- "referenceNumber": "356",
- "name": "Server Side Public License, v 1",
- "licenseId": "SSPL-1.0",
- "seeAlso": [
- "https://www.mongodb.com/licensing/server-side-public-license"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SWL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SWL.json",
- "referenceNumber": "208",
- "name": "Scheme Widget Library (SWL) Software License Agreement",
- "licenseId": "SWL",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/SWL"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Saxpath.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Saxpath.json",
- "referenceNumber": "18",
- "name": "Saxpath License",
- "licenseId": "Saxpath",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Saxpath_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Sendmail.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Sendmail.json",
- "referenceNumber": "151",
- "name": "Sendmail License",
- "licenseId": "Sendmail",
- "seeAlso": [
- "http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf",
- "https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Sendmail-8.23.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Sendmail-8.23.json",
- "referenceNumber": "41",
- "name": "Sendmail License 8.23",
- "licenseId": "Sendmail-8.23",
- "seeAlso": [
- "https://www.proofpoint.com/sites/default/files/sendmail-license.pdf",
- "https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SimPL-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SimPL-2.0.json",
- "referenceNumber": "184",
- "name": "Simple Public License 2.0",
- "licenseId": "SimPL-2.0",
- "seeAlso": [
- "https://opensource.org/licenses/SimPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Sleepycat.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Sleepycat.json",
- "referenceNumber": "290",
- "name": "Sleepycat License",
- "licenseId": "Sleepycat",
- "seeAlso": [
- "https://opensource.org/licenses/Sleepycat"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Spencer-86.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Spencer-86.json",
- "referenceNumber": "313",
- "name": "Spencer License 86",
- "licenseId": "Spencer-86",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Spencer-94.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Spencer-94.json",
- "referenceNumber": "29",
- "name": "Spencer License 94",
- "licenseId": "Spencer-94",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Spencer-99.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Spencer-99.json",
- "referenceNumber": "386",
- "name": "Spencer License 99",
- "licenseId": "Spencer-99",
- "seeAlso": [
- "http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./StandardML-NJ.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/StandardML-NJ.json",
- "referenceNumber": "219",
- "name": "Standard ML of New Jersey License",
- "licenseId": "StandardML-NJ",
- "seeAlso": [
- "http://www.smlnj.org//license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./SugarCRM-1.1.3.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/SugarCRM-1.1.3.json",
- "referenceNumber": "292",
- "name": "SugarCRM Public License v1.1.3",
- "licenseId": "SugarCRM-1.1.3",
- "seeAlso": [
- "http://www.sugarcrm.com/crm/SPL"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TAPR-OHL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TAPR-OHL-1.0.json",
- "referenceNumber": "267",
- "name": "TAPR Open Hardware License v1.0",
- "licenseId": "TAPR-OHL-1.0",
- "seeAlso": [
- "https://www.tapr.org/OHL"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TCL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TCL.json",
- "referenceNumber": "265",
- "name": "TCL/TK License",
- "licenseId": "TCL",
- "seeAlso": [
- "http://www.tcl.tk/software/tcltk/license.html",
- "https://fedoraproject.org/wiki/Licensing/TCL"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TCP-wrappers.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TCP-wrappers.json",
- "referenceNumber": "274",
- "name": "TCP Wrappers License",
- "licenseId": "TCP-wrappers",
- "seeAlso": [
- "http://rc.quest.com/topics/openssh/license.php#tcpwrappers"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TMate.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TMate.json",
- "referenceNumber": "253",
- "name": "TMate Open Source License",
- "licenseId": "TMate",
- "seeAlso": [
- "http://svnkit.com/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TORQUE-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TORQUE-1.1.json",
- "referenceNumber": "171",
- "name": "TORQUE v2.5+ Software License v1.1",
- "licenseId": "TORQUE-1.1",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/TORQUEv1.1"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TOSL.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TOSL.json",
- "referenceNumber": "360",
- "name": "Trusster Open Source License",
- "licenseId": "TOSL",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/TOSL"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TU-Berlin-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TU-Berlin-1.0.json",
- "referenceNumber": "373",
- "name": "Technische Universitaet Berlin License 1.0",
- "licenseId": "TU-Berlin-1.0",
- "seeAlso": [
- "https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./TU-Berlin-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/TU-Berlin-2.0.json",
- "referenceNumber": "391",
- "name": "Technische Universitaet Berlin License 2.0",
- "licenseId": "TU-Berlin-2.0",
- "seeAlso": [
- "https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./UPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/UPL-1.0.json",
- "referenceNumber": "205",
- "name": "Universal Permissive License v1.0",
- "licenseId": "UPL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/UPL"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Unicode-DFS-2015.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2015.json",
- "referenceNumber": "11",
- "name": "Unicode License Agreement - Data Files and Software (2015)",
- "licenseId": "Unicode-DFS-2015",
- "seeAlso": [
- "https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Unicode-DFS-2016.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2016.json",
- "referenceNumber": "382",
- "name": "Unicode License Agreement - Data Files and Software (2016)",
- "licenseId": "Unicode-DFS-2016",
- "seeAlso": [
- "http://www.unicode.org/copyright.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Unicode-TOU.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Unicode-TOU.json",
- "referenceNumber": "70",
- "name": "Unicode Terms of Use",
- "licenseId": "Unicode-TOU",
- "seeAlso": [
- "http://www.unicode.org/copyright.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Unlicense.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Unlicense.json",
- "referenceNumber": "293",
- "name": "The Unlicense",
- "licenseId": "Unlicense",
- "seeAlso": [
- "http://unlicense.org/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./VOSTROM.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/VOSTROM.json",
- "referenceNumber": "228",
- "name": "VOSTROM Public License for Open Source",
- "licenseId": "VOSTROM",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/VOSTROM"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./VSL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/VSL-1.0.json",
- "referenceNumber": "180",
- "name": "Vovida Software License v1.0",
- "licenseId": "VSL-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/VSL-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Vim.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Vim.json",
- "referenceNumber": "133",
- "name": "Vim License",
- "licenseId": "Vim",
- "seeAlso": [
- "http://vimdoc.sourceforge.net/htmldoc/uganda.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./W3C.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/W3C.json",
- "referenceNumber": "351",
- "name": "W3C Software Notice and License (2002-12-31)",
- "licenseId": "W3C",
- "seeAlso": [
- "http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html",
- "https://opensource.org/licenses/W3C"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./W3C-19980720.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/W3C-19980720.json",
- "referenceNumber": "323",
- "name": "W3C Software Notice and License (1998-07-20)",
- "licenseId": "W3C-19980720",
- "seeAlso": [
- "http://www.w3.org/Consortium/Legal/copyright-software-19980720.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./W3C-20150513.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/W3C-20150513.json",
- "referenceNumber": "51",
- "name": "W3C Software Notice and Document License (2015-05-13)",
- "licenseId": "W3C-20150513",
- "seeAlso": [
- "https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./WTFPL.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/WTFPL.json",
- "referenceNumber": "368",
- "name": "Do What The F*ck You Want To Public License",
- "licenseId": "WTFPL",
- "seeAlso": [
- "http://sam.zoy.org/wtfpl/COPYING"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Watcom-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Watcom-1.0.json",
- "referenceNumber": "177",
- "name": "Sybase Open Watcom Public License 1.0",
- "licenseId": "Watcom-1.0",
- "seeAlso": [
- "https://opensource.org/licenses/Watcom-1.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./Wsuipa.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Wsuipa.json",
- "referenceNumber": "135",
- "name": "Wsuipa License",
- "licenseId": "Wsuipa",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Wsuipa"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./X11.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/X11.json",
- "referenceNumber": "188",
- "name": "X11 License",
- "licenseId": "X11",
- "seeAlso": [
- "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./XFree86-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/XFree86-1.1.json",
- "referenceNumber": "243",
- "name": "XFree86 License 1.1",
- "licenseId": "XFree86-1.1",
- "seeAlso": [
- "http://www.xfree86.org/current/LICENSE4.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./XSkat.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/XSkat.json",
- "referenceNumber": "96",
- "name": "XSkat License",
- "licenseId": "XSkat",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/XSkat_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Xerox.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Xerox.json",
- "referenceNumber": "163",
- "name": "Xerox License",
- "licenseId": "Xerox",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Xerox"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Xnet.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Xnet.json",
- "referenceNumber": "388",
- "name": "X.Net License",
- "licenseId": "Xnet",
- "seeAlso": [
- "https://opensource.org/licenses/Xnet"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./YPL-1.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/YPL-1.0.json",
- "referenceNumber": "174",
- "name": "Yahoo! Public License v1.0",
- "licenseId": "YPL-1.0",
- "seeAlso": [
- "http://www.zimbra.com/license/yahoo_public_license_1.0.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./YPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/YPL-1.1.json",
- "referenceNumber": "57",
- "name": "Yahoo! Public License v1.1",
- "licenseId": "YPL-1.1",
- "seeAlso": [
- "http://www.zimbra.com/license/yahoo_public_license_1.1.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./ZPL-1.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/ZPL-1.1.json",
- "referenceNumber": "359",
- "name": "Zope Public License 1.1",
- "licenseId": "ZPL-1.1",
- "seeAlso": [
- "http://old.zope.org/Resources/License/ZPL-1.1"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./ZPL-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/ZPL-2.0.json",
- "referenceNumber": "78",
- "name": "Zope Public License 2.0",
- "licenseId": "ZPL-2.0",
- "seeAlso": [
- "http://old.zope.org/Resources/License/ZPL-2.0",
- "https://opensource.org/licenses/ZPL-2.0"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./ZPL-2.1.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/ZPL-2.1.json",
- "referenceNumber": "345",
- "name": "Zope Public License 2.1",
- "licenseId": "ZPL-2.1",
- "seeAlso": [
- "http://old.zope.org/Resources/ZPL/"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Zed.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Zed.json",
- "referenceNumber": "248",
- "name": "Zed License",
- "licenseId": "Zed",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Zed"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Zend-2.0.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Zend-2.0.json",
- "referenceNumber": "198",
- "name": "Zend License v2.0",
- "licenseId": "Zend-2.0",
- "seeAlso": [
- "https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Zimbra-1.3.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Zimbra-1.3.json",
- "referenceNumber": "40",
- "name": "Zimbra Public License v1.3",
- "licenseId": "Zimbra-1.3",
- "seeAlso": [
- "http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Zimbra-1.4.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/Zimbra-1.4.json",
- "referenceNumber": "238",
- "name": "Zimbra Public License v1.4",
- "licenseId": "Zimbra-1.4",
- "seeAlso": [
- "http://www.zimbra.com/legal/zimbra-public-license-1-4"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./Zlib.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/Zlib.json",
- "referenceNumber": "320",
- "name": "zlib License",
- "licenseId": "Zlib",
- "seeAlso": [
- "http://www.zlib.net/zlib_license.html",
- "https://opensource.org/licenses/Zlib"
- ],
- "isOsiApproved": true
- },
- {
- "reference": "./blessing.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/blessing.json",
- "referenceNumber": "331",
- "name": "SQLite Blessing",
- "licenseId": "blessing",
- "seeAlso": [
- "https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln\u003d4-9",
- "https://sqlite.org/src/artifact/df5091916dbb40e6"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./bzip2-1.0.5.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.5.json",
- "referenceNumber": "200",
- "name": "bzip2 and libbzip2 License v1.0.5",
- "licenseId": "bzip2-1.0.5",
- "seeAlso": [
- "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./bzip2-1.0.6.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.6.json",
- "referenceNumber": "302",
- "name": "bzip2 and libbzip2 License v1.0.6",
- "licenseId": "bzip2-1.0.6",
- "seeAlso": [
- "https://github.com/asimonov-im/bzip2/blob/master/LICENSE"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./copyleft-next-0.3.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.0.json",
- "referenceNumber": "176",
- "name": "copyleft-next 0.3.0",
- "licenseId": "copyleft-next-0.3.0",
- "seeAlso": [
- "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./copyleft-next-0.3.1.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.1.json",
- "referenceNumber": "347",
- "name": "copyleft-next 0.3.1",
- "licenseId": "copyleft-next-0.3.1",
- "seeAlso": [
- "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./curl.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/curl.json",
- "referenceNumber": "260",
- "name": "curl License",
- "licenseId": "curl",
- "seeAlso": [
- "https://github.com/bagder/curl/blob/master/COPYING"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./diffmark.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/diffmark.json",
- "referenceNumber": "367",
- "name": "diffmark license",
- "licenseId": "diffmark",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/diffmark"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./dvipdfm.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/dvipdfm.json",
- "referenceNumber": "143",
- "name": "dvipdfm License",
- "licenseId": "dvipdfm",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/dvipdfm"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./eCos-2.0.html",
- "isDeprecatedLicenseId": true,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/eCos-2.0.json",
- "referenceNumber": "329",
- "name": "eCos license version 2.0",
- "licenseId": "eCos-2.0",
- "seeAlso": [
- "https://www.gnu.org/licenses/ecos-license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./eGenix.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/eGenix.json",
- "referenceNumber": "204",
- "name": "eGenix.com Public License 1.1.0",
- "licenseId": "eGenix",
- "seeAlso": [
- "http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf",
- "https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./gSOAP-1.3b.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/gSOAP-1.3b.json",
- "referenceNumber": "346",
- "name": "gSOAP Public License v1.3b",
- "licenseId": "gSOAP-1.3b",
- "seeAlso": [
- "http://www.cs.fsu.edu/~engelen/license.html"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./gnuplot.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/gnuplot.json",
- "referenceNumber": "10",
- "name": "gnuplot License",
- "licenseId": "gnuplot",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Gnuplot"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./iMatix.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/iMatix.json",
- "referenceNumber": "342",
- "name": "iMatix Standard Function Library Agreement",
- "licenseId": "iMatix",
- "seeAlso": [
- "http://legacy.imatix.com/html/sfl/sfl4.htm#license"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./libpng-2.0.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/libpng-2.0.json",
- "referenceNumber": "76",
- "name": "PNG Reference Library version 2",
- "licenseId": "libpng-2.0",
- "seeAlso": [
- "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./libtiff.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/libtiff.json",
- "referenceNumber": "220",
- "name": "libtiff License",
- "licenseId": "libtiff",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/libtiff"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./mpich2.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/mpich2.json",
- "referenceNumber": "318",
- "name": "mpich2 License",
- "licenseId": "mpich2",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/MIT"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./psfrag.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/psfrag.json",
- "referenceNumber": "245",
- "name": "psfrag License",
- "licenseId": "psfrag",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/psfrag"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./psutils.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/psutils.json",
- "referenceNumber": "126",
- "name": "psutils License",
- "licenseId": "psutils",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/psutils"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./wxWindows.html",
- "isDeprecatedLicenseId": true,
- "detailsUrl": "http://spdx.org/licenses/wxWindows.json",
- "referenceNumber": "86",
- "name": "wxWindows Library License",
- "licenseId": "wxWindows",
- "seeAlso": [
- "https://opensource.org/licenses/WXwindows"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./xinetd.html",
- "isDeprecatedLicenseId": false,
- "isFsfLibre": true,
- "detailsUrl": "http://spdx.org/licenses/xinetd.json",
- "referenceNumber": "146",
- "name": "xinetd License",
- "licenseId": "xinetd",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/Xinetd_License"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./xpp.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/xpp.json",
- "referenceNumber": "275",
- "name": "XPP License",
- "licenseId": "xpp",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/xpp"
- ],
- "isOsiApproved": false
- },
- {
- "reference": "./zlib-acknowledgement.html",
- "isDeprecatedLicenseId": false,
- "detailsUrl": "http://spdx.org/licenses/zlib-acknowledgement.json",
- "referenceNumber": "321",
- "name": "zlib/libpng License with Acknowledgement",
- "licenseId": "zlib-acknowledgement",
- "seeAlso": [
- "https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement"
- ],
- "isOsiApproved": false
- }
- ],
- "releaseDate": "2019-07-10"
-}
\ No newline at end of file
diff --git a/spdx/package.py b/spdx/package.py
deleted file mode 100644
index 75c50dbe6..000000000
--- a/spdx/package.py
+++ /dev/null
@@ -1,364 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-import warnings
-from datetime import datetime
-from enum import Enum
-from functools import reduce
-from typing import Optional
-
-from spdx import creationinfo
-from spdx import license
-from spdx import utils
-from spdx.checksum import Checksum, ChecksumAlgorithm
-from spdx.parsers.builderexceptions import SPDXValueError
-from spdx.parsers.loggers import ErrorMessages
-
-
-class PackagePurpose(Enum):
- APPLICATION = 1
- FRAMEWORK = 2
- LIBRARY = 3
- CONTAINER = 4
- OPERATING_SYSTEM = 5
- DEVICE = 6
- FIRMWARE = 7
- SOURCE = 8
- ARCHIVE = 9
- FILE = 10
- INSTALL = 11
- OTHER = 12
-
-
-class Package(object):
- """
- Represent an analyzed Package.
- Fields:
- - name: Mandatory, string.
- - spdx_id: Uniquely identify any element in an SPDX document which may be
- referenced by other elements. Mandatory, one. Type: str.
- - version: Optional, string.
- - file_name: Optional, string.
- - supplier: Optional, Organization or Person or NO_ASSERTION.
- - originator: Optional, Organization or Person.
- - download_location: Mandatory, URL as string.
- - files_analyzed: Indicates whether the file content of this package has
- been available for or subjected to analysis when creating the SPDX
- document. If "false" indicates packages that represent metadata or URI
- references to a project, product, artifact, distribution or a component.
- If set to "false", the package must not contain any files.
- Optional, boolean.
- - homepage: Optional, URL as string or NONE or NO_ASSERTION.
- - verif_code: string. According to the specification, this is Mandatory
- whenever files_analyzed is True or None (omitted) and Must be None (omitted)
- if files_analyzed is False. However, as a convenience within this library,
- we allow this to be Optional even when files_analyzed is True/None.
- - checksums: Optional, Dict with checksum.ChecksumAlgorithm as key and checksum.Checksum as value.
- - source_info: Optional string.
- - conc_lics: Mandatory license.License or utils.SPDXNone or
- utils.NoAssert.
- - license_declared: Mandatory license.License or utils.SPDXNone or
- utils.NoAssert.
- - license_comment: optional string.
- - licenses_from_files: list of license.License or utils.SPDXNone or
- utils.NoAssert.
- - cr_text: Copyright text, string , utils.NoAssert or utils.SPDXNone. Mandatory.
- - summary: Optional str.
- - description: Optional str.
- - comment: Comments about the package being described, optional one.
- Type: str
- - verif_exc_files: list of file names excluded from verification code or None.
- - ext_pkg_refs: External references referenced within the given package.
- Optional, one or many. Type: ExternalPackageRef
- - attribution_text: optional string.
- - primary_package_purpose: Optional one. Type: PackagePurpose
- """
-
- def __init__(
- self,
- name=None,
- spdx_id=None,
- download_location=None,
- version=None,
- file_name=None,
- supplier=None,
- originator=None,
- ):
- self.name = name
- self.spdx_id = spdx_id
- self.version = version
- self.file_name = file_name
- self.supplier = supplier
- self.originator = originator
- self.download_location = download_location
- self.files_analyzed = None
- self.homepage = None
- self.verif_code = None
- self.checksums = {}
- self.source_info = None
- self.conc_lics = None
- self.license_declared = None
- self.license_comment = None
- self.licenses_from_files = []
- self.cr_text = None
- self.summary = None
- self.description = None
- self.comment = None
- self.attribution_text = None
- self.verif_exc_files = []
- self.pkg_ext_refs = []
- self.primary_package_purpose: Optional[PackagePurpose] = None
- self.release_date: Optional[datetime] = None
- self.built_date: Optional[datetime] = None
- self.valid_until_date: Optional[datetime] = None
-
- @property
- def checksum(self):
- """
- Backwards compatibility, return SHA1 checksum.
- """
- warnings.warn("This property is deprecated. Use get_checksum instead.")
- return self.get_checksum(ChecksumAlgorithm.SHA1)
-
- @checksum.setter
- def checksum(self, value):
- """
- Backwards compatibility, set SHA1 checksum.
- """
- warnings.warn("This property is deprecated. Use set_checksum instead.")
- if isinstance(value, str):
- self.set_checksum(Checksum("SHA1", value))
- elif isinstance(value, Checksum):
- self.set_checksum(value)
-
- @property
- def are_files_analyzed(self):
- return self.files_analyzed is not False
- # as default None Value is False, previous line is simplification of
- # return self.files_analyzed or self.files_analyzed is None
-
- def add_lics_from_file(self, lics):
- self.licenses_from_files.append(lics)
-
- def add_exc_file(self, filename):
- self.verif_exc_files.append(filename)
-
- def add_pkg_ext_refs(self, pkg_ext_ref):
- self.pkg_ext_refs.append(pkg_ext_ref)
-
- def validate(self, messages):
- """
- Validate the package fields.
- Append user friendly error messages to the `messages` list.
- """
- messages.push_context(self.name)
- self.validate_files_analyzed(messages)
- self.validate_checksums(messages)
- self.validate_optional_str_fields(messages)
- self.validate_mandatory_str_fields(messages)
- self.validate_pkg_ext_refs(messages)
- self.validate_optional_fields(messages)
- messages.pop_context()
-
- return messages
-
- def validate_files_analyzed(self, messages):
- if self.files_analyzed not in [True, False, None]:
- messages.append(
- 'Package files_analyzed must be True or False or None (omitted)'
- )
- if not self.are_files_analyzed and self.verif_code is not None:
- messages.append(
- 'Package verif_code must be None (omitted) when files_analyzed is False'
- )
-
- return messages
-
- def validate_primary_package_purposes(self, messages: ErrorMessages) -> ErrorMessages:
- if self.primary_package_purpose not in PackagePurpose:
- messages.append("Primary package purpose has a value that is not allowed!")
- return messages
-
- def validate_optional_fields(self, messages):
- if self.originator and not isinstance(
- self.originator, (utils.NoAssert, creationinfo.Creator)
- ):
- messages.append(
- "Package originator must be instance of "
- "spdx.utils.NoAssert or spdx.creationinfo.Creator"
- )
-
- if self.supplier and not isinstance(
- self.supplier, (utils.NoAssert, creationinfo.Creator)
- ):
- messages.append(
- "Package supplier must be instance of "
- "spdx.utils.NoAssert or spdx.creationinfo.Creator"
- )
-
- if self.conc_lics and not isinstance(
- self.conc_lics, (utils.SPDXNone, utils.NoAssert, license.License)
- ):
- messages.append(
- "Package concluded license must be instance of "
- "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
- "spdx.license.License"
- )
-
- if self.license_declared and not isinstance(
- self.license_declared, (utils.SPDXNone, utils.NoAssert, license.License)
- ):
- messages.append(
- "Package declared license must be instance of "
- "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
- "spdx.license.License"
- )
-
- license_from_file_check = lambda prev, el: prev and isinstance(
- el, (license.License, utils.SPDXNone, utils.NoAssert)
- )
- if not reduce(license_from_file_check, self.licenses_from_files, True):
- messages.append(
- "Each element in licenses_from_files must be instance of "
- "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
- "spdx.license.License"
- )
-
- return messages
-
- def validate_pkg_ext_refs(self, messages):
- for ref in self.pkg_ext_refs:
- if isinstance(ref, ExternalPackageRef):
- messages = ref.validate(messages)
- else:
- messages.append(
- "External package references must be of the type "
- "spdx.package.ExternalPackageRef and not " + str(type(ref))
- )
-
- return messages
-
- def validate_optional_str_fields(self, messages):
- """Fields marked as optional and of type string in class
- docstring must be of a type that provides __str__ method.
- """
- FIELDS = [
- "file_name",
- "version",
- "homepage",
- "source_info",
- "summary",
- "description",
- "attribution_text",
- "comment",
- "cr_text"
- ]
- self.validate_str_fields(FIELDS, True, messages)
-
- return messages
-
- def validate_mandatory_str_fields(self, messages):
- """Fields marked as Mandatory and of type string in class
- docstring must be of a type that provides __str__ method.
- """
- FIELDS = ["name", "spdx_id", "download_location"]
- self.validate_str_fields(FIELDS, False, messages)
-
- return messages
-
- def validate_str_fields(self, fields, optional, messages):
- """Helper for validate_mandatory_str_field and
- validate_optional_str_fields"""
- for field_str in fields:
- field = getattr(self, field_str)
- if field is not None:
- # FIXME: this does not make sense???
- attr = getattr(field, "__str__", None)
- if not callable(attr):
- messages.append(
- "{0} must provide __str__ method.".format(field)
- )
- # Continue checking.
- elif not optional:
- messages.append("Package {0} can not be None.".format(field_str))
-
- return messages
-
- def validate_checksums(self, messages: ErrorMessages):
- if not self.checksums:
- return
- for checksum in self.checksums.values():
- if not isinstance(checksum, Checksum):
- messages.append("Package checksum must be instance of spdx.checksum.Checksum")
-
- def get_checksum(self, hash_algorithm: ChecksumAlgorithm = ChecksumAlgorithm.SHA1) -> Optional[Checksum]:
- return self.checksums.get(hash_algorithm)
-
- def set_checksum(self, new_checksum: Checksum):
- if not isinstance(new_checksum, Checksum):
- raise SPDXValueError("Package::Checksum")
-
- self.checksums[new_checksum.identifier] = new_checksum
-
- def has_optional_field(self, field):
- return bool(getattr(self, field, None))
-
-
-class ExternalPackageRef(object):
- """
- An External Reference allows a Package to reference an external source of
- additional information, metadata, enumerations, asset identifiers, or
- downloadable content believed to be relevant to the Package.
- Fields:
- - category: "SECURITY" or "PACKAGE-MANAGER" or "OTHER".
- - pkg_ext_ref_type: A unique string containing letters, numbers, ".","-".
- - locator: A unique string with no spaces necessary to access the
- package-specific information, metadata, or content within the target
- location.
- - comment: To provide information about the purpose and target of the
- reference.
- """
-
- def __init__(
- self, category=None, pkg_ext_ref_type=None, locator=None, comment=None
- ):
- self.category = category
- self.pkg_ext_ref_type = pkg_ext_ref_type
- self.locator = locator
- self.comment = comment
-
- def validate(self, messages):
- """
- Check that all the fields are valid.
- Appends any error messages to messages parameter shall be a ErrorMessages.
- """
- self.validate_category(messages)
- self.validate_pkg_ext_ref_type(messages)
- self.validate_locator(messages)
-
- return messages
-
- def validate_category(self, messages):
- if self.category is None:
- messages.append("ExternalPackageRef has no category.")
-
- return messages
-
- def validate_pkg_ext_ref_type(self, messages):
- if self.pkg_ext_ref_type is None:
- messages.append("ExternalPackageRef has no type.")
-
- return messages
-
- def validate_locator(self, messages):
- if self.locator is None:
- messages.append("ExternalPackageRef has no locator.")
-
- return messages
diff --git a/spdx/parsers/__init__.py b/spdx/parsers/__init__.py
deleted file mode 100644
index 588b404ec..000000000
--- a/spdx/parsers/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
diff --git a/spdx/parsers/builderexceptions.py b/spdx/parsers/builderexceptions.py
deleted file mode 100644
index fe03f8d51..000000000
--- a/spdx/parsers/builderexceptions.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-class BuilderException(Exception):
- """Builder exception base class."""
-
- pass
-
-
-class CardinalityError(BuilderException):
- def __init__(self, msg):
- self.msg = msg
-
-
-class SPDXValueError(BuilderException):
- def __init__(self, msg):
- self.msg = msg
-
-
-class OrderError(BuilderException):
- def __init__(self, msg):
- self.msg = msg
-
-
-class FileTypeError(BuilderException):
- def __init__(self, msg):
- self.msg = msg
diff --git a/spdx/parsers/jsonparser.py b/spdx/parsers/jsonparser.py
deleted file mode 100644
index 5436a6798..000000000
--- a/spdx/parsers/jsonparser.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-
-from spdx.parsers import jsonyamlxml
-
-
-class Parser(jsonyamlxml.Parser):
- """
- Wrapper class for jsonyamlxml.Parser to provide an interface similar to
- RDF and TV Parser classes (i.e., spdx.parsers..Parser) for JSON parser.
- It also avoids to repeat jsonyamlxml.Parser.parse code for JSON, YAML and XML parsers
- """
-
- def __init__(self, builder, logger):
- super(Parser, self).__init__(builder, logger)
-
- def parse(self, file):
- self.json_yaml_set_document(json.load(file))
- return super(Parser, self).parse()
diff --git a/spdx/parsers/jsonyamlxml.py b/spdx/parsers/jsonyamlxml.py
deleted file mode 100644
index 5469376b0..000000000
--- a/spdx/parsers/jsonyamlxml.py
+++ /dev/null
@@ -1,1885 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-from datetime import datetime
-from enum import Enum, auto
-from typing import List, Dict, Tuple, Callable, Optional
-
-from spdx import document
-from spdx import utils
-from spdx.license import LicenseConjunction, LicenseDisjunction
-from spdx.package import ExternalPackageRef, PackagePurpose, Package
-from spdx.parsers import rdf
-from spdx.parsers.builderexceptions import SPDXValueError, CardinalityError, OrderError
-from spdx.parsers.loggers import ErrorMessages
-from spdx.snippet import Snippet
-from spdx.utils import UnKnown
-
-ERROR_MESSAGES = rdf.ERROR_MESSAGES
-
-
-class BaseParser(object):
- def __init__(self, builder, logger):
- self.builder = builder
- self.logger = logger
-
- def order_error(self, first_tag, second_tag):
- """
- Helper method for logging an OrderError raised.
- - first_tag: field to be added
- - second_tag: required field
- """
- self.error = True
- msg = "{0} Can not appear before {1}".format(first_tag, second_tag)
- self.logger.log(msg)
-
- def more_than_one_error(self, field):
- """
- Helper method for logging an CardinalityError raised.
- - field: field/property that has been already defined.
- """
- msg = "More than one {0} defined.".format(field)
- self.logger.log(msg)
- self.error = True
-
- def value_error(self, key, bad_value):
- """
- Helper method for logging an SPDXValueError raised.
- It reports a value error using ERROR_MESSAGES dict.
- - key: key to use for ERROR_MESSAGES. If not present, a default message is logged
- - bad_value: malformed value
- """
- msg = ERROR_MESSAGES.get(key)
- if msg:
- self.logger.log(msg.format(bad_value))
- else:
- msg = "'{0}' is not a valid value for {1}".format(bad_value, key)
- self.logger.log(msg)
- self.error = True
-
-
-class CreationInfoParser(BaseParser):
- def __init__(self, builder, logger):
- super(CreationInfoParser, self).__init__(builder, logger)
-
- def parse_creation_info(self, creation_info):
- """
- Parse Creation Information fields
- - creation_info: Python dict with Creation Information fields in it
- """
- if isinstance(creation_info, dict):
- self.parse_creation_info_comment(creation_info.get("comment"))
- self.parse_creation_info_lic_list_version(
- creation_info.get("licenseListVersion")
- )
- self.parse_creation_info_created(creation_info.get("created"))
- self.parse_creation_info_creators(creation_info.get("creators"))
- else:
- self.value_error("CREATION_INFO_SECTION", creation_info)
-
- def parse_creation_info_comment(self, comment):
- """
- Parse CreationInfo comment
- - comment: Python str/unicode
- """
- if isinstance(comment, str):
- try:
- return self.builder.set_creation_comment(self.document, comment)
- except CardinalityError:
- self.more_than_one_error("CreationInfo comment")
- elif comment is not None:
- self.value_error("CREATION_COMMENT", comment)
-
- def parse_creation_info_lic_list_version(self, license_list_version):
- """
- Parse CreationInfo license list version
- - license_list_version: Python str/unicode
- """
- if isinstance(license_list_version, str):
- try:
- return self.builder.set_lics_list_ver(
- self.document, license_list_version
- )
- except SPDXValueError:
- raise
- self.value_error("LL_VALUE", license_list_version)
- except CardinalityError:
- self.more_than_one_error("CreationInfo licenseListVersion")
- elif license_list_version is not None:
- self.value_error("LL_VALUE", license_list_version)
-
- def parse_creation_info_created(self, created):
- """
- Parse CreationInfo creation date
- - created: Python str/unicode (ISO-8601 representation of datetime)
- """
- if isinstance(created, str):
- try:
- return self.builder.set_created_date(self.document, created)
- except SPDXValueError:
- self.value_error("CREATED_VALUE", created)
- except CardinalityError:
- self.more_than_one_error("CreationInfo created")
- else:
- self.value_error("CREATED_VALUE", created)
-
- def parse_creation_info_creators(self, creators):
- """
- Parse CreationInfo creators
- - creators: Python list of creators (str/unicode)
- """
- if isinstance(creators, list):
- for creator in creators:
- if isinstance(creator, str):
- entity = self.builder.create_entity(self.document, creator)
- try:
- self.builder.add_creator(self.document, entity)
- except SPDXValueError:
- self.value_error("CREATOR_VALUE", creator)
- else:
- self.value_error("CREATOR_VALUE", creator)
- else:
- self.value_error("CREATORS_SECTION", creators)
-
-
-class ExternalDocumentRefsParser(BaseParser):
- def __init__(self, builder, logger):
- super(ExternalDocumentRefsParser, self).__init__(builder, logger)
-
- def parse_external_document_refs(self, external_document_refs):
- """
- Parse External Document References fields
- - external_document_refs: Python list with External Document References dicts in it
- """
- if isinstance(external_document_refs, list):
- for external_document_ref in external_document_refs:
- if isinstance(external_document_ref, dict):
- self.parse_ext_doc_ref_id(
- external_document_ref.get("externalDocumentId")
- )
- self.parse_ext_doc_ref_namespace(
- external_document_ref.get("spdxDocument")
- )
- self.parse_ext_doc_ref_chksum(external_document_ref.get("checksum"))
- else:
- self.value_error("EXT_DOC_REF", external_document_ref)
- elif external_document_refs is not None:
- self.value_error("EXT_DOC_REFS_SECTION", external_document_refs)
-
- def parse_ext_doc_ref_id(self, ext_doc_ref_id):
- """
- Parse ExternalDocumentReference id
- ext_doc_ref_id: Python str/unicode
- """
- if isinstance(ext_doc_ref_id, str):
- return self.builder.set_ext_doc_id(self.document, ext_doc_ref_id)
- self.value_error("EXT_DOC_REF_ID", ext_doc_ref_id)
- return self.builder.set_ext_doc_id(self.document, "dummy_ext_doc_ref")
- # ext_doc_ref_id is set even if it is None or not string. If weren't, the other attributes
- # would be added to the ex_doc_ref previously added.
- # Another approach is to skip the whole ex_doc_ref itself
-
- def parse_ext_doc_ref_namespace(self, namespace):
- """
- Parse ExternalDocumentReference namespace
- namespace: Python str/unicode
- """
- if isinstance(namespace, str):
- try:
- return self.builder.set_spdx_doc_uri(self.document, namespace)
- except SPDXValueError:
- self.value_error("EXT_DOC_REF_VALUE", namespace)
- else:
- self.value_error("EXT_DOC_REF_VALUE", namespace)
-
- def parse_ext_doc_ref_chksum(self, chksum):
- """
- Parse ExternalDocumentReference checksum
- chksum: Python dict('algorithm':str/unicode, 'value':str/unicode)
- """
- if isinstance(chksum, dict):
- value = chksum.get("checksumValue")
- if isinstance(value, str):
- try:
- return self.builder.set_chksum(self.document, value)
- except SPDXValueError:
- self.value_error("CHECKSUM_VALUE", value)
- else:
- self.value_error("CHECKSUM_VALUE", value)
- else:
- self.value_error("CHECKSUM_FIELD", chksum)
-
-
-class LicenseParser(BaseParser):
- def __init__(self, builder, logger):
- super(LicenseParser, self).__init__(builder, logger)
-
- def parse_extracted_license_info(self, extracted_license_info):
- """
- Parse Extracted Lisence Information fields
- - extracted_license_info: Python list with Extracted Lisence Information dicts in it
- """
- if isinstance(extracted_license_info, list):
- for extracted_license in extracted_license_info:
- if isinstance(extracted_license, dict):
- if self.parse_ext_lic_id(extracted_license.get("licenseId")):
- self.parse_ext_lic_name(extracted_license.get("name"))
- self.parse_ext_lic_comment(extracted_license.get("comment"))
- self.parse_ext_lic_text(extracted_license.get("extractedText"))
- self.parse_ext_lic_cross_refs(extracted_license.get("seeAlsos"))
- else:
- self.value_error("EXTR_LIC", extracted_license)
-
- def parse_ext_lic_id(self, ext_lic_id):
- """
- Parse ExtractedLicenseInformation id
- ext_lic_id: Python str/unicode
- """
- if isinstance(ext_lic_id, str):
- try:
- return self.builder.set_lic_id(self.document, ext_lic_id)
- except SPDXValueError:
- self.value_error("EXTR_LIC_ID", ext_lic_id)
- else:
- self.value_error("EXTR_LIC_ID", ext_lic_id)
-
- def parse_ext_lic_name(self, ext_lic_name):
- """
- Parse ExtractedLicenseInformation name
- ext_lic_name: Python str/unicode
- """
- try:
- return self.builder.set_lic_name(self.document, ext_lic_name)
- except SPDXValueError:
- self.value_error("EXTR_LIC_NAME", ext_lic_name)
- except CardinalityError:
- self.more_than_one_error("ExtractedLicense name")
- except OrderError:
- self.order_error("ExtractedLicense name", "ExtractedLicense id")
-
- def parse_ext_lic_comment(self, ext_lic_comment):
- """
- Parse ExtractedLicenseInformation comment
- ext_lic_comment: Python str/unicode
- """
- if isinstance(ext_lic_comment, str):
- try:
- return self.builder.set_lic_comment(self.document, ext_lic_comment)
- except CardinalityError:
- self.more_than_one_error("ExtractedLicense comment")
- except OrderError:
- self.order_error("ExtractedLicense comment", "ExtractedLicense id")
- elif ext_lic_comment is not None:
- self.value_error("EXTR_LIC_COMMENT", ext_lic_comment)
-
- def parse_ext_lic_text(self, ext_lic_text):
- """
- Parse ExtractedLicenseInformation text
- ext_lic_text: Python str/unicode
- """
- if isinstance(ext_lic_text, str):
- try:
- return self.builder.set_lic_text(self.document, ext_lic_text)
- except CardinalityError:
- self.more_than_one_error("ExtractedLicense text")
- except OrderError:
- self.order_error("ExtractedLicense text", "ExtractedLicense id")
- else:
- self.value_error("EXTR_LIC_TXT", ext_lic_text)
-
- def parse_ext_lic_cross_refs(self, cross_refs):
- """
- Parse ExtractedLicenseInformation cross references
- cross_refs: Python list of cross references (str/unicode)
- """
- if isinstance(cross_refs, list):
- for cross_ref in cross_refs:
- if isinstance(cross_ref, str):
- try:
- self.builder.add_lic_xref(self.document, cross_ref)
- except OrderError:
- self.order_error(
- "ExtractedLicense cross references", "ExtractedLicense id"
- )
- else:
- self.value_error("CROSS_REF", cross_ref)
-
- def replace_license(self, license_object):
- if isinstance(license_object, LicenseConjunction):
- return LicenseConjunction(
- self.replace_license(license_object.license_1),
- self.replace_license(license_object.license_2),
- )
- elif isinstance(license_object, LicenseDisjunction):
- return LicenseDisjunction(
- self.replace_license(license_object.license_1),
- self.replace_license(license_object.license_2),
- )
- else:
- license_objects = list(
- filter(
- lambda lic: lic.identifier == license_object.identifier,
- self.document.extracted_licenses,
- )
- )
- return license_objects[-1] if license_objects else license_object
-
-
-class AnnotationParser(BaseParser):
- def __init__(self, builder, logger):
- super(AnnotationParser, self).__init__(builder, logger)
-
- def parse_annotations(self, annotations, spdx_id: str = None):
- """
- Parse Annotation Information fields
- - annotations: Python list with Annotation Information dicts in it
- """
- if isinstance(annotations, list):
- for annotation in annotations:
- if isinstance(annotation, dict):
- if self.parse_annotation_annotator(annotation.get("annotator")):
- self.parse_annotation_date(annotation.get("annotationDate"))
- self.parse_annotation_comment(annotation.get("comment"))
- self.parse_annotation_type(annotation.get("annotationType"))
- if annotation.get("SPDXID"):
- self.parse_annotation_id(annotation.get("SPDXID"))
- else:
- self.parse_annotation_id(spdx_id)
- else:
- self.value_error("ANNOTATION", annotation)
-
- def parse_annotation_annotator(self, annotator):
- """
- Parse Annotation annotator
- - annotator: Python str/unicode
- """
- if isinstance(annotator, str):
- entity = self.builder.create_entity(self.document, annotator)
- try:
- return self.builder.add_annotator(self.document, entity)
- except SPDXValueError:
- self.value_error("ANNOTATOR_VALUE", annotator)
- else:
- self.value_error("ANNOTATOR_VALUE", annotator)
-
- def parse_annotation_date(self, date):
- """
- Parse Annotation date
- - date: Python str/unicode (ISO-8601 representation of datetime)
- """
- if isinstance(date, str):
- try:
- return self.builder.add_annotation_date(self.document, date)
- except SPDXValueError:
- self.value_error("ANNOTATION_DATE", date)
- except CardinalityError:
- self.more_than_one_error("Annotation date")
- except OrderError:
- self.order_error("ANNOTATION_DATE", "ANNOTATOR_VALUE")
- else:
- self.value_error("ANNOTATION_DATE", date)
-
- def parse_annotation_comment(self, comment):
- """
- Parse Annotation comment
- - comment: Python str/unicode
- """
- if isinstance(comment, str):
- try:
- return self.builder.add_annotation_comment(self.document, comment)
- except CardinalityError:
- self.more_than_one_error("Annotation comment")
- except OrderError:
- self.order_error("ANNOTATION_COMMENT", "ANNOTATOR_VALUE")
- else:
- self.value_error("ANNOTATION_COMMENT", comment)
-
- def parse_annotation_type(self, annotation_type):
- """
- Parse Annotation type
- - annotation_type: Python str/unicode (REVIEW or OTHER)
- """
- if isinstance(annotation_type, str):
- try:
- return self.builder.add_annotation_type(self.document, annotation_type)
- except SPDXValueError:
- self.value_error("ANNOTATION_TYPE", annotation_type)
- except CardinalityError:
- self.more_than_one_error("ANNOTATION_TYPE")
- except OrderError:
- self.order_error("ANNOTATION_TYPE", "ANNOTATOR_VALUE")
- else:
- self.value_error("ANNOTATION_TYPE", annotation_type)
-
- def parse_annotation_id(self, annotation_id):
- """
- Parse Annotation id
- - annotation_id: Python str/unicode
- """
- if isinstance(annotation_id, str):
- try:
- return self.builder.set_annotation_spdx_id(self.document, annotation_id)
- except CardinalityError:
- self.more_than_one_error("ANNOTATION_ID")
- except OrderError:
- self.order_error("ANNOTATION_ID", "ANNOTATOR_VALUE")
- else:
- self.value_error("ANNOTATION_ID", annotation_id)
-
-
-class RelationshipParser(BaseParser):
- def __init__(self, builder, logger):
- super(RelationshipParser, self).__init__(builder, logger)
-
- def parse_relationships(self, relationships):
- """
- Parse Relationship Information fields
- - relationships: Python list with Relationship Information dicts in it
- """
- if isinstance(relationships, list):
- for relationship in relationships:
- if isinstance(relationship, dict):
- if self.parse_relationship(
- relationship.get("spdxElementId"),
- relationship.get("relationshipType"),
- relationship.get("relatedSpdxElement"),
- ):
- self.parse_relationship_comment(relationship.get("comment"))
- else:
- self.value_error("RELATIONSHIP", relationship)
-
- def parse_relationship(self, spdxelementid, relationshiptype, relatedspdxelement):
- """
- Parse Relationshiptype, spdxElementId and relatedSpdxElement
- - relationship: Python str/unicode
- """
- if not isinstance(relationshiptype, str):
- self.value_error("RELATIONSHIP_VALUE", relationshiptype)
- return
- if not isinstance(spdxelementid, str):
- self.value_error("SPDXELEMENTID", spdxelementid)
- return
- if not isinstance(relatedspdxelement, str):
- self.value_error("RELATEDSPDXELEMENT", relatedspdxelement)
- return
- relate = spdxelementid + " " + relationshiptype + " " + relatedspdxelement
- try:
- return self.builder.add_relationship(self.document, relate)
- except SPDXValueError:
- self.value_error("RELATIONSHIP_VALUE", relate)
-
-
- def parse_relationship_comment(self, relationship_comment):
- """
- Parse relationship comment
- - relationship_comment: Python str/unicode
- """
- if isinstance(relationship_comment, str):
- try:
- return self.builder.add_relationship_comment(
- self.document, relationship_comment
- )
- except CardinalityError:
- self.more_than_one_error("RELATIONSHIP_COMMENT")
- except OrderError:
- self.order_error("RELATIONSHIP_COMMENT", "RELATIONSHIP")
- elif relationship_comment is not None:
- self.value_error("RELATIONSHIP_COMMENT", relationship_comment)
-
-
-class RangeType(Enum):
- BYTE = auto()
- LINE = auto()
-
-
-class SnippetParser(BaseParser):
- def __init__(self, builder, logger):
- super(SnippetParser, self).__init__(builder, logger)
-
- @property
- def snippet(self) -> Snippet:
- return self.document.snippet[-1]
-
- def parse_snippets(self, snippets):
- """
- Parse Snippet Information fields
- - snippets: Python list with Snippet Information dicts in it
- """
- if isinstance(snippets, list):
- for snippet in snippets:
- if isinstance(snippet, dict):
- if self.parse_snippet_id(snippet.get("SPDXID")):
- self.parse_snippet_name(snippet.get("name"))
- self.parse_snippet_comment(snippet.get("comment"))
- self.parse_snippet_copyright(snippet.get("copyrightText"))
- self.parse_snippet_license_comment(
- snippet.get("licenseComments")
- )
- self.parse_snippet_file_spdxid(snippet.get("snippetFromFile"))
- self.parse_snippet_concluded_license(
- snippet.get("licenseConcluded")
- )
- self.parse_snippet_attribution_text(
- snippet.get("attributionTexts")
- )
- self.parse_snippet_license_info_from_snippet(
- snippet.get("licenseInfoInSnippets")
- )
- self.parse_annotations(snippet.get("annotations"), spdx_id=snippet.get("SPDXID"))
- self.parse_snippet_ranges(snippet.get("ranges"))
- else:
- self.value_error("SNIPPET", snippet)
-
- def parse_snippet_id(self, snippet_id):
- """
- Parse Snippet id
- - snippet_id: Python str/unicode
- """
- if isinstance(snippet_id, str):
- try:
- return self.builder.create_snippet(self.document, snippet_id)
- except SPDXValueError:
- self.value_error("SNIPPET_SPDX_ID_VALUE", snippet_id)
- else:
- self.value_error("SNIPPET_SPDX_ID_VALUE", snippet_id)
-
- def parse_snippet_name(self, snippet_name):
- """
- Parse Snippet name
- - snippet_name: Python str/unicode
- """
- if isinstance(snippet_name, str):
- try:
- return self.builder.set_snippet_name(self.document, snippet_name)
- except CardinalityError:
- self.more_than_one_error("SNIPPET_NAME")
- elif snippet_name is not None:
- self.value_error("SNIPPET_NAME", snippet_name)
-
- def parse_snippet_comment(self, snippet_comment):
- """
- Parse Snippet comment
- - snippet_comment: Python str/unicode
- """
- if isinstance(snippet_comment, str):
- try:
- return self.builder.set_snippet_comment(self.document, snippet_comment)
- except CardinalityError:
- self.more_than_one_error("SNIPPET_COMMENT")
- elif snippet_comment is not None:
- self.value_error("SNIPPET_COMMENT", snippet_comment)
-
- def parse_snippet_attribution_text(self, snippet_attribution_texts):
- """
- Parse Snippet attribution texts
- - snippet_attribution_texts: list in yaml, json and string in xml format
- """
- if isinstance(snippet_attribution_texts, list) or isinstance(
- snippet_attribution_texts, str
- ):
- for snippet_attribution_text in snippet_attribution_texts:
- try:
- return self.builder.set_snippet_attribution_text(
- self.document, snippet_attribution_texts
- )
- except CardinalityError:
- self.more_than_one_error("SNIPPET_ATTRIBUTION_TEXT")
- except OrderError:
- self.order_error("SNIPPET_ATTRIBUTION_TEXT", "SNIPPET_NAME")
- else:
- self.value_error("SNIPPET_ATTRIBUTION_TEXT", snippet_attribution_texts)
-
- def parse_snippet_copyright(self, copyright_text):
- """
- Parse Snippet copyright text
- - copyright_text: Python str/unicode
- """
- if isinstance(copyright_text, str):
- try:
- return self.builder.set_snippet_copyright(self.document, copyright_text)
- except CardinalityError:
- self.more_than_one_error("SNIPPET_COPYRIGHT")
- elif copyright_text is not None:
- self.value_error("SNIPPET_COPYRIGHT", copyright_text)
-
- def parse_snippet_license_comment(self, license_comment):
- """
- Parse Snippet license comment
- - license_comment: Python str/unicode
- """
- if isinstance(license_comment, str):
- try:
- return self.builder.set_snippet_lic_comment(
- self.document, license_comment
- )
- except CardinalityError:
- self.more_than_one_error("SNIPPET_LIC_COMMENTS")
- elif license_comment is not None:
- self.value_error("SNIPPET_LIC_COMMENTS", license_comment)
-
- def parse_snippet_file_spdxid(self, file_spdxid):
- """
- Parse Snippet file id
- - file_spdxid: Python str/unicode
- """
- if isinstance(file_spdxid, str):
- try:
- return self.builder.set_snip_from_file_spdxid(
- self.document, file_spdxid
- )
- except SPDXValueError:
- self.value_error("SNIPPET_FILE_ID", file_spdxid)
- except CardinalityError:
- self.more_than_one_error("SNIPPET_FILE_ID")
- else:
- self.value_error("SNIPPET_FILE_ID", file_spdxid)
-
- def parse_snippet_concluded_license(self, concluded_license):
- """
- Parse Snippet concluded license
- - concluded_license: Python str/unicode
- """
- if isinstance(concluded_license, str):
- lic_parser = utils.LicenseListParser()
- lic_parser.build(write_tables=0, debug=0)
- license_object = self.replace_license(lic_parser.parse(concluded_license))
- try:
- return self.builder.set_snip_concluded_license(
- self.document, license_object
- )
- except SPDXValueError:
- self.value_error("SNIPPET_CONCLUDED_LICENSE", concluded_license)
- except CardinalityError:
- self.more_than_one_error("SNIPPET_CONCLUDED_LICENSE")
- elif concluded_license is not None:
- self.value_error("SNIPPET_CONCLUDED_LICENSE", concluded_license)
-
- def parse_snippet_license_info_from_snippet(self, license_info_from_snippet):
- """
- Parse Snippet license information from snippet
- - license_info_from_snippet: Python list of licenses information from snippet (str/unicode)
- """
- if isinstance(license_info_from_snippet, list):
- for lic_in_snippet in license_info_from_snippet:
- if isinstance(lic_in_snippet, str):
- lic_parser = utils.LicenseListParser()
- lic_parser.build(write_tables=0, debug=0)
- license_object = self.replace_license(
- lic_parser.parse(lic_in_snippet)
- )
- try:
- self.builder.set_snippet_lics_info(
- self.document, license_object
- )
- except SPDXValueError:
- self.value_error("SNIPPET_LIC_INFO", lic_in_snippet)
- else:
- self.value_error("SNIPPET_LIC_INFO", lic_in_snippet)
- elif license_info_from_snippet is not None:
- self.value_error("SNIPPET_LIC_INFO_FIELD", license_info_from_snippet)
-
- def parse_snippet_ranges(self, ranges_from_snippet: List[Dict]) -> None:
- """
- Parse ranges (byte range and optional line range) from snippet
- - ranges_from_snippet; Python list of dict
- """
- if not isinstance(ranges_from_snippet, list):
- self.value_error("SNIPPET_RANGES", ranges_from_snippet)
- return
-
- for range_dict in ranges_from_snippet:
- try:
- range_type = self.validate_range_and_get_type(range_dict)
- start_end_tuple: Tuple[int, int] = SnippetParser.get_start_end_tuple(range_dict, range_type)
- except SPDXValueError:
- self.value_error("SNIPPET_RANGE", range_dict)
- return
-
- if range_type == RangeType.BYTE:
- self.snippet.byte_range = start_end_tuple
- elif range_type == RangeType.LINE:
- self.snippet.line_range = start_end_tuple
-
- @staticmethod
- def get_start_end_tuple(range_dict: Dict, range_type: RangeType) -> Tuple[int, int]:
- end_pointer = range_dict["endPointer"]
- start_pointer = range_dict["startPointer"]
- if range_type == RangeType.BYTE:
- start = int(start_pointer["offset"])
- end = int(end_pointer["offset"])
- else:
- start = int(start_pointer["lineNumber"])
- end = int(end_pointer["lineNumber"])
- if start > end:
- raise SPDXValueError("Snippet::ranges")
-
- return start, end
-
- def validate_range_and_get_type(self, range_dict: Dict) -> RangeType:
- if ("startPointer" not in range_dict) or ("endPointer" not in range_dict):
- raise SPDXValueError("Snippet::ranges")
- start_pointer_type = self.validate_pointer_and_get_type(range_dict["startPointer"])
- end_pointer_type = self.validate_pointer_and_get_type(range_dict["endPointer"])
- if start_pointer_type != end_pointer_type:
- raise SPDXValueError("Snippet::ranges")
- return start_pointer_type
-
- def validate_pointer_and_get_type(self, pointer: Dict) -> RangeType:
- if self.snippet.snip_from_file_spdxid != pointer["reference"]:
- raise SPDXValueError("Snippet::ranges")
- if ("offset" in pointer and "lineNumber" in pointer) or (
- "offset" not in pointer and "lineNumber" not in pointer):
- raise SPDXValueError("Snippet::ranges")
-
- return RangeType.BYTE if "offset" in pointer else RangeType.LINE
-
-
-class ReviewParser(BaseParser):
- def __init__(self, builder, logger):
- super(ReviewParser, self).__init__(builder, logger)
-
- def parse_reviews(self, reviews):
- """
- Parse Review Information fields
- - reviews: Python list with Review Information dicts in it
- """
- if isinstance(reviews, list):
- for review in reviews:
- if isinstance(review, dict):
- if self.parse_review_reviewer(review.get("reviewer")):
- self.parse_review_date(review.get("reviewDate"))
- self.parse_review_comment(review.get("comment"))
- else:
- self.value_error("REVIEW", review)
-
- def parse_review_reviewer(self, reviewer):
- """
- Parse Review reviewer
- - reviewer: Python str/unicode
- """
- if isinstance(reviewer, str):
- entity = self.builder.create_entity(self.document, reviewer)
- try:
- return self.builder.add_reviewer(self.document, entity)
- except SPDXValueError:
- self.value_error("REVIEWER_VALUE", reviewer)
- else:
- self.value_error("REVIEWER_VALUE", reviewer)
-
- def parse_review_date(self, review_date):
- """
- Parse Review date
- - review_date: Python str/unicode (ISO-8601 representation of datetime)
- """
- if isinstance(review_date, str):
- try:
- return self.builder.add_review_date(self.document, review_date)
- except SPDXValueError:
- self.value_error("REVIEW_DATE", review_date)
- except CardinalityError:
- self.more_than_one_error("REVIEW_DATE")
- except OrderError:
- self.order_error("REVIEW_DATE", "REVIEWER_VALUE")
- else:
- self.value_error("REVIEW_DATE", review_date)
-
- def parse_review_comment(self, review_comment):
- """
- Parse Review comment
- - review_comment: Python str/unicode
- """
- if isinstance(review_comment, str):
- try:
- return self.builder.add_review_comment(self.document, review_comment)
- except CardinalityError:
- self.more_than_one_error("REVIEW_COMMENT")
- except OrderError:
- self.order_error("REVIEW_COMMENT", "REVIEWER_VALUE")
- elif review_comment is not None:
- self.value_error("REVIEW_COMMENT", review_comment)
-
-
-class FileParser(BaseParser):
- def __init__(self, builder, logger):
- super(FileParser, self).__init__(builder, logger)
-
- def parse_file(self, file):
- """
- Parse File Information fields
- - file: Python dict with File Information fields in it
- """
- if isinstance(file, dict):
- self.parse_file_name(file.get("fileName"))
- self.parse_file_id(file.get("SPDXID"))
- self.parse_file_types(file.get("fileTypes"))
- self.parse_file_concluded_license(file.get("licenseConcluded"))
- self.parse_file_license_info_in_files(file.get("licenseInfoInFiles"))
- self.parse_file_license_comments(file.get("licenseComments"))
- self.parse_file_copyright_text(file.get("copyrightText"))
- self.parse_file_artifacts(file.get("artifactOf"))
- self.parse_file_comment(file.get("comment"))
- self.parse_file_notice_text(file.get("noticeText"))
- self.parse_file_contributors(file.get("fileContributors"))
- self.parse_file_attribution_text(file.get("attributionTexts"))
- self.parse_file_dependencies(file.get("fileDependencies"))
- self.parse_annotations(file.get("annotations"), spdx_id=file.get("SPDXID"))
- self.parse_file_checksums(file.get("checksums"))
- else:
- self.value_error("FILE", file)
-
- def parse_file_name(self, file_name):
- """
- Parse File name
- - file_name: Python str/unicode
- """
- if isinstance(file_name, str):
- return self.builder.set_file_name(self.document, file_name)
- self.value_error("FILE_NAME", file_name)
- return self.builder.set_file_name(self.document, "dummy_file")
- # file_name is set even if it is None or not string. If weren't, the other attributes
- # would be added to the file previously added.
- # Another approach is to skip the whole file itself
-
- def parse_file_id(self, file_id):
- """
- Parse File id
- - file_id: Python str/unicode
- """
- if isinstance(file_id, str):
- try:
- return self.builder.set_file_spdx_id(self.document, file_id)
- except SPDXValueError:
- self.value_error("FILE_ID", file_id)
- except CardinalityError:
- self.more_than_one_error("FILE_ID")
- except OrderError:
- self.order_error("FILE_ID", "FILE_NAME")
- else:
- self.value_error("FILE_ID", file_id)
-
- def parse_file_types(self, file_types):
- """
- Parse File types
- - file_types: Python list of file types (str/unicode: fileType_archive, fileType_binary, fileType_source or
- fileType_other)
- """
- if isinstance(file_types, list): # file_types is an array in JSON examples...
- for file_type in file_types:
- self.parse_file_type(file_type)
- # ...but file.File allows only one type at the moment.
- elif isinstance(file_types, str):
- return self.parse_file_type(file_types)
- elif file_types is not None:
- self.value_error("FILE_TYPES", file_types)
-
- def parse_file_type(self, file_type):
- """
- Parse File type
- - file_type: Python str/unicode (fileType_archive, fileType_binary, fileType_source or fileType_other)
- """
- if isinstance(file_type, str):
- try:
- return self.builder.set_file_type(self.document, file_type)
- except SPDXValueError:
- self.value_error("FILE_TYPE", file_type)
- except OrderError:
- self.order_error("FILE_TYPE", "FILE_NAME")
- else:
- self.value_error("FILE_TYPE", file_type)
-
- def parse_file_concluded_license(self, concluded_license):
- """
- Parse File concluded license
- - concluded_license: Python str/unicode
- """
- if isinstance(concluded_license, str):
- lic_parser = utils.LicenseListParser()
- lic_parser.build(write_tables=0, debug=0)
- license_object = self.replace_license(lic_parser.parse(concluded_license))
- try:
- return self.builder.set_concluded_license(self.document, license_object)
- except SPDXValueError:
- self.value_error("FILE_SINGLE_LICS", concluded_license)
- except CardinalityError:
- self.more_than_one_error("FILE_SINGLE_LICS")
- except OrderError:
- self.order_error("FILE_SINGLE_LICS", "FILE_NAME")
- elif concluded_license is not None:
- self.value_error("FILE_SINGLE_LICS", concluded_license)
-
- def parse_file_license_info_in_files(self, license_info_in_files):
- """
- Parse File license information from files
- - license_info_from_files: Python list of licenses information from files (str/unicode)
- """
- if isinstance(license_info_in_files, list):
- for license_info_in_file in license_info_in_files:
- if isinstance(license_info_in_file, str):
- lic_parser = utils.LicenseListParser()
- lic_parser.build(write_tables=0, debug=0)
- license_object = self.replace_license(
- lic_parser.parse(license_info_in_file)
- )
- try:
- self.builder.set_file_license_in_file(
- self.document, license_object
- )
- except SPDXValueError:
- self.value_error("FILE_LIC_IN_FILES", license_info_in_file)
- except OrderError:
- self.order_error("FILE_LIC_IN_FILES", "FILE_NAME")
- else:
- self.value_error("FILE_LIC_IN_FILES", license_info_in_file)
- elif license_info_in_files is not None:
- self.value_error("FILE_LIC_IN_FILES_FIELD", license_info_in_files)
-
- def parse_file_license_comments(self, license_comments):
- """
- Parse File license comments
- - license_comments: Python str/unicode
- """
- if isinstance(license_comments, str):
- try:
- return self.builder.set_file_license_comment(
- self.document, license_comments
- )
- except CardinalityError:
- self.more_than_one_error("FILE_LIC_COMMENTS")
- except OrderError:
- self.order_error("FILE_LIC_COMMENTS", "FILE_NAME")
- elif license_comments is not None:
- self.value_error("FILE_LIC_COMMENTS", license_comments)
-
- def parse_file_attribution_text(self, file_attribution_texts):
- """
- Parse File attribution texts
- - file_attribution_texts: list in yaml, json and string in xml format
- """
- if isinstance(file_attribution_texts, list):
- for file_attribution_text in file_attribution_texts:
- try:
- return self.builder.set_file_attribution_text(
- self.document, file_attribution_text
- )
- except CardinalityError:
- self.more_than_one_error("FILE_ATTRIBUTION_TEXT")
- except OrderError:
- self.order_error("FILE_ATTRIBUTION_TEXT", "FILE_NAME")
- else:
- self.value_error("FILE_ATTRIBUTION_TEXT", file_attribution_texts)
- elif isinstance(file_attribution_texts, str):
- try:
- return self.builder.set_file_attribution_text(
- self.document, file_attribution_texts
- )
- except CardinalityError:
- self.more_than_one_error("FILE_ATTRIBUTION_TEXT")
- except OrderError:
- self.order_error("FILE_ATTRIBUTION_TEXT", "FILE_NAME")
-
- def parse_file_copyright_text(self, copyright_text):
- """
- Parse File copyright text
- - copyright_text: Python str/unicode
- """
- if isinstance(copyright_text, str):
- try:
- return self.builder.set_file_copyright(self.document, copyright_text)
- except CardinalityError:
- self.more_than_one_error("FILE_COPYRIGHT_TEXT")
- except OrderError:
- self.order_error("FILE_COPYRIGHT_TEXT", "FILE_NAME")
- elif copyright_text is not None:
- self.value_error("FILE_COPYRIGHT_TEXT", copyright_text)
-
- def parse_file_artifacts(self, file_artifacts):
- """
- Parse File artifacts
- - file_artifacts: Python list of dict('name':str/unicode, 'homePage':str/unicode, 'projectUri':str/unicode)
- """
- if isinstance(file_artifacts, list):
- for artifact in file_artifacts:
- if isinstance(artifact, dict):
- self.builder.set_file_atrificat_of_project(
- self.document, "name", artifact.get("name", UnKnown())
- )
- self.builder.set_file_atrificat_of_project(
- self.document, "home", artifact.get("homePage", UnKnown())
- )
- self.builder.set_file_atrificat_of_project(
- self.document, "uri", artifact.get("projectUri", UnKnown())
- )
- return True
- else:
- self.value_error("ARTIFACT_OF_VALUE", artifact)
- elif file_artifacts is not None:
- self.value_error("ARTIFACT_OF_FIELD", file_artifacts)
-
- def parse_file_comment(self, file_comment):
- """
- Parse File comment
- - file_comment: Python str/unicode
- """
- if isinstance(file_comment, str):
- try:
- return self.builder.set_file_comment(self.document, file_comment)
- except CardinalityError:
- self.more_than_one_error("FILE_COMMENT")
- except OrderError:
- self.order_error("FILE_COMMENT", "FILE_NAME")
- elif file_comment is not None:
- self.value_error("FILE_COMMENT", file_comment)
-
- def parse_file_notice_text(self, notice_text):
- """
- Parse File notice text
- - notice_text: Python str/unicode
- """
- if isinstance(notice_text, str):
- try:
- return self.builder.set_file_notice(self.document, notice_text)
- except CardinalityError:
- self.more_than_one_error("FILE_NOTICE_TEXT")
- except OrderError:
- self.order_error("FILE_NOTICE_TEXT", "FILE_NAME")
- elif notice_text is not None:
- self.value_error("FILE_NOTICE_TEXT", notice_text)
-
- def parse_file_contributors(self, file_contributors):
- """
- Parse File contributors
- - file_contributors: Python list of contributors (str/unicode)
- """
- if isinstance(file_contributors, list):
- for contributor in file_contributors:
- if isinstance(contributor, str):
- try:
- self.builder.add_file_contribution(self.document, contributor)
- except OrderError:
- self.order_error("FILE_CONTRIBUTOR", "FILE_NAME")
- else:
- self.value_error("FILE_CONTRIBUTOR", contributor)
- elif file_contributors is not None:
- self.value_error("FILE_CONTRIBUTORS", file_contributors)
-
- def parse_file_dependencies(self, file_dependencies):
- """
- Parse File dependencies
- - file_dependencies: Python list of dependencies (str/unicode or file dict as in FileParser.parse_file)
- """
- if isinstance(file_dependencies, list):
- for dependency in file_dependencies:
- dependency = self._handle_file_dependency(dependency)
- if isinstance(dependency, str):
- try:
- self.builder.add_file_dep(self.document, dependency)
- except OrderError:
- self.order_error("FILE_DEPENDENCY", "FILE_NAME")
- else:
- self.value_error("FILE_DEPENDENCY", dependency)
- elif file_dependencies is not None:
- self.value_error("FILE_DEPENDENCIES", file_dependencies)
-
- def _handle_file_dependency(self, file_dependency):
- """
- Helper method that handles file-like dependency
- - file_dependency: Python dict as in FileParser.parse_file
- return: file name (str/unicode) or None
- """
- if isinstance(file_dependency, dict):
- filelike_dependency = file_dependency.get("File")
- if isinstance(filelike_dependency, dict):
- return filelike_dependency.get("name")
- return None
- return None
-
- def parse_file_checksums(self, file_checksums: List[Dict]) -> Optional[bool]:
- """
- Parse File checksums
- - file_checksums: Python List
- """
- if isinstance(file_checksums, list):
- for checksum in file_checksums:
- self.builder.set_file_checksum(self.document, checksum)
- return True
- if isinstance(file_checksums, str):
- # kept for backwards compatibility
- try:
- return self.builder.set_file_checksum(self.document, file_checksums)
- except CardinalityError:
- self.more_than_one_error("FILE_CHECKSUM")
- except OrderError:
- self.order_error("FILE_CHECKSUM", "FILE_NAME")
- else:
- self.value_error("FILE_CHECKSUM", file_checksums)
-
- def parse_files(self, files: List[Dict]) -> None:
- if files is None:
- return
- if isinstance(files, list):
- for file in files:
- self.parse_file(file)
- else:
- self.value_error("FILES", files)
-
-
-
-class PackageParser(BaseParser):
- def __init__(self, builder, logger):
- super(PackageParser, self).__init__(builder, logger)
-
- @property
- def package(self):
- # current package being parsed is the last one
- return self.document.packages[-1]
-
- def parse_package(self, package: Package, method_to_parse_relationship: Callable):
- """
- Parse Package Information fields
- - package: Python dict with Package Information fields in it
- """
- if isinstance(package, dict):
- # The builder has the notion of current package, here, we force to start a new one
- self.builder.reset_package()
- self.parse_pkg_name(package.get("name"))
- self.parse_pkg_id(package.get("SPDXID"))
- self.parse_pkg_files_analyzed(package.get("filesAnalyzed"))
- self.parse_pkg_version(package.get("versionInfo"))
- self.parse_pkg_file_name(package.get("packageFileName"))
- self.parse_pkg_supplier(package.get("supplier"))
- self.parse_pkg_originator(package.get("originator"))
- self.parse_pkg_down_location(package.get("downloadLocation"))
- self.parse_pkg_verif_code_field(package.get("packageVerificationCode"))
- self.parse_pkg_homepage(package.get("homepage"))
- self.parse_pkg_source_info(package.get("sourceInfo"))
- self.parse_pkg_concluded_license(package.get("licenseConcluded"))
- self.parse_pkg_license_info_from_files(package.get("licenseInfoFromFiles"))
- self.parse_pkg_declared_license(package.get("licenseDeclared"))
- self.parse_pkg_license_comment(package.get("licenseComments"))
- self.parse_pkg_copyright_text(package.get("copyrightText"))
- self.parse_pkg_summary(package.get("summary"))
- self.parse_pkg_comment(package.get("comment"))
- self.parse_pkg_description(package.get("description"))
- self.parse_annotations(package.get("annotations"), spdx_id=package.get("SPDXID"))
- self.parse_pkg_attribution_text(package.get("attributionTexts"))
- self.parse_pkg_files(package.get("hasFiles"), method_to_parse_relationship)
- self.parse_pkg_checksums(package.get("checksums"))
- self.parse_package_external_refs(package.get("externalRefs"))
- self.parse_primary_package_purpose(package.get("primaryPackagePurpose"))
- self.parse_release_date(package.get("releaseDate"))
- self.parse_built_date(package.get("builtDate"))
- self.parse_valid_until_date(package.get("validUntilDate"))
- else:
- self.value_error("PACKAGE", package)
-
- def parse_pkg_name(self, pkg_name):
- """
- Parse Package name
- - pkg_name: Python str/unicode
- """
- if isinstance(pkg_name, str):
- return self.builder.create_package(self.document, pkg_name)
- self.value_error("PKG_NAME", pkg_name)
- return self.builder.create_package(self.document, "dummy_package")
- # pkg_name is set even if it is None or not string. If weren't, the other attributes
- # would be added to the package previously added.
- # Another approach is to skip the whole package itself
-
- def parse_pkg_id(self, pkg_id):
- """
- Parse Package id
- - pkg_id: Python str/unicode
- """
- if isinstance(pkg_id, str):
- try:
- return self.builder.set_pkg_spdx_id(self.document, pkg_id)
- except SPDXValueError:
- self.value_error("PKG_ID", pkg_id)
- except CardinalityError:
- self.more_than_one_error("PKG_ID")
- else:
- self.value_error("PKG_ID", pkg_id)
-
- def parse_pkg_version(self, pkg_version):
- """
- Parse Package version
- - pkg_name: Python str/unicode
- """
- if isinstance(pkg_version, str):
- try:
- return self.builder.set_pkg_vers(self.document, pkg_version)
- except CardinalityError:
- self.more_than_one_error("PKG_VERSION")
- except OrderError:
- self.order_error("PKG_VERSION", "PKG_NAME")
- elif pkg_version is not None:
- self.value_error("PKG_VERSION", pkg_version)
-
- def parse_pkg_file_name(self, pkg_file_name):
- """
- Parse Package file name
- - pkg_file_name: Python str/unicode
- """
- if isinstance(pkg_file_name, str):
- try:
- return self.builder.set_pkg_file_name(self.document, pkg_file_name)
- except CardinalityError:
- self.more_than_one_error("PKG_FILE_NAME")
- except OrderError:
- self.order_error("PKG_FILE_NAME", "PKG_NAME")
- elif pkg_file_name is not None:
- self.value_error("PKG_FILE_NAME", pkg_file_name)
-
- def parse_pkg_supplier(self, pkg_supplier):
- """
- Parse Package supplier
- - pkg_supplier: Python str/unicode
- """
- if isinstance(pkg_supplier, str):
- entity = self.builder.create_entity(self.document, pkg_supplier)
- try:
- return self.builder.set_pkg_supplier(self.document, entity)
- except SPDXValueError:
- self.value_error("PKG_SUPPL_VALUE", pkg_supplier)
- except CardinalityError:
- self.more_than_one_error("PKG_SUPPL_VALUE")
- except OrderError:
- self.order_error("PKG_SUPPL_VALUE", "PKG_NAME")
- elif pkg_supplier is not None:
- self.value_error("PKG_SUPPL_VALUE", pkg_supplier)
-
- def parse_pkg_originator(self, pkg_originator):
- """
- Parse Package originator
- - pkg_originator: Python str/unicode
- """
- if isinstance(pkg_originator, str):
- entity = self.builder.create_entity(self.document, pkg_originator)
- try:
- return self.builder.set_pkg_originator(self.document, entity)
- except SPDXValueError:
- self.value_error("PKG_ORIGINATOR_VALUE", pkg_originator)
- except CardinalityError:
- self.more_than_one_error("PKG_ORIGINATOR_VALUE")
- except OrderError:
- self.order_error("PKG_ORIGINATOR_VALUE", "PKG_NAME")
- elif pkg_originator is not None:
- self.value_error("PKG_ORIGINATOR_VALUE", pkg_originator)
-
- def parse_pkg_down_location(self, pkg_down_location):
- """
- Parse Package download location
- - pkg_down_location: Python str/unicode
- """
- if isinstance(pkg_down_location, str):
- try:
- return self.builder.set_pkg_down_location(
- self.document, pkg_down_location
- )
- except CardinalityError:
- self.more_than_one_error("PKG_DOWN_LOC")
- except OrderError:
- self.order_error("PKG_DOWN_LOC", "PKG_NAME")
- else:
- self.value_error("PKG_DOWN_LOC", pkg_down_location)
-
- def parse_pkg_files_analyzed(self, pkg_files_analyzed):
- """
- Parse Package files analyzed
- - pkg_files_analyzed: Python boolean
- """
- # Files Analyzed optional
- if pkg_files_analyzed is None:
- return
-
- # For XML, this is a string, not a boolean.
- # xmltodict doesn't do this translation for us, so we do it here.
- if isinstance(pkg_files_analyzed, str):
- if pkg_files_analyzed.lower() in ['true', '1']:
- pkg_files_analyzed = True
- elif pkg_files_analyzed.lower() in ['false', '0']:
- pkg_files_analyzed = False
-
- if isinstance(pkg_files_analyzed, bool):
- try:
- return self.builder.set_pkg_files_analyzed(
- self.document, pkg_files_analyzed
- )
- except CardinalityError:
- self.more_than_one_error("PKG_FILES_ANALYZED")
- else:
- self.value_error("PKG_FILES_ANALYZED", pkg_files_analyzed)
-
- def parse_pkg_verif_code_field(self, pkg_verif_code_field):
- """
- Parse Package verification code dict
- - pkg_verif_code_field: Python dict('value':str/unicode, 'excludedFilesNames':list)
- """
- if not self.package.are_files_analyzed:
- if pkg_verif_code_field is not None:
- self.value_error("PKG_VERIF_CODE_FIELD", pkg_verif_code_field)
- return
-
- if isinstance(pkg_verif_code_field, dict):
- self.parse_pkg_verif_exc_files(
- pkg_verif_code_field.get("packageVerificationCodeExcludedFiles")
- )
- return self.parse_pkg_verif_code(pkg_verif_code_field.get("packageVerificationCodeValue"))
- elif pkg_verif_code_field is not None:
- self.value_error("PKG_VERIF_CODE_FIELD", pkg_verif_code_field)
-
- def parse_pkg_verif_code(self, pkg_verif_code):
- """
- Parse Package verification code value
- - pkg_verif_code: Python str/unicode
- """
- if not self.package.are_files_analyzed:
- if pkg_verif_code is not None:
- self.value_error("PKG_VERIF_CODE", pkg_verif_code)
- return
-
- if isinstance(pkg_verif_code, str):
- try:
- return self.builder.set_pkg_verif_code(self.document, pkg_verif_code)
- except CardinalityError:
- self.more_than_one_error("PKG_VERIF_CODE")
- except OrderError:
- self.order_error("PKG_VERIF_CODE", "PKG_NAME")
- else:
- self.value_error("PKG_VERIF_CODE", pkg_verif_code)
-
- def parse_pkg_verif_exc_files(self, pkg_verif_exc_files):
- """
- Parse Package files excluded from verification code
- - pkg_verif_exc_files: Python list of files excluded (str/unicode)
- """
- if isinstance(pkg_verif_exc_files, list):
- for pkg_verif_exc_file in pkg_verif_exc_files:
- if isinstance(pkg_verif_exc_file, str):
- try:
- self.builder.set_pkg_excl_file(
- self.document, pkg_verif_exc_file
- )
- except OrderError:
- self.order_error("PKG_VERIF_EXC_FILE", "PKG_NAME")
- else:
- self.value_error("PKG_VERIF_EXC_FILE", pkg_verif_exc_file)
- elif pkg_verif_exc_files is not None:
- self.value_error("PKG_VERIF_EXC_FILE_FIELD", pkg_verif_exc_files)
-
- def parse_pkg_homepage(self, pkg_homepage):
- """
- Parse Package homepage
- - pkg_homepage: Python str/unicode
- """
- if isinstance(pkg_homepage, str):
- try:
- return self.builder.set_pkg_home(self.document, pkg_homepage)
- except SPDXValueError:
- self.value_error("PKG_HOMEPAGE", pkg_homepage)
- except CardinalityError:
- self.more_than_one_error("PKG_HOMEPAGE")
- except OrderError:
- self.order_error("PKG_HOMEPAGE", "PKG_NAME")
- elif pkg_homepage is not None:
- self.value_error("PKG_HOMEPAGE", pkg_homepage)
-
- def parse_pkg_source_info(self, pkg_source_info):
- """
- Parse Package source information
- - pkg_source_info: Python str/unicode
- """
- if isinstance(pkg_source_info, str):
- try:
- return self.builder.set_pkg_source_info(self.document, pkg_source_info)
- except CardinalityError:
- self.more_than_one_error("PKG_SRC_INFO")
- except OrderError:
- self.order_error("PKG_SRC_INFO", "PKG_NAME")
- elif pkg_source_info is not None:
- self.value_error("PKG_SRC_INFO", pkg_source_info)
-
- def parse_pkg_concluded_license(self, pkg_concluded_license):
- """
- Parse Package concluded license
- - pkg_concluded_license: Python str/unicode
- """
- if isinstance(pkg_concluded_license, str):
- lic_parser = utils.LicenseListParser()
- lic_parser.build(write_tables=0, debug=0)
- license_object = self.replace_license(
- lic_parser.parse(pkg_concluded_license)
- )
- try:
- return self.builder.set_pkg_licenses_concluded(
- self.document, license_object
- )
- except SPDXValueError:
- self.value_error("PKG_SINGLE_LICS", pkg_concluded_license)
- except CardinalityError:
- self.more_than_one_error("PKG_SINGLE_LICS")
- except OrderError:
- self.order_error("PKG_SINGLE_LICS", "PKG_NAME")
- elif pkg_concluded_license is not None:
- self.value_error("PKG_SINGLE_LICS", pkg_concluded_license)
-
- def parse_pkg_license_info_from_files(self, license_info_from_files):
- """
- Parse Package license information from files
- - license_info_from_files: Python list of licenses information from files (str/unicode)
- """
- if not self.package.are_files_analyzed:
- if license_info_from_files is not None:
- self.value_error("PKG_LIC_FRM_FILES", license_info_from_files)
- return
- if isinstance(license_info_from_files, list):
- for license_info_from_file in license_info_from_files:
- if isinstance(license_info_from_file, str):
- lic_parser = utils.LicenseListParser()
- lic_parser.build(write_tables=0, debug=0)
- license_object = self.replace_license(
- lic_parser.parse(license_info_from_file)
- )
- try:
- self.builder.set_pkg_license_from_file(
- self.document, license_object
- )
- except SPDXValueError:
- self.value_error("PKG_LIC_FRM_FILES", license_info_from_file)
- except OrderError:
- self.order_error("PKG_LIC_FRM_FILES", "PKG_NAME")
- else:
- self.value_error("PKG_LIC_FRM_FILES", license_info_from_file)
- elif license_info_from_files is not None:
- self.value_error("PKG_LIC_FRM_FILES_FIELD", license_info_from_files)
-
- def parse_pkg_attribution_text(self, pkg_attribution_texts):
- """
- Parse Package attribution texts
- - pkg_attribution_texts: list in yaml, json and string in xml format
- """
- if isinstance(pkg_attribution_texts, list) or isinstance(
- pkg_attribution_texts, str
- ):
- for pkg_attribution_text in pkg_attribution_texts:
- try:
- return self.builder.set_pkg_attribution_text(
- self.document, pkg_attribution_text
- )
- except CardinalityError:
- self.more_than_one_error("PKG_ATTRIBUTION_TEXT")
- except OrderError:
- self.order_error("PKG_ATTRIBUTION_TEXT", "PKG_NAME")
- else:
- self.value_error("PKG_ATTRIBUTION_TEXT", pkg_attribution_texts)
-
- def parse_pkg_declared_license(self, pkg_declared_license):
- """
- Parse Package license declared
- - pkg_declared_license: Python str/unicode
- """
- if isinstance(pkg_declared_license, str):
- lic_parser = utils.LicenseListParser()
- lic_parser.build(write_tables=0, debug=0)
- license_object = self.replace_license(
- lic_parser.parse(pkg_declared_license)
- )
- try:
- return self.builder.set_pkg_license_declared(
- self.document, license_object
- )
- except SPDXValueError:
- self.value_error("PKG_DECL_LIC", pkg_declared_license)
- except CardinalityError:
- self.more_than_one_error("PKG_DECL_LIC")
- except OrderError:
- self.order_error("PKG_DECL_LIC", "PKG_NAME")
- elif pkg_declared_license is not None:
- self.value_error("PKG_DECL_LIC", pkg_declared_license)
-
- def parse_pkg_license_comment(self, pkg_license_comment):
- """
- Parse Package license comment
- - pkg_license_comment: Python str/unicode
- """
- if isinstance(pkg_license_comment, str):
- try:
- return self.builder.set_pkg_license_comment(
- self.document, pkg_license_comment
- )
- except CardinalityError:
- self.more_than_one_error("PKG_LIC_COMMENT")
- except OrderError:
- self.order_error("PKG_LIC_COMMENT", "PKG_NAME")
- elif pkg_license_comment is not None:
- self.value_error("PKG_LIC_COMMENT", pkg_license_comment)
-
- def parse_pkg_copyright_text(self, pkg_copyright_text):
- """
- Parse Package copyright text
- - pkg_copyright_text: Python str/unicode
- """
- if isinstance(pkg_copyright_text, str):
- try:
- return self.builder.set_pkg_cr_text(self.document, pkg_copyright_text)
- except CardinalityError:
- self.more_than_one_error("PKG_COPYRIGHT_TEXT")
- except OrderError:
- self.order_error("PKG_COPYRIGHT_TEXT", "PKG_NAME")
- elif pkg_copyright_text is not None:
- self.value_error("PKG_COPYRIGHT_TEXT", pkg_copyright_text)
-
- def parse_pkg_summary(self, pkg_summary):
- """
- Parse Package summary
- - pkg_summary: Python str/unicode
- """
- if isinstance(pkg_summary, str):
- try:
- return self.builder.set_pkg_summary(self.document, pkg_summary)
- except CardinalityError:
- self.more_than_one_error("PKG_SUMMARY")
- except OrderError:
- self.order_error("PKG_SUMMARY", "PKG_NAME")
- elif pkg_summary is not None:
- self.value_error("PKG_SUMMARY", pkg_summary)
-
- def parse_pkg_comment(self, pkg_comment):
- """
- Parse Package comment
- - pkg_comment: Python str/unicode
- """
- if isinstance(pkg_comment, str):
- try:
- return self.builder.set_pkg_comment(self.document, pkg_comment)
- except CardinalityError:
- self.more_than_one_error("PKG_COMMENT")
- except OrderError:
- self.order_error("PKG_COMMENT", "PKG_NAME")
- elif pkg_comment is not None:
- self.value_error("PKG_COMMENT", pkg_comment)
-
- def parse_pkg_description(self, pkg_description):
- """
- Parse Package description
- - pkg_description: Python str/unicode
- """
- if isinstance(pkg_description, str):
- try:
- return self.builder.set_pkg_desc(self.document, pkg_description)
- except CardinalityError:
- self.more_than_one_error("PKG_DESCRIPTION")
- except OrderError:
- self.order_error("PKG_DESCRIPTION", "PKG_NAME")
- elif pkg_description is not None:
- self.value_error("PKG_DESCRIPTION", pkg_description)
-
- def parse_pkg_files(self, pkg_has_files: List[str], method_to_parse_relationship: Callable) -> None:
- """
- Parse Package files
- - pkg_has_files: Python list of spdx_ids
- """
- if not self.package.are_files_analyzed:
- if pkg_has_files is not None:
- self.value_error("PKG_FILES", pkg_has_files)
- return
-
- if isinstance(pkg_has_files, list):
- for pkg_file_spdx_id in pkg_has_files:
- if isinstance(pkg_file_spdx_id, str):
- method_to_parse_relationship(self.package.spdx_id, "CONTAINS", pkg_file_spdx_id)
- else:
- self.value_error("PKG_FILE", pkg_file_spdx_id)
- elif pkg_has_files is not None:
- self.value_error("PKG_HAS_FILES", pkg_has_files)
-
- def parse_pkg_checksums(self, pkg_checksums: List[Dict]) -> Optional[bool]:
- """
- Parse Package checksums
- - pkg_chksums: Python List
- """
- if isinstance(pkg_checksums, list):
- for checksum in pkg_checksums:
- self.builder.set_pkg_checksum(self.document, checksum)
- return True
- if isinstance(pkg_checksums, str):
- # kept for backwards compatibility
- try:
- return self.builder.set_pkg_checksum(self.document, pkg_checksums)
- except CardinalityError:
- self.more_than_one_error("PKG_CHECKSUM")
- except OrderError:
- self.order_error("PKG_CHECKSUM", "PKG_NAME")
- elif pkg_checksums is not None:
- self.value_error("PKG_CHECKSUM", pkg_checksums)
-
- def parse_package_external_refs(self, external_refs: List[Dict]):
- if external_refs is None:
- return
- if not isinstance(external_refs, list):
- self.value_error("PACKAGE_EXTERNAL_REFS", external_refs)
- return
-
- for external_ref_dict in external_refs:
- external_ref = ExternalPackageRef(category=external_ref_dict["referenceCategory"],
- pkg_ext_ref_type=external_ref_dict["referenceType"],
- locator=external_ref_dict["referenceLocator"])
- if "comment" in external_ref_dict:
- external_ref.comment = external_ref_dict["comment"]
- self.package.add_pkg_ext_refs(external_ref)
-
- def parse_primary_package_purpose(self, primary_package_purpose: str):
- if primary_package_purpose is None:
- return
- primary_package_purpose = primary_package_purpose.replace("-", "_") # OPERATING-SYSTEM -> OPERATING_SYSTEM
- if primary_package_purpose not in [purpose.name for purpose in PackagePurpose]:
- self.value_error("PRIMARY_PACKAGE_PURPOSE", primary_package_purpose)
- return
-
- purpose_enum = PackagePurpose[primary_package_purpose]
- self.package.primary_package_purpose = purpose_enum
-
- def parse_release_date(self, release_date: str):
- if release_date is None:
- return
-
- parsed_date: datetime = utils.datetime_from_iso_format(release_date)
- if parsed_date is not None:
- self.package.release_date = parsed_date
- else:
- self.value_error("RELEASE_DATE", release_date)
-
- def parse_built_date(self, built_date: str):
- if built_date is None:
- return
-
- parsed_date: datetime = utils.datetime_from_iso_format(built_date)
- if parsed_date is not None:
- self.package.built_date = parsed_date
- else:
- self.value_error("BUILT_DATE", built_date)
-
- def parse_valid_until_date(self, valid_until_date: str):
- if valid_until_date is None:
- return
-
- parsed_date: datetime = utils.datetime_from_iso_format(valid_until_date)
- if parsed_date is not None:
- self.package.valid_until_date = parsed_date
- else:
- self.value_error("VALID_UNTIL_DATE", valid_until_date)
-
-def flatten_document(document):
- """
- Flatten document to match current data model. File objects are nested within packages according to hasFiles-tag.
- """
- files_by_id = {}
- if "files" in document:
- for f in document.get("files"):
- for checksum in f["checksums"]:
- if checksum["algorithm"] == "SHA1" or "sha1" in checksum["algorithm"]:
- f["sha1"] = checksum["checksumValue"]
- break
- files_by_id[f["SPDXID"]] = f
- if "packages" in document:
- packages = document.get("packages")
- for package in packages:
- if "hasFiles" in package:
- package["files"] = [{
- "File": files_by_id[spdxid.split("#")[-1]]} for spdxid in package["hasFiles"]
- ]
- for checksum in package.get("checksums", []):
- if checksum["algorithm"] == "SHA1" or "sha1" in checksum["algorithm"]:
- package["sha1"] = checksum["checksumValue"]
- break
-
- return document
-
-
-class Parser(
- CreationInfoParser,
- ExternalDocumentRefsParser,
- LicenseParser,
- AnnotationParser,
- RelationshipParser,
- SnippetParser,
- ReviewParser,
- FileParser,
- PackageParser,
-):
- def __init__(self, builder, logger):
- super(Parser, self).__init__(builder, logger)
-
- def json_yaml_set_document(self, data):
- # we could verify that the spdxVersion >= 2.2, but we try to be resilient in parsing
- if data.get("spdxVersion"):
- self.document_object = data
- return
- self.document_object = data.get("Document")
-
- def parse(self):
- """
- Parse Document Information fields
- """
- self.error = False
- self.document = document.Document()
- self.document_object = flatten_document(self.document_object)
- if not isinstance(self.document_object, dict):
- self.logger.log("Empty or not valid SPDX Document")
- self.error = True
- return self.document, self.error
-
- self.parse_doc_version(self.document_object.get("spdxVersion"))
- self.parse_doc_data_license(self.document_object.get("dataLicense"))
- self.parse_doc_id(self.document_object.get("SPDXID"))
- self.parse_doc_name(self.document_object.get("name"))
- self.parse_doc_namespace(self.document_object.get("documentNamespace"))
- self.parse_doc_comment(self.document_object.get("comment"))
- self.parse_creation_info(self.document_object.get("creationInfo"))
- self.parse_external_document_refs(
- self.document_object.get("externalDocumentRefs")
- )
- self.parse_extracted_license_info(
- self.document_object.get("hasExtractedLicensingInfos")
- )
- self.parse_annotations(self.document_object.get("annotations"), spdx_id=self.document_object.get("SPDXID"))
- self.parse_relationships(self.document_object.get("relationships"))
- self.parse_reviews(self.document_object.get("reviewers"))
- self.parse_snippets(self.document_object.get("snippets"))
-
- self.parse_packages(self.document_object.get("packages"))
- self.parse_files(self.document_object.get("files"))
-
- if self.document_object.get("documentDescribes"):
- self.parse_doc_described_objects(self.document_object.get("documentDescribes"))
-
- validation_messages = ErrorMessages()
- # Report extra errors if self.error is False otherwise there will be
- # redundant messages
- validation_messages = self.document.validate(validation_messages)
- if not self.error:
- if validation_messages:
- for msg in validation_messages:
- self.logger.log(msg)
- self.error = True
-
- return self.document, self.error
-
- def parse_doc_version(self, doc_version):
- """
- Parse Document version
- - doc_version: Python str/unicode
- """
- if isinstance(doc_version, str):
- try:
- return self.builder.set_doc_version(self.document, doc_version)
- except SPDXValueError:
- self.value_error("DOC_VERS_VALUE", doc_version)
- except CardinalityError:
- self.more_than_one_error("DOC_VERS_VALUE")
- else:
- self.value_error("DOC_VERS_VALUE", doc_version)
-
- def parse_doc_data_license(self, doc_data_license):
- """
- Parse Document data license
- - doc_data_license: Python str/unicode
- """
- try:
- return self.builder.set_doc_data_lics(self.document, doc_data_license)
- except SPDXValueError:
- self.value_error("DOC_D_LICS", doc_data_license)
- except CardinalityError:
- self.more_than_one_error("DOC_D_LICS")
-
- def parse_doc_id(self, doc_id):
- """
- Parse Document SPDX id
- - doc_id: Python str/unicode
- """
- if isinstance(doc_id, str):
- try:
- return self.builder.set_doc_spdx_id(self.document, doc_id)
- except SPDXValueError:
- self.value_error("DOC_SPDX_ID_VALUE", doc_id)
- except CardinalityError:
- self.more_than_one_error("DOC_SPDX_ID_VALUE")
- else:
- self.value_error("DOC_SPDX_ID_VALUE", doc_id)
-
- def parse_doc_name(self, doc_name):
- """
- Parse Document name
- - doc_name: Python str/unicode
- """
- if isinstance(doc_name, str):
- try:
- return self.builder.set_doc_name(self.document, doc_name)
- except CardinalityError:
- self.more_than_one_error("DOC_NAME_VALUE")
- else:
- self.value_error("DOC_NAME_VALUE", doc_name)
-
- def parse_doc_namespace(self, doc_namespace):
- """
- Parse Document namespace
- - doc_namespace: Python str/unicode
- """
- if isinstance(doc_namespace, str):
- try:
- return self.builder.set_doc_namespace(self.document, doc_namespace)
- except SPDXValueError:
- self.value_error("DOC_NAMESPACE_VALUE", doc_namespace)
- except CardinalityError:
- self.more_than_one_error("DOC_NAMESPACE_VALUE")
- else:
- self.value_error("DOC_NAMESPACE_VALUE", doc_namespace)
-
- def parse_doc_comment(self, doc_comment):
- """
- Parse Document comment
- - doc_comment: Python str/unicode
- """
- if isinstance(doc_comment, str):
- try:
- return self.builder.set_doc_comment(self.document, doc_comment)
- except CardinalityError:
- self.more_than_one_error("DOC_COMMENT_VALUE")
- elif doc_comment is not None:
- self.value_error("DOC_COMMENT_VALUE", doc_comment)
-
- def parse_doc_described_objects(self, doc_described_objects):
- """
- Parse Document documentDescribes (SPDXIDs)
- - doc_described_objects: Python list of strings
- """
- if isinstance(doc_described_objects, list):
- described_spdxids = filter(
- lambda described: isinstance(described, str), doc_described_objects
- )
- for spdxid in described_spdxids:
- self.parse_relationship(self.document.spdx_id, "DESCRIBES", spdxid)
-
- return True
- else:
- self.value_error("DOC_DESCRIBES", doc_described_objects)
-
- def parse_packages(self, packages):
- """
- Parse SPDXLite packages list
- """
- if packages is None:
- return
- if isinstance(packages, list):
- for package in packages:
- self.parse_package(package, self.parse_relationship)
- return True
- else:
- self.value_error("PACKAGES", packages)
diff --git a/spdx/parsers/jsonyamlxmlbuilders.py b/spdx/parsers/jsonyamlxmlbuilders.py
deleted file mode 100644
index 94d464edc..000000000
--- a/spdx/parsers/jsonyamlxmlbuilders.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-from typing import Dict, Union
-
-from spdx.document import Document
-from spdx.parsers import rdfbuilders
-from spdx.parsers import tagvaluebuilders
-from spdx.parsers import validations
-from spdx.checksum import Checksum, ChecksumAlgorithm
-from spdx.parsers.builderexceptions import SPDXValueError
-from spdx.parsers.builderexceptions import CardinalityError
-from spdx.parsers.builderexceptions import OrderError
-
-
-class CreationInfoBuilder(rdfbuilders.CreationInfoBuilder):
- def __init__(self):
- super(CreationInfoBuilder, self).__init__()
-
-
-class ExternalDocumentRefsBuilder(rdfbuilders.ExternalDocumentRefBuilder):
- def __init__(self):
- super(ExternalDocumentRefsBuilder, self).__init__()
-
-
-class EntityBuilder(rdfbuilders.EntityBuilder):
- def __init__(self):
- super(EntityBuilder, self).__init__()
-
-
-class SnippetBuilder(rdfbuilders.SnippetBuilder):
- def __init__(self):
- super(SnippetBuilder, self).__init__()
-
-
-class ReviewBuilder(rdfbuilders.ReviewBuilder):
- def __init__(self):
- super(ReviewBuilder, self).__init__()
-
-
-class PackageBuilder(rdfbuilders.PackageBuilder):
- def __init__(self):
- super(PackageBuilder, self).__init__()
-
-
-class DocBuilder(tagvaluebuilders.DocBuilder):
- def __init__(self):
- super(DocBuilder, self).__init__()
-
- def set_doc_spdx_id(self, doc, doc_spdx_id_line):
- """
- Set the document SPDX Identifier.
- Raise SPDXValueError if malformed value, CardinalityError
- if already defined.
- """
- if not self.doc_spdx_id_set:
- if (
- doc_spdx_id_line == "SPDXRef-DOCUMENT"
- or validations.validate_doc_spdx_id(doc_spdx_id_line)
- ):
- doc.spdx_id = doc_spdx_id_line
- self.doc_spdx_id_set = True
- return True
- else:
- raise SPDXValueError("Document::SPDXID")
- else:
- raise CardinalityError("Document::SPDXID")
-
- def set_doc_comment(self, doc, comment):
- """
- Set document comment.
- Raise CardinalityError if comment already set.
- """
- if not self.doc_comment_set:
- self.doc_comment_set = True
- doc.comment = comment
- else:
- raise CardinalityError("Document::Comment")
-
- def set_doc_namespace(self, doc, namespace):
- """
- Set the document namespace.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if already defined.
- """
- if not self.doc_namespace_set:
- self.doc_namespace_set = True
- if validations.validate_doc_namespace(namespace):
- doc.namespace = namespace
- return True
- else:
- raise SPDXValueError("Document::Namespace")
- else:
- raise CardinalityError("Document::Comment")
-
-
-class LicenseBuilder(tagvaluebuilders.LicenseBuilder):
- def __init__(self):
- super(LicenseBuilder, self).__init__()
-
- def set_lic_name(self, doc, name):
- """
- Set license name.
- Raise SPDXValueError if name is not str or utils.NoAssert
- Raise CardinalityError if it is already set
- Raise OrderError if no license id defined.
- """
- if self.has_extr_lic(doc):
- if not self.extr_lic_name_set:
- self.extr_lic_name_set = True
- if validations.validate_extr_lic_name(name, True):
- self.extr_lic(doc).full_name = name
- return True
- else:
- raise SPDXValueError("ExtractedLicense::Name")
- else:
- raise CardinalityError("ExtractedLicense::Name")
- else:
- raise OrderError("ExtractedLicense::Name")
-
- def set_lic_text(self, doc, text):
- """
- Set license name.
- Raise CardinalityError if it is already set.
- Raise OrderError if no license id defined.
- """
- if self.has_extr_lic(doc):
- if not self.extr_text_set:
- self.extr_text_set = True
- self.extr_lic(doc).text = text
- return True
- else:
- raise CardinalityError("ExtractedLicense::text")
- else:
- raise OrderError("ExtractedLicense::text")
-
- def set_lic_comment(self, doc, comment):
- """
- Set license comment.
- Raise CardinalityError if it is already set.
- Raise OrderError if no license ID defined.
- """
- if self.has_extr_lic(doc):
- if not self.extr_lic_comment_set:
- self.extr_lic_comment_set = True
- self.extr_lic(doc).comment = comment
- return True
- else:
- raise CardinalityError("ExtractedLicense::comment")
- else:
- raise OrderError("ExtractedLicense::comment")
-
-
-class FileBuilder(rdfbuilders.FileBuilder):
- def __init__(self):
- super(FileBuilder, self).__init__()
-
- def set_file_checksum(self, doc: Document, checksum: Union[Dict, Checksum, str]) -> bool:
- """
- Set the file checksum.
- checksum - A string
- raise OrderError if no file defined.
- """
- if not self.has_file(doc):
- raise OrderError("No file for checksum defined.")
-
- if isinstance(checksum, dict):
- algo = checksum.get('algorithm') or 'SHA1'
- identifier = ChecksumAlgorithm.checksum_algorithm_from_string(algo)
- self.file(doc).set_checksum(Checksum(identifier, checksum.get('checksumValue')))
- elif isinstance(checksum, Checksum):
- self.file(doc).set_checksum(checksum)
- elif isinstance(checksum, str):
- self.file(doc).set_checksum(Checksum(ChecksumAlgorithm.SHA1, checksum))
- return True
-
- def set_file_notice(self, doc, text):
- """
- Set file notice
- Raise OrderError if no file defined.
- Raise CardinalityError if more than one.
- """
- if not self.has_file(doc):
- raise OrderError("File::Notice")
-
- self.file_notice_set = True
- self.file(doc).notice = text
- return True
-
- def set_file_type(self, doc, type_value):
- """
- Wrap rdfbuilders.FileBuilder.set_file_type to match the different
- fileType representations.
- This method does not make much sense as it converts the file type (e.g. SOURCE)
- to rdf format (e.g. fileType_source) which is then converted back.
- But this seems to be the kind of workflow that is currently in use here.
- """
-
- return super(FileBuilder, self).set_file_type(doc, f"namespace#fileType_{type_value.lower()}")
-
- def set_file_copyright(self, doc, text):
- """
- Raise OrderError if no file defined.
- Raise CardinalityError if more than one.
- """
- if not self.has_file(doc):
- raise OrderError("File::CopyRight")
- if self.file_copytext_set:
- raise CardinalityError("File::CopyRight")
- self.file_copytext_set = True
- self.file(doc).copyright = text
- return True
-
- def set_file_license_comment(self, doc, text):
- """
- Raise OrderError if no file defined.
- Raise CardinalityError if more than one per file.
- """
- if not self.has_file(doc):
- raise OrderError("File::LicenseComment")
- if self.file_license_comment_set:
- raise CardinalityError("File::LicenseComment")
- self.file(doc).license_comment = text
- return True
-
- def set_file_attribution_text(self, doc, text):
- """
- Set the file's attribution text.
- """
- if self.has_file(doc):
- self.file(doc).attribution_text = text
- return True
-
- def set_file_comment(self, doc, text):
- """
- Raise OrderError if no file defined.
- Raise CardinalityError if more than one comment set.
- """
- if not self.has_file(doc):
- raise OrderError("File::Comment")
- if self.file_comment_set:
- raise CardinalityError("File::Comment")
- self.file_comment_set = True
- self.file(doc).comment = text
- return True
-
-
-class AnnotationBuilder(tagvaluebuilders.AnnotationBuilder):
- def __init__(self):
- super(AnnotationBuilder, self).__init__()
-
- def add_annotation_comment(self, doc, comment):
- """
- Set the annotation comment.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- """
- if len(doc.annotations) != 0:
- if not self.annotation_comment_set:
- self.annotation_comment_set = True
- doc.annotations[-1].comment = comment
- return True
- else:
- raise CardinalityError("AnnotationComment")
- else:
- raise OrderError("AnnotationComment")
-
-
-class RelationshipBuilder(tagvaluebuilders.RelationshipBuilder):
- def __init__(self):
- super(RelationshipBuilder, self).__init__()
-
- def add_relationship_comment(self, doc, comment):
- """
- Set the relationship comment.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- """
- if len(doc.relationships) != 0:
- if not self.relationship_comment_set:
- self.relationship_comment_set = True
- doc.relationships[-1].comment = comment
- return True
- else:
- raise CardinalityError("RelationshipComment")
- else:
- raise OrderError("RelationshipComment")
-
-
-class Builder(
- DocBuilder,
- CreationInfoBuilder,
- ExternalDocumentRefsBuilder,
- EntityBuilder,
- SnippetBuilder,
- ReviewBuilder,
- LicenseBuilder,
- FileBuilder,
- PackageBuilder,
- AnnotationBuilder,
- RelationshipBuilder,
-):
- """
- SPDX document builder.
- """
-
- def __init__(self):
- super(Builder, self).__init__()
- # FIXME: this state does not make sense
- self.reset()
-
- def reset(self):
- """
- Reset builder's state for building new documents.
- Must be called between usage with different documents.
- """
- # FIXME: this state does not make sense
- self.reset_creation_info()
- self.reset_document()
- self.reset_package()
- self.reset_file_stat()
- self.reset_reviews()
- self.reset_annotations()
- self.reset_relationship()
- self.reset_extr_lics()
diff --git a/spdx/parsers/loggers.py b/spdx/parsers/loggers.py
deleted file mode 100644
index a74715f77..000000000
--- a/spdx/parsers/loggers.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-class StandardLogger(object):
- def log(self, msg):
- print(msg)
-
-
-class FileLogger(object):
- def __init__(self, logfile):
- self.dest = logfile
-
- def log(self, msg):
- self.dest.write(msg + "\n")
-
-
-class ErrorMessages:
-
- def __init__(self):
- self.messages = []
- self.context = []
-
- def push_context(self, context):
- """push some context information to better identify where is the problem"""
- self.context.append(context)
-
- def pop_context(self):
- """pop the last context information"""
- self.context.pop()
-
- def append(self, message, *args, **kwargs):
- """add a message with standard python format
- the current context is prefixed to the message
- """
- message = message.format(*args, **kwargs)
- message = "".join([c + ": " for c in self.context if c]) + message
- self.messages.append(message)
-
- def __iter__(self):
- return self.messages.__iter__()
-
- def __bool__(self):
- return len(self.messages)>0
-
- def __nonzero__(self):
- return len(self.messages)>0
-
- def __eq__(self, b):
- if isinstance(b, ErrorMessages):
- return self.messages == b.messages
- return self.messages == b
diff --git a/spdx/parsers/parse_anything.py b/spdx/parsers/parse_anything.py
deleted file mode 100644
index 329ea6944..000000000
--- a/spdx/parsers/parse_anything.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (c) spdx contributors
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-import spdx.file as spdxfile
-from spdx.parsers import jsonparser
-from spdx.parsers import yamlparser
-from spdx.parsers import rdf
-from spdx.parsers import xmlparser
-from spdx.parsers import tagvalue
-from spdx.parsers.loggers import StandardLogger
-from spdx.parsers import jsonyamlxmlbuilders, tagvaluebuilders, rdfbuilders
-from spdx.parsers.builderexceptions import FileTypeError
-
-
-def parse_file(fn):
- builder_module = jsonyamlxmlbuilders
- read_data = False
- if fn.endswith(".rdf") or fn.endswith(".rdf.xml"):
- parsing_module = rdf
- builder_module = rdfbuilders
- elif fn.endswith(".tag") or fn.endswith(".spdx"):
- parsing_module = tagvalue
- builder_module = tagvaluebuilders
- read_data = True
- elif fn.endswith(".json"):
- parsing_module = jsonparser
- elif fn.endswith(".xml"):
- parsing_module = xmlparser
- elif fn.endswith(".yaml") or fn.endswith(".yml"):
- parsing_module = yamlparser
- else:
- raise FileTypeError("FileType Not Supported" + str(fn))
-
- p = parsing_module.Parser(builder_module.Builder(), StandardLogger())
- if hasattr(p, "build"):
- p.build()
- with open(fn) as f:
- if read_data:
- data = f.read()
- return p.parse(data)
- else:
- return p.parse(f)
diff --git a/spdx/parsers/rdf.py b/spdx/parsers/rdf.py
deleted file mode 100644
index c78d3ca56..000000000
--- a/spdx/parsers/rdf.py
+++ /dev/null
@@ -1,1518 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import re
-
-from functools import reduce
-
-from rdflib import Graph
-from rdflib import Namespace
-from rdflib import RDF
-from rdflib import RDFS
-
-from spdx import document
-from spdx import license
-from spdx import utils
-from spdx.checksum import Checksum, ChecksumAlgorithm
-from spdx.parsers.builderexceptions import CardinalityError
-from spdx.parsers.builderexceptions import SPDXValueError
-from spdx.parsers.loggers import ErrorMessages
-
-
-ERROR_MESSAGES = {
- "DOC_VERS_VALUE": "Invalid specVersion '{0}' must be SPDX-M.N where M and N are numbers.",
- "DOC_D_LICS": "Invalid dataLicense '{0}' must be http://spdx.org/licenses/CC0-1.0.",
- "DOC_SPDX_ID_VALUE": "Invalid SPDXID value, SPDXID must be the document namespace appended "
- 'by "#SPDXRef-DOCUMENT", line: {0}',
- "DOC_NAMESPACE_VALUE": 'Invalid DocumentNamespace value {0}, must contain a scheme (e.g. "https:") '
- 'and should not contain the "#" delimiter.',
- "LL_VALUE": "Invalid licenseListVersion '{0}' must be of the format N.N where N is a number",
- "CREATED_VALUE": "Invalid created value '{0}' must be date in ISO 8601 format.",
- "CREATOR_VALUE": "Invalid creator value '{0}' must be Organization, Tool or Person.",
- "EXT_DOC_REF_VALUE": "Failed to extract {0} from ExternalDocumentRef.",
- "PKG_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing '
- 'letters, numbers, ".", "-".',
- "PKG_SUPPL_VALUE": "Invalid package supplier value '{0}' must be Organization, Person or NOASSERTION.",
- "PKG_ORIGINATOR_VALUE": "Invalid package supplier value '{0}' must be Organization, Person or NOASSERTION.",
- "PKG_DOWN_LOC": "Invalid package download location value '{0}' must be a url or NONE or NOASSERTION",
- "PKG_FILES_ANALYZED_VALUE": "FilesAnalyzed must be a boolean value, line: {0}",
- "PKG_CONC_LIST": "Package concluded license list must have more than one member",
- "LICS_LIST_MEMBER": "Declarative or Conjunctive license set member must be a license url or identifier",
- "PKG_SINGLE_LICS": "Package concluded license must be a license url or spdx:noassertion or spdx:none.",
- "PKG_LICS_INFO_FILES": "Package licenseInfoFromFiles must be a license or spdx:none or spdx:noassertion",
- "FILE_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing '
- 'letters, numbers, ".", "-".',
- "PKG_EXT_REF_CATEGORY": '\'{0}\' must be "SECURITY", "PACKAGE-MANAGER", or "OTHER".',
- "PKG_EXT_REF_TYPE": '{0} must be a unique string containing letters, numbers, ".", or "-".',
- "FILE_TYPE": "Unknown file type.",
- "FILE_SINGLE_LICS": "File concluded license must be a license url or spdx:noassertion or spdx:none.",
- "REVIEWER_VALUE": "Invalid reviewer value '{0}' must be Organization, Tool or Person.",
- "REVIEW_DATE": "Invalid review date value '{0}' must be date in ISO 8601 format.",
- "ANNOTATOR_VALUE": "Invalid annotator value '{0}' must be Organization, Tool or Person.",
- "ANNOTATION_DATE": "Invalid annotation date value '{0}' must be date in ISO 8601 format.",
- "SNIPPET_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string '
- 'containing letters, numbers, ".", "-".',
- "SNIPPET_SINGLE_LICS": "Snippet Concluded License must be a license url or spdx:noassertion or spdx:none.",
- "SNIPPET_LIC_INFO": "License Information in Snippet must be a license url or a reference "
- "to the license, denoted by LicenseRef-[idstring] or spdx:noassertion or spdx:none.",
- "RELATIONSHIP": "relationship type must be of supported type",
-}
-
-
-def convert_rdf_checksum_algorithm(rdf_checksum_algorithm: str) -> ChecksumAlgorithm:
- split_string = rdf_checksum_algorithm.split('#')
- if len(split_string) != 2:
- raise SPDXValueError('Unknown checksum algorithm {}'.format(rdf_checksum_algorithm))
- checksum_algorithm = ChecksumAlgorithm.checksum_from_rdf(split_string[1])
- return checksum_algorithm
-
-
-class BaseParser(object):
- """
- Base class for all parsers.
- Contains logger, doap_namespace, spdx_namespace and model builder.
- Also provides utility functions used by the deriving parsers.
- """
-
- def __init__(self, builder, logger):
- self.logger = logger
- self.doap_namespace = Namespace("http://usefulinc.com/ns/doap#")
- self.spdx_namespace = Namespace("http://spdx.org/rdf/terms#")
- self.builder = builder
-
- def more_than_one_error(self, field):
- """
- Logs a more than one error.
- field is the field/property that has more than one defined.
- """
- msg = "More than one {0} defined.".format(field)
- self.logger.log(msg)
- self.error = True
-
- def value_error(self, key, bad_value):
- """
- Report a value error using ERROR_MESSAGES dict.
- key - key to use for ERROR_MESSAGES.
- bad_value - is passed to format which is called on what key maps to
- in ERROR_MESSAGES.
- """
- msg = ERROR_MESSAGES[key].format(bad_value)
- self.logger.log(msg)
- self.error = True
-
- def to_special_value(self, value):
- """
- Check if value is a special SPDX value such as
- NONE, NOASSERTION or UNKNOWN if so returns proper model.
- else returns value
- """
- if value == self.spdx_namespace.none:
- return utils.SPDXNone()
- elif value == self.spdx_namespace.noassertion:
- return utils.NoAssert()
- elif value == self.spdx_namespace.unknown:
- return utils.UnKnown()
- else:
- return str(value)
-
-
-class LicenseParser(BaseParser):
- """
- Helper class for parsing extracted licenses and license lists.
- """
-
- LICS_REF_REGEX = re.compile("LicenseRef-.+", re.UNICODE)
-
- def __init__(self, builder, logger):
- super(LicenseParser, self).__init__(builder, logger)
-
- def handle_lics(self, lics):
- """
- Return a License from a `lics` license resource.
- """
- # Handle extracted licensing info type.
- if (
- lics,
- RDF.type,
- self.spdx_namespace["ExtractedLicensingInfo"],
- ) in self.graph:
- return self.parse_only_extr_license(lics)
-
- # Assume resource, hence the path separator
- ident_start = lics.rfind("/") + 1
- if ident_start == 0:
- # special values such as spdx:noassertion
- special = self.to_special_value(lics)
- if special == lics:
- if self.LICS_REF_REGEX.match(lics):
- # Is a license ref i.e LicenseRef-1
- return license.License.from_identifier(str(lics))
- else:
- # Not a known license form
- raise SPDXValueError("License")
- else:
- # is a special value
- return special
- else:
- # license url
- return license.License.from_identifier(lics[ident_start:])
-
- def get_extr_license_ident(self, extr_lic):
- """
- Return a license identifier from an ExtractedLicense or None.
- """
- identifier_triples = list(
- self.graph.triples((extr_lic, self.spdx_namespace["licenseId"], None))
- )
-
- if not identifier_triples:
- self.error = True
- msg = "Extracted license must have licenseId property."
- self.logger.log(msg)
- return
-
- if len(identifier_triples) > 1:
- self.more_than_one_error("extracted license identifier_triples")
- return
-
- identifier_triple = identifier_triples[0]
- _s, _p, identifier = identifier_triple
- return str(identifier)
-
- def get_extr_license_text(self, extr_lic):
- """
- Return extracted text from an ExtractedLicense or None.
- """
- text_triples = list(
- self.graph.triples((extr_lic, self.spdx_namespace["extractedText"], None))
- )
- if not text_triples:
- self.error = True
- msg = "Extracted license must have extractedText property"
- self.logger.log(msg)
- return
-
- if len(text_triples) > 1:
- self.more_than_one_error("extracted license text")
- return
-
- text_triple = text_triples[0]
- _s, _p, text = text_triple
- return str(text)
-
- def get_extr_lic_name(self, extr_lic):
- """
- Return the license name from an ExtractedLicense or None
- """
- extr_name_list = list(
- self.graph.triples((extr_lic, self.spdx_namespace["licenseName"], None))
- )
- if len(extr_name_list) > 1:
- self.more_than_one_error("extracted license name")
- return
- elif len(extr_name_list) == 0:
- return
- return str(self.to_special_value(extr_name_list[0][2]))
-
- def get_extr_lics_xref(self, extr_lic):
- """
- Return a list of cross references.
- """
- xrefs = list(self.graph.triples((extr_lic, RDFS.seeAlso, None)))
- return list(map(lambda xref_triple: xref_triple[2], xrefs))
-
- def get_extr_lics_comment(self, extr_lics):
- """
- Return license comment or None.
- """
- comment_list = list(self.graph.triples((extr_lics, RDFS.comment, None)))
- if len(comment_list) > 1:
- self.more_than_one_error("extracted license comment")
- return
- elif len(comment_list) == 1:
- return str(comment_list[0][2])
- else:
- return
-
- def parse_only_extr_license(self, extr_lic):
- """
- Return an ExtractedLicense object to represent a license object.
- But does not add it to the SPDXDocument model.
- Return None if failed.
- """
- # Grab all possible values
- ident = self.get_extr_license_ident(extr_lic)
- text = self.get_extr_license_text(extr_lic)
- comment = self.get_extr_lics_comment(extr_lic)
- xrefs = self.get_extr_lics_xref(extr_lic)
- name = self.get_extr_lic_name(extr_lic)
-
- if not ident:
- # Must have identifier
- return
-
- # Set fields
- # FIXME: the constructor of the license should always accept a name
- lic = license.ExtractedLicense(ident)
- if text is not None:
- lic.text = text
- if name is not None:
- lic.full_name = name
- if comment is not None:
- lic.comment = comment
- lic.cross_ref = list(map(lambda x: str(x), xrefs))
- return lic
-
- def handle_extracted_license(self, extr_lic):
- """
- Build and return an ExtractedLicense or None.
- Note that this function adds the license to the document.
- """
- lic = self.parse_only_extr_license(extr_lic)
- if lic is not None:
- self.doc.add_extr_lic(lic)
- return lic
-
- def _handle_license_list(self, lics_set, cls=None):
- """
- Return a license representing a `cls` object (LicenseConjunction
- or LicenseDisjunction) from a list of license resources or None.
- """
- licenses = []
- for _, _, lics_member in self.graph.triples(
- (lics_set, self.spdx_namespace["member"], None)
- ):
- try:
- licenses.append(self.handle_lics(lics_member))
- except CardinalityError:
- self.value_error("LICS_LIST_MEMBER", lics_member)
- break
- if len(licenses) > 1:
- return reduce(lambda a, b: cls(a, b), licenses)
- else:
- self.value_error("PKG_CONC_LIST", "")
- return
-
- def handle_conjunctive_list(self, lics_set):
- """
- Return a license representing the conjunction from a list of
- license resources or None.
- """
- return self._handle_license_list(lics_set, cls=license.LicenseConjunction)
-
- def handle_disjunctive_list(self, lics_set):
- """
- Return a license representing the disjunction from a list of
- license resources or None.
- """
- return self._handle_license_list(lics_set, cls=license.LicenseDisjunction)
-
-
-class PackageParser(LicenseParser):
- """
- Helper class for parsing packages.
- """
-
- def __init__(self, builder, logger):
- super(PackageParser, self).__init__(builder, logger)
-
- def parse_package(self, p_term):
- """
- Parse package fields.
- """
- # Check there is a package name
- if not (p_term, self.spdx_namespace["name"], None) in self.graph:
- self.error = True
- self.logger.log("Package must have a name.")
- # Create dummy package so that we may continue parsing the rest of
- # the package fields.
- self.builder.create_package(self.doc, "dummy_package")
- else:
- for _s, _p, o in self.graph.triples(
- (p_term, self.spdx_namespace["name"], None)
- ):
- try:
- self.builder.create_package(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("Package name")
- break
- # Set SPDXID
- try:
- if p_term.count("#", 0, len(p_term)) == 1:
- pkg_spdx_id = p_term.split("#")[-1]
- self.builder.set_pkg_spdx_id(self.doc, pkg_spdx_id)
- else:
- self.value_error("PKG_SPDX_ID_VALUE", p_term)
- except SPDXValueError:
- self.value_error("PKG_SPDX_ID_VALUE", p_term)
-
- self.p_pkg_vinfo(p_term, self.spdx_namespace["versionInfo"])
- self.p_pkg_fname(p_term, self.spdx_namespace["packageFileName"])
- self.p_pkg_suppl(p_term, self.spdx_namespace["supplier"])
- self.p_pkg_originator(p_term, self.spdx_namespace["originator"])
- self.p_pkg_down_loc(p_term, self.spdx_namespace["downloadLocation"])
- self.p_pkg_files_analyzed(p_term, self.spdx_namespace["filesAnalyzed"])
- self.p_pkg_homepg(p_term, self.doap_namespace["homepage"])
- self.p_pkg_checksum(p_term, self.spdx_namespace["checksum"])
- self.p_pkg_src_info(p_term, self.spdx_namespace["sourceInfo"])
- self.p_pkg_verif_code(p_term, self.spdx_namespace["packageVerificationCode"])
- self.p_pkg_attribution_text(p_term, self.spdx_namespace["attributionText"])
- self.p_pkg_lic_conc(p_term, self.spdx_namespace["licenseConcluded"])
- self.p_pkg_lic_decl(p_term, self.spdx_namespace["licenseDeclared"])
- self.p_pkg_lics_info_from_files(
- p_term, self.spdx_namespace["licenseInfoFromFiles"]
- )
- self.p_pkg_comments_on_lics(p_term, self.spdx_namespace["licenseComments"])
- self.p_pkg_cr_text(p_term, self.spdx_namespace["copyrightText"])
- self.p_pkg_summary(p_term, self.spdx_namespace["summary"])
- self.p_pkg_descr(p_term, self.spdx_namespace["description"])
- self.p_pkg_comment(p_term, self.spdx_namespace["comment"])
-
- def p_pkg_cr_text(self, p_term, predicate):
- try:
- for _, _, text in self.graph.triples((p_term, predicate, None)):
- self.builder.set_pkg_cr_text(
- self.doc, str(self.to_special_value(text))
- )
- except CardinalityError:
- self.more_than_one_error("package copyright text")
-
- def p_pkg_summary(self, p_term, predicate):
- try:
- for _, _, summary in self.graph.triples((p_term, predicate, None)):
- self.builder.set_pkg_summary(self.doc, str(summary))
- except CardinalityError:
- self.more_than_one_error("package summary")
-
- def p_pkg_descr(self, p_term, predicate):
- try:
- for _, _, desc in self.graph.triples((p_term, predicate, None)):
- self.builder.set_pkg_desc(self.doc, str(desc))
- except CardinalityError:
- self.more_than_one_error("package description")
-
- def p_pkg_comment(self, p_term, predicate):
- try:
- for _, _, comment in self.graph.triples((p_term, predicate, None)):
- self.builder.set_pkg_comment(self.doc, str(comment))
- except CardinalityError:
- self.more_than_one_error("package comment")
-
- def p_pkg_attribution_text(self, p_term, predicate):
- try:
- for _, _, attribute_text in self.graph.triples((p_term, predicate, None)):
- self.builder.set_pkg_attribution_text(
- self.doc, str(attribute_text)
- )
- except CardinalityError:
- self.more_than_one_error("package attribution text")
-
- def p_pkg_comments_on_lics(self, p_term, predicate):
- for _, _, comment in self.graph.triples((p_term, predicate, None)):
- try:
- self.builder.set_pkg_license_comment(self.doc, str(comment))
- except CardinalityError:
- self.more_than_one_error("package comments on license")
- break
-
- def p_pkg_lics_info_from_files(self, p_term, predicate):
- for _, _, lics in self.graph.triples((p_term, predicate, None)):
- try:
- if (
- lics,
- RDF.type,
- self.spdx_namespace["ExtractedLicensingInfo"],
- ) in self.graph:
- self.builder.set_pkg_license_from_file(
- self.doc, self.parse_only_extr_license(lics)
- )
- else:
- self.builder.set_pkg_license_from_file(
- self.doc, self.handle_lics(lics)
- )
-
- except SPDXValueError:
- self.value_error("PKG_LICS_INFO_FILES", lics)
-
- def p_pkg_lic_decl(self, p_term, predicate):
- self.handle_pkg_lic(p_term, predicate, self.builder.set_pkg_license_declared)
-
- def handle_pkg_lic(self, p_term, predicate, builder_func):
- """
- Handle package lics concluded or declared.
- """
- try:
- for _, _, licenses in self.graph.triples((p_term, predicate, None)):
- if (
- licenses,
- RDF.type,
- self.spdx_namespace["ConjunctiveLicenseSet"],
- ) in self.graph:
- lics = self.handle_conjunctive_list(licenses)
- builder_func(self.doc, lics)
-
- elif (
- licenses,
- RDF.type,
- self.spdx_namespace["DisjunctiveLicenseSet"],
- ) in self.graph:
- lics = self.handle_disjunctive_list(licenses)
- builder_func(self.doc, lics)
-
- else:
- try:
- lics = self.handle_lics(licenses)
- builder_func(self.doc, lics)
- except SPDXValueError:
- self.value_error("PKG_SINGLE_LICS", licenses)
- except CardinalityError:
- self.more_than_one_error("package {0}".format(predicate))
-
- def p_pkg_lic_conc(self, p_term, predicate):
- self.handle_pkg_lic(p_term, predicate, self.builder.set_pkg_licenses_concluded)
-
- def p_pkg_verif_code(self, p_term, predicate):
- for _, _, verifcode in self.graph.triples((p_term, predicate, None)):
- # Parse verification code
- for _, _, code in self.graph.triples(
- (verifcode, self.spdx_namespace["packageVerificationCodeValue"], None)
- ):
- try:
- self.builder.set_pkg_verif_code(self.doc, str(code))
- except CardinalityError:
- self.more_than_one_error("package verification code")
- break
- # Parse excluded file
- for _, _, filename in self.graph.triples(
- (
- verifcode,
- self.spdx_namespace["packageVerificationCodeExcludedFile"],
- None,
- )
- ):
- try:
- self.builder.set_pkg_excl_file(self.doc, str(filename))
- except CardinalityError:
- self.more_than_one_error("package verification code excluded file")
- break
-
- def p_pkg_src_info(self, p_term, predicate):
- for _, _, o in self.graph.triples((p_term, predicate, None)):
- try:
- self.builder.set_pkg_source_info(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("package source info")
- break
-
- def p_pkg_checksum(self, p_term, predicate):
- for _s, _p, pkg_checksum in self.graph.triples((p_term, predicate, None)):
- for _, _, value in self.graph.triples(
- (pkg_checksum, self.spdx_namespace["checksumValue"], None)
- ):
- for _, _, algo in self.graph.triples(
- (pkg_checksum, self.spdx_namespace["algorithm"], None)
- ):
- algorithm_identifier = convert_rdf_checksum_algorithm(str(algo))
- checksum = Checksum(algorithm_identifier, str(value))
- self.builder.set_pkg_checksum(self.doc, checksum)
-
- def p_pkg_homepg(self, p_term, predicate):
- for _s, _p, o in self.graph.triples((p_term, predicate, None)):
- try:
- self.builder.set_pkg_home(
- self.doc, str(self.to_special_value(o))
- )
- except CardinalityError:
- self.more_than_one_error("Package home page")
- break
- except SPDXValueError:
- self.value_error("PKG_HOME_PAGE", o)
-
- def p_pkg_down_loc(self, p_term, predicate):
- for _s, _p, o in self.graph.triples((p_term, predicate, None)):
- try:
- self.builder.set_pkg_down_location(
- self.doc, str(self.to_special_value(o))
- )
- except CardinalityError:
- self.more_than_one_error("Package download location")
- break
- except SPDXValueError:
- self.value_error("PKG_DOWN_LOC", o)
-
- def p_pkg_files_analyzed(self, p_term, predicate):
- for _s, _p, o in self.graph.triples((p_term, predicate, None)):
- try:
- self.builder.set_pkg_files_analyzed(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("Package Files Analyzed")
- break
- except SPDXValueError:
- self.value_error("PKG_FILES_ANALYZED_VALUE", o)
-
- def p_pkg_originator(self, p_term, predicate):
- for _s, _p, o in self.graph.triples((p_term, predicate, None)):
- try:
- if o == "NOASSERTION":
- self.builder.set_pkg_originator(self.doc, utils.NoAssert())
- else:
- ent = self.builder.create_entity(self.doc, str(o))
- self.builder.set_pkg_originator(self.doc, ent)
- except CardinalityError:
- self.more_than_one_error("Package originator")
- break
- except SPDXValueError:
- self.value_error("PKG_ORIGINATOR_VALUE", o)
-
- def p_pkg_suppl(self, p_term, predicate):
- for _s, _p, o in self.graph.triples((p_term, predicate, None)):
- try:
- if o == "NOASSERTION":
- self.builder.set_pkg_supplier(self.doc, utils.NoAssert())
- else:
- ent = self.builder.create_entity(self.doc, str(o))
- self.builder.set_pkg_supplier(self.doc, ent)
- except CardinalityError:
- self.more_than_one_error("Package supplier")
- break
- except SPDXValueError:
- self.value_error("PKG_SUPPL_VALUE", o)
-
- def p_pkg_fname(self, p_term, predicate):
- for _s, _p, o in self.graph.triples((p_term, predicate, None)):
- try:
- self.builder.set_pkg_file_name(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("Package file name")
- break
-
- def p_pkg_vinfo(self, p_term, predicate):
- for _s, _p, o in self.graph.triples((p_term, predicate, None)):
- try:
- self.builder.set_pkg_vers(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("Package version info")
- break
-
-
-class FileParser(LicenseParser):
- """
- Helper class for parsing files.
- """
-
- def __init__(self, builder, logger):
- super(FileParser, self).__init__(builder, logger)
-
- def parse_file(self, f_term):
- if not (f_term, self.spdx_namespace["fileName"], None) in self.graph:
- self.error = True
- self.logger.log("File must have a name.")
- # Dummy name to continue
- self.builder.set_file_name(self.doc, "Dummy file")
- else:
- for _, _, name in self.graph.triples(
- (f_term, self.spdx_namespace["fileName"], None)
- ):
- self.builder.set_file_name(self.doc, str(name))
-
- self.p_file_spdx_id(f_term, self.spdx_namespace["File"])
- self.p_file_type(f_term, self.spdx_namespace["fileType"])
- self.p_file_checksum(f_term, self.spdx_namespace["checksum"])
- self.p_file_lic_conc(f_term, self.spdx_namespace["licenseConcluded"])
- self.p_file_lic_info(f_term, self.spdx_namespace["licenseInfoInFile"])
- self.p_file_comments_on_lics(f_term, self.spdx_namespace["licenseComments"])
- self.p_file_attribution_text(f_term, self.spdx_namespace["attributionText"])
- self.p_file_cr_text(f_term, self.spdx_namespace["copyrightText"])
- self.p_file_artifact(f_term, self.spdx_namespace["artifactOf"])
- self.p_file_comment(f_term, RDFS.comment)
- self.p_file_notice(f_term, self.spdx_namespace["noticeText"])
- self.p_file_contributor(f_term, self.spdx_namespace["fileContributor"])
- self.p_file_depends(f_term, self.spdx_namespace["fileDependency"])
-
- def get_file_name(self, f_term):
- """Returns first found fileName property or None if not found."""
- for _, _, name in self.graph.triples(
- (f_term, self.spdx_namespace["fileName"], None)
- ):
- return name
- return
-
- def p_file_depends(self, f_term, predicate):
- """
- Set file dependencies.
- """
- for _, _, other_file in self.graph.triples((f_term, predicate, None)):
- name = self.get_file_name(other_file)
- if name is not None:
- self.builder.add_file_dep(str(name))
- else:
- self.error = True
- msg = "File depends on file with no name"
- self.logger.log(msg)
-
- def p_file_contributor(self, f_term, predicate):
- """
- Parse all file contributors and adds them to the model.
- """
- for _, _, contributor in self.graph.triples((f_term, predicate, None)):
- self.builder.add_file_contribution(self.doc, str(contributor))
-
- def p_file_notice(self, f_term, predicate):
- """
- Set file notice text.
- """
- try:
- for _, _, notice in self.graph.triples((f_term, predicate, None)):
- self.builder.set_file_notice(self.doc, str(notice))
- except CardinalityError:
- self.more_than_one_error("file notice")
-
- def p_file_comment(self, f_term, predicate):
- """
- Set file comment text.
- """
- try:
- for _, _, comment in self.graph.triples((f_term, predicate, None)):
- self.builder.set_file_comment(self.doc, str(comment))
- except CardinalityError:
- self.more_than_one_error("file comment")
-
- def p_file_attribution_text(self, f_term, predicate):
- """
- Set file attribution text
- """
- try:
- for _, _, attribute_text in self.graph.triples((f_term, predicate, None)):
- self.builder.set_file_attribution_text(
- self.doc, str(attribute_text)
- )
- except CardinalityError:
- self.more_than_one_error("file attribution text")
-
- def p_file_artifact(self, f_term, predicate):
- """
- Handle file artifactOf.
- Note: does not handle artifact of project URI.
- """
- for _, _, project in self.graph.triples((f_term, predicate, None)):
- if (project, RDF.type, self.doap_namespace["Project"]):
- self.p_file_project(project)
- else:
- self.error = True
- msg = "File must be artifact of doap:Project"
- self.logger.log(msg)
-
- def p_file_project(self, project):
- """
- Helper function for parsing doap:project name and homepage.
- and setting them using the file builder.
- """
- for _, _, name in self.graph.triples(
- (project, self.doap_namespace["name"], None)
- ):
- self.builder.set_file_atrificat_of_project(
- self.doc, "name", str(name)
- )
- for _, _, homepage in self.graph.triples(
- (project, self.doap_namespace["homepage"], None)
- ):
- self.builder.set_file_atrificat_of_project(
- self.doc, "home", str(homepage)
- )
-
- def p_file_cr_text(self, f_term, predicate):
- """
- Set file copyright text.
- """
- try:
- for _, _, cr_text in self.graph.triples((f_term, predicate, None)):
- self.builder.set_file_copyright(self.doc, str(cr_text))
- except CardinalityError:
- self.more_than_one_error("file copyright text")
-
- def p_file_comments_on_lics(self, f_term, predicate):
- """
- Set file license comment.
- """
- try:
- for _, _, comment in self.graph.triples((f_term, predicate, None)):
- self.builder.set_file_license_comment(self.doc, str(comment))
- except CardinalityError:
- self.more_than_one_error("file comments on license")
-
- def p_file_lic_info(self, f_term, predicate):
- """
- Set file license information.
- """
- for _, _, info in self.graph.triples((f_term, predicate, None)):
- lic = self.handle_lics(info)
- if lic is not None:
- self.builder.set_file_license_in_file(self.doc, lic)
-
- def p_file_spdx_id(self, f_term, predicate):
- try:
- try:
- self.builder.set_file_spdx_id(self.doc, str(f_term))
- except SPDXValueError:
- self.value_error("FILE_SPDX_ID_VALUE", f_term)
- except CardinalityError:
- self.more_than_one_error("FILE_SPDX_ID_VALUE")
-
- def p_file_type(self, f_term, predicate):
- """
- Set file type.
- """
- try:
- for _, _, ftype in self.graph.triples((f_term, predicate, None)):
- try:
- self.builder.set_file_type(self.doc, ftype)
- except SPDXValueError:
- self.value_error("FILE_TYPE", ftype)
- except CardinalityError:
- self.more_than_one_error("file type")
-
- def p_file_checksum(self, f_term, predicate):
- """
- Set file checksum.
- """
- for _s, _p, file_checksum in self.graph.triples((f_term, predicate, None)):
- for _, _, value in self.graph.triples(
- (file_checksum, self.spdx_namespace["checksumValue"], None)
- ):
- for _, _, algo in self.graph.triples(
- (file_checksum, self.spdx_namespace["algorithm"], None)
- ):
- algorithm_identifier = convert_rdf_checksum_algorithm(str(algo))
- checksum = Checksum(algorithm_identifier, str(value))
- self.builder.set_file_checksum(self.doc, checksum)
-
- def p_file_lic_conc(self, f_term, predicate):
- """
- Set file licenses concluded.
- """
- try:
- for _, _, licenses in self.graph.triples((f_term, predicate, None)):
- if (
- licenses,
- RDF.type,
- self.spdx_namespace["ConjunctiveLicenseSet"],
- ) in self.graph:
- lics = self.handle_conjunctive_list(licenses)
- self.builder.set_concluded_license(self.doc, lics)
-
- elif (
- licenses,
- RDF.type,
- self.spdx_namespace["DisjunctiveLicenseSet"],
- ) in self.graph:
- lics = self.handle_disjunctive_list(licenses)
- self.builder.set_concluded_license(self.doc, lics)
-
- else:
- try:
- lics = self.handle_lics(licenses)
- self.builder.set_concluded_license(self.doc, lics)
- except SPDXValueError:
- self.value_error("FILE_SINGLE_LICS", licenses)
- except CardinalityError:
- self.more_than_one_error("file {0}".format(predicate))
-
-
-class SnippetParser(LicenseParser):
- """
- Helper class for parsing snippet information.
- """
-
- def __init__(self, builder, logger):
- super(SnippetParser, self).__init__(builder, logger)
-
- def parse_snippet(self, snippet_term):
- try:
- self.builder.create_snippet(self.doc, snippet_term)
- except SPDXValueError:
- self.value_error("SNIPPET_SPDX_ID_VALUE", snippet_term)
-
- for _s, _p, o in self.graph.triples(
- (snippet_term, self.spdx_namespace["name"], None)
- ):
- try:
- self.builder.set_snippet_name(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("snippetName")
- break
-
- for _s, _p, o in self.graph.triples(
- (snippet_term, self.spdx_namespace["licenseComments"], None)
- ):
- try:
- self.builder.set_snippet_lic_comment(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("licenseComments")
- break
-
- for _s, _p, o in self.graph.triples((snippet_term, RDFS.comment, None)):
- try:
- self.builder.set_snippet_comment(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("comment")
- break
-
- for _s, _p, o in self.graph.triples(
- (snippet_term, self.spdx_namespace["copyrightText"], None)
- ):
- try:
- self.builder.set_snippet_copyright(
- self.doc, self.to_special_value(str(o))
- )
- except CardinalityError:
- self.more_than_one_error("copyrightText")
- break
-
- try:
- for _, _, licenses in self.graph.triples(
- (snippet_term, self.spdx_namespace["licenseConcluded"], None)
- ):
- if (
- licenses,
- RDF.type,
- self.spdx_namespace["ConjunctiveLicenseSet"],
- ) in self.graph:
- lics = self.handle_conjunctive_list(licenses)
- self.builder.set_snip_concluded_license(self.doc, lics)
-
- elif (
- licenses,
- RDF.type,
- self.spdx_namespace["DisjunctiveLicenseSet"],
- ) in self.graph:
- lics = self.handle_disjunctive_list(licenses)
- self.builder.set_snip_concluded_license(self.doc, lics)
-
- else:
- try:
- lics = self.handle_lics(licenses)
- self.builder.set_snip_concluded_license(self.doc, lics)
- except SPDXValueError:
- self.value_error("SNIPPET_CONCLUDED_LICENSE", licenses)
- except CardinalityError:
- self.more_than_one_error(
- "package {0}".format(self.spdx_namespace["licenseConcluded"])
- )
-
- for _, _, info in self.graph.triples(
- (snippet_term, self.spdx_namespace["licenseInfoInSnippet"], None)
- ):
- lic = self.handle_lics(info)
- if lic is not None:
- try:
- self.builder.set_snippet_lics_info(self.doc, lic)
- except SPDXValueError:
- self.value_error("SNIPPET_LIC_INFO", lic)
-
- for _s, _p, o in self.graph.triples(
- (snippet_term, self.spdx_namespace["snippetFromFile"], None)
- ):
- try:
- self.builder.set_snip_from_file_spdxid(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("snippetFromFile")
- break
-
- try:
- for _, _, attribute_text in self.graph.triples(
- (snippet_term, self.spdx_namespace["attributionText"], None)
- ):
- self.builder.set_snippet_attribution_text(
- self.doc, str(attribute_text)
- )
- except CardinalityError:
- self.more_than_one_error("snippetAttributionText")
-
-
-class ReviewParser(BaseParser):
- """
- Helper class for parsing review information.
- """
-
- def __init__(self, builder, logger):
- super(ReviewParser, self).__init__(builder, logger)
-
- def parse_review(self, r_term):
- reviewer = self.get_reviewer(r_term)
- reviewed_date = self.get_review_date(r_term)
- if reviewer is not None:
- self.builder.add_reviewer(self.doc, reviewer)
- if reviewed_date is not None:
- try:
- self.builder.add_review_date(self.doc, reviewed_date)
- except SPDXValueError:
- self.value_error("REVIEW_DATE", reviewed_date)
- comment = self.get_review_comment(r_term)
- if comment is not None:
- self.builder.add_review_comment(self.doc, comment)
-
- def get_review_comment(self, r_term):
- """
- Return review comment or None if found none or more than one.
- Report errors.
- """
- comment_list = list(self.graph.triples((r_term, RDFS.comment, None)))
- if len(comment_list) > 1:
- self.error = True
- msg = "Review can have at most one comment"
- self.logger.log(msg)
- return
- else:
- return str(comment_list[0][2])
-
- def get_review_date(self, r_term):
- """
- Return review date or None if not found.
- Report error on failure.
- Note does not check value format.
- """
- reviewed_list = list(
- self.graph.triples((r_term, self.spdx_namespace["reviewDate"], None))
- )
- if len(reviewed_list) != 1:
- self.error = True
- msg = "Review must have exactly one review date"
- self.logger.log(msg)
- return
- return str(reviewed_list[0][2])
-
- def get_reviewer(self, r_term):
- """
- Return reviewer as creator object or None if failed.
- Report errors on failure.
- """
- reviewer_list = list(
- self.graph.triples((r_term, self.spdx_namespace["reviewer"], None))
- )
- if len(reviewer_list) != 1:
- self.error = True
- msg = "Review must have exactly one reviewer"
- self.logger.log(msg)
- return
- try:
- return self.builder.create_entity(
- self.doc, str(reviewer_list[0][2])
- )
- except SPDXValueError:
- self.value_error("REVIEWER_VALUE", reviewer_list[0][2])
-
-
-class AnnotationParser(BaseParser):
- """
- Helper class for parsing annotation information.
- """
-
- def __init__(self, builder, logger):
- super(AnnotationParser, self).__init__(builder, logger)
-
- def parse_annotation(self, r_term):
- annotator = self.get_annotator(r_term)
- annotation_date = self.get_annotation_date(r_term)
- if annotator is not None:
- self.builder.add_annotator(self.doc, annotator)
- if annotation_date is not None:
- try:
- self.builder.add_annotation_date(self.doc, annotation_date)
- except SPDXValueError:
- self.value_error("ANNOTATION_DATE", annotation_date)
- comment = self.get_annotation_comment(r_term)
- if comment is not None:
- self.builder.add_annotation_comment(self.doc, comment)
- annotation_type = self.get_annotation_type(r_term)
- self.builder.add_annotation_type(self.doc, annotation_type)
- try:
- self.builder.set_annotation_spdx_id(self.doc, str(r_term))
- except CardinalityError:
- self.more_than_one_error("SPDX Identifier Reference")
-
- def get_annotation_type(self, r_term):
- """
- Return annotation type or None if found none or more than one.
- Report errors on failure.
- """
- for _, _, typ in self.graph.triples(
- (r_term, self.spdx_namespace["annotationType"], None)
- ):
- if typ is not None:
- return str(typ)
- else:
- self.error = True
- msg = "Annotation must have exactly one annotation type."
- self.logger.log(msg)
- return
-
- def get_annotation_comment(self, r_term):
- """
- Return annotation comment or None if found none or more than one.
- Report errors.
- """
- comment_list = list(self.graph.triples((r_term, RDFS.comment, None)))
- if len(comment_list) > 1:
- self.error = True
- msg = "Annotation can have at most one comment."
- self.logger.log(msg)
- return
- else:
- return str(comment_list[0][2])
-
- def get_annotation_date(self, r_term):
- """
- Return annotation date or None if not found.
- Report error on failure.
- Note does not check value format.
- """
- annotation_date_list = list(
- self.graph.triples((r_term, self.spdx_namespace["annotationDate"], None))
- )
- if len(annotation_date_list) != 1:
- self.error = True
- msg = "Annotation must have exactly one annotation date."
- self.logger.log(msg)
- return
- return str(annotation_date_list[0][2])
-
- def get_annotator(self, r_term):
- """
- Return annotator as creator object or None if failed.
- Report errors on failure.
- """
- annotator_list = list(
- self.graph.triples((r_term, self.spdx_namespace["annotator"], None))
- )
- if len(annotator_list) != 1:
- self.error = True
- msg = "Annotation must have exactly one annotator"
- self.logger.log(msg)
- return
- try:
- return self.builder.create_entity(
- self.doc, str(annotator_list[0][2])
- )
- except SPDXValueError:
- self.value_error("ANNOTATOR_VALUE", annotator_list[0][2])
-
-
-class RelationshipParser(BaseParser):
- """
- Helper Class for parsing relationship information
- """
-
- def __init__(self, builder, logger):
- super(RelationshipParser, self).__init__(builder, logger)
-
- def parse_relationship(self, subject_term, relation_term):
- relationship = self.get_relationship(subject_term, relation_term)
- relationship_comment = self.get_relationship_comment(relation_term)
- if relationship is not None:
- relationship_added: bool = self.builder.add_relationship(self.doc, relationship)
- if relationship_comment is not None and relationship_added:
- self.builder.add_relationship_comment(self.doc, relationship_comment)
-
- def get_relationship(self, subject_term, relation_term):
- """
- Returns a string with relationship type and the related elements.
- """
- relation_subject = str(subject_term.split("#")[1])
-
- for _, _, rtype in self.graph.triples(
- (relation_term, self.spdx_namespace["relationshipType"], None)
- ):
- try:
- if rtype.endswith("describes"):
- rtype = "DESCRIBES"
- elif rtype.endswith("describedBy"):
- rtype = "DESCRIBED_BY"
- elif rtype.endswith("contains"):
- rtype = "CONTAINS"
- elif rtype.endswith("containedBy"):
- rtype = "CONTAINED_BY"
- elif rtype.endswith("dependsOn"):
- rtype = "DEPENDS_ON"
- elif rtype.endswith("dependencyOf"):
- rtype = "DEPENDENCY_OF"
- elif rtype.endswith("dependencyManifestOf"):
- rtype = "DEPENDENCY_MANIFEST_OF"
- elif rtype.endswith("buildDependencyOf"):
- rtype = "BUILD_DEPENDENCY_OF"
- elif rtype.endswith("devDependencyOf"):
- rtype = "DEV_DEPENDENCY_OF"
- elif rtype.endswith("optionalDependencyOf"):
- rtype = "OPTIONAL_DEPENDENCY_OF"
- elif rtype.endswith("providedDependencyOf"):
- rtype = "PROVIDED_DEPENDENCY_OF"
- elif rtype.endswith("testDependencyOf"):
- rtype = "TEST_DEPENDENCY_OF"
- elif rtype.endswith("runtimeDependencyOf"):
- rtype = "RUNTIME_DEPENDENCY_OF"
- elif rtype.endswith("exampleOf"):
- rtype = "EXAMPLE_OF"
- elif rtype.endswith("generates"):
- rtype = "GENERATES"
- elif rtype.endswith("generatedFrom"):
- rtype = "GENERATED_FROM"
- elif rtype.endswith("ancestorOf"):
- rtype = "ANCESTOR_OF"
- elif rtype.endswith("descendantOf"):
- rtype = "DESCENDANT_OF"
- elif rtype.endswith("variantOf"):
- rtype = "VARIANT_OF"
- elif rtype.endswith("distributionArtifact"):
- rtype = "DISTRIBUTION_ARTIFACT"
- elif rtype.endswith("patchFor"):
- rtype = "PATCH_FOR"
- elif rtype.endswith("patchApplied"):
- rtype = "PATCH_APPLIED"
- elif rtype.endswith("copyOf"):
- rtype = "COPY_OF"
- elif rtype.endswith("fileAdded"):
- rtype = "FILE_ADDED"
- elif rtype.endswith("fileDeleted"):
- rtype = "FILE_DELETED"
- elif rtype.endswith("fileModified"):
- rtype = "FILE_MODIFIED"
- elif rtype.endswith("expandedFromArchive"):
- rtype = "EXPANDED_FROM_ARCHIVE"
- elif rtype.endswith("dynamicLink"):
- rtype = "DYNAMIC_LINK"
- elif rtype.endswith("staticLink"):
- rtype = "STATIC_LINK"
- elif rtype.endswith("dataFileOf"):
- rtype = "DATA_FILE_OF"
- elif rtype.endswith("testCaseOf"):
- rtype = "TEST_CASE_OF"
- elif rtype.endswith("buildToolOf"):
- rtype = "BUILD_TOOL_OF"
- elif rtype.endswith("devToolOf"):
- rtype = "DEV_TOOL_OF"
- elif rtype.endswith("testOf"):
- rtype = "TEST_OF"
- elif rtype.endswith("testToolOf"):
- rtype = "TEST_TOOL_OF"
- elif rtype.endswith("documentationOf"):
- rtype = "DOCUMENTATION_OF"
- elif rtype.endswith("optionalComponentOf"):
- rtype = "OPTIONAL_COMPONENT_OF"
- elif rtype.endswith("metafileOf"):
- rtype = "METAFILE_OF"
- elif rtype.endswith("packageOf"):
- rtype = "PACKAGE_OF"
- elif rtype.endswith("amends"):
- rtype = "AMENDS"
- elif rtype.endswith("prerequisiteFor"):
- rtype = "PREREQUISITE_FOR"
- elif rtype.endswith("hasPrerequisite"):
- rtype = "HAS_PREREQUISITE"
- elif rtype.endswith("other"):
- rtype = "OTHER"
- elif rtype.endswith("specificationFor"):
- rtype = "SPECIFICATION_FOR"
- elif rtype.endswith("requirementDescriptionFor"):
- rtype = "REQUIREMENT_DESCRIPTION_FOR"
-
- except SPDXValueError:
- self.value_error("RELATIONSHIP", rtype)
-
- try:
- for sub, pre, rel_ele in self.graph.triples(
- (relation_term, self.spdx_namespace["relatedSpdxElement"], None)
- ):
- related_element = str(rel_ele.split("#")[1]) if '#' in rel_ele else rel_ele
- except:
- related_element = None
-
- try:
- if related_element == None:
- return str(relation_subject + " " + rtype)
- else:
- return str(
- relation_subject + " " + rtype + " " + related_element
- )
-
- except SPDXValueError:
- self.value_error("RELATIONSHIP_VALUE", relation_subject + " " + rtype)
-
- def get_relationship_comment(self, relation_term):
- """
- Returns relationship comment or None if found none or more than one.
- Reports errors.
- """
-
- comment_list = list(self.graph.triples((relation_term, RDFS.comment, None)))
- if len(comment_list) == 0:
- return None
- else:
- if len(comment_list) > 1:
- self.error = True
- msg = "Relationship can have at most one comment."
- self.logger.log(msg)
- return
- else:
- return str(comment_list[0][2])
-
-
-class Parser(
- PackageParser,
- FileParser,
- SnippetParser,
- ReviewParser,
- AnnotationParser,
- RelationshipParser,
-):
- """
- RDF/XML file parser.
- """
-
- def __init__(self, builder, logger):
- super(Parser, self).__init__(builder, logger)
-
- def parse(self, fil):
- """
- Parse a file and returns a document object.
- fil is a file like object.
- """
- self.error = False
- self.graph = Graph()
- self.graph.parse(file=fil, format="xml")
- self.doc = document.Document()
-
- for s, _p, o in self.graph.triples(
- (None, RDF.type, self.spdx_namespace["SpdxDocument"])
- ):
- self.parse_doc_fields(s)
-
- for s, _p, o in self.graph.triples(
- (None, RDF.type, self.spdx_namespace["ExternalDocumentRef"])
- ):
- self.parse_ext_doc_ref(s)
-
- for s, _p, o in self.graph.triples(
- (None, RDF.type, self.spdx_namespace["CreationInfo"])
- ):
- self.parse_creation_info(s)
-
- for s, _p, o in self.graph.triples(
- (None, None, self.spdx_namespace["ExtractedLicensingInfo"])
- ):
- self.handle_extracted_license(s)
-
- for s, _p, o in self.graph.triples(
- (None, RDF.type, self.spdx_namespace["Package"])
- ):
- self.parse_package(s)
-
- for s, _p, o in self.graph.triples(
- (None, RDF.type, self.spdx_namespace["ExternalRef"])
- ):
- self.parse_pkg_ext_ref(s)
-
- for s, _p, o in self.graph.triples(
- (None, self.spdx_namespace["referencesFile"], None)
- ):
- self.parse_file(o)
-
- for s, _p, o in self.graph.triples(
- (None, RDF.type, self.spdx_namespace["Snippet"])
- ):
- self.parse_snippet(s)
-
- for s, _p, o in self.graph.triples(
- (None, self.spdx_namespace["reviewed"], None)
- ):
- self.parse_review(o)
-
- for s, _p, o in self.graph.triples(
- (None, self.spdx_namespace["annotation"], None)
- ):
- self.parse_annotation(o)
-
- for s, _p, o in self.graph.triples(
- (None, self.spdx_namespace["relationship"], None)
- ):
- self.parse_relationship(s, o)
-
- validation_messages = ErrorMessages()
- # Report extra errors if self.error is False otherwise there will be
- # redundant messages
- validation_messages = self.doc.validate(validation_messages)
- if not self.error:
- if validation_messages:
- for msg in validation_messages:
- self.logger.log(msg)
- self.error = True
- return self.doc, self.error
-
- def parse_creation_info(self, ci_term):
- """
- Parse creators, created and comment.
- """
- for _s, _p, o in self.graph.triples(
- (ci_term, self.spdx_namespace["creator"], None)
- ):
- try:
- ent = self.builder.create_entity(self.doc, str(o))
- self.builder.add_creator(self.doc, ent)
- except SPDXValueError:
- self.value_error("CREATOR_VALUE", o)
-
- for _s, _p, o in self.graph.triples(
- (ci_term, self.spdx_namespace["created"], None)
- ):
- try:
- self.builder.set_created_date(self.doc, str(o))
- except SPDXValueError:
- self.value_error("CREATED_VALUE", o)
- except CardinalityError:
- self.more_than_one_error("created")
- break
-
- for _s, _p, o in self.graph.triples((ci_term, RDFS.comment, None)):
- try:
- self.builder.set_creation_comment(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("CreationInfo comment")
- break
- for _s, _p, o in self.graph.triples(
- (ci_term, self.spdx_namespace["licenseListVersion"], None)
- ):
- try:
- self.builder.set_lics_list_ver(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("licenseListVersion")
- break
- except SPDXValueError:
- self.value_error("LL_VALUE", o)
-
- def parse_doc_fields(self, doc_term):
- """
- Parse the version, data license, name, SPDX Identifier, namespace,
- and comment.
- """
- try:
- self.builder.set_doc_spdx_id(self.doc, str(doc_term))
- except SPDXValueError:
- self.value_error("DOC_SPDX_ID_VALUE", doc_term)
- try:
- if doc_term.count("#", 0, len(doc_term)) <= 1:
- doc_namespace = doc_term.split("#")[0]
- self.builder.set_doc_namespace(self.doc, doc_namespace)
- else:
- self.value_error("DOC_NAMESPACE_VALUE", doc_term)
- except SPDXValueError:
- self.value_error("DOC_NAMESPACE_VALUE", doc_term)
- for _s, _p, o in self.graph.triples(
- (doc_term, self.spdx_namespace["specVersion"], None)
- ):
- try:
- self.builder.set_doc_version(self.doc, str(o))
- except SPDXValueError:
- self.value_error("DOC_VERS_VALUE", o)
- except CardinalityError:
- self.more_than_one_error("specVersion")
- break
- for _s, _p, o in self.graph.triples(
- (doc_term, self.spdx_namespace["dataLicense"], None)
- ):
- try:
- self.builder.set_doc_data_lic(self.doc, str(o))
- except SPDXValueError:
- self.value_error("DOC_D_LICS", o)
- except CardinalityError:
- self.more_than_one_error("dataLicense")
- break
- for _s, _p, o in self.graph.triples(
- (doc_term, self.spdx_namespace["name"], None)
- ):
- try:
- self.builder.set_doc_name(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("name")
- break
- for _s, _p, o in self.graph.triples((doc_term, RDFS.comment, None)):
- try:
- self.builder.set_doc_comment(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("Document comment")
- break
-
- def parse_ext_doc_ref(self, ext_doc_ref_term):
- """
- Parse the External Document ID, SPDX Document URI and Checksum.
- """
- for _s, _p, o in self.graph.triples(
- (ext_doc_ref_term, self.spdx_namespace["externalDocumentId"], None)
- ):
- try:
- self.builder.set_ext_doc_id(self.doc, str(o))
- except SPDXValueError:
- self.value_error("EXT_DOC_REF_VALUE", "External Document ID")
- break
-
- for _s, _p, o in self.graph.triples(
- (ext_doc_ref_term, self.spdx_namespace["spdxDocument"], None)
- ):
- try:
- self.builder.set_spdx_doc_uri(self.doc, str(o))
- except SPDXValueError:
- self.value_error("EXT_DOC_REF_VALUE", "SPDX Document URI")
- break
-
- for _s, _p, checksum in self.graph.triples(
- (ext_doc_ref_term, self.spdx_namespace["checksum"], None)
- ):
- for _, _, value in self.graph.triples(
- (checksum, self.spdx_namespace["checksumValue"], None)
- ):
- try:
- self.builder.set_chksum(self.doc, str(value))
- except SPDXValueError:
- self.value_error("EXT_DOC_REF_VALUE", "Checksum")
- break
-
- def parse_pkg_ext_ref(self, pkg_ext_term):
- """
- Parse the category, type, locator, and comment.
- """
- for _s, _p, o in self.graph.triples(
- (pkg_ext_term, self.spdx_namespace["referenceCategory"], None)
- ):
- try:
- self.builder.set_pkg_ext_ref_category(self.doc, str(o))
- except SPDXValueError:
- self.value_error(
- "PKG_EXT_REF_CATEGORY", "Package External Reference Category"
- )
- break
-
- for _s, _p, o in self.graph.triples(
- (pkg_ext_term, self.spdx_namespace["referenceType"], None)
- ):
- try:
- self.builder.set_pkg_ext_ref_type(self.doc, str(o))
- except SPDXValueError:
- self.value_error("PKG_EXT_REF_TYPE", "Package External Reference Type")
- break
-
- for _s, _p, o in self.graph.triples(
- (pkg_ext_term, self.spdx_namespace["referenceLocator"], None)
- ):
- self.builder.set_pkg_ext_ref_locator(self.doc, str(o))
-
- for _s, _p, o in self.graph.triples((pkg_ext_term, RDFS.comment, None)):
- try:
- self.builder.set_pkg_ext_ref_comment(self.doc, str(o))
- except CardinalityError:
- self.more_than_one_error("Package External Reference Comment")
- break
diff --git a/spdx/parsers/rdfbuilders.py b/spdx/parsers/rdfbuilders.py
deleted file mode 100644
index c7845ae4d..000000000
--- a/spdx/parsers/rdfbuilders.py
+++ /dev/null
@@ -1,669 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import re
-from typing import Dict, Union
-
-from spdx import file
-from spdx import license
-from spdx import package
-from spdx import version
-from spdx.checksum import Checksum, ChecksumAlgorithm
-from spdx.document import Document
-from spdx.parsers.builderexceptions import CardinalityError
-from spdx.parsers.builderexceptions import OrderError
-from spdx.parsers.builderexceptions import SPDXValueError
-from spdx.parsers import tagvaluebuilders
-from spdx.parsers import validations
-from spdx.parsers.rdf import convert_rdf_checksum_algorithm
-
-
-class DocBuilder(object):
- VERS_STR_REGEX = re.compile(r"SPDX-(\d+)\.(\d+)", re.UNICODE)
-
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_document()
-
- def set_doc_version(self, doc, value):
- """
- Set the document version.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if already defined.
- """
- if not self.doc_version_set:
- self.doc_version_set = True
- m = self.VERS_STR_REGEX.match(value)
- if m is None:
- raise SPDXValueError("Document::Version")
- else:
- doc.version = version.Version(
- major=int(m.group(1)), minor=int(m.group(2))
- )
- return True
- else:
- raise CardinalityError("Document::Version")
-
- def set_doc_data_lic(self, doc, res):
- """
- Set the document data license.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if already defined.
- """
- if not self.doc_data_lics_set:
- self.doc_data_lics_set = True
- # TODO: what is this split?
- res_parts = res.split("/")
- if len(res_parts) != 0:
- identifier = res_parts[-1]
- doc.data_license = license.License.from_identifier(identifier)
- else:
- raise SPDXValueError("Document::License")
- else:
- raise CardinalityError("Document::License")
-
- def set_doc_name(self, doc, name):
- """
- Set the document name.
- Raise CardinalityError if already defined.
- """
- if not self.doc_name_set:
- doc.name = name
- self.doc_name_set = True
- return True
- else:
- raise CardinalityError("Document::Name")
-
- def set_doc_spdx_id(self, doc, doc_spdx_id_line):
- """
- Set the document SPDX Identifier.
- Raise value error if malformed value.
- Raise CardinalityError if already defined.
- """
- if not self.doc_spdx_id_set:
- if validations.validate_doc_spdx_id(doc_spdx_id_line):
- doc.spdx_id = doc_spdx_id_line
- self.doc_spdx_id_set = True
- return True
- else:
- raise SPDXValueError("Document::SPDXID")
- else:
- raise CardinalityError("Document::SPDXID")
-
- def set_doc_comment(self, doc, comment):
- """
- Set document comment.
- Raise CardinalityError if comment already set.
- """
- if not self.doc_comment_set:
- self.doc_comment_set = True
- doc.comment = comment
- else:
- raise CardinalityError("Document::Comment")
-
- def set_doc_namespace(self, doc, namespace):
- """
- Set the document namespace.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if already defined.
- """
- if not self.doc_namespace_set:
- self.doc_namespace_set = True
- if validations.validate_doc_namespace(namespace):
- doc.namespace = namespace
- return True
- else:
- raise SPDXValueError("Document::Namespace")
- else:
- raise CardinalityError("Document::Comment")
-
- def reset_document(self):
- """
- Reset the internal state to allow building new document
- """
- # FIXME: this state does not make sense
- self.doc_version_set = False
- self.doc_comment_set = False
- self.doc_namespace_set = False
- self.doc_data_lics_set = False
- self.doc_name_set = False
- self.doc_spdx_id_set = False
-
-
-class ExternalDocumentRefBuilder(tagvaluebuilders.ExternalDocumentRefBuilder):
- def set_chksum(self, doc, chk_sum):
- """
- Set the external document reference's check sum, if not already set.
- chk_sum - The checksum value in the form of a string.
- """
- if chk_sum:
- doc.ext_document_references[-1].checksum = Checksum(
- ChecksumAlgorithm.SHA1, chk_sum
- )
- else:
- raise SPDXValueError("ExternalDocumentRef::Checksum")
-
-
-class EntityBuilder(tagvaluebuilders.EntityBuilder):
- def __init__(self):
- super(EntityBuilder, self).__init__()
-
- def create_entity(self, doc, value):
- if self.tool_re.match(value):
- return self.build_tool(doc, value)
- elif self.person_re.match(value):
- return self.build_person(doc, value)
- elif self.org_re.match(value):
- return self.build_org(doc, value)
- else:
- raise SPDXValueError("Entity")
-
-
-class CreationInfoBuilder(tagvaluebuilders.CreationInfoBuilder):
- def __init__(self):
- super(CreationInfoBuilder, self).__init__()
-
- def set_creation_comment(self, doc, comment):
- """
- Set creation comment.
- Raise CardinalityError if comment already set.
- """
- if not self.creation_comment_set:
- self.creation_comment_set = True
- doc.creation_info.comment = comment
- return True
- else:
- raise CardinalityError("CreationInfo::Comment")
-
-
-class PackageBuilder(tagvaluebuilders.PackageBuilder):
- def __init__(self):
- super(PackageBuilder, self).__init__()
-
- def set_pkg_checksum(self, doc, checksum: Union[Checksum, Dict]):
- """
- Set the package checksum.
- checksum - A Checksum or a Dict
- Raise SPDXValueError if checksum type invalid.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if isinstance(checksum, dict):
- algo = checksum.get('algorithm') or ChecksumAlgorithm.SHA1
- if algo.startswith('checksumAlgorithm_'):
- algo = convert_rdf_checksum_algorithm(algo) or ChecksumAlgorithm.SHA1
- else:
- algo = ChecksumAlgorithm.checksum_algorithm_from_string(algo)
- doc.packages[-1].set_checksum(Checksum(identifier=algo, value=checksum.get('checksumValue')))
- elif isinstance(checksum, Checksum):
- doc.packages[-1].set_checksum(checksum)
- elif isinstance(checksum, str):
- # kept for backwards compatibility
- doc.packages[-1].set_checksum(Checksum(identifier=ChecksumAlgorithm.SHA1, value=checksum))
- else:
- raise SPDXValueError("Invalid value for package checksum.")
-
- def set_pkg_source_info(self, doc, text):
- """
- Set the package's source information, if not already set.
- text - Free form text.
- Raise CardinalityError if already defined.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if not self.package_source_info_set:
- self.package_source_info_set = True
- doc.packages[-1].source_info = text
- return True
- else:
- raise CardinalityError("Package::SourceInfo")
-
- def set_pkg_verif_code(self, doc, code):
- """
- Set the package verification code, if not already set.
- code - A string.
- Raise CardinalityError if already defined.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if not self.package_verif_set:
- self.package_verif_set = True
- doc.packages[-1].verif_code = code
- else:
- raise CardinalityError("Package::VerificationCode")
-
- def set_pkg_excl_file(self, doc, filename):
- """
- Set the package's verification code excluded file.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- doc.packages[-1].add_exc_file(filename)
-
- def set_pkg_license_comment(self, doc, text):
- """
- Set the package's license comment.
- Raise OrderError if no package previously defined.
- Raise CardinalityError if already set.
- """
- self.assert_package_exists()
- if not self.package_license_comment_set:
- self.package_license_comment_set = True
- doc.packages[-1].license_comment = text
- return True
- else:
- raise CardinalityError("Package::LicenseComment")
-
- def set_pkg_attribution_text(self, doc, text):
- """
- Set the package's attribution text.
- """
- self.assert_package_exists()
- doc.packages[-1].attribution_text = text
- return True
-
- def set_pkg_cr_text(self, doc, text):
- """
- Set the package's license comment.
- Raise OrderError if no package previously defined.
- Raise CardinalityError if already set.
- """
- self.assert_package_exists()
- if not self.package_cr_text_set:
- self.package_cr_text_set = True
- doc.packages[-1].cr_text = text
- else:
- raise CardinalityError("Package::CopyrightText")
-
- def set_pkg_summary(self, doc, text):
- """
- Set the package summary.
- Raise CardinalityError if summary already set.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if not self.package_summary_set:
- self.package_summary_set = True
- doc.packages[-1].summary = text
- else:
- raise CardinalityError("Package::Summary")
-
- def set_pkg_desc(self, doc, text):
- """
- Set the package's description.
- Raise CardinalityError if description already set.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if not self.package_desc_set:
- self.package_desc_set = True
- doc.packages[-1].description = text
- else:
- raise CardinalityError("Package::Description")
-
- def set_pkg_comment(self, doc, text):
- """
- Set the package's comment.
- Raise CardinalityError if comment already set.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if not self.package_comment_set:
- self.package_comment_set = True
- doc.packages[-1].comment = text
- else:
- raise CardinalityError("Package::Comment")
-
- def set_pkg_ext_ref_category(self, doc, category):
- """
- Set the package's external reference locator.
- Raise OrderError if no package previously defined.
- Raise SPDXValueError if malformed value.
- """
- self.assert_package_exists()
- category = category.split("_")[-1]
-
- if category.lower() == "packagemanager":
- category = "PACKAGE-MANAGER"
-
- if validations.validate_pkg_ext_ref_category(category):
- if (
- len(doc.packages[-1].pkg_ext_refs)
- and doc.packages[-1].pkg_ext_refs[-1].category is None
- ):
- doc.packages[-1].pkg_ext_refs[-1].category = category
- else:
- doc.packages[-1].add_pkg_ext_refs(
- package.ExternalPackageRef(category=category)
- )
- else:
- raise SPDXValueError("ExternalRef::Category")
-
- def set_pkg_ext_ref_type(self, doc, typ):
- """
- Set the package's external reference type.
- Raise OrderError if no package previously defined.
- Raise SPDXValueError if malformed value.
- """
- self.assert_package_exists()
- if "#" in typ:
- typ = typ.split("#")[-1]
- else:
- typ = typ.split("/")[-1]
-
- if validations.validate_pkg_ext_ref_type(typ):
- if (
- len(doc.packages[-1].pkg_ext_refs)
- and doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type is None
- ):
- doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type = typ
- else:
- doc.packages[-1].add_pkg_ext_refs(
- package.ExternalPackageRef(pkg_ext_ref_type=typ)
- )
- else:
- raise SPDXValueError("ExternalRef::Type")
-
- def set_pkg_ext_ref_comment(self, doc, comment):
- """
- Set the package's external reference comment.
- Raise CardinalityError if comment already set.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if not len(doc.packages[-1].pkg_ext_refs):
- raise OrderError("Package::ExternalRef")
- if not self.pkg_ext_comment_set:
- self.pkg_ext_comment_set = True
- doc.packages[-1].pkg_ext_refs[-1].comment = comment
- return True
- else:
- raise CardinalityError("ExternalRef::Comment")
-
-
-class FileBuilder(tagvaluebuilders.FileBuilder):
- def __init__(self):
- super(FileBuilder, self).__init__()
-
- def set_file_checksum(self, doc: Document, chk_sum: Union[Checksum, Dict, str]):
- """
- Set the file check sum, if not already set.
- chk_sum - A checksum.Checksum or a dict
- """
- if self.has_file(doc):
- if isinstance(chk_sum, dict):
- identifier = ChecksumAlgorithm.checksum_algorithm_from_string(chk_sum.get('algorithm'))
- self.file(doc).set_checksum(Checksum(identifier,
- chk_sum.get('checksumValue')))
- elif isinstance(chk_sum, Checksum):
- self.file(doc).set_checksum(chk_sum)
- elif isinstance(chk_sum, str):
- # kept for backwards compatibility
- self.file(doc).set_checksum(Checksum(ChecksumAlgorithm.SHA1, chk_sum))
- return True
-
- def set_file_license_comment(self, doc, text):
- """
- Raise OrderError if no package or file defined.
- Raise CardinalityError if more than one per file.
- """
- if self.has_package(doc) and self.has_file(doc):
- if not self.file_license_comment_set:
- self.file_license_comment_set = True
- self.file(doc).license_comment = text
- return True
- else:
- raise CardinalityError("File::LicenseComment")
- else:
- raise OrderError("File::LicenseComment")
-
- def set_file_attribution_text(self, doc, text):
- """
- Set the file's attribution text.
- """
- if self.has_package(doc) and self.has_file(doc):
- self.assert_package_exists()
- self.file(doc).attribution_text = text
- return True
-
- def set_file_copyright(self, doc, text):
- """
- Raise OrderError if no package or file defined.
- Raise CardinalityError if more than one.
- """
- if self.has_package(doc) and self.has_file(doc):
- if not self.file_copytext_set:
- self.file_copytext_set = True
- self.file(doc).copyright = text
- return True
- else:
- raise CardinalityError("File::CopyRight")
- else:
- raise OrderError("File::CopyRight")
-
- def set_file_comment(self, doc, text):
- """
- Raise OrderError if no package or no file defined.
- Raise CardinalityError if more than one comment set.
- """
- if self.has_package(doc) and self.has_file(doc):
- if not self.file_comment_set:
- self.file_comment_set = True
- self.file(doc).comment = text
- return True
- else:
- raise CardinalityError("File::Comment")
- else:
- raise OrderError("File::Comment")
-
- def set_file_notice(self, doc, text):
- """
- Raise OrderError if no package or file defined.
- Raise CardinalityError if more than one.
- """
- if self.has_package(doc) and self.has_file(doc):
- if not self.file_notice_set:
- self.file_notice_set = True
- self.file(doc).notice = tagvaluebuilders.str_from_text(text)
- return True
- else:
- raise CardinalityError("File::Notice")
- else:
- raise OrderError("File::Notice")
-
- def set_file_type(self, doc, filetype):
- """
- Set the file type for RDF values.
- """
- if not self.has_file(doc):
- raise OrderError("File::FileType")
-
- split_string = filetype.split('#')
- if len(split_string) != 2:
- raise SPDXValueError('Unknown file type {}'.format(filetype))
- file_type = file.file_type_from_rdf(filetype)
-
- spdx_file = self.file(doc)
- if file_type in spdx_file.file_types:
- raise CardinalityError("File::FileType")
-
- spdx_file.file_types.append(file_type)
-
-
-class SnippetBuilder(tagvaluebuilders.SnippetBuilder):
- def __init__(self):
- super(SnippetBuilder, self).__init__()
-
- def set_snippet_lic_comment(self, doc, lic_comment):
- """
- Set the snippet's license comment.
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if already set.
- """
- self.assert_snippet_exists()
- if not self.snippet_lic_comment_set:
- self.snippet_lic_comment_set = True
- doc.snippet[-1].license_comment = lic_comment
- else:
- CardinalityError("Snippet::licenseComments")
-
- def set_snippet_comment(self, doc, comment):
- """
- Set general comments about the snippet.
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if comment already set.
- """
- self.assert_snippet_exists()
- if not self.snippet_comment_set:
- self.snippet_comment_set = True
- doc.snippet[-1].comment = comment
- return True
- else:
- raise CardinalityError("Snippet::comment")
-
- def set_snippet_attribution_text(self, doc, text):
- """
- Set the snippet's attribution text.
- """
- self.assert_snippet_exists()
- doc.snippet[-1].attribution_text = text
- return True
-
- def set_snippet_copyright(self, doc, copyright):
- """
- Set the snippet's copyright text.
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if already set.
- """
- self.assert_snippet_exists()
- if not self.snippet_copyright_set:
- self.snippet_copyright_set = True
- doc.snippet[-1].copyright = copyright
- else:
- raise CardinalityError("Snippet::copyrightText")
-
-
-class ReviewBuilder(tagvaluebuilders.ReviewBuilder):
- def __init__(self):
- super(ReviewBuilder, self).__init__()
-
- def add_review_comment(self, doc, comment):
- """
- Set the review comment.
- Raise CardinalityError if already set.
- Raise OrderError if no reviewer defined before.
- """
- if len(doc.reviews) != 0:
- if not self.review_comment_set:
- self.review_comment_set = True
- doc.reviews[-1].comment = comment
- return True
- else:
- raise CardinalityError("ReviewComment")
- else:
- raise OrderError("ReviewComment")
-
-
-class AnnotationBuilder(tagvaluebuilders.AnnotationBuilder):
- def __init__(self):
- super(AnnotationBuilder, self).__init__()
-
- def add_annotation_comment(self, doc, comment):
- """
- Set the annotation comment.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- """
- if len(doc.annotations) != 0:
- if not self.annotation_comment_set:
- self.annotation_comment_set = True
- doc.annotations[-1].comment = comment
- return True
- else:
- raise CardinalityError("AnnotationComment")
- else:
- raise OrderError("AnnotationComment")
-
- def add_annotation_type(self, doc, annotation_type):
- """
- Set the annotation type.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- """
- if len(doc.annotations) != 0:
- if not self.annotation_type_set:
- if annotation_type.endswith("annotationType_other"):
- self.annotation_type_set = True
- doc.annotations[-1].annotation_type = "OTHER"
- return True
- elif annotation_type.endswith("annotationType_review"):
- self.annotation_type_set = True
- doc.annotations[-1].annotation_type = "REVIEW"
- return True
- else:
- raise SPDXValueError("Annotation::AnnotationType")
- else:
- raise CardinalityError("Annotation::AnnotationType")
- else:
- raise OrderError("Annotation::AnnotationType")
-
-
-class RelationshipBuilder(tagvaluebuilders.RelationshipBuilder):
- def __init__(self):
- super(RelationshipBuilder, self).__init__()
-
- def add_relationship_comment(self, doc, comment):
- """
- Set the relationship comment.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- """
- if len(doc.relationships) != 0:
- if not self.relationship_comment_set:
- self.relationship_comment_set = True
- doc.relationships[-1].comment = comment
- return True
- else:
- raise CardinalityError("RelationshipComment")
- else:
- raise OrderError("RelationshipComment")
-
-
-class Builder(
- DocBuilder,
- EntityBuilder,
- CreationInfoBuilder,
- PackageBuilder,
- FileBuilder,
- SnippetBuilder,
- ReviewBuilder,
- ExternalDocumentRefBuilder,
- AnnotationBuilder,
- RelationshipBuilder,
-):
- def __init__(self):
- super(Builder, self).__init__()
- # FIXME: this state does not make sense
- self.reset()
-
- def reset(self):
- """
- Reset builder's state for building new documents.
- Must be called between usage with different documents.
- """
- # FIXME: this state does not make sense
- self.reset_creation_info()
- self.reset_document()
- self.reset_package()
- self.reset_file_stat()
- self.reset_reviews()
- self.reset_annotations()
- self.reset_relationship()
diff --git a/spdx/parsers/tagvalue.py b/spdx/parsers/tagvalue.py
deleted file mode 100644
index bf2e937af..000000000
--- a/spdx/parsers/tagvalue.py
+++ /dev/null
@@ -1,1822 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import re
-
-from ply import yacc
-
-from spdx import config
-from spdx import license
-from spdx import utils
-from spdx.parsers.builderexceptions import CardinalityError
-from spdx.parsers.builderexceptions import OrderError
-from spdx.parsers.builderexceptions import SPDXValueError
-from spdx.parsers.lexers.tagvalue import Lexer
-from spdx.parsers.loggers import ErrorMessages
-from spdx import document
-
-ERROR_MESSAGES = {
- "TOOL_VALUE": "Invalid tool value {0} at line: {1}",
- "ORG_VALUE": "Invalid organization value {0} at line: {1}",
- "PERSON_VALUE": "Invalid person value {0} at line: {1}",
- "CREATED_VALUE_TYPE": "Created value must be date in ISO 8601 format, line: {0}",
- "MORE_THAN_ONE": "Only one {0} allowed, extra at line: {1}",
- "CREATOR_COMMENT_VALUE_TYPE": "CreatorComment value must be free form text between tags or"
- "single line of text, line:{0}",
- "DOC_LICENSE_VALUE": "Invalid DataLicense value '{0}', line:{1} must be CC0-1.0",
- "DOC_LICENSE_VALUE_TYPE": "DataLicense must be CC0-1.0, line: {0}",
- "DOC_VERSION_VALUE": "Invalid SPDXVersion '{0}' must be SPDX-M.N where M and N are numbers. Line: {1}",
- "DOC_VERSION_VALUE_TYPE": "Invalid SPDXVersion value, must be SPDX-M.N where M and N are numbers. Line: {0}",
- "DOC_NAME_VALUE": "DocumentName must be single line of text, line: {0}",
- "DOC_SPDX_ID_VALUE": "Invalid SPDXID value, SPDXID must be SPDXRef-DOCUMENT, line: {0}",
- "EXT_DOC_REF_VALUE": "ExternalDocumentRef must contain External Document ID, SPDX Document URI and Checksum"
- "in the standard format, line:{0}.",
- "DOC_COMMENT_VALUE_TYPE": "DocumentComment value must be free form text between tags"
- "or single line of text, line:{0}",
- "DOC_NAMESPACE_VALUE": 'Invalid DocumentNamespace value {0}, must contain a scheme (e.g. "https:") '
- 'and should not contain the "#" delimiter, line:{1}',
- "DOC_NAMESPACE_VALUE_TYPE": 'Invalid DocumentNamespace value, must contain a scheme (e.g. "https:") '
- 'and should not contain the "#" delimiter, line: {0}',
- "REVIEWER_VALUE_TYPE": "Invalid Reviewer value must be a Person, Organization or Tool. Line: {0}",
- "CREATOR_VALUE_TYPE": "Invalid Reviewer value must be a Person, Organization or Tool. Line: {0}",
- "REVIEW_DATE_VALUE_TYPE": "ReviewDate value must be date in ISO 8601 format, line: {0}",
- "REVIEW_COMMENT_VALUE_TYPE": "ReviewComment value must be free form text between tags"
- "or single line of text, line:{0}",
- "ANNOTATOR_VALUE_TYPE": "Invalid Annotator value must be a Person, Organization or Tool. Line: {0}",
- "ANNOTATION_DATE_VALUE_TYPE": "AnnotationDate value must be date in ISO 8601 format, line: {0}",
- "ANNOTATION_COMMENT_VALUE_TYPE": "AnnotationComment value must be free form text between tags"
- "or single line of text, line:{0}",
- "ANNOTATION_TYPE_VALUE": 'AnnotationType must be "REVIEW" or "OTHER". Line: {0}',
- "ANNOTATION_SPDX_ID_VALUE": 'SPDXREF must be ["DocumentRef-"[idstring]":"]SPDXID where'
- '["DocumentRef-"[idstring]":"] is an optional reference to an external SPDX document'
- 'and SPDXID is a unique string containing letters, numbers, ".","-".',
- "A_BEFORE_B": "{0} Can not appear before {1}, line: {2}",
- "PACKAGE_NAME_VALUE": "PackageName must be single line of text, line: {0}",
- "PKG_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing '
- 'letters, numbers, ".", "-".',
- "PKG_VERSION_VALUE": "PackageVersion must be single line of text, line: {0}",
- "PKG_FILE_NAME_VALUE": "PackageFileName must be single line of text, line: {0}",
- "PKG_SUPPL_VALUE": "PackageSupplier must be Organization, Person or NOASSERTION, line: {0}",
- "PKG_ORIG_VALUE": "PackageOriginator must be Organization, Person or NOASSERTION, line: {0}",
- "PKG_DOWN_VALUE": "PackageDownloadLocation must be a url or NONE or NOASSERTION, line: {0}",
- "PKG_FILES_ANALYZED_VALUE": "FilesAnalyzed must be a boolean value, line: {0}",
- "PKG_HOME_VALUE": "PackageHomePage must be a url or NONE or NOASSERTION, line: {0}",
- "PKG_SRC_INFO_VALUE": "PackageSourceInfo must be free form text or single line of text, line: {0}",
- "PKG_CHKSUM_VALUE": "PackageChecksum must be a single line of text, line: {0}",
- "PKG_LICS_CONC_VALUE": "PackageLicenseConcluded must be NOASSERTION, NONE, license identifier "
- "or license list, line: {0}",
- "PKG_LIC_FFILE_VALUE": "PackageLicenseInfoFromFiles must be, line: {0}",
- "PKG_LICS_DECL_VALUE": "PackageLicenseDeclared must be NOASSERTION, NONE, license identifier "
- "or license list, line: {0}",
- "PKG_LICS_COMMENT_VALUE": "PackageLicenseComments must be free form text or single line of text, line: {0}",
- "PKG_ATTRIBUTION_TEXT_VALUE": "PackageAttributionText must be free form text or single line of text, line: {0}",
- "PKG_SUM_VALUE": "PackageSummary must be free form text or single line of text, line: {0}",
- "PKG_DESC_VALUE": "PackageDescription must be free form text or single line of text, line: {0}",
- "PKG_COMMENT_VALUE": "PackageComment must be free form text or single line of text, line: {0}",
- "PKG_EXT_REF_VALUE": "ExternalRef must contain category, type, and locator in the standard format, line:{0}.",
- "PKG_EXT_REF_COMMENT_VALUE": "ExternalRefComment must be free form text or single line of text, line:{0}",
- "PKG_VERF_CODE_VALUE": "VerificationCode doesn't match verifcode form, line:{0}",
- "PRIMARY_PACKAGE_PURPOSE_VALUE": 'PrimaryPackagePurpose must be one of APPLICATION, FRAMEWORK, LIBRARY, CONTAINER, '
- 'OPERATING-SYSTEM, DEVICE, FIRMWARE, SOURCE, ARCHIVE, FILE, INSTALL, OTHER',
- "BUILT_DATE_VALUE_TYPE": "Built date value must be date in ISO 8601 format, line: {0}",
- "RELEASE_DATE_VALUE_TYPE": "Release date value must be date in ISO 8601 format, line: {0}",
- "VALID_UNTIL_DATE_VALUE_TYPE": "Valid until date value must be date in ISO 8601 format, line: {0}",
- "FILE_NAME_VALUE": "FileName must be a single line of text, line: {0}",
- "FILE_COMMENT_VALUE": "FileComment must be free form text or single line of text, line:{0}",
- "FILE_TYPE_VALUE": 'FileType must be one of SOURCE, BINARY, ARCHIVE, APPLICATION, AUDIO, IMAGE, TEXT, VIDEO, '
- 'DOCUMENTATION, SPDX, OTHER, line: {0}',
- "FILE_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing '
- 'letters, numbers, ".", "-".',
- "FILE_ATTRIBUTION_TEXT_VALUE": "FileAttributionText must be free form text or single line of text, line: {0}",
- "FILE_CHKSUM_VALUE": "FileChecksum must be a single line of text starting with 'SHA1:', line:{0}",
- "FILE_LICS_CONC_VALUE": "LicenseConcluded must be NOASSERTION, NONE, license identifier or license list, line:{0}",
- "FILE_LICS_INFO_VALUE": "LicenseInfoInFile must be NOASSERTION, NONE or license identifier, line: {0}",
- "FILE_LICS_COMMENT_VALUE": "FileLicenseComments must be free form text or single line of text, line: {0}",
- "FILE_CR_TEXT_VALUE": "FileCopyrightText must be one of NOASSERTION, NONE, free form text or single line of text,"
- "line: {0}",
- "FILE_NOTICE_VALUE": "FileNotice must be free form text or single line of text, line: {0}",
- "FILE_CONTRIB_VALUE": "FileContributor must be a single line, line: {0}",
- "FILE_DEP_VALUE": "FileDependency must be a single line, line: {0}",
- "ART_PRJ_NAME_VALUE": "ArtifactOfProjectName must be a single line, line: {0}",
- "FILE_ART_OPT_ORDER": "ArtificatOfProjectHomePage and ArtificatOfProjectURI must immediately "
- "follow ArtifactOfProjectName, line: {0}",
- "ART_PRJ_HOME_VALUE": "ArtificatOfProjectHomePage must be a URL or UNKNOWN, line: {0}",
- "ART_PRJ_URI_VALUE": "ArtificatOfProjectURI must be a URI or UNKNOWN, line: {0}",
- "UNKNOWN_TAG": "Found unknown tag : {0} at line: {1}",
- "LICS_ID_VALUE": "LicenseID must start with 'LicenseRef-', line: {0}",
- "LICS_TEXT_VALUE": "ExtractedText must be free form text or single line of text, line: {0}",
- "LICS_NAME_VALE": "LicenseName must be single line of text or NOASSERTION, line: {0}",
- "LICS_COMMENT_VALUE": "LicenseComment must be free form text or single line of text, line: {0}",
- "LICS_CRS_REF_VALUE": "LicenseCrossReference must be uri as single line of text, line: {0}",
- "RELATIONSHIP_VALUE": "Relationship types must be one of the defined types, line: {0}",
- "RELATIONSHIP_COMMENT_VALUE": "RelationshipComment value must be free form text between tags "
- "or single line of text, line:{0}",
- "PKG_CPY_TEXT_VALUE": "Package copyright text must be free form text or single line of text, line: {0}",
- "SNIP_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string '
- 'containing letters, numbers, ".", "-".',
- "SNIPPET_NAME_VALUE": "SnippetName must be a single line of text, line: {0}",
- "SNIP_COMMENT_VALUE": "SnippetComment must be free form text or single line of text, line: {0}",
- "SNIP_COPYRIGHT_VALUE": "SnippetCopyrightText must be one of NOASSERTION, NONE, "
- "free form text or single line of text, line: {0}",
- "SNIP_LICS_COMMENT_VALUE": "SnippetLicenseComments must be free form text or single line of text, line: {0}",
- "SNIPPET_ATTRIBUTION_TEXT_VALUE": "SnippetAttributionText must be free form text or single line of text, line: {0}",
- "SNIP_FILE_SPDXID_VALUE": 'SnippetFromFileSPDXID must be ["DocumentRef-"[idstring]":"] SPDXID '
- "where DocumentRef-[idstring]: is an optional reference to an external"
- "SPDX Document and SPDXID is a string containing letters, "
- 'numbers, ".", "-".',
- "SNIP_LICS_CONC_VALUE": "SnippetLicenseConcluded must be NOASSERTION, NONE, license identifier "
- "or license list, line:{0}",
- "SNIP_LICS_INFO_VALUE": "LicenseInfoInSnippet must be NOASSERTION, NONE or license identifier, line: {0}",
-}
-
-
-class Parser(object):
- def __init__(self, builder, logger):
- self.tokens = Lexer.tokens
- self.builder = builder
- self.logger = logger
- self.error = False
- self.license_list_parser = utils.LicenseListParser()
- self.license_list_parser.build(write_tables=0, debug=0)
-
- def p_start_1(self, p):
- "start : start attrib "
- pass
-
- def p_start_2(self, p):
- "start : attrib "
- pass
-
- def p_attrib(self, p):
- """attrib : spdx_version
- | spdx_id
- | data_lics
- | doc_name
- | ext_doc_ref
- | doc_comment
- | doc_namespace
- | creator
- | created
- | creator_comment
- | locs_list_ver
- | reviewer
- | review_date
- | review_comment
- | annotator
- | annotation_date
- | annotation_comment
- | annotation_type
- | annotation_spdx_id
- | relationship
- | relationship_comment
- | package_name
- | package_version
- | pkg_down_location
- | pkg_files_analyzed
- | pkg_home
- | pkg_summary
- | pkg_src_info
- | pkg_file_name
- | pkg_supplier
- | pkg_orig
- | pkg_chksum
- | pkg_verif
- | pkg_desc
- | pkg_comment
- | pkg_attribution_text
- | pkg_lic_decl
- | pkg_lic_conc
- | pkg_lic_ff
- | pkg_lic_comment
- | pkg_cr_text
- | pkg_ext_ref
- | pkg_ext_ref_comment
- | primary_package_purpose
- | built_date
- | release_date
- | valid_until_date
- | file_name
- | file_type
- | file_chksum
- | file_conc
- | file_lics_info
- | file_cr_text
- | file_lics_comment
- | file_attribution_text
- | file_notice
- | file_comment
- | file_contrib
- | file_dep
- | file_artifact
- | snip_spdx_id
- | snip_name
- | snip_comment
- | snippet_attribution_text
- | snip_cr_text
- | snip_lic_comment
- | snip_file_spdx_id
- | snip_lics_conc
- | snip_lics_info
- | snip_byte_range
- | snip_line_range
- | extr_lic_id
- | extr_lic_text
- | extr_lic_name
- | lic_xref
- | lic_comment
- | unknown_tag
- """
- pass
-
- def more_than_one_error(self, tag, line):
- self.error = True
- msg = ERROR_MESSAGES["MORE_THAN_ONE"].format(tag, line)
- self.logger.log(msg)
-
- def order_error(self, first_tag, second_tag, line):
- """Reports an OrderError. Error message will state that
- first_tag came before second_tag.
- """
- self.error = True
- msg = ERROR_MESSAGES["A_BEFORE_B"].format(first_tag, second_tag, line)
- self.logger.log(msg)
-
- def p_lic_xref_1(self, p):
- """lic_xref : LICS_CRS_REF LINE"""
- try:
- value = p[2]
- self.builder.add_lic_xref(self.document, value)
- except OrderError:
- self.order_error("LicenseCrossReference", "LicenseName", p.lineno(1))
-
- def p_lic_xref_2(self, p):
- """lic_xref : LICS_CRS_REF error"""
- self.error = True
- msg = ERROR_MESSAGES["LICS_CRS_REF_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_lic_comment_1(self, p):
- """lic_comment : LICS_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_lic_comment(self.document, value)
- except OrderError:
- self.order_error("LicenseComment", "LicenseID", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("LicenseComment", p.lineno(1))
-
- def p_lic_comment_2(self, p):
- """lic_comment : LICS_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["LICS_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_extr_lic_name_1(self, p):
- """extr_lic_name : LICS_NAME extr_lic_name_value"""
- try:
- self.builder.set_lic_name(self.document, p[2])
- except OrderError:
- self.order_error("LicenseName", "LicenseID", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("LicenseName", p.lineno(1))
-
- def p_extr_lic_name_2(self, p):
- """extr_lic_name : LICS_NAME error"""
- self.error = True
- msg = ERROR_MESSAGES["LICS_NAME_VALE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_extr_lic_name_value_1(self, p):
- """extr_lic_name_value : LINE"""
- p[0] = p[1]
-
- def p_extr_lic_name_value_2(self, p):
- """extr_lic_name_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_extr_lic_text_1(self, p):
- """extr_lic_text : LICS_TEXT text_or_line"""
- try:
- value = p[2]
- self.builder.set_lic_text(self.document, value)
- except OrderError:
- self.order_error("ExtractedText", "LicenseID", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("ExtractedText", p.lineno(1))
-
- def p_extr_lic_text_2(self, p):
- """extr_lic_text : LICS_TEXT error"""
- self.error = True
- msg = ERROR_MESSAGES["LICS_TEXT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_extr_lic_id_1(self, p):
- """extr_lic_id : LICS_ID LINE"""
- try:
- value = p[2]
- self.builder.set_lic_id(self.document, value)
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["LICS_ID_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_extr_lic_id_2(self, p):
- """extr_lic_id : LICS_ID error"""
- self.error = True
- msg = ERROR_MESSAGES["LICS_ID_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_unknown_tag(self, p):
- """unknown_tag : UNKNOWN_TAG LINE"""
- self.error = True
- msg = ERROR_MESSAGES["UNKNOWN_TAG"].format(p[1], p.lineno(1))
- self.logger.log(msg)
-
- def p_file_artifact_1(self, p):
- """file_artifact : prj_name_art file_art_rest
- | prj_name_art
- """
- pass
-
- def p_file_artifact_2(self, p):
- """file_artifact : prj_name_art error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_ART_OPT_ORDER"].format(p.lineno(2))
- self.logger.log(msg)
-
- def p_file_art_rest(self, p):
- """file_art_rest : prj_home_art prj_uri_art
- | prj_uri_art prj_home_art
- | prj_home_art
- | prj_uri_art
- """
- pass
-
- def p_prj_uri_art_1(self, p):
- """prj_uri_art : ART_PRJ_URI UN_KNOWN"""
- try:
- self.builder.set_file_atrificat_of_project(
- self.document, "uri", utils.UnKnown()
- )
- except OrderError:
- self.order_error("ArtificatOfProjectURI", "FileName", p.lineno(1))
-
- def p_prj_uri_art_2(self, p):
- """prj_uri_art : ART_PRJ_URI LINE"""
- try:
- value = p[2]
- self.builder.set_file_atrificat_of_project(self.document, "uri", value)
- except OrderError:
- self.order_error("ArtificatOfProjectURI", "FileName", p.lineno(1))
-
- def p_prj_uri_art_3(self, p):
- """prj_uri_art : ART_PRJ_URI error"""
- self.error = True
- msg = ERROR_MESSAGES["ART_PRJ_URI_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_prj_home_art_1(self, p):
- """prj_home_art : ART_PRJ_HOME LINE"""
- try:
- self.builder.set_file_atrificat_of_project(self.document, "home", p[2])
- except OrderError:
- self.order_error("ArtificatOfProjectHomePage", "FileName", p.lineno(1))
-
- def p_prj_home_art_2(self, p):
- """prj_home_art : ART_PRJ_HOME UN_KNOWN"""
- try:
- self.builder.set_file_atrificat_of_project(
- self.document, "home", utils.UnKnown()
- )
- except OrderError:
- self.order_error("ArtifactOfProjectName", "FileName", p.lineno(1))
-
- def p_prj_home_art_3(self, p):
- """prj_home_art : ART_PRJ_HOME error"""
- self.error = True
- msg = ERROR_MESSAGES["ART_PRJ_HOME_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_prj_name_art_1(self, p):
- """prj_name_art : ART_PRJ_NAME LINE"""
- try:
- value = p[2]
- self.builder.set_file_atrificat_of_project(self.document, "name", value)
- except OrderError:
- self.order_error("ArtifactOfProjectName", "FileName", p.lineno(1))
-
- def p_prj_name_art_2(self, p):
- """prj_name_art : ART_PRJ_NAME error"""
- self.error = True
- msg = ERROR_MESSAGES["ART_PRJ_NAME_VALUE"].format(p.lineno())
- self.logger.log(msg)
-
- def p_file_dep_1(self, p):
- """file_dep : FILE_DEP LINE"""
- try:
- value = p[2]
- self.builder.add_file_dep(self.document, value)
- except OrderError:
- self.order_error("FileDependency", "FileName", p.lineno(1))
-
- def p_file_dep_2(self, p):
- """file_dep : FILE_DEP error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_DEP_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_contrib_1(self, p):
- """file_contrib : FILE_CONTRIB LINE"""
- try:
- value = p[2]
- self.builder.add_file_contribution(self.document, value)
- except OrderError:
- self.order_error("FileContributor", "FileName", p.lineno(1))
-
- def p_file_contrib_2(self, p):
- """file_contrib : FILE_CONTRIB error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_CONTRIB_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_notice_1(self, p):
- """file_notice : FILE_NOTICE text_or_line"""
- try:
- value = p[2]
- self.builder.set_file_notice(self.document, value)
- except OrderError:
- self.order_error("FileNotice", "FileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("FileNotice", p.lineno(1))
-
- def p_file_notice_2(self, p):
- """file_notice : FILE_NOTICE error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_NOTICE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_cr_text_1(self, p):
- """file_cr_text : FILE_CR_TEXT file_cr_value"""
- try:
- self.builder.set_file_copyright(self.document, p[2])
- except OrderError:
- self.order_error("FileCopyrightText", "FileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("FileCopyrightText", p.lineno(1))
-
- def p_file_cr_text_2(self, p):
- """file_cr_text : FILE_CR_TEXT error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_CR_TEXT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_cr_value_1(self, p):
- """file_cr_value : text_or_line"""
- p[0] = p[1]
-
- def p_file_cr_value_2(self, p):
- """file_cr_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_file_cr_value_3(self, p):
- """file_cr_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_file_lics_comment_1(self, p):
- """file_lics_comment : FILE_LICS_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_file_license_comment(self.document, value)
- except OrderError:
- self.order_error("LicenseComments", "FileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("LicenseComments", p.lineno(1))
-
- def p_file_lics_comment_2(self, p):
- """file_lics_comment : FILE_LICS_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_LICS_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_attribution_text_1(self, p):
- """file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line"""
- try:
- value = p[2]
- self.builder.set_file_attribution_text(self.document, value)
- except CardinalityError:
- self.more_than_one_error("FileAttributionText", p.lineno(1))
- except OrderError:
- self.order_error("FileAttributionText", "FileAttributionText", p.lineno(1))
-
- def p_file_attribution_text_2(self, p):
- """file_attribution_text : FILE_ATTRIBUTION_TEXT error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_ATTRIBUTION_TEXT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_lics_info_1(self, p):
- """file_lics_info : FILE_LICS_INFO file_lic_info_value"""
- try:
- self.builder.set_file_license_in_file(self.document, p[2])
- except OrderError:
- self.order_error("LicenseInfoInFile", "FileName", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["FILE_LICS_INFO_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_lics_info_2(self, p):
- """file_lics_info : FILE_LICS_INFO error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_LICS_INFO_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_lic_info_value_1(self, p):
- """file_lic_info_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_file_lic_info_value_2(self, p):
- """file_lic_info_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- # License Identifier
- def p_file_lic_info_value_3(self, p):
- """file_lic_info_value : LINE"""
- value = p[1]
- p[0] = license.License.from_identifier(value)
-
- def p_conc_license_1(self, p):
- """conc_license : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_conc_license_2(self, p):
- """conc_license : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_conc_license_3(self, p):
- """conc_license : LINE"""
- value = p[1]
- ref_re = re.compile("LicenseRef-.+", re.UNICODE)
- if (p[1] in config.LICENSE_MAP.keys()) or (ref_re.match(p[1]) is not None):
- p[0] = license.License.from_identifier(value)
- else:
- p[0] = self.license_list_parser.parse(value)
-
- def p_file_name_1(self, p):
- """file_name : FILE_NAME LINE"""
- try:
- value = p[2]
- self.builder.set_file_name(self.document, value)
- self.builder.set_current_file_name(value)
- self.builder.set_current_file_id(None)
-
- except OrderError:
- self.order_error("FileName", "PackageName", p.lineno(1))
-
- def p_file_name_2(self, p):
- """file_name : FILE_NAME error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_NAME_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_spdx_id(self, p):
- """spdx_id : SPDX_ID LINE"""
- value = p[2]
- try:
- # first parsed spdx id belongs to the document
- if not self.builder.doc_spdx_id_set:
- self.builder.set_doc_spdx_id(self.document, value)
-
- # else if a package is in scope that doesn't have an id yet, the parsed spdx id belongs to the package
- elif self.builder.current_package_has_name() \
- and not self.builder.current_package_has_id():
- self.builder.set_pkg_spdx_id(self.document, value)
- self.builder.set_current_package_id(value)
-
- # else if a file is in scope that doesn't have an id yet, the parsed spdx id belongs to the file
- elif self.builder.current_file_has_name() \
- and not self.builder.current_file_has_id():
- self.builder.set_file_spdx_id(self.document, value)
- self.builder.set_current_file_id(value)
- if self.builder.has_current_package():
- self.builder.add_relationship(self.document,
- self.builder.current_package["spdx_id"] + " CONTAINS " + value)
- else:
- raise SPDXValueError("SPDX ID couldn't be assigned properly. Line no. {0}")
- except SPDXValueError as err:
- self.error = True
- self.logger.log(err.msg.format(p.lineno(2)))
-
- def p_file_comment_1(self, p):
- """file_comment : FILE_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_file_comment(self.document, value)
- except OrderError:
- self.order_error("FileComment", "FileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("FileComment", p.lineno(1))
-
- def p_file_comment_2(self, p):
- """file_comment : FILE_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_type_1(self, p):
- """file_type : FILE_TYPE file_type_value"""
- try:
- self.builder.set_file_type(self.document, p[2])
- except OrderError:
- self.order_error("FileType", "FileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("FileType", p.lineno(1))
-
- def p_file_type_2(self, p):
- """file_type : FILE_TYPE error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_TYPE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_chksum_1(self, p):
- """file_chksum : FILE_CHKSUM CHKSUM"""
- try:
- value = p[2]
- self.builder.set_file_checksum(self.document, value)
- except OrderError:
- self.order_error("FileChecksum", "FileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("FileChecksum", p.lineno(1))
-
- def p_file_chksum_2(self, p):
- """file_chksum : FILE_CHKSUM error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_CHKSUM_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_conc_1(self, p):
- """file_conc : FILE_LICS_CONC conc_license"""
- try:
- self.builder.set_concluded_license(self.document, p[2])
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["FILE_LICS_CONC_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
- except OrderError:
- self.order_error("LicenseConcluded", "FileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("LicenseConcluded", p.lineno(1))
-
- def p_file_conc_2(self, p):
- """file_conc : FILE_LICS_CONC error"""
- self.error = True
- msg = ERROR_MESSAGES["FILE_LICS_CONC_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_file_type_value(self, p):
- """file_type_value : SOURCE
- | BINARY
- | ARCHIVE
- | APPLICATION
- | AUDIO
- | IMAGE
- | FILETYPE_TEXT
- | VIDEO
- | DOCUMENTATION
- | SPDX
- | OTHER
- """
- p[0] = p[1]
-
- def p_annotation_type_value(self, p):
- """annotation_type_value : OTHER
- | REVIEW
- """
- p[0] = p[1]
-
- def p_pkg_desc_1(self, p):
- """pkg_desc : PKG_DESC text_or_line"""
- try:
- value = p[2]
- self.builder.set_pkg_desc(self.document, value)
- except CardinalityError:
- self.more_than_one_error("PackageDescription", p.lineno(1))
- except OrderError:
- self.order_error("PackageDescription", "PackageFileName", p.lineno(1))
-
- def p_pkg_desc_2(self, p):
- """pkg_desc : PKG_DESC error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_DESC_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_comment_1(self, p):
- """pkg_comment : PKG_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_pkg_comment(self.document, value)
- except CardinalityError:
- self.more_than_one_error("PackageComment", p.lineno(1))
- except OrderError:
- self.order_error("PackageComment", "PackageFileName", p.lineno(1))
-
- def p_pkg_comment_2(self, p):
- """pkg_comment : PKG_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_attribution_text_1(self, p):
- """pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line"""
- try:
- value = p[2]
- self.builder.set_pkg_attribution_text(self.document, value)
- except CardinalityError:
- self.more_than_one_error("PackageAttributionText", p.lineno(1))
- except OrderError:
- self.order_error(
- "PackageAttributionText", "PackageAttributionText", p.lineno(1)
- )
-
- def p_pkg_attribution_text_2(self, p):
- """pkg_attribution_text : PKG_ATTRIBUTION_TEXT error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_ATTRIBUTION_TEXT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_summary_1(self, p):
- """pkg_summary : PKG_SUM text_or_line"""
- try:
- value = p[2]
- self.builder.set_pkg_summary(self.document, value)
- except OrderError:
- self.order_error("PackageSummary", "PackageFileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageSummary", p.lineno(1))
-
- def p_pkg_summary_2(self, p):
- """pkg_summary : PKG_SUM error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_SUM_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_cr_text_1(self, p):
- """pkg_cr_text : PKG_CPY_TEXT pkg_cr_text_value"""
- try:
- self.builder.set_pkg_cr_text(self.document, p[2])
- except OrderError:
- self.order_error("PackageCopyrightText", "PackageFileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageCopyrightText", p.lineno(1))
-
- def p_pkg_cr_text_2(self, p):
- """pkg_cr_text : PKG_CPY_TEXT error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_CPY_TEXT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_ext_refs_1(self, p):
- """pkg_ext_ref : PKG_EXT_REF LINE"""
- try:
- pkg_ext_info = p[2]
- if len(pkg_ext_info.split()) != 3:
- raise SPDXValueError(ERROR_MESSAGES["PKG_EXT_REF_VALUE"].format(p.lineno(2)))
- else:
- pkg_ext_category, pkg_ext_type, pkg_ext_locator = pkg_ext_info.split()
- self.builder.add_pkg_ext_refs(
- self.document, pkg_ext_category, pkg_ext_type, pkg_ext_locator
- )
- except SPDXValueError as err:
- self.error = True
- self.logger.log(err.msg)
-
- def p_pkg_ext_refs_2(self, p):
- """pkg_ext_ref : PKG_EXT_REF error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_EXT_REF_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_ext_ref_comment_1(self, p):
- """pkg_ext_ref_comment : PKG_EXT_REF_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.add_pkg_ext_ref_comment(self.document, value)
- except CardinalityError:
- self.more_than_one_error("ExternalRefComment", p.lineno(1))
-
- def p_pkg_ext_ref_comment_2(self, p):
- """pkg_ext_ref_comment : PKG_EXT_REF_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_EXT_REF_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_cr_text_value_1(self, p):
- """pkg_cr_text_value : text_or_line"""
- p[0] = p[1]
-
- def p_pkg_cr_text_value_2(self, p):
- """pkg_cr_text_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_pkg_cr_text_value_3(self, p):
- """pkg_cr_text_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_pkg_lic_comment_1(self, p):
- """pkg_lic_comment : PKG_LICS_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_pkg_license_comment(self.document, value)
- except OrderError:
- self.order_error("PackageLicenseComments", "PackageFileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageLicenseComments", p.lineno(1))
-
- def p_pkg_lic_comment_2(self, p):
- """pkg_lic_comment : PKG_LICS_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_LICS_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_lic_decl_1(self, p):
- """pkg_lic_decl : PKG_LICS_DECL conc_license"""
- try:
- self.builder.set_pkg_license_declared(self.document, p[2])
- except OrderError:
- self.order_error("PackageLicenseDeclared", "PackageName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageLicenseDeclared", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["PKG_LICS_DECL_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_lic_decl_2(self, p):
- """pkg_lic_decl : PKG_LICS_DECL error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_LICS_DECL_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_lic_ff_1(self, p):
- """pkg_lic_ff : PKG_LICS_FFILE pkg_lic_ff_value"""
- try:
- self.builder.set_pkg_license_from_file(self.document, p[2])
- except OrderError:
- self.order_error("PackageLicenseInfoFromFiles", "PackageName", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["PKG_LIC_FFILE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_lic_ff_value_1(self, p):
- """pkg_lic_ff_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_pkg_lic_ff_value_2(self, p):
- """pkg_lic_ff_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_pkg_lic_ff_value_3(self, p):
- """pkg_lic_ff_value : LINE"""
- value = p[1]
- p[0] = license.License.from_identifier(value)
-
- def p_pkg_lic_ff_2(self, p):
- """pkg_lic_ff : PKG_LICS_FFILE error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_LIC_FFILE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_lic_conc_1(self, p):
- """pkg_lic_conc : PKG_LICS_CONC conc_license"""
- try:
- self.builder.set_pkg_licenses_concluded(self.document, p[2])
- except CardinalityError:
- self.more_than_one_error("PackageLicenseConcluded", p.lineno(1))
- except OrderError:
- self.order_error("PackageLicenseConcluded", "PackageFileName", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["PKG_LICS_CONC_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_lic_conc_2(self, p):
- """pkg_lic_conc : PKG_LICS_CONC error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_LICS_CONC_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_src_info_1(self, p):
- """pkg_src_info : PKG_SRC_INFO text_or_line"""
- try:
- value = p[2]
- self.builder.set_pkg_source_info(self.document, value)
- except CardinalityError:
- self.more_than_one_error("PackageSourceInfo", p.lineno(1))
- except OrderError:
- self.order_error("PackageSourceInfo", "PackageFileName", p.lineno(1))
-
- def p_pkg_src_info_2(self, p):
- """pkg_src_info : PKG_SRC_INFO error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_SRC_INFO_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_chksum_1(self, p):
- """pkg_chksum : PKG_CHKSUM CHKSUM"""
- try:
- value = p[2]
- self.builder.set_pkg_checksum(self.document, value)
- except OrderError:
- self.order_error("PackageChecksum", "PackageFileName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageChecksum", p.lineno(1))
-
- def p_pkg_chksum_2(self, p):
- """pkg_chksum : PKG_CHKSUM error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_CHKSUM_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_verif_1(self, p):
- """pkg_verif : PKG_VERF_CODE LINE"""
- try:
- value = p[2]
- self.builder.set_pkg_verif_code(self.document, value)
- except OrderError:
- self.order_error("PackageVerificationCode", "PackageName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageVerificationCode", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["PKG_VERF_CODE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_verif_2(self, p):
- """pkg_verif : PKG_VERF_CODE error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_VERF_CODE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_home_1(self, p):
- """pkg_home : PKG_HOME pkg_home_value"""
- try:
- self.builder.set_pkg_home(self.document, p[2])
- except OrderError:
- self.order_error("PackageHomePage", "PackageName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageHomePage", p.lineno(1))
-
- def p_pkg_home_2(self, p):
- """pkg_home : PKG_HOME error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_HOME_VALUE"]
- self.logger.log(msg)
-
- def p_pkg_home_value_1(self, p):
- """pkg_home_value : LINE"""
- p[0] = p[1]
-
- def p_pkg_home_value_2(self, p):
- """pkg_home_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_pkg_home_value_3(self, p):
- """pkg_home_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_pkg_down_location_1(self, p):
- """pkg_down_location : PKG_DOWN pkg_down_value"""
- try:
- self.builder.set_pkg_down_location(self.document, p[2])
- except OrderError:
- self.order_error("PackageDownloadLocation", "PackageName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageDownloadLocation", p.lineno(1))
-
- def p_pkg_down_location_2(self, p):
- """pkg_down_location : PKG_DOWN error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_DOWN_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_files_analyzed_1(self, p):
- """pkg_files_analyzed : PKG_FILES_ANALYZED LINE"""
- try:
- value = p[2]
- self.builder.set_pkg_files_analyzed(self.document, value)
- except CardinalityError:
- self.more_than_one_error("FilesAnalyzed", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["PKG_FILES_ANALYZED_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_files_analyzed_2(self, p):
- """pkg_files_analyzed : PKG_FILES_ANALYZED error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_FILES_ANALYZED_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_down_value_1(self, p):
- """pkg_down_value : LINE """
- p[0] = p[1]
-
- def p_pkg_down_value_2(self, p):
- """pkg_down_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_pkg_down_value_3(self, p):
- """pkg_down_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_pkg_orig_1(self, p):
- """pkg_orig : PKG_ORIG pkg_supplier_values"""
- try:
- self.builder.set_pkg_originator(self.document, p[2])
- except OrderError:
- self.order_error("PackageOriginator", "PackageName", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["PKG_ORIG_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("PackageOriginator", p.lineno(1))
-
- def p_pkg_orig_2(self, p):
- """pkg_orig : PKG_ORIG error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_ORIG_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_supplier_1(self, p):
- """pkg_supplier : PKG_SUPPL pkg_supplier_values"""
- try:
- self.builder.set_pkg_supplier(self.document, p[2])
- except OrderError:
- self.order_error("PackageSupplier", "PackageName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageSupplier", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["PKG_SUPPL_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_supplier_2(self, p):
- """pkg_supplier : PKG_SUPPL error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_SUPPL_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_pkg_supplier_values_1(self, p):
- """pkg_supplier_values : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_pkg_supplier_values_2(self, p):
- """pkg_supplier_values : entity"""
- p[0] = p[1]
-
- def p_pkg_file_name(self, p):
- """pkg_file_name : PKG_FILE_NAME LINE"""
- try:
- value = p[2]
- self.builder.set_pkg_file_name(self.document, value)
- except OrderError:
- self.order_error("PackageFileName", "PackageName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageFileName", p.lineno(1))
-
- def p_pkg_file_name_1(self, p):
- """pkg_file_name : PKG_FILE_NAME error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_FILE_NAME_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_package_version_1(self, p):
- """package_version : PKG_VERSION LINE"""
- try:
- value = p[2]
- self.builder.set_pkg_vers(self.document, value)
- except OrderError:
- self.order_error("PackageVersion", "PackageName", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("PackageVersion", p.lineno(1))
-
- def p_package_version_2(self, p):
- """package_version : PKG_VERSION error"""
- self.error = True
- msg = ERROR_MESSAGES["PKG_VERSION_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_package_name(self, p):
- """package_name : PKG_NAME LINE"""
- try:
- value = p[2]
- self.builder.create_package(self.document, value)
- self.builder.set_current_package_name(value)
- self.builder.set_current_package_id(None)
- self.builder.set_current_file_name(None) # start of a new package implies new file
- self.builder.set_current_file_id(None)
- except CardinalityError:
- self.more_than_one_error("PackageName", p.lineno(1))
-
- def p_package_name_1(self, p):
- """package_name : PKG_NAME error"""
- self.error = True
- msg = ERROR_MESSAGES["PACKAGE_NAME_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_primary_package_purpose_1(self, p):
- """primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value"""
- try:
- self.builder.set_pkg_primary_package_purpose(self.document, p[2])
- except CardinalityError:
- self.more_than_one_error("PrimaryPackagePurpose", p.lineno(1))
-
- def p_primary_package_purpose_2(self, p):
- """primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error"""
- self.error = True
- msg = ERROR_MESSAGES["PRIMARY_PACKAGE_PURPOSE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_primary_package_purpose_value(self, p):
- """primary_package_purpose_value : APPLICATION
- | FRAMEWORK
- | LIBRARY
- | CONTAINER
- | OPERATING_SYSTEM
- | DEVICE
- | FIRMWARE
- | SOURCE
- | ARCHIVE
- | FILE
- | INSTALL
- | OTHER
- """
- p[0] = p[1]
-
- def p_built_date_1(self, p):
- """built_date : BUILT_DATE DATE"""
- try:
- value = p[2]
- self.builder.set_pkg_built_date(self.document, value)
- except CardinalityError:
- self.more_than_one_error("BuiltDate", p.lineno(1))
-
- def p_built_date_2(self, p):
- """built_date : BUILT_DATE error"""
- self.error = True
- msg = ERROR_MESSAGES["BUILT_DATE_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_release_date_1(self, p):
- """release_date : RELEASE_DATE DATE"""
- try:
- value = p[2]
- self.builder.set_pkg_release_date(self.document, value)
- except CardinalityError:
- self.more_than_one_error("ReleaseDate", p.lineno(1))
-
- def p_release_date_2(self, p):
- """release_date : RELEASE_DATE error"""
- self.error = True
- msg = ERROR_MESSAGES["RELEASE_DATE_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_valid_until_date_1(self, p):
- """valid_until_date : VALID_UNTIL_DATE DATE"""
- try:
- value = p[2]
- self.builder.set_pkg_valid_until_date(self.document, value)
- except CardinalityError:
- self.more_than_one_error("ValidUntilDate", p.lineno(1))
-
- def p_valid_until_date_2(self, p):
- """valid_until_date : VALID_UNTIL_DATE error"""
- self.error = True
- msg = ERROR_MESSAGES["VALID_UNTIL_DATE_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snip_spdx_id(self, p):
- """snip_spdx_id : SNIPPET_SPDX_ID LINE"""
- try:
- value = p[2]
- self.builder.create_snippet(self.document, value)
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["SNIP_SPDX_ID_VALUE"].format(p.lineno(2))
- self.logger.log(msg)
-
- def p_snip_spdx_id_1(self, p):
- """snip_spdx_id : SNIPPET_SPDX_ID error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIP_SPDX_ID_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_name(self, p):
- """snip_name : SNIPPET_NAME LINE"""
- try:
- value = p[2]
- self.builder.set_snippet_name(self.document, value)
- except OrderError:
- self.order_error("SnippetName", "SnippetSPDXID", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("SnippetName", p.lineno(1))
-
- def p_snippet_name_1(self, p):
- """snip_name : SNIPPET_NAME error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIPPET_NAME_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_comment(self, p):
- """snip_comment : SNIPPET_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_snippet_comment(self.document, value)
- except OrderError:
- self.order_error("SnippetComment", "SnippetSPDXID", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["SNIP_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("SnippetComment", p.lineno(1))
-
- def p_snippet_comment_1(self, p):
- """snip_comment : SNIPPET_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIP_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_attribution_text_1(self, p):
- """snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line"""
- try:
- value = p[2]
- self.builder.set_snippet_attribution_text(self.document, value)
- except CardinalityError:
- self.more_than_one_error("SnippetAttributionText", p.lineno(1))
- except OrderError:
- self.order_error(
- "SnippetAttributionText", "SnippetAttributionText", p.lineno(1)
- )
-
- def p_snippet_attribution_text_2(self, p):
- """snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIPPET_ATTRIBUTION_TEXT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_cr_text(self, p):
- """snip_cr_text : SNIPPET_CR_TEXT snip_cr_value"""
- try:
- self.builder.set_snippet_copyright(self.document, p[2])
- except OrderError:
- self.order_error("SnippetCopyrightText", "SnippetSPDXID", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["SNIP_COPYRIGHT_VALUE"].format(p.lineno(2))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("SnippetCopyrightText", p.lineno(1))
-
- def p_snippet_cr_text_1(self, p):
- """snip_cr_text : SNIPPET_CR_TEXT error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIP_COPYRIGHT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_cr_value_1(self, p):
- """snip_cr_value : text_or_line"""
- p[0] = p[1]
-
- def p_snippet_cr_value_2(self, p):
- """snip_cr_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_snippet_cr_value_3(self, p):
- """snip_cr_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_snippet_lic_comment(self, p):
- """snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_snippet_lic_comment(self.document, value)
- except OrderError:
- self.order_error("SnippetLicenseComments", "SnippetSPDXID", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["SNIP_LICS_COMMENT_VALUE"].format(p.lineno(2))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("SnippetLicenseComments", p.lineno(1))
-
- def p_snippet_lic_comment_1(self, p):
- """snip_lic_comment : SNIPPET_LICS_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIP_LICS_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_text_or_line_value_1(self, p):
- """text_or_line : TEXT"""
- p[0] = p[1]
-
- def p_text_or_line_value_2(self, p):
- """text_or_line : LINE"""
- p[0] = p[1]
-
- def p_snip_from_file_spdxid(self, p):
- """snip_file_spdx_id : SNIPPET_FILE_SPDXID LINE"""
- try:
- value = p[2]
- self.builder.set_snip_from_file_spdxid(self.document, value)
- except OrderError:
- self.order_error("SnippetFromFileSPDXID", "SnippetSPDXID", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["SNIP_FILE_SPDXID_VALUE"].format(p.lineno(2))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("SnippetFromFileSPDXID", p.lineno(1))
-
- def p_snip_from_file_spdxid_1(self, p):
- """snip_file_spdx_id : SNIPPET_FILE_SPDXID error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIP_FILE_SPDXID_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_concluded_license(self, p):
- """snip_lics_conc : SNIPPET_LICS_CONC conc_license"""
- try:
- self.builder.set_snip_concluded_license(self.document, p[2])
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["SNIP_LICS_CONC_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
- except OrderError:
- self.order_error("SnippetLicenseConcluded", "SnippetSPDXID", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("SnippetLicenseConcluded", p.lineno(1))
-
- def p_snippet_concluded_license_1(self, p):
- """snip_lics_conc : SNIPPET_LICS_CONC error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIP_LICS_CONC_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_lics_info(self, p):
- """snip_lics_info : SNIPPET_LICS_INFO snip_lic_info_value"""
- try:
- self.builder.set_snippet_lics_info(self.document, p[2])
- except OrderError:
- self.order_error("LicenseInfoInSnippet", "SnippetSPDXID", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["SNIP_LICS_INFO_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_lics_info_1(self, p):
- """snip_lics_info : SNIPPET_LICS_INFO error"""
- self.error = True
- msg = ERROR_MESSAGES["SNIP_LICS_INFO_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_byte_range(self, p):
- """snip_byte_range : SNIPPET_BYTE_RANGE RANGE"""
- try:
- self.builder.set_snippet_byte_range(self.document, p[2])
- except OrderError:
- self.order_error("SnippetByteRange", "SnippetSPDXID", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = "Value for Snippet ByteRange invalid in line {}.".format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_byte_range_1(self, p):
- """snip_byte_range : SNIPPET_BYTE_RANGE error"""
-
- self.error = True
- msg = "Reading of SnippetByteRange failed for line {}.".format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_line_range(self, p):
- """snip_line_range : SNIPPET_LINE_RANGE RANGE"""
- try:
- self.builder.set_snippet_line_range(self.document, p[2])
- except OrderError:
- self.order_error("SnippetLineRange", "SnippetSPDXID", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = "Value for Snippet LineRange invalid in line {}.".format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snippet_line_range_1(self, p):
- """snip_line_range : SNIPPET_LINE_RANGE error"""
-
- self.error = True
- msg = "Reading of SnippetLineRange failed for line {}.".format(p.lineno(1))
- self.logger.log(msg)
-
- def p_snip_lic_info_value_1(self, p):
- """snip_lic_info_value : NONE"""
- p[0] = utils.SPDXNone()
-
- def p_snip_lic_info_value_2(self, p):
- """snip_lic_info_value : NO_ASSERT"""
- p[0] = utils.NoAssert()
-
- def p_snip_lic_info_value_3(self, p):
- """snip_lic_info_value : LINE"""
- value = p[1]
- p[0] = license.License.from_identifier(value)
-
- def p_reviewer_1(self, p):
- """reviewer : REVIEWER entity"""
- self.builder.add_reviewer(self.document, p[2])
-
- def p_reviewer_2(self, p):
- """reviewer : REVIEWER error"""
- self.error = True
- msg = ERROR_MESSAGES["REVIEWER_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_review_date_1(self, p):
- """review_date : REVIEW_DATE DATE"""
- try:
- value = p[2]
- self.builder.add_review_date(self.document, value)
- except CardinalityError:
- self.more_than_one_error("ReviewDate", p.lineno(1))
- except OrderError:
- self.order_error("ReviewDate", "Reviewer", p.lineno(1))
-
- def p_review_date_2(self, p):
- """review_date : REVIEW_DATE error"""
- self.error = True
- msg = ERROR_MESSAGES["REVIEW_DATE_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_review_comment_1(self, p):
- """review_comment : REVIEW_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.add_review_comment(self.document, value)
- except CardinalityError:
- self.more_than_one_error("ReviewComment", p.lineno(1))
- except OrderError:
- self.order_error("ReviewComment", "Reviewer", p.lineno(1))
-
- def p_review_comment_2(self, p):
- """review_comment : REVIEW_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["REVIEW_COMMENT_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_annotator_1(self, p):
- """annotator : ANNOTATOR entity"""
- self.builder.add_annotator(self.document, p[2])
-
- def p_annotator_2(self, p):
- """annotator : ANNOTATOR error"""
- self.error = True
- msg = ERROR_MESSAGES["ANNOTATOR_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_annotation_date_1(self, p):
- """annotation_date : ANNOTATION_DATE DATE"""
- try:
- value = p[2]
- self.builder.add_annotation_date(self.document, value)
- except CardinalityError:
- self.more_than_one_error("AnnotationDate", p.lineno(1))
- except OrderError:
- self.order_error("AnnotationDate", "Annotator", p.lineno(1))
-
- def p_annotation_date_2(self, p):
- """annotation_date : ANNOTATION_DATE error"""
- self.error = True
- msg = ERROR_MESSAGES["ANNOTATION_DATE_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_annotation_comment_1(self, p):
- """annotation_comment : ANNOTATION_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.add_annotation_comment(self.document, value)
- except CardinalityError:
- self.more_than_one_error("AnnotationComment", p.lineno(1))
- except OrderError:
- self.order_error("AnnotationComment", "Annotator", p.lineno(1))
-
- def p_annotation_comment_2(self, p):
- """annotation_comment : ANNOTATION_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["ANNOTATION_COMMENT_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_annotation_type_1(self, p):
- """annotation_type : ANNOTATION_TYPE annotation_type_value"""
- try:
- value = p[2]
- self.builder.add_annotation_type(self.document, value)
- except CardinalityError:
- self.more_than_one_error("AnnotationType", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["ANNOTATION_TYPE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
- except OrderError:
- self.order_error("AnnotationType", "Annotator", p.lineno(1))
-
- def p_annotation_type_2(self, p):
- """annotation_type : ANNOTATION_TYPE error"""
- self.error = True
- msg = ERROR_MESSAGES["ANNOTATION_TYPE_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_annotation_spdx_id_1(self, p):
- """annotation_spdx_id : ANNOTATION_SPDX_ID LINE"""
- try:
- value = p[2]
- self.builder.set_annotation_spdx_id(self.document, value)
- except CardinalityError:
- self.more_than_one_error("SPDXREF", p.lineno(1))
- except OrderError:
- self.order_error("SPDXREF", "Annotator", p.lineno(1))
-
- def p_annotation_spdx_id_2(self, p):
- """annotation_spdx_id : ANNOTATION_SPDX_ID error"""
- self.error = True
- msg = ERROR_MESSAGES["ANNOTATION_SPDX_ID_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_relationship_1(self, p):
- """relationship : RELATIONSHIP relationship_value"""
- try:
- value = p[2]
- self.builder.add_relationship(self.document, value)
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["RELATIONSHIP_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
- except OrderError:
- self.order_error("Relationship_type", "Relationship", p.lineno(1))
-
- def p_relationship_2(self, p):
- """relationship : RELATIONSHIP error"""
- self.error = True
- msg = ERROR_MESSAGES["RELATIONSHIP_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_relationship_value_with_doc_ref(self, p):
- """relationship_value : DOC_REF_ID LINE"""
- p[0] = p[1] + ":" + p[2]
-
- def p_relationship_value_without_doc_ref(self, p):
- """relationship_value : LINE"""
- p[0] = p[1]
-
- def p_relationship_comment_1(self, p):
- """relationship_comment : RELATIONSHIP_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.add_relationship_comment(self.document, value)
- except OrderError:
- self.order_error("RelationshipComment", "Relationship", p.lineno(1))
- except CardinalityError:
- self.more_than_one_error("RelationshipComment", p.lineno(1))
-
- def p_relationship_comment_2(self, p):
- """relationship_comment : RELATIONSHIP_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["RELATIONSHIP_COMMENT_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_lics_list_ver_1(self, p):
- """locs_list_ver : LIC_LIST_VER LINE"""
- try:
- value = p[2]
- self.builder.set_lics_list_ver(self.document, value)
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["LIC_LIST_VER_VALUE"].format(p[2], p.lineno(2))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("LicenseListVersion", p.lineno(1))
-
- def p_lics_list_ver_2(self, p):
- """locs_list_ver : LIC_LIST_VER error"""
- self.error = True
- msg = ERROR_MESSAGES["LIC_LIST_VER_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_doc_comment_1(self, p):
- """doc_comment : DOC_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_doc_comment(self.document, value)
- except CardinalityError:
- self.more_than_one_error("DocumentComment", p.lineno(1))
-
- def p_doc_comment_2(self, p):
- """doc_comment : DOC_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["DOC_COMMENT_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_doc_namespace_1(self, p):
- """doc_namespace : DOC_NAMESPACE LINE"""
- try:
- value = p[2]
- self.builder.set_doc_namespace(self.document, value)
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["DOC_NAMESPACE_VALUE"].format(p[2], p.lineno(2))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("DocumentNamespace", p.lineno(1))
-
- def p_doc_namespace_2(self, p):
- """doc_namespace : DOC_NAMESPACE error"""
- self.error = True
- msg = ERROR_MESSAGES["DOC_NAMESPACE_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_data_license_1(self, p):
- """data_lics : DOC_LICENSE LINE"""
- try:
- value = p[2]
- self.builder.set_doc_data_lics(self.document, value)
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["DOC_LICENSE_VALUE"].format(p[2], p.lineno(2))
- self.logger.log(msg)
- except CardinalityError:
- self.more_than_one_error("DataLicense", p.lineno(1))
-
- def p_data_license_2(self, p):
- """data_lics : DOC_LICENSE error"""
- self.error = True
- msg = ERROR_MESSAGES["DOC_LICENSE_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_doc_name_1(self, p):
- """doc_name : DOC_NAME LINE"""
- try:
- value = p[2]
- self.builder.set_doc_name(self.document, value)
- except CardinalityError:
- self.more_than_one_error("DocumentName", p.lineno(1))
-
- def p_doc_name_2(self, p):
- """doc_name : DOC_NAME error"""
- self.error = True
- msg = ERROR_MESSAGES["DOC_NAME_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_ext_doc_refs_1(self, p):
- """ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHKSUM"""
- try:
- doc_ref_id = p[2]
- doc_uri = p[3]
- ext_doc_chksum = p[4]
-
- self.builder.add_ext_doc_refs(
- self.document, doc_ref_id, doc_uri, ext_doc_chksum
- )
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["EXT_DOC_REF_VALUE"].format(p.lineno(2))
- self.logger.log(msg)
-
- def p_ext_doc_refs_2(self, p):
- """ext_doc_ref : EXT_DOC_REF error"""
- self.error = True
- msg = ERROR_MESSAGES["EXT_DOC_REF_VALUE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_spdx_version_1(self, p):
- """spdx_version : DOC_VERSION LINE"""
- try:
- value = p[2]
- self.builder.set_doc_version(self.document, value)
- except CardinalityError:
- self.more_than_one_error("SPDXVersion", p.lineno(1))
- except SPDXValueError:
- self.error = True
- msg = ERROR_MESSAGES["DOC_VERSION_VALUE"].format(p[2], p.lineno(1))
- self.logger.log(msg)
-
- def p_spdx_version_2(self, p):
- """spdx_version : DOC_VERSION error"""
- self.error = True
- msg = ERROR_MESSAGES["DOC_VERSION_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_creator_comment_1(self, p):
- """creator_comment : CREATOR_COMMENT text_or_line"""
- try:
- value = p[2]
- self.builder.set_creation_comment(self.document, value)
- except CardinalityError:
- self.more_than_one_error("CreatorComment", p.lineno(1))
-
- def p_creator_comment_2(self, p):
- """creator_comment : CREATOR_COMMENT error"""
- self.error = True
- msg = ERROR_MESSAGES["CREATOR_COMMENT_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_creator_1(self, p):
- """creator : CREATOR entity"""
- self.builder.add_creator(self.document, p[2])
-
- def p_creator_2(self, p):
- """creator : CREATOR error"""
- self.error = True
- msg = ERROR_MESSAGES["CREATOR_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_created_1(self, p):
- """created : CREATED DATE"""
- try:
- value = p[2]
- self.builder.set_created_date(self.document, value)
- except CardinalityError:
- self.more_than_one_error("Created", p.lineno(1))
-
- def p_created_2(self, p):
- """created : CREATED error"""
- self.error = True
- msg = ERROR_MESSAGES["CREATED_VALUE_TYPE"].format(p.lineno(1))
- self.logger.log(msg)
-
- def p_entity_1(self, p):
- """entity : TOOL_VALUE
- """
- try:
- value = p[1]
- p[0] = self.builder.build_tool(self.document, value)
- except SPDXValueError:
- msg = ERROR_MESSAGES["TOOL_VALUE"].format(p[1], p.lineno(1))
- self.logger.log(msg)
- self.error = True
- p[0] = None
-
- def p_entity_2(self, p):
- """entity : ORG_VALUE
- """
- try:
- value = p[1]
- p[0] = self.builder.build_org(self.document, value)
- except SPDXValueError:
- msg = ERROR_MESSAGES["ORG_VALUE"].format(p[1], p.lineno(1))
- self.logger.log(msg)
- self.error = True
- p[0] = None
-
- def p_entity_3(self, p):
- """entity : PERSON_VALUE
- """
- try:
- value = p[1]
- p[0] = self.builder.build_person(self.document, value)
- except SPDXValueError:
- msg = ERROR_MESSAGES["PERSON_VALUE"].format(p[1], p.lineno(1))
- self.logger.log(msg)
- self.error = True
- p[0] = None
-
- def p_error(self, p):
- pass
-
- def build(self, **kwargs):
- self.lex = Lexer()
- self.lex.build(reflags=re.UNICODE)
- self.yacc = yacc.yacc(module=self, **kwargs)
-
- def parse(self, text):
- self.document = document.Document()
- self.error = False
- self.yacc.parse(text, lexer=self.lex)
- # FIXME: this state does not make sense
- self.builder.reset()
- validation_messages = ErrorMessages()
- # Report extra errors if self.error is False otherwise there will be
- # redundant messages
- validation_messages = self.document.validate(validation_messages)
- if not self.error:
- if validation_messages:
- for msg in validation_messages:
- self.logger.log(msg)
- self.error = True
- return self.document, self.error
diff --git a/spdx/parsers/tagvaluebuilders.py b/spdx/parsers/tagvaluebuilders.py
deleted file mode 100644
index 2d380ead6..000000000
--- a/spdx/parsers/tagvaluebuilders.py
+++ /dev/null
@@ -1,1717 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import re
-from typing import Dict, List
-
-from spdx import annotation
-from spdx import creationinfo
-from spdx import file
-from spdx import license
-from spdx import package
-from spdx import review
-from spdx import snippet
-from spdx import utils
-from spdx import version
-from spdx.checksum import Checksum
-from spdx.document import ExternalDocumentRef, Document
-from spdx.package import PackagePurpose
-from spdx.parsers import validations
-from spdx.parsers.builderexceptions import CardinalityError
-from spdx.parsers.builderexceptions import OrderError
-from spdx.parsers.builderexceptions import SPDXValueError
-from spdx.relationship import Relationship
-
-
-def str_from_text(text) -> str:
- """
- Return content of a free form text block as a string.
- """
- REGEX = re.compile("((.|\n)+) ", re.UNICODE)
- match = REGEX.match(text)
- if match:
- return match.group(1)
- elif isinstance(text, str):
- return text
- else:
- return None
-
-
-class DocBuilder(object):
- """
- Set the fields of the top level document model.
- """
-
- VERS_STR_REGEX = re.compile(r"SPDX-(\d+)\.(\d+)", re.UNICODE)
-
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_document()
-
- def set_doc_version(self, doc, value):
- """
- Set the document version.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if already defined.
- """
- if self.doc_version_set:
- raise CardinalityError("Document::Version")
-
- m = self.VERS_STR_REGEX.match(value)
- if m is None:
- raise SPDXValueError("Document::Version")
-
- self.doc_version_set = True
- doc.version = version.Version(
- major=int(m.group(1)), minor=int(m.group(2))
- )
- return True
-
- def set_doc_data_lics(self, doc, lics):
- """
- Set the document data license.
- Raise value error if malformed value
- Raise CardinalityError if already defined.
- """
- if self.doc_data_lics_set:
- raise CardinalityError("Document::DataLicense")
-
- if not validations.validate_data_lics(lics):
- raise SPDXValueError("Document::DataLicense")
-
- self.doc_data_lics_set = True
- doc.data_license = license.License.from_identifier(lics)
- return True
-
- def set_doc_name(self, doc, name):
- """
- Set the document name.
- Raise CardinalityError if already defined.
- """
- if self.doc_name_set:
- raise CardinalityError("Document::Name")
-
- self.doc_name_set = True
- doc.name = name
- return True
-
- def set_doc_spdx_id(self, doc, doc_spdx_id_line):
- """
- Set the document SPDX Identifier.
- Raise value error if malformed value.
- Raise CardinalityError if already defined.
- """
- if self.doc_spdx_id_set:
- raise CardinalityError("Document::SPDXID")
-
- if not doc_spdx_id_line == "SPDXRef-DOCUMENT":
- raise SPDXValueError("Document::SPDXID")
-
- doc.spdx_id = doc_spdx_id_line
- self.doc_spdx_id_set = True
- return True
-
- def set_doc_comment(self, doc, comment):
- """
- Set document comment.
- Raise CardinalityError if comment already set.
- Raise SPDXValueError if comment is not free form text or single line of text.
- """
- if self.doc_comment_set:
- raise CardinalityError("Document::Comment")
-
- if not validations.validate_doc_comment(comment):
- raise SPDXValueError("Document::Comment")
-
- self.doc_comment_set = True
- doc.comment = str_from_text(comment)
- return True
-
- def set_doc_namespace(self, doc, namespace):
- """
- Set the document namespace.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if already defined.
- """
- if self.doc_namespace_set:
- raise CardinalityError("Document::Namespace")
-
- if not validations.validate_doc_namespace(namespace):
- raise SPDXValueError("Document::Namespace")
-
- self.doc_namespace_set = True
- doc.namespace = namespace
- return True
-
- def reset_document(self):
- """
- Reset the state to allow building new documents
- """
- # FIXME: this state does not make sense
- self.doc_version_set = False
- self.doc_comment_set = False
- self.doc_namespace_set = False
- self.doc_data_lics_set = False
- self.doc_name_set = False
- self.doc_spdx_id_set = False
-
-
-class ExternalDocumentRefBuilder(object):
- def set_ext_doc_id(self, doc, ext_doc_id):
- """
- Set the `external_document_id` attribute of the `ExternalDocumentRef` object.
- """
- doc.add_ext_document_reference(
- ExternalDocumentRef(external_document_id=ext_doc_id)
- )
-
- def set_spdx_doc_uri(self, doc, spdx_doc_uri):
- """
- Set the `spdx_document_uri` attribute of the `ExternalDocumentRef` object.
- """
- if not validations.validate_doc_namespace(spdx_doc_uri):
- raise SPDXValueError("Document::ExternalDocumentRef")
-
- doc.ext_document_references[-1].spdx_document_uri = spdx_doc_uri
-
- def set_chksum(self, doc, chksum):
- """
- Set the `check_sum` attribute of the `ExternalDocumentRef` object.
- """
- doc.ext_document_references[-1].checksum = Checksum.checksum_from_string(chksum)
-
- def add_ext_doc_refs(self, doc, ext_doc_id, spdx_doc_uri, chksum):
- self.set_ext_doc_id(doc, ext_doc_id)
- self.set_spdx_doc_uri(doc, spdx_doc_uri)
- self.set_chksum(doc, chksum)
-
-
-class EntityBuilder(object):
- tool_re = re.compile(r"Tool:\s*(.+)", re.UNICODE)
- person_re = re.compile(r"Person:\s*(([^(])+)(\((.*)\))?", re.UNICODE)
- org_re = re.compile(r"Organization:\s*(([^(])+)(\((.*)\))?", re.UNICODE)
- PERSON_NAME_GROUP = 1
- PERSON_EMAIL_GROUP = 4
- ORG_NAME_GROUP = 1
- ORG_EMAIL_GROUP = 4
- TOOL_NAME_GROUP = 1
-
- def build_tool(self, doc, entity):
- """
- Build a tool object out of a string representation.
- Return built tool.
- Raise SPDXValueError if failed to extract tool name or name is malformed
- """
- match = self.tool_re.match(entity)
- if not match or not validations.validate_tool_name(match.group(self.TOOL_NAME_GROUP)):
- raise SPDXValueError("Failed to extract tool name")
-
- name = match.group(self.TOOL_NAME_GROUP)
- return creationinfo.Tool(name)
-
- def build_org(self, doc, entity):
- """
- Build an organization object of of a string representation.
- Return built organization.
- Raise SPDXValueError if failed to extract name.
- """
- match = self.org_re.match(entity)
- if not match or not validations.validate_org_name(match.group(self.ORG_NAME_GROUP)):
- raise SPDXValueError("Failed to extract Organization name")
-
- name = match.group(self.ORG_NAME_GROUP).strip()
- email = match.group(self.ORG_EMAIL_GROUP)
- if (email is not None) and (len(email) != 0):
- return creationinfo.Organization(name=name, email=email.strip())
- else:
- return creationinfo.Organization(name=name, email=None)
-
- def build_person(self, doc, entity):
- """
- Build an organization object of of a string representation.
- Return built organization. Raise SPDXValueError if failed to extract name.
- """
- match = self.person_re.match(entity)
- if not match or not validations.validate_person_name(match.group(self.PERSON_NAME_GROUP)):
- raise SPDXValueError("Failed to extract person name")
-
- name = match.group(self.PERSON_NAME_GROUP).strip()
- email = match.group(self.PERSON_EMAIL_GROUP)
- if (email is not None) and (len(email) != 0):
- return creationinfo.Person(name=name, email=email.strip())
- else:
- return creationinfo.Person(name=name, email=None)
-
-
-class CreationInfoBuilder(object):
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_creation_info()
-
- def add_creator(self, doc, creator):
- """
- Add a creator to the document's creation info.
- Return true if creator is valid.
- Creator must be built by an EntityBuilder.
- Raise SPDXValueError if not a creator type.
- """
- if not validations.validate_creator(creator):
- raise SPDXValueError("CreationInfo::Creator")
-
- doc.creation_info.add_creator(creator)
- return True
-
- def set_created_date(self, doc, created):
- """
- Set created date.
- Raise CardinalityError if created date already set.
- Raise SPDXValueError if created is not a date.
- """
- if self.created_date_set:
- raise CardinalityError("CreationInfo::Created")
-
- date = utils.datetime_from_iso_format(created)
- if date is None:
- raise SPDXValueError("CreationInfo::Date")
-
- self.created_date_set = True
- doc.creation_info.created = date
- return True
-
- def set_creation_comment(self, doc, comment):
- """
- Set creation comment.
- Raise CardinalityError if comment already set.
- Raise SPDXValueError if not free form text or single line of text.
- """
- if self.creation_comment_set:
- raise CardinalityError("CreationInfo::Comment")
-
- if not validations.validate_creation_comment(comment):
- raise SPDXValueError("CreationInfo::Comment")
-
- self.creation_comment_set = True
- doc.creation_info.comment = str_from_text(comment)
- return True
-
- def set_lics_list_ver(self, doc, value):
- """
- Set the license list version.
- Raise CardinalityError if already set.
- Raise SPDXValueError if incorrect value.
- """
- if self.lics_list_ver_set:
- raise CardinalityError("CreationInfo::LicenseListVersion")
-
- vers = version.Version.from_str(value)
- if vers is None:
- raise SPDXValueError("CreationInfo::LicenseListVersion")
-
- self.lics_list_ver_set = True
- doc.creation_info.license_list_version = vers
- return True
-
- def reset_creation_info(self):
- """
- Reset builder state to allow building new creation info.
- """
- # FIXME: this state does not make sense
- self.created_date_set = False
- self.creation_comment_set = False
- self.lics_list_ver_set = False
-
-
-class ReviewBuilder(object):
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_reviews()
-
- def reset_reviews(self):
- """
- Reset the builder's state to allow building new reviews.
- """
- # FIXME: this state does not make sense
- self.review_date_set = False
- self.review_comment_set = False
-
- def add_reviewer(self, doc, reviewer):
- """
- Adds a reviewer to the SPDX Document.
- Reviewer is an entity created by an EntityBuilder.
- Raise SPDXValueError if not a valid reviewer type.
- """
- # Each reviewer marks the start of a new review object.
- # FIXME: this state does not make sense
- self.reset_reviews()
- if not validations.validate_reviewer(reviewer):
- raise SPDXValueError("Review::Reviewer")
-
- doc.add_review(review.Review(reviewer=reviewer))
- return True
-
- def add_review_date(self, doc, reviewed):
- """
- Set the review date.
- Raise CardinalityError if already set.
- Raise OrderError if no reviewer defined before.
- Raise SPDXValueError if invalid reviewed value.
- """
- if len(doc.reviews) == 0:
- raise OrderError("Review::ReviewDate")
-
- if self.review_date_set:
- raise CardinalityError("Review::ReviewDate")
-
- date = utils.datetime_from_iso_format(reviewed)
- if date is None:
- raise SPDXValueError("Review::ReviewDate")
-
- self.review_date_set = True
- doc.reviews[-1].review_date = date
- return True
-
- def add_review_comment(self, doc, comment):
- """
- Set the review comment.
- Raise CardinalityError if already set.
- Raise OrderError if no reviewer defined before.
- Raise SPDXValueError if comment is not free form text or single line of text.
- """
- if len(doc.reviews) == 0:
- raise OrderError("ReviewComment")
-
- if self.review_comment_set:
- raise CardinalityError("ReviewComment")
-
- if not validations.validate_review_comment(comment):
- raise SPDXValueError("ReviewComment::Comment")
-
- self.review_comment_set = True
- doc.reviews[-1].comment = str_from_text(comment)
- return True
-
-
-class AnnotationBuilder(object):
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_annotations()
-
- def reset_annotations(self):
- """
- Reset the builder's state to allow building new annotations.
- """
- # FIXME: this state does not make sense
- self.annotation_date_set = False
- self.annotation_comment_set = False
- self.annotation_type_set = False
- self.annotation_spdx_id_set = False
-
- def add_annotator(self, doc, annotator):
- """
- Add an annotator to the SPDX Document.
- Annotator is an entity created by an EntityBuilder.
- Raise SPDXValueError if not a valid annotator type.
- """
- # Each annotator marks the start of a new annotation object.
- # FIXME: this state does not make sense
- self.reset_annotations()
- if not validations.validate_annotator(annotator):
- raise SPDXValueError("Annotation::Annotator")
-
- doc.add_annotation(annotation.Annotation(annotator=annotator))
- return True
-
- def add_annotation_date(self, doc, annotation_date):
- """
- Set the annotation date.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- Raise SPDXValueError if invalid value.
- """
- if len(doc.annotations) == 0:
- raise OrderError("Annotation::AnnotationDate")
-
- if self.annotation_date_set:
- raise CardinalityError("Annotation::AnnotationDate")
-
- date = utils.datetime_from_iso_format(annotation_date)
- if date is None:
- raise SPDXValueError("Annotation::AnnotationDate")
-
- self.annotation_date_set = True
- doc.annotations[-1].annotation_date = date
- return True
-
- def add_annotation_comment(self, doc, comment):
- """
- Set the annotation comment.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- Raise SPDXValueError if comment is not free form text or single line of text.
- """
- if len(doc.annotations) == 0:
- raise OrderError("AnnotationComment::Comment")
-
- if self.annotation_comment_set:
- raise CardinalityError("AnnotationComment::Comment")
-
- if not validations.validate_annotation_comment(comment):
- raise SPDXValueError("AnnotationComment::Comment")
-
- self.annotation_comment_set = True
- doc.annotations[-1].comment = str_from_text(comment)
- return True
-
- def add_annotation_type(self, doc, annotation_type):
- """
- Set the annotation type.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- Raise SPDXValueError if invalid value.
- """
- if len(doc.annotations) == 0:
- raise OrderError("Annotation::AnnotationType")
-
- if self.annotation_type_set:
- raise CardinalityError("Annotation::AnnotationType")
-
- if not validations.validate_annotation_type(annotation_type):
- raise SPDXValueError("Annotation::AnnotationType")
-
- self.annotation_type_set = True
- doc.annotations[-1].annotation_type = annotation_type
- return True
-
- def set_annotation_spdx_id(self, doc, spdx_id):
- """
- Set the annotation SPDX Identifier.
- Raise CardinalityError if already set.
- Raise OrderError if no annotator defined before.
- """
- if len(doc.annotations) == 0:
- raise OrderError("Annotation::SPDXREF")
-
- if self.annotation_spdx_id_set:
- raise CardinalityError("Annotation::SPDXREF")
-
- self.annotation_spdx_id_set = True
- doc.annotations[-1].spdx_id = spdx_id
- return True
-
-
-class RelationshipBuilder(object):
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_relationship()
-
- def reset_relationship(self):
- """
- Reset the builder's state to allow building new relationships.
- """
- # FIXME: this state does not make sense
- self.relationship_comment_set = False
-
- def add_relationship(self, doc: Document, relationship_term: str) -> bool:
- """
- Raise SPDXValueError if type is unknown.
- """
- self.reset_relationship()
- relationship_to_add = Relationship(relationship_term)
- existing_relationships: List[Relationship] = doc.relationships
-
- if relationship_to_add not in existing_relationships:
- doc.add_relationship(relationship_to_add)
- return True
-
- existing_relationship: Relationship = existing_relationships[existing_relationships.index(relationship_to_add)]
-
- # If the relationship already exists without comment, we remove the old one and re-append it at the end. This
- # allows to add a comment to the relationship (since a comment will always be added to the latest
- # relationship). If an equal relationship with comment already exists, we ignore the new relationship.
- if not existing_relationship.has_comment:
- existing_relationships.remove(relationship_to_add)
- doc.add_relationship(relationship_to_add)
- return True
-
- return False
-
- def add_relationship_comment(self, doc: Document, comment: str) -> bool:
- """
- Set the annotation comment.
- Raise CardinalityError if already set.
- Raise OrderError if no relationship defined before it.
- Raise SPDXValueError if comment is not free form text or single line of text.
- """
- if len(doc.relationships) == 0:
- raise OrderError("RelationshipComment::Comment")
-
- if self.relationship_comment_set:
- raise CardinalityError("RelationshipComment::Comment")
-
- if not validations.validate_relationship_comment(comment):
- raise SPDXValueError("RelationshipComment::Comment")
-
- self.relationship_comment_set = True
- doc.relationships[-1].comment = str_from_text(comment)
- return True
-
-
-class PackageBuilder(object):
- VERIF_CODE_REGEX = re.compile(r"([0-9a-f]+)\s*(\(\s*(.+)\))?", re.UNICODE)
- VERIF_CODE_CODE_GRP = 1
- VERIF_CODE_EXC_FILES_GRP = 3
-
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_package()
-
- def reset_package(self):
- """Resets the builder's state in order to build new packages."""
- # FIXME: this state does not make sense
- self.package_set = False
- self.package_spdx_id_set = False
- self.package_vers_set = False
- self.package_file_name_set = False
- self.package_supplier_set = False
- self.package_originator_set = False
- self.package_down_location_set = False
- self.package_files_analyzed_set = False
- self.package_home_set = False
- self.package_verif_set = False
- self.package_chk_sum_set = False
- self.package_source_info_set = False
- self.package_conc_lics_set = False
- self.package_license_declared_set = False
- self.package_license_comment_set = False
- self.package_cr_text_set = False
- self.package_summary_set = False
- self.package_desc_set = False
- self.package_comment_set = False
- self.package_primary_purpose_set = False
- self.package_built_date_set = False
- self.package_release_date_set = False
- self.package_valid_until_date_set = False
- # self.package_attribution_text_set = False
- self.pkg_ext_comment_set = False
-
- def create_package(self, doc, name):
- """
- Create a package for the SPDX Document.
- name - any string.
- Raise CardinalityError if package already defined.
- """
- self.reset_package()
- self.package_set = True
- doc.add_package(package.Package(name=name))
- return True
-
- def set_pkg_spdx_id(self, doc, spdx_id):
- """
- Set the Package SPDX Identifier.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if already defined.
- """
- self.assert_package_exists()
- if self.package_spdx_id_set:
- raise CardinalityError("Package::SPDXID")
-
- if not validations.validate_pkg_spdx_id(spdx_id):
- raise SPDXValueError("Package::SPDXID")
-
- self.package_spdx_id_set = True
- doc.packages[-1].spdx_id = spdx_id
- return True
-
- def set_pkg_vers(self, doc, version):
- """
- Set package version, if not already set.
- version - Any string.
- Raise CardinalityError if already has a version.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_vers_set:
- raise CardinalityError("Package::Version")
-
- self.package_vers_set = True
- doc.packages[-1].version = version
- return True
-
- def set_pkg_file_name(self, doc, name):
- """
- Set the package file name, if not already set.
- name - Any string.
- Raise CardinalityError if already has a file_name.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_file_name_set:
- raise CardinalityError("Package::FileName")
-
- self.package_file_name_set = True
- doc.packages[-1].file_name = name
- return True
-
- def set_pkg_supplier(self, doc, entity):
- """
- Set the package supplier, if not already set.
- entity - Organization, Person or NoAssert.
- Raise CardinalityError if already has a supplier.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_supplier_set:
- raise CardinalityError("Package::Supplier")
-
- if not validations.validate_pkg_supplier(entity):
- raise SPDXValueError("Package::Supplier")
-
- self.package_supplier_set = True
- doc.packages[-1].supplier = entity
- return True
-
- def set_pkg_originator(self, doc, entity):
- """
- Set the package originator, if not already set.
- entity - Organization, Person or NoAssert.
- Raise CardinalityError if already has an originator.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_originator_set:
- raise CardinalityError("Package::Originator")
-
- if not validations.validate_pkg_originator(entity):
- raise SPDXValueError("Package::Originator")
-
- self.package_originator_set = True
- doc.packages[-1].originator = entity
- return True
-
- def set_pkg_down_location(self, doc, location):
- """
- Set the package download location, if not already set.
- location - A string
- Raise CardinalityError if already defined.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_down_location_set:
- raise CardinalityError("Package::DownloadLocation")
-
- self.package_down_location_set = True
- doc.packages[-1].download_location = location
- return True
-
- def set_pkg_files_analyzed(self, doc, files_analyzed):
- """
- Set the package files analyzed, if not already set.
- Raise SPDXValueError if malformed value, CardinalityError if
- already defined.
- """
- self.assert_package_exists()
- if self.package_files_analyzed_set:
- raise CardinalityError("Package::FilesAnalyzed")
-
- if files_analyzed is None:
- return True
-
- if not validations.validate_pkg_files_analyzed(files_analyzed):
- raise SPDXValueError("Package::FilesAnalyzed")
-
- self.package_files_analyzed_set = True
- if isinstance(files_analyzed, str):
- files_analyzed = files_analyzed.lower() == "true"
- doc.packages[-1].files_analyzed = files_analyzed
- # convert to boolean;
- # validate_pkg_files_analyzed already checked if
- # files_analyzed is in ['True', 'true', 'False', 'false']
- return True
-
- def set_pkg_home(self, doc, location):
- """Set the package homepage location if not already set.
- location - A string or None or NoAssert.
- Raise CardinalityError if already defined.
- Raise OrderError if no package previously defined.
- Raise SPDXValueError if location has incorrect value.
- """
- self.assert_package_exists()
- if self.package_home_set:
- raise CardinalityError("Package::HomePage")
-
- if not validations.validate_pkg_homepage(location):
- raise SPDXValueError("Package::HomePage")
-
- self.package_home_set = True
- doc.packages[-1].homepage = location
- return True
-
- def set_pkg_verif_code(self, doc, code):
- """
- Set the package verification code, if not already set.
- code - A string.
- Raise CardinalityError if already defined.
- Raise OrderError if no package previously defined.
- Raise Value error if doesn't match verifcode form
- """
- self.assert_package_exists()
- if self.package_verif_set:
- raise CardinalityError("Package::VerificationCode")
-
- match = self.VERIF_CODE_REGEX.match(code)
- if not match:
- raise SPDXValueError("Package::VerificationCode")
-
- self.package_verif_set = True
- doc.packages[-1].verif_code = match.group(self.VERIF_CODE_CODE_GRP)
-
- if match.group(self.VERIF_CODE_EXC_FILES_GRP) is not None:
- doc.packages[-1].verif_exc_files = match.group(
- self.VERIF_CODE_EXC_FILES_GRP
- ).split(",")
- return True
-
- def set_pkg_checksum(self, doc, checksum):
- """
- Set the package checksum, if not already set.
- checksum - A string
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- self.package_chk_sum_set = True
- doc.packages[-1].set_checksum(Checksum.checksum_from_string(checksum))
- return True
-
- def set_pkg_source_info(self, doc, text):
- """
- Set the package's source information, if not already set.
- text - Free form text.
- Raise CardinalityError if already defined.
- Raise OrderError if no package previously defined.
- SPDXValueError if text is not free form text or single line of text.
- """
- self.assert_package_exists()
- if self.package_source_info_set:
- raise CardinalityError("Package::SourceInfo")
-
- if not validations.validate_pkg_src_info(text):
- raise SPDXValueError("Package::SourceInfo")
-
- self.package_source_info_set = True
- doc.packages[-1].source_info = str_from_text(text)
- return True
-
- def set_pkg_licenses_concluded(self, doc, licenses):
- """
- Set the package's concluded licenses.
- licenses - License info.
- Raise CardinalityError if already defined.
- Raise OrderError if no package previously defined.
- Raise SPDXValueError if data malformed.
- """
- self.assert_package_exists()
- if self.package_conc_lics_set:
- raise CardinalityError("Package::ConcludedLicenses")
-
- if not validations.validate_lics_conc(licenses, optional=True):
- raise SPDXValueError("Package::ConcludedLicenses")
-
- self.package_conc_lics_set = True
- doc.packages[-1].conc_lics = licenses
- return True
-
- def set_pkg_license_from_file(self, doc, lic):
- """
- Add a license from a file to the package.
- Raise SPDXValueError if data malformed.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if not validations.validate_lics_from_file(lic, optional=True):
- raise SPDXValueError("Package::LicensesFromFile")
-
- doc.packages[-1].licenses_from_files.append(lic)
- return True
-
- def set_pkg_license_declared(self, doc, lic):
- """
- Set the package's declared license.
- Raise SPDXValueError if data malformed.
- Raise OrderError if no package previously defined.
- Raise CardinalityError if already set.
- """
- self.assert_package_exists()
- if self.package_license_declared_set:
- raise CardinalityError("Package::LicenseDeclared")
-
- if not validations.validate_lics_conc(lic, optional=True):
- raise SPDXValueError("Package::LicenseDeclared")
-
- self.package_license_declared_set = True
- doc.packages[-1].license_declared = lic
- return True
-
- def set_pkg_license_comment(self, doc, text):
- """
- Set the package's license comment.
- Raise OrderError if no package previously defined.
- Raise CardinalityError if already set.
- Raise SPDXValueError if text is not free form text or single line of text.
- """
- self.assert_package_exists()
- if self.package_license_comment_set:
- raise CardinalityError("Package::LicenseComment")
-
- if not validations.validate_pkg_lics_comment(text):
- raise SPDXValueError("Package::LicenseComment")
-
- self.package_license_comment_set = True
- doc.packages[-1].license_comment = str_from_text(text)
- return True
-
- def set_pkg_attribution_text(self, doc, text):
- """
- Set the package's attribution text .
- Raise SPDXValueError if text is not free form text or single line of text.
- """
- self.assert_package_exists()
- if not validations.validate_pkg_attribution_text(text):
- raise SPDXValueError("Package::AttributionText")
-
- doc.packages[-1].attribution_text = str_from_text(text)
- return True
-
- def set_pkg_cr_text(self, doc, text):
- """
- Set the package's copyright text.
- Raise OrderError if no package previously defined.
- Raise CardinalityError if already set.
- Raise value error if text is not one of [None, NOASSERT, TEXT] or single line of text.
- """
- self.assert_package_exists()
- if self.package_cr_text_set:
- raise CardinalityError("Package::CopyrightText")
-
- if not validations.validate_pkg_cr_text(text, optional=True):
- raise SPDXValueError("Package::CopyrightText")
-
- self.package_cr_text_set = True
- if isinstance(text, str):
- doc.packages[-1].cr_text = str_from_text(text)
- else:
- doc.packages[-1].cr_text = text # None or NoAssert
-
- def set_pkg_summary(self, doc, text):
- """
- Set the package summary.
- Raise SPDXValueError if text is not free form text or single line of text.
- Raise CardinalityError if summary already set.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_summary_set:
- raise CardinalityError("Package::Summary")
-
- if not validations.validate_pkg_summary(text):
- raise SPDXValueError("Package::Summary")
-
- self.package_summary_set = True
- doc.packages[-1].summary = str_from_text(text)
-
- def set_pkg_desc(self, doc, text):
- """
- Set the package's description.
- Raise SPDXValueError if text is not free form text or single line of text.
- Raise CardinalityError if description already set.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_desc_set:
- raise CardinalityError("Package::Description")
-
- if not validations.validate_pkg_desc(text):
- raise SPDXValueError("Package::Description")
-
- self.package_desc_set = True
- doc.packages[-1].description = str_from_text(text)
-
- def set_pkg_comment(self, doc, text):
- """
- Set the package's comment.
- Raise SPDXValueError if text is not free form text or single line of text.
- Raise CardinalityError if comment already set.
- Raise OrderError if no package previously defined.
- """
- self.assert_package_exists()
- if self.package_comment_set:
- raise CardinalityError("Package::Comment")
-
- if not validations.validate_pkg_comment(text):
- raise SPDXValueError("Package::Comment")
-
- self.package_comment_set = True
- doc.packages[-1].comment = str_from_text(text)
-
- def set_pkg_primary_package_purpose(self, doc, purpose):
- """
- Set the package's primary purpose.
- Raise CardinalityError if more than one purpose is set.
- Raise SPDXValueError if purpose is unknown.
- """
- self.assert_package_exists()
- if self.package_primary_purpose_set:
- raise CardinalityError("Package::PrimaryPackagePurpose")
-
- self.package_primary_purpose_set = True
- purpose = purpose.replace("-", "_")
- for purpose_enum in PackagePurpose:
- if purpose == purpose_enum.name:
- doc.packages[-1].primary_package_purpose = purpose_enum
- return True
- else:
- raise SPDXValueError("Package::PrimaryPackagePurpose")
-
- def set_pkg_built_date(self, doc, built_date):
- """
- Set the package`s built date.
- Raise CardinalityError if built_date date already set.
- Raise SPDXValueError if built_date is not a date.
- """
- self.assert_package_exists()
- if self.package_built_date_set:
- raise CardinalityError("Package::BuiltDate")
-
- date = utils.datetime_from_iso_format(built_date)
- if date is None:
- raise SPDXValueError("Package::BuiltDate")
-
- self.package_built_date_set = True
- doc.packages[-1].built_date = date
- return True
-
- def set_pkg_release_date(self, doc, release_date):
- """
- Set the package`s release date.
- Raise CardinalityError if release_date date already set.
- Raise SPDXValueError if release_date is not a date.
- """
- self.assert_package_exists()
- if self.package_release_date_set:
- raise CardinalityError("Package::ReleaseDate")
-
- date = utils.datetime_from_iso_format(release_date)
- if date is None:
- raise SPDXValueError("Package::ReleaseDate")
-
- self.package_release_date_set = True
- doc.packages[-1].release_date = date
- return True
-
- def set_pkg_valid_until_date(self, doc, valid_until_date):
- """
- Set the package`s valid_until date.
- Raise CardinalityError if valid_until_date date already set.
- Raise SPDXValueError if valid_until_date is not a date.
- """
- self.assert_package_exists()
- if self.package_valid_until_date_set:
- raise CardinalityError("Package::ValidUntilDate")
-
- date = utils.datetime_from_iso_format(valid_until_date)
- if date is None:
- raise SPDXValueError("Package::ValidUntilDate")
-
- self.package_valid_until_date_set = True
- doc.packages[-1].valid_until_date = date
- return True
-
- def set_pkg_ext_ref_category(self, doc, category):
- """
- Set the `category` attribute of the `ExternalPackageRef` object.
- """
- self.assert_package_exists()
- if not validations.validate_pkg_ext_ref_category(category):
- raise SPDXValueError("ExternalRef::Category")
-
- if (
- len(doc.packages[-1].pkg_ext_refs)
- and doc.packages[-1].pkg_ext_refs[-1].category is None
- ):
- doc.packages[-1].pkg_ext_refs[-1].category = category
- else:
- doc.packages[-1].add_pkg_ext_refs(
- package.ExternalPackageRef(category=category)
- )
-
- def set_pkg_ext_ref_type(self, doc, pkg_ext_ref_type):
- """
- Set the `pkg_ext_ref_type` attribute of the `ExternalPackageRef` object.
- """
- self.assert_package_exists()
- if not validations.validate_pkg_ext_ref_type(pkg_ext_ref_type):
- raise SPDXValueError("ExternalRef::Type")
-
- if (
- len(doc.packages[-1].pkg_ext_refs)
- and doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type is None
- ):
- doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type = pkg_ext_ref_type
- else:
- doc.packages[-1].add_pkg_ext_refs(
- package.ExternalPackageRef(pkg_ext_ref_type=pkg_ext_ref_type)
- )
-
- def set_pkg_ext_ref_locator(self, doc, locator):
- """
- Set the `locator` attribute of the `ExternalPackageRef` object.
- """
- self.assert_package_exists()
- if (
- len(doc.packages[-1].pkg_ext_refs)
- and doc.packages[-1].pkg_ext_refs[-1].locator is None
- ):
- doc.packages[-1].pkg_ext_refs[-1].locator = locator
- else:
- doc.packages[-1].add_pkg_ext_refs(package.ExternalPackageRef(locator=locator))
-
- def add_pkg_ext_ref_comment(self, doc, comment):
- """
- Set the `comment` attribute of the `ExternalPackageRef` object.
- """
- self.assert_package_exists()
- if not len(doc.packages[-1].pkg_ext_refs):
- raise OrderError("Package::ExternalRef")
-
- if not validations.validate_pkg_ext_ref_comment(comment):
- raise SPDXValueError("ExternalRef::Comment")
-
- doc.packages[-1].pkg_ext_refs[-1].comment = str_from_text(comment)
-
- def add_pkg_ext_refs(self, doc, category, pkg_ext_ref_type, locator):
- self.set_pkg_ext_ref_category(doc, category)
- self.set_pkg_ext_ref_type(doc, pkg_ext_ref_type)
- self.set_pkg_ext_ref_locator(doc, locator)
-
- def assert_package_exists(self):
- if not self.package_set:
- raise OrderError("Package")
-
-
-class FileBuilder(object):
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_file_stat()
-
- def set_file_name(self, doc, name):
- doc.files.append(file.File(name))
- # A file name marks the start of a new file instance.
- # The builder must be reset
- # FIXME: this state does not make sense
- self.reset_file_stat()
- return True
-
- def set_file_spdx_id(self, doc, spdx_id):
- """
- Set the file SPDX Identifier.
- Raise OrderError if no package or no file defined.
- Raise SPDXValueError if malformed value.
- Raise CardinalityError if more than one spdx_id set.
- """
- if not self.has_file(doc):
- raise OrderError("File::SPDXID")
-
- if self.file_spdx_id_set:
- raise CardinalityError("File::SPDXID")
-
- if not validations.validate_file_spdx_id(spdx_id):
- raise SPDXValueError("File::SPDXID")
-
- self.file_spdx_id_set = True
- self.file(doc).spdx_id = spdx_id
- return True
-
- def set_file_comment(self, doc, text):
- """
- Raise OrderError if no package or no file defined.
- Raise CardinalityError if more than one comment set.
- Raise SPDXValueError if text is not free form text or single line of text.
- """
- if not self.has_file(doc):
- raise OrderError("File::Comment")
-
- if self.file_comment_set:
- raise CardinalityError("File::Comment")
-
- if not validations.validate_file_comment(text):
- raise SPDXValueError("File::Comment")
-
- self.file_comment_set = True
- self.file(doc).comment = str_from_text(text)
- return True
-
- def set_file_attribution_text(self, doc, text):
- """
- Set the file's attribution text .
- Raise OrderError if no package or no file defined.
- Raise SPDXValueError if text is not free form text or single line of text.
- """
- if not self.has_file(doc):
- raise OrderError("File::AttributionText")
-
- if not validations.validate_file_attribution_text(text):
- raise SPDXValueError("File::AttributionText")
-
- self.file(doc).attribution_text = str_from_text(text)
- return True
-
- def set_file_type(self, doc, type_value):
- """
- Raise OrderError if no package or file defined.
- Raise CardinalityError if more than one type set.
- Raise SPDXValueError if type is unknown.
- """
- if not self.has_file(doc):
- raise OrderError("File::FileType")
-
- if type_value not in file.FileType.__members__:
- raise SPDXValueError("File:FileType")
-
- file_type = file.FileType[type_value]
-
- spdx_file = self.file(doc)
- if file_type in spdx_file.file_types:
- raise CardinalityError("File::FileType")
-
- spdx_file.file_types.append(file_type)
-
- def set_file_checksum(self, doc: Document, checksum: str):
- """
- Raise OrderError if no file defined.
- """
- if self.has_file(doc):
- new_checksum = Checksum.checksum_from_string(checksum)
- self.file(doc).set_checksum(new_checksum)
- else:
- raise OrderError("File::CheckSum")
- return True
-
- def set_concluded_license(self, doc, lic):
- """
- Raise OrderError if no package or file defined.
- Raise CardinalityError if already set.
- Raise SPDXValueError if malformed.
- """
- if not self.has_file(doc):
- raise OrderError("File::ConcludedLicense")
-
- if self.file_conc_lics_set:
- raise CardinalityError("File::ConcludedLicense")
-
- if not validations.validate_lics_conc(lic, optional=True):
- raise SPDXValueError("File::ConcludedLicense")
-
- self.file_conc_lics_set = True
- self.file(doc).conc_lics = lic
- return True
-
- def set_file_license_in_file(self, doc, lic):
- """
- Raise OrderError if no package or file defined.
- Raise SPDXValueError if malformed value.
- """
- if not self.has_file(doc):
- raise OrderError("File::LicenseInFile")
-
- if not validations.validate_file_lics_in_file(lic):
- raise SPDXValueError("File::LicenseInFile")
-
- self.file(doc).add_lics(lic)
- return True
-
- def set_file_license_comment(self, doc, text):
- """
- Raise OrderError if no package or file defined.
- Raise SPDXValueError if text is not free form text or single line of text.
- Raise CardinalityError if more than one per file.
- """
- if not self.has_file(doc):
- raise OrderError("File::LicenseComment")
-
- if self.file_license_comment_set:
- raise CardinalityError("File::LicenseComment")
-
- if not validations.validate_file_lics_comment(text):
- raise SPDXValueError("File::LicenseComment")
-
- self.file_license_comment_set = True
- self.file(doc).license_comment = str_from_text(text)
-
- def set_file_copyright(self, doc, text):
- """
- Raise OrderError if no package or file defined.
- Raise SPDXValueError if not free form text or NONE or NO_ASSERT or single line of text.
- Raise CardinalityError if more than one.
- """
- if not self.has_file(doc):
- raise OrderError("File::CopyRight")
-
- if self.file_copytext_set:
- raise CardinalityError("File::CopyRight")
-
- if not validations.validate_file_cpyright(text, optional=True):
- raise SPDXValueError("File::CopyRight")
-
- self.file_copytext_set = True
- if isinstance(text, str):
- self.file(doc).copyright = str_from_text(text)
- else:
- self.file(doc).copyright = text # None or NoAssert
- return True
-
- def set_file_notice(self, doc, text):
- """
- Raise OrderError if no package or file defined.
- Raise SPDXValueError if not free form text or single line of text.
- Raise CardinalityError if more than one.
- """
- if not self.has_file(doc):
- raise OrderError("File::Notice")
-
- if self.file_notice_set:
- raise CardinalityError("File::Notice")
-
- if not validations.validate_file_notice(text):
- raise SPDXValueError("File::Notice")
-
- self.file_notice_set = True
- self.file(doc).notice = str_from_text(text)
-
- def add_file_contribution(self, doc, value):
- """
- Raise OrderError if no package or file defined.
- """
- if not self.has_file(doc):
- raise OrderError("File::Contributor")
-
- self.file(doc).add_contrib(value)
-
- def add_file_dep(self, doc, value):
- """
- Raise OrderError if no package or file defined.
- """
- if not self.has_file(doc):
- raise OrderError("File::Dependency")
-
- self.file(doc).add_depend(value)
-
- def set_file_atrificat_of_project(self, doc, symbol, value):
- """
- Set a file name, uri or home artifact.
- Raise OrderError if no package or file defined.
- """
- if not self.has_file(doc):
- raise OrderError("File::Artifact")
-
- self.file(doc).add_artifact(symbol, value)
-
- def file(self, doc):
- """
- Return the last file in the document's file list.
- """
- return doc.files[-1]
-
- def has_file(self, doc):
- """
- Return true if the document has at least one file.
- """
- return len(doc.files) != 0
-
- def has_package(self, doc):
- """
- Return true if the document has a package.
- """
- return len(doc.packages) != 0
-
- def reset_file_stat(self):
- """
- Reset the builder's state to enable building new files.
- """
- # FIXME: this state does not make sense
- self.file_spdx_id_set = False
- self.file_comment_set = False
- self.file_type_set = False
- self.file_chksum_set = False
- self.file_conc_lics_set = False
- self.file_license_comment_set = False
- self.file_notice_set = False
- self.file_copytext_set = False
-
-
-class LicenseBuilder(object):
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_extr_lics()
-
- def extr_lic(self, doc):
- """
- Retrieve last license in extracted license list.
- """
- return doc.extracted_licenses[-1]
-
- def has_extr_lic(self, doc):
- return len(doc.extracted_licenses) != 0
-
- def set_lic_id(self, doc, lic_id):
- """
- Add a new extracted license to the document.
- Raise SPDXValueError if data format is incorrect.
- """
- # FIXME: this state does not make sense
- self.reset_extr_lics()
- if not validations.validate_extracted_lic_id(lic_id):
- raise SPDXValueError("ExtractedLicense::id")
-
- doc.add_extr_lic(license.ExtractedLicense(lic_id))
- return True
-
- def set_lic_text(self, doc, text):
- """
- Set license extracted text.
- Raise SPDXValueError if text is not free form text or single line of text.
- Raise OrderError if no license ID defined.
- """
- if not self.has_extr_lic(doc):
- raise OrderError("ExtractedLicense::text")
-
- if self.extr_text_set:
- raise CardinalityError("ExtractedLicense::text")
-
- if not validations.validate_is_free_form_text_or_str(text):
- raise SPDXValueError("ExtractedLicense::text")
-
- self.extr_text_set = True
- self.extr_lic(doc).text = str_from_text(text)
- return True
-
- def set_lic_name(self, doc, name):
- """
- Set license name.
- Raise SPDXValueError if name is not str or utils.NoAssert
- Raise OrderError if no license id defined.
- """
- if not self.has_extr_lic(doc):
- raise OrderError("ExtractedLicense::Name")
-
- if self.extr_lic_name_set:
- raise CardinalityError("ExtractedLicense::Name")
-
- if not validations.validate_extr_lic_name(name):
- raise SPDXValueError("ExtractedLicense::Name")
-
- self.extr_lic_name_set = True
- self.extr_lic(doc).full_name = name
- return True
-
- def set_lic_comment(self, doc, comment):
- """
- Set license comment.
- Raise SPDXValueError if comment is not free form text or single line of text.
- Raise OrderError if no license ID defined.
- """
- if not self.has_extr_lic(doc):
- raise OrderError("ExtractedLicense::comment")
-
- if self.extr_lic_comment_set:
- raise CardinalityError("ExtractedLicense::comment")
-
- if not validations.validate_is_free_form_text_or_str(comment):
- raise SPDXValueError("ExtractedLicense::comment")
-
- self.extr_lic_comment_set = True
- self.extr_lic(doc).comment = str_from_text(comment)
- return True
-
- def add_lic_xref(self, doc, ref):
- """
- Add a license cross reference.
- Raise OrderError if no License ID defined.
- """
- if not self.has_extr_lic(doc):
- raise OrderError("ExtractedLicense::CrossRef")
-
- self.extr_lic(doc).add_xref(ref)
- return True
-
- def reset_extr_lics(self):
- # FIXME: this state does not make sense
- self.extr_text_set = False
- self.extr_lic_name_set = False
- self.extr_lic_comment_set = False
-
-
-class SnippetBuilder(object):
- def __init__(self):
- # FIXME: this state does not make sense
- self.reset_snippet()
-
- def create_snippet(self, doc, spdx_id):
- """
- Create a snippet for the SPDX Document.
- spdx_id - To uniquely identify any element in an SPDX document which
- may be referenced by other elements.
- Raise SPDXValueError if the data is a malformed value.
- """
- self.reset_snippet()
- spdx_id = spdx_id.split("#")[-1]
- if not validations.validate_snippet_spdx_id(spdx_id):
- raise SPDXValueError("Snippet::SnippetSPDXID")
-
- self.snippet_spdx_id_set = True
- doc.add_snippet(snippet.Snippet(spdx_id=spdx_id))
- return True
-
- def set_snippet_name(self, doc, name):
- """
- Set name of the snippet.
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if the name is already set.
- """
- self.assert_snippet_exists()
- if self.snippet_name_set:
- raise CardinalityError("SnippetName")
-
- self.snippet_name_set = True
- doc.snippet[-1].name = name
- return True
-
- def set_snippet_comment(self, doc, comment):
- """
- Set general comments about the snippet.
- Raise OrderError if no snippet previously defined.
- Raise SPDXValueError if the data is not free form text or single line of text.
- Raise CardinalityError if comment already set.
- """
- self.assert_snippet_exists()
- if self.snippet_comment_set:
- raise CardinalityError("Snippet::SnippetComment")
-
- if not validations.validate_snip_comment(comment):
- raise SPDXValueError("Snippet::SnippetComment")
-
- self.snippet_comment_set = True
- doc.snippet[-1].comment = str_from_text(comment)
- return True
-
- def set_snippet_attribution_text(self, doc, text):
- """
- Set the snippet's attribution text .
- Raise SPDXValueError if text is not free form text or single line of text.
- """
- self.assert_snippet_exists()
- if not validations.validate_snippet_attribution_text(text):
- raise SPDXValueError("Snippet::AttributionText")
-
- doc.snippet[-1].attribution_text = str_from_text(text)
- return True
-
- def set_snippet_copyright(self, doc, text):
- """Set the snippet's copyright text.
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if already set.
- Raise SPDXValueError if text is not one of [None, NOASSERT, TEXT] or single line of text.
- """
- self.assert_snippet_exists()
- if self.snippet_copyright_set:
- raise CardinalityError("Snippet::SnippetCopyrightText")
-
- if not validations.validate_snippet_copyright(text, optional=True):
- raise SPDXValueError("Snippet::SnippetCopyrightText")
-
- self.snippet_copyright_set = True
- if isinstance(text, str):
- doc.snippet[-1].copyright = str_from_text(text)
- else:
- doc.snippet[-1].copyright = text # None or NoAssert
- return True
-
- def set_snippet_lic_comment(self, doc, text):
- """
- Set the snippet's license comment.
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if already set.
- Raise SPDXValueError if the data is not free form text or single line of text.
- """
- self.assert_snippet_exists()
- if self.snippet_lic_comment_set:
- raise CardinalityError("Snippet::SnippetLicenseComments")
-
- if not validations.validate_snip_lic_comment(text):
- raise SPDXValueError("Snippet::SnippetLicenseComments")
-
- self.snippet_lic_comment_set = True
- doc.snippet[-1].license_comment = str_from_text(text)
- return True
-
- def set_snip_from_file_spdxid(self, doc, snip_from_file_spdxid):
- """
- Set the snippet's 'Snippet from File SPDX Identifier'.
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if already set.
- Raise SPDXValueError if the data is a malformed value.
- """
- self.assert_snippet_exists()
- snip_from_file_spdxid = snip_from_file_spdxid.split("#")[-1]
- if self.snip_file_spdxid_set:
- raise CardinalityError("Snippet::SnippetFromFileSPDXID")
-
- if not validations.validate_snip_file_spdxid(snip_from_file_spdxid):
- raise SPDXValueError("Snippet::SnippetFromFileSPDXID")
-
- self.snip_file_spdxid_set = True
- doc.snippet[-1].snip_from_file_spdxid = snip_from_file_spdxid
- return True
-
- def set_snip_concluded_license(self, doc, conc_lics):
- """
- Raise OrderError if no snippet previously defined.
- Raise CardinalityError if already set.
- Raise SPDXValueError if the data is a malformed value.
- """
- self.assert_snippet_exists()
- if self.snippet_conc_lics_set:
- raise CardinalityError("Snippet::SnippetLicenseConcluded")
-
- if not validations.validate_lics_conc(conc_lics, optional=True):
- raise SPDXValueError("Snippet::SnippetLicenseConcluded")
-
- self.snippet_conc_lics_set = True
- doc.snippet[-1].conc_lics = conc_lics
- return True
-
- def set_snippet_lics_info(self, doc, lics_info):
- """
- Raise OrderError if no snippet previously defined.
- Raise SPDXValueError if the data is a malformed value.
- """
- self.assert_snippet_exists()
- if not validations.validate_snip_lics_info(lics_info, optional=True):
- raise SPDXValueError("Snippet::LicenseInfoInSnippet")
-
- doc.snippet[-1].add_lics(lics_info)
- return True
-
- def set_snippet_byte_range(self, doc, parsed):
- """
- Raise OrderError if no snippet previously defined.
- Raise SPDXValueError if the data is malformed.
- """
- self.assert_snippet_exists()
- startpoint = int(parsed.split(":")[0])
- endpoint = int(parsed.split(":")[-1])
- if startpoint <= endpoint:
- doc.snippet[-1].byte_range = (startpoint, endpoint)
- else:
- raise SPDXValueError("Snippet::ByteRange")
-
- def set_snippet_line_range(self, doc, parsed):
- """
- Raise OrderError if no snippet previously defined.
- Raise SPDXValueError if the data is malformed.
- """
- self.assert_snippet_exists()
- startpoint = int(parsed.split(":")[0])
- endpoint = int(parsed.split(":")[-1])
- if startpoint <= endpoint:
- doc.snippet[-1].line_range = (startpoint, endpoint)
- else:
- raise SPDXValueError("Snippet::LineRange")
-
- def reset_snippet(self):
- # FIXME: this state does not make sense
- self.snippet_spdx_id_set = False
- self.snippet_name_set = False
- self.snippet_comment_set = False
- self.snippet_copyright_set = False
- self.snippet_lic_comment_set = False
- self.snip_file_spdxid_set = False
- self.snippet_conc_lics_set = False
-
- def assert_snippet_exists(self):
- if not self.snippet_spdx_id_set:
- raise OrderError("Snippet")
-
-
-class Builder(
- DocBuilder,
- CreationInfoBuilder,
- EntityBuilder,
- ReviewBuilder,
- PackageBuilder,
- FileBuilder,
- LicenseBuilder,
- SnippetBuilder,
- ExternalDocumentRefBuilder,
- AnnotationBuilder,
- RelationshipBuilder,
-):
- """
- SPDX document builder.
- """
-
- def __init__(self):
- super(Builder, self).__init__()
- # FIXME: this state does not make sense
- self.reset()
- self.current_package: Dict = dict()
- self.current_file: Dict = dict()
-
- def set_current_package_name(self, name: str) -> None:
- self.current_package["name"] = name
-
- def set_current_file_name(self, name: str) -> None:
- self.current_file["name"] = name
-
- def set_current_file_id(self, spdx_id: str) -> None:
- self.current_file["spdx_id"] = spdx_id
-
- def set_current_package_id(self, spdx_id: str) -> None:
- self.current_package["spdx_id"] = spdx_id
-
- def current_package_has_name(self) -> bool:
- return bool(("name" in self.current_package) and (self.current_package["name"]))
-
- def current_file_has_name(self) -> bool:
- return bool(("name" in self.current_file) and (self.current_file["name"]))
-
- def current_package_has_id(self) -> bool:
- return bool("spdx_id" in self.current_package) and (self.current_package["spdx_id"])
-
- def current_file_has_id(self) -> bool:
- return bool("spdx_id" in self.current_file) and (self.current_file["spdx_id"])
-
- def has_current_package(self) -> bool:
- return bool(self.current_package)
-
- def reset(self):
- """
- Reset builder's state for building new documents.
- Must be called between usage with different documents.
- """
- # FIXME: this state does not make sense
- self.reset_creation_info()
- self.reset_document()
- self.reset_package()
- self.reset_file_stat()
- self.reset_reviews()
- self.reset_annotations()
- self.reset_extr_lics()
- self.reset_snippet()
- self.reset_relationship()
diff --git a/spdx/parsers/validations.py b/spdx/parsers/validations.py
deleted file mode 100644
index d8a8b4cf9..000000000
--- a/spdx/parsers/validations.py
+++ /dev/null
@@ -1,345 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import re
-
-import rdflib
-
-from spdx import creationinfo
-from spdx import license
-from spdx import utils
-
-
-def validate_is_free_form_text_or_str(value, optional=False) -> bool:
- if value is None:
- return optional
- if not isinstance(value, str):
- return False
- if "\n" in value:
- TEXT_RE = re.compile(r"(.|\n)* ", re.UNICODE)
- match = TEXT_RE.match(value)
- return match is not None
- return True
-
-
-def validate_tool_name(value, optional=False):
- striped_value = value.strip()
- if optional:
- if len(striped_value) == 0:
- return True
- else:
- return False
- else:
- return not (len(striped_value) == 0)
-
-
-def validate_person_name(value, optional=False):
- return validate_tool_name(value, optional)
-
-
-def validate_org_name(value, optional=False):
- return validate_tool_name(value, optional)
-
-
-def validate_data_lics(value):
- return value == "CC0-1.0"
-
-
-def validate_doc_name(value, optional=False):
- return validate_tool_name(value, optional)
-
-
-def validate_pkg_supplier(value, optional=False):
- if optional and value is None:
- return True
- elif isinstance(
- value, (utils.NoAssert, creationinfo.Person, creationinfo.Organization)
- ):
- return True
- else:
- return False
-
-
-def validate_pkg_originator(value, optional=False):
- return validate_pkg_supplier(value, optional)
-
-
-def validate_pkg_homepage(value, optional=False):
- if value is None:
- return optional
- elif isinstance(value, (str, utils.NoAssert, utils.SPDXNone)):
- return True
- else:
- return False
-
-
-def validate_pkg_cr_text(value, optional=False):
- if isinstance(value, (utils.NoAssert, utils.SPDXNone)):
- return True
- elif validate_is_free_form_text_or_str(value, optional):
- return True
- elif value is None:
- return optional
- else:
- return False
-
-
-def validate_pkg_summary(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_pkg_desc(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_pkg_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_pkg_attribution_text(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_file_attribution_text(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_snippet_attribution_text(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_pkg_ext_ref_category(value, optional=False):
- # PACKAGE_MANAGER is used in the json schema for 2.2. For now, we simply allow both versions
- if value.upper() in ["SECURITY", "OTHER", "PACKAGE-MANAGER", "PACKAGE_MANAGER"]:
- return True
- else:
- return False
-
-
-def validate_pkg_ext_ref_type(value, optional=False):
- if re.match(r"^\S+$", value) is not None:
- return True
- else:
- return False
-
-
-def validate_pkg_ext_ref_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_doc_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_doc_spdx_id(value, optional=False):
- if value is None:
- return optional
- elif value.endswith("#SPDXRef-DOCUMENT"):
- return True
- else:
- return False
-
-
-def validate_doc_namespace(value, optional=False):
- if value is None:
- return optional
- elif (
- value.startswith("http://")
- or value.startswith("https://")
- or value.startswith("ftp://")
- ) and ("#" not in value):
- return True
- else:
- return False
-
-
-def validate_creator(value, optional=False):
- if value is None:
- return optional
- else:
- return isinstance(value, creationinfo.Creator)
-
-
-def validate_creation_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_reviewer(value, optional=False):
- return validate_creator(value, optional)
-
-
-def validate_review_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_annotator(value, optional=False):
- return validate_creator(value, optional)
-
-
-def validate_annotation_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_annotation_type(value, optional=False):
- value = value.strip()
- if value == "REVIEW" or value == "OTHER":
- return True
- else:
- return False
-
-
-def validate_relationship_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_pkg_spdx_id(value, optional=False):
- value = value.split("#")[-1]
- TEXT_RE = re.compile(r"SPDXRef-([A-Za-z0-9\.\-]+)", re.UNICODE)
- if value is None:
- return optional
- else:
- return TEXT_RE.match(value) is not None
-
-
-def validate_pkg_files_analyzed(value, optional=False):
- if value in ["True", "true", "False", "false", True, False]:
- return True
- else:
- return optional
-
-
-def validate_pkg_src_info(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_pkg_lics_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_file_spdx_id(value, optional=False):
- value = value.split("#")[-1]
- TEXT_RE = re.compile(r"SPDXRef-([A-Za-z0-9.\-]+)", re.UNICODE)
- if value is None:
- return optional
- else:
- return TEXT_RE.match(value) is not None
-
-
-def validate_file_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_file_lics_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_file_cpyright(value, optional=False):
- if isinstance(value, (utils.NoAssert, utils.SPDXNone)):
- return True
- elif validate_is_free_form_text_or_str(value, optional):
- return True
- else:
- return False
-
-
-def validate_lics_from_file(value, optional=False):
- if value is None:
- return optional
- elif isinstance(value, (license.License, utils.SPDXNone, utils.NoAssert)):
- return True
- else:
- return False
-
-
-def validate_file_notice(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_lics_conc(value, optional=False):
- if value is None:
- return optional
- elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)):
- return True
- else:
- return False
-
-
-def validate_file_lics_in_file(value, optional=False):
- if value is None:
- return optional
- elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)):
- return True
- else:
- return False
-
-
-def validate_extracted_lic_id(value, optional=False):
- if value is None:
- return optional
- else:
- return value.startswith("LicenseRef-")
-
-
-def validate_extr_lic_name(value, optional=False):
- if value is None:
- return optional
- else:
- return isinstance(value, (str, utils.NoAssert, rdflib.Literal))
-
-
-def validate_snippet_spdx_id(value, optional=False):
- if value is None:
- return optional
- value = value.split("#")[-1]
- if re.match(r"^SPDXRef[A-Za-z0-9.\-]+$", value) is not None:
- return True
- else:
- return False
-
-
-def validate_snip_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_snippet_copyright(value, optional=False):
- if validate_is_free_form_text_or_str(value, optional):
- return True
- elif isinstance(value, (utils.NoAssert, utils.SPDXNone)):
- return True
- elif value is None:
- return optional
- else:
- return False
-
-
-def validate_snip_lic_comment(value, optional=False):
- return validate_is_free_form_text_or_str(value, optional)
-
-
-def validate_snip_file_spdxid(value, optional=False):
- if value is None:
- return optional
- if (
- re.match(r"(DocumentRef[A-Za-z0-9.\-]+:){0,1}SPDXRef[A-Za-z0-9.\-]+", value)
- is not None
- ):
- return True
- else:
- return False
-
-
-def validate_snip_lics_info(value, optional=False):
- if value is None:
- return optional
- elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)):
- return True
- else:
- return False
diff --git a/spdx/parsers/xmlparser.py b/spdx/parsers/xmlparser.py
deleted file mode 100644
index d57d8175e..000000000
--- a/spdx/parsers/xmlparser.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from collections import OrderedDict
-
-import xmltodict
-
-from spdx.parsers import jsonyamlxml
-
-
-class Parser(jsonyamlxml.Parser):
- """
- Wrapper class for jsonyamlxml.Parser to provide an interface similar to
- RDF and TV Parser classes (i.e., spdx.parsers..Parser) for XML parser.
- It also avoids to repeat jsonyamlxml.Parser.parse code for JSON, YAML and XML parsers
- """
-
- def __init__(self, builder, logger):
- super(Parser, self).__init__(builder, logger)
- self.LIST_LIKE_FIELDS = {
- "creators",
- "externalDocumentRefs",
- "extractedLicenseInfos",
- "seeAlsos",
- "annotations",
- "relationships",
- "snippets",
- "reviewers",
- "fileTypes",
- "licenseInfoFromFiles",
- "licenseInfoInFiles",
- "artifactOf",
- "fileContributors",
- "fileDependencies",
- "files",
- "documentDescribes",
- "packages",
- "checksums",
- "hasFiles",
- "externalRefs",
- "ranges",
- "licenseInfoInSnippets",
- "packageVerificationCodeExcludedFiles",
- }
-
- def parse(self, file):
- parsed_xml = xmltodict.parse(
- file.read(), strip_whitespace=False, encoding="utf-8"
- )
- fixed_object = self._set_in_list(parsed_xml, self.LIST_LIKE_FIELDS)
- self.document_object = fixed_object.get("Document")
- return super(Parser, self).parse()
-
- def _set_in_list(self, data, keys):
- """
- xmltodict parse list-like fields in different way when there is only one
- of them than when there are several of them.
- Set in lists those fields that are expected to be in them.
- """
- if isinstance(data, (dict, OrderedDict)):
- new_data = OrderedDict()
- for k, v in data.items():
- if k in keys and not isinstance(v, list):
- new_data[k] = [self._set_in_list(v, keys)]
- else:
- new_data[k] = self._set_in_list(v, keys)
- return new_data
- elif isinstance(data, list):
- new_data = []
- for element in data:
- new_data.append(self._set_in_list(element, keys))
- return new_data
- return data
diff --git a/spdx/parsers/yamlparser.py b/spdx/parsers/yamlparser.py
deleted file mode 100644
index 1130e1ca7..000000000
--- a/spdx/parsers/yamlparser.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import yaml
-
-from spdx.parsers import jsonyamlxml
-
-
-class Parser(jsonyamlxml.Parser):
- """
- Wrapper class for jsonyamlxml.Parser to provide an interface similar to
- RDF and TV Parser classes (i.e., spdx.parsers..Parser) for YAML parser.
- It also avoids to repeat jsonyamlxml.Parser.parse code for JSON, YAML and XML parsers
- """
-
- def __init__(self, builder, logger):
- super(Parser, self).__init__(builder, logger)
-
- def parse(self, file):
- self.json_yaml_set_document(yaml.safe_load(file))
- return super(Parser, self).parse()
diff --git a/spdx/relationship.py b/spdx/relationship.py
deleted file mode 100644
index 5eaa62fa3..000000000
--- a/spdx/relationship.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# Copyright (c) 2020 Yash Varshney
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from enum import auto, Enum
-
-from spdx.parsers.loggers import ErrorMessages
-
-
-class RelationshipType(Enum):
- AMENDS = auto()
- OTHER = auto()
- COPY_OF = auto()
- TEST_OF = auto()
- ANCESTOR_OF = auto()
- BUILD_DEPENDENCY_OF = auto()
- BUILD_TOOL_OF = auto()
- CONTAINED_BY = auto()
- CONTAINS = auto()
- DATA_FILE_OF = auto()
- DEPENDENCY_MANIFEST_OF = auto()
- DEPENDENCY_OF = auto()
- DEPENDS_ON = auto()
- DESCENDANT_OF = auto()
- DESCRIBED_BY = auto()
- DESCRIBES = auto()
- DEV_DEPENDENCY_OF = auto()
- DEV_TOOL_OF = auto()
- DISTRIBUTION_ARTIFACT = auto()
- DOCUMENTATION_OF = auto()
- DYNAMIC_LINK = auto()
- EXAMPLE_OF = auto()
- EXPANDED_FROM_ARCHIVE = auto()
- FILE_ADDED = auto()
- FILE_DELETED = auto()
- FILE_MODIFIED = auto()
- GENERATED_FROM = auto()
- GENERATES = auto()
- HAS_PREREQUISITE = auto()
- METAFILE_OF = auto()
- OPTIONAL_COMPONENT_OF = auto()
- OPTIONAL_DEPENDENCY_OF = auto()
- PACKAGE_OF = auto()
- PATCH_APPLIED = auto()
- PATCH_FOR = auto()
- PREREQUISITE_FOR = auto()
- PROVIDED_DEPENDENCY_OF = auto()
- RUNTIME_DEPENDENCY_OF = auto()
- STATIC_LINK = auto()
- TEST_CASE_OF = auto()
- TEST_DEPENDENCY_OF = auto()
- TEST_TOOL_OF = auto()
- VARIANT_OF = auto()
- REQUIREMENT_DESCRIPTION_FOR = auto()
- SPECIFICATION_FOR = auto()
-
-
-class Relationship(object):
- """
- Document relationship information
- Fields:
- - relationship: provides information about the relationship between two SPDX elements.
- - relationship_comment: place for the SPDX file creator to record any general comments. Optional, One
- """
-
- def __init__(self, relationship=None, relationship_comment=None):
- self.relationship = relationship
- self.relationship_comment = relationship_comment
-
- def __eq__(self, other):
- return (
- isinstance(other, Relationship)
- and self.relationship == other.relationship
- )
-
- @property
- def has_comment(self):
- return self.relationship_comment is not None
-
- @property
- def spdx_element_id(self):
- return self.relationship.split(" ")[0]
-
- @property
- def relationship_type(self):
- return self.relationship.split(" ")[1]
-
- @property
- def related_spdx_element(self):
- return self.relationship.split(" ")[2]
-
- def validate(self, messages: ErrorMessages) -> ErrorMessages:
- """
- Check that all the fields are valid.
- Appends any error messages to messages parameter shall be a ErrorMessages.
- """
- r_type = self.relationship_type
- if r_type not in [name for name, _ in RelationshipType.__members__.items()]:
- messages.append(
- "Relationship type must be one of the constants defined in "
- "class spdx.relationship.Relationship"
- )
- return messages
diff --git a/spdx/review.py b/spdx/review.py
deleted file mode 100644
index 42c4982ee..000000000
--- a/spdx/review.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from datetime import datetime
-from functools import total_ordering
-
-from spdx.utils import datetime_iso_format
-
-
-@total_ordering
-class Review(object):
-
- """
- Document review information.
- Fields:
- - reviewer: Person, Organization or tool that reviewed the SPDX file.
- Mandatory one.
- - review_date: Review date, mandatory one. Type: datetime.
- - comment: Review comment. Optional one. Type: str.
- """
-
- def __init__(self, reviewer=None, review_date=None, comment=None):
- self.reviewer = reviewer
- self.review_date = review_date
- self.comment = comment
-
- def __eq__(self, other):
- return (
- isinstance(other, Review)
- and self.reviewer == other.reviewer
- and self.review_date == other.review_date
- and self.comment == other.comment
- )
-
- def __lt__(self, other):
- return (self.reviewer, self.review_date, self.comment) < (
- other.reviewer,
- other.review_date,
- other.comment,
- )
-
- def set_review_date_now(self):
- self.review_date = datetime.utcnow().replace(microsecond=0)
-
- @property
- def review_date_iso_format(self):
- return datetime_iso_format(self.review_date)
-
- @property
- def has_comment(self):
- return self.comment is not None
-
- def validate(self, messages):
- """
- Check that all the fields are valid.
- Appends any error messages to messages parameter shall be a ErrorMessages.
- """
- self.validate_reviewer(messages)
- self.validate_review_date(messages)
-
- return messages
-
- def validate_reviewer(self, messages):
- if self.reviewer is None:
- messages.append("Review missing reviewer.")
-
- return messages
-
- def validate_review_date(self, messages):
- if self.review_date is None:
- messages.append("Review missing review date.")
-
- return messages
diff --git a/spdx/snippet.py b/spdx/snippet.py
deleted file mode 100644
index cd79e3b55..000000000
--- a/spdx/snippet.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright (c) 2018 Yash M. Nisar
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-from typing import Tuple, Optional
-
-from spdx import license
-from spdx import utils
-
-
-class Snippet(object):
- """
- Represents an analyzed snippet.
- Fields:
- - spdx_id: Uniquely identify any element in an SPDX document which may be
- referenced by other elements. Mandatory, one per snippet if the snippet
- is present.
- - name: Name of the snippet. Optional, one. Type: str.
- - comment: General comments about the snippet. Optional, one. Type: str.
- - copyright: Copyright text. Mandatory, one. Type: str.
- - license_comment: Relevant background references or analysis that went
- in to arriving at the Concluded License for a snippet. Optional, one.
- - snip_from_file_spdxid: Uniquely identify the file in an SPDX document
- which this snippet is associated with. Mandatory, one. Type: str.
- - conc_lics: Contains the license the SPDX file creator has concluded as
- governing the snippet or alternative values if the governing license
- cannot be determined. Mandatory one. Type: license.License or
- utils.NoAssert or utils.SPDXNone.
- - licenses_in_snippet: The list of licenses found in the snippet.
- Mandatory, one or more. Type: license.License or utils.SPDXNone or
- utils.NoAssert.
- - attribution_text: optional string.
- - byte_range: Defines the byte range in the original host file that the
- snippet information applies to. Mandatory, one. Type (int, int)
- - line_range: Defines the line range in the original host file that the
- snippet information applies to. Optional, one. Type (int, int)
- """
-
- def __init__(
- self, spdx_id=None, copyright=None, snip_from_file_spdxid=None, conc_lics=None, byte_range=None
- ):
- self.spdx_id = spdx_id
- self.name = None
- self.comment = None
- self.copyright = copyright
- self.license_comment = None
- self.attribution_text = None
- self.snip_from_file_spdxid = snip_from_file_spdxid
- self.conc_lics = conc_lics
- self.licenses_in_snippet = []
- self.byte_range: Optional[Tuple[int, int]] = byte_range
- self.line_range: Optional[Tuple[int, int]] = None
-
- def add_lics(self, lics):
- self.licenses_in_snippet.append(lics)
-
- def validate(self, messages):
- """
- Validate fields of the snippet and update the messages list with user
- friendly error messages for display.
- """
- self.validate_spdx_id(messages)
- self.validate_copyright_text(messages)
- self.validate_snip_from_file_spdxid(messages)
- self.validate_concluded_license(messages)
- self.validate_licenses_in_snippet(messages)
-
- return messages
-
- def validate_spdx_id(self, messages):
- if self.spdx_id is None:
- messages.append("Snippet has no SPDX Identifier.")
-
- def validate_copyright_text(self, messages):
- if self.copyright and not isinstance(
- self.copyright,
- (str, utils.NoAssert, utils.SPDXNone),
- ):
- messages.append(
- "Snippet copyright must be str or unicode or spdx.utils.NoAssert or spdx.utils.SPDXNone"
- )
-
- def validate_snip_from_file_spdxid(self, messages):
- if self.snip_from_file_spdxid is None:
- messages.append("Snippet has no Snippet from File SPDX Identifier.")
-
- def validate_concluded_license(self, messages):
- if self.conc_lics and not isinstance(
- self.conc_lics, (utils.SPDXNone, utils.NoAssert, license.License)
- ):
- messages.append(
- "Snippet concluded license must be instance of "
- "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
- "spdx.license.License"
- )
-
- def validate_licenses_in_snippet(self, messages):
- for lic in self.licenses_in_snippet:
- if not isinstance(
- lic, (license.License, utils.NoAssert, utils.SPDXNone)
- ):
- messages.append(
- "License in snippet must be instance of "
- "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
- "spdx.license.License"
- )
-
- def has_optional_field(self, field):
- return bool(getattr(self, field, None))
diff --git a/spdx/utils.py b/spdx/utils.py
deleted file mode 100644
index 3695cd0bd..000000000
--- a/spdx/utils.py
+++ /dev/null
@@ -1,248 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import datetime
-import hashlib
-import re
-from typing import Dict, List, TYPE_CHECKING
-
-from ply import lex
-from ply import yacc
-
-from spdx.checksum import ChecksumAlgorithm
-
-if TYPE_CHECKING:
- from spdx.file import File
- from spdx.package import Package
-from spdx.relationship import Relationship
-from spdx import license
-
-
-def datetime_iso_format(date):
- """
- Return an ISO-8601 representation of a datetime object.
- """
- return date.isoformat() + "Z"
-
-
-# Matches an iso 8601 date representation
-DATE_ISO_REGEX = re.compile(
- r"(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z", re.UNICODE
-)
-
-# Groups for retrieving values from DATE_ISO_REGEX matches.
-DATE_ISO_YEAR_GRP = 1
-DATE_ISO_MONTH_GRP = 2
-DATE_ISO_DAY_GRP = 3
-DATE_ISO_HOUR_GRP = 4
-DATE_ISO_MIN_GRP = 5
-DATE_ISO_SEC_GRP = 6
-
-
-def datetime_from_iso_format(string):
- """
- Return a datetime object from an iso 8601 representation.
- Return None if string is non conforming.
- """
- match = DATE_ISO_REGEX.match(string)
- if match:
- date = datetime.datetime(
- year=int(match.group(DATE_ISO_YEAR_GRP)),
- month=int(match.group(DATE_ISO_MONTH_GRP)),
- day=int(match.group(DATE_ISO_DAY_GRP)),
- hour=int(match.group(DATE_ISO_HOUR_GRP)),
- second=int(match.group(DATE_ISO_SEC_GRP)),
- minute=int(match.group(DATE_ISO_MIN_GRP)),
- )
- return date
- else:
- return None
-
-
-class NoAssert(object):
- """
- Represent SPDX NOASSERTION value.
- """
-
- def to_value(self):
- return "NOASSERTION"
-
- def __str__(self):
- return self.to_value()
-
- def __repr__(self):
- return self.to_value()
-
-
-class UnKnown(object):
- """
- Represent SPDX UNKNOWN value.
- """
-
- def to_value(self):
- return "UNKNOWN"
-
- def __str__(self):
- return self.to_value()
-
- def __repr__(self):
- return self.to_value()
-
- def __eq__(self, other):
- return self.to_value() == other.to_value()
-
-
-class SPDXNone(object):
- """
- Represent SPDX None value.
- """
-
- def to_value(self):
- return "NONE"
-
- def __str__(self):
- return self.to_value()
-
-
-class LicenseListLexer(object):
- tokens = ["LP", "RP", "AND", "OR", "LICENSE"]
-
- def t_LP(self, t):
- r"\("
- return t
-
- def t_RP(self, t):
- r"\)"
- return t
-
- def t_AND(self, t):
- r"\s(and|AND)\s"
- t.value = t.value.strip()
- return t
-
- def t_OR(self, t):
- r"\s(or|OR)\s"
- t.value = t.value.strip()
- return t
-
- def t_whitespace(self, t):
- r"\s+"
- pass
-
- def t_LICENSE(self, t):
- r"[A-Za-z.0-9\-+]+"
- t.value = t.value.strip()
- return t
-
- def t_error(self, t):
- pass
-
- def input(self, data):
- """Set input, data - str."""
- self.lexer.input(data)
-
- def token(self):
- """Get the next token or None if exhausted input."""
- return self.lexer.token()
-
- def build(self, **kwargs):
- """Build lexer, must be called before input or token methods.
- Only need to build once.
- """
- self.lexer = lex.lex(module=self, **kwargs)
-
-
-class LicenseListParser(object):
- def __init__(self):
- self.lex = LicenseListLexer()
- self.lex.build(reflags=re.UNICODE)
- self.tokens = self.lex.tokens
-
- def p_disjunction_1(self, p):
- """disjunction : disjunction OR conjunction
- """
- p[0] = license.LicenseDisjunction(p[1], p[3])
-
- def p_disjunction_2(self, p):
- """disjunction : conjunction
- """
- p[0] = p[1]
-
- def p_conjunction_1(self, p):
- """conjunction : conjunction AND license_atom
- """
- p[0] = license.LicenseConjunction(p[1], p[3])
-
- def p_conjunction_2(self, p):
- """conjunction : license_atom
- """
- p[0] = p[1]
-
- def p_license_atom_1(self, p):
- """license_atom : LICENSE
- """
- p[0] = license.License.from_identifier(p[1])
-
- def p_license_atom_2(self, p):
- """license_atom : LP disjunction RP
- """
- p[0] = p[2]
-
- def p_error(self, p):
- pass
-
- def build(self, **kwargs):
- """Must be called before parse."""
- self.yacc = yacc.yacc(module=self, **kwargs)
-
- def parse(self, data):
- """Parses a license list and returns a License or None if it failed."""
- try:
- return self.yacc.parse(data, lexer=self.lex)
- except:
- return None
-
-
-def calc_verif_code(files: List['File']) -> str:
- list_of_file_hashes = []
- hash_algorithm_name = ChecksumAlgorithm.SHA1
- for file in files:
- file_checksum = file.get_checksum(hash_algorithm_name)
- if file_checksum is not None:
- file_checksum_value = file_checksum.value
- else:
- file_checksum_value = file.calculate_checksum(hash_algorithm_name)
- list_of_file_hashes.append(file_checksum_value)
-
- list_of_file_hashes.sort()
-
- hasher = hashlib.new(hash_algorithm_name.name.lower())
- hasher.update("".join(list_of_file_hashes).encode("utf-8"))
- return hasher.hexdigest()
-
-
-def get_files_in_package(package: 'Package', files: List['File'], relationships: List[Relationship]) -> List['File']:
- files_in_package = []
- for file in files:
- if file.spdx_id in [relationship.related_spdx_element for relationship in relationships
- if relationship.relationship_type == "CONTAINS" and relationship.spdx_element_id == package.spdx_id] \
- or file.spdx_id in [relationship.spdx_element_id for relationship in relationships
- if relationship.relationship_type == "CONTAINED_BY" and relationship.related_spdx_element == package.spdx_id]:
- files_in_package.append(file)
-
- return files_in_package
-
-
-def update_dict_item_with_new_item(current_state: Dict, key: str, item_to_add: str) -> None:
- if key not in current_state:
- current_state[key] = [item_to_add]
- elif item_to_add not in current_state[key]:
- current_state[key].append(item_to_add)
diff --git a/spdx/version.py b/spdx/version.py
deleted file mode 100644
index 8be8e7ddc..000000000
--- a/spdx/version.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from functools import total_ordering
-import re
-
-
-@total_ordering
-class Version(object):
- """
- Version number composed of major and minor.
- Fields:
- - major: Major number, int.
- - minor: Minor number, int.
- """
-
- VERS_STR_REGEX = re.compile(r"(\d+)\.(\d+)")
-
- def __init__(self, major, minor):
- self.major = int(major)
- self.minor = int(minor)
-
- @classmethod
- def from_str(cls, value):
- """Constructs a Version from a string.
- Returns None if string not in N.N form where N represents a
- number.
- """
- m = cls.VERS_STR_REGEX.match(value)
- if m is not None:
- return cls(int(m.group(1)), int(m.group(2)))
- else:
- return None
-
- def __repr__(self):
- return "Version" + repr((self.major, self.minor))
-
- def __str__(self):
- return "SPDX-{major}.{minor}".format(**self.__dict__)
-
- def __eq__(self, other):
- return (
- isinstance(other, self.__class__)
- and self.major == other.major
- and self.minor == other.minor
- )
-
- def __lt__(self, other):
- return self.major < other.major or (
- self.major == other.major and self.minor < other.minor
- )
diff --git a/spdx/writers/json.py b/spdx/writers/json.py
deleted file mode 100644
index a2b226fdf..000000000
--- a/spdx/writers/json.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-
-from spdx.writers.tagvalue import InvalidDocumentError
-from spdx.writers.jsonyamlxml import Writer
-from spdx.parsers.loggers import ErrorMessages
-import datetime
-
-
-def json_converter(obj):
- if isinstance(obj, datetime.datetime):
- return str(obj)
- else:
- raise TypeError("No implementation available to serialize objects of type " + type(obj).__name__)
-
-
-def write_document(document, out, validate=True):
-
- if validate:
- messages = ErrorMessages()
- messages = document.validate(messages)
- if messages:
- raise InvalidDocumentError(messages)
-
- writer = Writer(document)
- document_object = writer.create_document()
- json.dump(document_object, out, indent=4, default=json_converter)
diff --git a/spdx/writers/jsonyamlxml.py b/spdx/writers/jsonyamlxml.py
deleted file mode 100644
index d36e21112..000000000
--- a/spdx/writers/jsonyamlxml.py
+++ /dev/null
@@ -1,627 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-from typing import Dict, List
-
-from rdflib import Literal
-
-from spdx import license, utils
-from spdx.checksum import Checksum
-from spdx.package import ExternalPackageRef
-from spdx.relationship import Relationship
-from spdx.utils import update_dict_item_with_new_item
-
-
-class BaseWriter(object):
- """
- Base class for all Writer classes.
- Provide utility functions and stores shared fields.
- - document: spdx.document class. Source of data to be written
- - document_object: python dictionary representation of the entire spdx.document
- """
-
- def __init__(self, document):
- self.document = document
- self.document_object = dict()
-
- def license(self, license_field):
- """
- Return a string representation of a license or spdx.utils special object
- """
- if isinstance(
- license_field, (license.LicenseDisjunction, license.LicenseConjunction)
- ):
- return "({})".format(license_field)
-
- if isinstance(license_field, license.License):
- license_str = license_field.identifier.__str__()
- else:
- license_str = license_field.__str__()
- return license_str
-
- def checksum_to_dict(self, checksum_field: Checksum) -> Dict:
- """
- Return a dictionary representation of a checksum.Checksum object
- """
- return {'algorithm': checksum_field.identifier.name, 'checksumValue': checksum_field.value}
-
- def spdx_id(self, spdx_id_field):
- return spdx_id_field.__str__().split("#")[-1]
-
-
-class CreationInfoWriter(BaseWriter):
- """
- Represent spdx.creationinfo as json-serializable objects
- """
-
- def __init__(self, document):
- super(CreationInfoWriter, self).__init__(document)
-
- def create_creation_info(self):
- creation_info_object = dict()
- creation_info = self.document.creation_info
- creation_info_object["creators"] = list(map(str, creation_info.creators))
- creation_info_object["created"] = creation_info.created_iso_format
-
- if creation_info.license_list_version:
- creation_info_object["licenseListVersion"] = "{0}.{1}".format(
- creation_info.license_list_version.major,
- creation_info.license_list_version.minor,
- )
-
- if creation_info.has_comment:
- creation_info_object["comment"] = creation_info.comment
-
- return creation_info_object
-
-
-class PackageWriter(BaseWriter):
- """
- Represent spdx.package as python objects
- """
-
- def __init__(self, document):
- super(PackageWriter, self).__init__(document)
-
- def package_verification_code(self, package):
- """
- Represent the package verification code information as
- as python dictionary
- """
-
- package_verification_code_object = dict()
-
- package_verification_code_object["packageVerificationCodeValue"] = package.verif_code
-
- if package.verif_exc_files:
- package_verification_code_object[
- "packageVerificationCodeExcludedFiles"
- ] = package.verif_exc_files
-
- return package_verification_code_object
-
- @staticmethod
- def external_reference_as_dict(external_ref: ExternalPackageRef) -> dict:
- """
- Create a dictionary representation of the provided external reference, renaming the properties as they should
- appear in a json/yaml/xml document.
- """
- external_ref_dict = dict()
- external_ref_dict["referenceCategory"] = external_ref.category
- external_ref_dict["referenceType"] = external_ref.pkg_ext_ref_type
- external_ref_dict["referenceLocator"] = external_ref.locator
- if external_ref.comment:
- external_ref_dict["comment"] = external_ref.comment
- return external_ref_dict
-
- def create_package_info(self, package, annotations_by_spdx_id):
- package_object = dict()
- package_object["SPDXID"] = self.spdx_id(package.spdx_id)
- package_object["name"] = package.name
- package_object["downloadLocation"] = package.download_location.__str__()
- if package.files_analyzed is not None:
- package_object["filesAnalyzed"] = package.files_analyzed
- if package.files_analyzed is None or package.files_analyzed is True:
- if package.verif_code:
- package_object["packageVerificationCode"] = self.package_verification_code(
- package
- )
- if package.has_optional_field("licenses_from_files"):
- package_object["licenseInfoFromFiles"] = list(
- map(self.license, package.licenses_from_files)
- )
- if package.has_optional_field("conc_lics"):
- package_object["licenseConcluded"] = self.license(package.conc_lics)
-
- if package.has_optional_field("license_declared"):
- package_object["licenseDeclared"] = self.license(package.license_declared)
-
- if package.has_optional_field("cr_text"):
- package_object["copyrightText"] = package.cr_text.__str__()
-
- if package.has_optional_field("version"):
- package_object["versionInfo"] = package.version
-
- if package.has_optional_field("summary"):
- package_object["summary"] = package.summary
-
- if package.has_optional_field("attribution_text"):
- package_object["attributionTexts"] = [package.attribution_text]
-
- if package.has_optional_field("source_info"):
- package_object["sourceInfo"] = package.source_info
-
- if package.has_optional_field("file_name"):
- package_object["packageFileName"] = package.file_name
-
- if package.has_optional_field("supplier"):
- package_object["supplier"] = package.supplier.to_value()
-
- if package.has_optional_field("originator"):
- package_object["originator"] = package.originator.to_value()
-
- for checksum in package.checksums.values():
- package_object.setdefault("checksums", []).append(self.checksum_to_dict(checksum))
-
- if package.has_optional_field("description"):
- package_object["description"] = package.description
-
- if package.has_optional_field("comment"):
- package_object["comment"] = package.comment
-
- if package.has_optional_field("license_comment"):
- package_object["licenseComments"] = package.license_comment
-
- if package.has_optional_field("homepage"):
- package_object["homepage"] = package.homepage.__str__()
-
- if package.has_optional_field("primary_package_purpose"):
- package_object["primaryPackagePurpose"] = package.primary_package_purpose.name.replace("_", "-")
-
- if package.has_optional_field("release_date"):
- package_object["releaseDate"] = utils.datetime_iso_format(package.release_date)
-
- if package.has_optional_field("built_date"):
- package_object["builtDate"] = utils.datetime_iso_format(package.built_date)
-
- if package.has_optional_field("valid_until_date"):
- package_object["validUntilDate"] = utils.datetime_iso_format(package.valid_until_date)
-
- if package.has_optional_field("pkg_ext_refs"):
- package_object["externalRefs"] = [self.external_reference_as_dict(external_ref) for external_ref in
- package.pkg_ext_refs]
- if package.spdx_id in annotations_by_spdx_id:
- package_object["annotations"] = annotations_by_spdx_id[package.spdx_id]
-
- return package_object
-
-
-class FileWriter(BaseWriter):
- """
- Represent spdx.file as json-serializable objects
- """
-
- def __init__(self, document):
- super(FileWriter, self).__init__(document)
-
- def create_artifact_info(self, file):
- """
- Create the artifact json-serializable representation from a spdx.file.File object
- """
- artifact_of_objects = []
-
- for i in range(len(file.artifact_of_project_name)):
- artifact_of_object = dict()
- artifact_of_object["name"] = file.artifact_of_project_name[i].__str__()
- artifact_of_object["homePage"] = file.artifact_of_project_home[i].__str__()
- artifact_of_object["projectUri"] = file.artifact_of_project_uri[i].__str__()
- artifact_of_objects.append(artifact_of_object)
-
- return artifact_of_objects
-
- def create_file_info(self, file, annotations_by_spdx_id):
- file_object = dict()
-
- file_object["fileName"] = file.name
- file_object["SPDXID"] = self.spdx_id(file.spdx_id)
- for checksum in file.checksums.values():
- file_object.setdefault("checksums", []).append(self.checksum_to_dict(checksum))
- if file.has_optional_field("conc_lics"):
- file_object["licenseConcluded"] = self.license(file.conc_lics)
-
- if file.has_optional_field("licenses_in_file"):
- file_object["licenseInfoInFiles"] = list(
- map(self.license, file.licenses_in_file)
- )
-
- if file.has_optional_field("copyright"):
- file_object["copyrightText"] = file.copyright.__str__()
-
- if file.has_optional_field("comment"):
- file_object["comment"] = file.comment
-
- if file.has_optional_field("file_types"):
- types = []
- for file_type in file.file_types:
- types.append(file_type.name)
- file_object["fileTypes"] = types
-
- if file.has_optional_field("license_comment"):
- file_object["licenseComments"] = file.license_comment
-
- if file.has_optional_field("attribution_text"):
- file_object["attributionTexts"] = [file.attribution_text]
-
- if file.has_optional_field("notice"):
- file_object["noticeText"] = file.notice
-
- if file.contributors:
- file_object["fileContributors"] = file.contributors
-
- if file.dependencies:
- file_object["fileDependencies"] = file.dependencies
-
- valid_artifacts = (
- file.artifact_of_project_name
- and len(file.artifact_of_project_name)
- == len(file.artifact_of_project_home)
- and len(file.artifact_of_project_home)
- == len(file.artifact_of_project_uri)
- )
-
- if valid_artifacts:
- file_object["artifactOf"] = self.create_artifact_info(file)
-
- if file.spdx_id in annotations_by_spdx_id:
- file_object["annotations"] = annotations_by_spdx_id[file.spdx_id]
-
- return file_object
-
-
-class ReviewInfoWriter(BaseWriter):
- """
- Represent spdx.review as json-serializable objects
- """
-
- def __init__(self, document):
- super(ReviewInfoWriter, self).__init__(document)
-
- def create_review_info(self):
- review_info_objects = []
- reviews = self.document.reviews
-
- for review in reviews:
- review_object = dict()
- review_object["reviewer"] = review.reviewer.__str__()
- review_object["reviewDate"] = review.review_date_iso_format
- if review.has_comment:
- review_object["comment"] = review.comment
-
- review_info_objects.append(review_object)
-
- return review_info_objects
-
-
-class AnnotationInfoWriter(BaseWriter):
- """
- Represent spdx.annotation as json-serializable objects
- """
-
- def __init__(self, document):
- super(AnnotationInfoWriter, self).__init__(document)
-
- def create_annotations_by_spdx_id(self) -> Dict:
- """
- Create a dict with annotations_by_spdx_id and use the spdx_id of the element that is annotated as key.
- These keys are then used to attach the annotation to the corresponding SPDX element.
- """
- annotations_by_spdx_id = dict()
-
- if not self.document.annotations:
- return annotations_by_spdx_id
-
- for annotation in self.document.annotations:
- annotation_object = dict()
- annotation_object["annotator"] = annotation.annotator.__str__()
- annotation_object["annotationDate"] = annotation.annotation_date_iso_format
- annotation_object["annotationType"] = annotation.annotation_type
- annotation_object["comment"] = annotation.comment
-
- annotation_spdx_id = self.spdx_id(annotation.spdx_id)
- if annotation_spdx_id not in annotations_by_spdx_id:
- annotations_by_spdx_id[annotation_spdx_id] = [annotation_object]
- else:
- annotations_by_spdx_id[annotation_spdx_id].append(annotation_object)
-
- return annotations_by_spdx_id
-
-
-class RelationshipInfoWriter(BaseWriter):
- """
- Represent spdx.relationship as json-serializable objects
- """
-
- def __init__(self, document):
- super(RelationshipInfoWriter, self).__init__(document)
-
- def create_relationship_info(self, relationship: Relationship):
- relationship_object = dict()
- relationship_object["spdxElementId"] = relationship.spdx_element_id
- relationship_object[
- "relatedSpdxElement"
- ] = relationship.related_spdx_element
- relationship_object["relationshipType"] = relationship.relationship_type
- if relationship.has_comment:
- relationship_object["comment"] = relationship.relationship_comment
-
- return relationship_object
-
-
-class SnippetWriter(BaseWriter):
- """
- Represent spdx.snippet as json-serializable objects
- """
-
- def __init__(self, document):
- super(SnippetWriter, self).__init__(document)
-
- def create_snippet_info(self, annotations_by_spdx_id):
- snippet_info_objects = []
- snippets = self.document.snippet
-
- for snippet in snippets:
- snippet_from_file_spdx_id = self.spdx_id(snippet.snip_from_file_spdxid)
- snippet_object = dict()
- snippet_object["SPDXID"] = self.spdx_id(snippet.spdx_id)
- snippet_object["snippetFromFile"] = snippet_from_file_spdx_id
-
- if snippet.has_optional_field("copyright"):
- snippet_object["copyrightText"] = snippet.copyright
-
- if snippet.has_optional_field("conc_lics"):
- snippet_object["licenseConcluded"] = self.license(snippet.conc_lics)
-
- if snippet.has_optional_field("licenses_in_snippet"):
- snippet_object["licenseInfoInSnippets"] = list(
- map(self.license, snippet.licenses_in_snippet)
- )
- byte_range = {"endPointer": {"offset": snippet.byte_range[1], "reference": snippet_from_file_spdx_id},
- "startPointer": {"offset": snippet.byte_range[0], "reference": snippet_from_file_spdx_id}}
- snippet_object["ranges"] = [byte_range]
-
- if snippet.has_optional_field("name"):
- snippet_object["name"] = snippet.name
-
- if snippet.has_optional_field("comment"):
- snippet_object["comment"] = snippet.comment
-
- if snippet.has_optional_field("attribution_text"):
- snippet_object["attributionTexts"] = [snippet.attribution_text]
-
- if snippet.has_optional_field("license_comment"):
- snippet_object["licenseComments"] = snippet.license_comment
-
- if snippet.spdx_id in annotations_by_spdx_id:
- snippet_object["annotations"] = annotations_by_spdx_id[snippet.spdx_id]
-
- if snippet.has_optional_field("line_range"):
- line_range = {
- "endPointer": {"lineNumber": snippet.line_range[1], "reference": snippet_from_file_spdx_id},
- "startPointer": {"lineNumber": snippet.line_range[0], "reference": snippet_from_file_spdx_id}}
- snippet_object["ranges"].append(line_range)
-
- snippet_info_objects.append(snippet_object)
-
- return snippet_info_objects
-
-
-class ExtractedLicenseWriter(BaseWriter):
- """
- Represent spdx.document.ExtractedLicense as json-serializable objects
- """
-
- def __init__(self, document):
- super(ExtractedLicenseWriter, self).__init__(document)
-
- def create_extracted_license(self):
- extracted_license_objects = []
- unique_extracted_licenses = {}
- for lic in self.document.extracted_licenses:
- if lic.identifier not in unique_extracted_licenses.keys():
- unique_extracted_licenses[lic.identifier] = lic
-
- for extracted_license in unique_extracted_licenses.values():
- extracted_license_object = dict()
-
- if isinstance(extracted_license.identifier, Literal):
- extracted_license_object[
- "licenseId"
- ] = extracted_license.identifier.toPython()
- else:
- extracted_license_object["licenseId"] = extracted_license.identifier
-
- if isinstance(extracted_license.text, Literal):
- extracted_license_object[
- "extractedText"
- ] = extracted_license.text.toPython()
- else:
- extracted_license_object["extractedText"] = extracted_license.text
-
- if extracted_license.full_name:
- if isinstance(extracted_license.full_name, Literal):
- extracted_license_object[
- "name"
- ] = extracted_license.full_name.toPython()
- else:
- extracted_license_object["name"] = extracted_license.full_name
-
- if extracted_license.cross_ref:
- if isinstance(extracted_license.cross_ref, Literal):
- extracted_license_object[
- "seeAlsos"
- ] = extracted_license.cross_ref.toPython()
- else:
- extracted_license_object["seeAlsos"] = extracted_license.cross_ref
-
- if extracted_license.comment:
- if isinstance(extracted_license.comment, Literal):
- extracted_license_object[
- "comment"
- ] = extracted_license.comment.toPython()
- else:
- extracted_license_object["comment"] = extracted_license.comment
-
- extracted_license_objects.append(extracted_license_object)
-
- return extracted_license_objects
-
-
-class Writer(
- CreationInfoWriter,
- ReviewInfoWriter,
- FileWriter,
- PackageWriter,
- AnnotationInfoWriter,
- RelationshipInfoWriter,
- SnippetWriter,
- ExtractedLicenseWriter,
-):
- """
- Wrapper for the other writers.
- Represent a whole SPDX Document as json-serializable objects to then
- be written as json or yaml files.
- """
-
- def __init__(self, document):
- self.doc_spdx_id = self.spdx_id(document.spdx_id)
- super(Writer, self).__init__(document)
-
- def create_ext_document_references(self):
- """
- Create the External Document References json-serializable representation
- """
- ext_document_references_field = self.document.ext_document_references
- ext_document_reference_objects = []
- for ext_document_reference in ext_document_references_field:
- ext_document_reference_object = dict()
- ext_document_reference_object[
- "externalDocumentId"
- ] = ext_document_reference.external_document_id
- ext_document_reference_object[
- "spdxDocument"
- ] = ext_document_reference.spdx_document_uri
-
- ext_document_reference_object["checksum"] = self.checksum_to_dict(
- ext_document_reference.checksum
- )
-
- ext_document_reference_objects.append(ext_document_reference_object)
-
- return ext_document_reference_objects
-
- def create_relationships(self) -> List[Dict]:
- packages_spdx_ids = [package.spdx_id for package in self.document.packages]
- files_spdx_ids = [file.spdx_id for file in self.document.files]
- # we take the package_objects from document_object if any exist because we will modify them to add
- # jsonyamlxml-specific fields
- if "packages" in self.document_object:
- packages_by_spdx_id = {package["SPDXID"]: package for package in self.document_object["packages"]}
- else:
- packages_by_spdx_id = {}
-
- relationship_objects = []
- for relationship in self.document.relationships:
- if relationship.relationship_type == "CONTAINS" and relationship.spdx_element_id in packages_spdx_ids \
- and relationship.related_spdx_element in files_spdx_ids:
- update_dict_item_with_new_item(packages_by_spdx_id[relationship.spdx_element_id], "hasFiles",
- relationship.related_spdx_element)
- if relationship.has_comment:
- relationship_objects.append(self.create_relationship_info(relationship))
-
- elif relationship.relationship_type == "CONTAINED_BY" and relationship.spdx_element_id in files_spdx_ids \
- and relationship.related_spdx_element in packages_spdx_ids:
- update_dict_item_with_new_item(packages_by_spdx_id[relationship.related_spdx_element],
- "hasFiles", relationship.spdx_element_id)
- if relationship.has_comment:
- relationship_objects.append(self.create_relationship_info(relationship))
-
- elif relationship.relationship_type == "DESCRIBES" and relationship.spdx_element_id == self.document.spdx_id:
- update_dict_item_with_new_item(self.document_object, "documentDescribes",
- relationship.related_spdx_element)
- if relationship.has_comment:
- relationship_objects.append(self.create_relationship_info(relationship))
-
- elif relationship.relationship_type == "DESCRIBED_BY" and relationship.related_spdx_element == self.document.spdx_id:
- update_dict_item_with_new_item(self.document_object, "documentDescribes",
- relationship.spdx_element_id)
- if relationship.has_comment:
- relationship_objects.append(self.create_relationship_info(relationship))
-
- else:
- relationship_objects.append(self.create_relationship_info(relationship))
-
- return relationship_objects
-
- def create_document(self):
- self.document_object = dict()
-
- self.document_object["spdxVersion"] = self.document.version.__str__()
- self.document_object["documentNamespace"] = self.document.namespace.__str__()
- self.document_object["creationInfo"] = self.create_creation_info()
- self.document_object["dataLicense"] = self.license(self.document.data_license)
- self.document_object["SPDXID"] = self.doc_spdx_id
- self.document_object["name"] = self.document.name
- annotations_by_spdx_id = self.create_annotations_by_spdx_id()
-
- unique_doc_packages = {}
- for doc_package in self.document.packages:
- if doc_package.spdx_id not in unique_doc_packages.keys():
- unique_doc_packages[doc_package.spdx_id] = doc_package
- if unique_doc_packages:
- package_objects = []
- for package in unique_doc_packages.values():
- package_info_object = self.create_package_info(package, annotations_by_spdx_id)
- package_objects.append(package_info_object)
- self.document_object["packages"] = package_objects
- if self.document.files:
- file_objects = []
- for file in self.document.files:
- file_object = self.create_file_info(file, annotations_by_spdx_id)
- file_objects.append(file_object)
- self.document_object["files"] = file_objects
-
- if self.document.has_comment:
- self.document_object["comment"] = self.document.comment
-
- if self.document.ext_document_references:
- self.document_object[
- "externalDocumentRefs"
- ] = self.create_ext_document_references()
-
- if self.document.extracted_licenses:
- self.document_object[
- "hasExtractedLicensingInfos"
- ] = self.create_extracted_license()
-
- if self.document.reviews:
- self.document_object["reviewers"] = self.create_review_info()
-
- if self.document.snippet:
- self.document_object["snippets"] = self.create_snippet_info(annotations_by_spdx_id)
-
- if self.doc_spdx_id in annotations_by_spdx_id:
- self.document_object["annotations"] = annotations_by_spdx_id[self.doc_spdx_id]
-
- if self.document.relationships:
- relationship_objects = self.create_relationships()
- if relationship_objects:
- self.document_object["relationships"] = relationship_objects
-
- return self.document_object
diff --git a/spdx/writers/rdf.py b/spdx/writers/rdf.py
deleted file mode 100644
index 7e2b898f7..000000000
--- a/spdx/writers/rdf.py
+++ /dev/null
@@ -1,1087 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from rdflib import BNode
-from rdflib import Graph
-from rdflib import Literal
-from rdflib import Namespace
-from rdflib import RDF
-from rdflib import RDFS
-from rdflib import URIRef
-from rdflib.compare import to_isomorphic
-
-from spdx import config
-from spdx import file
-from spdx import license
-from spdx import utils
-from spdx.checksum import Checksum
-from spdx.package import Package
-from spdx.parsers.loggers import ErrorMessages
-from spdx.relationship import Relationship
-from spdx.utils import get_files_in_package
-from spdx.writers.tagvalue import InvalidDocumentError
-
-import warnings
-
-
-class BaseWriter(object):
- """
- Base class for all Writer classes.
- Provide utility functions and stores shared fields.
- """
-
- def __init__(self, document, out):
- self.document = document
- self.out = out
- self.doap_namespace = Namespace("http://usefulinc.com/ns/doap#")
- self.spdx_namespace = Namespace("http://spdx.org/rdf/terms#")
- self.graph = Graph()
-
- def create_checksum_node(self, checksum: Checksum) -> BNode:
- """
- Return a node representing spdx.checksum.
- """
- algo = checksum.identifier.algorithm_to_rdf_representation() or 'checksumAlgorithm_sha1'
- checksum_node = BNode()
- type_triple = (checksum_node, RDF.type, self.spdx_namespace.Checksum)
- self.graph.add(type_triple)
- algorithm_triple = (
- checksum_node,
- self.spdx_namespace.algorithm,
- Literal('http://spdx.org/rdf/terms#' + algo),
- )
- self.graph.add(algorithm_triple)
- value_triple = (
- checksum_node,
- self.spdx_namespace.checksumValue,
- Literal(checksum.value),
- )
- self.graph.add(value_triple)
- return checksum_node
-
- def to_special_value(self, value):
- """
- Return proper spdx term or Literal
- """
- if isinstance(value, utils.NoAssert):
- return self.spdx_namespace.noassertion
- elif isinstance(value, utils.SPDXNone):
- return self.spdx_namespace.none
- else:
- return Literal(value)
-
-
-class LicenseWriter(BaseWriter):
- """
- Handle all License classes from spdx.document module.
- """
-
- def __init__(self, document, out):
- super(LicenseWriter, self).__init__(document, out)
-
- def licenses_from_tree_helper(self, current, licenses):
- if isinstance(
- current, (license.LicenseConjunction, license.LicenseDisjunction)
- ):
- self.licenses_from_tree_helper(current.license_1, licenses)
- self.licenses_from_tree_helper(current.license_2, licenses)
- else:
- licenses.add(self.create_license_helper(current))
-
- def licenses_from_tree(self, tree):
- """
- Traverse conjunctions and disjunctions like trees and return a
- set of all licenses in it as nodes.
- """
- # FIXME: this is unordered!
- licenses = set()
- self.licenses_from_tree_helper(tree, licenses)
- return licenses
-
- def create_conjunction_node(self, conjunction):
- """
- Return a node representing a conjunction of licenses.
- """
- node = BNode()
- type_triple = (node, RDF.type, self.spdx_namespace.ConjunctiveLicenseSet)
- self.graph.add(type_triple)
- licenses = self.licenses_from_tree(conjunction)
- for lic in licenses:
- member_triple = (node, self.spdx_namespace.member, lic)
- self.graph.add(member_triple)
- return node
-
- def create_disjunction_node(self, disjunction):
- """
- Return a node representing a disjunction of licenses.
- """
- node = BNode()
- type_triple = (node, RDF.type, self.spdx_namespace.DisjunctiveLicenseSet)
- self.graph.add(type_triple)
- licenses = self.licenses_from_tree(disjunction)
- for lic in licenses:
- member_triple = (node, self.spdx_namespace.member, lic)
- self.graph.add(member_triple)
- return node
-
- def create_license_helper(self, lic):
- """
- Handle single(no conjunction/disjunction) licenses.
- Return the created node.
- """
- if isinstance(lic, license.ExtractedLicense):
- return self.create_extracted_license(lic)
- if lic.identifier.rstrip("+") in config.LICENSE_MAP:
- return URIRef(lic.url)
- else:
- matches = [
- l
- for l in self.document.extracted_licenses
- if l.identifier == lic.identifier
- ]
- if len(matches) != 0:
- return self.create_extracted_license(matches[0])
- else:
- lic = license.ExtractedLicense(lic.identifier)
- warnings.warn(
- "Missing extracted license: {0}".format(lic.identifier)
- )
- return self.create_extracted_license(lic)
-
- def create_extracted_license(self, lic):
- """
- Handle extracted license.
- Return the license node.
- """
- licenses = list(
- self.graph.triples((None, self.spdx_namespace.licenseId, lic.identifier))
- )
- if len(licenses) != 0:
- return licenses[0][0] # return subject in first triple
- else:
- license_node = BNode()
- type_triple = (
- license_node,
- RDF.type,
- self.spdx_namespace.ExtractedLicensingInfo,
- )
- self.graph.add(type_triple)
- ident_triple = (
- license_node,
- self.spdx_namespace.licenseId,
- Literal(lic.identifier),
- )
- self.graph.add(ident_triple)
- text_triple = (
- license_node,
- self.spdx_namespace.extractedText,
- Literal(lic.text),
- )
- self.graph.add(text_triple)
- if lic.full_name is not None:
- name_triple = (
- license_node,
- self.spdx_namespace.licenseName,
- self.to_special_value(lic.full_name),
- )
- self.graph.add(name_triple)
- for ref in lic.cross_ref:
- triple = (license_node, RDFS.seeAlso, URIRef(ref))
- self.graph.add(triple)
- if lic.comment is not None:
- comment_triple = (license_node, RDFS.comment, Literal(lic.comment))
- self.graph.add(comment_triple)
- return license_node
-
- def create_license_node(self, lic):
- """
- Return a node representing a license.
- Could be a single license (extracted or part of license list.) or
- a conjunction/disjunction of licenses.
- """
- if isinstance(lic, license.LicenseConjunction):
- return self.create_conjunction_node(lic)
- elif isinstance(lic, license.LicenseDisjunction):
- return self.create_disjunction_node(lic)
- else:
- return self.create_license_helper(lic)
-
- def license_or_special(self, lic):
- """
- Check for special values spdx:none and spdx:noassertion.
- Return the term for the special value or the result of passing
- license to create_license_node.
- """
- if isinstance(lic, utils.NoAssert):
- return self.spdx_namespace.noassertion
- elif isinstance(lic, utils.SPDXNone):
- return self.spdx_namespace.none
- else:
- return self.create_license_node(lic)
-
-
-class FileWriter(LicenseWriter):
- """
- Write spdx.file.File
- """
-
- FILE_TYPES = {
- file.FileType.SOURCE: "fileType_source",
- file.FileType.OTHER: "fileType_other",
- file.FileType.BINARY: "fileType_binary",
- file.FileType.ARCHIVE: "fileType_archive",
- }
-
- def __init__(self, document, out):
- super(FileWriter, self).__init__(document, out)
-
- def create_file_node(self, doc_file):
- """
- Create a node for spdx.file.
- """
- file_node = URIRef(
- "http://www.spdx.org/files#{id}".format(id=str(doc_file.spdx_id))
- )
- type_triple = (file_node, RDF.type, self.spdx_namespace.File)
- self.graph.add(type_triple)
-
- name_triple = (file_node, self.spdx_namespace.fileName, Literal(doc_file.name))
- self.graph.add(name_triple)
-
- if doc_file.has_optional_field("comment"):
- comment_triple = (file_node, RDFS.comment, Literal(doc_file.comment))
- self.graph.add(comment_triple)
-
- if doc_file.has_optional_field("file_types"):
- for f_type in doc_file.file_types:
- ftype = self.spdx_namespace[file.file_type_to_rdf(f_type)]
- ftype_triple = (file_node, self.spdx_namespace.fileType, ftype)
- self.graph.add(ftype_triple)
-
- for chk_sum in doc_file.checksums.values():
- self.graph.add(
- (
- file_node,
- self.spdx_namespace.checksum,
- self.create_checksum_node(chk_sum),
- )
- )
-
- conc_lic_node = self.license_or_special(doc_file.conc_lics)
- conc_lic_triple = (
- file_node,
- self.spdx_namespace.licenseConcluded,
- conc_lic_node,
- )
- self.graph.add(conc_lic_triple)
-
- license_info_nodes = map(self.license_or_special, doc_file.licenses_in_file)
- for lic in license_info_nodes:
- triple = (file_node, self.spdx_namespace.licenseInfoInFile, lic)
- self.graph.add(triple)
-
- if doc_file.has_optional_field("license_comment"):
- comment_triple = (
- file_node,
- self.spdx_namespace.licenseComments,
- Literal(doc_file.license_comment),
- )
- self.graph.add(comment_triple)
-
- if doc_file.has_optional_field("attribution_text"):
- file_attribution_text_triple = (
- file_node,
- self.spdx_namespace.attributionText,
- Literal(doc_file.attribution_text),
- )
- self.graph.add(file_attribution_text_triple)
-
- cr_text_node = self.to_special_value(doc_file.copyright)
- cr_text_triple = (file_node, self.spdx_namespace.copyrightText, cr_text_node)
- self.graph.add(cr_text_triple)
-
- if doc_file.has_optional_field("notice"):
- notice_triple = (file_node, self.spdx_namespace.noticeText, doc_file.notice)
- self.graph.add(notice_triple)
-
- contrib_nodes = map(lambda c: Literal(c), doc_file.contributors)
- contrib_triples = [
- (file_node, self.spdx_namespace.fileContributor, node)
- for node in contrib_nodes
- ]
- for triple in contrib_triples:
- self.graph.add(triple)
-
- return file_node
-
- def files(self):
- """
- Return list of file nodes.
- """
- return map(self.create_file_node, self.document.files)
-
- def add_file_dependencies_helper(self, doc_file):
- """
- Handle dependencies for a single file.
- - doc_file - instance of spdx.file.File.
- """
- subj_triples = list(
- self.graph.triples(
- (None, self.spdx_namespace.fileName, Literal(doc_file.name))
- )
- )
- if len(subj_triples) != 1:
- raise InvalidDocumentError(
- "Could not find dependency subject {0}".format(doc_file.name)
- )
- subject_node = subj_triples[0][0]
- for dependency in doc_file.dependencies:
- dep_triples = list(
- self.graph.triples(
- (None, self.spdx_namespace.fileName, Literal(dependency))
- )
- )
- if len(dep_triples) == 1:
- dep_node = dep_triples[0][0]
- dep_triple = (
- subject_node,
- self.spdx_namespace.fileDependency,
- dep_node,
- )
- self.graph.add(dep_triple)
- else:
- print(
- "Warning could not resolve file dependency {0} -> {1}".format(
- doc_file.name, dependency
- )
- )
-
- def add_file_dependencies(self):
- """
- Add file dependencies to the graph.
- Called after all files have been added.
- """
- for doc_file in self.document.files:
- self.add_file_dependencies_helper(doc_file)
-
-
-class SnippetWriter(LicenseWriter):
- """
- Write spdx.snippet.Snippet
- """
-
- def __init__(self, document, out):
- super(SnippetWriter, self).__init__(document, out)
-
- def create_snippet_node(self, snippet):
- """
- Return a snippet node.
- """
- snippet_node = URIRef("http://spdx.org/rdf/terms/Snippet#" + snippet.spdx_id)
- type_triple = (snippet_node, RDF.type, self.spdx_namespace.Snippet)
- self.graph.add(type_triple)
-
- if snippet.has_optional_field("comment"):
- comment_triple = (snippet_node, RDFS.comment, Literal(snippet.comment))
- self.graph.add(comment_triple)
-
- if snippet.has_optional_field("name"):
- name_triple = (
- snippet_node,
- self.spdx_namespace.name,
- Literal(snippet.name),
- )
- self.graph.add(name_triple)
-
- if snippet.has_optional_field("license_comment"):
- lic_comment_triple = (
- snippet_node,
- self.spdx_namespace.licenseComments,
- Literal(snippet.license_comment),
- )
- self.graph.add(lic_comment_triple)
-
- if snippet.has_optional_field("attribution_text"):
- lic_attribution_text_triple = (
- snippet_node,
- self.spdx_namespace.attributionText,
- Literal(snippet.attribution_text),
- )
- self.graph.add(lic_attribution_text_triple)
-
- cr_text_node = self.to_special_value(snippet.copyright)
- cr_text_triple = (snippet_node, self.spdx_namespace.copyrightText, cr_text_node)
- self.graph.add(cr_text_triple)
-
- snip_from_file_triple = (
- snippet_node,
- self.spdx_namespace.snippetFromFile,
- Literal(snippet.snip_from_file_spdxid),
- )
- self.graph.add(snip_from_file_triple)
-
- conc_lic_node = self.license_or_special(snippet.conc_lics)
- conc_lic_triple = (
- snippet_node,
- self.spdx_namespace.licenseConcluded,
- conc_lic_node,
- )
- self.graph.add(conc_lic_triple)
-
- license_info_nodes = map(self.license_or_special, snippet.licenses_in_snippet)
- for lic in license_info_nodes:
- triple = (snippet_node, self.spdx_namespace.licenseInfoInSnippet, lic)
- self.graph.add(triple)
-
- return snippet_node
-
- def snippets(self):
- """
- Return list of snippet nodes.
- """
- return map(self.create_snippet_node, self.document.snippet)
-
-
-class ReviewInfoWriter(BaseWriter):
- """
- Write spdx.review.Review
- """
-
- def __init__(self, document, out):
- super(ReviewInfoWriter, self).__init__(document, out)
-
- def create_review_node(self, review):
- """
- Return a review node.
- """
- review_node = BNode()
- type_triple = (review_node, RDF.type, self.spdx_namespace.Review)
- self.graph.add(type_triple)
-
- reviewer_node = Literal(review.reviewer.to_value())
- self.graph.add((review_node, self.spdx_namespace.reviewer, reviewer_node))
- reviewed_date_node = Literal(review.review_date_iso_format)
- reviewed_triple = (
- review_node,
- self.spdx_namespace.reviewDate,
- reviewed_date_node,
- )
- self.graph.add(reviewed_triple)
- if review.has_comment:
- comment_node = Literal(review.comment)
- comment_triple = (review_node, RDFS.comment, comment_node)
- self.graph.add(comment_triple)
-
- return review_node
-
- def reviews(self):
- "Returns a list of review nodes"
- return map(self.create_review_node, self.document.reviews)
-
-
-class AnnotationInfoWriter(BaseWriter):
- """
- Write spdx.annotation.Annotation
- """
-
- def __init__(self, document, out):
- super(AnnotationInfoWriter, self).__init__(document, out)
-
- def create_annotation_node(self, annotation):
- """
- Return an annotation node.
- """
- annotation_node = URIRef(str(annotation.spdx_id))
- type_triple = (annotation_node, RDF.type, self.spdx_namespace.Annotation)
- self.graph.add(type_triple)
-
- annotator_node = Literal(annotation.annotator.to_value())
- self.graph.add((annotation_node, self.spdx_namespace.annotator, annotator_node))
- annotation_date_node = Literal(annotation.annotation_date_iso_format)
- annotation_triple = (
- annotation_node,
- self.spdx_namespace.annotationDate,
- annotation_date_node,
- )
- self.graph.add(annotation_triple)
- if annotation.has_comment:
- comment_node = Literal(annotation.comment)
- comment_triple = (annotation_node, RDFS.comment, comment_node)
- self.graph.add(comment_triple)
- annotation_type_node = Literal(annotation.annotation_type)
- annotation_type_triple = (
- annotation_node,
- self.spdx_namespace.annotationType,
- annotation_type_node,
- )
- self.graph.add(annotation_type_triple)
-
- return annotation_node
-
- def annotations(self):
- """
- Return a list of annotation nodes
- """
- return map(self.create_annotation_node, self.document.annotations)
-
-
-class RelationshipInfoWriter(BaseWriter):
- """
- Write spdx.relationship.Relationship
- """
-
- def __init__(self, document, out):
- super(RelationshipInfoWriter, self).__init__(document, out)
-
- def transform_relationship_type_to_rdf_model(self, relationship_type: str) -> str:
- """
- Transform relationship type from upper snake case to camel case to match rdf-specific output e.g.
- COPY_OF -> relationshipType_copyOf.
- """
- return "relationshipType_" + relationship_type[0].lower() + relationship_type.title().replace('_', '')[1:]
-
- def create_relationship_node(self, relationship: Relationship) -> BNode:
- """
- Return a relationship node.
- """
- relationship_node = BNode()
- type_triple = (relationship_node, RDF.type, self.spdx_namespace.Relationship)
- self.graph.add(type_triple)
-
- relationship_spdx_element_id = Literal(relationship.spdx_element_id)
- self.graph.add(
- (
- relationship_node,
- self.spdx_namespace.spdxElementId,
- relationship_spdx_element_id,
- )
- )
- relationship_type = self.transform_relationship_type_to_rdf_model(relationship.relationship_type)
- relationship_type_node = self.spdx_namespace[relationship_type]
- self.graph.add(
- (
- relationship_node,
- self.spdx_namespace.relationshipType,
- relationship_type_node,
- )
- )
- related_spdx_node = Literal(relationship.related_spdx_element)
- related_spdx_triple = (
- relationship_node,
- self.spdx_namespace.relatedSpdxElement,
- related_spdx_node,
- )
- self.graph.add(related_spdx_triple)
- if relationship.has_comment:
- comment_node = Literal(relationship.relationship_comment)
- comment_triple = (relationship_node, RDFS.comment, comment_node)
- self.graph.add(comment_triple)
-
- return relationship_node
-
- def relationships(self):
- """
- Return a list of relationship nodes
- """
- return map(self.create_relationship_node, self.document.relationships)
-
-
-class CreationInfoWriter(BaseWriter):
- """
- Write class spdx.creationinfo.CreationInfo
- """
-
- def __init__(self, document, out):
- super(CreationInfoWriter, self).__init__(document, out)
-
- def creators(self):
- """
- Return a list of creator nodes.
- Note: Does not add anything to the graph.
- """
- return map(
- lambda c: Literal(c.to_value()), self.document.creation_info.creators
- )
-
- def create_creation_info(self):
- """
- Add and return a creation info node to graph
- """
- ci_node = BNode()
- # Type property
- type_triple = (ci_node, RDF.type, self.spdx_namespace.CreationInfo)
- self.graph.add(type_triple)
-
- created_date = Literal(self.document.creation_info.created_iso_format)
- created_triple = (ci_node, self.spdx_namespace.created, created_date)
- self.graph.add(created_triple)
-
- creators = self.creators()
- for creator in creators:
- self.graph.add((ci_node, self.spdx_namespace.creator, creator))
-
- if self.document.creation_info.has_comment:
- comment_node = Literal(self.document.creation_info.comment)
- comment_triple = (ci_node, RDFS.comment, comment_node)
- self.graph.add(comment_triple)
-
- return ci_node
-
-
-class ExternalDocumentRefWriter(BaseWriter):
- """
- Write class spdx.external_document_ref.ExternalDocumentRef
- """
-
- def __init__(self, document, out):
- super(ExternalDocumentRefWriter, self).__init__(document, out)
-
- def create_external_document_ref_node(self, ext_document_references):
- """
- Add and return a creation info node to graph
- """
- ext_doc_ref_node = BNode()
- type_triple = (
- ext_doc_ref_node,
- RDF.type,
- self.spdx_namespace.ExternalDocumentRef,
- )
- self.graph.add(type_triple)
-
- ext_doc_id = Literal(ext_document_references.external_document_id)
- ext_doc_id_triple = (
- ext_doc_ref_node,
- self.spdx_namespace.externalDocumentId,
- ext_doc_id,
- )
- self.graph.add(ext_doc_id_triple)
-
- doc_uri = Literal(ext_document_references.spdx_document_uri)
- doc_uri_triple = (ext_doc_ref_node, self.spdx_namespace.spdxDocument, doc_uri)
- self.graph.add(doc_uri_triple)
-
- checksum_node = self.create_checksum_node(ext_document_references.checksum)
- self.graph.add((ext_doc_ref_node, self.spdx_namespace.checksum, checksum_node))
-
- return ext_doc_ref_node
-
- def ext_doc_refs(self):
- """
- Return a list of review nodes
- """
- return map(
- self.create_external_document_ref_node,
- self.document.ext_document_references,
- )
-
-
-class PackageWriter(LicenseWriter):
- """
- Write spdx.package.Package
- """
-
- def __init__(self, document, out):
- super(PackageWriter, self).__init__(document, out)
-
- def package_verif_node(self, package):
- """
- Return a node representing package verification code.
- """
- verif_node = BNode()
- type_triple = (
- verif_node,
- RDF.type,
- self.spdx_namespace.PackageVerificationCode,
- )
- self.graph.add(type_triple)
- value_triple = (
- verif_node,
- self.spdx_namespace.packageVerificationCodeValue,
- Literal(package.verif_code),
- )
- self.graph.add(value_triple)
- excl_file_nodes = map(lambda excl: Literal(excl), package.verif_exc_files)
- excl_predicate = self.spdx_namespace.packageVerificationCodeExcludedFile
- excl_file_triples = [
- (verif_node, excl_predicate, xcl_file) for xcl_file in excl_file_nodes
- ]
- for trp in excl_file_triples:
- self.graph.add(trp)
- return verif_node
-
- def handle_package_literal_optional(self, package, package_node, predicate, field):
- """
- Check if optional field is set.
- If so it adds the triple (package_node, predicate, $) to the graph.
- Where $ is a literal or special value term of the value of the field.
- """
- if package.has_optional_field(field):
- value = getattr(package, field, None)
- value_node = self.to_special_value(value)
- triple = (package_node, predicate, value_node)
- self.graph.add(triple)
-
- def handle_pkg_optional_fields(self, package: Package, package_node):
- """
- Write package optional fields.
- """
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.versionInfo, "version"
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.packageFileName, "file_name"
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.supplier, "supplier"
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.originator, "originator"
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.sourceInfo, "source_info"
- )
- self.handle_package_literal_optional(
- package,
- package_node,
- self.spdx_namespace.licenseComments,
- "license_comment",
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.summary, "summary"
- )
- self.handle_package_literal_optional(
- package,
- package_node,
- self.spdx_namespace.attributionText,
- "attribution_text",
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.description, "description"
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.comment, "comment"
- )
- self.handle_package_literal_optional(
- package, package_node, self.spdx_namespace.filesAnalyzed, "files_analyzed"
- )
-
- if package.has_optional_field("checksums"):
- for checksum in package.checksums.values():
- checksum_node = self.create_checksum_node(checksum)
- self.graph.add((package_node, self.spdx_namespace.checksum, checksum_node))
-
- if package.has_optional_field("homepage"):
- homepage_node = URIRef(self.to_special_value(package.homepage))
- homepage_triple = (
- package_node,
- self.doap_namespace.homepage,
- homepage_node,
- )
- self.graph.add(homepage_triple)
-
- def create_package_node(self, package):
- """
- Return a Node representing the package.
- Files must have been added to the graph before this method is called.
- """
- package_node = URIRef("http://www.spdx.org/tools#SPDXRef-Package")
- type_triple = (package_node, RDF.type, self.spdx_namespace.Package)
- self.graph.add(type_triple)
- # Package SPDXID
- if package.spdx_id:
- pkg_spdx_id = URIRef(package.spdx_id)
- pkg_spdx_id_triple = (
- package_node,
- self.spdx_namespace.Package,
- pkg_spdx_id,
- )
- self.graph.add(pkg_spdx_id_triple)
- # Handle optional fields:
- self.handle_pkg_optional_fields(package, package_node)
- # package name
- name_triple = (package_node, self.spdx_namespace.name, Literal(package.name))
- self.graph.add(name_triple)
- # Package download location
- down_loc_node = (
- package_node,
- self.spdx_namespace.downloadLocation,
- self.to_special_value(package.download_location),
- )
- self.graph.add(down_loc_node)
- # Handle package verification
- if package.files_analyzed != False:
- verif_node = self.package_verif_node(package)
- verif_triple = (
- package_node,
- self.spdx_namespace.packageVerificationCode,
- verif_node,
- )
- self.graph.add(verif_triple)
- # Handle concluded license
- if package.conc_lics:
- conc_lic_node = self.license_or_special(package.conc_lics)
- conc_lic_triple = (
- package_node,
- self.spdx_namespace.licenseConcluded,
- conc_lic_node,
- )
- self.graph.add(conc_lic_triple)
- # Handle declared license
- if package.license_declared:
- decl_lic_node = self.license_or_special(package.license_declared)
- decl_lic_triple = (
- package_node,
- self.spdx_namespace.licenseDeclared,
- decl_lic_node,
- )
- self.graph.add(decl_lic_triple)
- # Package licenses from files
- licenses_from_files_nodes = map(
- lambda el: self.license_or_special(el), package.licenses_from_files
- )
- lic_from_files_predicate = self.spdx_namespace.licenseInfoFromFiles
- lic_from_files_triples = [
- (package_node, lic_from_files_predicate, node)
- for node in licenses_from_files_nodes
- ]
- for triple in lic_from_files_triples:
- self.graph.add(triple)
- # Copyright Text
- cr_text_node = self.to_special_value(package.cr_text)
- cr_text_triple = (package_node, self.spdx_namespace.copyrightText, cr_text_node)
- self.graph.add(cr_text_triple)
- # Handle files
- self.handle_package_has_file(package, package_node)
- return package_node
-
- def packages(self):
- """
- Return a node that represents the package in the graph.
- Call this function to write package info.
- """
- return [self.create_package_node(x)
- for x in self.document.packages]
-
- def handle_package_has_file_helper(self, pkg_file):
- """
- Return node representing pkg_file
- pkg_file should be instance of spdx.file.
- """
- nodes = list(
- self.graph.triples(
- (None, self.spdx_namespace.fileName, Literal(pkg_file.name))
- )
- )
- if len(nodes) == 1:
- return nodes[0][0]
- else:
- raise InvalidDocumentError(
- "handle_package_has_file_helper could not"
- + " find file node for file: {0}".format(pkg_file.name)
- )
-
- def handle_package_has_file(self, package, package_node):
- """
- Add hasFile triples to graph.
- Must be called after files have been added.
- """
- files = get_files_in_package(package, self.document.files, self.document.relationships)
- file_nodes = map(self.handle_package_has_file_helper, files)
- triples = [
- (package_node, self.spdx_namespace.hasFile, node) for node in file_nodes
- ]
- for triple in triples:
- self.graph.add(triple)
-
-
-class PackageExternalRefWriter(BaseWriter):
- """
- Write class spdx.package.ExternalPackageRef
- """
-
- def __init__(self, document, out):
- super(PackageExternalRefWriter, self).__init__(document, out)
-
- def create_package_external_ref_node(self, pkg_ext_refs):
- """
- Add and return an external package reference node to graph.
- """
- pkg_ext_ref_node = BNode()
- pkg_ext_ref_triple = (
- pkg_ext_ref_node,
- RDF.type,
- self.spdx_namespace.ExternalRef,
- )
- self.graph.add(pkg_ext_ref_triple)
-
- pkg_ext_ref_category = Literal(pkg_ext_refs.category)
- pkg_ext_ref_category_triple = (
- pkg_ext_ref_node,
- self.spdx_namespace.referenceCategory,
- pkg_ext_ref_category,
- )
- self.graph.add(pkg_ext_ref_category_triple)
-
- pkg_ext_ref_type = Literal(pkg_ext_refs.pkg_ext_ref_type)
- pkg_ext_ref_type_triple = (
- pkg_ext_ref_node,
- self.spdx_namespace.referenceType,
- pkg_ext_ref_type,
- )
- self.graph.add(pkg_ext_ref_type_triple)
-
- pkg_ext_ref_locator = Literal(pkg_ext_refs.locator)
- pkg_ext_ref_locator_triple = (
- pkg_ext_ref_node,
- self.spdx_namespace.referenceLocator,
- pkg_ext_ref_locator,
- )
- self.graph.add(pkg_ext_ref_locator_triple)
-
- if pkg_ext_refs.comment:
- pkg_ext_ref_comment = Literal(pkg_ext_refs.comment)
- pkg_ext_ref_comment_triple = (
- pkg_ext_ref_node,
- RDFS.comment,
- pkg_ext_ref_comment,
- )
- self.graph.add(pkg_ext_ref_comment_triple)
-
- return pkg_ext_ref_node
-
- def pkg_ext_refs(self):
- """
- Return a list of package external references.
- """
- return [self.create_package_external_ref_node(ext_ref) for package in self.document.packages
- for ext_ref in package.pkg_ext_refs]
-
-
-class Writer(
- CreationInfoWriter,
- ReviewInfoWriter,
- FileWriter,
- PackageWriter,
- PackageExternalRefWriter,
- ExternalDocumentRefWriter,
- AnnotationInfoWriter,
- RelationshipInfoWriter,
- SnippetWriter,
-):
- """
- Wrapper for other writers to write all fields of spdx.document.Document
- Call `write()` to start writing.
- """
-
- def __init__(self, document, out):
- """
- - document is spdx.document instance that will be written.
- - out is a file-like object that will be written to.
- """
- super(Writer, self).__init__(document, out)
-
- def create_doc(self):
- """
- Add and return the root document node to graph.
- """
- doc_node = URIRef("http://www.spdx.org/tools#SPDXRef-DOCUMENT")
- # Doc type
- self.graph.add((doc_node, RDF.type, self.spdx_namespace.SpdxDocument))
- # Version
- vers_literal = Literal(str(self.document.version))
- self.graph.add((doc_node, self.spdx_namespace.specVersion, vers_literal))
- # Data license
- data_lics = URIRef(self.document.data_license.url)
- self.graph.add((doc_node, self.spdx_namespace.dataLicense, data_lics))
- if self.document.name:
- doc_name = URIRef(self.document.name)
- self.graph.add((doc_node, self.spdx_namespace.name, doc_name))
- return doc_node
-
- def write(self, doc_node=None):
- if not doc_node:
- doc_node = self.create_doc()
- # Add creation info
- creation_info_node = self.create_creation_info()
- ci_triple = (doc_node, self.spdx_namespace.creationInfo, creation_info_node)
- self.graph.add(ci_triple)
- # Add review info
- review_nodes = self.reviews()
- for review in review_nodes:
- self.graph.add((doc_node, self.spdx_namespace.reviewed, review))
- # Add external document references info
- ext_doc_ref_nodes = self.ext_doc_refs()
- for ext_doc_ref in ext_doc_ref_nodes:
- ext_doc_ref_triple = (
- doc_node,
- self.spdx_namespace.externalDocumentRef,
- ext_doc_ref,
- )
- self.graph.add(ext_doc_ref_triple)
- # Add extracted licenses
- licenses = map(self.create_extracted_license, self.document.extracted_licenses)
- for lic in licenses:
- self.graph.add(
- (doc_node, self.spdx_namespace.hasExtractedLicensingInfo, lic)
- )
- # Add files
- files = self.files()
- for file_node in files:
- self.graph.add((doc_node, self.spdx_namespace.referencesFile, file_node))
- self.add_file_dependencies()
- # Add package
- for package_node in self.packages():
- package_triple = (doc_node, self.spdx_namespace.describesPackage, package_node)
- self.graph.add(package_triple)
-
- # Add external package reference
- for pkg_ext_ref_node in self.pkg_ext_refs():
- pkg_ext_ref_triple = (
- doc_node,
- self.spdx_namespace.ExternalRef,
- pkg_ext_ref_node,
- )
- self.graph.add(pkg_ext_ref_triple)
-
- # Add relationship
- for relate_node in self.relationships():
- relate_triple = (doc_node, self.spdx_namespace.relationship, relate_node)
- self.graph.add(relate_triple)
-
- # Add snippet
- snippet_nodes = self.snippets()
- for snippet in snippet_nodes:
- self.graph.add((doc_node, self.spdx_namespace.Snippet, snippet))
-
- # normalize the graph to ensure that the sort order is stable
- self.graph = to_isomorphic(self.graph)
-
- # Write file
- self.graph.serialize(self.out, "pretty-xml", encoding="utf-8")
-
-
-def write_document(document, out, validate=True):
- """
- Write an SPDX RDF document.
- - document - spdx.document instance.
- - out - file like object that will be written to.
- Optionally `validate` the document before writing and raise
- InvalidDocumentError if document.validate returns False.
- """
-
- if validate:
- messages = ErrorMessages()
- messages = document.validate(messages)
- if messages:
- raise InvalidDocumentError(messages)
-
- writer = Writer(document, out)
- writer.write()
diff --git a/spdx/writers/tagvalue.py b/spdx/writers/tagvalue.py
deleted file mode 100644
index 8e734bf7f..000000000
--- a/spdx/writers/tagvalue.py
+++ /dev/null
@@ -1,475 +0,0 @@
-# Copyright (c) 2014 Ahmed H. Ismail
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from itertools import zip_longest
-from typing import List, TextIO, Tuple, Dict
-
-from spdx import license, utils
-from spdx import file as spdx_file
-from spdx.document import Document
-from spdx.file import File
-from spdx.package import Package
-from spdx.parsers.loggers import ErrorMessages
-from spdx.relationship import Relationship
-from spdx.snippet import Snippet
-from spdx.version import Version
-
-
-class InvalidDocumentError(Exception):
- """
- Raised when attempting to write an invalid document.
- """
-
- pass
-
-
-def write_separator(out):
- out.write("\n")
-
-
-def write_separators(out):
- out.write("\n" * 2)
-
-
-def format_verif_code(package):
- if len(package.verif_exc_files) == 0:
- return package.verif_code
- else:
- return "{0} ({1})".format(package.verif_code, ",".join(package.verif_exc_files))
-
-
-def write_value(tag, value, out):
- out.write("{0}: {1}\n".format(tag, value))
-
-
-def write_range(tag, value, out):
- out.write("{0}: {1}:{2}\n".format(tag, value[0], value[1]))
-
-
-def write_text_value(tag, value, out):
- if "\n" in value:
- out.write("{0}: {1} \n".format(tag, value))
- else:
- write_value(tag, value, out)
-
-
-def write_creation_info(creation_info, out):
- """
- Write the creation info to out.
- """
- out.write("# Creation Info\n\n")
- # Write sorted creators
- for creator in sorted(creation_info.creators):
- write_value("Creator", creator, out)
-
- # write created
- write_value("Created", creation_info.created_iso_format, out)
- # possible comment
- if creation_info.has_comment:
- write_text_value("CreatorComment", creation_info.comment, out)
-
-
-def write_review(review, out):
- """
- Write the fields of a single review to out.
- """
- write_value("Reviewer", review.reviewer, out)
- write_value("ReviewDate", review.review_date_iso_format, out)
- if review.has_comment:
- write_text_value("ReviewComment", review.comment, out)
-
-
-def write_annotation(annotation, out):
- """
- Write the fields of a single annotation to out.
- """
- write_value("Annotator", annotation.annotator, out)
- write_value("AnnotationDate", annotation.annotation_date_iso_format, out)
- if annotation.has_comment:
- write_text_value("AnnotationComment", annotation.comment, out)
- write_value("AnnotationType", annotation.annotation_type, out)
- if annotation.spdx_id:
- write_value("SPDXREF", annotation.spdx_id, out)
-
-
-def write_relationship(relationship_term, out):
- """
- Write the fields of relationships to out.
- """
- write_value("Relationship", relationship_term.relationship, out)
- if relationship_term.has_comment:
- write_text_value(
- "RelationshipComment", relationship_term.relationship_comment, out
- )
-
-
-def write_file_type(ftype, out):
- write_value("FileType", ftype, out)
-
-
-def write_file(spdx_file, out):
- """
- Write a file fields to out.
- """
- out.write("# File\n\n")
- write_value("FileName", spdx_file.name, out)
- if spdx_file.spdx_id:
- write_value("SPDXID", spdx_file.spdx_id, out)
- for file_type in spdx_file.file_types:
- write_file_type(file_type.name, out)
- for file_checksum in spdx_file.checksums.values():
- write_value("FileChecksum", file_checksum.to_tv(), out)
- if spdx_file.has_optional_field("conc_lics"):
- if isinstance(
- spdx_file.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction)
- ):
- write_value("LicenseConcluded", "({0})".format(spdx_file.conc_lics), out)
- else:
- write_value("LicenseConcluded", spdx_file.conc_lics, out)
-
- # write sorted list
- for lics in sorted(spdx_file.licenses_in_file):
- write_value("LicenseInfoInFile", lics, out)
-
- if spdx_file.has_optional_field("copyright"):
- if isinstance(spdx_file.copyright, str):
- write_text_value("FileCopyrightText", spdx_file.copyright, out)
- else:
- write_value("FileCopyrightText", spdx_file.copyright, out)
-
- if spdx_file.has_optional_field("license_comment"):
- write_text_value("LicenseComments", spdx_file.license_comment, out)
-
- if spdx_file.has_optional_field("attribution_text"):
- write_text_value("FileAttributionText", spdx_file.attribution_text, out)
-
- if spdx_file.has_optional_field("comment"):
- write_text_value("FileComment", spdx_file.comment, out)
-
- if spdx_file.has_optional_field("notice"):
- write_text_value("FileNotice", spdx_file.notice, out)
-
- for contributor in sorted(spdx_file.contributors):
- write_value("FileContributor", contributor, out)
-
- for dependency in sorted(spdx_file.dependencies):
- write_value("FileDependency", dependency, out)
-
- names = spdx_file.artifact_of_project_name
- homepages = spdx_file.artifact_of_project_home
- uris = spdx_file.artifact_of_project_uri
-
- for name, homepage, uri in sorted(zip_longest(names, homepages, uris)):
- write_value("ArtifactOfProjectName", name, out)
- if homepage is not None:
- write_value("ArtifactOfProjectHomePage", homepage, out)
- if uri is not None:
- write_value("ArtifactOfProjectURI", uri, out)
-
-
-def write_snippet(snippet, out):
- """
- Write snippet fields to out.
- """
- out.write("# Snippet\n\n")
- write_value("SnippetSPDXID", snippet.spdx_id, out)
- write_value("SnippetFromFileSPDXID", snippet.snip_from_file_spdxid, out)
- if snippet.has_optional_field("copyright"):
- write_text_value("SnippetCopyrightText", snippet.copyright, out)
- write_range("SnippetByteRange", snippet.byte_range, out)
- if snippet.has_optional_field("line_range"):
- write_range("SnippetLineRange", snippet.line_range, out)
- if snippet.has_optional_field("name"):
- write_value("SnippetName", snippet.name, out)
- if snippet.has_optional_field("comment"):
- write_text_value("SnippetComment", snippet.comment, out)
- if snippet.has_optional_field("license_comment"):
- write_text_value("SnippetLicenseComments", snippet.license_comment, out)
- if snippet.has_optional_field("attribution_text"):
- write_text_value("SnippetAttributionText", snippet.attribution_text, out)
- if snippet.has_optional_field("conc_lics"):
- if isinstance(
- snippet.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction)
- ):
- write_value("SnippetLicenseConcluded", "({0})".format(snippet.conc_lics), out)
- else:
- write_value("SnippetLicenseConcluded", snippet.conc_lics, out)
- # Write sorted list
- for lics in sorted(snippet.licenses_in_snippet):
- write_value("LicenseInfoInSnippet", lics, out)
-
-
-def write_package(package, out):
- """
- Write a package fields to out.
- """
- out.write("# Package\n\n")
- if package.name:
- write_value("PackageName", package.name, out)
- if package.spdx_id:
- write_value("SPDXID", package.spdx_id, out)
- if package.has_optional_field("version"):
- write_value("PackageVersion", package.version, out)
- write_value("PackageDownloadLocation", package.download_location, out)
-
- if package.has_optional_field("files_analyzed"):
- write_value("FilesAnalyzed", package.files_analyzed, out)
-
- if package.has_optional_field("summary"):
- write_text_value("PackageSummary", package.summary, out)
-
- if package.has_optional_field("attribution_text"):
- write_text_value("PackageAttributionText", package.attribution_text, out)
-
- if package.has_optional_field("source_info"):
- write_text_value("PackageSourceInfo", package.source_info, out)
-
- if package.has_optional_field("file_name"):
- write_value("PackageFileName", package.file_name, out)
-
- if package.has_optional_field("supplier"):
- write_value("PackageSupplier", package.supplier, out)
-
- if package.has_optional_field("originator"):
- write_value("PackageOriginator", package.originator, out)
-
- for package_checksum in package.checksums.values():
- write_value("PackageChecksum", package_checksum.to_tv(), out)
-
- if package.has_optional_field("verif_code"):
- write_value("PackageVerificationCode", format_verif_code(package), out)
-
- if package.has_optional_field("description"):
- write_text_value("PackageDescription", package.description, out)
-
- if package.has_optional_field("comment"):
- write_text_value("PackageComment", package.comment, out)
-
- if package.has_optional_field("license_declared"):
- if isinstance(
- package.license_declared,
- (license.LicenseConjunction, license.LicenseDisjunction),
- ):
- write_value(
- "PackageLicenseDeclared", "({0})".format(package.license_declared), out
- )
- else:
- write_value("PackageLicenseDeclared", package.license_declared, out)
-
- if package.has_optional_field("conc_lics"):
- if isinstance(
- package.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction)
- ):
- write_value("PackageLicenseConcluded", "({0})".format(package.conc_lics), out)
- else:
- write_value("PackageLicenseConcluded", package.conc_lics, out)
-
- # Write sorted list of licenses.
- for lics in sorted(package.licenses_from_files):
- write_value("PackageLicenseInfoFromFiles", lics, out)
-
- if package.has_optional_field("license_comment"):
- write_text_value("PackageLicenseComments", package.license_comment, out)
-
- if package.has_optional_field("cr_text"):
- if isinstance(package.cr_text, str):
- write_text_value("PackageCopyrightText", package.cr_text, out)
- else:
- write_value("PackageCopyrightText", package.cr_text, out)
-
- if package.has_optional_field("homepage"):
- write_value("PackageHomePage", package.homepage, out)
-
- for pkg_ref in package.pkg_ext_refs:
- pkg_ref_str = " ".join(
- [pkg_ref.category, pkg_ref.pkg_ext_ref_type, pkg_ref.locator]
- )
- write_value("ExternalRef", pkg_ref_str, out)
- if pkg_ref.comment:
- write_text_value("ExternalRefComment", pkg_ref.comment, out)
-
- if package.has_optional_field("primary_package_purpose"):
- write_value("PrimaryPackagePurpose", package.primary_package_purpose.name.replace("_", "-"), out)
-
- if package.has_optional_field("built_date"):
- write_value("BuiltDate", utils.datetime_iso_format(package.built_date), out)
-
- if package.has_optional_field("release_date"):
- write_value("ReleaseDate", utils.datetime_iso_format(package.release_date), out)
-
- if package.has_optional_field("valid_until_date"):
- write_value("ValidUntilDate", utils.datetime_iso_format(package.valid_until_date), out)
-
-
-def write_extracted_licenses(lics, out):
- """
- Write extracted licenses fields to out.
- """
- write_value("LicenseID", lics.identifier, out)
-
- if lics.full_name is not None:
- write_value("LicenseName", lics.full_name, out)
-
- if lics.comment is not None:
- write_text_value("LicenseComment", lics.comment, out)
-
- for xref in sorted(lics.cross_ref):
- write_value("LicenseCrossReference", xref, out)
-
- write_text_value("ExtractedText", lics.text, out)
-
-
-def write_document(document, out, validate=True):
- """
- Write an SPDX tag value document.
- - document - spdx.document instance.
- - out - file like object that will be written to.
- Optionally `validate` the document before writing and raise
- InvalidDocumentError if document.validate returns False.
- """
- messages = ErrorMessages()
- messages = document.validate(messages)
- if validate and messages:
- raise InvalidDocumentError(messages)
-
- # Write out document information
- out.write("# Document Information\n\n")
- write_value("SPDXVersion", str(document.version), out)
- write_value("DataLicense", document.data_license.identifier, out)
- if document.namespace:
- write_value("DocumentNamespace", document.namespace, out)
- if document.name:
- write_value("DocumentName", document.name, out)
- if document.creation_info.license_list_version:
- version: Version = document.creation_info.license_list_version
- write_value("LicenseListVersion", str(version.major) + "." + str(version.minor), out)
- write_value("SPDXID", "SPDXRef-DOCUMENT", out)
- if document.has_comment:
- write_text_value("DocumentComment", document.comment, out)
- for doc_ref in document.ext_document_references:
- doc_ref_str = " ".join(
- [
- doc_ref.external_document_id,
- doc_ref.spdx_document_uri,
- doc_ref.checksum.identifier.name + ": " + doc_ref.checksum.value,
- ]
- )
- write_value("ExternalDocumentRef", doc_ref_str, out)
- write_separators(out)
- # Write out creation info
- write_creation_info(document.creation_info, out)
- write_separators(out)
-
- # Write sorted reviews
- if document.reviews:
- out.write("# Reviews\n\n")
- for review in sorted(document.reviews):
- write_review(review, out)
- write_separator(out)
- write_separator(out)
-
- # Write sorted annotations
- if document.annotations:
- out.write("# Annotations\n\n")
- for annotation in sorted(document.annotations):
- write_annotation(annotation, out)
- write_separator(out)
- write_separator(out)
-
- relationships_to_write, contained_files_by_package_id = scan_relationships(document.relationships,
- document.packages, document.files)
- contained_snippets_by_file_id = determine_files_containing_snippets(document.snippet, document.files)
- packaged_file_ids = [file.spdx_id for files_list in contained_files_by_package_id.values()
- for file in files_list]
- filed_snippet_ids = [snippet.spdx_id for snippets_list in contained_snippets_by_file_id.values()
- for snippet in snippets_list]
-
- # Write Relationships
- if relationships_to_write:
- out.write("# Relationships\n\n")
- for relationship in relationships_to_write:
- write_relationship(relationship, out)
- write_separators(out)
-
- # Write snippet info
- for snippet in document.snippet:
- if snippet.spdx_id not in filed_snippet_ids:
- write_snippet(snippet, out)
- write_separators(out)
-
- # Write file info
- for file in document.files:
- if file.spdx_id not in packaged_file_ids:
- write_file(file, out)
- write_separators(out)
- if file.spdx_id in contained_snippets_by_file_id:
- write_snippets(contained_snippets_by_file_id[file.spdx_id], out)
-
- # Write out package info
- for package in document.packages:
- write_package(package, out)
- write_separators(out)
- if package.spdx_id in contained_files_by_package_id:
- for file in contained_files_by_package_id[package.spdx_id]:
- write_file(file, out)
- write_separators(out)
- if file.spdx_id in contained_snippets_by_file_id:
- write_snippets(contained_snippets_by_file_id[file.spdx_id], out)
- break
-
- if document.extracted_licenses:
- out.write("# Extracted Licenses\n\n")
- for lic in sorted(document.extracted_licenses):
- write_extracted_licenses(lic, out)
- write_separator(out)
- write_separator(out)
-
-
-def write_snippets(snippets_to_write: List, out: TextIO) -> None:
- for snippet in snippets_to_write:
- write_snippet(snippet, out)
- write_separators(out)
-
-
-def scan_relationships(relationships: List[Relationship], packages: List[Package], files: List[File]) \
- -> Tuple[List, Dict]:
- contained_files_by_package_id = dict()
- relationships_to_write = []
- files_by_spdx_id = {file.spdx_id: file for file in files}
- packages_spdx_ids = [package.spdx_id for package in packages]
- for relationship in relationships:
- if relationship.relationship_type == "CONTAINS" and \
- relationship.spdx_element_id in packages_spdx_ids and \
- relationship.related_spdx_element in files_by_spdx_id.keys():
- contained_files_by_package_id.setdefault(relationship.spdx_element_id, []).append(
- files_by_spdx_id[relationship.related_spdx_element])
- if relationship.has_comment:
- relationships_to_write.append(relationship)
- elif relationship.relationship_type == "CONTAINED_BY" and \
- relationship.related_spdx_element in packages_spdx_ids and \
- relationship.spdx_element_id in files_by_spdx_id:
- contained_files_by_package_id.setdefault(relationship.related_spdx_element, []).append(
- files_by_spdx_id[relationship.spdx_element_id])
- if relationship.has_comment:
- relationships_to_write.append(relationship)
- else:
- relationships_to_write.append(relationship)
-
- return relationships_to_write, contained_files_by_package_id
-
-
-def determine_files_containing_snippets(snippets: List[Snippet], files: List[File]) -> Dict:
- contained_snippets_by_file_id = dict()
- for snippet in snippets:
- if snippet.snip_from_file_spdxid in [file.spdx_id for file in files]:
- contained_snippets_by_file_id.setdefault(snippet.snip_from_file_spdxid, []).append(snippet)
-
- return contained_snippets_by_file_id
diff --git a/spdx/writers/write_anything.py b/spdx/writers/write_anything.py
deleted file mode 100644
index 5e479ef6e..000000000
--- a/spdx/writers/write_anything.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) spdx contributors
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-from spdx.writers import json
-from spdx.writers import yaml
-from spdx.writers import rdf
-from spdx.writers import xml
-from spdx.writers import tagvalue
-from spdx.parsers.builderexceptions import FileTypeError
-
-
-def write_file(doc, fn, validate=True):
- out_mode = "w"
- if fn.endswith(".rdf") or fn.endswith(".rdf.xml"):
- writer_module = rdf
- out_mode = "wb"
- elif fn.endswith(".tag") or fn.endswith(".spdx"):
- writer_module = tagvalue
- elif fn.endswith(".json"):
- writer_module = json
- elif fn.endswith(".xml"):
- writer_module = xml
- elif fn.endswith(".yaml"):
- writer_module = yaml
- else:
- raise FileTypeError("FileType Not Supported")
-
- with open(fn, out_mode) as out:
- p = writer_module.write_document(doc, out, validate)
diff --git a/spdx/writers/xml.py b/spdx/writers/xml.py
deleted file mode 100644
index 14bbd4b99..000000000
--- a/spdx/writers/xml.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (c) the SPDX tools authors
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import xmltodict
-
-from spdx.writers.tagvalue import InvalidDocumentError
-from spdx.writers.jsonyamlxml import Writer
-from spdx.parsers.loggers import ErrorMessages
-
-
-def write_document(document, out, validate=True):
-
- if validate:
- messages = ErrorMessages()
- messages = document.validate(messages)
- if messages:
- raise InvalidDocumentError(messages)
-
- writer = Writer(document)
- document_object = {"Document": writer.create_document()}
-
- xmltodict.unparse(document_object, out, encoding="utf-8", pretty=True)
diff --git a/spdx/writers/yaml.py b/spdx/writers/yaml.py
deleted file mode 100644
index 2ccc6884d..000000000
--- a/spdx/writers/yaml.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (c) Xavier Figueroa
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import yaml
-
-from spdx.writers.tagvalue import InvalidDocumentError
-from spdx.writers.jsonyamlxml import Writer
-from spdx.parsers.loggers import ErrorMessages
-
-
-def write_document(document, out, validate=True):
-
- if validate:
- messages = ErrorMessages()
- messages = document.validate(messages)
- if messages:
- raise InvalidDocumentError(messages)
-
- writer = Writer(document)
- document_object = writer.create_document()
-
- yaml.safe_dump(document_object, out, indent=2, explicit_start=True, encoding='utf-8')
diff --git a/spdx/parsers/lexers/__init__.py b/src/spdx_tools/common/__init__.py
similarity index 100%
rename from spdx/parsers/lexers/__init__.py
rename to src/spdx_tools/common/__init__.py
diff --git a/src/spdx_tools/common/spdx_licensing.py b/src/spdx_tools/common/spdx_licensing.py
new file mode 100644
index 000000000..a9a17e973
--- /dev/null
+++ b/src/spdx_tools/common/spdx_licensing.py
@@ -0,0 +1,7 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from license_expression import get_spdx_licensing
+
+# this getter takes quite long so we only call it once in this singleton module
+spdx_licensing = get_spdx_licensing()
diff --git a/spdx/writers/__init__.py b/src/spdx_tools/common/typing/__init__.py
similarity index 100%
rename from spdx/writers/__init__.py
rename to src/spdx_tools/common/typing/__init__.py
diff --git a/src/spdx_tools/common/typing/constructor_type_errors.py b/src/spdx_tools/common/typing/constructor_type_errors.py
new file mode 100644
index 000000000..a9b4046aa
--- /dev/null
+++ b/src/spdx_tools/common/typing/constructor_type_errors.py
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+
+class ConstructorTypeErrors(TypeError):
+ """
+ Helper class that holds a list of error messages. Intended to capture all TypeErrors encountered during a
+ constructor call, instead of raising only the first one.
+ """
+
+ messages: List[str]
+
+ def __init__(self, messages: List[str]):
+ self.messages = messages
+
+ def get_messages(self):
+ return list(self.messages)
diff --git a/src/spdx_tools/common/typing/dataclass_with_properties.py b/src/spdx_tools/common/typing/dataclass_with_properties.py
new file mode 100644
index 000000000..e0b5c9614
--- /dev/null
+++ b/src/spdx_tools/common/typing/dataclass_with_properties.py
@@ -0,0 +1,49 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import dataclass
+
+from beartype import beartype
+from beartype.roar import BeartypeCallHintParamViolation
+
+
+def dataclass_with_properties(cls):
+ """Decorator to generate a dataclass with properties out of the class' value:type list.
+ Their getters and setters will be subjected to the @typechecked decorator to ensure type conformity."""
+ data_cls = dataclass(cls)
+ for field_name, field_type in data_cls.__annotations__.items():
+ set_field = make_setter(field_name, field_type)
+ get_field = make_getter(field_name, field_type)
+
+ setattr(data_cls, field_name, property(get_field, set_field))
+
+ return data_cls
+
+
+def make_setter(field_name, field_type):
+ """helper method to avoid late binding when generating functions in a for loop"""
+
+ @beartype
+ def set_field(self, value: field_type):
+ setattr(self, f"_{field_name}", value)
+
+ def set_field_with_error_conversion(self, value: field_type):
+ try:
+ set_field(self, value)
+ except BeartypeCallHintParamViolation as err:
+ error_message: str = f"SetterError {self.__class__.__name__}: {err}"
+
+ # As setters are created dynamically, their argument name is always "value". We replace it by the
+ # actual name so the error message is more helpful.
+ raise TypeError(error_message.replace("value", field_name, 1) + f": {value}")
+
+ return set_field_with_error_conversion
+
+
+def make_getter(field_name, field_type):
+ """helper method to avoid late binding when generating functions in a for loop"""
+
+ def get_field(self) -> field_type:
+ return getattr(self, f"_{field_name}")
+
+ return get_field
diff --git a/src/spdx_tools/common/typing/type_checks.py b/src/spdx_tools/common/typing/type_checks.py
new file mode 100644
index 000000000..d9dc9733a
--- /dev/null
+++ b/src/spdx_tools/common/typing/type_checks.py
@@ -0,0 +1,31 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import fields
+
+from beartype.typing import Any, Dict
+
+from spdx_tools.common.typing.constructor_type_errors import ConstructorTypeErrors
+
+
+def check_types_and_set_values(instance_under_construction: Any, local_variables: Dict) -> None:
+ """
+ Helper method to accumulate all type errors encountered during a constructor call and return them in a
+ ConstructorTypeErrors instance.
+ Background: Our setters are enhanced with runtime typechecks using beartype. However, this means that by
+ default, a TypeError is raised on the first type violation that is encountered. We consider it more helpful to
+ return all type violations in one go.
+ As an aside, defining constructors "manually" using this utility method helps avoid a nasty PyCharm bug:
+ https://youtrack.jetbrains.com/issue/PY-34569
+ """
+ errors = []
+ for field in fields(instance_under_construction):
+ key = field.name
+ value = local_variables.get(key)
+ try:
+ setattr(instance_under_construction, key, value)
+ except TypeError as err:
+ error_message: str = err.args[0]
+ errors.append(error_message)
+ if errors:
+ raise ConstructorTypeErrors(errors)
diff --git a/src/spdx_tools/spdx/__init__.py b/src/spdx_tools/spdx/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/casing_tools.py b/src/spdx_tools/spdx/casing_tools.py
new file mode 100644
index 000000000..c143cfd45
--- /dev/null
+++ b/src/spdx_tools/spdx/casing_tools.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from re import sub
+
+
+def snake_case_to_camel_case(snake_case_string: str) -> str:
+ each_word_capitalized = sub(r"[_\-]+", " ", snake_case_string).title().replace(" ", "")
+ return each_word_capitalized[0].lower() + each_word_capitalized[1:]
+
+
+def camel_case_to_snake_case(camel_case_string: str) -> str:
+ snake_case_string = sub("(?!^)([A-Z]+)", r"_\1", camel_case_string).lower()
+ return snake_case_string
diff --git a/src/spdx_tools/spdx/clitools/__init__.py b/src/spdx_tools/spdx/clitools/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/clitools/pyspdxtools.py b/src/spdx_tools/spdx/clitools/pyspdxtools.py
new file mode 100644
index 000000000..4219c6b81
--- /dev/null
+++ b/src/spdx_tools/spdx/clitools/pyspdxtools.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Yash Varshney
+# Copyright (c) 2023 spdx contributors
+# SPDX-License-Identifier: Apache-2.0
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+import sys
+from json import JSONDecodeError
+from xml.parsers.expat import ExpatError
+from xml.sax import SAXParseException
+
+import click
+from beartype.typing import List
+from yaml.scanner import ScannerError
+
+from spdx_tools.spdx.graph_generation import export_graph_from_document
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.parse_anything import parse_file
+from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
+from spdx_tools.spdx.validation.validation_message import ValidationMessage
+from spdx_tools.spdx.writer.tagvalue import tagvalue_writer
+from spdx_tools.spdx.writer.write_anything import write_file
+
+
+@click.command()
+@click.option("--infile", "-i", required=True, help="The file containing the document to be validated or converted.")
+@click.option(
+ "--outfile",
+ "-o",
+ help="The file to write the converted document to (write a dash for output to stdout or omit for no conversion). "
+ "If you add the option --graph to the command the generated graph will be written to this file.",
+)
+@click.option(
+ "--version",
+ help='The SPDX version to be used during parsing and validation ("SPDX-2.2" or "SPDX-2.3"). '
+ "Will be read from the document if not provided.",
+ default=None,
+)
+@click.option("--novalidation", is_flag=True, help="Don't validate the provided document.")
+@click.option(
+ "--graph",
+ is_flag=True,
+ default=False,
+ help="Generate a relationship graph from the input file. "
+ "The generated graph is saved to the file specified with --outfile. "
+ "Note: You need to install the optional dependencies 'networkx' and 'pygraphviz' for this feature.",
+)
+def main(infile: str, outfile: str, version: str, novalidation: bool, graph: bool):
+ """
+ CLI-tool for validating SPDX documents and converting between RDF, TAG-VALUE, JSON, YAML and XML formats.
+ Formats are determined by the file endings.
+ To use, run: 'pyspdxtools --infile --outfile '
+ """
+ try:
+ document: Document = parse_file(infile)
+
+ if not novalidation:
+ if not version:
+ version = document.creation_info.spdx_version
+
+ if version not in ["SPDX-2.2", "SPDX-2.3"]:
+ logging.error(f"This tool only supports SPDX versions SPDX-2.2 and SPDX-2.3, but got: {version}")
+ sys.exit(1)
+
+ validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version)
+ if validation_messages:
+ log_string = "\n".join(
+ ["The document is invalid. The following issues have been found:"]
+ + [message.validation_message for message in validation_messages]
+ )
+ logging.error(log_string)
+ sys.exit(1)
+ else:
+ logging.info("The document is valid.")
+
+ if outfile == "-":
+ tagvalue_writer.write_document(document, sys.stdout)
+
+ elif graph:
+ try:
+ export_graph_from_document(document, outfile)
+ except ImportError:
+ logging.error(
+ "To be able to draw a relationship graph of the parsed document "
+ "you need to install 'networkx' and 'pygraphviz'. Run 'pip install \".[graph_generation]\"'."
+ )
+ sys.exit(1)
+
+ elif outfile:
+ write_file(document, outfile, validate=False)
+
+ except NotImplementedError as err:
+ logging.error(
+ err.args[0]
+ + "\nPlease note that this project is currently undergoing a major refactoring and therefore missing "
+ "a few features which will be added in time (refer to https://github.com/spdx/tools-python/issues "
+ "for insights into the current status).\n"
+ "In the meantime, please use the current PyPI release version."
+ )
+ sys.exit(1)
+
+ except SPDXParsingError as err:
+ log_string = "\n".join(
+ ["There have been issues while parsing the provided document:"]
+ + [message for message in err.get_messages()]
+ )
+ logging.error(log_string)
+ sys.exit(1)
+
+ except JSONDecodeError as err:
+ logging.error(f"Invalid JSON provided: {err.args[0]}")
+ sys.exit(1)
+
+ except ScannerError as err:
+ logging.error("Invalid YAML provided: " + "\n".join([str(arg) for arg in err.args]))
+ sys.exit(1)
+
+ except ExpatError as err:
+ logging.error(f"Invalid XML provided: {err.args[0]}")
+ sys.exit(1)
+
+ except SAXParseException as err:
+ logging.error(f"Invalid RDF-XML provided: {str(err)}")
+ sys.exit(1)
+
+ except FileNotFoundError as err:
+ logging.error(f"{err.strerror}: {err.filename}")
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/spdx_tools/spdx/constants.py b/src/spdx_tools/spdx/constants.py
new file mode 100644
index 000000000..167f98951
--- /dev/null
+++ b/src/spdx_tools/spdx/constants.py
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+DOCUMENT_SPDX_ID = "SPDXRef-DOCUMENT"
diff --git a/src/spdx_tools/spdx/datetime_conversions.py b/src/spdx_tools/spdx/datetime_conversions.py
new file mode 100644
index 000000000..cce624d57
--- /dev/null
+++ b/src/spdx_tools/spdx/datetime_conversions.py
@@ -0,0 +1,26 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime, timezone
+
+
+def datetime_from_str(date_str: str) -> datetime:
+ if not isinstance(date_str, str):
+ raise TypeError(f"Could not convert str to datetime, invalid type: {type(date_str).__name__}")
+
+ date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") # raises ValueError if format does not match
+ return date
+
+
+def datetime_to_iso_string(date: datetime) -> str:
+ """
+ Return an ISO-8601 representation of a datetime object.
+ """
+ if date.tzinfo is not None:
+ date = date.astimezone(timezone.utc) # Convert aware datetimes to UTC
+ date = date.replace(tzinfo=None) # Convert to naive datetime
+
+ if date.microsecond != 0:
+ date = date.replace(microsecond=0) # SPDX does not support microseconds
+
+ return date.isoformat() + "Z"
diff --git a/src/spdx_tools/spdx/document_utils.py b/src/spdx_tools/spdx/document_utils.py
new file mode 100644
index 000000000..c0c0f5b27
--- /dev/null
+++ b/src/spdx_tools/spdx/document_utils.py
@@ -0,0 +1,57 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from copy import deepcopy
+
+from beartype.typing import Any, Dict, List, Union
+
+from spdx_tools.spdx.model import Document, File, Package, Snippet
+
+
+def get_contained_spdx_element_ids(document: Document) -> List[str]:
+ element_ids = [file.spdx_id for file in document.files]
+ element_ids.extend([package.spdx_id for package in document.packages])
+ element_ids.extend([snippet.spdx_id for snippet in document.snippets])
+ return element_ids
+
+
+def get_element_from_spdx_id(document: Document, spdx_id: str) -> Union[Package, File, Snippet, None]:
+ contained_spdx_elements: Dict[str, Union[Package, File, Snippet]] = get_contained_spdx_elements(document)
+ if spdx_id not in contained_spdx_elements:
+ return None
+ return contained_spdx_elements[spdx_id]
+
+
+def get_contained_spdx_elements(document: Document) -> Dict[str, Union[Package, File, Snippet]]:
+ contained_spdx_elements = {package.spdx_id: package for package in document.packages}
+ contained_spdx_elements.update({file.spdx_id: file for file in document.files})
+ contained_spdx_elements.update({snippet.spdx_id: snippet for snippet in document.snippets})
+
+ return contained_spdx_elements
+
+
+def create_document_without_duplicates(document: Document) -> Document:
+ document_without_duplicates = deepcopy(document)
+ for elements in [
+ [document_without_duplicates.creation_info],
+ document_without_duplicates.files,
+ document_without_duplicates.packages,
+ document_without_duplicates.snippets,
+ document_without_duplicates.extracted_licensing_info,
+ ]:
+ for element in elements:
+ for key, value in element.__dict__.items():
+ if isinstance(value, list):
+ value_without_duplicates = create_list_without_duplicates(value)
+ setattr(element, key, value_without_duplicates)
+
+ return document_without_duplicates
+
+
+def create_list_without_duplicates(list_with_potential_duplicates: List[Any]) -> List[Any]:
+ list_without_duplicates = []
+ for element in list_with_potential_duplicates:
+ if element not in list_without_duplicates:
+ list_without_duplicates.append(element)
+
+ return list_without_duplicates
diff --git a/src/spdx_tools/spdx/formats.py b/src/spdx_tools/spdx/formats.py
new file mode 100644
index 000000000..56f5b1663
--- /dev/null
+++ b/src/spdx_tools/spdx/formats.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+from spdx_tools.spdx.parser.error import SPDXParsingError
+
+
+class FileFormat(Enum):
+ JSON = auto()
+ YAML = auto()
+ XML = auto()
+ TAG_VALUE = auto()
+ RDF_XML = auto()
+
+
+def file_name_to_format(file_name: str) -> FileFormat:
+ if file_name.endswith(".rdf") or file_name.endswith(".rdf.xml"):
+ return FileFormat.RDF_XML
+ elif file_name.endswith(".tag") or file_name.endswith(".spdx"):
+ return FileFormat.TAG_VALUE
+ elif file_name.endswith(".json"):
+ return FileFormat.JSON
+ elif file_name.endswith(".xml"):
+ return FileFormat.XML
+ elif file_name.endswith(".yaml") or file_name.endswith(".yml"):
+ return FileFormat.YAML
+ else:
+ raise SPDXParsingError(["Unsupported SPDX file type: " + str(file_name)])
diff --git a/src/spdx_tools/spdx/graph_generation.py b/src/spdx_tools/spdx/graph_generation.py
new file mode 100644
index 000000000..da4345d50
--- /dev/null
+++ b/src/spdx_tools/spdx/graph_generation.py
@@ -0,0 +1,73 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict, List, Union
+
+from spdx_tools.spdx.model import File, Package, Snippet
+
+try:
+ from networkx import DiGraph
+except ImportError:
+ DiGraph = None
+from spdx_tools.spdx.document_utils import get_contained_spdx_elements
+from spdx_tools.spdx.model import Document, Relationship
+
+
+def export_graph_from_document(document: Document, file_name: str) -> None:
+ from networkx.drawing import nx_agraph
+
+ graph = generate_relationship_graph_from_spdx(document)
+ _color_nodes(graph)
+ attributes_graph = nx_agraph.to_agraph(graph) # convert to a pygraphviz graph
+ attributes_graph.draw(file_name, prog="dot")
+
+
+def generate_relationship_graph_from_spdx(document: Document) -> DiGraph:
+ from networkx import DiGraph
+
+ graph = DiGraph()
+ graph.add_node(document.creation_info.spdx_id, element=document.creation_info)
+
+ contained_elements: Dict[str, Union[Package, File, Snippet]] = get_contained_spdx_elements(document)
+ contained_element_nodes = [(spdx_id, {"element": element}) for spdx_id, element in contained_elements.items()]
+ graph.add_nodes_from(contained_element_nodes)
+
+ relationships_by_spdx_id: Dict[str, List[Relationship]] = dict()
+ for relationship in document.relationships:
+ relationships_by_spdx_id.setdefault(relationship.spdx_element_id, []).append(relationship)
+
+ for spdx_id, relationships in relationships_by_spdx_id.items():
+ if spdx_id not in graph.nodes():
+ # this will add any external spdx_id to the graph where we have no further information about the element,
+ # to indicate that this node represents an element we add the attribute "element"
+ graph.add_node(spdx_id, element=None)
+ for relationship in relationships:
+ relationship_node_key = relationship.spdx_element_id + "_" + relationship.relationship_type.name
+ graph.add_node(relationship_node_key, comment=relationship.comment)
+ graph.add_edge(relationship.spdx_element_id, relationship_node_key)
+ # if the related spdx element is SpdxNone or SpdxNoAssertion we need a type conversion
+ related_spdx_element_id = str(relationship.related_spdx_element_id)
+
+ if related_spdx_element_id not in graph.nodes():
+ # this will add any external spdx_id to the graph where we have no further information about
+ # the element, to indicate that this node represents an element we add the attribute "element"
+ graph.add_node(
+ related_spdx_element_id,
+ element=None,
+ )
+ graph.add_edge(relationship_node_key, related_spdx_element_id)
+
+ return graph
+
+
+def _color_nodes(graph: DiGraph) -> None:
+ for node in graph.nodes():
+ if "_" in node:
+ # nodes representing a RelationshipType are concatenated with the spdx_element_id,
+ # to only see the RelationshipType when rendering the graph to a picture we add
+ # a label to these nodes
+ graph.add_node(node, color="lightgreen", label=node.split("_", 1)[-1])
+ elif node == "SPDXRef-DOCUMENT":
+ graph.add_node(node, color="indianred2")
+ else:
+ graph.add_node(node, color="lightskyblue")
diff --git a/src/spdx_tools/spdx/jsonschema/__init__.py b/src/spdx_tools/spdx/jsonschema/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/jsonschema/annotation_converter.py b/src/spdx_tools/spdx/jsonschema/annotation_converter.py
new file mode 100644
index 000000000..16645a216
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/annotation_converter.py
@@ -0,0 +1,30 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.jsonschema.annotation_properties import AnnotationProperty
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.model import Annotation, Document
+
+
+class AnnotationConverter(TypedConverter[Annotation]):
+ def _get_property_value(
+ self, annotation: Annotation, annotation_property: AnnotationProperty, document: Document = None
+ ) -> Any:
+ if annotation_property == AnnotationProperty.ANNOTATION_DATE:
+ return datetime_to_iso_string(annotation.annotation_date)
+ elif annotation_property == AnnotationProperty.ANNOTATION_TYPE:
+ return annotation.annotation_type.name
+ elif annotation_property == AnnotationProperty.ANNOTATOR:
+ return annotation.annotator.to_serialized_string()
+ elif annotation_property == AnnotationProperty.COMMENT:
+ return annotation.annotation_comment
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return AnnotationProperty
+
+ def get_data_model_type(self) -> Type[Annotation]:
+ return Annotation
diff --git a/src/spdx_tools/spdx/jsonschema/annotation_properties.py b/src/spdx_tools/spdx/jsonschema/annotation_properties.py
new file mode 100644
index 000000000..e8226f945
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/annotation_properties.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class AnnotationProperty(JsonProperty):
+ ANNOTATION_DATE = auto()
+ ANNOTATION_TYPE = auto()
+ ANNOTATOR = auto()
+ COMMENT = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/checksum_converter.py b/src/spdx_tools/spdx/jsonschema/checksum_converter.py
new file mode 100644
index 000000000..9ffb39b11
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/checksum_converter.py
@@ -0,0 +1,32 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Type
+
+from spdx_tools.spdx.jsonschema.checksum_properties import ChecksumProperty
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.model import Checksum, ChecksumAlgorithm, Document
+
+
+class ChecksumConverter(TypedConverter[Checksum]):
+ def get_data_model_type(self) -> Type[Checksum]:
+ return Checksum
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return ChecksumProperty
+
+ def _get_property_value(
+ self, checksum: Checksum, checksum_property: ChecksumProperty, _document: Document = None
+ ) -> str:
+ if checksum_property == ChecksumProperty.ALGORITHM:
+ return algorithm_to_json_string(checksum.algorithm)
+ elif checksum_property == ChecksumProperty.CHECKSUM_VALUE:
+ return checksum.value
+
+
+def algorithm_to_json_string(algorithm: ChecksumAlgorithm) -> str:
+ name_with_dash: str = algorithm.name.replace("_", "-")
+ if "BLAKE2B" in name_with_dash:
+ return name_with_dash.replace("BLAKE2B", "BLAKE2b")
+ return name_with_dash
diff --git a/src/spdx_tools/spdx/jsonschema/checksum_properties.py b/src/spdx_tools/spdx/jsonschema/checksum_properties.py
new file mode 100644
index 000000000..c9e2f70c6
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/checksum_properties.py
@@ -0,0 +1,11 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class ChecksumProperty(JsonProperty):
+ ALGORITHM = auto()
+ CHECKSUM_VALUE = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/converter.py b/src/spdx_tools/spdx/jsonschema/converter.py
new file mode 100644
index 000000000..55629f2ae
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/converter.py
@@ -0,0 +1,68 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import ABC, abstractmethod
+
+from beartype.typing import Any, Dict, Generic, Type, TypeVar
+
+from spdx_tools.spdx.casing_tools import snake_case_to_camel_case
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.model import Document
+
+MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented"
+
+T = TypeVar("T")
+
+
+class TypedConverter(ABC, Generic[T]):
+ """
+ Base class for all converters between an instance of the tools-python data model and the corresponding dictionary
+ representation, following the json schema. The generic type T is the type according to the data model.
+ Each converter has several methods:
+ - get_json_type and get_data_model_type: return the data model type and the corresponding JsonProperty subclass.
+ These methods are abstract in the base class and need to be implemented in subclasses.
+ - json_property_name: converts an enum value of a JsonProperty subclass to the corresponding property name in the
+ json schema. The default implementation simply converts from snake case to camel case. Can be overridden in case
+ of exceptions like "SPDXID".
+ - convert: converts an instance of type T (one of the data model types) to a dictionary representation. In some
+ cases, the full document is required (see below). The logic should be generic for all types.
+ - requires_full_document: indicates whether the full document is required for conversion. Returns False by
+ default, can be overridden as needed for specific types.
+ - _get_property_value: Retrieves the value of a specific json property from the data model instance. In some
+ cases, the full document is required.
+ """
+
+ @abstractmethod
+ def _get_property_value(self, instance: T, json_property: JsonProperty, document: Document = None) -> Any:
+ raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
+
+ @abstractmethod
+ def get_json_type(self) -> Type[JsonProperty]:
+ raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
+
+ @abstractmethod
+ def get_data_model_type(self) -> Type[T]:
+ raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
+
+ def json_property_name(self, json_property: JsonProperty) -> str:
+ return snake_case_to_camel_case(json_property.name)
+
+ def requires_full_document(self) -> bool:
+ return False
+
+ def convert(self, instance: T, document: Document = None) -> Dict:
+ if not isinstance(instance, self.get_data_model_type()):
+ raise TypeError(
+ f"Converter of type {self.__class__} can only convert objects of type "
+ f"{self.get_data_model_type()}. Received {type(instance)} instead."
+ )
+ if self.requires_full_document() and not document:
+ raise ValueError(f"Converter of type {self.__class__} requires the full document")
+
+ result = {}
+ for property_name in self.get_json_type():
+ property_value = self._get_property_value(instance, property_name, document)
+ if property_value is None:
+ continue
+ result[self.json_property_name(property_name)] = property_value
+ return result
diff --git a/src/spdx_tools/spdx/jsonschema/creation_info_converter.py b/src/spdx_tools/spdx/jsonschema/creation_info_converter.py
new file mode 100644
index 000000000..0a2ef875c
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/creation_info_converter.py
@@ -0,0 +1,31 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.creation_info_properties import CreationInfoProperty
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.optional_utils import apply_if_present
+from spdx_tools.spdx.model import CreationInfo, Document
+
+
+class CreationInfoConverter(TypedConverter[CreationInfo]):
+ def get_data_model_type(self) -> Type[CreationInfo]:
+ return CreationInfo
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return CreationInfoProperty
+
+ def _get_property_value(
+ self, creation_info: CreationInfo, creation_info_property: CreationInfoProperty, _document: Document = None
+ ) -> Any:
+ if creation_info_property == CreationInfoProperty.CREATED:
+ return datetime_to_iso_string(creation_info.created)
+ elif creation_info_property == CreationInfoProperty.CREATORS:
+ return [creator.to_serialized_string() for creator in creation_info.creators] or None
+ elif creation_info_property == CreationInfoProperty.LICENSE_LIST_VERSION:
+ return apply_if_present(str, creation_info.license_list_version)
+ elif creation_info_property == CreationInfoProperty.COMMENT:
+ return creation_info.creator_comment
diff --git a/src/spdx_tools/spdx/jsonschema/creation_info_properties.py b/src/spdx_tools/spdx/jsonschema/creation_info_properties.py
new file mode 100644
index 000000000..8385b646c
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/creation_info_properties.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class CreationInfoProperty(JsonProperty):
+ CREATED = auto()
+ CREATORS = auto()
+ LICENSE_LIST_VERSION = auto()
+ COMMENT = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/document_converter.py b/src/spdx_tools/spdx/jsonschema/document_converter.py
new file mode 100644
index 000000000..a3c2e3699
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/document_converter.py
@@ -0,0 +1,95 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.document_utils import get_contained_spdx_element_ids
+from spdx_tools.spdx.jsonschema.annotation_converter import AnnotationConverter
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.creation_info_converter import CreationInfoConverter
+from spdx_tools.spdx.jsonschema.document_properties import DocumentProperty
+from spdx_tools.spdx.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter
+from spdx_tools.spdx.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter
+from spdx_tools.spdx.jsonschema.file_converter import FileConverter
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.package_converter import PackageConverter
+from spdx_tools.spdx.jsonschema.relationship_converter import RelationshipConverter
+from spdx_tools.spdx.jsonschema.snippet_converter import SnippetConverter
+from spdx_tools.spdx.model import Document
+
+
+class DocumentConverter(TypedConverter[Document]):
+ creation_info_converter: CreationInfoConverter
+ external_document_ref_converter: ExternalDocumentRefConverter
+ package_converter: PackageConverter
+ file_converter: FileConverter
+ snippet_converter: SnippetConverter
+ annotation_converter: AnnotationConverter
+ relationship_converter: RelationshipConverter
+ extracted_licensing_info_converter: ExtractedLicensingInfoConverter
+
+ def __init__(self):
+ self.external_document_ref_converter = ExternalDocumentRefConverter()
+ self.creation_info_converter = CreationInfoConverter()
+ self.package_converter = PackageConverter()
+ self.file_converter = FileConverter()
+ self.snippet_converter = SnippetConverter()
+ self.annotation_converter = AnnotationConverter()
+ self.relationship_converter = RelationshipConverter()
+ self.extracted_licensing_info_converter = ExtractedLicensingInfoConverter()
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return DocumentProperty
+
+ def get_data_model_type(self) -> Type[Document]:
+ return Document
+
+ def json_property_name(self, document_property: DocumentProperty) -> str:
+ if document_property == DocumentProperty.SPDX_ID:
+ return "SPDXID"
+ return super().json_property_name(document_property)
+
+ def _get_property_value(
+ self, document: Document, document_property: DocumentProperty, _document: Document = None
+ ) -> Any:
+ if document_property == DocumentProperty.SPDX_ID:
+ return document.creation_info.spdx_id
+ elif document_property == DocumentProperty.ANNOTATIONS:
+ # annotations referencing files, packages or snippets will be added to those elements directly
+ element_ids = get_contained_spdx_element_ids(document)
+ document_annotations = filter(
+ lambda annotation: annotation.spdx_id not in element_ids, document.annotations
+ )
+ return [self.annotation_converter.convert(annotation) for annotation in document_annotations] or None
+ elif document_property == DocumentProperty.COMMENT:
+ return document.creation_info.document_comment
+ elif document_property == DocumentProperty.CREATION_INFO:
+ return self.creation_info_converter.convert(document.creation_info)
+ elif document_property == DocumentProperty.DATA_LICENSE:
+ return document.creation_info.data_license
+ elif document_property == DocumentProperty.EXTERNAL_DOCUMENT_REFS:
+ return [
+ self.external_document_ref_converter.convert(external_document_ref)
+ for external_document_ref in document.creation_info.external_document_refs
+ ] or None
+ elif document_property == DocumentProperty.HAS_EXTRACTED_LICENSING_INFOS:
+ return [
+ self.extracted_licensing_info_converter.convert(licensing_info)
+ for licensing_info in document.extracted_licensing_info
+ ] or None
+ elif document_property == DocumentProperty.NAME:
+ return document.creation_info.name
+ elif document_property == DocumentProperty.SPDX_VERSION:
+ return document.creation_info.spdx_version
+ elif document_property == DocumentProperty.DOCUMENT_NAMESPACE:
+ return document.creation_info.document_namespace
+ elif document_property == DocumentProperty.PACKAGES:
+ return [self.package_converter.convert(package, document) for package in document.packages] or None
+ elif document_property == DocumentProperty.FILES:
+ return [self.file_converter.convert(file, document) for file in document.files] or None
+ elif document_property == DocumentProperty.SNIPPETS:
+ return [self.snippet_converter.convert(snippet, document) for snippet in document.snippets] or None
+ elif document_property == DocumentProperty.RELATIONSHIPS:
+ return [
+ self.relationship_converter.convert(relationship) for relationship in document.relationships
+ ] or None
diff --git a/src/spdx_tools/spdx/jsonschema/document_properties.py b/src/spdx_tools/spdx/jsonschema/document_properties.py
new file mode 100644
index 000000000..9b56fa775
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/document_properties.py
@@ -0,0 +1,23 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class DocumentProperty(JsonProperty):
+ SPDX_ID = auto()
+ ANNOTATIONS = auto()
+ COMMENT = auto()
+ CREATION_INFO = auto()
+ DATA_LICENSE = auto()
+ EXTERNAL_DOCUMENT_REFS = auto()
+ HAS_EXTRACTED_LICENSING_INFOS = auto()
+ NAME = auto()
+ SPDX_VERSION = auto()
+ DOCUMENT_NAMESPACE = auto()
+ PACKAGES = auto()
+ FILES = auto()
+ SNIPPETS = auto()
+ RELATIONSHIPS = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/external_document_ref_converter.py b/src/spdx_tools/spdx/jsonschema/external_document_ref_converter.py
new file mode 100644
index 000000000..f25bf56e3
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/external_document_ref_converter.py
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.jsonschema.checksum_converter import ChecksumConverter
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.model import Document, ExternalDocumentRef
+
+
+class ExternalDocumentRefConverter(TypedConverter[ExternalDocumentRef]):
+ checksum_converter: ChecksumConverter
+
+ def __init__(self):
+ self.checksum_converter = ChecksumConverter()
+
+ def _get_property_value(
+ self,
+ external_document_ref: ExternalDocumentRef,
+ external_document_ref_property: ExternalDocumentRefProperty,
+ _document: Document = None,
+ ) -> Any:
+ if external_document_ref_property == ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID:
+ return external_document_ref.document_ref_id
+ elif external_document_ref_property == ExternalDocumentRefProperty.SPDX_DOCUMENT:
+ return external_document_ref.document_uri
+ elif external_document_ref_property == ExternalDocumentRefProperty.CHECKSUM:
+ return self.checksum_converter.convert(external_document_ref.checksum)
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return ExternalDocumentRefProperty
+
+ def get_data_model_type(self) -> Type[ExternalDocumentRef]:
+ return ExternalDocumentRef
diff --git a/src/spdx_tools/spdx/jsonschema/external_document_ref_properties.py b/src/spdx_tools/spdx/jsonschema/external_document_ref_properties.py
new file mode 100644
index 000000000..822275179
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/external_document_ref_properties.py
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class ExternalDocumentRefProperty(JsonProperty):
+ EXTERNAL_DOCUMENT_ID = auto()
+ SPDX_DOCUMENT = auto()
+ CHECKSUM = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/external_package_ref_converter.py b/src/spdx_tools/spdx/jsonschema/external_package_ref_converter.py
new file mode 100644
index 000000000..732673c36
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/external_package_ref_converter.py
@@ -0,0 +1,32 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.external_package_ref_properties import ExternalPackageRefProperty
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.model import Document, ExternalPackageRef
+
+
+class ExternalPackageRefConverter(TypedConverter[ExternalPackageRef]):
+ def _get_property_value(
+ self,
+ external_ref: ExternalPackageRef,
+ external_ref_property: ExternalPackageRefProperty,
+ document: Document = None,
+ ) -> Any:
+ if external_ref_property == ExternalPackageRefProperty.COMMENT:
+ return external_ref.comment
+ elif external_ref_property == ExternalPackageRefProperty.REFERENCE_CATEGORY:
+ return external_ref.category.name
+ elif external_ref_property == ExternalPackageRefProperty.REFERENCE_LOCATOR:
+ return external_ref.locator
+ elif external_ref_property == ExternalPackageRefProperty.REFERENCE_TYPE:
+ return external_ref.reference_type
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return ExternalPackageRefProperty
+
+ def get_data_model_type(self) -> Type[ExternalPackageRef]:
+ return ExternalPackageRef
diff --git a/src/spdx_tools/spdx/jsonschema/external_package_ref_properties.py b/src/spdx_tools/spdx/jsonschema/external_package_ref_properties.py
new file mode 100644
index 000000000..a9d249788
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/external_package_ref_properties.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class ExternalPackageRefProperty(JsonProperty):
+ COMMENT = auto()
+ REFERENCE_CATEGORY = auto()
+ REFERENCE_LOCATOR = auto()
+ REFERENCE_TYPE = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/extracted_licensing_info_converter.py b/src/spdx_tools/spdx/jsonschema/extracted_licensing_info_converter.py
new file mode 100644
index 000000000..9bf0a8530
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/extracted_licensing_info_converter.py
@@ -0,0 +1,35 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.optional_utils import apply_if_present
+from spdx_tools.spdx.model import Document, ExtractedLicensingInfo
+
+
+class ExtractedLicensingInfoConverter(TypedConverter[ExtractedLicensingInfo]):
+ def _get_property_value(
+ self,
+ extracted_licensing_info: ExtractedLicensingInfo,
+ extracted_licensing_info_property: ExtractedLicensingInfoProperty,
+ document: Document = None,
+ ) -> Any:
+ if extracted_licensing_info_property == ExtractedLicensingInfoProperty.COMMENT:
+ return extracted_licensing_info.comment
+ elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.EXTRACTED_TEXT:
+ return extracted_licensing_info.extracted_text
+ elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.LICENSE_ID:
+ return extracted_licensing_info.license_id
+ elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.NAME:
+ return apply_if_present(str, extracted_licensing_info.license_name)
+ elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.SEE_ALSOS:
+ return extracted_licensing_info.cross_references or None
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return ExtractedLicensingInfoProperty
+
+ def get_data_model_type(self) -> Type[ExtractedLicensingInfo]:
+ return ExtractedLicensingInfo
diff --git a/src/spdx_tools/spdx/jsonschema/extracted_licensing_info_properties.py b/src/spdx_tools/spdx/jsonschema/extracted_licensing_info_properties.py
new file mode 100644
index 000000000..b692c6ec3
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/extracted_licensing_info_properties.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class ExtractedLicensingInfoProperty(JsonProperty):
+ COMMENT = auto()
+ EXTRACTED_TEXT = auto()
+ LICENSE_ID = auto()
+ NAME = auto()
+ SEE_ALSOS = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/file_converter.py b/src/spdx_tools/spdx/jsonschema/file_converter.py
new file mode 100644
index 000000000..7c8ae4ad1
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/file_converter.py
@@ -0,0 +1,70 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.jsonschema.annotation_converter import AnnotationConverter
+from spdx_tools.spdx.jsonschema.checksum_converter import ChecksumConverter
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.file_properties import FileProperty
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.optional_utils import apply_if_present
+from spdx_tools.spdx.model import Document, File
+
+
+class FileConverter(TypedConverter[File]):
+ annotation_converter: AnnotationConverter
+ checksum_converter: ChecksumConverter
+
+ def __init__(self):
+ self.annotation_converter = AnnotationConverter()
+ self.checksum_converter = ChecksumConverter()
+
+ def json_property_name(self, file_property: FileProperty) -> str:
+ if file_property == FileProperty.SPDX_ID:
+ return "SPDXID"
+ return super().json_property_name(file_property)
+
+ def _get_property_value(self, file: Any, file_property: FileProperty, document: Document = None) -> Any:
+ if file_property == FileProperty.SPDX_ID:
+ return file.spdx_id
+ elif file_property == FileProperty.ANNOTATIONS:
+ file_annotations = filter(lambda annotation: annotation.spdx_id == file.spdx_id, document.annotations)
+ return [self.annotation_converter.convert(annotation) for annotation in file_annotations] or None
+ elif file_property == FileProperty.ARTIFACT_OFS:
+ # Deprecated property, automatically converted during parsing
+ pass
+ elif file_property == FileProperty.ATTRIBUTION_TEXTS:
+ return file.attribution_texts or None
+ elif file_property == FileProperty.CHECKSUMS:
+ return [self.checksum_converter.convert(checksum) for checksum in file.checksums] or None
+ elif file_property == FileProperty.COMMENT:
+ return file.comment
+ elif file_property == FileProperty.COPYRIGHT_TEXT:
+ return apply_if_present(str, file.copyright_text)
+ elif file_property == FileProperty.FILE_CONTRIBUTORS:
+ return file.contributors or None
+ elif file_property == FileProperty.FILE_DEPENDENCIES:
+ # Deprecated property, automatically converted during parsing
+ pass
+ elif file_property == FileProperty.FILE_NAME:
+ return file.name
+ elif file_property == FileProperty.FILE_TYPES:
+ return [file_type.name for file_type in file.file_types] or None
+ elif file_property == FileProperty.LICENSE_COMMENTS:
+ return file.license_comment
+ elif file_property == FileProperty.LICENSE_CONCLUDED:
+ return apply_if_present(str, file.license_concluded)
+ elif file_property == FileProperty.LICENSE_INFO_IN_FILES:
+ return [str(license_expression) for license_expression in file.license_info_in_file] or None
+ elif file_property == FileProperty.NOTICE_TEXT:
+ return file.notice
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return FileProperty
+
+ def get_data_model_type(self) -> Type[File]:
+ return File
+
+ def requires_full_document(self) -> bool:
+ return True
diff --git a/src/spdx_tools/spdx/jsonschema/file_properties.py b/src/spdx_tools/spdx/jsonschema/file_properties.py
new file mode 100644
index 000000000..c1d584eb5
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/file_properties.py
@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class FileProperty(JsonProperty):
+ SPDX_ID = auto()
+ ANNOTATIONS = auto()
+ ARTIFACT_OFS = auto()
+ ATTRIBUTION_TEXTS = auto()
+ CHECKSUMS = auto()
+ COMMENT = auto()
+ COPYRIGHT_TEXT = auto()
+ FILE_CONTRIBUTORS = auto()
+ FILE_DEPENDENCIES = auto()
+ FILE_NAME = auto()
+ FILE_TYPES = auto()
+ LICENSE_COMMENTS = auto()
+ LICENSE_CONCLUDED = auto()
+ LICENSE_INFO_IN_FILES = auto()
+ NOTICE_TEXT = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/json_property.py b/src/spdx_tools/spdx/jsonschema/json_property.py
new file mode 100644
index 000000000..9bdbe1265
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/json_property.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum
+
+
+class JsonProperty(Enum):
+ """
+ Parent class for all json property classes. Not meant to be instantiated directly, only to have a common parent
+ type that can be used in type hints.
+ In general, all the child enums list the properties of the corresponding objects from the json schema.
+ """
+
+ pass
diff --git a/src/spdx_tools/spdx/jsonschema/optional_utils.py b/src/spdx_tools/spdx/jsonschema/optional_utils.py
new file mode 100644
index 000000000..3d3f9e223
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/optional_utils.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Callable, Optional, TypeVar
+
+T = TypeVar("T")
+S = TypeVar("S")
+
+
+def apply_if_present(function: Callable[[T], S], optional_value: Optional[T]) -> Optional[S]:
+ """
+ Apply the passed function to the optional value if it is not None. Else returns None.
+ """
+ return function(optional_value) if optional_value is not None else None
diff --git a/src/spdx_tools/spdx/jsonschema/package_converter.py b/src/spdx_tools/spdx/jsonschema/package_converter.py
new file mode 100644
index 000000000..074fc7a78
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/package_converter.py
@@ -0,0 +1,112 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.jsonschema.annotation_converter import AnnotationConverter
+from spdx_tools.spdx.jsonschema.checksum_converter import ChecksumConverter
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.external_package_ref_converter import ExternalPackageRefConverter
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.optional_utils import apply_if_present
+from spdx_tools.spdx.jsonschema.package_properties import PackageProperty
+from spdx_tools.spdx.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter
+from spdx_tools.spdx.model import Actor, Document, Package
+
+
+class PackageConverter(TypedConverter[Package]):
+ annotation_converter: AnnotationConverter
+ checksum_converter: ChecksumConverter
+ external_package_ref_converter: ExternalPackageRefConverter
+ package_verification_code_converter: PackageVerificationCodeConverter
+
+ def __init__(self):
+ self.annotation_converter = AnnotationConverter()
+ self.checksum_converter = ChecksumConverter()
+ self.external_package_ref_converter = ExternalPackageRefConverter()
+ self.package_verification_code_converter = PackageVerificationCodeConverter()
+
+ def json_property_name(self, package_property: PackageProperty) -> str:
+ if package_property == PackageProperty.SPDX_ID:
+ return "SPDXID"
+ return super().json_property_name(package_property)
+
+ def _get_property_value(
+ self, package: Package, package_property: PackageProperty, document: Document = None
+ ) -> Any:
+ if package_property == PackageProperty.SPDX_ID:
+ return package.spdx_id
+ elif package_property == PackageProperty.ANNOTATIONS:
+ package_annotations = filter(
+ lambda annotation: annotation.spdx_id == package.spdx_id, document.annotations
+ )
+ return [
+ self.annotation_converter.convert(annotation, document) for annotation in package_annotations
+ ] or None
+ elif package_property == PackageProperty.ATTRIBUTION_TEXTS:
+ return package.attribution_texts or None
+ elif package_property == PackageProperty.BUILT_DATE:
+ return apply_if_present(datetime_to_iso_string, package.built_date)
+ elif package_property == PackageProperty.CHECKSUMS:
+ return [self.checksum_converter.convert(checksum, document) for checksum in package.checksums] or None
+ elif package_property == PackageProperty.COMMENT:
+ return package.comment
+ elif package_property == PackageProperty.COPYRIGHT_TEXT:
+ return apply_if_present(str, package.copyright_text)
+ elif package_property == PackageProperty.DESCRIPTION:
+ return package.description
+ elif package_property == PackageProperty.DOWNLOAD_LOCATION:
+ return str(package.download_location)
+ elif package_property == PackageProperty.EXTERNAL_REFS:
+ return [
+ self.external_package_ref_converter.convert(external_ref)
+ for external_ref in package.external_references
+ ] or None
+ elif package_property == PackageProperty.FILES_ANALYZED:
+ return package.files_analyzed
+ elif package_property == PackageProperty.HOMEPAGE:
+ return apply_if_present(str, package.homepage)
+ elif package_property == PackageProperty.LICENSE_COMMENTS:
+ return package.license_comment
+ elif package_property == PackageProperty.LICENSE_CONCLUDED:
+ return apply_if_present(str, package.license_concluded)
+ elif package_property == PackageProperty.LICENSE_DECLARED:
+ return apply_if_present(str, package.license_declared)
+ elif package_property == PackageProperty.LICENSE_INFO_FROM_FILES:
+ return [str(license_expression) for license_expression in package.license_info_from_files] or None
+ elif package_property == PackageProperty.NAME:
+ return package.name
+ elif package_property == PackageProperty.ORIGINATOR:
+ if isinstance(package.originator, Actor):
+ return package.originator.to_serialized_string()
+ return apply_if_present(str, package.originator)
+ elif package_property == PackageProperty.PACKAGE_FILE_NAME:
+ return package.file_name
+ elif package_property == PackageProperty.PACKAGE_VERIFICATION_CODE:
+ return apply_if_present(self.package_verification_code_converter.convert, package.verification_code)
+ elif package_property == PackageProperty.PRIMARY_PACKAGE_PURPOSE:
+ return package.primary_package_purpose.name if package.primary_package_purpose is not None else None
+ elif package_property == PackageProperty.RELEASE_DATE:
+ return apply_if_present(datetime_to_iso_string, package.release_date)
+ elif package_property == PackageProperty.SOURCE_INFO:
+ return package.source_info
+ elif package_property == PackageProperty.SUMMARY:
+ return package.summary
+ elif package_property == PackageProperty.SUPPLIER:
+ if isinstance(package.supplier, Actor):
+ return package.supplier.to_serialized_string()
+ return apply_if_present(str, package.supplier)
+ elif package_property == PackageProperty.VALID_UNTIL_DATE:
+ return apply_if_present(datetime_to_iso_string, package.valid_until_date)
+ elif package_property == PackageProperty.VERSION_INFO:
+ return package.version
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return PackageProperty
+
+ def get_data_model_type(self) -> Type[Package]:
+ return Package
+
+ def requires_full_document(self) -> bool:
+ return True
diff --git a/src/spdx_tools/spdx/jsonschema/package_properties.py b/src/spdx_tools/spdx/jsonschema/package_properties.py
new file mode 100644
index 000000000..903fb1968
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/package_properties.py
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class PackageProperty(JsonProperty):
+ SPDX_ID = auto()
+ ANNOTATIONS = auto()
+ ATTRIBUTION_TEXTS = auto()
+ BUILT_DATE = auto()
+ CHECKSUMS = auto()
+ COMMENT = auto()
+ COPYRIGHT_TEXT = auto()
+ DESCRIPTION = auto()
+ DOWNLOAD_LOCATION = auto()
+ EXTERNAL_REFS = auto()
+ FILES_ANALYZED = auto()
+ HOMEPAGE = auto()
+ LICENSE_COMMENTS = auto()
+ LICENSE_CONCLUDED = auto()
+ LICENSE_DECLARED = auto()
+ LICENSE_INFO_FROM_FILES = auto()
+ NAME = auto()
+ ORIGINATOR = auto()
+ PACKAGE_FILE_NAME = auto()
+ PACKAGE_VERIFICATION_CODE = auto()
+ PRIMARY_PACKAGE_PURPOSE = auto()
+ RELEASE_DATE = auto()
+ SOURCE_INFO = auto()
+ SUMMARY = auto()
+ SUPPLIER = auto()
+ VALID_UNTIL_DATE = auto()
+ VERSION_INFO = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/package_verification_code_converter.py b/src/spdx_tools/spdx/jsonschema/package_verification_code_converter.py
new file mode 100644
index 000000000..b98396272
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/package_verification_code_converter.py
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty
+from spdx_tools.spdx.model import Document, PackageVerificationCode
+
+
+class PackageVerificationCodeConverter(TypedConverter[PackageVerificationCode]):
+ def _get_property_value(
+ self,
+ verification_code: PackageVerificationCode,
+ verification_code_property: PackageVerificationCodeProperty,
+ document: Document = None,
+ ) -> Any:
+ if verification_code_property == PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES:
+ return verification_code.excluded_files or None
+ elif verification_code_property == PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE:
+ return verification_code.value
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return PackageVerificationCodeProperty
+
+ def get_data_model_type(self) -> Type[PackageVerificationCode]:
+ return PackageVerificationCode
diff --git a/src/spdx_tools/spdx/jsonschema/package_verification_code_properties.py b/src/spdx_tools/spdx/jsonschema/package_verification_code_properties.py
new file mode 100644
index 000000000..f73410385
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/package_verification_code_properties.py
@@ -0,0 +1,11 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class PackageVerificationCodeProperty(JsonProperty):
+ PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES = auto()
+ PACKAGE_VERIFICATION_CODE_VALUE = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/relationship_converter.py b/src/spdx_tools/spdx/jsonschema/relationship_converter.py
new file mode 100644
index 000000000..78442d25b
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/relationship_converter.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Type
+
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.relationship_properties import RelationshipProperty
+from spdx_tools.spdx.model import Document, Relationship
+
+
+class RelationshipConverter(TypedConverter[Relationship]):
+ def _get_property_value(
+ self, relationship: Relationship, relationship_property: RelationshipProperty, document: Document = None
+ ) -> Any:
+ if relationship_property == RelationshipProperty.SPDX_ELEMENT_ID:
+ return relationship.spdx_element_id
+ elif relationship_property == RelationshipProperty.COMMENT:
+ return relationship.comment
+ elif relationship_property == RelationshipProperty.RELATED_SPDX_ELEMENT:
+ return str(relationship.related_spdx_element_id)
+ elif relationship_property == RelationshipProperty.RELATIONSHIP_TYPE:
+ return relationship.relationship_type.name
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return RelationshipProperty
+
+ def get_data_model_type(self) -> Type[Relationship]:
+ return Relationship
diff --git a/src/spdx_tools/spdx/jsonschema/relationship_properties.py b/src/spdx_tools/spdx/jsonschema/relationship_properties.py
new file mode 100644
index 000000000..73c1259e9
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/relationship_properties.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class RelationshipProperty(JsonProperty):
+ SPDX_ELEMENT_ID = auto()
+ COMMENT = auto()
+ RELATED_SPDX_ELEMENT = auto()
+ RELATIONSHIP_TYPE = auto()
diff --git a/src/spdx_tools/spdx/jsonschema/snippet_converter.py b/src/spdx_tools/spdx/jsonschema/snippet_converter.py
new file mode 100644
index 000000000..a4e75c4fa
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/snippet_converter.py
@@ -0,0 +1,83 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Dict, Tuple, Type
+
+from spdx_tools.spdx.jsonschema.annotation_converter import AnnotationConverter
+from spdx_tools.spdx.jsonschema.converter import TypedConverter
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+from spdx_tools.spdx.jsonschema.optional_utils import apply_if_present
+from spdx_tools.spdx.jsonschema.snippet_properties import SnippetProperty
+from spdx_tools.spdx.model import Document, Snippet
+
+
+class SnippetConverter(TypedConverter[Snippet]):
+ annotation_converter: AnnotationConverter
+
+ def __init__(self):
+ self.annotation_converter = AnnotationConverter()
+
+ def json_property_name(self, snippet_property: SnippetProperty) -> str:
+ if snippet_property == SnippetProperty.SPDX_ID:
+ return "SPDXID"
+ return super().json_property_name(snippet_property)
+
+ def _get_property_value(
+ self, snippet: Snippet, snippet_property: SnippetProperty, document: Document = None
+ ) -> Any:
+ if snippet_property == SnippetProperty.SPDX_ID:
+ return snippet.spdx_id
+ elif snippet_property == SnippetProperty.ANNOTATIONS:
+ snippet_annotations = filter(
+ lambda annotation: annotation.spdx_id == snippet.spdx_id, document.annotations
+ )
+ return [self.annotation_converter.convert(annotation) for annotation in snippet_annotations] or None
+ elif snippet_property == SnippetProperty.ATTRIBUTION_TEXTS:
+ return snippet.attribution_texts or None
+ elif snippet_property == SnippetProperty.COMMENT:
+ return snippet.comment
+ elif snippet_property == SnippetProperty.COPYRIGHT_TEXT:
+ return apply_if_present(str, snippet.copyright_text)
+ elif snippet_property == SnippetProperty.LICENSE_COMMENTS:
+ return snippet.license_comment
+ elif snippet_property == SnippetProperty.LICENSE_CONCLUDED:
+ return apply_if_present(str, snippet.license_concluded)
+ elif snippet_property == SnippetProperty.LICENSE_INFO_IN_SNIPPETS:
+ return [str(license_expression) for license_expression in snippet.license_info_in_snippet] or None
+ elif snippet_property == SnippetProperty.NAME:
+ return snippet.name
+ elif snippet_property == SnippetProperty.RANGES:
+ ranges = [convert_byte_range_to_dict(snippet.byte_range, snippet.file_spdx_id)]
+ if snippet.line_range:
+ ranges.append(convert_line_range_to_dict(snippet.line_range, snippet.file_spdx_id))
+ return ranges
+ elif snippet_property == SnippetProperty.SNIPPET_FROM_FILE:
+ return snippet.file_spdx_id
+
+ def get_json_type(self) -> Type[JsonProperty]:
+ return SnippetProperty
+
+ def get_data_model_type(self) -> Type[Snippet]:
+ return Snippet
+
+ def requires_full_document(self) -> bool:
+ return True
+
+
+def convert_line_range_to_dict(line_range: Tuple[int, int], file_id: str) -> Dict:
+ return _convert_range_to_dict(line_range, file_id, "lineNumber")
+
+
+def convert_byte_range_to_dict(byte_range: Tuple[int, int], file_id: str) -> Dict:
+ return _convert_range_to_dict(byte_range, file_id, "offset")
+
+
+def _convert_range_to_dict(int_range: Tuple[int, int], file_id: str, pointer_property: str) -> Dict:
+ return {
+ "startPointer": _pointer(file_id, int_range[0], pointer_property),
+ "endPointer": _pointer(file_id, int_range[1], pointer_property),
+ }
+
+
+def _pointer(reference: str, target: int, pointer_property: str) -> Dict:
+ return {"reference": reference, pointer_property: target}
diff --git a/src/spdx_tools/spdx/jsonschema/snippet_properties.py b/src/spdx_tools/spdx/jsonschema/snippet_properties.py
new file mode 100644
index 000000000..772a58401
--- /dev/null
+++ b/src/spdx_tools/spdx/jsonschema/snippet_properties.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import auto
+
+from spdx_tools.spdx.jsonschema.json_property import JsonProperty
+
+
+class SnippetProperty(JsonProperty):
+ SPDX_ID = auto()
+ ANNOTATIONS = auto()
+ ATTRIBUTION_TEXTS = auto()
+ COMMENT = auto()
+ COPYRIGHT_TEXT = auto()
+ LICENSE_COMMENTS = auto()
+ LICENSE_CONCLUDED = auto()
+ LICENSE_INFO_IN_SNIPPETS = auto()
+ NAME = auto()
+ RANGES = auto()
+ SNIPPET_FROM_FILE = auto()
diff --git a/src/spdx_tools/spdx/model/__init__.py b/src/spdx_tools/spdx/model/__init__.py
new file mode 100644
index 000000000..e27a116d0
--- /dev/null
+++ b/src/spdx_tools/spdx/model/__init__.py
@@ -0,0 +1,22 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
+from spdx_tools.spdx.model.spdx_none import SpdxNone
+from spdx_tools.spdx.model.version import Version
+from spdx_tools.spdx.model.actor import Actor, ActorType
+from spdx_tools.spdx.model.annotation import Annotation, AnnotationType
+from spdx_tools.spdx.model.checksum import Checksum, ChecksumAlgorithm
+from spdx_tools.spdx.model.external_document_ref import ExternalDocumentRef
+from spdx_tools.spdx.model.extracted_licensing_info import ExtractedLicensingInfo
+from spdx_tools.spdx.model.file import File, FileType
+from spdx_tools.spdx.model.package import (
+ ExternalPackageRef,
+ ExternalPackageRefCategory,
+ Package,
+ PackagePurpose,
+ PackageVerificationCode,
+)
+from spdx_tools.spdx.model.relationship import Relationship, RelationshipType
+from spdx_tools.spdx.model.snippet import Snippet
+from spdx_tools.spdx.model.document import CreationInfo, Document
diff --git a/src/spdx_tools/spdx/model/actor.py b/src/spdx_tools/spdx/model/actor.py
new file mode 100644
index 000000000..9aeb9e059
--- /dev/null
+++ b/src/spdx_tools/spdx/model/actor.py
@@ -0,0 +1,35 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+from beartype.typing import Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+
+
+class ActorType(Enum):
+ PERSON = auto()
+ ORGANIZATION = auto()
+ TOOL = auto()
+
+
+@dataclass_with_properties
+class Actor:
+ actor_type: ActorType
+ name: str
+ email: Optional[str] = None
+
+ def __init__(self, actor_type: ActorType, name: str, email: Optional[str] = None):
+ check_types_and_set_values(self, locals())
+
+ def to_serialized_string(self) -> str:
+ """
+ All serialization formats use the same representation of an actor, so this method is included in the data model
+ """
+ optional_email = f" ({self.email})" if self.email else ""
+ return "".join([f"{self.actor_type.name.title()}:", f" {self.name}", optional_email])
+
+ def __str__(self):
+ return self.to_serialized_string()
diff --git a/src/spdx_tools/spdx/model/annotation.py b/src/spdx_tools/spdx/model/annotation.py
new file mode 100644
index 000000000..6ada0607b
--- /dev/null
+++ b/src/spdx_tools/spdx/model/annotation.py
@@ -0,0 +1,33 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+from enum import Enum, auto
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import Actor
+
+
+class AnnotationType(Enum):
+ REVIEW = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class Annotation:
+ spdx_id: str
+ annotation_type: AnnotationType
+ annotator: Actor
+ annotation_date: datetime
+ annotation_comment: str
+
+ def __init__(
+ self,
+ spdx_id: str,
+ annotation_type: AnnotationType,
+ annotator: Actor,
+ annotation_date: datetime,
+ annotation_comment: str,
+ ):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/checksum.py b/src/spdx_tools/spdx/model/checksum.py
new file mode 100644
index 000000000..7889e1163
--- /dev/null
+++ b/src/spdx_tools/spdx/model/checksum.py
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+
+
+class ChecksumAlgorithm(Enum):
+ SHA1 = auto()
+ SHA224 = auto()
+ SHA256 = auto()
+ SHA384 = auto()
+ SHA512 = auto()
+ SHA3_256 = auto()
+ SHA3_384 = auto()
+ SHA3_512 = auto()
+ BLAKE2B_256 = auto()
+ BLAKE2B_384 = auto()
+ BLAKE2B_512 = auto()
+ BLAKE3 = auto()
+ MD2 = auto()
+ MD4 = auto()
+ MD5 = auto()
+ MD6 = auto()
+ ADLER32 = auto()
+
+
+@dataclass_with_properties
+class Checksum:
+ algorithm: ChecksumAlgorithm
+ value: str
+
+ def __init__(self, algorithm: ChecksumAlgorithm, value: str):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/document.py b/src/spdx_tools/spdx/model/document.py
new file mode 100644
index 000000000..980c59ca5
--- /dev/null
+++ b/src/spdx_tools/spdx/model/document.py
@@ -0,0 +1,83 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import (
+ Actor,
+ Annotation,
+ ExternalDocumentRef,
+ ExtractedLicensingInfo,
+ File,
+ Package,
+ Relationship,
+ Snippet,
+ Version,
+)
+
+
+@dataclass_with_properties
+class CreationInfo:
+ spdx_version: str
+ spdx_id: str
+ name: str
+ document_namespace: str
+ creators: List[Actor]
+ created: datetime
+ creator_comment: Optional[str] = None
+ data_license: str = "CC0-1.0"
+ external_document_refs: List[ExternalDocumentRef] = field(default_factory=list)
+ license_list_version: Optional[Version] = None
+ document_comment: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_version: str,
+ spdx_id: str,
+ name: str,
+ document_namespace: str,
+ creators: List[Actor],
+ created: datetime,
+ creator_comment: Optional[str] = None,
+ data_license: str = "CC0-1.0",
+ external_document_refs: List[ExternalDocumentRef] = None,
+ license_list_version: Optional[Version] = None,
+ document_comment: Optional[str] = None,
+ ):
+ external_document_refs = [] if external_document_refs is None else external_document_refs
+ check_types_and_set_values(self, locals())
+
+
+@dataclass_with_properties
+class Document:
+ creation_info: CreationInfo
+
+ packages: List[Package] = field(default_factory=list)
+ files: List[File] = field(default_factory=list)
+ snippets: List[Snippet] = field(default_factory=list)
+ annotations: List[Annotation] = field(default_factory=list)
+ relationships: List[Relationship] = field(default_factory=list)
+ extracted_licensing_info: List[ExtractedLicensingInfo] = field(default_factory=list)
+
+ def __init__(
+ self,
+ creation_info: CreationInfo,
+ packages: List[Package] = None,
+ files: List[File] = None,
+ snippets: List[Snippet] = None,
+ annotations: List[Annotation] = None,
+ relationships: List[Relationship] = None,
+ extracted_licensing_info: List[ExtractedLicensingInfo] = None,
+ ):
+ packages = [] if packages is None else packages
+ files = [] if files is None else files
+ snippets = [] if snippets is None else snippets
+ annotations = [] if annotations is None else annotations
+ relationships = [] if relationships is None else relationships
+ extracted_licensing_info = [] if extracted_licensing_info is None else extracted_licensing_info
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/external_document_ref.py b/src/spdx_tools/spdx/model/external_document_ref.py
new file mode 100644
index 000000000..fbbaf4847
--- /dev/null
+++ b/src/spdx_tools/spdx/model/external_document_ref.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import Checksum
+
+
+@dataclass_with_properties
+class ExternalDocumentRef:
+ document_ref_id: str # of the form "DocumentRef-[idstring]"
+ document_uri: str
+ checksum: Checksum
+
+ def __init__(self, document_ref_id: str, document_uri: str, checksum: Checksum):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/extracted_licensing_info.py b/src/spdx_tools/spdx/model/extracted_licensing_info.py
new file mode 100644
index 000000000..e401dd7b6
--- /dev/null
+++ b/src/spdx_tools/spdx/model/extracted_licensing_info.py
@@ -0,0 +1,30 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+
+from beartype.typing import List, Optional, Union
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import SpdxNoAssertion
+
+
+@dataclass_with_properties
+class ExtractedLicensingInfo:
+ license_id: Optional[str] = None
+ extracted_text: Optional[str] = None
+ license_name: Optional[Union[str, SpdxNoAssertion]] = None
+ cross_references: List[str] = field(default_factory=list)
+ comment: Optional[str] = None
+
+ def __init__(
+ self,
+ license_id: Optional[str] = None,
+ extracted_text: Optional[str] = None,
+ license_name: Optional[Union[str, SpdxNoAssertion]] = None,
+ cross_references: List[str] = None,
+ comment: Optional[str] = None,
+ ):
+ cross_references = [] if cross_references is None else cross_references
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/file.py b/src/spdx_tools/spdx/model/file.py
new file mode 100644
index 000000000..27aae4ca3
--- /dev/null
+++ b/src/spdx_tools/spdx/model/file.py
@@ -0,0 +1,68 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from enum import Enum, auto
+
+from beartype.typing import List, Optional, Union
+from license_expression import LicenseExpression
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import Checksum, SpdxNoAssertion, SpdxNone
+
+
+class FileType(Enum):
+ SOURCE = auto()
+ BINARY = auto()
+ ARCHIVE = auto()
+ APPLICATION = auto()
+ AUDIO = auto()
+ IMAGE = auto()
+ TEXT = auto()
+ VIDEO = auto()
+ DOCUMENTATION = auto()
+ SPDX = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class File:
+ name: str
+ spdx_id: str
+ checksums: List[Checksum]
+ file_types: List[FileType] = field(default_factory=list)
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
+ license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = field(default_factory=list)
+ license_comment: Optional[str] = None
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None
+ comment: Optional[str] = None
+ notice: Optional[str] = None
+ contributors: List[str] = field(default_factory=list)
+ attribution_texts: List[str] = field(default_factory=list)
+
+ # Deprecated properties that should be replaced during parsing:
+ # - file dependencies: replace by a DEPENDENCY_OF relationship (or one of the more precise versions)
+ # - artifact of (3 properties): replace by an external package reference and a GENERATED_FROM relationship
+ # between the file and this package
+
+ def __init__(
+ self,
+ name: str,
+ spdx_id: str,
+ checksums: List[Checksum],
+ file_types: List[FileType] = None,
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+ license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+ license_comment: Optional[str] = None,
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None,
+ comment: str = None,
+ notice: Optional[str] = None,
+ contributors: List[str] = None,
+ attribution_texts: List[str] = None,
+ ):
+ file_types = [] if file_types is None else file_types
+ license_info_in_file = [] if license_info_in_file is None else license_info_in_file
+ contributors = [] if contributors is None else contributors
+ attribution_texts = [] if attribution_texts is None else attribution_texts
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/package.py b/src/spdx_tools/spdx/model/package.py
new file mode 100644
index 000000000..1cf89e607
--- /dev/null
+++ b/src/spdx_tools/spdx/model/package.py
@@ -0,0 +1,134 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import Dict, List, Optional, Union
+from license_expression import LicenseExpression
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import Actor, Checksum, SpdxNoAssertion, SpdxNone
+
+
+class PackagePurpose(Enum):
+ APPLICATION = auto()
+ FRAMEWORK = auto()
+ LIBRARY = auto()
+ CONTAINER = auto()
+ OPERATING_SYSTEM = auto()
+ DEVICE = auto()
+ FIRMWARE = auto()
+ SOURCE = auto()
+ ARCHIVE = auto()
+ FILE = auto()
+ INSTALL = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class PackageVerificationCode:
+ value: str
+ excluded_files: List[str] = field(default_factory=list)
+
+ def __init__(self, value: str, excluded_files: List[str] = None):
+ excluded_files = [] if excluded_files is None else excluded_files
+ check_types_and_set_values(self, locals())
+
+
+class ExternalPackageRefCategory(Enum):
+ SECURITY = auto()
+ PACKAGE_MANAGER = auto()
+ PERSISTENT_ID = auto()
+ OTHER = auto()
+
+
+CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES: Dict[ExternalPackageRefCategory, List[str]] = {
+ ExternalPackageRefCategory.SECURITY: ["cpe22Type", "cpe23Type", "advisory", "fix", "url", "swid"],
+ ExternalPackageRefCategory.PACKAGE_MANAGER: ["maven-central", "npm", "nuget", "bower", "purl"],
+ ExternalPackageRefCategory.PERSISTENT_ID: ["swh", "gitoid"],
+ ExternalPackageRefCategory.OTHER: [],
+}
+
+
+@dataclass_with_properties
+class ExternalPackageRef:
+ category: ExternalPackageRefCategory
+ # In theory, once could refine the typing,
+ # see https://spdx.github.io/spdx-spec/v2.3/external-repository-identifiers/. But it's probably not worth the
+ # effort.
+ reference_type: str
+ locator: str
+ comment: Optional[str] = None
+
+ def __init__(
+ self, category: ExternalPackageRefCategory, reference_type: str, locator: str, comment: Optional[str] = None
+ ):
+ check_types_and_set_values(self, locals())
+
+
+@dataclass_with_properties
+class Package:
+ spdx_id: str
+ name: str
+ download_location: Union[str, SpdxNoAssertion, SpdxNone]
+ version: Optional[str] = None
+ file_name: Optional[str] = None
+ supplier: Optional[Union[Actor, SpdxNoAssertion]] = None
+ originator: Optional[Union[Actor, SpdxNoAssertion]] = None
+ files_analyzed: bool = True
+ verification_code: Optional[PackageVerificationCode] = None
+ checksums: List[Checksum] = field(default_factory=list)
+ homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None
+ source_info: Optional[str] = None
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
+ license_info_from_files: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = field(default_factory=list)
+ license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
+ license_comment: Optional[str] = None
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None
+ summary: Optional[str] = None
+ description: Optional[str] = None
+ comment: Optional[str] = None
+ external_references: List[ExternalPackageRef] = field(default_factory=list)
+ attribution_texts: List[str] = field(default_factory=list)
+ primary_package_purpose: Optional[PackagePurpose] = None
+ release_date: Optional[datetime] = None
+ built_date: Optional[datetime] = None
+ valid_until_date: Optional[datetime] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ name: str,
+ download_location: Union[str, SpdxNoAssertion, SpdxNone],
+ version: Optional[str] = None,
+ file_name: Optional[str] = None,
+ supplier: Optional[Union[Actor, SpdxNoAssertion]] = None,
+ originator: Optional[Union[Actor, SpdxNoAssertion]] = None,
+ files_analyzed: bool = True,
+ verification_code: Optional[PackageVerificationCode] = None,
+ checksums: List[Checksum] = None,
+ homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None,
+ source_info: Optional[str] = None,
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+ license_info_from_files: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+ license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+ license_comment: Optional[str] = None,
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ external_references: List[ExternalPackageRef] = None,
+ attribution_texts: List[str] = None,
+ primary_package_purpose: Optional[PackagePurpose] = None,
+ release_date: Optional[datetime] = None,
+ built_date: Optional[datetime] = None,
+ valid_until_date: Optional[datetime] = None,
+ ):
+ checksums = [] if checksums is None else checksums
+ license_info_from_files = [] if license_info_from_files is None else license_info_from_files
+ external_references = [] if external_references is None else external_references
+ attribution_texts = [] if attribution_texts is None else attribution_texts
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/relationship.py b/src/spdx_tools/spdx/model/relationship.py
new file mode 100644
index 000000000..02b1326a9
--- /dev/null
+++ b/src/spdx_tools/spdx/model/relationship.py
@@ -0,0 +1,75 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+from beartype.typing import Optional, Union
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import SpdxNoAssertion, SpdxNone
+
+
+class RelationshipType(Enum):
+ AMENDS = auto()
+ ANCESTOR_OF = auto()
+ BUILD_DEPENDENCY_OF = auto()
+ BUILD_TOOL_OF = auto()
+ CONTAINED_BY = auto()
+ CONTAINS = auto()
+ COPY_OF = auto()
+ DATA_FILE_OF = auto()
+ DEPENDENCY_MANIFEST_OF = auto()
+ DEPENDENCY_OF = auto()
+ DEPENDS_ON = auto()
+ DESCENDANT_OF = auto()
+ DESCRIBED_BY = auto()
+ DESCRIBES = auto()
+ DEV_DEPENDENCY_OF = auto()
+ DEV_TOOL_OF = auto()
+ DISTRIBUTION_ARTIFACT = auto()
+ DOCUMENTATION_OF = auto()
+ DYNAMIC_LINK = auto()
+ EXAMPLE_OF = auto()
+ EXPANDED_FROM_ARCHIVE = auto()
+ FILE_ADDED = auto()
+ FILE_DELETED = auto()
+ FILE_MODIFIED = auto()
+ GENERATED_FROM = auto()
+ GENERATES = auto()
+ HAS_PREREQUISITE = auto()
+ METAFILE_OF = auto()
+ OPTIONAL_COMPONENT_OF = auto()
+ OPTIONAL_DEPENDENCY_OF = auto()
+ OTHER = auto()
+ PACKAGE_OF = auto()
+ PATCH_APPLIED = auto()
+ PATCH_FOR = auto()
+ PREREQUISITE_FOR = auto()
+ PROVIDED_DEPENDENCY_OF = auto()
+ REQUIREMENT_DESCRIPTION_FOR = auto()
+ RUNTIME_DEPENDENCY_OF = auto()
+ SPECIFICATION_FOR = auto()
+ STATIC_LINK = auto()
+ TEST_CASE_OF = auto()
+ TEST_DEPENDENCY_OF = auto()
+ TEST_OF = auto()
+ TEST_TOOL_OF = auto()
+ VARIANT_OF = auto()
+
+
+@dataclass_with_properties
+class Relationship:
+ spdx_element_id: str
+ relationship_type: RelationshipType
+ related_spdx_element_id: Union[str, SpdxNone, SpdxNoAssertion]
+ comment: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_element_id: str,
+ relationship_type: RelationshipType,
+ related_spdx_element_id: Union[str, SpdxNone, SpdxNoAssertion],
+ comment: Optional[str] = None,
+ ):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/relationship_filters.py b/src/spdx_tools/spdx/model/relationship_filters.py
new file mode 100644
index 000000000..8858fb038
--- /dev/null
+++ b/src/spdx_tools/spdx/model/relationship_filters.py
@@ -0,0 +1,50 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+from spdx_tools.spdx.model import Document, Package, Relationship, RelationshipType
+
+
+def find_package_contains_file_relationships(document: Document, package: Package) -> List[Relationship]:
+ file_ids_in_document = [file.spdx_id for file in document.files]
+ package_contains_relationships = filter_by_type_and_origin(
+ document.relationships, RelationshipType.CONTAINS, package.spdx_id
+ )
+ return [
+ relationship
+ for relationship in package_contains_relationships
+ if relationship.related_spdx_element_id in file_ids_in_document
+ ]
+
+
+def find_file_contained_by_package_relationships(document: Document, package: Package) -> List[Relationship]:
+ file_ids_in_document = [file.spdx_id for file in document.files]
+ contained_by_package_relationships = filter_by_type_and_target(
+ document.relationships, RelationshipType.CONTAINED_BY, package.spdx_id
+ )
+ return [
+ relationship
+ for relationship in contained_by_package_relationships
+ if relationship.spdx_element_id in file_ids_in_document
+ ]
+
+
+def filter_by_type_and_target(
+ relationships: List[Relationship], relationship_type: RelationshipType, target_id: str
+) -> List[Relationship]:
+ return [
+ relationship
+ for relationship in relationships
+ if relationship.relationship_type == relationship_type and relationship.related_spdx_element_id == target_id
+ ]
+
+
+def filter_by_type_and_origin(
+ relationships: List[Relationship], relationship_type: RelationshipType, origin_id: str
+) -> List[Relationship]:
+ return [
+ relationship
+ for relationship in relationships
+ if relationship.relationship_type == relationship_type and relationship.spdx_element_id == origin_id
+ ]
diff --git a/src/spdx_tools/spdx/model/snippet.py b/src/spdx_tools/spdx/model/snippet.py
new file mode 100644
index 000000000..5a1669f4a
--- /dev/null
+++ b/src/spdx_tools/spdx/model/snippet.py
@@ -0,0 +1,44 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+
+from beartype.typing import List, Optional, Tuple, Union
+from license_expression import LicenseExpression
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx.model import SpdxNoAssertion, SpdxNone
+
+
+@dataclass_with_properties
+class Snippet:
+ spdx_id: str
+ file_spdx_id: str
+ byte_range: Tuple[int, int]
+ line_range: Optional[Tuple[int, int]] = None
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
+ license_info_in_snippet: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
+ license_comment: Optional[str] = None
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None
+ comment: Optional[str] = None
+ name: Optional[str] = None
+ attribution_texts: List[str] = field(default_factory=list)
+
+ def __init__(
+ self,
+ spdx_id: str,
+ file_spdx_id: str,
+ byte_range: Tuple[int, int],
+ line_range: Optional[Tuple[int, int]] = None,
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+ license_info_in_snippet: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+ license_comment: Optional[str] = None,
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None,
+ comment: Optional[str] = None,
+ name: Optional[str] = None,
+ attribution_texts: List[str] = None,
+ ):
+ attribution_texts = [] if attribution_texts is None else attribution_texts
+ license_info_in_snippet = [] if license_info_in_snippet is None else license_info_in_snippet
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx/model/spdx_no_assertion.py b/src/spdx_tools/spdx/model/spdx_no_assertion.py
new file mode 100644
index 000000000..1255dba35
--- /dev/null
+++ b/src/spdx_tools/spdx/model/spdx_no_assertion.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+SPDX_NO_ASSERTION_STRING = "NOASSERTION"
+
+
+class SpdxNoAssertion:
+ """
+ Represents the SPDX NOASSERTION value.
+ """
+
+ def __str__(self):
+ return SPDX_NO_ASSERTION_STRING
+
+ def __repr__(self):
+ return SPDX_NO_ASSERTION_STRING
+
+ def __eq__(self, other):
+ return isinstance(other, SpdxNoAssertion)
diff --git a/src/spdx_tools/spdx/model/spdx_none.py b/src/spdx_tools/spdx/model/spdx_none.py
new file mode 100644
index 000000000..25f63099f
--- /dev/null
+++ b/src/spdx_tools/spdx/model/spdx_none.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+SPDX_NONE_STRING = "NONE"
+
+
+class SpdxNone:
+ """
+ Represents the SPDX NONE value.
+ """
+
+ def __str__(self):
+ return SPDX_NONE_STRING
+
+ def __repr__(self):
+ return SPDX_NONE_STRING
+
+ def __eq__(self, other):
+ return isinstance(other, SpdxNone)
diff --git a/src/spdx_tools/spdx/model/version.py b/src/spdx_tools/spdx/model/version.py
new file mode 100644
index 000000000..c3874246b
--- /dev/null
+++ b/src/spdx_tools/spdx/model/version.py
@@ -0,0 +1,40 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+
+import re
+from re import Pattern
+
+
+class Version:
+ VERSION_REGEX: Pattern = re.compile(r"^(\d+)\.(\d+)$")
+
+ major: int
+ minor: int
+
+ @classmethod
+ def is_valid_version_string(cls, value: str) -> bool:
+ return cls.VERSION_REGEX.match(value) is not None
+
+ # No type hint for Python reasons.
+ # See https://stackoverflow.com/questions/33533148/how-do-i-type-hint-a-method-with-the-type-of-the-enclosing-class
+ @classmethod
+ def from_string(cls, value: str):
+ if not Version.is_valid_version_string(value):
+ raise ValueError(f"{value} is not a valid version string")
+
+ match = cls.VERSION_REGEX.match(value)
+ return cls(int(match.group(1)), int(match.group(2)))
+
+ def __init__(self, major: int, minor: int):
+ self.major = major
+ self.minor = minor
+
+ def __str__(self):
+ return f"{self.major}.{self.minor}"
+
+ def __eq__(self, other):
+ if not isinstance(other, Version):
+ return False
+ return self.major == other.major and self.minor == other.minor
diff --git a/src/spdx_tools/spdx/parser/__init__.py b/src/spdx_tools/spdx/parser/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/parser/actor_parser.py b/src/spdx_tools/spdx/parser/actor_parser.py
new file mode 100644
index 000000000..14cc4ffb0
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/actor_parser.py
@@ -0,0 +1,53 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import re
+
+from beartype.typing import Match, Pattern
+
+from spdx_tools.spdx.model import Actor, ActorType
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.parsing_functions import construct_or_raise_parsing_error
+
+
+class ActorParser:
+ @staticmethod
+ def parse_actor(actor: str) -> Actor:
+ tool_re: Pattern = re.compile(r"^Tool:\s*(.+)", re.UNICODE)
+ person_re: Pattern = re.compile(r"^Person:\s*(?:(.*)\((.*)\)|(.*))$", re.UNICODE)
+ org_re: Pattern = re.compile(r"^Organization:\s*(?:(.*)\((.*)\)|(.*))$", re.UNICODE)
+ tool_match: Match = tool_re.match(actor)
+ person_match: Match = person_re.match(actor)
+ org_match: Match = org_re.match(actor)
+
+ if tool_match:
+ name: str = tool_match.group(1).strip()
+ if not name:
+ raise SPDXParsingError([f"No name for Tool provided: {actor}."])
+ return construct_or_raise_parsing_error(Actor, dict(actor_type=ActorType.TOOL, name=name))
+
+ if person_match:
+ actor_type = ActorType.PERSON
+ match = person_match
+ elif org_match:
+ actor_type = ActorType.ORGANIZATION
+ match = org_match
+ else:
+ raise SPDXParsingError([f"Actor {actor} doesn't match any of person, organization or tool."])
+
+ if match.group(3):
+ return construct_or_raise_parsing_error(
+ Actor, dict(actor_type=actor_type, name=match.group(3).strip(), email=None)
+ )
+ else:
+ name = match.group(1)
+ if not name:
+ raise SPDXParsingError([f"No name for Actor provided: {actor}."])
+ else:
+ name = name.strip()
+
+ email = match.group(2).strip()
+
+ return construct_or_raise_parsing_error(
+ Actor, dict(actor_type=actor_type, name=name, email=email if email else None)
+ )
diff --git a/src/spdx_tools/spdx/parser/error.py b/src/spdx_tools/spdx/parser/error.py
new file mode 100644
index 000000000..1e58c1122
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/error.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+
+class SPDXParsingError(Exception):
+ messages: List[str]
+
+ def __init__(self, messages: List[str]):
+ self.messages = messages
+
+ def get_messages(self):
+ return list(self.messages)
diff --git a/src/spdx_tools/spdx/parser/json/__init__.py b/src/spdx_tools/spdx/parser/json/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/parser/json/json_parser.py b/src/spdx_tools/spdx/parser/json/json_parser.py
new file mode 100644
index 000000000..675ed2560
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/json/json_parser.py
@@ -0,0 +1,35 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import json
+
+from beartype.typing import Any, Dict
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser
+
+# chars we don't want to see in SBOMs
+CONTROL_CHARS_MAP = {
+ 8: None, # ASCII/UTF-8: backspace
+ 12: None, # ASCII/UTF-8: formfeed
+}
+
+
+def remove_control_chars_from_value(value: Any) -> Any:
+ if isinstance(value, str):
+ return value.translate(CONTROL_CHARS_MAP)
+ elif isinstance(value, list):
+ for i in range(len(value)):
+ value[i] = remove_control_chars_from_value(value[i])
+ return value
+
+
+def remove_json_control_chars_hook(pairs: list) -> dict:
+ return {k: remove_control_chars_from_value(v) for k, v in pairs}
+
+
+def parse_from_file(file_name: str, encoding: str = "utf-8") -> Document:
+ with open(file_name, encoding=encoding) as file:
+ input_doc_as_dict: Dict = json.load(file, object_pairs_hook=remove_json_control_chars_hook)
+
+ return JsonLikeDictParser().parse(input_doc_as_dict)
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/__init__.py b/src/spdx_tools/spdx/parser/jsonlikedict/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/annotation_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/annotation_parser.py
new file mode 100644
index 000000000..38ccac3ff
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/annotation_parser.py
@@ -0,0 +1,125 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import Dict, List, Optional
+
+from spdx_tools.spdx.datetime_conversions import datetime_from_str
+from spdx_tools.spdx.model import Actor, Annotation, AnnotationType
+from spdx_tools.spdx.parser.actor_parser import ActorParser
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import (
+ append_parsed_field_or_log_error,
+ parse_field_or_log_error,
+)
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+
+
+class AnnotationParser:
+ logger: Logger
+ actor_parser: ActorParser
+
+ def __init__(self):
+ self.logger = Logger()
+ self.actor_parser = ActorParser()
+
+ def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]:
+ annotations = []
+ self.parse_annotations_from_object(annotations, [input_doc_dict])
+ reviews: List[Dict] = input_doc_dict.get("revieweds", [])
+ for review in reviews:
+ annotations = append_parsed_field_or_log_error(
+ self.logger, annotations, review, lambda x: self.parse_review(x, spdx_id=input_doc_dict.get("SPDXID"))
+ )
+ packages: List[Dict] = input_doc_dict.get("packages", [])
+ self.parse_annotations_from_object(annotations, packages)
+ files: List[Dict] = input_doc_dict.get("files", [])
+ self.parse_annotations_from_object(annotations, files)
+ snippets: List[Dict] = input_doc_dict.get("snippets", [])
+ self.parse_annotations_from_object(annotations, snippets)
+
+ raise_parsing_error_if_logger_has_messages(self.logger, "annotations")
+ return annotations
+
+ def parse_annotations_from_object(self, annotations: List[Annotation], element_list: List[Dict]):
+ for element in element_list:
+ element_spdx_id: Optional[str] = element.get("SPDXID")
+ element_annotations: List[Dict] = element.get("annotations", [])
+ annotations.extend(
+ parse_field_or_log_error(
+ self.logger,
+ element_annotations,
+ lambda y: self.parse_annotation(y, spdx_id=element_spdx_id),
+ [],
+ True,
+ )
+ )
+
+ def parse_annotation(self, annotation_dict: Dict, spdx_id: Optional[str] = None) -> Annotation:
+ logger = Logger()
+ spdx_id: Optional[str] = annotation_dict.get("SPDXID") or spdx_id
+
+ annotation_type: Optional[AnnotationType] = parse_field_or_log_error(
+ logger, annotation_dict.get("annotationType"), self.parse_annotation_type
+ )
+
+ annotator: Optional[Actor] = parse_field_or_log_error(
+ logger, annotation_dict.get("annotator"), self.actor_parser.parse_actor
+ )
+
+ annotation_date: Optional[datetime] = parse_field_or_log_error(
+ logger, annotation_dict.get("annotationDate"), datetime_from_str
+ )
+
+ annotation_comment: Optional[str] = annotation_dict.get("comment")
+ raise_parsing_error_if_logger_has_messages(logger, "Annotation")
+ annotation_dict = construct_or_raise_parsing_error(
+ Annotation,
+ dict(
+ spdx_id=spdx_id,
+ annotation_type=annotation_type,
+ annotator=annotator,
+ annotation_date=annotation_date,
+ annotation_comment=annotation_comment,
+ ),
+ )
+
+ return annotation_dict
+
+ @staticmethod
+ def parse_annotation_type(annotation_type: str) -> AnnotationType:
+ try:
+ return AnnotationType[annotation_type]
+ except KeyError:
+ raise SPDXParsingError([f"Invalid AnnotationType: {annotation_type}"])
+
+ def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation:
+ logger = Logger()
+ annotator: Optional[Actor] = parse_field_or_log_error(
+ logger, review_dict.get("reviewer"), self.actor_parser.parse_actor
+ )
+
+ annotation_date: Optional[datetime] = parse_field_or_log_error(
+ logger, review_dict.get("reviewDate"), datetime_from_str
+ )
+
+ annotation_type = AnnotationType.REVIEW
+ comment: Optional[str] = review_dict.get("comment")
+ raise_parsing_error_if_logger_has_messages(logger, "Annotation from revieweds")
+
+ annotation = construct_or_raise_parsing_error(
+ Annotation,
+ dict(
+ spdx_id=spdx_id,
+ annotation_type=annotation_type,
+ annotator=annotator,
+ annotation_date=annotation_date,
+ annotation_comment=comment,
+ ),
+ )
+ return annotation
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/checksum_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/checksum_parser.py
new file mode 100644
index 000000000..5da8ff544
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/checksum_parser.py
@@ -0,0 +1,33 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict, Optional
+
+from spdx_tools.spdx.model import Checksum, ChecksumAlgorithm
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import json_str_to_enum_name
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+
+
+class ChecksumParser:
+ logger: Logger
+
+ def __init__(self):
+ self.logger = Logger()
+
+ @staticmethod
+ def parse_checksum(checksum_dict: Dict) -> Checksum:
+ logger = Logger()
+ algorithm: str = json_str_to_enum_name(checksum_dict.get("algorithm", ""))
+ try:
+ checksum_algorithm = ChecksumAlgorithm[algorithm]
+ except KeyError:
+ logger.append(f"Invalid ChecksumAlgorithm: {algorithm}")
+ checksum_algorithm = None
+ checksum_value: Optional[str] = checksum_dict.get("checksumValue")
+ raise_parsing_error_if_logger_has_messages(logger, "Checksum")
+ checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=checksum_algorithm, value=checksum_value))
+ return checksum
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/creation_info_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/creation_info_parser.py
new file mode 100644
index 000000000..c530999f1
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/creation_info_parser.py
@@ -0,0 +1,117 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import Dict, List, Optional
+
+from spdx_tools.spdx.datetime_conversions import datetime_from_str
+from spdx_tools.spdx.model import Actor, Checksum, CreationInfo, ExternalDocumentRef, Version
+from spdx_tools.spdx.parser.actor_parser import ActorParser
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.jsonlikedict.checksum_parser import ChecksumParser
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_log_error
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+
+
+class CreationInfoParser:
+ logger: Logger
+ actor_parser: ActorParser
+ checksum_parser: ChecksumParser
+
+ def __init__(self):
+ self.logger = Logger()
+ self.actor_parser = ActorParser()
+ self.checksum_parser = ChecksumParser()
+
+ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo:
+ logger = Logger()
+ spdx_version: Optional[str] = doc_dict.get("spdxVersion")
+ spdx_id: Optional[str] = doc_dict.get("SPDXID")
+ name: Optional[str] = doc_dict.get("name")
+ document_namespace: Optional[str] = doc_dict.get("documentNamespace")
+ creation_info_dict: Optional[Dict] = doc_dict.get("creationInfo")
+
+ # There are nested required properties. If creationInfo is not set, we cannot continue parsing.
+ if creation_info_dict is None:
+ logger.append("CreationInfo does not exist.")
+ raise SPDXParsingError([f"Error while parsing document {name}: {logger.get_messages()}"])
+
+ creators: List[Actor] = parse_field_or_log_error(
+ logger, creation_info_dict.get("creators"), self.actor_parser.parse_actor, field_is_list=True
+ )
+
+ created: Optional[datetime] = parse_field_or_log_error(
+ logger, creation_info_dict.get("created"), datetime_from_str
+ )
+
+ creator_comment: Optional[str] = creation_info_dict.get("comment")
+ data_license: Optional[str] = doc_dict.get("dataLicense")
+
+ external_document_refs: List[ExternalDocumentRef] = parse_field_or_log_error(
+ logger, doc_dict.get("externalDocumentRefs"), self.parse_external_document_refs
+ )
+ license_list_version: Optional[Version] = parse_field_or_log_error(
+ logger, creation_info_dict.get("licenseListVersion"), self.parse_version
+ )
+ document_comment: Optional[str] = doc_dict.get("comment")
+ raise_parsing_error_if_logger_has_messages(logger, "Document")
+
+ creation_info = construct_or_raise_parsing_error(
+ CreationInfo,
+ dict(
+ spdx_version=spdx_version,
+ spdx_id=spdx_id,
+ name=name,
+ document_namespace=document_namespace,
+ creators=creators,
+ created=created,
+ license_list_version=license_list_version,
+ document_comment=document_comment,
+ creator_comment=creator_comment,
+ data_license=data_license,
+ external_document_refs=external_document_refs,
+ ),
+ )
+
+ return creation_info
+
+ @staticmethod
+ def parse_version(version_str: str) -> Version:
+ try:
+ return Version.from_string(version_str)
+ except ValueError as err:
+ raise SPDXParsingError([f"Error while parsing version {version_str}: {err.args[0]}"])
+
+ def parse_external_document_refs(self, external_document_ref_dicts: List[Dict]) -> List[ExternalDocumentRef]:
+ logger = Logger()
+ external_document_refs = []
+ for external_document_ref_dict in external_document_ref_dicts:
+ external_document_ref: ExternalDocumentRef = parse_field_or_log_error(
+ logger, external_document_ref_dict, self.parse_external_document_ref
+ )
+
+ external_document_refs.append(external_document_ref)
+
+ raise_parsing_error_if_logger_has_messages(logger)
+ return external_document_refs
+
+ def parse_external_document_ref(self, external_document_ref_dict: Dict) -> ExternalDocumentRef:
+ logger = Logger()
+ checksum: Optional[Checksum] = parse_field_or_log_error(
+ logger, external_document_ref_dict.get("checksum"), self.checksum_parser.parse_checksum
+ )
+
+ external_document_id: Optional[str] = external_document_ref_dict.get("externalDocumentId")
+ document_uri: Optional[str] = external_document_ref_dict.get("spdxDocument")
+ raise_parsing_error_if_logger_has_messages(logger, "ExternalDocumentRef")
+ external_document_ref: ExternalDocumentRef = construct_or_raise_parsing_error(
+ ExternalDocumentRef,
+ dict(document_ref_id=external_document_id, checksum=checksum, document_uri=document_uri),
+ )
+
+ return external_document_ref
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/dict_parsing_functions.py b/src/spdx_tools/spdx/parser/jsonlikedict/dict_parsing_functions.py
new file mode 100644
index 000000000..0733317d1
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/dict_parsing_functions.py
@@ -0,0 +1,82 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Callable, Dict, List, Optional
+
+from spdx_tools.spdx.model import SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages
+
+
+def json_str_to_enum_name(json_str: str) -> str:
+ if not isinstance(json_str, str):
+ raise SPDXParsingError([f"Type for enum must be str not {type(json_str).__name__}"])
+ return json_str.replace("-", "_").upper()
+
+
+def parse_field_or_log_error(
+ logger: Logger,
+ field: Any,
+ parsing_method: Callable = lambda x: x,
+ default: Any = None,
+ field_is_list: bool = False,
+) -> Any:
+ if not field:
+ return default
+ try:
+ if field_is_list:
+ return parse_list_of_elements(field, parsing_method)
+ else:
+ return parsing_method(field)
+ except SPDXParsingError as err:
+ logger.extend(err.get_messages())
+ except (TypeError, ValueError) as err:
+ logger.append(err.args[0])
+ return default
+
+
+def append_parsed_field_or_log_error(
+ logger: Logger, list_to_append_to: List[Any], field: Any, method_to_parse: Callable
+) -> List[Any]:
+ try:
+ parsed_element = method_to_parse(field)
+ list_to_append_to.append(parsed_element)
+ except SPDXParsingError as err:
+ logger.extend(err.get_messages())
+ except (TypeError, ValueError) as err:
+ logger.append(err.args[0])
+ return list_to_append_to
+
+
+def parse_field_or_no_assertion_or_none(field: Optional[str], method_for_field: Callable = lambda x: x) -> Any:
+ if field == SpdxNoAssertion().__str__():
+ return SpdxNoAssertion()
+ elif field == SpdxNone().__str__():
+ return SpdxNone()
+ else:
+ return method_for_field(field)
+
+
+def parse_field_or_no_assertion(field: Optional[str], method_for_field: Callable = lambda x: x) -> Any:
+ if field == SpdxNoAssertion().__str__():
+ return SpdxNoAssertion()
+ else:
+ return method_for_field(field)
+
+
+def parse_list_of_elements(list_of_elements: List[Dict], method_to_parse_element: Callable, logger=None) -> List[Any]:
+ if not logger:
+ logger = Logger()
+ parsed_elements = []
+ for element_dict in list_of_elements:
+ parsed_elements = append_parsed_field_or_log_error(
+ logger, parsed_elements, element_dict, method_to_parse_element
+ )
+ raise_parsing_error_if_logger_has_messages(logger)
+ return parsed_elements
+
+
+def delete_duplicates_from_list(list_with_potential_duplicates: List[Any]) -> List[Any]:
+ list_without_duplicates = list(dict.fromkeys(list_with_potential_duplicates))
+ return list_without_duplicates
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py
new file mode 100644
index 000000000..c0646084f
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py
@@ -0,0 +1,37 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict, List, Optional, Union
+
+from spdx_tools.spdx.model import ExtractedLicensingInfo, SpdxNoAssertion
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_no_assertion
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import construct_or_raise_parsing_error
+
+
+class ExtractedLicensingInfoParser:
+ logger: Logger
+
+ def __init__(self):
+ self.logger = Logger()
+
+ @staticmethod
+ def parse_extracted_licensing_info(extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo:
+ license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId")
+ extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText")
+ license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_no_assertion(
+ extracted_licensing_info_dict.get("name")
+ )
+ cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos", [])
+ comment: Optional[str] = extracted_licensing_info_dict.get("comment")
+ extracted_licensing_info = construct_or_raise_parsing_error(
+ ExtractedLicensingInfo,
+ dict(
+ license_id=license_id,
+ extracted_text=extracted_text,
+ comment=comment,
+ license_name=license_name,
+ cross_references=cross_references,
+ ),
+ )
+ return extracted_licensing_info
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/file_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/file_parser.py
new file mode 100644
index 000000000..77138ab40
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/file_parser.py
@@ -0,0 +1,96 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict, List, Optional, Union
+from license_expression import LicenseExpression
+
+from spdx_tools.spdx.model import Checksum, File, FileType, SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.parser.jsonlikedict.checksum_parser import ChecksumParser
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import (
+ parse_field_or_log_error,
+ parse_field_or_no_assertion_or_none,
+)
+from spdx_tools.spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+
+
+class FileParser:
+ logger: Logger
+ checksum_parser: ChecksumParser
+ license_expression_parser: LicenseExpressionParser
+
+ def __init__(self):
+ self.logger = Logger()
+ self.checksum_parser = ChecksumParser()
+ self.license_expression_parser = LicenseExpressionParser()
+
+ def parse_file(self, file_dict: Dict) -> Optional[File]:
+ logger = Logger()
+ name: Optional[str] = file_dict.get("fileName")
+ spdx_id: Optional[str] = file_dict.get("SPDXID")
+ checksums_list: List[Dict] = file_dict.get("checksums")
+ checksums: List[Checksum] = parse_field_or_log_error(
+ logger, checksums_list, self.checksum_parser.parse_checksum, field_is_list=True
+ )
+
+ attribution_texts: List[str] = file_dict.get("attributionTexts", [])
+ comment: Optional[str] = file_dict.get("comment")
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = parse_field_or_no_assertion_or_none(
+ file_dict.get("copyrightText")
+ )
+ file_contributors: List[str] = file_dict.get("fileContributors", [])
+ file_types: List[FileType] = parse_field_or_log_error(
+ logger, file_dict.get("fileTypes"), self.parse_file_types
+ )
+
+ license_comments: Optional[str] = file_dict.get("licenseComments")
+
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(
+ logger, file_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression
+ )
+
+ license_info_in_files: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(
+ logger,
+ file_dict.get("licenseInfoInFiles"),
+ self.license_expression_parser.parse_license_expression,
+ field_is_list=True,
+ )
+ notice_text: Optional[str] = file_dict.get("noticeText")
+ raise_parsing_error_if_logger_has_messages(logger, "File")
+
+ file = construct_or_raise_parsing_error(
+ File,
+ dict(
+ name=name,
+ spdx_id=spdx_id,
+ checksums=checksums,
+ attribution_texts=attribution_texts,
+ comment=comment,
+ copyright_text=copyright_text,
+ file_types=file_types,
+ contributors=file_contributors,
+ license_comment=license_comments,
+ license_concluded=license_concluded,
+ license_info_in_file=license_info_in_files,
+ notice=notice_text,
+ ),
+ )
+ return file
+
+ @staticmethod
+ def parse_file_types(file_types_list: List[str]) -> List[FileType]:
+ logger = Logger()
+ file_types = []
+ for file_type in file_types_list:
+ try:
+ file_type = FileType[file_type]
+ except KeyError:
+ logger.append(f"Invalid FileType: {file_type}")
+ continue
+ file_types.append(file_type)
+ raise_parsing_error_if_logger_has_messages(logger, "FileType")
+ return file_types
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/json_like_dict_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/json_like_dict_parser.py
new file mode 100644
index 000000000..31a41559a
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/json_like_dict_parser.py
@@ -0,0 +1,92 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.jsonlikedict.annotation_parser import AnnotationParser
+from spdx_tools.spdx.parser.jsonlikedict.creation_info_parser import CreationInfoParser
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements
+from spdx_tools.spdx.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser
+from spdx_tools.spdx.parser.jsonlikedict.file_parser import FileParser
+from spdx_tools.spdx.parser.jsonlikedict.package_parser import PackageParser
+from spdx_tools.spdx.parser.jsonlikedict.relationship_parser import RelationshipParser
+from spdx_tools.spdx.parser.jsonlikedict.snippet_parser import SnippetParser
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+
+
+class JsonLikeDictParser:
+ logger: Logger
+ creation_info_parser: CreationInfoParser
+ package_parser: PackageParser
+ file_parser: FileParser
+ snippet_parser: SnippetParser
+ extracted_licensing_info_parser: ExtractedLicensingInfoParser
+ relationship_parser: RelationshipParser
+ annotation_parser: AnnotationParser
+
+ def __init__(self):
+ self.logger = Logger()
+ self.creation_info_parser = CreationInfoParser()
+ self.package_parser = PackageParser()
+ self.file_parser = FileParser()
+ self.snippet_parser = SnippetParser()
+ self.extracted_licensing_info_parser = ExtractedLicensingInfoParser()
+ self.relationship_parser = RelationshipParser()
+ self.annotation_parser = AnnotationParser()
+
+ def parse(self, json_like_dict: Dict) -> Document:
+ fields_to_parse = [
+ ("creation_info", json_like_dict, self.creation_info_parser.parse_creation_info, False),
+ (
+ "packages",
+ json_like_dict.get("packages"),
+ lambda x: parse_list_of_elements(x, self.package_parser.parse_package, self.package_parser.logger),
+ True,
+ ),
+ (
+ "files",
+ json_like_dict.get("files"),
+ lambda x: parse_list_of_elements(x, self.file_parser.parse_file, self.file_parser.logger),
+ True,
+ ),
+ ("annotations", json_like_dict, self.annotation_parser.parse_all_annotations, True),
+ (
+ "snippets",
+ json_like_dict.get("snippets"),
+ lambda x: parse_list_of_elements(x, self.snippet_parser.parse_snippet, self.snippet_parser.logger),
+ True,
+ ),
+ ("relationships", json_like_dict, self.relationship_parser.parse_all_relationships, True),
+ (
+ "extracted_licensing_info",
+ json_like_dict.get("hasExtractedLicensingInfos"),
+ lambda x: parse_list_of_elements(
+ x,
+ self.extracted_licensing_info_parser.parse_extracted_licensing_info,
+ self.extracted_licensing_info_parser.logger,
+ ),
+ True,
+ ),
+ ]
+
+ parsed_fields = {}
+
+ for argument_name, field, parsing_method, optional in fields_to_parse:
+ if optional and not field:
+ continue
+ try:
+ parsed_fields[argument_name] = parsing_method(field)
+ except SPDXParsingError as err:
+ self.logger.extend(err.get_messages())
+
+ raise_parsing_error_if_logger_has_messages(self.logger)
+
+ document = construct_or_raise_parsing_error(Document, parsed_fields)
+
+ return document
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/license_expression_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/license_expression_parser.py
new file mode 100644
index 000000000..5a3b545f6
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/license_expression_parser.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Union
+from license_expression import ExpressionError, LicenseExpression
+
+from spdx_tools.common.spdx_licensing import spdx_licensing
+from spdx_tools.spdx.model import SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.parser.error import SPDXParsingError
+
+
+class LicenseExpressionParser:
+ @staticmethod
+ def parse_license_expression(license_expression_str: str) -> Union[LicenseExpression, SpdxNone, SpdxNoAssertion]:
+ if isinstance(license_expression_str, str):
+ if license_expression_str.upper() == "NOASSERTION":
+ return SpdxNoAssertion()
+ if license_expression_str.upper() == "NONE":
+ return SpdxNone()
+
+ try:
+ license_expression = spdx_licensing.parse(license_expression_str)
+ except ExpressionError as err:
+ err_msg = f'Error parsing LicenseExpression: "{license_expression_str}"'
+ if err.args:
+ err_msg += f": {err.args[0]}"
+ raise SPDXParsingError([err_msg])
+
+ return license_expression
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/package_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/package_parser.py
new file mode 100644
index 000000000..b395abd39
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/package_parser.py
@@ -0,0 +1,218 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import Dict, List, Optional, Union
+from license_expression import LicenseExpression
+
+from spdx_tools.spdx.datetime_conversions import datetime_from_str
+from spdx_tools.spdx.model import (
+ Actor,
+ ExternalPackageRef,
+ ExternalPackageRefCategory,
+ Package,
+ PackagePurpose,
+ PackageVerificationCode,
+ SpdxNoAssertion,
+ SpdxNone,
+)
+from spdx_tools.spdx.parser.actor_parser import ActorParser
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.jsonlikedict.checksum_parser import ChecksumParser
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import (
+ append_parsed_field_or_log_error,
+ json_str_to_enum_name,
+ parse_field_or_log_error,
+ parse_field_or_no_assertion,
+ parse_field_or_no_assertion_or_none,
+)
+from spdx_tools.spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+
+
+class PackageParser:
+ logger: Logger
+ actor_parser: ActorParser
+ checksum_parser: ChecksumParser
+ license_expression_parser: LicenseExpressionParser
+
+ def __init__(self):
+ self.actor_parser = ActorParser()
+ self.checksum_parser = ChecksumParser()
+ self.license_expression_parser = LicenseExpressionParser()
+ self.logger = Logger()
+
+ def parse_package(self, package_dict: Dict) -> Package:
+ logger = Logger()
+ name: Optional[str] = package_dict.get("name")
+ spdx_id: Optional[str] = package_dict.get("SPDXID")
+ attribution_texts: List[str] = package_dict.get("attributionTexts", [])
+
+ built_date: Optional[datetime] = parse_field_or_log_error(
+ logger, package_dict.get("builtDate"), datetime_from_str
+ )
+
+ checksums = parse_field_or_log_error(
+ logger, package_dict.get("checksums"), self.checksum_parser.parse_checksum, field_is_list=True
+ )
+ comment: Optional[str] = package_dict.get("comment")
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = parse_field_or_no_assertion_or_none(
+ package_dict.get("copyrightText")
+ )
+ description: Optional[str] = package_dict.get("description")
+ download_location: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = parse_field_or_no_assertion_or_none(
+ package_dict.get("downloadLocation")
+ )
+
+ external_refs: List[ExternalPackageRef] = parse_field_or_log_error(
+ logger, package_dict.get("externalRefs"), self.parse_external_refs
+ )
+
+ files_analyzed: Optional[Union[bool, str]] = package_dict.get("filesAnalyzed")
+
+ if files_analyzed is None: # default value is True
+ files_analyzed = True
+ elif isinstance(files_analyzed, str): # XML does not support boolean typed values
+ if files_analyzed.lower() == "true":
+ files_analyzed = True
+ elif files_analyzed.lower() == "false":
+ files_analyzed = False
+
+ homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = parse_field_or_no_assertion_or_none(
+ package_dict.get("homepage")
+ )
+ license_comments: Optional[str] = package_dict.get("licenseComments")
+ license_concluded = parse_field_or_log_error(
+ logger, package_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression
+ )
+
+ license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(
+ logger, package_dict.get("licenseDeclared"), self.license_expression_parser.parse_license_expression
+ )
+
+ license_info_from_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(
+ logger,
+ package_dict.get("licenseInfoFromFiles"),
+ self.license_expression_parser.parse_license_expression,
+ field_is_list=True,
+ )
+ originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(
+ logger,
+ package_dict.get("originator"),
+ lambda x: parse_field_or_no_assertion(x, self.actor_parser.parse_actor),
+ )
+ package_file_name: Optional[str] = package_dict.get("packageFileName")
+
+ package_verification_code: Optional[PackageVerificationCode] = parse_field_or_log_error(
+ logger, package_dict.get("packageVerificationCode"), self.parse_package_verification_code
+ )
+ primary_package_purpose: Optional[PackagePurpose] = parse_field_or_log_error(
+ logger, package_dict.get("primaryPackagePurpose"), self.parse_primary_package_purpose
+ )
+
+ release_date: Optional[datetime] = parse_field_or_log_error(
+ logger, package_dict.get("releaseDate"), datetime_from_str
+ )
+ source_info: Optional[str] = package_dict.get("sourceInfo")
+ summary: Optional[str] = package_dict.get("summary")
+ supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(
+ logger,
+ package_dict.get("supplier"),
+ lambda x: parse_field_or_no_assertion(x, self.actor_parser.parse_actor),
+ )
+ valid_until_date: Optional[datetime] = parse_field_or_log_error(
+ logger, package_dict.get("validUntilDate"), datetime_from_str
+ )
+
+ version_info: Optional[str] = package_dict.get("versionInfo")
+ raise_parsing_error_if_logger_has_messages(logger, "Package")
+
+ package = construct_or_raise_parsing_error(
+ Package,
+ dict(
+ spdx_id=spdx_id,
+ name=name,
+ download_location=download_location,
+ version=version_info,
+ file_name=package_file_name,
+ supplier=supplier,
+ originator=originator,
+ files_analyzed=files_analyzed,
+ verification_code=package_verification_code,
+ checksums=checksums,
+ homepage=homepage,
+ source_info=source_info,
+ license_concluded=license_concluded,
+ license_info_from_files=license_info_from_file,
+ license_declared=license_declared,
+ license_comment=license_comments,
+ copyright_text=copyright_text,
+ summary=summary,
+ description=description,
+ comment=comment,
+ external_references=external_refs,
+ attribution_texts=attribution_texts,
+ primary_package_purpose=primary_package_purpose,
+ release_date=release_date,
+ built_date=built_date,
+ valid_until_date=valid_until_date,
+ ),
+ )
+
+ return package
+
+ def parse_external_refs(self, external_ref_dicts: List[Dict]) -> List[ExternalPackageRef]:
+ external_refs = []
+ for external_ref_dict in external_ref_dicts:
+ external_refs = append_parsed_field_or_log_error(
+ self.logger, external_refs, external_ref_dict, self.parse_external_ref
+ )
+ return external_refs
+
+ def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef:
+ logger = Logger()
+ ref_category = parse_field_or_log_error(
+ logger, external_ref_dict.get("referenceCategory"), self.parse_external_ref_category
+ )
+ ref_locator: Optional[str] = external_ref_dict.get("referenceLocator")
+ ref_type: Optional[str] = external_ref_dict.get("referenceType")
+ comment: Optional[str] = external_ref_dict.get("comment")
+ raise_parsing_error_if_logger_has_messages(logger, "ExternalPackageRef")
+ external_ref = construct_or_raise_parsing_error(
+ ExternalPackageRef,
+ dict(category=ref_category, reference_type=ref_type, locator=ref_locator, comment=comment),
+ )
+
+ return external_ref
+
+ @staticmethod
+ def parse_external_ref_category(external_ref_category_str: str) -> ExternalPackageRefCategory:
+ try:
+ external_ref_category = ExternalPackageRefCategory[json_str_to_enum_name(external_ref_category_str)]
+ except KeyError:
+ raise SPDXParsingError([f"Invalid ExternalPackageRefCategory: {external_ref_category_str}"])
+
+ return external_ref_category
+
+ @staticmethod
+ def parse_package_verification_code(verification_code_dict: Dict) -> PackageVerificationCode:
+ excluded_files: List[str] = verification_code_dict.get("packageVerificationCodeExcludedFiles", [])
+ verification_code_value: Optional[str] = verification_code_dict.get("packageVerificationCodeValue")
+
+ package_verification_code = construct_or_raise_parsing_error(
+ PackageVerificationCode, dict(value=verification_code_value, excluded_files=excluded_files)
+ )
+
+ return package_verification_code
+
+ @staticmethod
+ def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpose:
+ try:
+ return PackagePurpose[json_str_to_enum_name(primary_package_purpose)]
+ except KeyError:
+ raise SPDXParsingError([f"Invalid PrimaryPackagePurpose: {primary_package_purpose}"])
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/relationship_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/relationship_parser.py
new file mode 100644
index 000000000..17374bef5
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/relationship_parser.py
@@ -0,0 +1,213 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict, List, Optional
+
+from spdx_tools.common.typing.constructor_type_errors import ConstructorTypeErrors
+from spdx_tools.spdx.model import Relationship, RelationshipType
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import (
+ delete_duplicates_from_list,
+ json_str_to_enum_name,
+ parse_field_or_log_error,
+ parse_field_or_no_assertion_or_none,
+)
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+
+
+class RelationshipParser:
+ logger: Logger
+
+ def __init__(self):
+ self.logger = Logger()
+
+ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]:
+ relationships = []
+ relationship_dicts: List[Dict] = input_doc_dict.get("relationships", [])
+ relationships.extend(
+ parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationship, [], True)
+ )
+
+ document_describes: List[str] = delete_duplicates_from_list(input_doc_dict.get("documentDescribes", []))
+ doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID")
+
+ existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
+ relationships
+ )
+ relationships.extend(
+ parse_field_or_log_error(
+ self.logger,
+ document_describes,
+ lambda x: self.parse_document_describes(
+ doc_spdx_id=doc_spdx_id,
+ described_spdx_ids=x,
+ existing_relationships=existing_relationships_without_comments,
+ ),
+ [],
+ )
+ )
+
+ package_dicts: List[Dict] = input_doc_dict.get("packages", [])
+ existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
+ relationships
+ )
+
+ relationships.extend(
+ parse_field_or_log_error(
+ self.logger,
+ package_dicts,
+ lambda x: self.parse_has_files(
+ package_dicts=x, existing_relationships=existing_relationships_without_comments
+ ),
+ [],
+ )
+ )
+
+ file_dicts: List[Dict] = input_doc_dict.get("files", [])
+
+ # not implemented yet: deal with deprecated fields in file:
+ # https://github.com/spdx/tools-python/issues/294 & https://github.com/spdx/tools-python/issues/387
+ _ = self.parse_artifact_of(file_dicts=file_dicts)
+ _ = self.parse_file_dependencies(file_dicts=file_dicts)
+
+ raise_parsing_error_if_logger_has_messages(self.logger)
+
+ return relationships
+
+ def parse_relationship(self, relationship_dict: Dict) -> Relationship:
+ logger = Logger()
+ spdx_element_id: Optional[str] = relationship_dict.get("spdxElementId")
+ related_spdx_element: Optional[str] = parse_field_or_no_assertion_or_none(
+ relationship_dict.get("relatedSpdxElement")
+ )
+ relationship_type: Optional[RelationshipType] = parse_field_or_log_error(
+ logger, relationship_dict.get("relationshipType"), self.parse_relationship_type
+ )
+ relationship_comment: Optional[str] = relationship_dict.get("comment")
+ raise_parsing_error_if_logger_has_messages(logger, "Relationship")
+
+ relationship = construct_or_raise_parsing_error(
+ Relationship,
+ dict(
+ spdx_element_id=spdx_element_id,
+ relationship_type=relationship_type,
+ related_spdx_element_id=related_spdx_element,
+ comment=relationship_comment,
+ ),
+ )
+ return relationship
+
+ @staticmethod
+ def parse_relationship_type(relationship_type_str: str) -> RelationshipType:
+ try:
+ relationship_type = RelationshipType[json_str_to_enum_name(relationship_type_str)]
+ except KeyError:
+ raise SPDXParsingError([f"Invalid RelationshipType: {relationship_type_str}"])
+ return relationship_type
+
+ def parse_document_describes(
+ self, doc_spdx_id: str, described_spdx_ids: List[str], existing_relationships: List[Relationship]
+ ) -> List[Relationship]:
+ logger = Logger()
+ describes_relationships = []
+ for spdx_id in described_spdx_ids:
+ try:
+ describes_relationship = Relationship(
+ spdx_element_id=doc_spdx_id,
+ relationship_type=RelationshipType.DESCRIBES,
+ related_spdx_element_id=spdx_id,
+ )
+ except ConstructorTypeErrors as err:
+ logger.append(err.get_messages())
+ continue
+ if not self.check_if_relationship_exists(describes_relationship, existing_relationships):
+ describes_relationships.append(describes_relationship)
+ raise_parsing_error_if_logger_has_messages(logger, "document describes relationships")
+
+ return describes_relationships
+
+ def parse_has_files(
+ self, package_dicts: List[Dict], existing_relationships: List[Relationship]
+ ) -> List[Relationship]:
+ # assume existing relationships are stripped of comments
+ logger = Logger()
+ contains_relationships = []
+ for package in package_dicts:
+ package_spdx_id: Optional[str] = package.get("SPDXID")
+ contained_files: List[str] = delete_duplicates_from_list(package.get("hasFiles", []))
+ if not contained_files:
+ continue
+ for file_spdx_id in contained_files:
+ try:
+ contains_relationship = Relationship(
+ spdx_element_id=package_spdx_id,
+ relationship_type=RelationshipType.CONTAINS,
+ related_spdx_element_id=file_spdx_id,
+ )
+ except ConstructorTypeErrors as err:
+ logger.append(err.get_messages())
+ continue
+ if not self.check_if_relationship_exists(
+ relationship=contains_relationship, existing_relationships=existing_relationships
+ ):
+ contains_relationships.append(contains_relationship)
+ raise_parsing_error_if_logger_has_messages(logger, "package contains relationships")
+
+ return contains_relationships
+
+ def check_if_relationship_exists(
+ self, relationship: Relationship, existing_relationships: List[Relationship]
+ ) -> bool:
+ # assume existing relationships are stripped of comments
+ if relationship in existing_relationships:
+ return True
+ relationship_inverted: Relationship = self.invert_relationship(relationship)
+ if relationship_inverted in existing_relationships:
+ return True
+
+ return False
+
+ @staticmethod
+ def get_all_relationships_without_comments(existing_relationships: List[Relationship]) -> List[Relationship]:
+ relationships_without_comments = [
+ Relationship(
+ relationship_type=relationship.relationship_type,
+ related_spdx_element_id=relationship.related_spdx_element_id,
+ spdx_element_id=relationship.spdx_element_id,
+ )
+ for relationship in existing_relationships
+ ]
+ return relationships_without_comments
+
+ def invert_relationship(self, relationship: Relationship) -> Relationship:
+ return Relationship(
+ related_spdx_element_id=relationship.spdx_element_id,
+ spdx_element_id=relationship.related_spdx_element_id,
+ relationship_type=self.invert_relationship_types[relationship.relationship_type],
+ comment=relationship.comment,
+ )
+
+ invert_relationship_types = {
+ RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY,
+ RelationshipType.DESCRIBED_BY: RelationshipType.DESCRIBES,
+ RelationshipType.CONTAINS: RelationshipType.CONTAINED_BY,
+ RelationshipType.CONTAINED_BY: RelationshipType.CONTAINS,
+ }
+
+ @staticmethod
+ def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]:
+ dependency_relationships = []
+ # the field fileDependencies is deprecated and should be converted to a relationship
+ # https://github.com/spdx/tools-python/issues/387
+ return dependency_relationships
+
+ @staticmethod
+ def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]:
+ generated_relationships = []
+ # artifactOfs is deprecated and should be converted to an external package and a generated from relationship
+ # https://github.com/spdx/tools-python/issues/294
+ return generated_relationships
diff --git a/src/spdx_tools/spdx/parser/jsonlikedict/snippet_parser.py b/src/spdx_tools/spdx/parser/jsonlikedict/snippet_parser.py
new file mode 100644
index 000000000..3914e686b
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/jsonlikedict/snippet_parser.py
@@ -0,0 +1,140 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+from beartype.typing import Dict, List, Optional, Tuple, Union
+from license_expression import LicenseExpression
+
+from spdx_tools.spdx.model import Snippet, SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import (
+ parse_field_or_log_error,
+ parse_field_or_no_assertion_or_none,
+)
+from spdx_tools.spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import construct_or_raise_parsing_error
+
+
+class RangeType(Enum):
+ BYTE = auto()
+ LINE = auto()
+
+
+class SnippetParser:
+ logger: Logger
+ license_expression_parser = LicenseExpressionParser
+
+ def __init__(self):
+ self.logger = Logger()
+ self.license_expression_parser = LicenseExpressionParser()
+
+ def parse_snippet(self, snippet_dict: Dict) -> Snippet:
+ logger = Logger()
+ spdx_id: Optional[str] = snippet_dict.get("SPDXID")
+ file_spdx_id: Optional[str] = snippet_dict.get("snippetFromFile")
+ name: Optional[str] = snippet_dict.get("name")
+
+ ranges: Dict = parse_field_or_log_error(logger, snippet_dict.get("ranges", []), self.parse_ranges, default={})
+ byte_range: Optional[Tuple[Union[int, str], Union[int, str]]] = ranges.get(RangeType.BYTE)
+ line_range: Optional[Tuple[Union[int, str], Union[int, str]]] = ranges.get(RangeType.LINE)
+ byte_range = self.convert_range_from_str(byte_range)
+ line_range = self.convert_range_from_str(line_range)
+
+ attribution_texts: List[str] = snippet_dict.get("attributionTexts", [])
+ comment: Optional[str] = snippet_dict.get("comment")
+ copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = parse_field_or_no_assertion_or_none(
+ snippet_dict.get("copyrightText")
+ )
+ license_comment: Optional[str] = snippet_dict.get("licenseComments")
+ license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(
+ logger, snippet_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression
+ )
+
+ license_info: List[Union[LicenseExpression], SpdxNoAssertion, SpdxNone] = parse_field_or_log_error(
+ logger,
+ snippet_dict.get("licenseInfoInSnippets"),
+ self.license_expression_parser.parse_license_expression,
+ field_is_list=True,
+ )
+ if logger.has_messages():
+ raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"])
+
+ snippet = construct_or_raise_parsing_error(
+ Snippet,
+ dict(
+ spdx_id=spdx_id,
+ name=name,
+ byte_range=byte_range,
+ file_spdx_id=file_spdx_id,
+ line_range=line_range,
+ attribution_texts=attribution_texts,
+ comment=comment,
+ copyright_text=copyright_text,
+ license_comment=license_comment,
+ license_concluded=license_concluded,
+ license_info_in_snippet=license_info,
+ ),
+ )
+
+ return snippet
+
+ def parse_ranges(self, ranges_from_snippet: List[Dict]) -> Dict:
+ logger = Logger()
+ ranges = {}
+ for range_dict in ranges_from_snippet:
+ try:
+ range_type: RangeType = self.validate_range_and_get_type(range_dict)
+ start_end_tuple: Tuple[int, int] = SnippetParser.get_start_end_tuple(range_dict, range_type)
+ ranges[range_type] = start_end_tuple
+ except ValueError as error:
+ logger.append(error.args[0])
+ if logger.has_messages():
+ raise SPDXParsingError([f"Error while parsing snippet ranges: {logger.get_messages()}"])
+ return ranges
+
+ @staticmethod
+ def get_start_end_tuple(range_dict: Dict, range_type: RangeType) -> Tuple[int, int]:
+ end_pointer: Dict = range_dict["endPointer"]
+ start_pointer: Dict = range_dict["startPointer"]
+ if range_type == RangeType.BYTE:
+ start: int = start_pointer["offset"]
+ end: int = end_pointer["offset"]
+ else:
+ start: int = start_pointer["lineNumber"]
+ end: int = end_pointer["lineNumber"]
+ return start, end
+
+ def validate_range_and_get_type(self, range_dict: Dict) -> RangeType:
+ if "startPointer" not in range_dict:
+ raise ValueError("Startpointer missing in snippet ranges.")
+ if "endPointer" not in range_dict:
+ raise ValueError("Endpointer missing in snippet ranges.")
+ start_pointer_type: RangeType = self.validate_pointer_and_get_type(range_dict["startPointer"])
+ end_pointer_type: RangeType = self.validate_pointer_and_get_type(range_dict["endPointer"])
+ if start_pointer_type != end_pointer_type:
+ raise ValueError("Type of startpointer is not the same as type of endpointer.")
+ return start_pointer_type
+
+ @staticmethod
+ def validate_pointer_and_get_type(pointer: Dict) -> RangeType:
+ if "offset" in pointer and "lineNumber" in pointer:
+ raise ValueError('Couldn\'t determine type of pointer: "offset" and "lineNumber" provided as key.')
+ if "offset" not in pointer and "lineNumber" not in pointer:
+ raise ValueError('Couldn\'t determine type of pointer: neither "offset" nor "lineNumber" provided as key.')
+ return RangeType.BYTE if "offset" in pointer else RangeType.LINE
+
+ @staticmethod
+ def convert_range_from_str(
+ _range: Tuple[Union[int, str], Union[int, str]],
+ ) -> Tuple[Union[int, str], Union[int, str]]:
+ # XML does not support integers, so we have to convert from string (if possible)
+ if not _range:
+ return _range
+
+ if isinstance(_range[0], str) and _range[0].isdigit():
+ _range = int(_range[0]), _range[1]
+ if isinstance(_range[1], str) and _range[1].isdigit():
+ _range = _range[0], int(_range[1])
+ return _range
diff --git a/src/spdx_tools/spdx/parser/logger.py b/src/spdx_tools/spdx/parser/logger.py
new file mode 100644
index 000000000..343de7398
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/logger.py
@@ -0,0 +1,23 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+
+class Logger:
+ messages: List[str]
+
+ def __init__(self):
+ self.messages = []
+
+ def append(self, message: str):
+ self.messages.append(message)
+
+ def extend(self, messages_to_append: List[str]):
+ self.messages.extend(messages_to_append)
+
+ def has_messages(self):
+ return bool(self.messages)
+
+ def get_messages(self):
+ return list(self.messages)
diff --git a/src/spdx_tools/spdx/parser/parse_anything.py b/src/spdx_tools/spdx/parser/parse_anything.py
new file mode 100644
index 000000000..b47dc50dc
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/parse_anything.py
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+
+from spdx_tools.spdx.formats import FileFormat, file_name_to_format
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.json import json_parser
+from spdx_tools.spdx.parser.rdf import rdf_parser
+from spdx_tools.spdx.parser.tagvalue import tagvalue_parser
+from spdx_tools.spdx.parser.xml import xml_parser
+from spdx_tools.spdx.parser.yaml import yaml_parser
+
+
+def parse_file(file_name: str, encoding: str = "utf-8") -> Document:
+ if encoding != "utf-8":
+ logging.warning(
+ "It's recommended to use the UTF-8 encoding for any SPDX file. Consider changing the encoding of the file."
+ )
+
+ input_format = file_name_to_format(file_name)
+ if input_format == FileFormat.RDF_XML:
+ return rdf_parser.parse_from_file(file_name, encoding)
+ elif input_format == FileFormat.TAG_VALUE:
+ return tagvalue_parser.parse_from_file(file_name, encoding)
+ elif input_format == FileFormat.JSON:
+ return json_parser.parse_from_file(file_name, encoding)
+ elif input_format == FileFormat.XML:
+ return xml_parser.parse_from_file(file_name, encoding)
+ elif input_format == FileFormat.YAML:
+ return yaml_parser.parse_from_file(file_name, encoding)
diff --git a/src/spdx_tools/spdx/parser/parsing_functions.py b/src/spdx_tools/spdx/parser/parsing_functions.py
new file mode 100644
index 000000000..47f73951d
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/parsing_functions.py
@@ -0,0 +1,26 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Dict
+
+from spdx_tools.common.typing.constructor_type_errors import ConstructorTypeErrors
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+
+
+def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construction: Dict) -> Any:
+ try:
+ constructed_object = object_to_construct(**args_for_construction)
+ except ConstructorTypeErrors as err:
+ raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.get_messages()}"])
+ except TypeError as err:
+ raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.args[0]}"])
+ return constructed_object
+
+
+def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_name: str = None):
+ if logger.has_messages():
+ if parsed_object_name:
+ raise SPDXParsingError([f"Error while parsing {parsed_object_name}: {logger.get_messages()}"])
+ else:
+ raise SPDXParsingError(logger.get_messages())
diff --git a/src/spdx_tools/spdx/parser/rdf/__init__.py b/src/spdx_tools/spdx/parser/rdf/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/parser/rdf/annotation_parser.py b/src/spdx_tools/spdx/parser/rdf/annotation_parser.py
new file mode 100644
index 000000000..880637bf1
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/annotation_parser.py
@@ -0,0 +1,48 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import RDFS, BNode, Graph, URIRef
+
+from spdx_tools.spdx.datetime_conversions import datetime_from_str
+from spdx_tools.spdx.model import Annotation, AnnotationType
+from spdx_tools.spdx.parser.actor_parser import ActorParser
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import parse_enum_value, parse_literal, parse_spdx_id
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def parse_annotation(annotation_node: BNode, graph: Graph, parent_node: URIRef, doc_namespace: str) -> Annotation:
+ logger = Logger()
+ spdx_id = parse_spdx_id(parent_node, doc_namespace, graph)
+ annotator = parse_literal(
+ logger, graph, annotation_node, SPDX_NAMESPACE.annotator, parsing_method=ActorParser.parse_actor
+ )
+ annotation_type = parse_literal(
+ logger,
+ graph,
+ annotation_node,
+ SPDX_NAMESPACE.annotationType,
+ parsing_method=lambda x: parse_enum_value(x, AnnotationType, SPDX_NAMESPACE.annotationType_),
+ )
+ annotation_date = parse_literal(
+ logger, graph, annotation_node, SPDX_NAMESPACE.annotationDate, parsing_method=datetime_from_str
+ )
+ annotation_comment = parse_literal(logger, graph, annotation_node, RDFS.comment)
+
+ raise_parsing_error_if_logger_has_messages(logger, "Annotation")
+ annotation = construct_or_raise_parsing_error(
+ Annotation,
+ dict(
+ spdx_id=spdx_id,
+ annotation_type=annotation_type,
+ annotator=annotator,
+ annotation_date=annotation_date,
+ annotation_comment=annotation_comment,
+ ),
+ )
+
+ return annotation
diff --git a/src/spdx_tools/spdx/parser/rdf/checksum_parser.py b/src/spdx_tools/spdx/parser/rdf/checksum_parser.py
new file mode 100644
index 000000000..722f20340
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/checksum_parser.py
@@ -0,0 +1,37 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import BNode, Graph
+
+from spdx_tools.spdx.model import Checksum, ChecksumAlgorithm
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import parse_literal, remove_prefix
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def parse_checksum(parent_node: BNode, graph: Graph) -> Checksum:
+ logger = Logger()
+ algorithm = parse_literal(
+ logger, graph, parent_node, SPDX_NAMESPACE.algorithm, parsing_method=convert_rdf_to_algorithm
+ )
+ value = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.checksumValue)
+
+ raise_parsing_error_if_logger_has_messages(logger, "Checksum")
+ checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=algorithm, value=value))
+ return checksum
+
+
+def convert_rdf_to_algorithm(algorithm: str) -> ChecksumAlgorithm:
+ algorithm = remove_prefix(algorithm, SPDX_NAMESPACE.checksumAlgorithm_).upper()
+ if "BLAKE2B" in algorithm:
+ algorithm = algorithm.replace("BLAKE2B", "BLAKE2B_")
+ try:
+ checksum = ChecksumAlgorithm[algorithm]
+ except KeyError:
+ raise SPDXParsingError([f"Invalid value for ChecksumAlgorithm: {algorithm}"])
+ return checksum
diff --git a/src/spdx_tools/spdx/parser/rdf/creation_info_parser.py b/src/spdx_tools/spdx/parser/rdf/creation_info_parser.py
new file mode 100644
index 000000000..7c70e0242
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/creation_info_parser.py
@@ -0,0 +1,145 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import logging
+import sys
+from urllib.parse import urldefrag
+
+from beartype.typing import Tuple
+from rdflib import RDF, RDFS, Graph, Namespace
+from rdflib.exceptions import UniquenessError
+from rdflib.term import URIRef
+
+from spdx_tools.spdx.constants import DOCUMENT_SPDX_ID
+from spdx_tools.spdx.datetime_conversions import datetime_from_str
+from spdx_tools.spdx.model import CreationInfo, ExternalDocumentRef, Version
+from spdx_tools.spdx.parser.actor_parser import ActorParser
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.checksum_parser import parse_checksum
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import (
+ get_correctly_typed_triples,
+ parse_literal,
+ parse_spdx_id,
+ remove_prefix,
+)
+from spdx_tools.spdx.rdfschema.namespace import LICENSE_NAMESPACE, SPDX_NAMESPACE
+
+
+def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]:
+ logger = Logger()
+ namespace, spdx_id, doc_node = parse_namespace_and_spdx_id(graph)
+ spec_version = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.specVersion)
+ data_license = parse_literal(
+ logger,
+ graph,
+ doc_node,
+ SPDX_NAMESPACE.dataLicense,
+ parsing_method=lambda x: remove_prefix(x, LICENSE_NAMESPACE),
+ )
+ comment = parse_literal(logger, graph, doc_node, RDFS.comment)
+ name = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.name)
+
+ creation_info_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.CreationInfo)
+ if not creation_info_node:
+ logger.append("CreationInfo does not exist.")
+ raise SPDXParsingError([f"Error while parsing document {name}: {logger.get_messages()}"])
+
+ created = parse_literal(
+ logger, graph, creation_info_node, SPDX_NAMESPACE.created, parsing_method=datetime_from_str
+ )
+ license_list_version = parse_literal(
+ logger, graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion, parsing_method=Version.from_string
+ )
+ creator_comment = parse_literal(logger, graph, creation_info_node, RDFS.comment)
+ creators = []
+ for _, _, creator_literal in get_correctly_typed_triples(
+ logger, graph, creation_info_node, SPDX_NAMESPACE.creator
+ ):
+ creators.append(ActorParser.parse_actor(creator_literal.toPython()))
+ if not creators:
+ logger.append("No creators provided.")
+ external_document_refs = []
+ for _, _, external_document_node in get_correctly_typed_triples(
+ logger, graph, doc_node, SPDX_NAMESPACE.externalDocumentRef
+ ):
+ external_document_refs.append(parse_external_document_refs(external_document_node, graph, namespace))
+
+ raise_parsing_error_if_logger_has_messages(logger, "CreationInfo")
+ creation_info = construct_or_raise_parsing_error(
+ CreationInfo,
+ dict(
+ spdx_id=spdx_id,
+ document_namespace=namespace,
+ spdx_version=spec_version,
+ name=name,
+ data_license=data_license,
+ document_comment=comment,
+ created=created,
+ license_list_version=license_list_version,
+ creator_comment=creator_comment,
+ creators=creators,
+ external_document_refs=external_document_refs,
+ ),
+ )
+ return creation_info, doc_node
+
+
+def parse_namespace_and_spdx_id(graph: Graph) -> (str, str):
+ try:
+ subject = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.SpdxDocument, any=False)
+ except UniquenessError:
+ logging.error("Multiple SpdxDocuments found, can't parse rdf file.")
+ sys.exit(1)
+
+ if not subject:
+ logging.error("No SpdxDocument found, can't parse rdf file.")
+ sys.exit(1)
+ if "#" not in subject:
+ logging.error(
+ "No '#' found in the URI of SpdxDocument, "
+ f"the URI for the SpdxDocument should be the namespace appended by '#{DOCUMENT_SPDX_ID}."
+ )
+ sys.exit(1)
+
+ namespace, spdx_id = urldefrag(str(subject))
+
+ if not namespace:
+ logging.error(
+ f"No namespace found, the URI for the SpdxDocument should be the namespace appended by "
+ f"'#{DOCUMENT_SPDX_ID}."
+ )
+ sys.exit(1)
+
+ if not spdx_id:
+ spdx_id = None
+
+ return namespace, spdx_id, subject
+
+
+def parse_external_document_refs(
+ external_document_node: URIRef, graph: Graph, doc_namespace: str
+) -> ExternalDocumentRef:
+ logger = Logger()
+ document_ref_id = parse_spdx_id(external_document_node, doc_namespace, graph)
+ document_uri = parse_literal(logger, graph, external_document_node, SPDX_NAMESPACE.spdxDocument)
+ checksum = parse_literal(
+ logger,
+ graph,
+ external_document_node,
+ SPDX_NAMESPACE.checksum,
+ parsing_method=lambda x: parse_checksum(x, graph),
+ )
+ external_document_ref = construct_or_raise_parsing_error(
+ ExternalDocumentRef, dict(document_ref_id=document_ref_id, document_uri=document_uri, checksum=checksum)
+ )
+
+ # To replace the external doc namespaces by the ref id in spdx ids later (e.g. in a relationship), we need to bind
+ # the namespace to the graph.
+ graph.bind(external_document_ref.document_ref_id, Namespace(external_document_ref.document_uri + "#"))
+
+ return external_document_ref
diff --git a/src/spdx_tools/spdx/parser/rdf/extracted_licensing_info_parser.py b/src/spdx_tools/spdx/parser/rdf/extracted_licensing_info_parser.py
new file mode 100644
index 000000000..e77e8e8ad
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/extracted_licensing_info_parser.py
@@ -0,0 +1,54 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import RDFS, Graph, URIRef
+
+from spdx_tools.spdx.model import ExtractedLicensingInfo
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import (
+ get_correctly_typed_triples,
+ parse_literal,
+ parse_literal_or_no_assertion_or_none,
+)
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def parse_extracted_licensing_info(
+ extracted_licensing_info_node: URIRef, graph: Graph, doc_namespace: str
+) -> ExtractedLicensingInfo:
+ logger = Logger()
+ license_id = parse_literal(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.licenseId)
+ if not license_id:
+ license_id = (
+ extracted_licensing_info_node.fragment
+ if extracted_licensing_info_node.startswith(f"{doc_namespace}#")
+ else extracted_licensing_info_node.toPython()
+ )
+
+ extracted_text = parse_literal(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.extractedText)
+ comment = parse_literal(logger, graph, extracted_licensing_info_node, RDFS.comment)
+ license_name = parse_literal_or_no_assertion_or_none(
+ logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.name
+ )
+ cross_references = []
+ for _, _, cross_reference_node in get_correctly_typed_triples(
+ logger, graph, extracted_licensing_info_node, RDFS.seeAlso
+ ):
+ cross_references.append(cross_reference_node.toPython())
+ raise_parsing_error_if_logger_has_messages(logger, "ExtractedLicensingInfo")
+ extracted_licensing_info = construct_or_raise_parsing_error(
+ ExtractedLicensingInfo,
+ dict(
+ license_id=license_id,
+ extracted_text=extracted_text,
+ comment=comment,
+ license_name=license_name,
+ cross_references=cross_references,
+ ),
+ )
+
+ return extracted_licensing_info
diff --git a/src/spdx_tools/spdx/parser/rdf/file_parser.py b/src/spdx_tools/spdx/parser/rdf/file_parser.py
new file mode 100644
index 000000000..22323d09c
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/file_parser.py
@@ -0,0 +1,91 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Union
+from rdflib import RDFS, BNode, Graph, URIRef
+
+from spdx_tools.spdx.model import File, FileType
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.checksum_parser import parse_checksum
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import (
+ apply_parsing_method_or_log_error,
+ get_correctly_typed_triples,
+ get_correctly_typed_value,
+ parse_enum_value,
+ parse_literal,
+ parse_literal_or_no_assertion_or_none,
+ parse_spdx_id,
+)
+from spdx_tools.spdx.parser.rdf.license_expression_parser import parse_license_expression
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def parse_file(file_node: Union[URIRef, BNode], graph: Graph, doc_namespace: str) -> File:
+ logger = Logger()
+ spdx_id = parse_spdx_id(file_node, doc_namespace, graph)
+ name = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.fileName)
+ checksums = []
+ for _, _, checksum_node in get_correctly_typed_triples(logger, graph, file_node, SPDX_NAMESPACE.checksum):
+ checksums.append(parse_checksum(checksum_node, graph))
+
+ file_types = []
+ for _, _, file_type_ref in graph.triples((file_node, SPDX_NAMESPACE.fileType, None)):
+ file_types.append(
+ apply_parsing_method_or_log_error(
+ logger, file_type_ref, parsing_method=lambda x: parse_enum_value(x, FileType, SPDX_NAMESPACE.fileType_)
+ )
+ )
+ license_concluded = parse_literal_or_no_assertion_or_none(
+ logger,
+ graph,
+ file_node,
+ SPDX_NAMESPACE.licenseConcluded,
+ parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace, logger),
+ )
+ license_info_in_file = []
+ for _, _, license_info_from_files_node in graph.triples((file_node, SPDX_NAMESPACE.licenseInfoInFile, None)):
+ license_info_in_file.append(
+ get_correctly_typed_value(
+ logger,
+ license_info_from_files_node,
+ lambda x: parse_license_expression(x, graph, doc_namespace, logger),
+ )
+ )
+ license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments)
+ copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText)
+ file_contributors = []
+ for _, _, file_contributor in get_correctly_typed_triples(
+ logger, graph, file_node, SPDX_NAMESPACE.fileContributor, None
+ ):
+ file_contributors.append(file_contributor.toPython())
+
+ notice_text = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.noticeText)
+ comment = parse_literal(logger, graph, file_node, RDFS.comment)
+ attribution_texts = []
+ for _, _, attribution_text_literal in get_correctly_typed_triples(
+ logger, graph, file_node, SPDX_NAMESPACE.attributionText, None
+ ):
+ attribution_texts.append(attribution_text_literal.toPython())
+ raise_parsing_error_if_logger_has_messages(logger, "File")
+ file = construct_or_raise_parsing_error(
+ File,
+ dict(
+ name=name,
+ spdx_id=spdx_id,
+ checksums=checksums,
+ attribution_texts=attribution_texts,
+ comment=comment,
+ copyright_text=copyright_text,
+ file_types=file_types,
+ contributors=file_contributors,
+ license_comment=license_comment,
+ license_concluded=license_concluded,
+ license_info_in_file=license_info_in_file,
+ notice=notice_text,
+ ),
+ )
+ return file
diff --git a/src/spdx_tools/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx_tools/spdx/parser/rdf/graph_parsing_functions.py
new file mode 100644
index 000000000..5e2b8e099
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/graph_parsing_functions.py
@@ -0,0 +1,148 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum
+
+from beartype.typing import Any, Callable, Optional, Tuple, Type, Union
+from rdflib import RDF, Graph, URIRef
+from rdflib.exceptions import UniquenessError
+from rdflib.namespace import NamespaceManager
+from rdflib.term import BNode, Literal, Node
+
+from spdx_tools.spdx.casing_tools import camel_case_to_snake_case
+from spdx_tools.spdx.model import SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.model.spdx_no_assertion import SPDX_NO_ASSERTION_STRING
+from spdx_tools.spdx.model.spdx_none import SPDX_NONE_STRING
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def parse_literal(
+ logger: Logger,
+ graph: Graph,
+ subject: Node,
+ predicate: Node,
+ parsing_method: Callable = lambda x: x.strip(),
+ default: Any = None,
+):
+ value = get_unique_value(logger, graph, subject, predicate, default)
+ if not value:
+ return default
+ return apply_parsing_method_or_log_error(logger, value, parsing_method, default)
+
+
+def apply_parsing_method_or_log_error(
+ logger: Logger, value: Any, parsing_method: Callable = lambda x: x.strip(), default: Any = None
+):
+ try:
+ return parsing_method(value)
+ except SPDXParsingError as err:
+ logger.extend(err.get_messages())
+ except (TypeError, ValueError) as err:
+ logger.append(err.args[0])
+ return default
+
+
+def parse_literal_or_no_assertion_or_none(
+ logger: Logger,
+ graph: Graph,
+ subject: Node,
+ predicate: Node,
+ parsing_method: Callable = lambda x: x.strip(),
+ default: Any = None,
+):
+ value = get_unique_value(logger, graph, subject, predicate, default)
+ return get_correctly_typed_value(logger, value, parsing_method, default)
+
+
+def get_correctly_typed_value(
+ logger: Logger, value: Any, parsing_method: Callable = lambda x: x.strip(), default: Any = None
+):
+ if not value:
+ return default
+ if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING:
+ return SpdxNoAssertion()
+ if value == SPDX_NAMESPACE.none or value.toPython() == SPDX_NONE_STRING:
+ return SpdxNone()
+ return apply_parsing_method_or_log_error(logger, value, parsing_method, default)
+
+
+def get_unique_value(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any) -> Any:
+ try:
+ value = graph.value(subject=subject, predicate=predicate, default=default, any=False)
+ return value
+ except UniquenessError:
+ logger.append(f"Multiple values for unique value {predicate} found.")
+ return default
+
+
+def parse_enum_value(enum_str: str, enum_class: Type[Enum], prefix: str) -> Enum:
+ try:
+ enum_without_rdf_prefix = remove_prefix(enum_str, prefix)
+ value = camel_case_to_snake_case(enum_without_rdf_prefix).upper()
+ return enum_class[value]
+ except KeyError:
+ raise SPDXParsingError([f"Invalid value for {enum_class}: {enum_str}"])
+
+
+def parse_spdx_id(resource: Union[URIRef, BNode], doc_namespace: str, graph: Graph) -> Optional[str]:
+ if not resource or isinstance(resource, BNode):
+ return None
+ if resource.startswith(f"{doc_namespace}#"):
+ return resource.fragment
+ if "#" in resource:
+ namespace_manager = NamespaceManager(graph)
+ return namespace_manager.normalizeUri(resource)
+ return resource.toPython() or None
+
+
+# Python 3.9 introduced the method removeprefix() for strings, but as we are also supporting Python 3.7 and 3.8 we need
+# to write our own helper method to delete prefixes.
+def remove_prefix(string: str, prefix: str) -> str:
+ if string.startswith(prefix):
+ return string[len(prefix) :]
+ return string
+
+
+def get_correctly_typed_triples(
+ logger: Logger,
+ graph: Graph,
+ subject: Optional[Node] = None,
+ predicate: Optional[Node] = None,
+ _object: Optional[Node] = None,
+) -> Tuple[Union[BNode, URIRef], Node, Union[BNode, Literal, URIRef]]:
+ # this is a helper method to cast some rdf types from graph.triples() to be compatible with the
+ # code that follows
+ for s, p, o in graph.triples((subject, predicate, _object)):
+ if not isinstance(s, (BNode, URIRef)):
+ logger.append(
+ f"Warning: Subject {s} should be of type BNode or URIRef, but is {type(s).__name__}. "
+ f"This might lead to a failure."
+ )
+ if not isinstance(o, (BNode, Literal, URIRef)):
+ logger.append(
+ f"Warning: Object {o} should be of type BNode, Literal or URIRef, but is {type(o).__name__}. "
+ f"This might lead to a failure."
+ )
+ yield s, p, o
+
+
+def get_value_from_graph(
+ logger: Logger,
+ graph: Graph,
+ subject: Optional[Node] = None,
+ predicate: Optional[Node] = RDF.value,
+ _object: Optional[Node] = None,
+ default: Optional[Any] = None,
+ _any: Optional[bool] = True,
+) -> Optional[Union[URIRef, Literal, BNode]]:
+ # this is a helper method to cast some rdf types from graph.value() to be compatible with the
+ # code that follows
+ value = graph.value(subject=subject, predicate=predicate, object=_object, default=default, any=_any)
+ if value != default and value is not None and not isinstance(value, (URIRef, Literal, BNode)):
+ logger.append(
+ f"Warning: Node {value} should be of type BNode, Literal or URIRef, but is {type(value).__name__}. "
+ f"This might lead to a failure."
+ )
+ return value
diff --git a/src/spdx_tools/spdx/parser/rdf/license_expression_parser.py b/src/spdx_tools/spdx/parser/rdf/license_expression_parser.py
new file mode 100644
index 000000000..2ae547232
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/license_expression_parser.py
@@ -0,0 +1,62 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Optional, Union
+from license_expression import LicenseExpression
+from rdflib import RDF, Graph
+from rdflib.term import BNode, Identifier, Node, URIRef
+
+from spdx_tools.common.spdx_licensing import spdx_licensing
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import get_value_from_graph, remove_prefix
+from spdx_tools.spdx.rdfschema.namespace import LICENSE_NAMESPACE, SPDX_NAMESPACE
+
+
+def parse_license_expression(
+ license_expression_node: Union[URIRef, BNode, Node],
+ graph: Graph,
+ doc_namespace: str,
+ logger: Optional[Logger] = None,
+) -> LicenseExpression:
+ if not logger:
+ logger = Logger()
+
+ expression = ""
+ if license_expression_node.startswith(LICENSE_NAMESPACE):
+ expression = remove_prefix(license_expression_node, LICENSE_NAMESPACE)
+ return spdx_licensing.parse(expression)
+ if license_expression_node.startswith(doc_namespace):
+ expression = license_expression_node.fragment
+ return spdx_licensing.parse(expression)
+
+ node_type = graph.value(license_expression_node, RDF.type)
+ if node_type == SPDX_NAMESPACE.ConjunctiveLicenseSet:
+ members = []
+ for _, _, member_node in graph.triples((license_expression_node, SPDX_NAMESPACE.member, None)):
+ members.append(parse_license_expression(member_node, graph, doc_namespace, logger))
+ expression = " AND ".join([str(member) for member in members])
+ if node_type == SPDX_NAMESPACE.DisjunctiveLicenseSet:
+ members = []
+ for _, _, member_node in graph.triples((license_expression_node, SPDX_NAMESPACE.member, None)):
+ members.append(parse_license_expression(member_node, graph, doc_namespace, logger))
+ expression = " OR ".join([str(member) for member in members])
+ if node_type == SPDX_NAMESPACE.WithExceptionOperator:
+ license_expression = parse_license_expression(
+ graph.value(license_expression_node, SPDX_NAMESPACE.member), graph, doc_namespace, logger
+ )
+ exception = parse_license_exception(
+ get_value_from_graph(logger, graph, license_expression_node, SPDX_NAMESPACE.licenseException),
+ graph,
+ logger,
+ )
+ expression = f"{license_expression} WITH {exception}"
+
+ return spdx_licensing.parse(expression)
+
+
+def parse_license_exception(exception_node: Identifier, graph: Graph, logger) -> str:
+ if exception_node.startswith(LICENSE_NAMESPACE):
+ exception = remove_prefix(exception_node, LICENSE_NAMESPACE)
+ else:
+ exception = get_value_from_graph(logger, graph, exception_node, SPDX_NAMESPACE.licenseExceptionId).toPython()
+ return exception
diff --git a/src/spdx_tools/spdx/parser/rdf/package_parser.py b/src/spdx_tools/spdx/parser/rdf/package_parser.py
new file mode 100644
index 000000000..668377162
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/package_parser.py
@@ -0,0 +1,207 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Optional, Union
+from rdflib import DOAP, RDFS, Graph, URIRef
+from rdflib.term import BNode
+
+from spdx_tools.spdx.datetime_conversions import datetime_from_str
+from spdx_tools.spdx.model import (
+ ExternalPackageRef,
+ ExternalPackageRefCategory,
+ Package,
+ PackagePurpose,
+ PackageVerificationCode,
+)
+from spdx_tools.spdx.parser.actor_parser import ActorParser
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.checksum_parser import parse_checksum
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import (
+ get_correctly_typed_triples,
+ get_correctly_typed_value,
+ get_value_from_graph,
+ parse_enum_value,
+ parse_literal,
+ parse_literal_or_no_assertion_or_none,
+ parse_spdx_id,
+)
+from spdx_tools.spdx.parser.rdf.license_expression_parser import parse_license_expression
+from spdx_tools.spdx.rdfschema.namespace import REFERENCE_NAMESPACE, SPDX_NAMESPACE
+
+
+def parse_package(package_node: Union[URIRef, BNode], graph: Graph, doc_namespace: str) -> Package:
+ logger = Logger()
+ spdx_id = parse_spdx_id(package_node, doc_namespace, graph)
+ name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.name)
+ download_location = parse_literal_or_no_assertion_or_none(
+ logger, graph, package_node, SPDX_NAMESPACE.downloadLocation
+ )
+ checksums = []
+ for _, _, checksum_node in get_correctly_typed_triples(logger, graph, package_node, SPDX_NAMESPACE.checksum):
+ checksums.append(parse_checksum(checksum_node, graph))
+
+ version_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.versionInfo)
+ package_file_name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageFileName)
+
+ supplier = parse_literal_or_no_assertion_or_none(
+ logger, graph, package_node, SPDX_NAMESPACE.supplier, parsing_method=ActorParser.parse_actor
+ )
+ originator = parse_literal_or_no_assertion_or_none(
+ logger, graph, package_node, SPDX_NAMESPACE.originator, parsing_method=ActorParser.parse_actor
+ )
+ verification_code = parse_literal(
+ logger,
+ graph,
+ package_node,
+ SPDX_NAMESPACE.packageVerificationCode,
+ parsing_method=lambda x: parse_package_verification_code(x, graph),
+ )
+
+ external_package_refs = []
+ for _, _, external_package_ref_node in get_correctly_typed_triples(
+ logger, graph, package_node, SPDX_NAMESPACE.externalRef
+ ):
+ external_package_refs.append(parse_external_package_ref(external_package_ref_node, graph, doc_namespace))
+ files_analyzed = bool(
+ get_value_from_graph(logger, graph, package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)
+ )
+ license_concluded = parse_literal_or_no_assertion_or_none(
+ logger,
+ graph,
+ package_node,
+ SPDX_NAMESPACE.licenseConcluded,
+ parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace, logger),
+ )
+ license_declared = parse_literal_or_no_assertion_or_none(
+ logger,
+ graph,
+ package_node,
+ SPDX_NAMESPACE.licenseDeclared,
+ parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace, logger),
+ )
+ license_info_from_files = []
+ for _, _, license_info_from_files_node in graph.triples((package_node, SPDX_NAMESPACE.licenseInfoFromFiles, None)):
+ license_info_from_files.append(
+ get_correctly_typed_value(
+ logger,
+ license_info_from_files_node,
+ lambda x: parse_license_expression(x, graph, doc_namespace, logger),
+ )
+ )
+ license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments)
+ comment = parse_literal(logger, graph, package_node, RDFS.comment)
+ summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary)
+ description = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.description)
+ copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.copyrightText)
+ source_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.sourceInfo)
+ primary_package_purpose = parse_literal(
+ logger,
+ graph,
+ package_node,
+ SPDX_NAMESPACE.primaryPackagePurpose,
+ parsing_method=lambda x: parse_enum_value(x, PackagePurpose, SPDX_NAMESPACE.purpose_),
+ )
+ homepage = parse_literal(logger, graph, package_node, DOAP.homepage)
+ attribution_texts = []
+ for _, _, attribution_text_literal in get_correctly_typed_triples(
+ logger, graph, package_node, SPDX_NAMESPACE.attributionText, None
+ ):
+ attribution_texts.append(attribution_text_literal.toPython())
+
+ release_date = parse_literal(
+ logger, graph, package_node, SPDX_NAMESPACE.releaseDate, parsing_method=datetime_from_str
+ )
+ built_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.builtDate, parsing_method=datetime_from_str)
+ valid_until_date = parse_literal(
+ logger, graph, package_node, SPDX_NAMESPACE.validUntilDate, parsing_method=datetime_from_str
+ )
+ raise_parsing_error_if_logger_has_messages(logger, "Package")
+ package = construct_or_raise_parsing_error(
+ Package,
+ dict(
+ name=name,
+ spdx_id=spdx_id,
+ download_location=download_location,
+ version=version_info,
+ file_name=package_file_name,
+ supplier=supplier,
+ originator=originator,
+ files_analyzed=files_analyzed,
+ verification_code=verification_code,
+ checksums=checksums,
+ homepage=homepage,
+ source_info=source_info,
+ license_concluded=license_concluded,
+ license_info_from_files=license_info_from_files,
+ license_declared=license_declared,
+ license_comment=license_comment,
+ copyright_text=copyright_text,
+ summary=summary,
+ description=description,
+ comment=comment,
+ external_references=external_package_refs,
+ attribution_texts=attribution_texts,
+ primary_package_purpose=primary_package_purpose,
+ release_date=release_date,
+ built_date=built_date,
+ valid_until_date=valid_until_date,
+ ),
+ )
+
+ return package
+
+
+def parse_package_verification_code(
+ package_verification_code_node: URIRef, graph: Graph
+) -> Optional[PackageVerificationCode]:
+ logger = Logger()
+ value = parse_literal(logger, graph, package_verification_code_node, SPDX_NAMESPACE.packageVerificationCodeValue)
+ excluded_files = []
+ for _, _, excluded_file_literal in graph.triples(
+ (package_verification_code_node, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, None)
+ ):
+ excluded_files.append(excluded_file_literal.toPython())
+
+ raise_parsing_error_if_logger_has_messages(logger, "PackageVerificationCode")
+ package_verification_code = construct_or_raise_parsing_error(
+ PackageVerificationCode, dict(value=value, excluded_files=excluded_files)
+ )
+ return package_verification_code
+
+
+def parse_external_package_ref(external_package_ref_node: BNode, graph: Graph, doc_namespace) -> ExternalPackageRef:
+ logger = Logger()
+ ref_locator = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceLocator)
+ ref_category = parse_literal(
+ logger,
+ graph,
+ external_package_ref_node,
+ SPDX_NAMESPACE.referenceCategory,
+ parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, SPDX_NAMESPACE.referenceCategory_),
+ )
+ ref_type = parse_literal(
+ logger,
+ graph,
+ external_package_ref_node,
+ SPDX_NAMESPACE.referenceType,
+ parsing_method=lambda x: parse_external_package_ref_type(x, doc_namespace),
+ )
+ comment = parse_literal(logger, graph, external_package_ref_node, RDFS.comment)
+
+ raise_parsing_error_if_logger_has_messages(logger, "ExternalPackageRef")
+ external_package_ref = construct_or_raise_parsing_error(
+ ExternalPackageRef, dict(category=ref_category, reference_type=ref_type, locator=ref_locator, comment=comment)
+ )
+ return external_package_ref
+
+
+def parse_external_package_ref_type(external_package_ref_type_resource: URIRef, doc_namespace: str) -> str:
+ if external_package_ref_type_resource.startswith(doc_namespace):
+ return external_package_ref_type_resource.fragment
+ if external_package_ref_type_resource.startswith(REFERENCE_NAMESPACE):
+ return external_package_ref_type_resource.replace(REFERENCE_NAMESPACE, "")
+ return external_package_ref_type_resource.toPython()
diff --git a/src/spdx_tools/spdx/parser/rdf/rdf_parser.py b/src/spdx_tools/spdx/parser/rdf/rdf_parser.py
new file mode 100644
index 000000000..5dc0b8f19
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/rdf_parser.py
@@ -0,0 +1,96 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Any, Dict
+from rdflib import RDF, Graph
+
+from spdx_tools.spdx.model import Document, RelationshipType
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.annotation_parser import parse_annotation
+from spdx_tools.spdx.parser.rdf.creation_info_parser import parse_creation_info
+from spdx_tools.spdx.parser.rdf.extracted_licensing_info_parser import parse_extracted_licensing_info
+from spdx_tools.spdx.parser.rdf.file_parser import parse_file
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import get_correctly_typed_triples
+from spdx_tools.spdx.parser.rdf.package_parser import parse_package
+from spdx_tools.spdx.parser.rdf.relationship_parser import parse_implicit_relationship, parse_relationship
+from spdx_tools.spdx.parser.rdf.snippet_parser import parse_snippet
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def parse_from_file(file_name: str, encoding: str = "utf-8") -> Document:
+ graph = Graph()
+ with open(file_name, encoding=encoding) as file:
+ graph.parse(file, format="xml")
+
+ document: Document = translate_graph_to_document(graph)
+ return document
+
+
+def translate_graph_to_document(graph: Graph) -> Document:
+ parsed_fields: Dict[str, Any] = dict()
+ logger = Logger()
+ creation_info, doc_node = parse_creation_info(graph)
+
+ parsed_fields["creation_info"] = creation_info
+
+ for element, triple, parsing_method in [
+ ("packages", (None, RDF.type, SPDX_NAMESPACE.Package), parse_package),
+ ("files", (None, RDF.type, SPDX_NAMESPACE.File), parse_file),
+ ("snippets", (None, RDF.type, SPDX_NAMESPACE.Snippet), parse_snippet),
+ ]:
+ elements = []
+ for element_node, _, _ in get_correctly_typed_triples(logger, graph, *triple):
+ try:
+ elements.append(parsing_method(element_node, graph, creation_info.document_namespace))
+ except SPDXParsingError as err:
+ logger.extend(err.get_messages())
+ parsed_fields[element] = elements
+
+ for element, triple, parsing_method in [
+ ("annotations", (None, SPDX_NAMESPACE.annotation, None), parse_annotation),
+ ("relationships", (None, SPDX_NAMESPACE.relationship, None), parse_relationship),
+ ]:
+ elements = []
+ for parent_node, _, element_node in graph.triples(triple):
+ try:
+ elements.append(parsing_method(element_node, graph, parent_node, creation_info.document_namespace))
+ except SPDXParsingError as err:
+ logger.extend(err.get_messages())
+ parsed_fields[element] = elements
+
+ for triple, relationship_type in [
+ ((None, SPDX_NAMESPACE.hasFile, None), RelationshipType.CONTAINS),
+ ((None, SPDX_NAMESPACE.describesPackage, None), RelationshipType.DESCRIBES),
+ ]:
+ for parent_node, _, element_node in get_correctly_typed_triples(logger, graph, *triple):
+ try:
+ relationship = parse_implicit_relationship(
+ parent_node, relationship_type, element_node, graph, creation_info.document_namespace
+ )
+ if relationship not in parsed_fields["relationships"]:
+ parsed_fields["relationships"].append(relationship)
+
+ except SPDXParsingError as err:
+ logger.extend(err.get_messages())
+
+ extracted_licensing_infos = []
+ for _, _, extracted_licensing_info_node in get_correctly_typed_triples(
+ logger, graph, None, SPDX_NAMESPACE.hasExtractedLicensingInfo
+ ):
+ try:
+ extracted_licensing_infos.append(
+ parse_extracted_licensing_info(extracted_licensing_info_node, graph, creation_info.document_namespace)
+ )
+ except SPDXParsingError as err:
+ logger.extend(err.get_messages())
+ parsed_fields["extracted_licensing_info"] = extracted_licensing_infos
+
+ raise_parsing_error_if_logger_has_messages(logger)
+ document = construct_or_raise_parsing_error(Document, parsed_fields)
+
+ return document
diff --git a/src/spdx_tools/spdx/parser/rdf/relationship_parser.py b/src/spdx_tools/spdx/parser/rdf/relationship_parser.py
new file mode 100644
index 000000000..562e0ab92
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/relationship_parser.py
@@ -0,0 +1,73 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import RDFS, Graph, URIRef
+from rdflib.term import Node
+
+from spdx_tools.spdx.model import Relationship, RelationshipType
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import (
+ parse_enum_value,
+ parse_literal,
+ parse_literal_or_no_assertion_or_none,
+ parse_spdx_id,
+)
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def parse_relationship(relationship_node: Node, graph: Graph, parent_node: URIRef, doc_namespace: str) -> Relationship:
+ logger = Logger()
+ spdx_element_id = parse_spdx_id(parent_node, doc_namespace, graph)
+
+ relationship_type = parse_literal(
+ logger,
+ graph,
+ relationship_node,
+ SPDX_NAMESPACE.relationshipType,
+ parsing_method=lambda x: parse_enum_value(x, RelationshipType, SPDX_NAMESPACE.relationshipType_),
+ )
+ related_spdx_element = parse_literal_or_no_assertion_or_none(
+ logger,
+ graph,
+ relationship_node,
+ SPDX_NAMESPACE.relatedSpdxElement,
+ parsing_method=lambda x: parse_spdx_id(x, doc_namespace, graph),
+ )
+
+ comment = parse_literal(logger, graph, relationship_node, RDFS.comment)
+ raise_parsing_error_if_logger_has_messages(logger, "Relationship")
+ relationship = construct_or_raise_parsing_error(
+ Relationship,
+ dict(
+ spdx_element_id=spdx_element_id,
+ relationship_type=relationship_type,
+ related_spdx_element_id=related_spdx_element,
+ comment=comment,
+ ),
+ )
+
+ return relationship
+
+
+def parse_implicit_relationship(
+ spdx_element_node: URIRef,
+ relationship_type: RelationshipType,
+ related_spdx_element_node: URIRef,
+ graph: Graph,
+ doc_namespace: str,
+) -> Relationship:
+ spdx_element_id = parse_spdx_id(spdx_element_node, doc_namespace, graph)
+ related_spdx_element_id = parse_spdx_id(related_spdx_element_node, doc_namespace, graph)
+ relationship = construct_or_raise_parsing_error(
+ Relationship,
+ dict(
+ spdx_element_id=spdx_element_id,
+ relationship_type=relationship_type,
+ related_spdx_element_id=related_spdx_element_id,
+ ),
+ )
+ return relationship
diff --git a/src/spdx_tools/spdx/parser/rdf/snippet_parser.py b/src/spdx_tools/spdx/parser/rdf/snippet_parser.py
new file mode 100644
index 000000000..d09671f60
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/rdf/snippet_parser.py
@@ -0,0 +1,147 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict, Optional, Tuple, Union
+from rdflib import RDF, RDFS, Graph
+from rdflib.exceptions import UniquenessError
+from rdflib.term import BNode, Node, URIRef
+
+from spdx_tools.spdx.model import Snippet
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.rdf.graph_parsing_functions import (
+ apply_parsing_method_or_log_error,
+ get_correctly_typed_triples,
+ get_correctly_typed_value,
+ get_value_from_graph,
+ parse_literal,
+ parse_literal_or_no_assertion_or_none,
+ parse_spdx_id,
+)
+from spdx_tools.spdx.parser.rdf.license_expression_parser import parse_license_expression
+from spdx_tools.spdx.rdfschema.namespace import POINTER_NAMESPACE, SPDX_NAMESPACE
+
+
+def parse_snippet(snippet_node: Union[URIRef, BNode], graph: Graph, doc_namespace: str) -> Snippet:
+ logger = Logger()
+ spdx_id = parse_spdx_id(snippet_node, doc_namespace, graph)
+ file_spdx_id_uri = get_value_from_graph(
+ logger, graph, subject=snippet_node, predicate=SPDX_NAMESPACE.snippetFromFile
+ )
+ file_spdx_id = parse_spdx_id(file_spdx_id_uri, doc_namespace, graph)
+ byte_range = None
+ line_range = None
+ for _, _, start_end_pointer in graph.triples((snippet_node, SPDX_NAMESPACE.range, None)):
+ parsed_range = apply_parsing_method_or_log_error(
+ logger, start_end_pointer, parsing_method=lambda x: parse_ranges(x, graph)
+ )
+ byte_range, line_range = set_range_or_log_error(byte_range, line_range, logger, parsed_range)
+
+ license_concluded = parse_literal_or_no_assertion_or_none(
+ logger,
+ graph,
+ snippet_node,
+ SPDX_NAMESPACE.licenseConcluded,
+ parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace, logger),
+ )
+ license_info_in_snippet = []
+ for _, _, license_info_in_snippet_node in graph.triples((snippet_node, SPDX_NAMESPACE.licenseInfoInSnippet, None)):
+ license_info_in_snippet.append(
+ get_correctly_typed_value(
+ logger,
+ license_info_in_snippet_node,
+ lambda x: parse_license_expression(x, graph, doc_namespace, logger),
+ )
+ )
+ license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments)
+ copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText)
+ comment = parse_literal(logger, graph, snippet_node, RDFS.comment)
+ name = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.name)
+ attribution_texts = []
+ for _, _, attribution_text_literal in get_correctly_typed_triples(
+ logger, graph, snippet_node, SPDX_NAMESPACE.attributionText, None
+ ):
+ attribution_texts.append(attribution_text_literal.toPython())
+
+ raise_parsing_error_if_logger_has_messages(logger, "Snippet")
+ snippet = construct_or_raise_parsing_error(
+ Snippet,
+ dict(
+ spdx_id=spdx_id,
+ file_spdx_id=file_spdx_id,
+ byte_range=byte_range,
+ line_range=line_range,
+ license_concluded=license_concluded,
+ license_info_in_snippet=license_info_in_snippet,
+ license_comment=license_comment,
+ copyright_text=copyright_text,
+ comment=comment,
+ name=name,
+ attribution_texts=attribution_texts,
+ ),
+ )
+ return snippet
+
+
+def set_range_or_log_error(
+ byte_range: Optional[Tuple[int, int]],
+ line_range: Optional[Tuple[int, int]],
+ logger: Logger,
+ parsed_range: Dict[str, Tuple[int, int]],
+) -> Tuple[Optional[Tuple[int, int]], Optional[Tuple[int, int]]]:
+ if not parsed_range:
+ return byte_range, line_range
+ if "ByteOffsetPointer" in parsed_range.keys() and not byte_range:
+ byte_range = parsed_range["ByteOffsetPointer"]
+ elif "ByteOffsetPointer" in parsed_range.keys() and byte_range:
+ logger.append("Multiple ByteOffsetPointer found.")
+ elif "LineCharPointer" in parsed_range.keys() and not line_range:
+ line_range = parsed_range["LineCharPointer"]
+ elif "LineCharPointer" in parsed_range.keys() and line_range:
+ logger.append("Multiple LineCharPointer found.")
+ return byte_range, line_range
+
+
+def parse_ranges(start_end_pointer: BNode, graph: Graph) -> Dict[str, Tuple[int, int]]:
+ range_values = dict()
+ start_pointer_type, start_pointer_node = get_pointer_type(graph, POINTER_NAMESPACE.startPointer, start_end_pointer)
+ end_pointer_type, end_pointer_node = get_pointer_type(graph, POINTER_NAMESPACE.endPointer, start_end_pointer)
+
+ if start_pointer_type != end_pointer_type:
+ raise SPDXParsingError(["Types of startPointer and endPointer don't match"])
+
+ range_values["startPointer"] = parse_range_value(graph, start_pointer_node, POINTER_MATCHING[start_pointer_type])
+ range_values["endPointer"] = parse_range_value(graph, end_pointer_node, POINTER_MATCHING[end_pointer_type])
+
+ return {str(start_pointer_type.fragment): (range_values["startPointer"], range_values["endPointer"])}
+
+
+def get_pointer_type(graph: Graph, pointer: URIRef, start_end_pointer: BNode) -> Tuple[URIRef, Node]:
+ try:
+ pointer_node = graph.value(start_end_pointer, pointer, any=False)
+ except UniquenessError:
+ raise SPDXParsingError([f"Multiple values for {pointer.fragment}"])
+ if not pointer_node:
+ raise SPDXParsingError([f"Couldn't find pointer of type {pointer.fragment}."])
+ pointer_type = get_value_from_graph(Logger(), graph, pointer_node, RDF.type)
+ return pointer_type, pointer_node
+
+
+POINTER_MATCHING = {
+ POINTER_NAMESPACE.ByteOffsetPointer: POINTER_NAMESPACE.offset,
+ POINTER_NAMESPACE.LineCharPointer: POINTER_NAMESPACE.lineNumber,
+}
+
+
+def parse_range_value(graph: Graph, pointer_node: Node, predicate: URIRef) -> Optional[int]:
+ try:
+ value = get_value_from_graph(Logger(), graph, pointer_node, predicate, _any=False)
+ except UniquenessError:
+ raise SPDXParsingError([f"Multiple values for {predicate.fragment} found."])
+ if value:
+ value = int(value.toPython())
+ return value
diff --git a/src/spdx_tools/spdx/parser/tagvalue/__init__.py b/src/spdx_tools/spdx/parser/tagvalue/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/parser/tagvalue/helper_methods.py b/src/spdx_tools/spdx/parser/tagvalue/helper_methods.py
new file mode 100644
index 000000000..ea528a434
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/tagvalue/helper_methods.py
@@ -0,0 +1,134 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import re
+
+from beartype.typing import Any, Callable, Dict, Optional
+from ply.yacc import YaccProduction
+
+from spdx_tools.spdx.casing_tools import camel_case_to_snake_case
+from spdx_tools.spdx.model import (
+ Annotation,
+ Checksum,
+ ChecksumAlgorithm,
+ CreationInfo,
+ ExtractedLicensingInfo,
+ File,
+ Package,
+ Snippet,
+)
+from spdx_tools.spdx.parser.error import SPDXParsingError
+
+
+def grammar_rule(doc):
+ # this is a helper method to use decorators for the parsing methods instead of docstrings
+ def decorate(func):
+ func.__doc__ = doc
+ return func
+
+ return decorate
+
+
+def str_from_text(text: Optional[str]) -> Optional[str]:
+ regex = re.compile("((.|\n)+) ", re.UNICODE)
+ match = regex.match(text)
+ if match:
+ return match.group(1)
+ elif isinstance(text, str):
+ return text
+ else:
+ return None
+
+
+def parse_checksum(checksum_str: str) -> Checksum:
+ # The lexer and the corresponding regex for the token CHECKSUM and EXT_DOC_REF_CHECKSUM ensure that the passed
+ # checksum_str is formatted in the way that the following lines of code can't cause an error.
+ algorithm, value = checksum_str.split(":")
+ algorithm = ChecksumAlgorithm[algorithm.upper().replace("-", "_")]
+ value = value.strip()
+ checksum = Checksum(algorithm, value)
+ return checksum
+
+
+def set_value(
+ parsed_value: YaccProduction,
+ dict_to_fill: Dict[str, Any],
+ argument_name: Optional[str] = None,
+ method_to_apply: Callable = lambda x: x,
+):
+ if not argument_name:
+ argument_name = get_property_name(parsed_value[1])
+ if argument_name in dict_to_fill:
+ dict_to_fill["logger"].append(f"Multiple values for {parsed_value[1]} found. Line: {parsed_value.lineno(1)}")
+ return
+ try:
+ dict_to_fill[argument_name] = method_to_apply(parsed_value[2])
+ except SPDXParsingError as err:
+ dict_to_fill["logger"].append(err.get_messages())
+ except ValueError as err:
+ dict_to_fill["logger"].append(err.args[0])
+ except KeyError:
+ dict_to_fill["logger"].append(f"Invalid {parsed_value[1]}: {parsed_value[2]}. Line: {parsed_value.lineno(1)}")
+
+
+def get_property_name(tag: str):
+ if tag not in TAG_DATA_MODEL_FIELD.keys():
+ return camel_case_to_snake_case(tag)
+ return TAG_DATA_MODEL_FIELD[tag][1]
+
+
+# This dictionary serves as a mapping from a tag to the corresponding class and field in the internal data model.
+# This mapping is not complete as we only list the values which can be parsed by a generic method and don't need any
+# individual logic.
+TAG_DATA_MODEL_FIELD = {
+ "SPDXVersion": (CreationInfo, "spdx_version"),
+ "DataLicense": (CreationInfo, "data_license"),
+ "DocumentName": (CreationInfo, "name"),
+ "DocumentComment": (CreationInfo, "document_comment"),
+ "DocumentNamespace": (CreationInfo, "document_namespace"),
+ "Creator": (CreationInfo, "creator"),
+ "Created": (CreationInfo, "created"),
+ "CreatorComment": (CreationInfo, "creator_comment"),
+ "LicenseListVersion": (CreationInfo, "license_list_version"),
+ "ExternalDocumentRef": (CreationInfo, "external_document_refs"),
+ "FileName": (File, "name"),
+ "FileType": (File, "file_type"),
+ "FileChecksum": (File, "checksums"),
+ "FileNotice": (File, "notice"),
+ "FileCopyrightText": (File, "copyright_text"),
+ "LicenseComments": (File, "license_comment"),
+ "FileComment": (File, "comment"),
+ "LicenseConcluded": (File, "license_concluded"),
+ "LicenseDeclared": (File, "license_declared"),
+ "PackageName": (Package, "name"),
+ "PackageComment": (Package, "comment"),
+ "PackageCopyrightText": (Package, "copyright_text"),
+ "PackageLicenseComments": (Package, "license_comment"),
+ "PackageLicenseDeclared": (Package, "license_declared"),
+ "PackageLicenseConcluded": (Package, "license_concluded"),
+ "PackageFileName": (Package, "file_name"),
+ "PackageVersion": (Package, "version"),
+ "PackageDownloadLocation": (Package, "download_location"),
+ "PackageSummary": (Package, "summary"),
+ "PackageSourceInfo": (Package, "source_info"),
+ "PackageSupplier": (Package, "supplier"),
+ "PackageOriginator": (Package, "originator"),
+ "PackageDescription": (Package, "description"),
+ "PackageHomePage": (Package, "homepage"),
+ "SnippetSPDXID": (Snippet, "spdx_id"),
+ "SnippetFromFileSPDXID": (Snippet, "file_spdx_id"),
+ "SnippetName": (Snippet, "name"),
+ "SnippetComment": (Snippet, "comment"),
+ "SnippetCopyrightText": (Snippet, "copyright_text"),
+ "SnippetLicenseComments": (Snippet, "license_comment"),
+ "SnippetLicenseConcluded": (Snippet, "license_concluded"),
+ "SnippetByteRange": (Snippet, "byte_range"),
+ "SnippetLineRange": (Snippet, "line_range"),
+ "Annotator": (Annotation, "annotator"),
+ "SPDXREF": (Annotation, "spdx_id"),
+ "AnnotationComment": (Annotation, "annotation_comment"),
+ "LicenseID": (ExtractedLicensingInfo, "license_id"),
+ "ExtractedText": (ExtractedLicensingInfo, "extracted_text"),
+ "LicenseComment": (ExtractedLicensingInfo, "comment"),
+ "LicenseName": (ExtractedLicensingInfo, "license_name"),
+}
diff --git a/spdx/parsers/lexers/tagvalue.py b/src/spdx_tools/spdx/parser/tagvalue/lexer.py
similarity index 52%
rename from spdx/parsers/lexers/tagvalue.py
rename to src/spdx_tools/spdx/parser/tagvalue/lexer.py
index 83c709a0d..9a857827b 100644
--- a/spdx/parsers/lexers/tagvalue.py
+++ b/src/spdx_tools/spdx/parser/tagvalue/lexer.py
@@ -1,4 +1,6 @@
# Copyright (c) 2014 Ahmed H. Ismail
+# Copyright (c) 2023 spdx contributors
+# SPDX-License-Identifier: Apache-2.0
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -10,9 +12,10 @@
# limitations under the License.
from ply import lex
+from ply.lex import TOKEN
-class Lexer(object):
+class SPDXLexer:
reserved = {
# Top level fields
"SPDXVersion": "DOC_VERSION",
@@ -22,109 +25,80 @@ class Lexer(object):
"DocumentComment": "DOC_COMMENT",
"DocumentNamespace": "DOC_NAMESPACE",
"ExternalDocumentRef": "EXT_DOC_REF",
- # Creation info
+ # Creation info fields
"Creator": "CREATOR",
"Created": "CREATED",
"CreatorComment": "CREATOR_COMMENT",
- "LicenseListVersion": "LIC_LIST_VER",
- # Review info
- "Reviewer": "REVIEWER",
- "ReviewDate": "REVIEW_DATE",
- "ReviewComment": "REVIEW_COMMENT",
- # Annotation info
+ "LicenseListVersion": "LICENSE_LIST_VERSION",
+ # Annotation fields
"Annotator": "ANNOTATOR",
"AnnotationDate": "ANNOTATION_DATE",
"AnnotationComment": "ANNOTATION_COMMENT",
"AnnotationType": "ANNOTATION_TYPE",
"SPDXREF": "ANNOTATION_SPDX_ID",
- # Relationships
+ # Relationship fields
"Relationship": "RELATIONSHIP",
"RelationshipComment": "RELATIONSHIP_COMMENT",
- # Package Fields
+ # Package fields
"PackageName": "PKG_NAME",
"PackageVersion": "PKG_VERSION",
- "PackageDownloadLocation": "PKG_DOWN",
+ "PackageDownloadLocation": "PKG_DOWNLOAD_LOCATION",
"FilesAnalyzed": "PKG_FILES_ANALYZED",
- "PackageSummary": "PKG_SUM",
- "PackageSourceInfo": "PKG_SRC_INFO",
+ "PackageSummary": "PKG_SUMMARY",
+ "PackageSourceInfo": "PKG_SOURCE_INFO",
"PackageFileName": "PKG_FILE_NAME",
- "PackageSupplier": "PKG_SUPPL",
- "PackageOriginator": "PKG_ORIG",
- "PackageChecksum": "PKG_CHKSUM",
- "PackageVerificationCode": "PKG_VERF_CODE",
- "PackageDescription": "PKG_DESC",
+ "PackageSupplier": "PKG_SUPPLIER",
+ "PackageOriginator": "PKG_ORIGINATOR",
+ "PackageChecksum": "PKG_CHECKSUM",
+ "PackageVerificationCode": "PKG_VERIFICATION_CODE",
+ "PackageDescription": "PKG_DESCRIPTION",
"PackageComment": "PKG_COMMENT",
- "PackageLicenseDeclared": "PKG_LICS_DECL",
- "PackageLicenseConcluded": "PKG_LICS_CONC",
- "PackageLicenseInfoFromFiles": "PKG_LICS_FFILE",
- "PackageLicenseComments": "PKG_LICS_COMMENT",
- "PackageCopyrightText": "PKG_CPY_TEXT",
- "PackageHomePage": "PKG_HOME",
- "ExternalRef": "PKG_EXT_REF",
- "ExternalRefComment": "PKG_EXT_REF_COMMENT",
+ "PackageLicenseDeclared": "PKG_LICENSE_DECLARED",
+ "PackageLicenseConcluded": "PKG_LICENSE_CONCLUDED",
+ "PackageLicenseInfoFromFiles": "PKG_LICENSE_INFO",
+ "PackageLicenseComments": "PKG_LICENSE_COMMENT",
+ "PackageCopyrightText": "PKG_COPYRIGHT_TEXT",
+ "PackageHomePage": "PKG_HOMEPAGE",
+ "ExternalRef": "PKG_EXTERNAL_REF",
+ "ExternalRefComment": "PKG_EXTERNAL_REF_COMMENT",
"PackageAttributionText": "PKG_ATTRIBUTION_TEXT",
"PrimaryPackagePurpose": "PRIMARY_PACKAGE_PURPOSE",
"BuiltDate": "BUILT_DATE",
"ReleaseDate": "RELEASE_DATE",
"ValidUntilDate": "VALID_UNTIL_DATE",
- # Files
+ # File fields
"FileName": "FILE_NAME",
"FileType": "FILE_TYPE",
- "FileChecksum": "FILE_CHKSUM",
- "LicenseConcluded": "FILE_LICS_CONC",
- "LicenseInfoInFile": "FILE_LICS_INFO",
- "FileCopyrightText": "FILE_CR_TEXT",
- "LicenseComments": "FILE_LICS_COMMENT",
+ "FileChecksum": "FILE_CHECKSUM",
+ "LicenseConcluded": "FILE_LICENSE_CONCLUDED",
+ "LicenseInfoInFile": "FILE_LICENSE_INFO",
+ "FileCopyrightText": "FILE_COPYRIGHT_TEXT",
+ "LicenseComments": "FILE_LICENSE_COMMENT",
"FileComment": "FILE_COMMENT",
"FileNotice": "FILE_NOTICE",
- "FileContributor": "FILE_CONTRIB",
- "FileDependency": "FILE_DEP",
- "ArtifactOfProjectName": "ART_PRJ_NAME",
- "ArtifactOfProjectHomePage": "ART_PRJ_HOME",
- "ArtifactOfProjectURI": "ART_PRJ_URI",
+ "FileContributor": "FILE_CONTRIBUTOR",
"FileAttributionText": "FILE_ATTRIBUTION_TEXT",
- # License
- "LicenseID": "LICS_ID",
- "ExtractedText": "LICS_TEXT",
- "LicenseName": "LICS_NAME",
- "LicenseCrossReference": "LICS_CRS_REF",
- "LicenseComment": "LICS_COMMENT",
- # Snippet
+ # ExtractedLicensingInfo fields
+ "LicenseID": "LICENSE_ID",
+ "ExtractedText": "LICENSE_TEXT",
+ "LicenseName": "LICENSE_NAME",
+ "LicenseCrossReference": "LICENSE_CROSS_REF",
+ "LicenseComment": "LICENSE_COMMENT",
+ # Snippet fields
"SnippetSPDXID": "SNIPPET_SPDX_ID",
"SnippetName": "SNIPPET_NAME",
"SnippetComment": "SNIPPET_COMMENT",
- "SnippetCopyrightText": "SNIPPET_CR_TEXT",
- "SnippetLicenseComments": "SNIPPET_LICS_COMMENT",
+ "SnippetCopyrightText": "SNIPPET_COPYRIGHT_TEXT",
+ "SnippetLicenseComments": "SNIPPET_LICENSE_COMMENT",
"SnippetFromFileSPDXID": "SNIPPET_FILE_SPDXID",
- "SnippetLicenseConcluded": "SNIPPET_LICS_CONC",
- "LicenseInfoInSnippet": "SNIPPET_LICS_INFO",
+ "SnippetLicenseConcluded": "SNIPPET_LICENSE_CONCLUDED",
+ "LicenseInfoInSnippet": "SNIPPET_LICENSE_INFO",
"SnippetAttributionText": "SNIPPET_ATTRIBUTION_TEXT",
"SnippetByteRange": "SNIPPET_BYTE_RANGE",
"SnippetLineRange": "SNIPPET_LINE_RANGE",
- # Common
- "NOASSERTION": "NO_ASSERT",
- "UNKNOWN": "UN_KNOWN",
+ # Common fields
+ "NOASSERTION": "NO_ASSERTION",
"NONE": "NONE",
- "SOURCE": "SOURCE",
- "BINARY": "BINARY",
- "ARCHIVE": "ARCHIVE",
- "APPLICATION": "APPLICATION",
- "AUDIO": "AUDIO",
- "IMAGE": "IMAGE",
- "TEXT": "FILETYPE_TEXT",
- "VIDEO": "VIDEO",
- "DOCUMENTATION": "DOCUMENTATION",
- "SPDX": "SPDX",
- "OTHER": "OTHER",
- "REVIEW": "REVIEW",
- "FRAMEWORK": "FRAMEWORK",
- "LIBRARY": "LIBRARY",
- "CONTAINER": "CONTAINER",
- "OPERATING-SYSTEM": "OPERATING_SYSTEM",
- "DEVICE": "DEVICE",
- "FIRMWARE": "FIRMWARE",
- "FILE": "FILE",
- "INSTALL": "INSTALL"
}
states = (("text", "exclusive"),)
@@ -132,92 +106,73 @@ class Lexer(object):
"TEXT",
"TOOL_VALUE",
"UNKNOWN_TAG",
- "ORG_VALUE",
+ "ORGANIZATION_VALUE",
"PERSON_VALUE",
- "DATE",
+ "ISO8601_DATE",
"LINE",
- "RANGE",
- "CHKSUM",
- "DOC_REF_ID",
- "DOC_URI",
- "EXT_DOC_REF_CHKSUM",
+ "CHECKSUM",
] + list(reserved.values())
+ def __init__(self):
+ self.lexer = None
+
+ @TOKEN(r":\s*")
def t_text(self, t):
- r":\s*"
t.lexer.text_start = t.lexer.lexpos - len("")
t.lexer.begin("text")
+ @TOKEN(r" \s*")
def t_text_end(self, t):
- r" \s*"
t.type = "TEXT"
- t.value = t.lexer.lexdata[t.lexer.text_start: t.lexer.lexpos]
+ t.value = t.lexer.lexdata[t.lexer.text_start : t.lexer.lexpos]
t.lexer.lineno += t.value.count("\n")
t.value = t.value.strip()
t.lexer.begin("INITIAL")
return t
+ @TOKEN(r".|\n")
def t_text_any(self, t):
- r".|\n"
pass
def t_text_error(self, t):
print("Lexer error in text state")
- def t_CHKSUM(self, t):
- r":\s*(ADLER32|BLAKE2b-256|BLAKE2b-384|BLAKE2b-512|BLAKE3|MD2|MD4|MD5|MD6|" \
- "SHA1|SHA224|SHA256|SHA384|SHA512|SHA3-256|SHA3-384|SHA3-512):\s*([a-fA-F0-9]*)"
- t.value = t.value[1:].strip()
- return t
-
- def t_RANGE(self, t):
- r":\s*\d+:\d+"
- t.value = t.value[1:].strip()
- return t
-
- def t_DOC_REF_ID(self, t):
- r":\s*DocumentRef-([A-Za-z0-9\+\.\-]+)"
- t.value = t.value[1:].strip()
- return t
-
- def t_DOC_URI(self, t):
- r"\s*((ht|f)tps?:\/\/\S*)"
- t.value = t.value.strip()
- return t
-
- def t_EXT_DOC_REF_CHKSUM(self, t):
- r"\s*SHA1:\s*[a-f0-9]{40,40}"
+ @TOKEN(
+ r":\s*(ADLER32|BLAKE2b-256|BLAKE2b-384|BLAKE2b-512|BLAKE3|MD2|MD4|MD5|MD6|SHA1|SHA224|SHA256|SHA384|SHA512|"
+ r"SHA3-256|SHA3-384|SHA3-512):\s*([a-f0-9]*)"
+ )
+ def t_CHECKSUM(self, t):
t.value = t.value[1:].strip()
return t
+ @TOKEN(r":\s*Tool:.+")
def t_TOOL_VALUE(self, t):
- r":\s*Tool:.+"
t.value = t.value[1:].strip()
return t
- def t_ORG_VALUE(self, t):
- r":\s*Organization:.+"
+ @TOKEN(r":\s*Organization:.+")
+ def t_ORGANIZATION_VALUE(self, t):
t.value = t.value[1:].strip()
return t
+ @TOKEN(r":\s*Person:.+")
def t_PERSON_VALUE(self, t):
- r":\s*Person:.+"
t.value = t.value[1:].strip()
return t
- def t_DATE(self, t):
- r":\s*\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ"
+ @TOKEN(r":\s*\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ")
+ def t_ISO8601_DATE(self, t):
t.value = t.value[1:].strip()
return t
+ @TOKEN(r"[a-zA-Z]+")
def t_KEYWORD_AS_TAG(self, t):
- r"[a-zA-Z]+"
t.type = self.reserved.get(t.value, "UNKNOWN_TAG")
t.value = t.value.strip()
return t
+ @TOKEN(r":.+")
def t_LINE_OR_KEYWORD_VALUE(self, t):
- r":.+"
t.value = t.value[1:].strip()
if t.value in self.reserved.keys():
t.type = self.reserved[t.value]
@@ -225,16 +180,16 @@ def t_LINE_OR_KEYWORD_VALUE(self, t):
t.type = "LINE"
return t
+ @TOKEN(r"\#.*")
def t_comment(self, t):
- r"\#.*"
pass
+ @TOKEN(r"\n+")
def t_newline(self, t):
- r"\n+"
t.lexer.lineno += len(t.value)
+ @TOKEN(r"[ \t]+")
def t_whitespace(self, t):
- r"\s+"
pass
def build(self, **kwargs):
diff --git a/src/spdx_tools/spdx/parser/tagvalue/parser.py b/src/spdx_tools/spdx/parser/tagvalue/parser.py
new file mode 100644
index 000000000..50096bda2
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/tagvalue/parser.py
@@ -0,0 +1,608 @@
+# Copyright (c) 2014 Ahmed H. Ismail
+# Copyright (c) 2023 spdx contributors
+# SPDX-License-Identifier: Apache-2.0
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+
+from beartype.typing import Any, Dict, List
+from license_expression import ExpressionError, get_spdx_licensing
+from ply import yacc
+from ply.yacc import LRParser
+
+from spdx_tools.spdx.datetime_conversions import datetime_from_str
+from spdx_tools.spdx.model import (
+ Annotation,
+ AnnotationType,
+ CreationInfo,
+ Document,
+ ExternalDocumentRef,
+ ExternalPackageRef,
+ ExternalPackageRefCategory,
+ ExtractedLicensingInfo,
+ File,
+ FileType,
+ Package,
+ PackagePurpose,
+ PackageVerificationCode,
+ Relationship,
+ RelationshipType,
+ Snippet,
+ SpdxNoAssertion,
+ SpdxNone,
+ Version,
+)
+from spdx_tools.spdx.parser.actor_parser import ActorParser
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.logger import Logger
+from spdx_tools.spdx.parser.parsing_functions import (
+ construct_or_raise_parsing_error,
+ raise_parsing_error_if_logger_has_messages,
+)
+from spdx_tools.spdx.parser.tagvalue.helper_methods import (
+ TAG_DATA_MODEL_FIELD,
+ grammar_rule,
+ parse_checksum,
+ set_value,
+ str_from_text,
+)
+from spdx_tools.spdx.parser.tagvalue.lexer import SPDXLexer
+
+CLASS_MAPPING = dict(
+ File="files",
+ Annotation="annotations",
+ Relationship="relationships",
+ Snippet="snippets",
+ Package="packages",
+ ExtractedLicensingInfo="extracted_licensing_info",
+)
+ELEMENT_EXPECTED_START_TAG = dict(
+ File="FileName",
+ Annotation="Annotator",
+ Relationship="Relationship",
+ Snippet="SnippetSPDXID",
+ Package="PackageName",
+ ExtractedLicensingInfo="LicenseID",
+)
+
+
+class Parser:
+ tokens: List[str]
+ logger: Logger
+ current_element: Dict[str, Any]
+ creation_info: Dict[str, Any]
+ elements_built: Dict[str, Any]
+ lex: SPDXLexer
+ yacc: LRParser
+
+ def __init__(self, **kwargs):
+ self.tokens = SPDXLexer.tokens
+ self.logger = Logger()
+ self.current_element = {"logger": Logger()}
+ self.creation_info = {"logger": Logger()}
+ self.elements_built = dict()
+ self.lex = SPDXLexer()
+ self.lex.build(reflags=re.UNICODE)
+ self.yacc = yacc.yacc(module=self, **kwargs)
+
+ @grammar_rule("start : start attrib ")
+ def p_start_start_attrib(self, p):
+ pass
+
+ @grammar_rule("start : attrib ")
+ def p_start_attrib(self, p):
+ pass
+
+ @grammar_rule(
+ "attrib : spdx_version\n| spdx_id\n| data_license\n| doc_name\n| document_comment\n| document_namespace\n| "
+ "creator\n| created\n| creator_comment\n| license_list_version\n| ext_doc_ref\n"
+ # attributes for file
+ "| file_name\n| file_type\n| file_checksum\n| file_license_concluded\n| file_license_info\n"
+ "| file_copyright_text\n| file_license_comment\n| file_attribution_text\n| file_notice\n| file_comment\n"
+ "| file_contributor\n"
+ # attributes for annotation
+ "| annotator\n| annotation_date\n| annotation_comment\n| annotation_type\n| annotation_spdx_id\n"
+ # attributes for relationship
+ "| relationship\n"
+ # attributes for snippet
+ "| snippet_spdx_id\n| snippet_name\n| snippet_comment\n| snippet_attribution_text\n| snippet_copyright_text\n"
+ "| snippet_license_comment\n| file_spdx_id\n| snippet_license_concluded\n| snippet_license_info\n"
+ "| snippet_byte_range\n| snippet_line_range\n"
+ # attributes for package
+ "| package_name\n| package_version\n| download_location\n| files_analyzed\n| homepage\n"
+ "| summary\n| source_info\n| pkg_file_name\n| supplier\n| originator\n| pkg_checksum\n"
+ "| verification_code\n| description\n| pkg_comment\n| pkg_attribution_text\n| pkg_license_declared\n"
+ "| pkg_license_concluded\n| pkg_license_info\n| pkg_license_comment\n| pkg_copyright_text\n"
+ "| pkg_external_ref\n| primary_package_purpose\n| built_date\n| release_date\n| valid_until_date\n"
+ # attributes for extracted licensing info
+ "| license_id\n| extracted_text\n| license_name\n| license_cross_ref\n| lic_comment\n"
+ "| unknown_tag "
+ )
+ def p_attrib(self, p):
+ pass
+
+ # general parsing methods
+ @grammar_rule(
+ "license_id : LICENSE_ID error\n license_cross_ref : LICENSE_CROSS_REF error\n "
+ "lic_comment : LICENSE_COMMENT error\n license_name : LICENSE_NAME error\n "
+ "extracted_text : LICENSE_TEXT error\n "
+ "file_name : FILE_NAME error\n file_contributor : FILE_CONTRIBUTOR error\n "
+ "file_notice : FILE_NOTICE error\n file_copyright_text : FILE_COPYRIGHT_TEXT error\n "
+ "file_license_comment : FILE_LICENSE_COMMENT error\n "
+ "file_license_info : FILE_LICENSE_INFO error\n file_comment : FILE_COMMENT error\n "
+ "file_checksum : FILE_CHECKSUM error\n file_license_concluded : FILE_LICENSE_CONCLUDED error\n "
+ "file_type : FILE_TYPE error\n file_attribution_text : FILE_ATTRIBUTION_TEXT error\n "
+ "package_name : PKG_NAME error\n pkg_attribution_text : PKG_ATTRIBUTION_TEXT error\n "
+ "description : PKG_DESCRIPTION error\n pkg_comment : PKG_COMMENT error\n "
+ "summary : PKG_SUMMARY error\n pkg_copyright_text : PKG_COPYRIGHT_TEXT error\n "
+ "pkg_external_ref : PKG_EXTERNAL_REF error\n pkg_license_comment : PKG_LICENSE_COMMENT error\n "
+ "pkg_license_declared : PKG_LICENSE_DECLARED error\n pkg_license_info : PKG_LICENSE_INFO error \n "
+ "pkg_license_concluded : PKG_LICENSE_CONCLUDED error\n source_info : PKG_SOURCE_INFO error\n "
+ "homepage : PKG_HOMEPAGE error\n pkg_checksum : PKG_CHECKSUM error\n "
+ "verification_code : PKG_VERIFICATION_CODE error\n originator : PKG_ORIGINATOR error\n "
+ "download_location : PKG_DOWNLOAD_LOCATION error\n files_analyzed : PKG_FILES_ANALYZED error\n "
+ "supplier : PKG_SUPPLIER error\n pkg_file_name : PKG_FILE_NAME error\n "
+ "package_version : PKG_VERSION error\n primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error\n "
+ "built_date : BUILT_DATE error\n release_date : RELEASE_DATE error\n "
+ "valid_until_date : VALID_UNTIL_DATE error\n snippet_spdx_id : SNIPPET_SPDX_ID error\n "
+ "snippet_name : SNIPPET_NAME error\n snippet_comment : SNIPPET_COMMENT error\n "
+ "snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error\n "
+ "snippet_copyright_text : SNIPPET_COPYRIGHT_TEXT error\n "
+ "snippet_license_comment : SNIPPET_LICENSE_COMMENT error\n file_spdx_id : SNIPPET_FILE_SPDXID error\n "
+ "snippet_license_concluded : SNIPPET_LICENSE_CONCLUDED error\n "
+ "snippet_license_info : SNIPPET_LICENSE_INFO error\n "
+ "snippet_byte_range : SNIPPET_BYTE_RANGE error\n snippet_line_range : SNIPPET_LINE_RANGE error\n "
+ "annotator : ANNOTATOR error\n annotation_date : ANNOTATION_DATE error\n "
+ "annotation_comment : ANNOTATION_COMMENT error\n annotation_type : ANNOTATION_TYPE error\n "
+ "annotation_spdx_id : ANNOTATION_SPDX_ID error\n relationship : RELATIONSHIP error"
+ )
+ def p_current_element_error(self, p):
+ if p[1] in ELEMENT_EXPECTED_START_TAG.values():
+ self.initialize_new_current_element(TAG_DATA_MODEL_FIELD[p[1]][0])
+ self.current_element["logger"].append(
+ f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}"
+ )
+
+ @grammar_rule(
+ "license_name : LICENSE_NAME line_or_no_assertion\n extracted_text : LICENSE_TEXT text_or_line\n "
+ "lic_comment : LICENSE_COMMENT text_or_line\n license_id : LICENSE_ID LINE\n "
+ "file_name : FILE_NAME LINE \n file_notice : FILE_NOTICE text_or_line\n "
+ "file_copyright_text : FILE_COPYRIGHT_TEXT line_or_no_assertion_or_none\n "
+ "file_license_comment : FILE_LICENSE_COMMENT text_or_line\n "
+ "file_comment : FILE_COMMENT text_or_line\n "
+ "file_license_concluded : FILE_LICENSE_CONCLUDED license_or_no_assertion_or_none\n "
+ "package_name : PKG_NAME LINE\n description : PKG_DESCRIPTION text_or_line\n "
+ "summary : PKG_SUMMARY text_or_line\n source_info : PKG_SOURCE_INFO text_or_line\n "
+ "homepage : PKG_HOMEPAGE line_or_no_assertion_or_none\n "
+ "download_location : PKG_DOWNLOAD_LOCATION line_or_no_assertion_or_none\n "
+ "originator : PKG_ORIGINATOR actor_or_no_assertion\n supplier : PKG_SUPPLIER actor_or_no_assertion\n "
+ "pkg_comment : PKG_COMMENT text_or_line\n "
+ "pkg_copyright_text : PKG_COPYRIGHT_TEXT line_or_no_assertion_or_none\n "
+ "pkg_license_declared : PKG_LICENSE_DECLARED license_or_no_assertion_or_none\n "
+ "pkg_file_name : PKG_FILE_NAME LINE\n "
+ "pkg_license_concluded : PKG_LICENSE_CONCLUDED license_or_no_assertion_or_none\n "
+ "package_version : PKG_VERSION LINE\n pkg_license_comment : PKG_LICENSE_COMMENT text_or_line\n "
+ "snippet_spdx_id : SNIPPET_SPDX_ID LINE\n snippet_name : SNIPPET_NAME LINE\n "
+ "snippet_comment : SNIPPET_COMMENT text_or_line\n "
+ "snippet_copyright_text : SNIPPET_COPYRIGHT_TEXT line_or_no_assertion_or_none\n "
+ "snippet_license_comment : SNIPPET_LICENSE_COMMENT text_or_line\n "
+ "file_spdx_id : SNIPPET_FILE_SPDXID LINE\n "
+ "snippet_license_concluded : SNIPPET_LICENSE_CONCLUDED license_or_no_assertion_or_none\n "
+ "annotation_spdx_id : ANNOTATION_SPDX_ID LINE\n "
+ "annotation_comment : ANNOTATION_COMMENT text_or_line"
+ )
+ def p_generic_value(self, p):
+ if p[1] in ELEMENT_EXPECTED_START_TAG.values():
+ self.initialize_new_current_element(TAG_DATA_MODEL_FIELD[p[1]][0])
+ if self.check_that_current_element_matches_class_for_value(TAG_DATA_MODEL_FIELD[p[1]][0], p.lineno(1)):
+ set_value(p, self.current_element)
+
+ @grammar_rule(
+ "unknown_tag : UNKNOWN_TAG text_or_line\n | UNKNOWN_TAG ISO8601_DATE\n | UNKNOWN_TAG PERSON_VALUE \n"
+ "| UNKNOWN_TAG"
+ )
+ def p_unknown_tag(self, p):
+ self.logger.append(f"Unknown tag provided in line {p.lineno(1)}")
+
+ @grammar_rule("text_or_line : TEXT\n line_or_no_assertion_or_none : TEXT")
+ def p_text(self, p):
+ p[0] = str_from_text(p[1])
+
+ @grammar_rule(
+ "text_or_line : LINE\n line_or_no_assertion : LINE\nline_or_no_assertion_or_none : LINE\n"
+ "text_or_line : NO_ASSERTION\n text_or_line : NONE"
+ )
+ def p_line(self, p):
+ p[0] = p[1]
+
+ @grammar_rule(
+ "license_or_no_assertion_or_none : NO_ASSERTION\n actor_or_no_assertion : NO_ASSERTION\n"
+ "line_or_no_assertion : NO_ASSERTION\n line_or_no_assertion_or_none : NO_ASSERTION"
+ )
+ def p_no_assertion(self, p):
+ p[0] = SpdxNoAssertion()
+
+ @grammar_rule("license_or_no_assertion_or_none : NONE\n line_or_no_assertion_or_none : NONE")
+ def p_none(self, p):
+ p[0] = SpdxNone()
+
+ @grammar_rule("license_or_no_assertion_or_none : LINE")
+ def p_license(self, p):
+ try:
+ p[0] = get_spdx_licensing().parse(p[1])
+ except ExpressionError as err:
+ error_message = f"Error while parsing license expression: {p[1]}"
+ if err.args:
+ error_message += f": {err.args[0]}"
+ self.current_element["logger"].append(error_message)
+
+ @grammar_rule("actor_or_no_assertion : PERSON_VALUE\n | ORGANIZATION_VALUE")
+ def p_actor_values(self, p):
+ p[0] = ActorParser.parse_actor(p[1])
+
+ @grammar_rule("spdx_id : SPDX_ID LINE")
+ def p_spdx_id(self, p):
+ # As all SPDX Ids share the same tag, there is no knowing which spdx_id belongs to the document.
+ # We assume that to be the first spdx_id we encounter. As the specification does not explicitly require this,
+ # our approach might lead to unwanted behavior when the document's SPDX Id is defined later in the document.
+ if "spdx_id" in self.creation_info:
+ self.current_element["spdx_id"] = p[2]
+ else:
+ self.creation_info["spdx_id"] = p[2]
+
+ # parsing methods for creation info / document level
+
+ @grammar_rule(
+ "license_list_version : LICENSE_LIST_VERSION error\n document_comment : DOC_COMMENT error\n "
+ "document_namespace : DOC_NAMESPACE error\n data_license : DOC_LICENSE error\n "
+ "doc_name : DOC_NAME error\n ext_doc_ref : EXT_DOC_REF error\n spdx_version : DOC_VERSION error\n "
+ "creator_comment : CREATOR_COMMENT error\n creator : CREATOR error\n created : CREATED error"
+ )
+ def p_creation_info_value_error(self, p):
+ self.creation_info["logger"].append(
+ f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}"
+ )
+
+ @grammar_rule(
+ "document_comment : DOC_COMMENT text_or_line\n document_namespace : DOC_NAMESPACE LINE\n "
+ "data_license : DOC_LICENSE LINE\n spdx_version : DOC_VERSION LINE\n "
+ "creator_comment : CREATOR_COMMENT text_or_line\n doc_name : DOC_NAME LINE"
+ )
+ def p_generic_value_creation_info(self, p):
+ set_value(p, self.creation_info)
+
+ @grammar_rule("license_list_version : LICENSE_LIST_VERSION LINE")
+ def p_license_list_version(self, p):
+ set_value(p, self.creation_info, method_to_apply=Version.from_string)
+
+ @grammar_rule("ext_doc_ref : EXT_DOC_REF LINE")
+ def p_external_document_ref(self, p):
+ external_doc_ref_regex = re.compile(r"(.*)(\s*SHA1:\s*[a-f0-9]{40})")
+ external_doc_ref_match = external_doc_ref_regex.match(p[2])
+ if not external_doc_ref_match:
+ self.creation_info["logger"].append(
+ f"Error while parsing ExternalDocumentRef: Couldn't match Checksum. Line: {p.lineno(1)}"
+ )
+ return
+ try:
+ document_ref_id, document_uri = external_doc_ref_match.group(1).strip().split(" ")
+ except ValueError:
+ self.creation_info["logger"].append(
+ f"Error while parsing ExternalDocumentRef: Couldn't split the first part of the value into "
+ f"document_ref_id and document_uri. Line: {p.lineno(1)}"
+ )
+ return
+ checksum = parse_checksum(external_doc_ref_match.group(2).strip())
+ external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum)
+ self.creation_info.setdefault("external_document_refs", []).append(external_document_ref)
+
+ @grammar_rule("creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORGANIZATION_VALUE")
+ def p_creator(self, p):
+ self.creation_info.setdefault("creators", []).append(ActorParser.parse_actor(p[2]))
+
+ @grammar_rule("created : CREATED ISO8601_DATE")
+ def p_created(self, p):
+ set_value(p, self.creation_info, method_to_apply=datetime_from_str)
+
+ # parsing methods for extracted licensing info
+
+ @grammar_rule("license_cross_ref : LICENSE_CROSS_REF LINE")
+ def p_extracted_cross_reference(self, p):
+ if self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)):
+ self.current_element.setdefault("cross_references", []).append(p[2])
+
+ # parsing methods for file
+
+ @grammar_rule("file_contributor : FILE_CONTRIBUTOR LINE")
+ def p_file_contributor(self, p):
+ if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)):
+ self.current_element.setdefault("contributors", []).append(p[2])
+
+ @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line")
+ def p_file_attribution_text(self, p):
+ if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)):
+ self.current_element.setdefault("attribution_texts", []).append(p[2])
+
+ @grammar_rule("file_license_info : FILE_LICENSE_INFO license_or_no_assertion_or_none")
+ def p_file_license_info(self, p):
+ if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)):
+ self.current_element.setdefault("license_info_in_file", []).append(p[2])
+
+ @grammar_rule("file_type : FILE_TYPE LINE")
+ def p_file_type(self, p):
+ if not self.check_that_current_element_matches_class_for_value(File, p.lineno(1)):
+ return
+ try:
+ file_type = FileType[p[2].strip()]
+ except KeyError:
+ self.current_element["logger"].append(f"Invalid FileType: {p[2]}. Line {p.lineno(1)}")
+ return
+ self.current_element.setdefault("file_types", []).append(file_type)
+
+ @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM")
+ def p_file_checksum(self, p):
+ if not self.check_that_current_element_matches_class_for_value(File, p.lineno(1)):
+ return
+ checksum = parse_checksum(p[2])
+ self.current_element.setdefault("checksums", []).append(checksum)
+
+ # parsing methods for package
+
+ @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line")
+ def p_pkg_attribution_text(self, p):
+ self.check_that_current_element_matches_class_for_value(Package, p.lineno(1))
+ self.current_element.setdefault("attribution_texts", []).append(p[2])
+
+ @grammar_rule(
+ "pkg_external_ref : PKG_EXTERNAL_REF LINE PKG_EXTERNAL_REF_COMMENT text_or_line\n | PKG_EXTERNAL_REF LINE"
+ )
+ def p_pkg_external_refs(self, p):
+ if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)):
+ return
+ try:
+ category, reference_type, locator = p[2].split(" ")
+ except ValueError:
+ self.current_element["logger"].append(
+ f"Couldn't split PackageExternalRef in category, reference_type and locator. Line: {p.lineno(1)}"
+ )
+ return
+ comment = None
+ if len(p) == 5:
+ comment = p[4]
+ try:
+ category = ExternalPackageRefCategory[category.replace("-", "_")]
+ except KeyError:
+ self.current_element["logger"].append(
+ f"Invalid ExternalPackageRefCategory: {category}. Line: {p.lineno(1)}"
+ )
+ return
+ try:
+ external_package_ref = construct_or_raise_parsing_error(
+ ExternalPackageRef,
+ {"category": category, "reference_type": reference_type, "locator": locator, "comment": comment},
+ )
+ except SPDXParsingError as err:
+ self.current_element["logger"].append(err.get_messages())
+ return
+ self.current_element.setdefault("external_references", []).append(external_package_ref)
+
+ @grammar_rule("pkg_license_info : PKG_LICENSE_INFO license_or_no_assertion_or_none")
+ def p_pkg_license_info_from_file(self, p):
+ if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)):
+ self.current_element.setdefault("license_info_from_files", []).append(p[2])
+
+ @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM")
+ def p_pkg_checksum(self, p):
+ if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)):
+ return
+ checksum = parse_checksum(p[2])
+ self.current_element.setdefault("checksums", []).append(checksum)
+
+ @grammar_rule("verification_code : PKG_VERIFICATION_CODE LINE")
+ def p_pkg_verification_code(self, p):
+ if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)):
+ return
+
+ if "verification_code" in self.current_element:
+ self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}")
+ return
+ verif_code_regex = re.compile(r"([0-9a-f]{40})\s*(\(excludes:\s*(.+)\))?", re.UNICODE)
+ verif_code_code_grp = 1
+ verif_code_exc_files_grp = 3
+ match = verif_code_regex.match(p[2])
+ if not match:
+ self.current_element["logger"].append(
+ f"Error while parsing {p[1]}: Value did not match expected format. Line: {p.lineno(1)}"
+ )
+ return
+ value = match.group(verif_code_code_grp)
+ excluded_files = None
+ if match.group(verif_code_exc_files_grp):
+ excluded_files = match.group(verif_code_exc_files_grp).split(",")
+ self.current_element["verification_code"] = PackageVerificationCode(value, excluded_files)
+
+ @grammar_rule("files_analyzed : PKG_FILES_ANALYZED LINE")
+ def p_pkg_files_analyzed(self, p):
+ if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)):
+ return
+ if "files_analyzed" in self.current_element:
+ self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}")
+ return
+ if p[2] == "true":
+ self.current_element["files_analyzed"] = True
+ elif p[2] == "false":
+ self.current_element["files_analyzed"] = False
+ else:
+ self.current_element["logger"].append(
+ f'The value of FilesAnalyzed must be either "true" or "false", but is: {p[2]}'
+ )
+
+ @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE LINE")
+ def p_primary_package_purpose(self, p):
+ if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)):
+ set_value(p, self.current_element, method_to_apply=lambda x: PackagePurpose[x.replace("-", "_")])
+
+ @grammar_rule(
+ "built_date : BUILT_DATE ISO8601_DATE\n release_date : RELEASE_DATE ISO8601_DATE\n "
+ "valid_until_date : VALID_UNTIL_DATE ISO8601_DATE"
+ )
+ def p_package_dates(self, p):
+ if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)):
+ set_value(p, self.current_element, method_to_apply=datetime_from_str)
+
+ # parsing methods for snippet
+
+ @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line")
+ def p_snippet_attribution_text(self, p):
+ if self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)):
+ self.current_element.setdefault("attribution_texts", []).append(p[2])
+
+ @grammar_rule("snippet_license_info : SNIPPET_LICENSE_INFO license_or_no_assertion_or_none")
+ def p_snippet_license_info(self, p):
+ if self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)):
+ self.current_element.setdefault("license_info_in_snippet", []).append(p[2])
+
+ @grammar_rule("snippet_byte_range : SNIPPET_BYTE_RANGE LINE\n snippet_line_range : SNIPPET_LINE_RANGE LINE")
+ def p_snippet_range(self, p):
+ if not self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)):
+ return
+
+ argument_name = TAG_DATA_MODEL_FIELD[p[1]][1]
+ if argument_name in self.current_element:
+ self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}")
+ return
+ range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE)
+ if not range_re.match(p[2].strip()):
+ self.current_element["logger"].append(
+ f"Value for {p[1]} doesn't match valid range pattern. " f"Line: {p.lineno(1)}"
+ )
+ return
+ startpoint = int(p[2].split(":")[0])
+ endpoint = int(p[2].split(":")[-1])
+ self.current_element[argument_name] = startpoint, endpoint
+
+ # parsing methods for annotation
+
+ @grammar_rule("annotator : ANNOTATOR PERSON_VALUE\n| ANNOTATOR TOOL_VALUE\n| ANNOTATOR ORGANIZATION_VALUE")
+ def p_annotator(self, p):
+ self.initialize_new_current_element(Annotation)
+ set_value(p, self.current_element, method_to_apply=ActorParser.parse_actor)
+
+ @grammar_rule("annotation_date : ANNOTATION_DATE ISO8601_DATE")
+ def p_annotation_date(self, p):
+ if self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)):
+ set_value(p, self.current_element, method_to_apply=datetime_from_str)
+
+ @grammar_rule("annotation_type : ANNOTATION_TYPE LINE")
+ def p_annotation_type(self, p):
+ if self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)):
+ set_value(p, self.current_element, method_to_apply=lambda x: AnnotationType[x])
+
+ # parsing methods for relationship
+
+ @grammar_rule("relationship : RELATIONSHIP LINE RELATIONSHIP_COMMENT text_or_line\n " "| RELATIONSHIP LINE")
+ def p_relationship(self, p):
+ self.initialize_new_current_element(Relationship)
+ try:
+ spdx_element_id, relationship_type, related_spdx_element_id = p[2].split(" ")
+ except ValueError:
+ self.current_element["logger"].append(
+ f"Relationship couldn't be split in spdx_element_id, relationship_type and "
+ f"related_spdx_element. Line: {p.lineno(1)}"
+ )
+ return
+ try:
+ self.current_element["relationship_type"] = RelationshipType[relationship_type]
+ except KeyError:
+ self.current_element["logger"].append(f"Invalid RelationshipType {relationship_type}. Line: {p.lineno(1)}")
+ if related_spdx_element_id == "NONE":
+ related_spdx_element_id = SpdxNone()
+ if related_spdx_element_id == "NOASSERTION":
+ related_spdx_element_id = SpdxNoAssertion()
+ self.current_element["related_spdx_element_id"] = related_spdx_element_id
+ self.current_element["spdx_element_id"] = spdx_element_id
+ if len(p) == 5:
+ self.current_element["comment"] = p[4]
+
+ def p_error(self, p):
+ pass
+
+ def parse(self, text):
+ # entry point for the tag-value parser
+ self.yacc.parse(text, lexer=self.lex)
+ # this constructs the last remaining element; all other elements are constructed at the start of
+ # their subsequent element
+ self.construct_current_element()
+
+ # To be able to parse creation info values if they appear in between other elements, e.g. packages, we use
+ # two different dictionaries to collect the creation info and all other elements. Therefore, we have a separate
+ # logger for the creation info whose messages we need to add to the main logger to than raise all collected
+ # messages at once.
+ creation_info_logger = self.creation_info.pop("logger")
+ if creation_info_logger.has_messages():
+ self.logger.extend([f"Error while parsing CreationInfo: {creation_info_logger.get_messages()}"])
+
+ raise_parsing_error_if_logger_has_messages(self.logger)
+ creation_info = construct_or_raise_parsing_error(CreationInfo, self.creation_info)
+ self.elements_built["creation_info"] = creation_info
+ document = construct_or_raise_parsing_error(Document, self.elements_built)
+ return document
+
+ def initialize_new_current_element(self, clazz: Any):
+ self.construct_current_element()
+ self.current_element["class"] = clazz
+
+ def check_that_current_element_matches_class_for_value(self, expected_class, line_number) -> bool:
+ if "class" not in self.current_element or expected_class != self.current_element["class"]:
+ self.logger.append(
+ f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to "
+ f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing. "
+ f"Line: {line_number}"
+ )
+ return False
+ return True
+
+ def construct_current_element(self):
+ if "class" not in self.current_element:
+ # This happens when the first element is initialized via initialize_new_current_element() or if the first
+ # element is missing its expected starting tag. In both cases we are unable to construct an element.
+ return
+
+ clazz = self.current_element.pop("class")
+ try:
+ raise_parsing_error_if_logger_has_messages(self.current_element.pop("logger"), clazz.__name__)
+ self.elements_built.setdefault(CLASS_MAPPING[clazz.__name__], []).append(
+ construct_or_raise_parsing_error(clazz, self.current_element)
+ )
+ if clazz == File:
+ self.check_for_preceding_package_and_build_contains_relationship()
+ except SPDXParsingError as err:
+ self.logger.extend(err.get_messages())
+ self.current_element = {"logger": Logger()}
+
+ def check_for_preceding_package_and_build_contains_relationship(self):
+ file_spdx_id = self.current_element["spdx_id"]
+ if "packages" not in self.elements_built:
+ return
+ # We assume that all files that are not contained in a package precede any package information. Any file
+ # information that follows any package information is assigned to the last parsed package by creating a
+ # corresponding contains relationship.
+ # (see https://spdx.github.io/spdx-spec/v2.3/composition-of-an-SPDX-document/#5.2.2)
+ if not self.elements_built["packages"]:
+ self.logger.append(
+ f"Error while building contains relationship for file {file_spdx_id}, "
+ f"preceding package was not parsed successfully."
+ )
+ return
+ package_spdx_id = self.elements_built["packages"][-1].spdx_id
+ relationship = Relationship(package_spdx_id, RelationshipType.CONTAINS, file_spdx_id)
+ if relationship not in self.elements_built.setdefault("relationships", []):
+ self.elements_built["relationships"].append(relationship)
diff --git a/src/spdx_tools/spdx/parser/tagvalue/tagvalue_parser.py b/src/spdx_tools/spdx/parser/tagvalue/tagvalue_parser.py
new file mode 100644
index 000000000..c281279e9
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/tagvalue/tagvalue_parser.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.tagvalue.parser import Parser
+
+
+def parse_from_file(file_name: str, encoding: str = "utf-8") -> Document:
+ parser = Parser()
+ with open(file_name, encoding=encoding) as file:
+ data = file.read()
+ document: Document = parser.parse(data)
+ return document
diff --git a/src/spdx_tools/spdx/parser/xml/__init__.py b/src/spdx_tools/spdx/parser/xml/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/parser/xml/xml_parser.py b/src/spdx_tools/spdx/parser/xml/xml_parser.py
new file mode 100644
index 000000000..ba7ff3491
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/xml/xml_parser.py
@@ -0,0 +1,71 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import xmltodict
+from beartype.typing import Any, Dict
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.error import SPDXParsingError
+from spdx_tools.spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser
+
+LIST_LIKE_FIELDS = [
+ "creators",
+ "externalDocumentRefs",
+ "hasExtractedLicensingInfos",
+ "seeAlsos",
+ "annotations",
+ "relationships",
+ "snippets",
+ "reviewers",
+ "fileTypes",
+ "licenseInfoFromFiles",
+ "licenseInfoInFiles",
+ "artifactOf",
+ "fileContributors",
+ "fileDependencies",
+ "files",
+ "documentDescribes",
+ "packages",
+ "checksums",
+ "hasFiles",
+ "externalRefs",
+ "ranges",
+ "licenseInfoInSnippets",
+ "packageVerificationCodeExcludedFiles",
+ "attributionTexts",
+]
+
+
+def parse_from_file(file_name: str, encoding: str = "utf-8") -> Document:
+ with open(file_name, encoding=encoding) as file:
+ parsed_xml: Dict = xmltodict.parse(file.read(), encoding="utf-8")
+
+ input_doc_as_dict: Dict = _fix_list_like_fields(parsed_xml).get("Document")
+
+ if not input_doc_as_dict:
+ raise SPDXParsingError(['Did not find the XML top level tag "Document".'])
+
+ return JsonLikeDictParser().parse(input_doc_as_dict)
+
+
+def _fix_list_like_fields(data: Any) -> Any:
+ """
+ XML files do not contain lists. Thus, single fields that should be a list in SPDX have to be manually cast.
+ This method takes a parsed dictionary and converts all values with key from LIST_LIKE_FIELDS to lists.
+ """
+ if isinstance(data, dict):
+ new_data = {}
+ for key, value in data.items():
+ if key in LIST_LIKE_FIELDS and not isinstance(value, list):
+ new_data[key] = [_fix_list_like_fields(value)] if value else []
+ else:
+ new_data[key] = _fix_list_like_fields(value)
+ return new_data
+
+ if isinstance(data, list):
+ new_data = []
+ for element in data:
+ new_data.append(_fix_list_like_fields(element))
+ return new_data
+
+ return data
diff --git a/src/spdx_tools/spdx/parser/yaml/__init__.py b/src/spdx_tools/spdx/parser/yaml/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/parser/yaml/yaml_parser.py b/src/spdx_tools/spdx/parser/yaml/yaml_parser.py
new file mode 100644
index 000000000..98fbd7ce8
--- /dev/null
+++ b/src/spdx_tools/spdx/parser/yaml/yaml_parser.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import yaml
+from beartype.typing import Dict
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser
+
+
+def parse_from_file(file_name: str, encoding: str = "utf-8") -> Document:
+ with open(file_name, encoding=encoding) as file:
+ input_doc_as_dict: Dict = yaml.safe_load(file)
+
+ return JsonLikeDictParser().parse(input_doc_as_dict)
diff --git a/src/spdx_tools/spdx/py.typed b/src/spdx_tools/spdx/py.typed
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/rdfschema/__init__.py b/src/spdx_tools/spdx/rdfschema/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/rdfschema/namespace.py b/src/spdx_tools/spdx/rdfschema/namespace.py
new file mode 100644
index 000000000..5f330fb82
--- /dev/null
+++ b/src/spdx_tools/spdx/rdfschema/namespace.py
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import Namespace
+
+SPDX_NAMESPACE = Namespace("http://spdx.org/rdf/terms#")
+POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#")
+REFERENCE_NAMESPACE = Namespace("http://spdx.org/rdf/references/")
+LICENSE_NAMESPACE = Namespace("http://spdx.org/licenses/")
diff --git a/src/spdx_tools/spdx/spdx_element_utils.py b/src/spdx_tools/spdx/spdx_element_utils.py
new file mode 100644
index 000000000..0d6bd8946
--- /dev/null
+++ b/src/spdx_tools/spdx/spdx_element_utils.py
@@ -0,0 +1,91 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import hashlib
+
+from beartype.typing import List, Optional, Type, Union
+
+from spdx_tools.spdx.model import (
+ ChecksumAlgorithm,
+ Document,
+ ExternalDocumentRef,
+ File,
+ Package,
+ PackageVerificationCode,
+ Snippet,
+)
+
+
+def get_element_type_from_spdx_id(
+ spdx_id: str, document: Document
+) -> Optional[Union[Type[Package], Type[File], Type[Snippet]]]:
+ if spdx_id in [package.spdx_id for package in document.packages]:
+ return Package
+ if spdx_id in [file.spdx_id for file in document.files]:
+ return File
+ if spdx_id in [snippet.spdx_id for snippet in document.snippets]:
+ return Snippet
+ return None
+
+
+def get_full_element_spdx_id(
+ element: Union[Package, File, Snippet],
+ document_namespace: str,
+ external_document_refs: List[ExternalDocumentRef],
+) -> str:
+ """
+ Returns the spdx_id of the element prefixed with the correct document namespace and,
+ if the element is from an external document, sets the correct entry in the imports property.
+ """
+ if ":" not in element.spdx_id:
+ return f"{document_namespace}#{element.spdx_id}"
+
+ external_id, local_id = element.spdx_id.split(":")
+ external_uri = None
+ for entry in external_document_refs:
+ if entry.document_ref_id == external_id:
+ external_uri = entry.document_uri
+ break
+
+ if not external_uri:
+ raise ValueError(f"external id {external_id} not found in external document references")
+
+ return external_uri + "#" + local_id
+
+
+def calculate_package_verification_code(files: List[File]) -> PackageVerificationCode:
+ list_of_file_hashes = []
+ for file in files:
+ file_checksum_value = None
+ for checksum in file.checksums:
+ if checksum.algorithm == ChecksumAlgorithm.SHA1:
+ file_checksum_value = checksum.value
+ if not file_checksum_value:
+ try:
+ file_checksum_value = calculate_file_checksum(file.name, ChecksumAlgorithm.SHA1)
+ except FileNotFoundError:
+ raise FileNotFoundError(
+ f"Cannot calculate package verification code because the file '{file.name}' "
+ f"provides no SHA1 checksum and can't be found at the specified location."
+ )
+ list_of_file_hashes.append(file_checksum_value)
+
+ list_of_file_hashes.sort()
+ hasher = hashlib.new("sha1")
+ hasher.update("".join(list_of_file_hashes).encode("utf-8"))
+ value = hasher.hexdigest()
+ return PackageVerificationCode(value)
+
+
+def calculate_file_checksum(file_name: str, hash_algorithm=ChecksumAlgorithm.SHA1) -> str:
+ BUFFER_SIZE = 65536
+
+ file_hash = hashlib.new(hash_algorithm.name.lower())
+ with open(file_name, "rb") as file_handle:
+ while True:
+ data = file_handle.read(BUFFER_SIZE)
+ if not data:
+ break
+ file_hash.update(data)
+
+ return file_hash.hexdigest()
diff --git a/src/spdx_tools/spdx/validation/__init__.py b/src/spdx_tools/spdx/validation/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/validation/actor_validator.py b/src/spdx_tools/spdx/validation/actor_validator.py
new file mode 100644
index 000000000..3ae5f8656
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/actor_validator.py
@@ -0,0 +1,30 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List
+
+from spdx_tools.spdx.model import Actor, ActorType
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_actors(actors: List[Actor], parent_id: str) -> List[ValidationMessage]:
+ validation_messages = []
+ for actor in actors:
+ validation_messages.extend(validate_actor(actor, parent_id))
+
+ return validation_messages
+
+
+def validate_actor(actor: Actor, parent_id: str) -> List[ValidationMessage]:
+ validation_messages = []
+
+ if actor.actor_type == ActorType.TOOL and actor.email is not None:
+ validation_messages.append(
+ ValidationMessage(
+ f"email must be None if actor_type is TOOL, but is: {actor.email}",
+ ValidationContext(parent_id=parent_id, element_type=SpdxElementType.ACTOR, full_element=actor),
+ )
+ )
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/annotation_validator.py b/src/spdx_tools/spdx/validation/annotation_validator.py
new file mode 100644
index 000000000..3fec188c7
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/annotation_validator.py
@@ -0,0 +1,31 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List
+
+from spdx_tools.spdx.model import Annotation, Document
+from spdx_tools.spdx.validation.actor_validator import validate_actor
+from spdx_tools.spdx.validation.spdx_id_validators import validate_spdx_id
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_annotations(annotations: List[Annotation], document: Document) -> List[ValidationMessage]:
+ validation_messages = []
+ for annotation in annotations:
+ validation_messages.extend(validate_annotation(annotation, document))
+
+ return validation_messages
+
+
+def validate_annotation(annotation: Annotation, document: Document) -> List[ValidationMessage]:
+ validation_messages = []
+ context = ValidationContext(element_type=SpdxElementType.ANNOTATION, full_element=annotation)
+
+ validation_messages.extend(validate_actor(annotation.annotator, "annotation"))
+
+ messages: List[str] = validate_spdx_id(annotation.spdx_id, document, check_document=True)
+ for message in messages:
+ validation_messages.append(ValidationMessage(message, context))
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/checksum_validator.py b/src/spdx_tools/spdx/validation/checksum_validator.py
new file mode 100644
index 000000000..2a9d055c8
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/checksum_validator.py
@@ -0,0 +1,74 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import re
+
+from beartype.typing import Dict, List
+
+from spdx_tools.spdx.model import Checksum, ChecksumAlgorithm
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+# in hexadecimal digits
+algorithm_length: Dict = {
+ ChecksumAlgorithm.SHA1: "40",
+ ChecksumAlgorithm.SHA224: "56",
+ ChecksumAlgorithm.SHA256: "64",
+ ChecksumAlgorithm.SHA384: "96",
+ ChecksumAlgorithm.SHA512: "128",
+ ChecksumAlgorithm.SHA3_256: "64",
+ ChecksumAlgorithm.SHA3_384: "96",
+ ChecksumAlgorithm.SHA3_512: "128",
+ ChecksumAlgorithm.BLAKE2B_256: "64",
+ ChecksumAlgorithm.BLAKE2B_384: "96",
+ ChecksumAlgorithm.BLAKE2B_512: "128",
+ ChecksumAlgorithm.BLAKE3: "256,", # at least 256 bits
+ ChecksumAlgorithm.MD2: "32",
+ ChecksumAlgorithm.MD4: "32",
+ ChecksumAlgorithm.MD5: "32",
+ ChecksumAlgorithm.MD6: "0,512", # between 0 and 512 bits
+ ChecksumAlgorithm.ADLER32: "8",
+}
+
+
+def validate_checksums(checksums: List[Checksum], parent_id: str, spdx_version: str) -> List[ValidationMessage]:
+ validation_messages = []
+ for checksum in checksums:
+ validation_messages.extend(validate_checksum(checksum, parent_id, spdx_version))
+
+ return validation_messages
+
+
+def validate_checksum(checksum: Checksum, parent_id: str, spdx_version: str) -> List[ValidationMessage]:
+ validation_messages = []
+ algorithm = checksum.algorithm
+ context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum)
+
+ if spdx_version == "SPDX-2.2" and algorithm in [
+ ChecksumAlgorithm.SHA3_512,
+ ChecksumAlgorithm.SHA3_384,
+ ChecksumAlgorithm.SHA3_256,
+ ChecksumAlgorithm.BLAKE3,
+ ChecksumAlgorithm.BLAKE2B_512,
+ ChecksumAlgorithm.BLAKE2B_384,
+ ChecksumAlgorithm.BLAKE2B_256,
+ ChecksumAlgorithm.ADLER32,
+ ]:
+ return [ValidationMessage(f"{checksum.algorithm.name} is not supported in SPDX-2.2", context)]
+
+ if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value):
+ if algorithm == ChecksumAlgorithm.BLAKE3:
+ length = "at least 256"
+ elif algorithm == ChecksumAlgorithm.MD6:
+ length = "between 0 and 512"
+ else:
+ length = algorithm_length[algorithm]
+ validation_messages.append(
+ ValidationMessage(
+ f"value of {algorithm} must consist of {length} lowercase hexadecimal digits, but is: "
+ f"{checksum.value} (length: {len(checksum.value)} digits)",
+ context,
+ )
+ )
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/creation_info_validator.py b/src/spdx_tools/spdx/validation/creation_info_validator.py
new file mode 100644
index 000000000..79490c77c
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/creation_info_validator.py
@@ -0,0 +1,38 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List
+
+from spdx_tools.spdx.constants import DOCUMENT_SPDX_ID
+from spdx_tools.spdx.model import CreationInfo
+from spdx_tools.spdx.validation.actor_validator import validate_actors
+from spdx_tools.spdx.validation.external_document_ref_validator import validate_external_document_refs
+from spdx_tools.spdx.validation.uri_validators import validate_uri
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_creation_info(creation_info: CreationInfo, spdx_version: str) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+
+ context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT)
+
+ if creation_info.spdx_id != DOCUMENT_SPDX_ID:
+ validation_messages.append(
+ ValidationMessage(f"spdx_id must be {DOCUMENT_SPDX_ID}, but is: {creation_info.spdx_id}", context)
+ )
+
+ if creation_info.data_license != "CC0-1.0":
+ validation_messages.append(
+ ValidationMessage(f'data_license must be "CC0-1.0", but is: {creation_info.data_license}', context)
+ )
+
+ for message in validate_uri(creation_info.document_namespace):
+ validation_messages.append(ValidationMessage("document_namespace " + message, context))
+
+ validation_messages.extend(validate_actors(creation_info.creators, creation_info.spdx_id))
+ validation_messages.extend(
+ validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id, spdx_version)
+ )
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/document_validator.py b/src/spdx_tools/spdx/validation/document_validator.py
new file mode 100644
index 000000000..b6fba74b8
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/document_validator.py
@@ -0,0 +1,97 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+from spdx_tools.spdx.model import Document, RelationshipType
+from spdx_tools.spdx.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target
+from spdx_tools.spdx.validation.annotation_validator import validate_annotations
+from spdx_tools.spdx.validation.creation_info_validator import validate_creation_info
+from spdx_tools.spdx.validation.extracted_licensing_info_validator import validate_extracted_licensing_infos
+from spdx_tools.spdx.validation.file_validator import validate_files
+from spdx_tools.spdx.validation.package_validator import validate_packages
+from spdx_tools.spdx.validation.relationship_validator import validate_relationships
+from spdx_tools.spdx.validation.snippet_validator import validate_snippets
+from spdx_tools.spdx.validation.spdx_id_validators import get_list_of_all_spdx_ids
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_full_spdx_document(document: Document, spdx_version: str = None) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+
+ # SPDX version validation has to happen here because subsequent validators rely on it
+ document_version: str = document.creation_info.spdx_version
+ context = ValidationContext(spdx_id=document.creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT)
+ if not spdx_version:
+ spdx_version = document_version
+
+ if document_version not in ["SPDX-2.2", "SPDX-2.3"]:
+ validation_messages.append(
+ ValidationMessage(
+ f'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: '
+ f"{document_version}",
+ context,
+ )
+ )
+ elif spdx_version != document_version:
+ validation_messages.append(
+ ValidationMessage(
+ f"provided SPDX version {spdx_version} does not match "
+ f"the document's SPDX version {document_version}",
+ context,
+ )
+ )
+
+ if validation_messages:
+ validation_messages.append(
+ ValidationMessage(
+ "There are issues concerning the SPDX version of the document. "
+ "As subsequent validation relies on the correct version, "
+ "the validation process has been cancelled.",
+ context,
+ )
+ )
+ return validation_messages
+
+ validation_messages.extend(validate_creation_info(document.creation_info, spdx_version))
+ validation_messages.extend(validate_packages(document.packages, spdx_version, document))
+ validation_messages.extend(validate_files(document.files, spdx_version, document))
+ validation_messages.extend(validate_snippets(document.snippets, spdx_version, document))
+ validation_messages.extend(validate_annotations(document.annotations, document))
+ validation_messages.extend(validate_relationships(document.relationships, spdx_version, document))
+ validation_messages.extend(validate_extracted_licensing_infos(document.extracted_licensing_info))
+
+ document_id = document.creation_info.spdx_id
+ document_describes_relationships = filter_by_type_and_origin(
+ document.relationships, RelationshipType.DESCRIBES, document_id
+ )
+ described_by_document_relationships = filter_by_type_and_target(
+ document.relationships, RelationshipType.DESCRIBED_BY, document_id
+ )
+
+ only_a_single_package = len(document.packages) == 1 and not document.files and not document.snippets
+ if not only_a_single_package and not document_describes_relationships + described_by_document_relationships:
+ validation_messages.append(
+ ValidationMessage(
+ f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY '
+ f'{document_id}" when there is not only a single package present',
+ ValidationContext(spdx_id=document_id, element_type=SpdxElementType.DOCUMENT),
+ )
+ )
+
+ all_spdx_ids: List[str] = get_list_of_all_spdx_ids(document)
+ auxiliary_set = set()
+ duplicated_spdx_ids = set(
+ spdx_id for spdx_id in all_spdx_ids if spdx_id in auxiliary_set or auxiliary_set.add(spdx_id)
+ )
+
+ if duplicated_spdx_ids:
+ validation_messages.append(
+ ValidationMessage(
+ f"every spdx_id must be unique within the document, but found the following duplicates: "
+ f"{sorted(duplicated_spdx_ids)}",
+ context,
+ )
+ )
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/external_document_ref_validator.py b/src/spdx_tools/spdx/validation/external_document_ref_validator.py
new file mode 100644
index 000000000..52f50a665
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/external_document_ref_validator.py
@@ -0,0 +1,46 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List
+
+from spdx_tools.spdx.model import ExternalDocumentRef
+from spdx_tools.spdx.validation.checksum_validator import validate_checksum
+from spdx_tools.spdx.validation.spdx_id_validators import is_valid_external_doc_ref_id
+from spdx_tools.spdx.validation.uri_validators import validate_uri
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_external_document_refs(
+ external_document_refs: List[ExternalDocumentRef], parent_id: str, spdx_version: str
+) -> List[ValidationMessage]:
+ validation_messages = []
+ for external_document_ref in external_document_refs:
+ validation_messages.extend(validate_external_document_ref(external_document_ref, parent_id, spdx_version))
+
+ return validation_messages
+
+
+def validate_external_document_ref(
+ external_document_ref: ExternalDocumentRef, parent_id: str, spdx_version: str
+) -> List[ValidationMessage]:
+ validation_messages = []
+ context = ValidationContext(
+ parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, full_element=external_document_ref
+ )
+
+ if not is_valid_external_doc_ref_id(external_document_ref.document_ref_id):
+ validation_messages.append(
+ ValidationMessage(
+ f'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with '
+ f'"DocumentRef-", but is: {external_document_ref.document_ref_id}',
+ context,
+ )
+ )
+
+ for message in validate_uri(external_document_ref.document_uri):
+ validation_messages.append(ValidationMessage("document_uri " + message, context))
+
+ validation_messages.extend(validate_checksum(external_document_ref.checksum, parent_id, spdx_version))
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/external_package_ref_validator.py b/src/spdx_tools/spdx/validation/external_package_ref_validator.py
new file mode 100644
index 000000000..7dd316354
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/external_package_ref_validator.py
@@ -0,0 +1,120 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import re
+
+import uritools
+from beartype.typing import Dict, List
+
+from spdx_tools.spdx.model import ExternalPackageRef, ExternalPackageRefCategory
+from spdx_tools.spdx.model.package import CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES
+from spdx_tools.spdx.validation.uri_validators import validate_url
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+CPE22TYPE_REGEX = r"^c[pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\-~%]*){0,6}$"
+CPE23TYPE_REGEX = (
+ r'^cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^'
+ r"`\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*"
+ r'|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){4}$'
+)
+MAVEN_CENTRAL_REGEX = r"^[^:]+:[^:]+(:[^:]+)?$"
+NPM_REGEX = r"^[^@]+@[^@]+$"
+NUGET_REGEX = r"^[^/]+/[^/]+$"
+BOWER_REGEX = r"^[^#]+#[^#]+$"
+PURL_REGEX = r"^pkg:.+(\/.+)?\/.+(@.+)?(\?.+)?(#.+)?$"
+SWH_REGEX = r"^swh:1:(snp|rel|rev|dir|cnt):[0-9a-fA-F]{40}$"
+GITOID_REGEX = r"^gitoid:(blob|tree|commit|tag):(sha1:[0-9a-fA-F]{40}|sha256:[0-9a-fA-F]{64})$"
+
+TYPE_TO_REGEX: Dict[str, str] = {
+ "cpe22Type": CPE22TYPE_REGEX,
+ "cpe23Type": CPE23TYPE_REGEX,
+ "maven-central": MAVEN_CENTRAL_REGEX,
+ "npm": NPM_REGEX,
+ "nuget": NUGET_REGEX,
+ "bower": BOWER_REGEX,
+ "purl": PURL_REGEX,
+ "swh": SWH_REGEX,
+ "gitoid": GITOID_REGEX,
+}
+
+
+def validate_external_package_refs(
+ external_package_refs: List[ExternalPackageRef], parent_id: str, spdx_version: str
+) -> List[ValidationMessage]:
+ validation_messages = []
+ for external_package_ref in external_package_refs:
+ validation_messages.extend(validate_external_package_ref(external_package_ref, parent_id, spdx_version))
+
+ return validation_messages
+
+
+def validate_external_package_ref(
+ external_package_ref: ExternalPackageRef, parent_id: str, spdx_version: str
+) -> List[ValidationMessage]:
+ validation_messages = []
+ context = ValidationContext(
+ parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, full_element=external_package_ref
+ )
+
+ category = external_package_ref.category
+ locator = external_package_ref.locator
+ reference_type = external_package_ref.reference_type
+
+ if category == ExternalPackageRefCategory.OTHER:
+ if " " in locator:
+ validation_messages.append(
+ ValidationMessage(
+ f"externalPackageRef locator in category OTHER must contain no spaces, but is: {locator}", context
+ )
+ )
+
+ elif spdx_version == "SPDX-2.2" and reference_type in ["advisory", "fix", "url", "swid"]:
+ return [ValidationMessage(f'externalPackageRef type "{reference_type}" is not supported in SPDX-2.2', context)]
+
+ elif reference_type not in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]:
+ validation_messages.append(
+ ValidationMessage(
+ f"externalPackageRef type in category {category.name} must be one of "
+ f"{CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]}, but is: {reference_type}",
+ context,
+ )
+ )
+
+ elif reference_type in ["advisory", "fix", "url"]:
+ if validate_url(locator):
+ validation_messages.append(
+ ValidationMessage(
+ f'externalPackageRef locator of type "{reference_type}" must be a valid URL, but is: {locator}',
+ context,
+ )
+ )
+
+ elif reference_type == "swid":
+ if not uritools.isuri(locator) or not locator.startswith("swid"):
+ validation_messages.append(
+ ValidationMessage(
+ f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, '
+ f"but is: {locator}",
+ context,
+ )
+ )
+
+ else:
+ validation_messages.extend(validate_against_regex(locator, reference_type, context))
+
+ return validation_messages
+
+
+def validate_against_regex(
+ string_to_validate: str, reference_type: str, context: ValidationContext
+) -> List[ValidationMessage]:
+ regex = TYPE_TO_REGEX[reference_type]
+ if not re.match(regex, string_to_validate):
+ return [
+ ValidationMessage(
+ f'externalPackageRef locator of type "{reference_type}" must conform with the regex {regex}, '
+ f"but is: {string_to_validate}",
+ context,
+ )
+ ]
+ return []
diff --git a/src/spdx_tools/spdx/validation/extracted_licensing_info_validator.py b/src/spdx_tools/spdx/validation/extracted_licensing_info_validator.py
new file mode 100644
index 000000000..6cca07576
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/extracted_licensing_info_validator.py
@@ -0,0 +1,49 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import re
+
+from beartype.typing import List, Optional
+
+from spdx_tools.spdx.model import ExtractedLicensingInfo
+from spdx_tools.spdx.validation.uri_validators import validate_url
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_extracted_licensing_infos(
+ extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]],
+) -> List[ValidationMessage]:
+ validation_messages = []
+ for extracted_licensing_info in extracted_licensing_infos:
+ validation_messages.extend(validate_extracted_licensing_info(extracted_licensing_info))
+
+ return validation_messages
+
+
+def validate_extracted_licensing_info(extracted_licensing_infos: ExtractedLicensingInfo) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+ context = ValidationContext(
+ element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, full_element=extracted_licensing_infos
+ )
+
+ license_id: str = extracted_licensing_infos.license_id
+ if license_id and not re.match(r"^LicenseRef-[\da-zA-Z.-]+$", license_id):
+ validation_messages.append(
+ ValidationMessage(
+ f'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", '
+ f"but is: {license_id}",
+ context,
+ )
+ )
+
+ if license_id and not extracted_licensing_infos.extracted_text:
+ validation_messages.append(
+ ValidationMessage("extracted_text must be provided if there is a license_id assigned", context)
+ )
+
+ for cross_reference in extracted_licensing_infos.cross_references:
+ for message in validate_url(cross_reference):
+ validation_messages.append(ValidationMessage("cross_reference " + message, context))
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/file_validator.py b/src/spdx_tools/spdx/validation/file_validator.py
new file mode 100644
index 000000000..a14efd4c4
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/file_validator.py
@@ -0,0 +1,85 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List, Optional
+
+from spdx_tools.spdx.model import ChecksumAlgorithm, Document, File
+from spdx_tools.spdx.validation.checksum_validator import validate_checksums
+from spdx_tools.spdx.validation.license_expression_validator import (
+ validate_license_expression,
+ validate_license_expressions,
+)
+from spdx_tools.spdx.validation.spdx_id_validators import validate_spdx_id
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_files(
+ files: List[File], spdx_version: str, document: Optional[Document] = None
+) -> List[ValidationMessage]:
+ validation_messages = []
+ if document:
+ for file in files:
+ validation_messages.extend(validate_file_within_document(file, spdx_version, document))
+ else:
+ for file in files:
+ validation_messages.extend(validate_file(file, spdx_version))
+
+ return validation_messages
+
+
+def validate_file_within_document(file: File, spdx_version: str, document: Document) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+ context = ValidationContext(
+ spdx_id=file.spdx_id,
+ parent_id=document.creation_info.spdx_id,
+ element_type=SpdxElementType.FILE,
+ full_element=file,
+ )
+
+ for message in validate_spdx_id(file.spdx_id, document):
+ validation_messages.append(ValidationMessage(message, context))
+
+ validation_messages.extend(validate_license_expression(file.license_concluded, document, file.spdx_id))
+
+ validation_messages.extend(validate_license_expressions(file.license_info_in_file, document, file.spdx_id))
+
+ validation_messages.extend(validate_file(file, spdx_version, context))
+
+ return validation_messages
+
+
+def validate_file(
+ file: File, spdx_version: str, context: Optional[ValidationContext] = None
+) -> List[ValidationMessage]:
+ validation_messages = []
+ if not context:
+ context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file)
+
+ if file.name.startswith("/"):
+ validation_messages.append(
+ ValidationMessage(
+ f'file name must not be an absolute path starting with "/", but is: {file.name}', context
+ )
+ )
+
+ if ChecksumAlgorithm.SHA1 not in [checksum.algorithm for checksum in file.checksums]:
+ validation_messages.append(
+ ValidationMessage(
+ f"checksums must contain a SHA1 algorithm checksum, but only contains: "
+ f"{[checksum.algorithm for checksum in file.checksums]}",
+ context,
+ )
+ )
+
+ validation_messages.extend(validate_checksums(file.checksums, file.spdx_id, spdx_version))
+
+ if spdx_version == "SPDX-2.2":
+ if file.license_concluded is None:
+ validation_messages.append(ValidationMessage("license_concluded is mandatory in SPDX-2.2", context))
+ if not file.license_info_in_file:
+ validation_messages.append(ValidationMessage("license_info_in_file is mandatory in SPDX-2.2", context))
+ if file.copyright_text is None:
+ validation_messages.append(ValidationMessage("copyright_text is mandatory in SPDX-2.2", context))
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/license_expression_validator.py b/src/spdx_tools/spdx/validation/license_expression_validator.py
new file mode 100644
index 000000000..e463aa9b6
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/license_expression_validator.py
@@ -0,0 +1,96 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List, Optional, Union
+from license_expression import ExpressionError, ExpressionParseError, LicenseExpression
+
+from spdx_tools.common.spdx_licensing import spdx_licensing
+from spdx_tools.spdx.model import Document, SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.validation.spdx_id_validators import is_external_doc_ref_present_in_document
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_license_expressions(
+ license_expressions: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]], document: Document, parent_id: str
+) -> List[ValidationMessage]:
+ context = ValidationContext(
+ parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expressions
+ )
+ validation_messages = []
+
+ for license_expression in license_expressions:
+ validation_messages.extend(validate_license_expression(license_expression, document, parent_id, context))
+
+ return validation_messages
+
+
+def validate_license_expression(
+ license_expression: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]],
+ document: Document,
+ parent_id: str,
+ context: ValidationContext = None,
+) -> List[ValidationMessage]:
+ if license_expression in [SpdxNoAssertion(), SpdxNone(), None]:
+ return []
+
+ if not context:
+ context = ValidationContext(
+ parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expression
+ )
+
+ validation_messages = []
+ license_ref_ids: List[str] = [license_ref.license_id for license_ref in document.extracted_licensing_info]
+
+ for non_spdx_token in spdx_licensing.validate(license_expression).invalid_symbols:
+ if ":" in non_spdx_token:
+ split_token: List[str] = non_spdx_token.split(":")
+ if len(split_token) != 2:
+ validation_messages.append(
+ ValidationMessage(
+ f"Too many colons in license reference: {non_spdx_token}. "
+ "A license reference must only contain a single colon to "
+ "separate an external document reference from the license reference.",
+ context,
+ )
+ )
+ else:
+ if not split_token[1].startswith("LicenseRef-"):
+ validation_messages.append(
+ ValidationMessage(
+ f'A license reference must start with "LicenseRef-", but is: {split_token[1]} '
+ f"in external license reference {non_spdx_token}.",
+ context,
+ )
+ )
+ if not is_external_doc_ref_present_in_document(split_token[0], document):
+ validation_messages.append(
+ ValidationMessage(
+ f'Did not find the external document reference "{split_token[0]}" in the SPDX document. '
+ f"From the external license reference {non_spdx_token}.",
+ context,
+ )
+ )
+
+ elif non_spdx_token not in license_ref_ids:
+ validation_messages.append(
+ ValidationMessage(
+ f"Unrecognized license reference: {non_spdx_token}. license_expression must only use IDs from the "
+ f"license list or extracted licensing info, but is: {license_expression}",
+ context,
+ )
+ )
+
+ try:
+ spdx_licensing.parse(str(license_expression), validate=True, strict=True)
+ except ExpressionParseError as err:
+ # This error is raised when an exception symbol is used as a license symbol and vice versa.
+ # So far, it only catches the first such error in the provided string.
+ validation_messages.append(ValidationMessage(f"{err}. for license_expression: {license_expression}", context))
+ except ExpressionError:
+ # This error is raised for invalid symbols within the license_expression, but it provides only a string of
+ # these. On the other hand, spdx_licensing.validate() gives an actual list of invalid symbols, so this is
+ # handled above.
+ pass
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/package_validator.py b/src/spdx_tools/spdx/validation/package_validator.py
new file mode 100644
index 000000000..25cd6147f
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/package_validator.py
@@ -0,0 +1,160 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List, Optional
+
+from spdx_tools.spdx.model import Document, File, Package, Relationship, RelationshipType
+from spdx_tools.spdx.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target
+from spdx_tools.spdx.spdx_element_utils import get_element_type_from_spdx_id
+from spdx_tools.spdx.validation.checksum_validator import validate_checksums
+from spdx_tools.spdx.validation.external_package_ref_validator import validate_external_package_refs
+from spdx_tools.spdx.validation.license_expression_validator import (
+ validate_license_expression,
+ validate_license_expressions,
+)
+from spdx_tools.spdx.validation.package_verification_code_validator import validate_verification_code
+from spdx_tools.spdx.validation.spdx_id_validators import validate_spdx_id
+from spdx_tools.spdx.validation.uri_validators import validate_download_location, validate_url
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_packages(
+ packages: List[Package], spdx_version: str, document: Optional[Document] = None
+) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+ if document:
+ for package in packages:
+ validation_messages.extend(validate_package_within_document(package, spdx_version, document))
+ else:
+ for package in packages:
+ validation_messages.extend(validate_package(package, spdx_version))
+
+ return validation_messages
+
+
+def validate_package_within_document(
+ package: Package, spdx_version: str, document: Document
+) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+ context = ValidationContext(
+ spdx_id=package.spdx_id,
+ parent_id=document.creation_info.spdx_id,
+ element_type=SpdxElementType.PACKAGE,
+ full_element=package,
+ )
+
+ for message in validate_spdx_id(package.spdx_id, document):
+ validation_messages.append(ValidationMessage(message, context))
+
+ if not package.files_analyzed:
+ package_contains_relationships = filter_by_type_and_origin(
+ document.relationships, RelationshipType.CONTAINS, package.spdx_id
+ )
+ package_contains_file_relationships = [
+ relationship
+ for relationship in package_contains_relationships
+ if get_element_type_from_spdx_id(relationship.related_spdx_element_id, document) == File
+ ]
+
+ contained_in_package_relationships = filter_by_type_and_target(
+ document.relationships, RelationshipType.CONTAINED_BY, package.spdx_id
+ )
+ file_contained_in_package_relationships = [
+ relationship
+ for relationship in contained_in_package_relationships
+ if get_element_type_from_spdx_id(relationship.spdx_element_id, document) == File
+ ]
+
+ combined_relationships: List[Relationship] = (
+ package_contains_file_relationships + file_contained_in_package_relationships
+ )
+
+ if combined_relationships:
+ validation_messages.append(
+ ValidationMessage(
+ f"package must contain no elements if files_analyzed is False, but found {combined_relationships}",
+ context,
+ )
+ )
+
+ validation_messages.extend(validate_license_expression(package.license_concluded, document, package.spdx_id))
+
+ license_info_from_files = package.license_info_from_files
+ if license_info_from_files:
+ if not package.files_analyzed:
+ validation_messages.append(
+ ValidationMessage(
+ f"license_info_from_files must be None if files_analyzed is False, but is: "
+ f"{license_info_from_files}",
+ context,
+ )
+ )
+ else:
+ validation_messages.extend(
+ validate_license_expressions(license_info_from_files, document, package.spdx_id)
+ )
+
+ validation_messages.extend(validate_license_expression(package.license_declared, document, package.spdx_id))
+
+ validation_messages.extend(validate_package(package, spdx_version, context))
+
+ return validation_messages
+
+
+def validate_package(
+ package: Package, spdx_version: str, context: Optional[ValidationContext] = None
+) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+ if not context:
+ context = ValidationContext(
+ spdx_id=package.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package
+ )
+
+ download_location = package.download_location
+ if isinstance(download_location, str):
+ for message in validate_download_location(download_location):
+ validation_messages.append(ValidationMessage("package download_location " + message, context))
+
+ homepage = package.homepage
+ if isinstance(homepage, str):
+ for message in validate_url(homepage):
+ validation_messages.append(ValidationMessage("homepage " + message, context))
+
+ verification_code = package.verification_code
+ if verification_code:
+ if not package.files_analyzed:
+ validation_messages.append(
+ ValidationMessage(
+ f"verification_code must be None if files_analyzed is False, but is: {verification_code}", context
+ )
+ )
+ else:
+ validation_messages.extend(validate_verification_code(verification_code, package.spdx_id))
+
+ validation_messages.extend(validate_checksums(package.checksums, package.spdx_id, spdx_version))
+
+ validation_messages.extend(
+ validate_external_package_refs(package.external_references, package.spdx_id, spdx_version)
+ )
+
+ if spdx_version == "SPDX-2.2":
+ if package.primary_package_purpose is not None:
+ validation_messages.append(
+ ValidationMessage("primary_package_purpose is not supported in SPDX-2.2", context)
+ )
+ if package.built_date is not None:
+ validation_messages.append(ValidationMessage("built_date is not supported in SPDX-2.2", context))
+ if package.release_date is not None:
+ validation_messages.append(ValidationMessage("release_date is not supported in SPDX-2.2", context))
+ if package.valid_until_date is not None:
+ validation_messages.append(ValidationMessage("valid_until_date is not supported in SPDX-2.2", context))
+
+ if package.license_concluded is None:
+ validation_messages.append(ValidationMessage("license_concluded is mandatory in SPDX-2.2", context))
+ if package.license_declared is None:
+ validation_messages.append(ValidationMessage("license_declared is mandatory in SPDX-2.2", context))
+ if package.copyright_text is None:
+ validation_messages.append(ValidationMessage("copyright_text is mandatory in SPDX-2.2", context))
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/package_verification_code_validator.py b/src/spdx_tools/spdx/validation/package_verification_code_validator.py
new file mode 100644
index 000000000..55dd1bb35
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/package_verification_code_validator.py
@@ -0,0 +1,35 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import re
+
+from beartype.typing import List
+
+from spdx_tools.spdx.model import PackageVerificationCode
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_verification_code(verification_code: PackageVerificationCode, parent_id: str) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+ context = ValidationContext(
+ parent_id=parent_id, element_type=SpdxElementType.PACKAGE_VERIFICATION_CODE, full_element=verification_code
+ )
+
+ for file in verification_code.excluded_files:
+ if file.startswith("/"):
+ validation_messages.append(
+ ValidationMessage(f'file name must not be an absolute path starting with "/", but is: {file}', context)
+ )
+
+ value: str = verification_code.value
+ if not re.match("^[0-9a-f]{40}$", value):
+ validation_messages.append(
+ ValidationMessage(
+ f"value of verification_code must consist of 40 lowercase hexadecimal digits, but is: {value} "
+ f"(length: {len(value)} digits)",
+ context,
+ )
+ )
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/relationship_validator.py b/src/spdx_tools/spdx/validation/relationship_validator.py
new file mode 100644
index 000000000..1c194ac99
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/relationship_validator.py
@@ -0,0 +1,46 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List
+
+from spdx_tools.spdx.model import Document, Relationship, RelationshipType, SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.validation.spdx_id_validators import validate_spdx_id
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_relationships(
+ relationships: List[Relationship], spdx_version: str, document: Document
+) -> List[ValidationMessage]:
+ validation_messages = []
+ for relationship in relationships:
+ validation_messages.extend(validate_relationship(relationship, spdx_version, document))
+
+ return validation_messages
+
+
+def validate_relationship(
+ relationship: Relationship, spdx_version: str, document: Document
+) -> List[ValidationMessage]:
+ validation_messages = []
+ context = ValidationContext(element_type=SpdxElementType.RELATIONSHIP, full_element=relationship)
+
+ relationship_type: RelationshipType = relationship.relationship_type
+
+ messages: List[str] = validate_spdx_id(relationship.spdx_element_id, document, check_document=True)
+ for message in messages:
+ validation_messages.append(ValidationMessage(message, context))
+
+ if relationship.related_spdx_element_id not in [SpdxNone(), SpdxNoAssertion()]:
+ messages: List[str] = validate_spdx_id(relationship.related_spdx_element_id, document, check_document=True)
+ for message in messages:
+ validation_messages.append(ValidationMessage(message, context))
+
+ if spdx_version == "SPDX-2.2":
+ if (
+ relationship_type == RelationshipType.SPECIFICATION_FOR
+ or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR
+ ):
+ validation_messages.append(ValidationMessage(f"{relationship_type} is not supported in SPDX-2.2", context))
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/snippet_validator.py b/src/spdx_tools/spdx/validation/snippet_validator.py
new file mode 100644
index 000000000..8c1aed9c5
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/snippet_validator.py
@@ -0,0 +1,108 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import List, Optional
+
+from spdx_tools.spdx.model import Document, Snippet
+from spdx_tools.spdx.validation.license_expression_validator import (
+ validate_license_expression,
+ validate_license_expressions,
+)
+from spdx_tools.spdx.validation.spdx_id_validators import validate_spdx_id
+from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
+
+
+def validate_snippets(
+ snippets: List[Snippet], spdx_version: str, document: Optional[Document] = None
+) -> List[ValidationMessage]:
+ validation_messages = []
+ if document:
+ for snippet in snippets:
+ validation_messages.extend(validate_snippet_within_document(snippet, spdx_version, document))
+ else:
+ for snippet in snippets:
+ validation_messages.extend(validate_snippet(snippet, spdx_version))
+
+ return validation_messages
+
+
+def validate_snippet_within_document(
+ snippet: Snippet, spdx_version: str, document: Document
+) -> List[ValidationMessage]:
+ validation_messages: List[ValidationMessage] = []
+ context = ValidationContext(
+ spdx_id=snippet.spdx_id,
+ parent_id=document.creation_info.spdx_id,
+ element_type=SpdxElementType.SNIPPET,
+ full_element=snippet,
+ )
+
+ messages: List[str] = validate_spdx_id(snippet.spdx_id, document)
+ for message in messages:
+ validation_messages.append(ValidationMessage(message, context))
+
+ messages: List[str] = validate_spdx_id(snippet.file_spdx_id, document, check_files=True)
+ for message in messages:
+ validation_messages.append(ValidationMessage(message, context))
+
+ validation_messages.extend(validate_license_expression(snippet.license_concluded, document, snippet.spdx_id))
+
+ validation_messages.extend(
+ validate_license_expressions(snippet.license_info_in_snippet, document, snippet.spdx_id)
+ )
+
+ validation_messages.extend(validate_snippet(snippet, spdx_version, context))
+
+ return validation_messages
+
+
+def validate_snippet(
+ snippet: Snippet, spdx_version: str, context: Optional[ValidationContext] = None
+) -> List[ValidationMessage]:
+ validation_messages = []
+ if not context:
+ context = ValidationContext(
+ spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet
+ )
+
+ if snippet.byte_range[0] < 1:
+ validation_messages.append(
+ ValidationMessage(
+ f"byte_range values must be greater than or equal to 1, but is: {snippet.byte_range}", context
+ )
+ )
+
+ if snippet.byte_range[0] > snippet.byte_range[1]:
+ validation_messages.append(
+ ValidationMessage(
+ f"the first value of byte_range must be less than or equal to the second, but is: "
+ f"{snippet.byte_range}",
+ context,
+ )
+ )
+
+ if snippet.line_range:
+ if snippet.line_range[0] < 1:
+ validation_messages.append(
+ ValidationMessage(
+ f"line_range values must be greater than or equal to 1, but is: {snippet.line_range}", context
+ )
+ )
+
+ if snippet.line_range[0] > snippet.line_range[1]:
+ validation_messages.append(
+ ValidationMessage(
+ f"the first value of line_range must be less than or equal to the second, "
+ f"but is: {snippet.line_range}",
+ context,
+ )
+ )
+
+ if spdx_version == "SPDX-2.2":
+ if snippet.license_concluded is None:
+ validation_messages.append(ValidationMessage("license_concluded is mandatory in SPDX-2.2", context))
+ if snippet.copyright_text is None:
+ validation_messages.append(ValidationMessage("copyright_text is mandatory in SPDX-2.2", context))
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/spdx_id_validators.py b/src/spdx_tools/spdx/validation/spdx_id_validators.py
new file mode 100644
index 000000000..6441236a9
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/spdx_id_validators.py
@@ -0,0 +1,99 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import re
+
+from beartype.typing import List
+
+from spdx_tools.spdx.document_utils import get_contained_spdx_element_ids
+from spdx_tools.spdx.model import Document, File
+
+
+def is_valid_internal_spdx_id(spdx_id: str) -> bool:
+ return bool(re.match(r"^SPDXRef-[\da-zA-Z.-]+$", spdx_id))
+
+
+def is_valid_external_doc_ref_id(external_ref_id: str) -> bool:
+ return bool(re.match(r"^DocumentRef-[\da-zA-Z.+-]+$", external_ref_id))
+
+
+def is_spdx_id_present_in_files(spdx_id: str, files: List[File]) -> bool:
+ return spdx_id in [file.spdx_id for file in files]
+
+
+def is_spdx_id_present_in_document(spdx_id: str, document: Document) -> bool:
+ all_spdx_ids_in_document: List[str] = get_list_of_all_spdx_ids(document)
+
+ return spdx_id in all_spdx_ids_in_document
+
+
+def get_list_of_all_spdx_ids(document: Document) -> List[str]:
+ all_spdx_ids_in_document: List[str] = [document.creation_info.spdx_id]
+ all_spdx_ids_in_document.extend(get_contained_spdx_element_ids(document))
+
+ return all_spdx_ids_in_document
+
+
+def is_external_doc_ref_present_in_document(external_ref_id: str, document: Document) -> bool:
+ all_external_doc_ref_ids_in_document = [
+ external_doc_ref.document_ref_id for external_doc_ref in document.creation_info.external_document_refs
+ ]
+
+ return external_ref_id in all_external_doc_ref_ids_in_document
+
+
+def validate_spdx_id(
+ spdx_id: str, document: Document, check_document: bool = False, check_files: bool = False
+) -> List[str]:
+ """Test that the given spdx_id (and a potential DocumentRef to an external document) is valid
+ and, if it is a reference, actually exists in the document. Optionally checks files or the whole document
+ for the existence of the spdx_id (i.e. if it is used as a reference). Returns a list of validation messages."""
+
+ validation_messages: List[str] = []
+ split_id: List[str] = spdx_id.split(":")
+
+ # # # invalid case # # #
+ if len(split_id) > 2:
+ return [
+ f"spdx_id must not contain more than one colon in order to separate the external document reference id "
+ f"from the internal SPDX id, but is: {spdx_id}"
+ ]
+
+ # # # case with external document ref prefix # # #
+ if len(split_id) == 2:
+ if not is_valid_external_doc_ref_id(split_id[0]):
+ validation_messages.append(
+ f'the external document reference part of spdx_id must only contain letters, numbers, ".", "-" and '
+ f'"+" and must begin with "DocumentRef-", but is: {split_id[0]}'
+ )
+ if not is_valid_internal_spdx_id(split_id[1]):
+ validation_messages.append(
+ f'the internal SPDX id part of spdx_id must only contain letters, numbers, "." and "-" and must begin '
+ f'with "SPDXRef-", but is: {split_id[1]}'
+ )
+ if not is_external_doc_ref_present_in_document(split_id[0], document):
+ validation_messages.append(
+ f'did not find the external document reference "{split_id[0]}" in the SPDX document'
+ )
+
+ return validation_messages
+
+ # # # "normal" case # # #
+ if not is_valid_internal_spdx_id(spdx_id):
+ validation_messages.append(
+ f'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: '
+ f"{spdx_id}"
+ )
+
+ if check_document:
+ if not is_spdx_id_present_in_document(spdx_id, document):
+ validation_messages.append(f'did not find the referenced spdx_id "{spdx_id}" in the SPDX document')
+
+ if check_files:
+ if not is_spdx_id_present_in_files(spdx_id, document.files):
+ validation_messages.append(
+ f'did not find the referenced spdx_id "{spdx_id}" in the SPDX document\'s files'
+ )
+
+ return validation_messages
diff --git a/src/spdx_tools/spdx/validation/uri_validators.py b/src/spdx_tools/spdx/validation/uri_validators.py
new file mode 100644
index 000000000..7720c3fb5
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/uri_validators.py
@@ -0,0 +1,48 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import re
+
+from beartype.typing import List
+from uritools import isabsuri, urisplit
+
+url_pattern = (
+ "(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/|ssh:\\/\\/|git:\\/\\/|svn:\\/\\/|sftp:"
+ "\\/\\/|ftp:\\/\\/)?([\\w\\-.!~*'()%;:&=+$,]+@)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+){0,100}\\.[a-z]{2,5}"
+ "(:[0-9]{1,5})?(\\/.*)?"
+)
+url_pattern_ignore_case = re.compile(url_pattern, re.IGNORECASE)
+
+supported_download_repos: str = "(git|hg|svn|bzr)"
+git_pattern = "(git\\+git@[a-zA-Z0-9\\.\\-]+:[a-zA-Z0-9/\\\\.@\\-]+)"
+bazaar_pattern = "(bzr\\+lp:[a-zA-Z0-9\\.\\-]+)"
+download_location_pattern = (
+ "^(((" + supported_download_repos + "\\+)?" + url_pattern + ")|" + git_pattern + "|" + bazaar_pattern + ")$"
+)
+compiled_pattern = re.compile(download_location_pattern, re.IGNORECASE)
+
+
+def validate_url(url: str) -> List[str]:
+ if not url_pattern_ignore_case.match(url):
+ return [f"must be a valid URL, but is: {url}"]
+
+ return []
+
+
+def validate_download_location(location: str) -> List[str]:
+ if not (validate_url(location) == [] or compiled_pattern.match(location)):
+ return [f"must be a valid URL or download location according to the specification, but is: {location}"]
+
+ return []
+
+
+def validate_uri(uri: str) -> List[str]:
+ if not isabsuri(uri):
+ return [f"must be a valid URI specified in RFC-3986 and must contain no fragment (#), but is: {uri}"]
+ else:
+ split = urisplit(uri)
+ if split.scheme is None:
+ return [f"must have a URI scheme, but is: {uri}"]
+
+ return []
diff --git a/src/spdx_tools/spdx/validation/validation_message.py b/src/spdx_tools/spdx/validation/validation_message.py
new file mode 100644
index 000000000..2ef1ba241
--- /dev/null
+++ b/src/spdx_tools/spdx/validation/validation_message.py
@@ -0,0 +1,39 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from dataclasses import dataclass
+from enum import Enum, auto
+
+from beartype.typing import Any, Optional
+
+
+class SpdxElementType(Enum):
+ LICENSE_EXPRESSION = auto()
+ PACKAGE_VERIFICATION_CODE = auto()
+ EXTERNAL_DOCUMENT_REF = auto()
+ CHECKSUM = auto()
+ EXTERNAL_PACKAGE_REF = auto()
+ ACTOR = auto()
+ DOCUMENT = auto()
+ CREATION_INFO = auto()
+ PACKAGE = auto()
+ FILE = auto()
+ SNIPPET = auto()
+ ANNOTATION = auto()
+ RELATIONSHIP = auto()
+ EXTRACTED_LICENSING_INFO = auto()
+
+
+@dataclass(frozen=True)
+class ValidationContext:
+ spdx_id: Optional[str] = None # not every type has an id, or it might be missing
+ parent_id: Optional[str] = None # if a parent is known and has a valid id
+ element_type: Optional[SpdxElementType] = None
+ full_element: Any = None # can be any class of the data model
+
+
+@dataclass(frozen=True)
+class ValidationMessage:
+ validation_message: str
+ context: ValidationContext
diff --git a/src/spdx_tools/spdx/writer/__init__.py b/src/spdx_tools/spdx/writer/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/writer/json/__init__.py b/src/spdx_tools/spdx/writer/json/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/writer/json/json_writer.py b/src/spdx_tools/spdx/writer/json/json_writer.py
new file mode 100644
index 000000000..bdcfdf718
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/json/json_writer.py
@@ -0,0 +1,38 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import json
+
+from beartype.typing import IO
+
+from spdx_tools.spdx.jsonschema.document_converter import DocumentConverter
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.writer.write_utils import convert, validate_and_deduplicate
+
+
+def write_document_to_stream(
+ document: Document,
+ stream: IO[str],
+ validate: bool = True,
+ converter: DocumentConverter = None,
+ drop_duplicates: bool = True,
+):
+ """
+ Serializes the provided document to json and writes it to a file with the provided name. Unless validate is set
+ to False, validates the document before serialization. Unless a DocumentConverter instance is provided,
+ a new one is created.
+ """
+ document = validate_and_deduplicate(document, validate, drop_duplicates)
+ document_dict = convert(document, converter)
+ json.dump(document_dict, stream, indent=4)
+
+
+def write_document_to_file(
+ document: Document,
+ file_name: str,
+ validate: bool = True,
+ converter: DocumentConverter = None,
+ drop_duplicates: bool = True,
+):
+ with open(file_name, "w", encoding="utf-8") as out:
+ write_document_to_stream(document, out, validate, converter, drop_duplicates)
diff --git a/src/spdx_tools/spdx/writer/rdf/__init__.py b/src/spdx_tools/spdx/writer/rdf/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/writer/rdf/annotation_writer.py b/src/spdx_tools/spdx/writer/rdf/annotation_writer.py
new file mode 100644
index 000000000..e973eeb0d
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/annotation_writer.py
@@ -0,0 +1,35 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict
+from rdflib import RDF, RDFS, BNode, Graph, Literal, URIRef
+
+from spdx_tools.spdx.casing_tools import snake_case_to_camel_case
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.model import Annotation
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id
+
+
+def add_annotation_to_graph(
+ annotation: Annotation, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]
+):
+ annotation_resource = URIRef(
+ add_namespace_to_spdx_id(annotation.spdx_id, doc_namespace, external_doc_ref_to_namespace)
+ )
+ annotation_node = BNode()
+ graph.add((annotation_node, RDF.type, SPDX_NAMESPACE.Annotation))
+ graph.add(
+ (
+ annotation_node,
+ SPDX_NAMESPACE.annotationType,
+ SPDX_NAMESPACE[f"annotationType_{snake_case_to_camel_case(annotation.annotation_type.name)}"],
+ )
+ )
+ graph.add((annotation_node, SPDX_NAMESPACE.annotator, Literal(annotation.annotator.to_serialized_string())))
+ graph.add(
+ (annotation_node, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))
+ )
+ graph.add((annotation_node, RDFS.comment, Literal(annotation.annotation_comment)))
+
+ graph.add((annotation_resource, SPDX_NAMESPACE.annotation, annotation_node))
diff --git a/src/spdx_tools/spdx/writer/rdf/checksum_writer.py b/src/spdx_tools/spdx/writer/rdf/checksum_writer.py
new file mode 100644
index 000000000..1f7172e88
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/checksum_writer.py
@@ -0,0 +1,25 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import RDF, BNode, Graph, Literal, URIRef
+
+from spdx_tools.spdx.model import Checksum, ChecksumAlgorithm
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+
+
+def add_checksum_to_graph(checksum: Checksum, graph: Graph, parent: URIRef):
+ checksum_node = BNode()
+ graph.add((checksum_node, RDF.type, SPDX_NAMESPACE.Checksum))
+ graph.add((checksum_node, SPDX_NAMESPACE.algorithm, algorithm_to_rdf_string(checksum.algorithm)))
+ graph.add((checksum_node, SPDX_NAMESPACE.checksumValue, Literal(checksum.value)))
+
+ graph.add((parent, SPDX_NAMESPACE.checksum, checksum_node))
+
+
+def algorithm_to_rdf_string(algorithm: ChecksumAlgorithm) -> URIRef:
+ if "BLAKE2B" in algorithm.name:
+ algorithm_rdf_string = algorithm.name.replace("_", "").lower()
+ else:
+ algorithm_rdf_string = algorithm.name.lower()
+
+ return SPDX_NAMESPACE[f"checksumAlgorithm_{algorithm_rdf_string}"]
diff --git a/src/spdx_tools/spdx/writer/rdf/creation_info_writer.py b/src/spdx_tools/spdx/writer/rdf/creation_info_writer.py
new file mode 100644
index 000000000..6e77bc8d9
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/creation_info_writer.py
@@ -0,0 +1,39 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import RDF, RDFS, BNode, Graph, Literal, URIRef
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.model import CreationInfo
+from spdx_tools.spdx.rdfschema.namespace import LICENSE_NAMESPACE, SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph
+from spdx_tools.spdx.writer.rdf.writer_utils import add_optional_literal
+
+
+def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph):
+ doc_node = URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}")
+ graph.add((doc_node, RDF.type, SPDX_NAMESPACE.SpdxDocument))
+ graph.add((doc_node, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version)))
+ graph.add((doc_node, SPDX_NAMESPACE.dataLicense, LICENSE_NAMESPACE[creation_info.data_license]))
+ graph.add((doc_node, SPDX_NAMESPACE.name, Literal(creation_info.name)))
+ add_optional_literal(creation_info.document_comment, graph, doc_node, RDFS.comment)
+
+ creation_info_node = BNode()
+ graph.add((creation_info_node, RDF.type, SPDX_NAMESPACE.CreationInfo))
+
+ graph.add((creation_info_node, SPDX_NAMESPACE.created, Literal(datetime_to_iso_string(creation_info.created))))
+
+ for creator in creation_info.creators:
+ graph.add((creation_info_node, SPDX_NAMESPACE.creator, Literal(creator.to_serialized_string())))
+
+ add_optional_literal(
+ creation_info.license_list_version, graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion
+ )
+ add_optional_literal(creation_info.creator_comment, graph, creation_info_node, RDFS.comment)
+
+ graph.add((doc_node, SPDX_NAMESPACE.creationInfo, creation_info_node))
+
+ for external_document_ref in creation_info.external_document_refs:
+ add_external_document_ref_to_graph(external_document_ref, graph, doc_node, creation_info.document_namespace)
+
+ return doc_node
diff --git a/src/spdx_tools/spdx/writer/rdf/external_document_ref_writer.py b/src/spdx_tools/spdx/writer/rdf/external_document_ref_writer.py
new file mode 100644
index 000000000..958ac8b5e
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/external_document_ref_writer.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import RDF, Graph, URIRef
+
+from spdx_tools.spdx.model import ExternalDocumentRef
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.checksum_writer import add_checksum_to_graph
+
+
+def add_external_document_ref_to_graph(
+ external_document_ref: ExternalDocumentRef, graph: Graph, doc_node: URIRef, doc_namespace: str
+):
+ external_document_ref_resource = URIRef(f"{doc_namespace}#{external_document_ref.document_ref_id}")
+ graph.add((external_document_ref_resource, RDF.type, SPDX_NAMESPACE.ExternalDocumentRef))
+ graph.add(
+ (external_document_ref_resource, SPDX_NAMESPACE.spdxDocument, URIRef(external_document_ref.document_uri))
+ )
+ add_checksum_to_graph(external_document_ref.checksum, graph, external_document_ref_resource)
+
+ graph.add((doc_node, SPDX_NAMESPACE.externalDocumentRef, external_document_ref_resource))
diff --git a/src/spdx_tools/spdx/writer/rdf/extracted_licensing_info_writer.py b/src/spdx_tools/spdx/writer/rdf/extracted_licensing_info_writer.py
new file mode 100644
index 000000000..f30ab5d60
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/extracted_licensing_info_writer.py
@@ -0,0 +1,32 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from rdflib import RDF, RDFS, BNode, Graph, Literal, URIRef
+
+from spdx_tools.spdx.model import ExtractedLicensingInfo
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.writer_utils import add_literal_or_no_assertion, add_optional_literal
+
+
+def add_extracted_licensing_info_to_graph(
+ extracted_licensing_info: ExtractedLicensingInfo, graph: Graph, doc_node, doc_namespace: str
+):
+ if extracted_licensing_info.license_id:
+ extracted_licensing_info_resource = URIRef(f"{doc_namespace}#{extracted_licensing_info.license_id}")
+ graph.add((extracted_licensing_info_resource, RDF.type, SPDX_NAMESPACE.ExtractedLicensingInfo))
+ else:
+ extracted_licensing_info_resource = BNode()
+ add_optional_literal(
+ extracted_licensing_info.license_id, graph, extracted_licensing_info_resource, SPDX_NAMESPACE.licenseId
+ )
+ add_optional_literal(
+ extracted_licensing_info.extracted_text, graph, extracted_licensing_info_resource, SPDX_NAMESPACE.extractedText
+ )
+ add_literal_or_no_assertion(
+ extracted_licensing_info.license_name, graph, extracted_licensing_info_resource, SPDX_NAMESPACE.name
+ )
+ for cross_reference in extracted_licensing_info.cross_references:
+ graph.add((extracted_licensing_info_resource, RDFS.seeAlso, Literal(cross_reference)))
+ add_optional_literal(extracted_licensing_info.comment, graph, extracted_licensing_info_resource, RDFS.comment)
+
+ graph.add((doc_node, SPDX_NAMESPACE.hasExtractedLicensingInfo, extracted_licensing_info_resource))
diff --git a/src/spdx_tools/spdx/writer/rdf/file_writer.py b/src/spdx_tools/spdx/writer/rdf/file_writer.py
new file mode 100644
index 000000000..679be8d22
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/file_writer.py
@@ -0,0 +1,45 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict
+from rdflib import RDF, RDFS, Graph, Literal, URIRef
+
+from spdx_tools.spdx.casing_tools import snake_case_to_camel_case
+from spdx_tools.spdx.model import File
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.checksum_writer import add_checksum_to_graph
+from spdx_tools.spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion
+from spdx_tools.spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id, add_optional_literal
+
+
+def add_file_to_graph(file: File, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]):
+ file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_ref_to_namespace))
+ graph.add((file_resource, RDF.type, SPDX_NAMESPACE.File))
+ graph.add((file_resource, SPDX_NAMESPACE.fileName, Literal(file.name)))
+ for file_type in file.file_types:
+ graph.add(
+ (
+ file_resource,
+ SPDX_NAMESPACE.fileType,
+ SPDX_NAMESPACE[f"fileType_{snake_case_to_camel_case(file_type.name)}"],
+ )
+ )
+
+ for checksum in file.checksums:
+ add_checksum_to_graph(checksum, graph, file_resource)
+
+ add_license_expression_or_none_or_no_assertion(
+ file.license_concluded, graph, file_resource, SPDX_NAMESPACE.licenseConcluded, doc_namespace
+ )
+ add_license_expression_or_none_or_no_assertion(
+ file.license_info_in_file, graph, file_resource, SPDX_NAMESPACE.licenseInfoInFile, doc_namespace
+ )
+
+ add_optional_literal(file.license_comment, graph, file_resource, SPDX_NAMESPACE.licenseComments)
+ add_optional_literal(file.copyright_text, graph, file_resource, SPDX_NAMESPACE.copyrightText)
+ add_optional_literal(file.comment, graph, file_resource, RDFS.comment)
+ add_optional_literal(file.notice, graph, file_resource, SPDX_NAMESPACE.noticeText)
+ for contributor in file.contributors:
+ graph.add((file_resource, SPDX_NAMESPACE.fileContributor, Literal(contributor)))
+ for attribution_text in file.attribution_texts:
+ graph.add((file_resource, SPDX_NAMESPACE.attributionText, Literal(attribution_text)))
diff --git a/src/spdx_tools/spdx/writer/rdf/license_expression_writer.py b/src/spdx_tools/spdx/writer/rdf/license_expression_writer.py
new file mode 100644
index 000000000..c8a76035a
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/license_expression_writer.py
@@ -0,0 +1,84 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Union
+from boolean import Expression
+from license_expression import AND, OR, ExpressionInfo, LicenseExpression, LicenseSymbol, LicenseWithExceptionSymbol
+from rdflib import RDF, BNode, Graph, URIRef
+from rdflib.term import Literal, Node
+
+from spdx_tools.common.spdx_licensing import spdx_licensing
+from spdx_tools.spdx.model import SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.rdfschema.namespace import LICENSE_NAMESPACE, SPDX_NAMESPACE
+
+
+def add_license_expression_or_none_or_no_assertion(
+ value: Union[
+ List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]], LicenseExpression, SpdxNoAssertion, SpdxNone
+ ],
+ graph: Graph,
+ parent: Node,
+ predicate: Node,
+ doc_namespace: str,
+):
+ if isinstance(value, SpdxNoAssertion):
+ graph.add((parent, predicate, SPDX_NAMESPACE.noassertion))
+ return
+ if isinstance(value, SpdxNone):
+ graph.add((parent, predicate, SPDX_NAMESPACE.none))
+ return
+ if isinstance(value, list):
+ for license_expression in value:
+ add_license_expression_or_none_or_no_assertion(license_expression, graph, parent, predicate, doc_namespace)
+ if isinstance(value, LicenseExpression):
+ add_license_expression_to_graph(value, graph, parent, predicate, doc_namespace)
+
+
+def add_license_expression_to_graph(
+ license_expression: Expression, graph: Graph, parent: Node, predicate: Node, doc_namespace: str
+):
+ if isinstance(license_expression, AND):
+ member_node = BNode()
+ graph.add((member_node, RDF.type, SPDX_NAMESPACE.ConjunctiveLicenseSet))
+ graph.add((parent, predicate, member_node))
+ for arg in license_expression.args:
+ add_license_expression_to_graph(arg, graph, member_node, SPDX_NAMESPACE.member, doc_namespace)
+ if isinstance(license_expression, OR):
+ member_node = BNode()
+ graph.add((member_node, RDF.type, SPDX_NAMESPACE.DisjunctiveLicenseSet))
+ graph.add((parent, predicate, member_node))
+ for arg in license_expression.args:
+ add_license_expression_to_graph(arg, graph, member_node, SPDX_NAMESPACE.member, doc_namespace)
+ if isinstance(license_expression, LicenseWithExceptionSymbol):
+ member_node = BNode()
+ graph.add((member_node, RDF.type, SPDX_NAMESPACE.WithExceptionOperator))
+ graph.add((parent, predicate, member_node))
+
+ add_license_expression_to_graph(
+ license_expression.license_symbol, graph, member_node, SPDX_NAMESPACE.member, doc_namespace
+ )
+ add_license_exception_to_graph(license_expression.exception_symbol, graph, member_node)
+
+ if isinstance(license_expression, LicenseSymbol):
+ if license_or_exception_is_on_spdx_licensing_list(license_expression):
+ graph.add((parent, predicate, LICENSE_NAMESPACE[str(license_expression)]))
+ else:
+ # assuming that the license expression is a LicenseRef to an instance of ExtractedLicensingInfo
+ graph.add((parent, predicate, URIRef(f"{doc_namespace}#{license_expression}")))
+
+
+def license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol) -> bool:
+ symbol_info: ExpressionInfo = spdx_licensing.validate(license_symbol)
+ return not symbol_info.errors
+
+
+def add_license_exception_to_graph(license_exception: LicenseSymbol, graph: Graph, parent: Node):
+ if license_or_exception_is_on_spdx_licensing_list(license_exception):
+ exception_node = LICENSE_NAMESPACE[str(license_exception)]
+ graph.add((parent, SPDX_NAMESPACE.licenseException, exception_node))
+ else:
+ exception_node = BNode()
+ graph.add((exception_node, SPDX_NAMESPACE.licenseExceptionId, Literal(license_exception)))
+ graph.add((parent, SPDX_NAMESPACE.licenseException, exception_node))
+
+ graph.add((exception_node, RDF.type, SPDX_NAMESPACE.LicenseException))
diff --git a/src/spdx_tools/spdx/writer/rdf/package_writer.py b/src/spdx_tools/spdx/writer/rdf/package_writer.py
new file mode 100644
index 000000000..2137d0dbd
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/package_writer.py
@@ -0,0 +1,133 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict
+from rdflib import DOAP, RDF, RDFS, XSD, BNode, Graph, Literal, URIRef
+
+from spdx_tools.spdx.casing_tools import snake_case_to_camel_case
+from spdx_tools.spdx.model import ExternalPackageRef, Package, PackageVerificationCode
+from spdx_tools.spdx.model.package import CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES
+from spdx_tools.spdx.rdfschema.namespace import REFERENCE_NAMESPACE, SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.checksum_writer import add_checksum_to_graph
+from spdx_tools.spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion
+from spdx_tools.spdx.writer.rdf.writer_utils import (
+ add_datetime_to_graph,
+ add_literal_or_no_assertion_or_none,
+ add_namespace_to_spdx_id,
+ add_optional_literal,
+)
+
+
+def add_package_to_graph(
+ package: Package, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]
+):
+ package_resource = URIRef(add_namespace_to_spdx_id(package.spdx_id, doc_namespace, external_doc_ref_to_namespace))
+ graph.add((package_resource, RDF.type, SPDX_NAMESPACE.Package))
+
+ graph.add((package_resource, SPDX_NAMESPACE.name, Literal(package.name)))
+ add_optional_literal(package.version, graph, package_resource, SPDX_NAMESPACE.versionInfo)
+ add_optional_literal(package.file_name, graph, package_resource, SPDX_NAMESPACE.packageFileName)
+ add_optional_literal(package.supplier, graph, package_resource, SPDX_NAMESPACE.supplier)
+ add_optional_literal(package.originator, graph, package_resource, SPDX_NAMESPACE.originator)
+ add_literal_or_no_assertion_or_none(
+ package.download_location, graph, package_resource, SPDX_NAMESPACE.downloadLocation
+ )
+ graph.add((package_resource, SPDX_NAMESPACE.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean)))
+ add_package_verification_code_to_graph(package.verification_code, graph, package_resource)
+ for checksum in package.checksums:
+ add_checksum_to_graph(checksum, graph, package_resource)
+
+ add_optional_literal(package.homepage, graph, package_resource, DOAP.homepage)
+ add_optional_literal(package.source_info, graph, package_resource, SPDX_NAMESPACE.sourceInfo)
+ add_license_expression_or_none_or_no_assertion(
+ package.license_concluded, graph, package_resource, SPDX_NAMESPACE.licenseConcluded, doc_namespace
+ )
+ add_license_expression_or_none_or_no_assertion(
+ package.license_info_from_files, graph, package_resource, SPDX_NAMESPACE.licenseInfoFromFiles, doc_namespace
+ )
+ add_license_expression_or_none_or_no_assertion(
+ package.license_declared, graph, package_resource, SPDX_NAMESPACE.licenseDeclared, doc_namespace
+ )
+ add_optional_literal(package.license_comment, graph, package_resource, SPDX_NAMESPACE.licenseComments)
+ add_optional_literal(package.copyright_text, graph, package_resource, SPDX_NAMESPACE.copyrightText)
+ add_optional_literal(package.summary, graph, package_resource, SPDX_NAMESPACE.summary)
+ add_optional_literal(package.description, graph, package_resource, SPDX_NAMESPACE.description)
+ add_optional_literal(package.comment, graph, package_resource, RDFS.comment)
+ for external_reference in package.external_references:
+ add_external_package_ref_to_graph(external_reference, graph, package_resource, doc_namespace)
+ for attribution_text in package.attribution_texts:
+ add_optional_literal(attribution_text, graph, package_resource, SPDX_NAMESPACE.attributionText)
+ if package.primary_package_purpose:
+ graph.add(
+ (
+ package_resource,
+ SPDX_NAMESPACE.primaryPackagePurpose,
+ SPDX_NAMESPACE[f"purpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"],
+ )
+ )
+
+ add_datetime_to_graph(package.release_date, graph, package_resource, SPDX_NAMESPACE.releaseDate)
+ add_datetime_to_graph(package.built_date, graph, package_resource, SPDX_NAMESPACE.builtDate)
+ add_datetime_to_graph(package.valid_until_date, graph, package_resource, SPDX_NAMESPACE.validUntilDate)
+
+
+def add_package_verification_code_to_graph(
+ package_verification_code: PackageVerificationCode, graph: Graph, package_node: URIRef
+):
+ if not package_verification_code:
+ return
+ package_verification_code_node = BNode()
+ graph.add((package_verification_code_node, RDF.type, SPDX_NAMESPACE.PackageVerificationCode))
+ graph.add(
+ (
+ package_verification_code_node,
+ SPDX_NAMESPACE.packageVerificationCodeValue,
+ Literal(package_verification_code.value),
+ )
+ )
+ for excluded_file in package_verification_code.excluded_files:
+ graph.add(
+ (
+ package_verification_code_node,
+ SPDX_NAMESPACE.packageVerificationCodeExcludedFile,
+ Literal(excluded_file),
+ )
+ )
+
+ graph.add((package_node, SPDX_NAMESPACE.packageVerificationCode, package_verification_code_node))
+
+
+def add_external_package_ref_to_graph(
+ external_package_ref: ExternalPackageRef, graph: Graph, package_node: URIRef, doc_namespace: str
+):
+ external_package_ref_node = BNode()
+ graph.add((external_package_ref_node, RDF.type, SPDX_NAMESPACE.ExternalRef))
+ graph.add(
+ (
+ external_package_ref_node,
+ SPDX_NAMESPACE.referenceCategory,
+ SPDX_NAMESPACE[f"referenceCategory_{snake_case_to_camel_case(external_package_ref.category.name)}"],
+ )
+ )
+
+ if external_package_ref.reference_type in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[external_package_ref.category]:
+ graph.add(
+ (
+ external_package_ref_node,
+ SPDX_NAMESPACE.referenceType,
+ REFERENCE_NAMESPACE[external_package_ref.reference_type],
+ )
+ )
+ else:
+ graph.add(
+ (
+ external_package_ref_node,
+ SPDX_NAMESPACE.referenceType,
+ URIRef(f"{doc_namespace}#{external_package_ref.reference_type}"),
+ )
+ )
+ graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceLocator, Literal(external_package_ref.locator)))
+ if external_package_ref.comment:
+ graph.add((external_package_ref_node, RDFS.comment, Literal(external_package_ref.comment)))
+
+ graph.add((package_node, SPDX_NAMESPACE.externalRef, external_package_ref_node))
diff --git a/src/spdx_tools/spdx/writer/rdf/rdf_writer.py b/src/spdx_tools/spdx/writer/rdf/rdf_writer.py
new file mode 100644
index 000000000..206494def
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/rdf_writer.py
@@ -0,0 +1,58 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import IO, Dict
+from rdflib import DOAP, Graph
+from rdflib.compare import to_isomorphic
+
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.rdfschema.namespace import POINTER_NAMESPACE, SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.annotation_writer import add_annotation_to_graph
+from spdx_tools.spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph
+from spdx_tools.spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph
+from spdx_tools.spdx.writer.rdf.file_writer import add_file_to_graph
+from spdx_tools.spdx.writer.rdf.package_writer import add_package_to_graph
+from spdx_tools.spdx.writer.rdf.relationship_writer import add_relationship_to_graph
+from spdx_tools.spdx.writer.rdf.snippet_writer import add_snippet_to_graph
+from spdx_tools.spdx.writer.write_utils import validate_and_deduplicate
+
+
+def write_document_to_stream(
+ document: Document, stream: IO[bytes], validate: bool = True, drop_duplicates: bool = True
+):
+ document = validate_and_deduplicate(document, validate, drop_duplicates)
+ graph = Graph()
+ doc_namespace = document.creation_info.document_namespace
+ external_doc_ref_to_namespace: Dict[str, str] = {
+ external_doc_ref.document_ref_id: external_doc_ref.document_uri
+ for external_doc_ref in document.creation_info.external_document_refs
+ }
+ doc_node = add_creation_info_to_graph(document.creation_info, graph)
+ for annotation in document.annotations:
+ add_annotation_to_graph(annotation, graph, doc_namespace, external_doc_ref_to_namespace)
+
+ for file in document.files:
+ add_file_to_graph(file, graph, doc_namespace, external_doc_ref_to_namespace)
+
+ for package in document.packages:
+ add_package_to_graph(package, graph, doc_namespace, external_doc_ref_to_namespace)
+
+ for relationship in document.relationships:
+ add_relationship_to_graph(relationship, graph, doc_namespace, external_doc_ref_to_namespace)
+
+ for snippet in document.snippets:
+ add_snippet_to_graph(snippet, graph, doc_namespace, external_doc_ref_to_namespace)
+
+ for extracted_licensing_info in document.extracted_licensing_info:
+ add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node, doc_namespace)
+
+ graph = to_isomorphic(graph)
+ graph.bind("spdx", SPDX_NAMESPACE)
+ graph.bind("doap", DOAP)
+ graph.bind("ptr", POINTER_NAMESPACE)
+ graph.serialize(stream, "pretty-xml", encoding="UTF-8", max_depth=100)
+
+
+def write_document_to_file(document: Document, file_name: str, validate: bool = True, drop_duplicates: bool = True):
+ with open(file_name, "wb") as out:
+ write_document_to_stream(document, out, validate, drop_duplicates)
diff --git a/src/spdx_tools/spdx/writer/rdf/relationship_writer.py b/src/spdx_tools/spdx/writer/rdf/relationship_writer.py
new file mode 100644
index 000000000..6d49431af
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/relationship_writer.py
@@ -0,0 +1,46 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict
+from rdflib import RDF, RDFS, BNode, Graph, Literal, URIRef
+
+from spdx_tools.spdx.casing_tools import snake_case_to_camel_case
+from spdx_tools.spdx.model import Relationship, SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id
+
+
+def add_relationship_to_graph(
+ relationship: Relationship, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]
+):
+ relationship_node = BNode()
+ graph.add((relationship_node, RDF.type, SPDX_NAMESPACE.Relationship))
+ graph.add(
+ (
+ relationship_node,
+ SPDX_NAMESPACE.relationshipType,
+ SPDX_NAMESPACE[f"relationshipType_{snake_case_to_camel_case(relationship.relationship_type.name)}"],
+ )
+ )
+ if isinstance(relationship.related_spdx_element_id, SpdxNone):
+ graph.add((relationship_node, SPDX_NAMESPACE.relatedSpdxElement, SPDX_NAMESPACE.none))
+ elif isinstance(relationship.related_spdx_element_id, SpdxNoAssertion):
+ graph.add((relationship_node, SPDX_NAMESPACE.relatedSpdxElement, SPDX_NAMESPACE.noassertion))
+ else:
+ graph.add(
+ (
+ relationship_node,
+ SPDX_NAMESPACE.relatedSpdxElement,
+ URIRef(
+ add_namespace_to_spdx_id(
+ relationship.related_spdx_element_id, doc_namespace, external_doc_ref_to_namespace
+ )
+ ),
+ )
+ )
+ if relationship.comment:
+ graph.add((relationship_node, RDFS.comment, Literal(relationship.comment)))
+ relationship_resource = URIRef(
+ add_namespace_to_spdx_id(relationship.spdx_element_id, doc_namespace, external_doc_ref_to_namespace)
+ )
+ graph.add((relationship_resource, SPDX_NAMESPACE.relationship, relationship_node))
diff --git a/src/spdx_tools/spdx/writer/rdf/snippet_writer.py b/src/spdx_tools/spdx/writer/rdf/snippet_writer.py
new file mode 100644
index 000000000..0b8cd4e95
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/snippet_writer.py
@@ -0,0 +1,65 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict, Optional, Tuple
+from rdflib import RDF, RDFS, BNode, Graph, Literal, URIRef
+
+from spdx_tools.spdx.model import Snippet
+from spdx_tools.spdx.rdfschema.namespace import POINTER_NAMESPACE, SPDX_NAMESPACE
+from spdx_tools.spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion
+from spdx_tools.spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id, add_optional_literal
+
+
+def add_snippet_to_graph(
+ snippet: Snippet, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]
+):
+ snippet_resource = URIRef(add_namespace_to_spdx_id(snippet.spdx_id, doc_namespace, external_doc_ref_to_namespace))
+ graph.add((snippet_resource, RDF.type, SPDX_NAMESPACE.Snippet))
+
+ snippet_from_file_ref = URIRef(
+ add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_ref_to_namespace)
+ )
+ graph.add((snippet_resource, SPDX_NAMESPACE.snippetFromFile, snippet_from_file_ref))
+ add_range_to_graph(
+ snippet.byte_range, graph, snippet_resource, snippet_from_file_ref, POINTER_NAMESPACE.ByteOffsetPointer
+ )
+ add_range_to_graph(
+ snippet.line_range, graph, snippet_resource, snippet_from_file_ref, POINTER_NAMESPACE.LineCharPointer
+ )
+ add_license_expression_or_none_or_no_assertion(
+ snippet.license_concluded, graph, snippet_resource, SPDX_NAMESPACE.licenseConcluded, doc_namespace
+ )
+ add_license_expression_or_none_or_no_assertion(
+ snippet.license_info_in_snippet, graph, snippet_resource, SPDX_NAMESPACE.licenseInfoInSnippet, doc_namespace
+ )
+ add_optional_literal(snippet.license_comment, graph, snippet_resource, SPDX_NAMESPACE.licenseComments)
+ add_optional_literal(snippet.copyright_text, graph, snippet_resource, SPDX_NAMESPACE.copyrightText)
+ add_optional_literal(snippet.comment, graph, snippet_resource, RDFS.comment)
+ add_optional_literal(snippet.name, graph, snippet_resource, SPDX_NAMESPACE.name)
+ for attribution_text in snippet.attribution_texts:
+ graph.add((snippet_resource, SPDX_NAMESPACE.attributionText, Literal(attribution_text)))
+
+
+def add_range_to_graph(
+ range_information: Optional[Tuple[int, int]],
+ graph: Graph,
+ snippet_node: URIRef,
+ snippet_from_file_ref: URIRef,
+ pointer_class: URIRef,
+):
+ start_end_pointer = BNode()
+ graph.add((start_end_pointer, RDF.type, POINTER_NAMESPACE.StartEndPointer))
+ for predicate, value in [
+ (POINTER_NAMESPACE.startPointer, range_information[0]),
+ (POINTER_NAMESPACE.endPointer, range_information[1]),
+ ]:
+ pointer_node = BNode()
+ graph.add((pointer_node, RDF.type, pointer_class))
+ graph.add((start_end_pointer, predicate, pointer_node))
+ graph.add((pointer_node, POINTER_NAMESPACE.reference, snippet_from_file_ref))
+ if pointer_class == POINTER_NAMESPACE.ByteOffsetPointer:
+ graph.add((pointer_node, POINTER_NAMESPACE.offset, Literal(value)))
+ else:
+ graph.add((pointer_node, POINTER_NAMESPACE.lineNumber, Literal(value)))
+
+ graph.add((snippet_node, SPDX_NAMESPACE.range, start_end_pointer))
diff --git a/src/spdx_tools/spdx/writer/rdf/writer_utils.py b/src/spdx_tools/spdx/writer/rdf/writer_utils.py
new file mode 100644
index 000000000..fb27aa18b
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/rdf/writer_utils.py
@@ -0,0 +1,61 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import logging
+from datetime import datetime
+
+from beartype.typing import Any, Dict, Optional
+from rdflib import Graph, Literal
+from rdflib.term import Node
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.model import SpdxNoAssertion, SpdxNone
+from spdx_tools.spdx.rdfschema.namespace import SPDX_NAMESPACE
+from spdx_tools.spdx.validation.spdx_id_validators import is_valid_internal_spdx_id
+
+
+def add_optional_literal(value: Any, graph: Graph, parent: Node, predicate: Node):
+ if value is None:
+ return
+ if isinstance(value, list):
+ for element in value:
+ graph.add((parent, predicate, Literal(str(element))))
+ return
+ graph.add((parent, predicate, Literal(str(value))))
+
+
+def add_literal_or_no_assertion_or_none(value: Any, graph: Graph, parent: Node, predicate: Node):
+ if value is None:
+ return
+ if isinstance(value, SpdxNone):
+ graph.add((parent, predicate, SPDX_NAMESPACE.none))
+ return
+ add_literal_or_no_assertion(value, graph, parent, predicate)
+
+
+def add_literal_or_no_assertion(value: Any, graph: Graph, parent: Node, predicate: Node):
+ if value is None:
+ return
+ if isinstance(value, SpdxNoAssertion):
+ graph.add((parent, predicate, SPDX_NAMESPACE.noassertion))
+ return
+ add_optional_literal(value, graph, parent, predicate)
+
+
+def add_datetime_to_graph(value: Optional[datetime], graph: Graph, parent: Node, predicate: Node):
+ if value:
+ graph.add((parent, predicate, Literal(datetime_to_iso_string(value))))
+
+
+def add_namespace_to_spdx_id(spdx_id: str, doc_namespace: str, external_doc_namespaces: Dict[str, str]) -> str:
+ if ":" in spdx_id:
+ external_doc_ref_id = spdx_id.split(":")[0]
+ if external_doc_ref_id not in external_doc_namespaces.keys():
+ logging.warning(f"No namespace for external document reference with id {external_doc_ref_id} provided.")
+ return spdx_id
+ return f"{external_doc_namespaces[external_doc_ref_id]}#{spdx_id.split(':')[1]}"
+
+ if is_valid_internal_spdx_id(spdx_id):
+ return f"{doc_namespace}#{spdx_id}"
+
+ return spdx_id
diff --git a/src/spdx_tools/spdx/writer/tagvalue/__init__.py b/src/spdx_tools/spdx/writer/tagvalue/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/writer/tagvalue/annotation_writer.py b/src/spdx_tools/spdx/writer/tagvalue/annotation_writer.py
new file mode 100644
index 000000000..71637073f
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/annotation_writer.py
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.model import Annotation
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_text_value, write_value
+
+
+def write_annotation(annotation: Annotation, text_output: TextIO):
+ write_value("Annotator", annotation.annotator.to_serialized_string(), text_output)
+ write_value("AnnotationDate", datetime_to_iso_string(annotation.annotation_date), text_output)
+ write_value("AnnotationType", annotation.annotation_type.name, text_output)
+ write_value("SPDXREF", annotation.spdx_id, text_output)
+ write_text_value("AnnotationComment", annotation.annotation_comment, text_output)
diff --git a/src/spdx_tools/spdx/writer/tagvalue/checksum_writer.py b/src/spdx_tools/spdx/writer/tagvalue/checksum_writer.py
new file mode 100644
index 000000000..cf87e1a4e
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/checksum_writer.py
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from spdx_tools.spdx.model import Checksum, ChecksumAlgorithm
+
+
+def write_checksum_to_tag_value(checksum: Checksum) -> str:
+ algorithm_name: str = checksum.algorithm.name
+ # Convert underscores to dashes, and other Blake2b-specific casing rules
+ if "_" in algorithm_name:
+ algorithm_name = CHECKSUM_ALGORITHM_TO_TV.get(algorithm_name)
+ if algorithm_name is None:
+ raise ValueError(f"Missing conversion rule for converting {checksum.algorithm.name} to tag-value string")
+ return f"{algorithm_name}: {checksum.value}"
+
+
+CHECKSUM_ALGORITHM_TO_TV = {
+ ChecksumAlgorithm.BLAKE2B_256.name: "BLAKE2b-256",
+ ChecksumAlgorithm.BLAKE2B_384.name: "BLAKE2b-384",
+ ChecksumAlgorithm.BLAKE2B_512.name: "BLAKE2b-512",
+ ChecksumAlgorithm.SHA3_256.name: "SHA3-256",
+ ChecksumAlgorithm.SHA3_384.name: "SHA3-384",
+ ChecksumAlgorithm.SHA3_512.name: "SHA3-512",
+}
diff --git a/src/spdx_tools/spdx/writer/tagvalue/creation_info_writer.py b/src/spdx_tools/spdx/writer/tagvalue/creation_info_writer.py
new file mode 100644
index 000000000..81fd3586c
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/creation_info_writer.py
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.model import CreationInfo
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import (
+ write_optional_heading,
+ write_separator,
+ write_text_value,
+ write_value,
+)
+
+
+def write_creation_info(creation_info: CreationInfo, text_output: TextIO):
+ write_value("SPDXVersion", creation_info.spdx_version, text_output)
+ write_value("DataLicense", creation_info.data_license, text_output)
+ write_value("SPDXID", creation_info.spdx_id, text_output)
+ write_value("DocumentName", creation_info.name, text_output)
+ write_value("DocumentNamespace", creation_info.document_namespace, text_output)
+ write_text_value("DocumentComment", creation_info.document_comment, text_output)
+
+ write_optional_heading(creation_info.external_document_refs, "\n## External Document References\n", text_output)
+ for external_document_ref in creation_info.external_document_refs:
+ external_document_ref_str = " ".join(
+ [
+ external_document_ref.document_ref_id,
+ external_document_ref.document_uri,
+ external_document_ref.checksum.algorithm.name + ": " + external_document_ref.checksum.value,
+ ]
+ )
+ write_value("ExternalDocumentRef", external_document_ref_str, text_output)
+ write_separator(text_output)
+
+ text_output.write("## Creation Information\n")
+ write_value("LicenseListVersion", creation_info.license_list_version, text_output)
+ for creator in creation_info.creators:
+ write_value("Creator", creator.to_serialized_string(), text_output)
+ write_value("Created", datetime_to_iso_string(creation_info.created), text_output)
+ write_text_value("CreatorComment", creation_info.creator_comment, text_output)
diff --git a/src/spdx_tools/spdx/writer/tagvalue/extracted_licensing_info_writer.py b/src/spdx_tools/spdx/writer/tagvalue/extracted_licensing_info_writer.py
new file mode 100644
index 000000000..0e89faa34
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/extracted_licensing_info_writer.py
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.model import ExtractedLicensingInfo
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_text_value, write_value
+
+
+def write_extracted_licensing_info(extracted_licensing_info: ExtractedLicensingInfo, text_output: TextIO):
+ write_value("LicenseID", extracted_licensing_info.license_id, text_output)
+ write_text_value("ExtractedText", extracted_licensing_info.extracted_text, text_output)
+ write_value("LicenseName", extracted_licensing_info.license_name, text_output)
+
+ for cross_reference in sorted(extracted_licensing_info.cross_references):
+ write_value("LicenseCrossReference", cross_reference, text_output)
+
+ write_text_value("LicenseComment", extracted_licensing_info.comment, text_output)
diff --git a/src/spdx_tools/spdx/writer/tagvalue/file_writer.py b/src/spdx_tools/spdx/writer/tagvalue/file_writer.py
new file mode 100644
index 000000000..0b1d8c8f5
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/file_writer.py
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.model import File
+from spdx_tools.spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_text_value, write_value
+
+
+def write_file(file: File, text_output: TextIO):
+ text_output.write("## File Information\n")
+
+ write_value("FileName", file.name, text_output)
+ write_value("SPDXID", file.spdx_id, text_output)
+
+ for file_type in file.file_types:
+ write_value("FileType", file_type.name, text_output)
+
+ for file_checksum in file.checksums:
+ write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output)
+
+ write_value("LicenseConcluded", file.license_concluded, text_output)
+ for license_info in file.license_info_in_file:
+ write_value("LicenseInfoInFile", license_info, text_output)
+ write_text_value("LicenseComments", file.license_comment, text_output)
+ write_text_value("FileCopyrightText", file.copyright_text, text_output)
+
+ write_text_value("FileComment", file.comment, text_output)
+ write_text_value("FileNotice", file.notice, text_output)
+
+ for contributor in sorted(file.contributors):
+ write_value("FileContributor", contributor, text_output)
+
+ for attribution_text in file.attribution_texts:
+ write_text_value("FileAttributionText", attribution_text, text_output)
diff --git a/src/spdx_tools/spdx/writer/tagvalue/package_writer.py b/src/spdx_tools/spdx/writer/tagvalue/package_writer.py
new file mode 100644
index 000000000..f61474fa6
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/package_writer.py
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+from spdx_tools.spdx.model import Package, PackageVerificationCode
+from spdx_tools.spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import (
+ transform_enum_name_to_tv,
+ write_actor,
+ write_text_value,
+ write_value,
+)
+
+
+def write_package(package: Package, text_output: TextIO):
+ text_output.write("## Package Information\n")
+
+ write_value("PackageName", package.name, text_output)
+ write_value("SPDXID", package.spdx_id, text_output)
+ write_value("PackageVersion", package.version, text_output)
+ write_value("PackageFileName", package.file_name, text_output)
+ write_actor("PackageSupplier", package.supplier, text_output)
+ write_actor("PackageOriginator", package.originator, text_output)
+ write_value("PackageDownloadLocation", package.download_location, text_output)
+
+ write_value("FilesAnalyzed", str(package.files_analyzed).lower(), text_output)
+ if package.verification_code:
+ package_verification_code = get_package_verification_code_string(package.verification_code)
+ write_value("PackageVerificationCode", package_verification_code, text_output)
+
+ for package_checksum in package.checksums:
+ write_value("PackageChecksum", write_checksum_to_tag_value(package_checksum), text_output)
+
+ write_value("PackageHomePage", package.homepage, text_output)
+ write_text_value("PackageSourceInfo", package.source_info, text_output)
+
+ write_value("PackageLicenseConcluded", package.license_concluded, text_output)
+ for license_info in package.license_info_from_files:
+ write_value("PackageLicenseInfoFromFiles", license_info, text_output)
+ write_value("PackageLicenseDeclared", package.license_declared, text_output)
+ write_text_value("PackageLicenseComments", package.license_comment, text_output)
+ write_text_value("PackageCopyrightText", package.copyright_text, text_output)
+
+ write_text_value("PackageSummary", package.summary, text_output)
+ write_text_value("PackageDescription", package.description, text_output)
+ write_text_value("PackageComment", package.comment, text_output)
+
+ for external_reference in package.external_references:
+ external_reference_str = " ".join(
+ [
+ transform_enum_name_to_tv(external_reference.category.name),
+ external_reference.reference_type,
+ external_reference.locator,
+ ]
+ )
+ write_value("ExternalRef", external_reference_str, text_output)
+ if external_reference.comment:
+ write_text_value("ExternalRefComment", external_reference.comment, text_output)
+
+ for attribution_text in package.attribution_texts:
+ write_text_value("PackageAttributionText", attribution_text, text_output)
+
+ if package.primary_package_purpose:
+ write_value(
+ "PrimaryPackagePurpose", transform_enum_name_to_tv(package.primary_package_purpose.name), text_output
+ )
+
+ if package.release_date:
+ write_value("ReleaseDate", datetime_to_iso_string(package.release_date), text_output)
+ if package.built_date:
+ write_value("BuiltDate", datetime_to_iso_string(package.built_date), text_output)
+ if package.valid_until_date:
+ write_value("ValidUntilDate", datetime_to_iso_string(package.valid_until_date), text_output)
+
+
+def get_package_verification_code_string(verification_code: PackageVerificationCode) -> str:
+ if not verification_code.excluded_files:
+ return verification_code.value
+
+ excluded_files_str = " (excludes: " + " ".join(verification_code.excluded_files) + ")"
+ return verification_code.value + excluded_files_str
diff --git a/src/spdx_tools/spdx/writer/tagvalue/relationship_writer.py b/src/spdx_tools/spdx/writer/tagvalue/relationship_writer.py
new file mode 100644
index 000000000..446bc6fd9
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/relationship_writer.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.model import Relationship
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_text_value, write_value
+
+
+def write_relationship(relationship: Relationship, text_output: TextIO):
+ write_value(
+ "Relationship",
+ " ".join(
+ [
+ relationship.spdx_element_id,
+ relationship.relationship_type.name,
+ str(relationship.related_spdx_element_id),
+ ]
+ ),
+ text_output,
+ )
+ write_text_value("RelationshipComment", relationship.comment, text_output)
diff --git a/src/spdx_tools/spdx/writer/tagvalue/snippet_writer.py b/src/spdx_tools/spdx/writer/tagvalue/snippet_writer.py
new file mode 100644
index 000000000..f6449951f
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/snippet_writer.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.model import Snippet
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_range, write_text_value, write_value
+
+
+def write_snippet(snippet: Snippet, text_output: TextIO):
+ text_output.write("## Snippet Information\n")
+
+ write_value("SnippetSPDXID", snippet.spdx_id, text_output)
+ write_value("SnippetFromFileSPDXID", snippet.file_spdx_id, text_output)
+ write_range("SnippetByteRange", snippet.byte_range, text_output)
+ write_range("SnippetLineRange", snippet.line_range, text_output)
+
+ write_value("SnippetLicenseConcluded", snippet.license_concluded, text_output)
+ for license_info in snippet.license_info_in_snippet:
+ write_value("LicenseInfoInSnippet", license_info, text_output)
+ write_text_value("SnippetLicenseComments", snippet.license_comment, text_output)
+ write_text_value("SnippetCopyrightText", snippet.copyright_text, text_output)
+
+ write_text_value("SnippetComment", snippet.comment, text_output)
+ write_value("SnippetName", snippet.name, text_output)
+
+ for attribution_text in snippet.attribution_texts:
+ write_text_value("SnippetAttributionText", attribution_text, text_output)
diff --git a/src/spdx_tools/spdx/writer/tagvalue/tagvalue_writer.py b/src/spdx_tools/spdx/writer/tagvalue/tagvalue_writer.py
new file mode 100644
index 000000000..6e8de2e94
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/tagvalue_writer.py
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx.model import Document, Relationship, RelationshipType
+from spdx_tools.spdx.writer.tagvalue.annotation_writer import write_annotation
+from spdx_tools.spdx.writer.tagvalue.creation_info_writer import write_creation_info
+from spdx_tools.spdx.writer.tagvalue.extracted_licensing_info_writer import write_extracted_licensing_info
+from spdx_tools.spdx.writer.tagvalue.file_writer import write_file
+from spdx_tools.spdx.writer.tagvalue.package_writer import write_package
+from spdx_tools.spdx.writer.tagvalue.relationship_writer import write_relationship
+from spdx_tools.spdx.writer.tagvalue.snippet_writer import write_snippet
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import (
+ get_file_ids_with_contained_snippets,
+ scan_relationships,
+ write_list_of_elements,
+ write_optional_heading,
+ write_separator,
+)
+from spdx_tools.spdx.writer.write_utils import validate_and_deduplicate
+
+
+def write_document_to_stream(document: Document, stream: TextIO, validate: bool = True, drop_duplicates: bool = True):
+ document = validate_and_deduplicate(document, validate, drop_duplicates)
+ write_document(document, stream)
+
+
+def write_document_to_file(document: Document, file_name: str, validate: bool = True, drop_duplicates: bool = True):
+ with open(file_name, "w", encoding="utf-8") as out:
+ write_document_to_stream(document, out, validate, drop_duplicates)
+
+
+def write_document(document: Document, text_output: TextIO):
+ relationships_to_write, contained_files_by_package_id = scan_relationships(
+ document.relationships, document.packages, document.files
+ )
+ file_ids_with_contained_snippets = get_file_ids_with_contained_snippets(document.snippets, document.files)
+ packaged_file_ids = [file.spdx_id for files_list in contained_files_by_package_id.values() for file in files_list]
+ filed_snippet_ids = [
+ snippet.spdx_id for snippets_list in file_ids_with_contained_snippets.values() for snippet in snippets_list
+ ]
+
+ text_output.write("## Document Information\n")
+ write_creation_info(document.creation_info, text_output)
+ write_separator(text_output)
+
+ for snippet in document.snippets:
+ if snippet.spdx_id not in filed_snippet_ids:
+ write_snippet(snippet, text_output)
+ write_separator(text_output)
+
+ for file in document.files:
+ if file.spdx_id not in packaged_file_ids:
+ write_file(file, text_output)
+ write_separator(text_output)
+ if file.spdx_id in file_ids_with_contained_snippets:
+ write_list_of_elements(
+ file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output, with_separator=True
+ )
+
+ already_written_file_ids = [] # a file can belong to multiple packages but must appear only once
+ for package in document.packages:
+ write_package(package, text_output)
+ write_separator(text_output)
+ if package.spdx_id in contained_files_by_package_id:
+ for file in contained_files_by_package_id[package.spdx_id]:
+ if file.spdx_id in already_written_file_ids:
+ relationships_to_write.append(
+ Relationship(package.spdx_id, RelationshipType.CONTAINS, file.spdx_id)
+ )
+ else:
+ write_file(file, text_output)
+ write_separator(text_output)
+ if file.spdx_id in file_ids_with_contained_snippets:
+ write_list_of_elements(
+ file_ids_with_contained_snippets[file.spdx_id],
+ write_snippet,
+ text_output,
+ with_separator=True,
+ )
+ already_written_file_ids.append(file.spdx_id)
+
+ write_optional_heading(document.extracted_licensing_info, "## License Information\n", text_output)
+ write_list_of_elements(
+ document.extracted_licensing_info, write_extracted_licensing_info, text_output, with_separator=True
+ )
+
+ write_optional_heading(relationships_to_write, "## Relationships\n", text_output)
+ write_list_of_elements(relationships_to_write, write_relationship, text_output)
+ write_separator(text_output)
+
+ write_optional_heading(document.annotations, "## Annotations\n", text_output)
+ write_list_of_elements(document.annotations, write_annotation, text_output, with_separator=True)
diff --git a/src/spdx_tools/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/spdx_tools/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py
new file mode 100644
index 000000000..98f670252
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import Any, Callable, Dict, List, Optional, TextIO, Tuple, Union
+from license_expression import LicenseExpression
+
+from spdx_tools.spdx.model import (
+ Actor,
+ File,
+ Package,
+ Relationship,
+ RelationshipType,
+ Snippet,
+ SpdxNoAssertion,
+ SpdxNone,
+)
+
+
+def write_separator(out: TextIO):
+ out.write("\n")
+
+
+def write_value(
+ tag: str, value: Optional[Union[bool, str, SpdxNone, SpdxNoAssertion, LicenseExpression]], out: TextIO
+):
+ if value is not None:
+ out.write(f"{tag}: {value}\n")
+
+
+def write_range(tag: str, value: Optional[Tuple[int, int]], out: TextIO):
+ if value:
+ out.write(f"{tag}: {value[0]}:{value[1]}\n")
+
+
+def write_text_value(tag: str, value: Optional[Union[str, SpdxNone, SpdxNoAssertion]], out: TextIO):
+ if isinstance(value, str) and "\n" in value:
+ out.write(f"{tag}: {value} \n")
+ else:
+ write_value(tag, value, out)
+
+
+def transform_enum_name_to_tv(enum_str: str) -> str:
+ return enum_str.replace("_", "-")
+
+
+def write_optional_heading(optional_field: Any, heading: str, text_output: TextIO):
+ if optional_field:
+ text_output.write(heading)
+
+
+def write_list_of_elements(
+ list_of_elements: List[Any],
+ write_method: Callable[[Any, TextIO], None],
+ text_output: TextIO,
+ with_separator: bool = False,
+):
+ for element in list_of_elements:
+ write_method(element, text_output)
+ if with_separator:
+ write_separator(text_output)
+
+
+def write_actor(tag: str, element_to_write: Optional[Union[Actor, SpdxNoAssertion]], text_output: TextIO):
+ if isinstance(element_to_write, Actor):
+ write_value(tag, element_to_write.to_serialized_string(), text_output)
+ else:
+ write_value(tag, element_to_write, text_output)
+
+
+def scan_relationships(
+ relationships: List[Relationship], packages: List[Package], files: List[File]
+) -> Tuple[List, Dict]:
+ contained_files_by_package_id = dict()
+ relationships_to_write = []
+ files_by_spdx_id = {file.spdx_id: file for file in files}
+ packages_spdx_ids = [package.spdx_id for package in packages]
+ for relationship in relationships:
+ if relationship.related_spdx_element_id in [SpdxNoAssertion(), SpdxNone()]:
+ relationships_to_write.append(relationship)
+ elif (
+ relationship.relationship_type == RelationshipType.CONTAINS
+ and relationship.spdx_element_id in packages_spdx_ids
+ and relationship.related_spdx_element_id in files_by_spdx_id.keys()
+ ):
+ contained_files_by_package_id.setdefault(relationship.spdx_element_id, []).append(
+ files_by_spdx_id[relationship.related_spdx_element_id]
+ )
+ if relationship.comment:
+ relationships_to_write.append(relationship)
+ elif (
+ relationship.relationship_type == RelationshipType.CONTAINED_BY
+ and relationship.related_spdx_element_id in packages_spdx_ids
+ and relationship.spdx_element_id in files_by_spdx_id
+ ):
+ contained_files_by_package_id.setdefault(relationship.related_spdx_element_id, []).append(
+ files_by_spdx_id[relationship.spdx_element_id]
+ )
+ if relationship.comment:
+ relationships_to_write.append(relationship)
+ else:
+ relationships_to_write.append(relationship)
+
+ return relationships_to_write, contained_files_by_package_id
+
+
+def get_file_ids_with_contained_snippets(snippets: List[Snippet], files: List[File]) -> Dict:
+ file_ids_with_contained_snippets = dict()
+ file_spdx_ids: List[str] = [file.spdx_id for file in files]
+ for snippet in snippets:
+ if snippet.file_spdx_id in file_spdx_ids:
+ file_ids_with_contained_snippets.setdefault(snippet.file_spdx_id, []).append(snippet)
+
+ return file_ids_with_contained_snippets
diff --git a/src/spdx_tools/spdx/writer/write_anything.py b/src/spdx_tools/spdx/writer/write_anything.py
new file mode 100644
index 000000000..d811b35c9
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/write_anything.py
@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2022 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx.formats import FileFormat, file_name_to_format
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.writer.json import json_writer
+from spdx_tools.spdx.writer.rdf import rdf_writer
+from spdx_tools.spdx.writer.tagvalue import tagvalue_writer
+from spdx_tools.spdx.writer.xml import xml_writer
+from spdx_tools.spdx.writer.yaml import yaml_writer
+
+
+def write_file(document: Document, file_name: str, validate: bool = True):
+ output_format = file_name_to_format(file_name)
+ if output_format == FileFormat.JSON:
+ json_writer.write_document_to_file(document, file_name, validate)
+ elif output_format == FileFormat.YAML:
+ yaml_writer.write_document_to_file(document, file_name, validate)
+ elif output_format == FileFormat.XML:
+ xml_writer.write_document_to_file(document, file_name, validate)
+ elif output_format == FileFormat.TAG_VALUE:
+ tagvalue_writer.write_document_to_file(document, file_name, validate)
+ elif output_format == FileFormat.RDF_XML:
+ rdf_writer.write_document_to_file(document, file_name, validate)
diff --git a/src/spdx_tools/spdx/writer/write_utils.py b/src/spdx_tools/spdx/writer/write_utils.py
new file mode 100644
index 000000000..13a48bb80
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/write_utils.py
@@ -0,0 +1,26 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+from spdx_tools.spdx.document_utils import create_document_without_duplicates
+from spdx_tools.spdx.jsonschema.document_converter import DocumentConverter
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
+from spdx_tools.spdx.validation.validation_message import ValidationMessage
+
+
+def validate_and_deduplicate(document: Document, validate: bool = True, drop_duplicates: bool = True) -> Document:
+ if validate:
+ validation_messages: List[ValidationMessage] = validate_full_spdx_document(document)
+ if validation_messages:
+ raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}")
+ if drop_duplicates:
+ document = create_document_without_duplicates(document)
+ return document
+
+
+def convert(document: Document, converter: DocumentConverter) -> dict:
+ if converter is None:
+ converter = DocumentConverter()
+ return converter.convert(document)
diff --git a/src/spdx_tools/spdx/writer/xml/__init__.py b/src/spdx_tools/spdx/writer/xml/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/writer/xml/xml_writer.py b/src/spdx_tools/spdx/writer/xml/xml_writer.py
new file mode 100644
index 000000000..9be152928
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/xml/xml_writer.py
@@ -0,0 +1,37 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import xmltodict
+from beartype.typing import IO
+
+from spdx_tools.spdx.jsonschema.document_converter import DocumentConverter
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.writer.write_utils import convert, validate_and_deduplicate
+
+
+def write_document_to_stream(
+ document: Document,
+ stream: IO[str],
+ validate: bool = True,
+ converter: DocumentConverter = None,
+ drop_duplicates: bool = True,
+):
+ """
+ Serializes the provided document to XML and writes it to a file with the provided name. Unless validate is set
+ to False, validates the document before serialization. Unless a DocumentConverter instance is provided,
+ a new one is created.
+ """
+ document = validate_and_deduplicate(document, validate, drop_duplicates)
+ document_dict = {"Document": convert(document, converter)}
+ xmltodict.unparse(document_dict, stream, encoding="utf-8", pretty=True)
+
+
+def write_document_to_file(
+ document: Document,
+ file_name: str,
+ validate: bool = True,
+ converter: DocumentConverter = None,
+ drop_duplicates: bool = True,
+):
+ with open(file_name, "w", encoding="utf-8") as out:
+ write_document_to_stream(document, out, validate, converter, drop_duplicates)
diff --git a/src/spdx_tools/spdx/writer/yaml/__init__.py b/src/spdx_tools/spdx/writer/yaml/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx/writer/yaml/yaml_writer.py b/src/spdx_tools/spdx/writer/yaml/yaml_writer.py
new file mode 100644
index 000000000..b3e105dc6
--- /dev/null
+++ b/src/spdx_tools/spdx/writer/yaml/yaml_writer.py
@@ -0,0 +1,37 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import yaml
+from beartype.typing import IO
+
+from spdx_tools.spdx.jsonschema.document_converter import DocumentConverter
+from spdx_tools.spdx.model import Document
+from spdx_tools.spdx.writer.write_utils import convert, validate_and_deduplicate
+
+
+def write_document_to_stream(
+ document: Document,
+ stream: IO[str],
+ validate: bool = True,
+ converter: DocumentConverter = None,
+ drop_duplicates: bool = True,
+):
+ """
+ Serializes the provided document to yaml and writes it to a file with the provided name. Unless validate is set
+ to False, validates the document before serialization. Unless a DocumentConverter instance is provided,
+ a new one is created.
+ """
+ document = validate_and_deduplicate(document, validate, drop_duplicates)
+ document_dict = convert(document, converter)
+ yaml.safe_dump(document_dict, stream, indent=2)
+
+
+def write_document_to_file(
+ document: Document,
+ file_name: str,
+ validate: bool = True,
+ converter: DocumentConverter = None,
+ drop_duplicates: bool = True,
+):
+ with open(file_name, "w", encoding="utf-8") as out:
+ write_document_to_stream(document, out, validate, converter, drop_duplicates)
diff --git a/src/spdx_tools/spdx3/__init__.py b/src/spdx_tools/spdx3/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/__init__.py b/src/spdx_tools/spdx3/bump_from_spdx2/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/actor.py b/src/spdx_tools/spdx3/bump_from_spdx2/actor.py
new file mode 100644
index 000000000..3283bf11a
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/actor.py
@@ -0,0 +1,53 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from typing import Optional
+
+from beartype.typing import List
+
+from spdx_tools.spdx3.model import CreationInfo, ExternalIdentifier, ExternalIdentifierType, Organization, Person, Tool
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model.actor import Actor as Spdx2_Actor
+from spdx_tools.spdx.model.actor import ActorType
+
+
+def bump_actor(
+ spdx2_actor: Spdx2_Actor, payload: Payload, document_namespace: str, creation_info: Optional[CreationInfo] = None
+) -> str:
+ name: str = spdx2_actor.name
+ email: str = spdx2_actor.email
+ actor_type: ActorType = spdx2_actor.actor_type
+
+ external_identifiers: List[ExternalIdentifier] = []
+ name_without_whitespace = "".join(name.split())
+ if email:
+ external_identifiers.append(ExternalIdentifier(ExternalIdentifierType.EMAIL, email))
+ spdx_id: str = f"{document_namespace}#SPDXRef-Actor-{name_without_whitespace}-{email}"
+ else:
+ spdx_id: str = f"{document_namespace}#SPDXRef-Actor-{name_without_whitespace}"
+
+ if spdx_id in payload.get_full_map(): # the agent/tool already exists, so we don't need to create a new one
+ return spdx_id
+
+ value_dict = {
+ "spdx_id": spdx_id,
+ "creation_info": creation_info,
+ "name": name,
+ "external_identifier": external_identifiers,
+ }
+
+ if actor_type == ActorType.PERSON:
+ agent_or_tool = Person(**value_dict)
+
+ elif actor_type == ActorType.ORGANIZATION:
+ agent_or_tool = Organization(**value_dict)
+
+ elif actor_type == ActorType.TOOL:
+ agent_or_tool = Tool(**value_dict)
+
+ else:
+ raise ValueError(f"no conversion rule defined for ActorType {actor_type}")
+
+ payload.add_element(agent_or_tool)
+
+ return spdx_id
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/annotation.py b/src/spdx_tools/spdx3/bump_from_spdx2/annotation.py
new file mode 100644
index 000000000..bae61160c
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/annotation.py
@@ -0,0 +1,49 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from copy import deepcopy
+
+from spdx_tools.spdx3.bump_from_spdx2.actor import bump_actor
+from spdx_tools.spdx3.bump_from_spdx2.message import print_missing_conversion
+from spdx_tools.spdx3.model import Annotation, AnnotationType, CreationInfo
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model.actor import ActorType
+from spdx_tools.spdx.model.annotation import Annotation as Spdx2_Annotation
+
+
+def bump_annotation(
+ spdx2_annotation: Spdx2_Annotation,
+ payload: Payload,
+ creation_info: CreationInfo,
+ document_namespace: str,
+ counter: int,
+):
+ spdx_id: str = "#".join([document_namespace, f"SPDXRef-Annotation-{counter}"])
+ creation_info = deepcopy(creation_info)
+ creation_info.created = spdx2_annotation.annotation_date
+
+ # caution: the annotator and the annotation will only share the same creation_info if the actor
+ # has not been previously defined
+ annotator = spdx2_annotation.annotator
+ creator_id: str = bump_actor(annotator, payload, document_namespace, creation_info)
+ if annotator.actor_type in [ActorType.PERSON, ActorType.ORGANIZATION]:
+ creation_info.created_by = [creator_id]
+ else:
+ print_missing_conversion(
+ "Annotator",
+ 0,
+ "The SPDX2 annotation is not of Type Person or Organization."
+ " This case leads to an invalid SPDX3 document and is currently not supported."
+ "https://github.com/spdx/spdx-3-model/issues/180",
+ )
+ annotation_type: AnnotationType = AnnotationType[spdx2_annotation.annotation_type.name]
+
+ payload.add_element(
+ Annotation(
+ spdx_id,
+ annotation_type,
+ creation_info=creation_info,
+ subject=f"{document_namespace}#{spdx2_annotation.spdx_id}",
+ statement=spdx2_annotation.annotation_comment,
+ )
+ )
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/bump_utils.py b/src/spdx_tools/spdx3/bump_from_spdx2/bump_utils.py
new file mode 100644
index 000000000..f4b6a4bf9
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/bump_utils.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Optional, Union
+
+from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
+from spdx_tools.spdx.model.spdx_none import SpdxNone
+
+
+def handle_no_assertion_or_none(field: Union[SpdxNone, SpdxNoAssertion, str], field_name: str) -> Optional[str]:
+ if isinstance(field, SpdxNone):
+ print(f"{field_name}: Missing conversion for SpdxNone.")
+ return None
+ if isinstance(field, SpdxNoAssertion):
+ return None
+ if isinstance(field, str):
+ return field
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/checksum.py b/src/spdx_tools/spdx3/bump_from_spdx2/checksum.py
new file mode 100644
index 000000000..ae056081f
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/checksum.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model import Hash, HashAlgorithm
+from spdx_tools.spdx.model.checksum import Checksum as Spdx2_Checksum
+from spdx_tools.spdx.model.checksum import ChecksumAlgorithm
+
+
+def bump_checksum(spdx2_checksum: Spdx2_Checksum) -> Hash:
+ algorithm: HashAlgorithm = convert_checksum_algorithm_to_hash_algorithm(spdx2_checksum.algorithm)
+ value: str = spdx2_checksum.value
+
+ return Hash(algorithm, value)
+
+
+def convert_checksum_algorithm_to_hash_algorithm(checksum_algorithm: ChecksumAlgorithm) -> HashAlgorithm:
+ if checksum_algorithm.name.startswith("BLAKE"):
+ return HashAlgorithm[checksum_algorithm.name.replace("_", "")]
+ if checksum_algorithm == ChecksumAlgorithm.ADLER32:
+ return HashAlgorithm.OTHER
+ return HashAlgorithm[checksum_algorithm.name]
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/creation_info.py b/src/spdx_tools/spdx3/bump_from_spdx2/creation_info.py
new file mode 100644
index 000000000..914d12226
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/creation_info.py
@@ -0,0 +1,79 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+from semantic_version import Version
+
+from spdx_tools.spdx3.bump_from_spdx2.actor import bump_actor
+from spdx_tools.spdx3.bump_from_spdx2.external_document_ref import bump_external_document_ref
+from spdx_tools.spdx3.bump_from_spdx2.message import print_missing_conversion
+from spdx_tools.spdx3.model import CreationInfo, ProfileIdentifierType, SpdxDocument
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model.actor import ActorType
+from spdx_tools.spdx.model.document import CreationInfo as Spdx2_CreationInfo
+
+
+def bump_creation_info(spdx2_creation_info: Spdx2_CreationInfo, payload: Payload) -> SpdxDocument:
+ document_namespace = spdx2_creation_info.document_namespace
+ spdx_id = f"{document_namespace}#{spdx2_creation_info.spdx_id}"
+
+ print_missing_conversion("creation_info.document_namespace", 0, "https://github.com/spdx/spdx-3-model/issues/87")
+
+ namespaces, imports = (
+ zip(
+ *[
+ bump_external_document_ref(external_document_ref)
+ for external_document_ref in spdx2_creation_info.external_document_refs
+ ]
+ )
+ if spdx2_creation_info.external_document_refs
+ else ([], [])
+ )
+ namespaces = list(namespaces)
+ imports = list(imports)
+ print_missing_conversion(
+ "creation_info.license_list_version",
+ 0,
+ "part of licensing profile, " "https://github.com/spdx/spdx-3-model/issues/131",
+ )
+ creation_info = CreationInfo(
+ spec_version=Version("3.0.0"),
+ created=spdx2_creation_info.created,
+ created_by=[],
+ profile=[ProfileIdentifierType.CORE, ProfileIdentifierType.SOFTWARE, ProfileIdentifierType.LICENSING],
+ data_license="https://spdx.org/licenses/" + spdx2_creation_info.data_license,
+ )
+
+ # due to creators having a creation_info themselves which inherits from the document's one,
+ # we have to add them after the creation_info has been initialized
+ creator_ids: List[str] = []
+ tool_ids: List[str] = []
+ for creator in spdx2_creation_info.creators:
+ bumped_actor_id = bump_actor(creator, payload, document_namespace, creation_info)
+ if creator.actor_type in [ActorType.PERSON, ActorType.ORGANIZATION]:
+ creator_ids.append(bumped_actor_id)
+ else:
+ tool_ids.append(bumped_actor_id)
+
+ if not creator_ids:
+ print_missing_conversion(
+ "Creators",
+ 0,
+ "The SPDX2 creation_info does not contain creators of Type Person or Organization."
+ " This case leads to an invalid SPDX3 document and is currently not supported."
+ "https://github.com/spdx/spdx-3-model/issues/180",
+ )
+
+ creation_info.created_by = creator_ids
+ creation_info.created_using = tool_ids
+
+ return SpdxDocument(
+ spdx_id=spdx_id,
+ creation_info=creation_info,
+ name=spdx2_creation_info.name,
+ comment=spdx2_creation_info.document_comment,
+ element=[],
+ root_element=[],
+ imports=imports,
+ namespaces=namespaces,
+ )
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/external_document_ref.py b/src/spdx_tools/spdx3/bump_from_spdx2/external_document_ref.py
new file mode 100644
index 000000000..41360ffa4
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/external_document_ref.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Tuple
+
+from spdx_tools.spdx3.bump_from_spdx2.checksum import bump_checksum
+from spdx_tools.spdx3.model import ExternalMap, Hash, NamespaceMap
+from spdx_tools.spdx.model.external_document_ref import ExternalDocumentRef
+
+
+def bump_external_document_ref(external_document_ref: ExternalDocumentRef) -> Tuple[NamespaceMap, ExternalMap]:
+ verified_using: List[Hash] = [bump_checksum(external_document_ref.checksum)]
+
+ return NamespaceMap(external_document_ref.document_ref_id, external_document_ref.document_uri + "#"), ExternalMap(
+ external_id=f"{external_document_ref.document_ref_id}:SPDXRef-DOCUMENT",
+ verified_using=verified_using,
+ )
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/file.py b/src/spdx_tools/spdx3/bump_from_spdx2/file.py
new file mode 100644
index 000000000..824f95a1f
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/file.py
@@ -0,0 +1,56 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+from spdx_tools.spdx3.bump_from_spdx2.checksum import bump_checksum
+from spdx_tools.spdx3.bump_from_spdx2.message import print_missing_conversion
+from spdx_tools.spdx3.model import ExternalMap
+from spdx_tools.spdx3.model.software import File
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model import ExternalDocumentRef, SpdxNoAssertion
+from spdx_tools.spdx.model.file import File as Spdx2_File
+from spdx_tools.spdx.spdx_element_utils import get_full_element_spdx_id
+
+
+def bump_file(
+ spdx2_file: Spdx2_File,
+ payload: Payload,
+ document_namespace: str,
+ external_document_refs: List[ExternalDocumentRef],
+ imports: List[ExternalMap],
+):
+ spdx_id = get_full_element_spdx_id(spdx2_file, document_namespace, external_document_refs)
+ if ":" in spdx2_file.spdx_id:
+ imports.append(
+ ExternalMap(
+ external_id=spdx2_file.spdx_id,
+ defining_document=f"{spdx2_file.spdx_id.split(':')[0]}:SPDXRef-DOCUMENT",
+ )
+ )
+
+ integrity_methods = [bump_checksum(checksum) for checksum in spdx2_file.checksums]
+ print_missing_conversion(
+ "file.file_type", 0, "different cardinalities, " "https://github.com/spdx/spdx-3-model/issues/82"
+ )
+ copyright_text = None
+ if isinstance(spdx2_file.copyright_text, str):
+ copyright_text = spdx2_file.copyright_text
+ elif isinstance(spdx2_file.copyright_text, SpdxNoAssertion):
+ print_missing_conversion("package2.copyright_text", 0)
+ print_missing_conversion(
+ "file.notice, file.contributors, file.license_info_in_file, file.license_comment",
+ 0,
+ "missing definition for license profile",
+ )
+
+ payload.add_element(
+ File(
+ spdx_id,
+ name=spdx2_file.name,
+ comment=spdx2_file.comment,
+ verified_using=integrity_methods,
+ copyright_text=copyright_text,
+ attribution_text=", ".join(spdx2_file.attribution_texts),
+ )
+ )
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/license_expression.py b/src/spdx_tools/spdx3/bump_from_spdx2/license_expression.py
new file mode 100644
index 000000000..de5f006d3
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/license_expression.py
@@ -0,0 +1,89 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Union
+from license_expression import AND, OR, LicenseExpression, LicenseSymbol, LicenseWithExceptionSymbol
+
+from spdx_tools.common.spdx_licensing import spdx_licensing
+from spdx_tools.spdx3.model.licensing import (
+ AnyLicenseInfo,
+ ConjunctiveLicenseSet,
+ CustomLicense,
+ CustomLicenseAddition,
+ DisjunctiveLicenseSet,
+ License,
+ LicenseAddition,
+ LicenseField,
+ ListedLicense,
+ ListedLicenseException,
+ NoAssertionLicense,
+ NoneLicense,
+ WithAdditionOperator,
+)
+from spdx_tools.spdx.model import ExtractedLicensingInfo, SpdxNoAssertion, SpdxNone
+
+
+def bump_license_expression_or_none_or_no_assertion(
+ element: Union[LicenseExpression, SpdxNoAssertion, SpdxNone],
+ extracted_licensing_info: List[ExtractedLicensingInfo],
+) -> LicenseField:
+ if isinstance(element, SpdxNone):
+ return NoneLicense()
+ elif isinstance(element, SpdxNoAssertion):
+ return NoAssertionLicense()
+ else:
+ return bump_license_expression(element, extracted_licensing_info)
+
+
+def bump_license_expression(
+ license_expression: LicenseExpression, extracted_licensing_info: List[ExtractedLicensingInfo]
+) -> AnyLicenseInfo:
+ if isinstance(license_expression, AND):
+ return ConjunctiveLicenseSet(
+ member=[bump_license_expression(element, extracted_licensing_info) for element in license_expression.args]
+ )
+ if isinstance(license_expression, OR):
+ return DisjunctiveLicenseSet(
+ member=[bump_license_expression(element, extracted_licensing_info) for element in license_expression.args]
+ )
+ if isinstance(license_expression, LicenseWithExceptionSymbol):
+ subject_license = bump_license_expression(license_expression.license_symbol, extracted_licensing_info)
+ if not isinstance(subject_license, License):
+ raise ValueError("Subject of LicenseException couldn't be converted to License.")
+ return WithAdditionOperator(
+ subject_license=subject_license,
+ subject_addition=bump_license_exception(license_expression.exception_symbol, extracted_licensing_info),
+ )
+ if isinstance(license_expression, LicenseSymbol):
+ if not spdx_licensing.validate(license_expression).invalid_symbols:
+ return ListedLicense(license_expression.key, license_expression.obj, "blank")
+ else:
+ for licensing_info in extracted_licensing_info:
+ if licensing_info.license_id == license_expression.key:
+ # the fields are optional in ExtractedLicensingInfo, to prevent type errors we use a type
+ # conversion to str as a quick fix
+ return CustomLicense(
+ str(licensing_info.license_id),
+ str(licensing_info.license_name),
+ str(licensing_info.extracted_text),
+ )
+
+ return CustomLicense(license_expression.key, "", "")
+
+
+def bump_license_exception(
+ license_exception: LicenseSymbol, extracted_licensing_info: List[ExtractedLicensingInfo]
+) -> LicenseAddition:
+ if not spdx_licensing.validate(license_exception).invalid_symbols:
+ return ListedLicenseException(license_exception.key, "", "")
+ else:
+ for licensing_info in extracted_licensing_info:
+ if licensing_info.license_id == license_exception.key:
+ # the fields are optional in ExtractedLicensingInfo, to prevent type errors we use a type conversion
+ # to str as a quick fix
+ return CustomLicenseAddition(
+ str(licensing_info.license_id),
+ str(licensing_info.license_name),
+ str(licensing_info.extracted_text),
+ )
+ return CustomLicenseAddition(license_exception.key, "", "")
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/message.py b/src/spdx_tools/spdx3/bump_from_spdx2/message.py
new file mode 100644
index 000000000..6351fce89
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/message.py
@@ -0,0 +1,11 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import sys
+
+MISSING_CONVERSION_REASONS = {0: "missing conversion rule", 1: "missing implementation"}
+
+
+def print_missing_conversion(field: str, reason, additional_information: str = ""):
+ print(f"{field} not converted: {MISSING_CONVERSION_REASONS[reason]} {additional_information}", file=sys.stderr)
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/package.py b/src/spdx_tools/spdx3/bump_from_spdx2/package.py
new file mode 100644
index 000000000..3d358babd
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/package.py
@@ -0,0 +1,155 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional, Union
+
+from spdx_tools.spdx3.bump_from_spdx2.actor import bump_actor
+from spdx_tools.spdx3.bump_from_spdx2.bump_utils import handle_no_assertion_or_none
+from spdx_tools.spdx3.bump_from_spdx2.checksum import bump_checksum
+from spdx_tools.spdx3.bump_from_spdx2.message import print_missing_conversion
+from spdx_tools.spdx3.model import (
+ ExternalIdentifier,
+ ExternalIdentifierType,
+ ExternalMap,
+ ExternalReference,
+ ExternalReferenceType,
+)
+from spdx_tools.spdx3.model.software import Package, SoftwarePurpose
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model import Actor as Spdx2_Actor
+from spdx_tools.spdx.model import ExternalDocumentRef, SpdxNoAssertion
+from spdx_tools.spdx.model.package import ExternalPackageRef
+from spdx_tools.spdx.model.package import Package as Spdx2_Package
+from spdx_tools.spdx.spdx_element_utils import get_full_element_spdx_id
+
+
+def bump_package(
+ spdx2_package: Spdx2_Package,
+ payload: Payload,
+ document_namespace: str,
+ external_document_refs: List[ExternalDocumentRef],
+ imports: List[ExternalMap],
+):
+ spdx_id = get_full_element_spdx_id(spdx2_package, document_namespace, external_document_refs)
+ if ":" in spdx2_package.spdx_id:
+ imports.append(
+ ExternalMap(
+ external_id=spdx2_package.spdx_id,
+ defining_document=f"{spdx2_package.spdx_id.split(':')[0]}:SPDXRef-DOCUMENT",
+ )
+ )
+
+ download_location = handle_no_assertion_or_none(spdx2_package.download_location, "package.download_location")
+ print_missing_conversion("package2.file_name", 0, "https://github.com/spdx/spdx-3-model/issues/83")
+ if isinstance(spdx2_package.supplier, Spdx2_Actor):
+ supplied_by_spdx_id = [bump_actor(spdx2_package.supplier, payload, document_namespace)]
+ else:
+ supplied_by_spdx_id = None
+ if isinstance(spdx2_package.originator, Spdx2_Actor):
+ originated_by_spdx_id = [bump_actor(spdx2_package.originator, payload, document_namespace)]
+ else:
+ originated_by_spdx_id = None
+ print_missing_conversion("package2.files_analyzed", 0, "https://github.com/spdx/spdx-3-model/issues/84")
+ print_missing_conversion(
+ "package2.verification_code", 1, "of IntegrityMethod, https://github.com/spdx/spdx-3-model/issues/85"
+ )
+ integrity_methods = [bump_checksum(checksum) for checksum in spdx2_package.checksums]
+ copyright_text = None
+ if isinstance(spdx2_package.copyright_text, str):
+ copyright_text = spdx2_package.copyright_text
+ elif isinstance(spdx2_package.copyright_text, SpdxNoAssertion):
+ print_missing_conversion("package2.copyright_text", 0)
+ print_missing_conversion(
+ "package2.license_info_from_files, package2.license_comment",
+ 0,
+ "and missing definition of license profile",
+ )
+
+ external_reference = []
+ external_identifier = []
+ purl_refs = [
+ external_ref for external_ref in spdx2_package.external_references if external_ref.reference_type == "purl"
+ ]
+ exactly_one_purl_without_comment = len(purl_refs) == 1 and purl_refs[0].comment is None
+ package_url = None
+ if exactly_one_purl_without_comment:
+ package_url = purl_refs[0].locator
+ for spdx2_external_ref in spdx2_package.external_references:
+ if exactly_one_purl_without_comment and spdx2_external_ref.reference_type == "purl":
+ continue
+ id_or_ref = bump_external_package_ref(spdx2_external_ref)
+ if isinstance(id_or_ref, ExternalReference):
+ external_reference.append(id_or_ref)
+ elif isinstance(id_or_ref, ExternalIdentifier):
+ external_identifier.append(id_or_ref)
+
+ package_purpose = (
+ SoftwarePurpose[spdx2_package.primary_package_purpose.name] if spdx2_package.primary_package_purpose else None
+ )
+
+ payload.add_element(
+ Package(
+ spdx_id,
+ spdx2_package.name,
+ summary=spdx2_package.summary,
+ description=spdx2_package.description,
+ comment=spdx2_package.comment,
+ verified_using=integrity_methods,
+ external_reference=external_reference,
+ external_identifier=external_identifier,
+ originated_by=originated_by_spdx_id,
+ supplied_by=supplied_by_spdx_id,
+ built_time=spdx2_package.built_date,
+ release_time=spdx2_package.release_date,
+ valid_until_time=spdx2_package.valid_until_date,
+ primary_purpose=package_purpose,
+ package_version=spdx2_package.version,
+ download_location=download_location,
+ package_url=package_url,
+ homepage=spdx2_package.homepage,
+ source_info=spdx2_package.source_info,
+ copyright_text=copyright_text,
+ attribution_text=", ".join(spdx2_package.attribution_texts),
+ )
+ )
+
+
+external_ref_type_map = {
+ "cpe22Type": ExternalIdentifierType.CPE22,
+ "cpe23Type": ExternalIdentifierType.CPE23,
+ "advisory": ExternalReferenceType.SECURITY_ADVISORY,
+ "fix": ExternalReferenceType.SECURITY_FIX,
+ "url": None,
+ "swid": ExternalIdentifierType.SWID,
+ "maven-central": None,
+ "npm": None,
+ "nuget": None,
+ "bower": None,
+ "purl": ExternalIdentifierType.PURL,
+ "swh": ExternalIdentifierType.SWHID,
+ "gitoid": ExternalIdentifierType.GITOID,
+}
+
+
+def bump_external_package_ref(
+ spdx2_external_ref: ExternalPackageRef,
+) -> Optional[Union[ExternalReference, ExternalIdentifier]]:
+ reference_type = spdx2_external_ref.reference_type
+ locator = spdx2_external_ref.locator
+ comment = spdx2_external_ref.comment
+
+ if reference_type not in external_ref_type_map:
+ print_missing_conversion(
+ reference_type,
+ 0,
+ f"Conversion of ExternalPackageRef of type {reference_type} is currently not supported."
+ f"https://github.com/spdx/spdx-3-model/issues/81",
+ )
+ return None
+
+ id_or_ref_type = external_ref_type_map[reference_type]
+
+ if isinstance(id_or_ref_type, ExternalReferenceType):
+ return ExternalReference(id_or_ref_type, [locator], None, comment)
+ elif isinstance(id_or_ref_type, ExternalIdentifierType):
+ return ExternalIdentifier(id_or_ref_type, locator, comment)
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/relationship.py b/src/spdx_tools/spdx3/bump_from_spdx2/relationship.py
new file mode 100644
index 000000000..dd6909c49
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/relationship.py
@@ -0,0 +1,259 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import logging
+import sys
+
+from beartype.typing import Dict, List, Optional, Tuple, Union
+
+from spdx_tools.spdx3.bump_from_spdx2.message import print_missing_conversion
+from spdx_tools.spdx3.model import LifecycleScopeType, Relationship, RelationshipCompleteness, RelationshipType
+from spdx_tools.spdx3.model.software import (
+ DependencyConditionalityType,
+ SoftwareDependencyLinkType,
+ SoftwareDependencyRelationship,
+)
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model.relationship import Relationship as Spdx2_Relationship
+from spdx_tools.spdx.model.relationship import RelationshipType as Spdx2_RelationshipType
+from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
+from spdx_tools.spdx.model.spdx_none import SpdxNone
+
+# bump relationship type, map each relationship type to the corresponding class in 3.0,
+# the relationship type, other arguments and if swapped
+relationship_mapping: Dict[
+ Spdx2_RelationshipType,
+ Tuple[
+ Union[Relationship, SoftwareDependencyRelationship],
+ RelationshipType,
+ Dict[str, Union[bool, LifecycleScopeType, SoftwareDependencyLinkType, DependencyConditionalityType]],
+ ],
+] = {
+ Spdx2_RelationshipType.AMENDS: (Relationship, RelationshipType.AMENDS, {}),
+ Spdx2_RelationshipType.ANCESTOR_OF: (Relationship, RelationshipType.ANCESTOR, {}),
+ Spdx2_RelationshipType.BUILD_DEPENDENCY_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {
+ "scope": LifecycleScopeType.BUILD,
+ "linkage": SoftwareDependencyLinkType.TOOL,
+ },
+ ),
+ Spdx2_RelationshipType.BUILD_TOOL_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"scope": LifecycleScopeType.BUILD, "linkage": SoftwareDependencyLinkType.TOOL},
+ ),
+ Spdx2_RelationshipType.CONTAINED_BY: (Relationship, RelationshipType.CONTAINS, {"swap": True}),
+ Spdx2_RelationshipType.CONTAINS: (
+ Relationship,
+ RelationshipType.CONTAINS,
+ {},
+ ), # might be deleted in favor of depends on
+ Spdx2_RelationshipType.COPY_OF: (Relationship, RelationshipType.COPY, {}),
+ Spdx2_RelationshipType.DATA_FILE_OF: (None, None, {}), # not defined, probably input/ output
+ Spdx2_RelationshipType.DEPENDENCY_MANIFEST_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {},
+ ), # "expect purpose has been set to manifest"
+ Spdx2_RelationshipType.DEPENDENCY_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"swap": True},
+ ),
+ Spdx2_RelationshipType.DEPENDS_ON: (SoftwareDependencyRelationship, RelationshipType.DEPENDS_ON, {}),
+ Spdx2_RelationshipType.DESCENDANT_OF: (Relationship, RelationshipType.ANCESTOR, {"swap": True}),
+ Spdx2_RelationshipType.DESCRIBED_BY: (Relationship, RelationshipType.DESCRIBES, {"swap": True}),
+ Spdx2_RelationshipType.DESCRIBES: (
+ Relationship,
+ RelationshipType.DESCRIBES,
+ {},
+ ), # might be deleted in favor of root
+ # property
+ Spdx2_RelationshipType.DEV_DEPENDENCY_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"scope": LifecycleScopeType.DEVELOPMENT},
+ ),
+ Spdx2_RelationshipType.DEV_TOOL_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"scope": LifecycleScopeType.DEVELOPMENT, "linkage": SoftwareDependencyLinkType.TOOL},
+ ),
+ Spdx2_RelationshipType.DISTRIBUTION_ARTIFACT: (None, None, {}), # not defined yet, purpose?
+ Spdx2_RelationshipType.DOCUMENTATION_OF: (Relationship, RelationshipType.DOCUMENTATION, {}),
+ Spdx2_RelationshipType.DYNAMIC_LINK: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"linkage": SoftwareDependencyLinkType.DYNAMIC},
+ ),
+ Spdx2_RelationshipType.EXAMPLE_OF: (Relationship, RelationshipType.EXAMPLE, {}),
+ Spdx2_RelationshipType.EXPANDED_FROM_ARCHIVE: (Relationship, RelationshipType.EXPANDED_FROM_ARCHIVE, {}),
+ Spdx2_RelationshipType.FILE_ADDED: (Relationship, RelationshipType.FILE_ADDED, {}),
+ Spdx2_RelationshipType.FILE_DELETED: (Relationship, RelationshipType.FILE_DELETED, {}),
+ Spdx2_RelationshipType.FILE_MODIFIED: (Relationship, RelationshipType.FILE_MODIFIED, {}),
+ Spdx2_RelationshipType.GENERATED_FROM: (Relationship, RelationshipType.GENERATES, {"swap": True}),
+ Spdx2_RelationshipType.GENERATES: (Relationship, RelationshipType.GENERATES, {}),
+ Spdx2_RelationshipType.HAS_PREREQUISITE: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"conditionality": DependencyConditionalityType.PREREQUISITE},
+ ),
+ Spdx2_RelationshipType.METAFILE_OF: (Relationship, RelationshipType.METAFILE, {}),
+ Spdx2_RelationshipType.OPTIONAL_COMPONENT_OF: (None, None, {}), # converted to depends on and purpose? not clear
+ Spdx2_RelationshipType.OPTIONAL_DEPENDENCY_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"conditionality": DependencyConditionalityType.OPTIONAL},
+ ),
+ Spdx2_RelationshipType.OTHER: (Relationship, RelationshipType.OTHER, {}),
+ Spdx2_RelationshipType.PACKAGE_OF: (SoftwareDependencyRelationship, RelationshipType.DEPENDS_ON, {}),
+ Spdx2_RelationshipType.PATCH_APPLIED: (Relationship, RelationshipType.PATCH, {"swap": True}),
+ Spdx2_RelationshipType.PATCH_FOR: (Relationship, RelationshipType.PATCH, {}),
+ Spdx2_RelationshipType.PREREQUISITE_FOR: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"conditionality": DependencyConditionalityType.PREREQUISITE},
+ ),
+ Spdx2_RelationshipType.PROVIDED_DEPENDENCY_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"scope": LifecycleScopeType.BUILD, "conditionality": DependencyConditionalityType.PROVIDED},
+ ),
+ Spdx2_RelationshipType.RUNTIME_DEPENDENCY_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"scope": LifecycleScopeType.RUNTIME},
+ ),
+ Spdx2_RelationshipType.STATIC_LINK: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"linkage": SoftwareDependencyLinkType.STATIC},
+ ),
+ Spdx2_RelationshipType.TEST_CASE_OF: (Relationship, RelationshipType.TEST_CASE, {}),
+ Spdx2_RelationshipType.TEST_DEPENDENCY_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"scope": LifecycleScopeType.TEST},
+ ),
+ Spdx2_RelationshipType.TEST_OF: (Relationship, RelationshipType.TEST, {}),
+ Spdx2_RelationshipType.TEST_TOOL_OF: (
+ SoftwareDependencyRelationship,
+ RelationshipType.DEPENDS_ON,
+ {"scope": LifecycleScopeType.TEST, "linkage": SoftwareDependencyLinkType.TOOL},
+ ),
+ Spdx2_RelationshipType.VARIANT_OF: (Relationship, RelationshipType.VARIANT, {}),
+ Spdx2_RelationshipType.REQUIREMENT_DESCRIPTION_FOR: (Relationship, RelationshipType.REQUIREMENT_FOR, {}),
+ Spdx2_RelationshipType.SPECIFICATION_FOR: (Relationship, RelationshipType.SPECIFICATION_FOR, {}),
+}
+
+
+def bump_relationships(
+ spdx2_relationships: List[Spdx2_Relationship],
+ payload: Payload,
+ document_namespace: str,
+):
+ generated_relationships: Dict[Tuple[str, str], List[Relationship]] = {}
+ for counter, spdx2_relationship in enumerate(spdx2_relationships):
+ relationship = bump_relationship(spdx2_relationship, document_namespace, counter)
+ if relationship:
+ generated_relationships.setdefault(
+ (relationship.from_element, relationship.relationship_type.name), []
+ ).append(relationship)
+
+ for relationships in generated_relationships.values():
+ if len(relationships) > 1:
+ _merge_relationships_and_add_to_payload(relationships, payload)
+ else:
+ payload.add_element(relationships[0])
+
+
+def bump_relationship(
+ spdx2_relationship: Spdx2_Relationship,
+ document_namespace: str,
+ counter: int,
+) -> Optional[Union[Relationship, SoftwareDependencyRelationship]]:
+ completeness, to = determine_completeness_and_to(spdx2_relationship.related_spdx_element_id)
+ spdx_id = "#".join([document_namespace, f"SPDXRef-Relationship-{counter}"])
+ relationship_class, relationship_type, parameters = relationship_mapping[spdx2_relationship.relationship_type]
+ if relationship_class is None:
+ print_missing_conversion(spdx2_relationship.relationship_type.name, 0)
+ return
+
+ swap_direction = parameters.get("swap", False)
+
+ if swap_direction:
+ if not to:
+ print_missing_conversion("Swapped Relationship to NoAssertion/None", 0)
+ return
+ from_element = to[0]
+ to = [spdx2_relationship.spdx_element_id]
+ else:
+ from_element = spdx2_relationship.spdx_element_id
+
+ if relationship_class == SoftwareDependencyRelationship:
+ from_element = spdx2_relationship.spdx_element_id
+
+ return SoftwareDependencyRelationship(
+ spdx_id,
+ f"{document_namespace}#{from_element}",
+ relationship_type,
+ [f"{document_namespace}#{t}" for t in to],
+ comment=spdx2_relationship.comment,
+ completeness=completeness,
+ scope=parameters.get("scope"),
+ software_linkage=parameters.get("linkage"),
+ conditionality=parameters.get("conditionality"),
+ )
+
+ return Relationship(
+ spdx_id,
+ f"{document_namespace}#{from_element}",
+ relationship_type,
+ [f"{document_namespace}#{t}" for t in to],
+ comment=spdx2_relationship.comment,
+ completeness=completeness,
+ )
+
+
+def determine_completeness_and_to(
+ related_spdx_element_id: Union[str, SpdxNone, SpdxNoAssertion],
+) -> Tuple[Optional[RelationshipCompleteness], List[str]]:
+ if isinstance(related_spdx_element_id, SpdxNoAssertion):
+ completeness = RelationshipCompleteness.NOASSERTION
+ to = []
+ elif isinstance(related_spdx_element_id, SpdxNone):
+ completeness = RelationshipCompleteness.COMPLETE
+ to = []
+ else:
+ completeness = None
+ to = [related_spdx_element_id]
+ return completeness, to
+
+
+def _merge_relationships_and_add_to_payload(relationships: List[Relationship], payload: Payload):
+ to = []
+ completeness = None
+ spdx_id = None
+ merged_relationship = relationships[0]
+ for merged_relationship in relationships:
+ if merged_relationship.comment:
+ payload.add_element(merged_relationship)
+ continue
+ if merged_relationship.completeness:
+ if completeness and completeness != merged_relationship.completeness:
+ logging.warning(
+ f"Contradicting information about completeness of relationship: {merged_relationship}", sys.stderr
+ )
+ else:
+ completeness = merged_relationship.completeness
+
+ to += merged_relationship.to
+ spdx_id = merged_relationship.spdx_id
+ if to:
+ merged_relationship.spdx_id = spdx_id
+ merged_relationship.to = to
+ merged_relationship.completeness = completeness
+ merged_relationship.comment = None
+ payload.add_element(merged_relationship)
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/snippet.py b/src/spdx_tools/spdx3/bump_from_spdx2/snippet.py
new file mode 100644
index 000000000..b052511c1
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/snippet.py
@@ -0,0 +1,58 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional, Tuple
+
+from spdx_tools.spdx3.bump_from_spdx2.message import print_missing_conversion
+from spdx_tools.spdx3.model import ExternalMap
+from spdx_tools.spdx3.model.positive_integer_range import PositiveIntegerRange
+from spdx_tools.spdx3.model.software import Snippet
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model import ExternalDocumentRef, SpdxNoAssertion
+from spdx_tools.spdx.model.snippet import Snippet as Spdx2_Snippet
+from spdx_tools.spdx.spdx_element_utils import get_full_element_spdx_id
+
+
+def bump_integer_range(spdx2_range: Optional[Tuple[int, int]]) -> PositiveIntegerRange:
+ return PositiveIntegerRange(spdx2_range[0], spdx2_range[1]) if spdx2_range else None
+
+
+def bump_snippet(
+ spdx2_snippet: Spdx2_Snippet,
+ payload: Payload,
+ document_namespace: str,
+ external_document_refs: List[ExternalDocumentRef],
+ imports: List[ExternalMap],
+):
+ spdx_id = get_full_element_spdx_id(spdx2_snippet, document_namespace, external_document_refs)
+ if ":" in spdx2_snippet.spdx_id:
+ imports.append(
+ ExternalMap(
+ external_id=spdx2_snippet.spdx_id,
+ defining_document=f"{spdx2_snippet.spdx_id.split(':')[0]}:SPDXRef-DOCUMENT",
+ )
+ )
+
+ print_missing_conversion("snippet.file_spdx_id", 0, "https://github.com/spdx/spdx-3-model/issues/130")
+ copyright_text = None
+ if isinstance(spdx2_snippet.copyright_text, str):
+ copyright_text = spdx2_snippet.copyright_text
+ elif isinstance(spdx2_snippet.copyright_text, SpdxNoAssertion):
+ print_missing_conversion("package2.copyright_text", 0)
+ print_missing_conversion(
+ "snippet.license_info_in_snippet, snippet.license_comment,",
+ 0,
+ "missing definitions for license profile",
+ )
+
+ payload.add_element(
+ Snippet(
+ spdx_id=spdx_id,
+ name=spdx2_snippet.name,
+ comment=spdx2_snippet.comment,
+ byte_range=bump_integer_range(spdx2_snippet.byte_range),
+ line_range=bump_integer_range(spdx2_snippet.line_range),
+ copyright_text=copyright_text,
+ attribution_text=", ".join(spdx2_snippet.attribution_texts),
+ )
+ )
diff --git a/src/spdx_tools/spdx3/bump_from_spdx2/spdx_document.py b/src/spdx_tools/spdx3/bump_from_spdx2/spdx_document.py
new file mode 100644
index 000000000..0257c403f
--- /dev/null
+++ b/src/spdx_tools/spdx3/bump_from_spdx2/spdx_document.py
@@ -0,0 +1,70 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.bump_from_spdx2.annotation import bump_annotation
+from spdx_tools.spdx3.bump_from_spdx2.creation_info import bump_creation_info
+from spdx_tools.spdx3.bump_from_spdx2.file import bump_file
+from spdx_tools.spdx3.bump_from_spdx2.package import bump_package
+from spdx_tools.spdx3.bump_from_spdx2.relationship import bump_relationships
+from spdx_tools.spdx3.bump_from_spdx2.snippet import bump_snippet
+from spdx_tools.spdx3.model import CreationInfo, SpdxDocument
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.model import RelationshipType
+from spdx_tools.spdx.model.document import Document as Spdx2_Document
+from spdx_tools.spdx.model.relationship_filters import filter_by_type_and_origin
+
+""" We want to implement a bump_from_spdx2 from the data model in src.spdx to the data model in src.spdx3.
+ As there are many fundamental differences between these version we want each bump_from_spdx2 method to take
+ the object from src.spdx and add all objects that the input is translated to into the payload."""
+
+
+def bump_spdx_document(document: Spdx2_Document) -> Payload:
+ payload = Payload()
+ document_namespace: str = document.creation_info.document_namespace
+ spdx_document: SpdxDocument = bump_creation_info(document.creation_info, payload)
+ spdx_document.root_element = [
+ f"{document_namespace}#{relationship.related_spdx_element_id}"
+ for relationship in filter_by_type_and_origin(
+ document.relationships, RelationshipType.DESCRIBES, "SPDXRef-DOCUMENT"
+ )
+ ]
+
+ creation_info: CreationInfo = spdx_document.creation_info
+
+ payload.add_element(spdx_document)
+
+ for spdx2_package in document.packages:
+ bump_package(
+ spdx2_package,
+ payload,
+ document_namespace,
+ document.creation_info.external_document_refs,
+ spdx_document.imports,
+ )
+
+ for spdx2_file in document.files:
+ bump_file(
+ spdx2_file,
+ payload,
+ document_namespace,
+ document.creation_info.external_document_refs,
+ spdx_document.imports,
+ )
+
+ for spdx2_snippet in document.snippets:
+ bump_snippet(
+ spdx2_snippet,
+ payload,
+ document_namespace,
+ document.creation_info.external_document_refs,
+ spdx_document.imports,
+ )
+
+ bump_relationships(document.relationships, payload, document_namespace)
+
+ for counter, spdx2_annotation in enumerate(document.annotations):
+ bump_annotation(spdx2_annotation, payload, creation_info, document_namespace, counter)
+
+ spdx_document.element = [spdx_id for spdx_id in payload.get_full_map() if spdx_id != spdx_document.spdx_id]
+
+ return payload
diff --git a/src/spdx_tools/spdx3/clitools/__init__.py b/src/spdx_tools/spdx3/clitools/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/clitools/pyspdxtools3.py b/src/spdx_tools/spdx3/clitools/pyspdxtools3.py
new file mode 100644
index 000000000..9dd21a5d1
--- /dev/null
+++ b/src/spdx_tools/spdx3/clitools/pyspdxtools3.py
@@ -0,0 +1,67 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import sys
+
+import click
+from beartype.typing import List
+
+from spdx_tools.spdx3.bump_from_spdx2.spdx_document import bump_spdx_document
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx3.writer.console.payload_writer import write_payload as write_payload_to_console
+from spdx_tools.spdx3.writer.json_ld.json_ld_writer import write_payload
+from spdx_tools.spdx.model.document import Document
+from spdx_tools.spdx.parser.parse_anything import parse_file
+from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
+from spdx_tools.spdx.validation.validation_message import ValidationMessage
+
+
+@click.command()
+@click.option(
+ "--infile", "-i", prompt="input file path", help="The file containing the document to be validated or converted."
+)
+@click.option(
+ "--outfile",
+ "-o",
+ help="The file to write the converted document to (write a dash for output to stdout or omit for no conversion)."
+ "For now only a prototype serialization to json-ld is available. The provided file will therefore be extended "
+ "with `.jsonld`.",
+)
+@click.option(
+ "--version",
+ help='The SPDX version to be used during parsing and validation (format "SPDX-2.3").',
+ default="SPDX-2.3",
+)
+@click.option("--novalidation", is_flag=True, help="Don't validate the provided document.")
+def main(infile: str, outfile: str, version: str, novalidation: bool):
+ """
+ CLI-tool to parse and validate a SPDX 2.x document and migrate it into the prototype of SPDX 3.0. As there is no
+ definition for a serialization yet output can only be written to stdout.
+ To use, run: 'pyspdxtools3 --infile -o -'
+ """
+ try:
+ document: Document = parse_file(infile)
+
+ if not novalidation:
+ validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version)
+ if validation_messages:
+ print("The document is invalid. The following issues have been found:", file=sys.stderr)
+ for message in validation_messages:
+ print(message.validation_message, file=sys.stderr)
+ sys.exit(1)
+ else:
+ print("The document is valid.", file=sys.stderr)
+ if outfile:
+ payload: Payload = bump_spdx_document(document)
+ if outfile == "-":
+ write_payload_to_console(payload, sys.stdout)
+ else:
+ write_payload(payload, outfile)
+
+ except NotImplementedError as err:
+ print(err.args[0])
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/spdx_tools/spdx3/model/__init__.py b/src/spdx_tools/spdx3/model/__init__.py
new file mode 100644
index 000000000..8fab45e9e
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/__init__.py
@@ -0,0 +1,25 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model.profile_identifier import ProfileIdentifierType
+from spdx_tools.spdx3.model.creation_info import CreationInfo
+from spdx_tools.spdx3.model.integrity_method import IntegrityMethod
+from spdx_tools.spdx3.model.hash import Hash, HashAlgorithm
+from spdx_tools.spdx3.model.external_reference import ExternalReference, ExternalReferenceType
+from spdx_tools.spdx3.model.external_identifier import ExternalIdentifier, ExternalIdentifierType
+from spdx_tools.spdx3.model.external_map import ExternalMap
+from spdx_tools.spdx3.model.namespace_map import NamespaceMap
+from spdx_tools.spdx3.model.element import Element
+from spdx_tools.spdx3.model.agent import Agent
+from spdx_tools.spdx3.model.person import Person
+from spdx_tools.spdx3.model.organization import Organization
+from spdx_tools.spdx3.model.software_agent import SoftwareAgent
+from spdx_tools.spdx3.model.tool import Tool
+from spdx_tools.spdx3.model.spdx_collection import ElementCollection
+from spdx_tools.spdx3.model.bundle import Bundle
+from spdx_tools.spdx3.model.bom import Bom
+from spdx_tools.spdx3.model.spdx_document import SpdxDocument
+from spdx_tools.spdx3.model.annotation import Annotation, AnnotationType
+from spdx_tools.spdx3.model.relationship import Relationship, RelationshipType, RelationshipCompleteness
+from spdx_tools.spdx3.model.lifecycle_scoped_relationship import LifecycleScopedRelationship, LifecycleScopeType
+from spdx_tools.spdx3.model.artifact import Artifact
diff --git a/src/spdx_tools/spdx3/model/agent.py b/src/spdx_tools/spdx3/model/agent.py
new file mode 100644
index 000000000..9aa326e22
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/agent.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, Element, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+@dataclass_with_properties
+class Agent(Element):
+ def __init__(
+ self,
+ spdx_id: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/ai/__init__.py b/src/spdx_tools/spdx3/model/ai/__init__.py
new file mode 100644
index 000000000..1f711abf6
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/ai/__init__.py
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model.ai.ai_package import AIPackage
diff --git a/src/spdx_tools/spdx3/model/ai/ai_package.py b/src/spdx_tools/spdx3/model/ai/ai_package.py
new file mode 100644
index 000000000..b297385e3
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/ai/ai_package.py
@@ -0,0 +1,102 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import Dict, List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+from spdx_tools.spdx3.model.licensing import LicenseField
+from spdx_tools.spdx3.model.software import Package, SoftwarePurpose
+
+
+class SafetyRiskAssessmentType(Enum):
+ SERIOUS = auto()
+ HIGH = auto()
+ MEDIUM = auto()
+ LOW = auto()
+
+
+@dataclass_with_properties
+class AIPackage(Package):
+ energy_consumption: Optional[str] = None
+ standard_compliance: List[str] = field(default_factory=list)
+ limitation: Optional[str] = None
+ type_of_model: List[str] = field(default_factory=list)
+ information_about_training: Optional[str] = None
+ information_about_application: Optional[str] = None
+ hyperparameter: Dict[str, Optional[str]] = field(default_factory=dict)
+ model_data_preprocessing: List[str] = field(default_factory=list)
+ model_explainability: List[str] = field(default_factory=list)
+ sensitive_personal_information: Optional[bool] = None
+ metric_decision_threshold: Dict[str, Optional[str]] = field(default_factory=dict)
+ metric: Dict[str, Optional[str]] = field(default_factory=dict)
+ domain: List[str] = field(default_factory=list)
+ autonomy_type: Optional[bool] = None
+ safety_risk_assessment: Optional[SafetyRiskAssessmentType] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ name: str,
+ supplied_by: List[str],
+ download_location: str,
+ package_version: str,
+ primary_purpose: SoftwarePurpose,
+ release_time: datetime,
+ creation_info: Optional[CreationInfo] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ originated_by: List[str] = None,
+ built_time: Optional[datetime] = None,
+ valid_until_time: Optional[datetime] = None,
+ standard: List[str] = None,
+ content_identifier: Optional[str] = None,
+ additional_purpose: List[SoftwarePurpose] = None,
+ concluded_license: Optional[LicenseField] = None,
+ declared_license: Optional[LicenseField] = None,
+ copyright_text: Optional[str] = None,
+ attribution_text: Optional[str] = None,
+ package_url: Optional[str] = None,
+ homepage: Optional[str] = None,
+ source_info: Optional[str] = None,
+ energy_consumption: Optional[str] = None,
+ standard_compliance: List[str] = None,
+ limitation: Optional[str] = None,
+ type_of_model: List[str] = None,
+ information_about_training: Optional[str] = None,
+ information_about_application: Optional[str] = None,
+ hyperparameter: Dict[str, Optional[str]] = None,
+ model_data_preprocessing: List[str] = None,
+ model_explainability: List[str] = None,
+ sensitive_personal_information: Optional[bool] = None,
+ metric_decision_threshold: Dict[str, Optional[str]] = None,
+ metric: Dict[str, Optional[str]] = None,
+ domain: List[str] = None,
+ autonomy_type: Optional[bool] = None,
+ safety_risk_assessment: Optional[SafetyRiskAssessmentType] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ originated_by = [] if originated_by is None else originated_by
+ additional_purpose = [] if additional_purpose is None else additional_purpose
+ standard = [] if standard is None else standard
+ standard_compliance = [] if standard_compliance is None else standard_compliance
+ type_of_model = [] if type_of_model is None else type_of_model
+ hyperparameter = {} if hyperparameter is None else hyperparameter
+ model_data_preprocessing = [] if model_data_preprocessing is None else model_data_preprocessing
+ model_explainability = [] if model_explainability is None else model_explainability
+ metric_decision_threshold = {} if metric_decision_threshold is None else metric_decision_threshold
+ metric = {} if metric is None else metric
+ domain = [] if domain is None else domain
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/annotation.py b/src/spdx_tools/spdx3/model/annotation.py
new file mode 100644
index 000000000..e74d9d578
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/annotation.py
@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, Element, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+class AnnotationType(Enum):
+ REVIEW = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class Annotation(Element):
+ annotation_type: AnnotationType = None
+ subject: str = None
+ content_type: List[str] = field(default_factory=list) # placeholder for MediaType
+ statement: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ annotation_type: AnnotationType,
+ subject: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ content_type: List[str] = None,
+ statement: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ content_type = [] if content_type is None else content_type
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/artifact.py b/src/spdx_tools/spdx3/model/artifact.py
new file mode 100644
index 000000000..0fccec89c
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/artifact.py
@@ -0,0 +1,25 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import abstractmethod
+from dataclasses import field
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.spdx3.model import Element
+
+
+@dataclass_with_properties
+class Artifact(Element):
+ originated_by: List[str] = field(default_factory=list) # SPDXID of the Agent/Tool
+ supplied_by: List[str] = field(default_factory=list) # SPDXID of the Agent/Tool
+ built_time: Optional[datetime] = None
+ release_time: Optional[datetime] = None
+ valid_until_time: Optional[datetime] = None
+ standard: List[str] = field(default_factory=list)
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/bom.py b/src/spdx_tools/spdx3/model/bom.py
new file mode 100644
index 000000000..a9ad7d57e
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/bom.py
@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ Bundle,
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalMap,
+ ExternalReference,
+ IntegrityMethod,
+ NamespaceMap,
+)
+
+
+@dataclass_with_properties
+class Bom(Bundle):
+ # We overwrite the super-__init__ as check_types_and_set_values()
+ # takes care of all fields (including inherited ones).
+
+ def __init__(
+ self,
+ spdx_id: str,
+ element: List[str],
+ root_element: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ namespaces: List[NamespaceMap] = None,
+ imports: List[ExternalMap] = None,
+ context: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ namespaces = [] if namespaces is None else namespaces
+ imports = [] if imports is None else imports
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/build/__init__.py b/src/spdx_tools/spdx3/model/build/__init__.py
new file mode 100644
index 000000000..765cf3c2a
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/build/__init__.py
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model.build.build import Build
diff --git a/src/spdx_tools/spdx3/model/build/build.py b/src/spdx_tools/spdx3/model/build/build.py
new file mode 100644
index 000000000..c6662ccce
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/build/build.py
@@ -0,0 +1,57 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+
+from beartype.typing import Dict, List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, Element, ExternalIdentifier, ExternalReference, Hash, IntegrityMethod
+
+
+@dataclass_with_properties
+class Build(Element):
+ build_type: str = None
+ build_id: Optional[str] = None
+ config_source_entrypoint: List[str] = field(default_factory=list)
+ config_source_uri: List[str] = field(default_factory=list)
+ config_source_digest: List[Hash] = field(default_factory=list)
+ parameters: Dict[str, str] = field(default_factory=dict)
+ build_start_time: Optional[datetime] = None
+ build_end_time: Optional[datetime] = None
+ environment: Dict[str, str] = field(default_factory=dict)
+
+ def __init__(
+ self,
+ spdx_id: str,
+ build_type: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ build_id: Optional[str] = None,
+ config_source_entrypoint: List[str] = None,
+ config_source_uri: List[str] = None,
+ config_source_digest: List[Hash] = None,
+ parameters: Dict[str, str] = None,
+ build_start_time: Optional[datetime] = None,
+ build_end_time: Optional[datetime] = None,
+ environment: Dict[str, str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ config_source_entrypoint = [] if config_source_entrypoint is None else config_source_entrypoint
+ config_source_uri = [] if config_source_uri is None else config_source_uri
+ config_source_digest = [] if config_source_digest is None else config_source_digest
+ parameters = {} if parameters is None else parameters
+ environment = {} if environment is None else environment
+
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/bundle.py b/src/spdx_tools/spdx3/model/bundle.py
new file mode 100644
index 000000000..63640f845
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/bundle.py
@@ -0,0 +1,46 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ElementCollection,
+ ExternalIdentifier,
+ ExternalMap,
+ ExternalReference,
+ IntegrityMethod,
+ NamespaceMap,
+)
+
+
+@dataclass_with_properties
+class Bundle(ElementCollection):
+ context: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ element: List[str],
+ root_element: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ namespaces: List[NamespaceMap] = None,
+ imports: List[ExternalMap] = None,
+ context: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ namespaces = [] if namespaces is None else namespaces
+ imports = [] if imports is None else imports
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/creation_info.py b/src/spdx_tools/spdx3/model/creation_info.py
new file mode 100644
index 000000000..125d4d30d
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/creation_info.py
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+
+from beartype.typing import List, Optional
+from semantic_version import Version
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import ProfileIdentifierType
+
+
+@dataclass_with_properties
+class CreationInfo:
+ spec_version: Version
+ created: datetime
+ created_by: List[str] # SPDXID of Agents
+ profile: List[ProfileIdentifierType]
+ data_license: Optional[str] = "CC0-1.0"
+ created_using: List[str] = field(default_factory=list) # SPDXID of Tools
+ comment: Optional[str] = None
+
+ def __init__(
+ self,
+ spec_version: Version,
+ created: datetime,
+ created_by: List[str],
+ profile: List[ProfileIdentifierType],
+ data_license: Optional[str] = "CC0-1.0",
+ created_using: List[str] = None,
+ comment: Optional[str] = None,
+ ):
+ created_using = [] if created_using is None else created_using
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/dataset/__init__.py b/src/spdx_tools/spdx3/model/dataset/__init__.py
new file mode 100644
index 000000000..5e2b4e153
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/dataset/__init__.py
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model.dataset.dataset import Dataset, DatasetAvailabilityType, ConfidentialityLevelType
diff --git a/src/spdx_tools/spdx3/model/dataset/dataset.py b/src/spdx_tools/spdx3/model/dataset/dataset.py
new file mode 100644
index 000000000..bbb82cc3a
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/dataset/dataset.py
@@ -0,0 +1,120 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import Dict, List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+from spdx_tools.spdx3.model.licensing import LicenseField
+from spdx_tools.spdx3.model.software import Package, SoftwarePurpose
+
+
+class DatasetType(Enum):
+ STRUCTURED = auto()
+ NUMERIC = auto()
+ TEXT = auto()
+ CATEGORICAL = auto()
+ GRAPH = auto()
+ TIMESERIES = auto()
+ TIMESTAMP = auto()
+ SENSOR = auto()
+ IMAGE = auto()
+ SYNTACTIC = auto()
+ AUDIO = auto()
+ VIDEO = auto()
+ OTHER = auto()
+ NO_ASSERTION = auto()
+
+
+class ConfidentialityLevelType(Enum):
+ RED = auto()
+ AMBER = auto()
+ GREEN = auto()
+ CLEAR = auto()
+
+
+class DatasetAvailabilityType(Enum):
+ DIRECT_DOWNLOAD = auto()
+ SCRAPING_SCRIPT = auto()
+ QUERY = auto()
+ CLICKTHROUGH = auto()
+ REGISTRATION = auto()
+
+
+@dataclass_with_properties
+class Dataset(Package):
+ dataset_type: List[DatasetType] = None
+ data_collection_process: Optional[str] = None
+ intended_use: Optional[str] = None
+ dataset_size: Optional[int] = None
+ dataset_noise: Optional[str] = None
+ data_preprocessing: List[str] = field(default_factory=list)
+ sensor: Dict[str, Optional[str]] = field(default_factory=dict)
+ known_bias: List[str] = field(default_factory=list)
+ sensitive_personal_information: Optional[bool] = None
+ anonymization_method_used: List[str] = field(default_factory=list)
+ confidentiality_level: Optional[ConfidentialityLevelType] = None
+ dataset_update_mechanism: Optional[str] = None
+ dataset_availability: Optional[DatasetAvailabilityType] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ name: str,
+ originated_by: List[str],
+ download_location: str,
+ primary_purpose: SoftwarePurpose,
+ built_time: datetime,
+ release_time: datetime,
+ dataset_type: List[DatasetType],
+ creation_info: Optional[CreationInfo] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ supplied_by: List[str] = None,
+ valid_until_time: Optional[datetime] = None,
+ standard: List[str] = None,
+ content_identifier: Optional[str] = None,
+ additional_purpose: List[SoftwarePurpose] = None,
+ concluded_license: Optional[LicenseField] = None,
+ declared_license: Optional[LicenseField] = None,
+ copyright_text: Optional[str] = None,
+ attribution_text: Optional[str] = None,
+ package_version: Optional[str] = None,
+ package_url: Optional[str] = None,
+ homepage: Optional[str] = None,
+ source_info: Optional[str] = None,
+ data_collection_process: Optional[str] = None,
+ intended_use: Optional[str] = None,
+ dataset_size: Optional[int] = None,
+ dataset_noise: Optional[str] = None,
+ data_preprocessing: List[str] = None,
+ sensor: Dict[str, Optional[str]] = None,
+ known_bias: List[str] = None,
+ sensitive_personal_information: Optional[bool] = None,
+ anonymization_method_used: List[str] = None,
+ confidentiality_level: Optional[ConfidentialityLevelType] = None,
+ dataset_update_mechanism: Optional[str] = None,
+ dataset_availability: Optional[DatasetAvailabilityType] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ originated_by = [] if originated_by is None else originated_by
+ additional_purpose = [] if additional_purpose is None else additional_purpose
+ supplied_by = [] if supplied_by is None else supplied_by
+ standard = [] if standard is None else standard
+ data_preprocessing = [] if data_preprocessing is None else data_preprocessing
+ sensor = {} if sensor is None else sensor
+ known_bias = [] if known_bias is None else known_bias
+ anonymization_method_used = [] if anonymization_method_used is None else anonymization_method_used
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/element.py b/src/spdx_tools/spdx3/model/element.py
new file mode 100644
index 000000000..08f2d7b85
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/element.py
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import ABC, abstractmethod
+from dataclasses import field
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.spdx3.model import CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+@dataclass_with_properties
+class Element(ABC):
+ spdx_id: str # IRI
+ creation_info: Optional[CreationInfo] = None
+ name: Optional[str] = None
+ summary: Optional[str] = None
+ description: Optional[str] = None
+ comment: Optional[str] = None
+ verified_using: List[IntegrityMethod] = field(default_factory=list)
+ external_reference: List[ExternalReference] = field(default_factory=list)
+ external_identifier: List[ExternalIdentifier] = field(default_factory=list)
+ extension: Optional[str] = None # placeholder for extension
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/external_identifier.py b/src/spdx_tools/spdx3/model/external_identifier.py
new file mode 100644
index 000000000..ee458151e
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/external_identifier.py
@@ -0,0 +1,44 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+
+
+class ExternalIdentifierType(Enum):
+ CPE22 = auto()
+ CPE23 = auto()
+ CVE = auto()
+ EMAIL = auto()
+ GITOID = auto()
+ PURL = auto()
+ SECURITY_OTHER = auto()
+ SWHID = auto()
+ SWID = auto()
+ URL_SCHEME = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class ExternalIdentifier:
+ external_identifier_type: ExternalIdentifierType
+ identifier: str
+ comment: Optional[str] = None
+ identifier_locator: List[str] = field(default_factory=list)
+ issuing_authority: Optional[str] = None
+
+ def __init__(
+ self,
+ external_identifier_type: ExternalIdentifierType,
+ identifier: str,
+ comment: Optional[str] = None,
+ identifier_locator: List[str] = None,
+ issuing_authority: Optional[str] = None,
+ ):
+ identifier_locator = [] if identifier_locator is None else identifier_locator
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/external_map.py b/src/spdx_tools/spdx3/model/external_map.py
new file mode 100644
index 000000000..ab88a49e2
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/external_map.py
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import IntegrityMethod
+
+
+@dataclass_with_properties
+class ExternalMap:
+ external_id: str # anyURI
+ verified_using: List[IntegrityMethod] = field(default_factory=list)
+ location_hint: Optional[str] = None # anyURI
+ defining_document: Optional[str] = None
+
+ def __init__(
+ self,
+ external_id: str,
+ verified_using: List[IntegrityMethod] = None,
+ location_hint: Optional[str] = None,
+ defining_document: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/external_reference.py b/src/spdx_tools/spdx3/model/external_reference.py
new file mode 100644
index 000000000..2f44a54d6
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/external_reference.py
@@ -0,0 +1,69 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+
+
+class ExternalReferenceType(Enum):
+ ALT_DOWNLOAD_LOCATION = auto()
+ ALT_WEB_PAGE = auto()
+ BINARY_ARTIFACT = auto()
+ BUILD_META = auto()
+ BUILD_SYSTEM = auto()
+ CERTIFICATION_REPORT = auto()
+ CHAT = auto()
+ COMPONENT_ANALYSIS_REPORT = auto()
+ DOCUMENTATION = auto()
+ DYNAMIC_ANALYSIS_REPORT = auto()
+ EOL_NOTICE = auto()
+ FUNDING = auto()
+ ISSUE_TRACKER = auto()
+ LICENSE = auto()
+ MAILING_LIST = auto()
+ METRICS = auto()
+ OTHER = auto()
+ PRODUCT_METADATA = auto()
+ QUALITY_ASSESSMENT_REPORT = auto()
+ RELEASE_HISTORY = auto()
+ RELEASE_NOTES = auto()
+ RISK_ASSESSMENT = auto()
+ RUNTIME_ANALYSIS_REPORT = auto()
+ SECURE_SOFTWARE_ATTESTATION = auto()
+ SECURITY_ADVERSARY_MODEL = auto()
+ SECURITY_ADVISORY = auto()
+ SECURITY_FIX = auto()
+ SECURITY_OTHER = auto()
+ SECURITY_PEN_TEST_REPORT = auto()
+ SECURITY_POLICY = auto()
+ SECURITY_THREAT_MODEL = auto()
+ SOCIAL_MEDIA = auto()
+ SOURCE_ARTIFACT = auto()
+ STATIC_ANALYSIS_REPORT = auto()
+ SUPPORT = auto()
+ VCS = auto()
+ VULNERABILITY_DISCLOSURE_REPORT = auto()
+ VULNERABILITY_EXPLOITABILITY_ASSESSMENT = auto()
+
+
+@dataclass_with_properties
+class ExternalReference:
+ external_reference_type: Optional[ExternalReferenceType] = None
+ locator: List[str] = field(default_factory=list)
+ content_type: Optional[str] = None # placeholder for MediaType
+ comment: Optional[str] = None
+
+ def __init__(
+ self,
+ external_reference_type: Optional[ExternalReferenceType] = None,
+ locator: List[str] = None,
+ content_type: Optional[str] = None,
+ comment: Optional[str] = None,
+ ):
+ locator = [] if locator is None else locator
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/hash.py b/src/spdx_tools/spdx3/model/hash.py
new file mode 100644
index 000000000..42ef4ff4d
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/hash.py
@@ -0,0 +1,46 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+from beartype.typing import Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import IntegrityMethod
+
+
+class HashAlgorithm(Enum):
+ BLAKE2B256 = auto()
+ BLAKE2B384 = auto()
+ BLAKE2B512 = auto()
+ BLAKE3 = auto()
+ CRYSTALS_KYBER = auto()
+ CRYSTALS_DILITHIUM = auto()
+ FALCON = auto()
+ MD2 = auto()
+ MD4 = auto()
+ MD5 = auto()
+ MD6 = auto()
+ OTHER = auto()
+ SHA1 = auto()
+ SHA224 = auto()
+ SHA256 = auto()
+ SHA3_224 = auto()
+ SHA3_256 = auto()
+ SHA3_384 = auto()
+ SHA3_512 = auto()
+ SHA384 = auto()
+ SHA512 = auto()
+ SPDXPVCSHA1 = auto()
+ SPDXPVCSHA256 = auto()
+ SPHINCS_PLUS = auto()
+
+
+@dataclass_with_properties
+class Hash(IntegrityMethod):
+ algorithm: HashAlgorithm = None
+ hash_value: str = None
+
+ def __init__(self, algorithm: HashAlgorithm, hash_value: str, comment: Optional[str] = None):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/integrity_method.py b/src/spdx_tools/spdx3/model/integrity_method.py
new file mode 100644
index 000000000..17fefef16
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/integrity_method.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import ABC, abstractmethod
+
+from beartype.typing import Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+
+
+@dataclass_with_properties
+class IntegrityMethod(ABC):
+ comment: Optional[str] = None
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/licensing/__init__.py b/src/spdx_tools/spdx3/model/licensing/__init__.py
new file mode 100644
index 000000000..8d9b9c6af
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/__init__.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from .any_license_info import AnyLicenseInfo
+from .conjunctive_license_set import ConjunctiveLicenseSet
+from .custom_license import CustomLicense
+from .custom_license_addition import CustomLicenseAddition
+from .disjunctive_license_set import DisjunctiveLicenseSet
+from .license import License
+from .license_addition import LicenseAddition
+from .license_field import LicenseField
+from .listed_license import ListedLicense
+from .listed_license_exception import ListedLicenseException
+from .no_assertion_license import NoAssertionLicense
+from .none_license import NoneLicense
+from .or_later_operator import OrLaterOperator
+from .with_addition_operator import WithAdditionOperator
diff --git a/src/spdx_tools/spdx3/model/licensing/any_license_info.py b/src/spdx_tools/spdx3/model/licensing/any_license_info.py
new file mode 100644
index 000000000..1f1402427
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/any_license_info.py
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import abstractmethod
+
+from spdx_tools.spdx3.model.licensing.license_field import LicenseField
+
+
+class AnyLicenseInfo(LicenseField):
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/licensing/conjunctive_license_set.py b/src/spdx_tools/spdx3/model/licensing/conjunctive_license_set.py
new file mode 100644
index 000000000..fe5605761
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/conjunctive_license_set.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.any_license_info import AnyLicenseInfo
+
+
+@dataclass_with_properties
+class ConjunctiveLicenseSet(AnyLicenseInfo):
+ member: List[AnyLicenseInfo]
+
+ def __init__(self, member: List[AnyLicenseInfo]):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/licensing/custom_license.py b/src/spdx_tools/spdx3/model/licensing/custom_license.py
new file mode 100644
index 000000000..4617a18db
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/custom_license.py
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.license import License
+
+
+@dataclass_with_properties
+class CustomLicense(License):
+ def __init__(
+ self,
+ license_id: str,
+ license_name: str,
+ license_text: str,
+ license_comment: Optional[str] = None,
+ see_also: List[str] = None,
+ is_osi_approved: Optional[bool] = None,
+ is_fsf_libre: Optional[bool] = None,
+ standard_license_header: Optional[str] = None,
+ standard_license_template: Optional[str] = None,
+ is_deprecated_license_id: Optional[bool] = None,
+ obsoleted_by: Optional[str] = None,
+ ):
+ see_also = [] if see_also is None else see_also
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/licensing/custom_license_addition.py b/src/spdx_tools/spdx3/model/licensing/custom_license_addition.py
new file mode 100644
index 000000000..b50d27770
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/custom_license_addition.py
@@ -0,0 +1,25 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.license_addition import LicenseAddition
+
+
+@dataclass_with_properties
+class CustomLicenseAddition(LicenseAddition):
+ def __init__(
+ self,
+ addition_id: str,
+ addition_name: str,
+ addition_text: str,
+ addition_comment: Optional[str] = None,
+ see_also: List[str] = None,
+ standard_addition_template: Optional[str] = None,
+ is_deprecated_addition_id: Optional[bool] = None,
+ obsoleted_by: Optional[str] = None,
+ ):
+ see_also = [] if see_also is None else see_also
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/licensing/disjunctive_license_set.py b/src/spdx_tools/spdx3/model/licensing/disjunctive_license_set.py
new file mode 100644
index 000000000..a5ac3bdc8
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/disjunctive_license_set.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.any_license_info import AnyLicenseInfo
+
+
+@dataclass_with_properties
+class DisjunctiveLicenseSet(AnyLicenseInfo):
+ member: List[AnyLicenseInfo]
+
+ def __init__(self, member: List[AnyLicenseInfo]):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/licensing/license.py b/src/spdx_tools/spdx3/model/licensing/license.py
new file mode 100644
index 000000000..e2fd625ff
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/license.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import abstractmethod
+from dataclasses import field
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.spdx3.model.licensing.any_license_info import AnyLicenseInfo
+
+
+@dataclass_with_properties
+class License(AnyLicenseInfo):
+ license_id: str
+ license_name: str
+ license_text: str
+ license_comment: Optional[str] = None
+ see_also: List[str] = field(default_factory=list)
+ is_osi_approved: Optional[bool] = None
+ is_fsf_libre: Optional[bool] = None
+ standard_license_header: Optional[str] = None
+ standard_license_template: Optional[str] = None
+ is_deprecated_license_id: Optional[bool] = None
+ obsoleted_by: Optional[str] = None
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/licensing/license_addition.py b/src/spdx_tools/spdx3/model/licensing/license_addition.py
new file mode 100644
index 000000000..e3669b5cb
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/license_addition.py
@@ -0,0 +1,25 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import ABC, abstractmethod
+from dataclasses import field
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+
+
+@dataclass_with_properties
+class LicenseAddition(ABC):
+ addition_id: str
+ addition_name: str
+ addition_text: str
+ addition_comment: Optional[str] = None
+ see_also: List[str] = field(default_factory=list)
+ standard_addition_template: Optional[str] = None
+ is_deprecated_addition_id: Optional[bool] = None
+ obsoleted_by: Optional[str] = None
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/licensing/license_field.py b/src/spdx_tools/spdx3/model/licensing/license_field.py
new file mode 100644
index 000000000..babe141f2
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/license_field.py
@@ -0,0 +1,10 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import ABC, abstractmethod
+
+
+class LicenseField(ABC):
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/licensing/listed_license.py b/src/spdx_tools/spdx3/model/licensing/listed_license.py
new file mode 100644
index 000000000..2c0b02b3d
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/listed_license.py
@@ -0,0 +1,33 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.license import License
+
+
+@dataclass_with_properties
+class ListedLicense(License):
+ list_version_added: Optional[str] = None
+ deprecated_version: Optional[str] = None
+
+ def __init__(
+ self,
+ license_id: str,
+ license_name: str,
+ license_text: str,
+ license_comment: Optional[str] = None,
+ see_also: List[str] = None,
+ is_osi_approved: Optional[bool] = None,
+ is_fsf_libre: Optional[bool] = None,
+ standard_license_header: Optional[str] = None,
+ standard_license_template: Optional[str] = None,
+ is_deprecated_license_id: Optional[bool] = None,
+ obsoleted_by: Optional[str] = None,
+ list_version_added: Optional[str] = None,
+ deprecated_version: Optional[str] = None,
+ ):
+ see_also = [] if see_also is None else see_also
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/licensing/listed_license_exception.py b/src/spdx_tools/spdx3/model/licensing/listed_license_exception.py
new file mode 100644
index 000000000..799fcedae
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/listed_license_exception.py
@@ -0,0 +1,30 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.license_addition import LicenseAddition
+
+
+@dataclass_with_properties
+class ListedLicenseException(LicenseAddition):
+ list_version_added: Optional[str] = None
+ deprecated_version: Optional[str] = None
+
+ def __init__(
+ self,
+ addition_id: str,
+ addition_name: str,
+ addition_text: str,
+ addition_comment: Optional[str] = None,
+ see_also: List[str] = None,
+ standard_addition_template: Optional[str] = None,
+ is_deprecated_addition_id: Optional[bool] = None,
+ obsoleted_by: Optional[str] = None,
+ list_version_added: Optional[str] = None,
+ deprecated_version: Optional[str] = None,
+ ):
+ see_also = [] if see_also is None else see_also
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/licensing/no_assertion_license.py b/src/spdx_tools/spdx3/model/licensing/no_assertion_license.py
new file mode 100644
index 000000000..66a00b261
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/no_assertion_license.py
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model.licensing.license_field import LicenseField
+
+
+class NoAssertionLicense(LicenseField):
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/licensing/none_license.py b/src/spdx_tools/spdx3/model/licensing/none_license.py
new file mode 100644
index 000000000..e34253608
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/none_license.py
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model.licensing.license_field import LicenseField
+
+
+class NoneLicense(LicenseField):
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/licensing/or_later_operator.py b/src/spdx_tools/spdx3/model/licensing/or_later_operator.py
new file mode 100644
index 000000000..2aa204b98
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/or_later_operator.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.any_license_info import AnyLicenseInfo
+from spdx_tools.spdx3.model.licensing.license import License
+
+
+@dataclass_with_properties
+class OrLaterOperator(AnyLicenseInfo):
+ subject_license: License
+
+ def __init__(self, subject_license: License):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/licensing/with_addition_operator.py b/src/spdx_tools/spdx3/model/licensing/with_addition_operator.py
new file mode 100644
index 000000000..9e79f8d98
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/licensing/with_addition_operator.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model.licensing.any_license_info import AnyLicenseInfo
+from spdx_tools.spdx3.model.licensing.license import License
+from spdx_tools.spdx3.model.licensing.license_addition import LicenseAddition
+
+
+@dataclass_with_properties
+class WithAdditionOperator(AnyLicenseInfo):
+ subject_license: License
+ subject_addition: LicenseAddition
+
+ def __init__(self, subject_license: License, subject_addition: LicenseAddition):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/lifecycle_scoped_relationship.py b/src/spdx_tools/spdx3/model/lifecycle_scoped_relationship.py
new file mode 100644
index 000000000..a06e108ce
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/lifecycle_scoped_relationship.py
@@ -0,0 +1,59 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ Relationship,
+ RelationshipCompleteness,
+ RelationshipType,
+)
+
+
+class LifecycleScopeType(Enum):
+ DESIGN = auto()
+ BUILD = auto()
+ DEVELOPMENT = auto()
+ TEST = auto()
+ RUNTIME = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class LifecycleScopedRelationship(Relationship):
+ scope: Optional[LifecycleScopeType] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str] = None,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ scope: Optional[LifecycleScopeType] = None,
+ ):
+ to = [] if to is None else to
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/namespace_map.py b/src/spdx_tools/spdx3/model/namespace_map.py
new file mode 100644
index 000000000..c4d1217a5
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/namespace_map.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+
+
+@dataclass_with_properties
+class NamespaceMap:
+ prefix: str
+ namespace: str # anyURI
+
+ def __init__(self, prefix: str, namespace: str):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/organization.py b/src/spdx_tools/spdx3/model/organization.py
new file mode 100644
index 000000000..c297b24b3
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/organization.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import Agent, CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+@dataclass_with_properties
+class Organization(Agent):
+ def __init__(
+ self,
+ spdx_id: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/person.py b/src/spdx_tools/spdx3/model/person.py
new file mode 100644
index 000000000..782e5a366
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/person.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import Agent, CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+@dataclass_with_properties
+class Person(Agent):
+ def __init__(
+ self,
+ spdx_id: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/positive_integer_range.py b/src/spdx_tools/spdx3/model/positive_integer_range.py
new file mode 100644
index 000000000..cbe1c6322
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/positive_integer_range.py
@@ -0,0 +1,18 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+
+
+@dataclass_with_properties
+class PositiveIntegerRange:
+ begin: int
+ end: int
+
+ def __init__(
+ self,
+ begin: int,
+ end: int,
+ ):
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/profile_identifier.py b/src/spdx_tools/spdx3/model/profile_identifier.py
new file mode 100644
index 000000000..40fe7ac41
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/profile_identifier.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+
+class ProfileIdentifierType(Enum):
+ CORE = auto()
+ SOFTWARE = auto()
+ LICENSING = auto()
+ SECURITY = auto()
+ BUILD = auto()
+ AI = auto()
+ DATASET = auto()
+ USAGE = auto()
+ EXTENSION = auto()
diff --git a/src/spdx_tools/spdx3/model/relationship.py b/src/spdx_tools/spdx3/model/relationship.py
new file mode 100644
index 000000000..873559460
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/relationship.py
@@ -0,0 +1,120 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, Element, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+class RelationshipType(Enum):
+ AFFECTS = auto()
+ AMENDS = auto()
+ ANCESTOR = auto()
+ AVAILABLE_FROM = auto()
+ BUILD_DEPENDENCY = auto()
+ BUILD_TOOL = auto()
+ COORDINATED_BY = auto()
+ CONTAINS = auto()
+ CONFIG_OF = auto()
+ COPY = auto()
+ DATA_FILE = auto()
+ DEPENDENCY_MANIFEST = auto()
+ DEPENDS_ON = auto()
+ DESCENDANT = auto()
+ DESCRIBES = auto()
+ DEV_DEPENDENCY = auto()
+ DEV_TOOL = auto()
+ DISTRIBUTION_ARTIFACT = auto()
+ DOCUMENTATION = auto()
+ DOES_NOT_AFFECT = auto()
+ DYNAMIC_LINK = auto()
+ EXAMPLE = auto()
+ EVIDENCE_FOR = auto()
+ EXPANDED_FROM_ARCHIVE = auto()
+ EXPLOIT_CREATED_BY = auto()
+ FILE_ADDED = auto()
+ FILE_DELETED = auto()
+ FILE_MODIFIED = auto()
+ FIXED_BY = auto()
+ FIXED_IN = auto()
+ FOUND_BY = auto()
+ GENERATES = auto()
+ HAS_ASSESSMENT_FOR = auto()
+ HAS_ASSOCIATED_VULNERABILITY = auto()
+ HOST_OF = auto()
+ INPUT_OF = auto()
+ INVOKED_BY = auto()
+ METAFILE = auto()
+ ON_BEHALF_OF = auto()
+ OPTIONAL_COMPONENT = auto()
+ OPTIONAL_DEPENDENCY = auto()
+ OTHER = auto()
+ OUTPUT_OF = auto()
+ PACKAGES = auto()
+ PATCH = auto()
+ PREREQUISITE = auto()
+ PROVIDED_DEPENDENCY = auto()
+ PUBLISHED_BY = auto()
+ REPORTED_BY = auto()
+ REPUBLISHED_BY = auto()
+ REQUIREMENT_FOR = auto()
+ RUNTIME_DEPENDENCY = auto()
+ SPECIFICATION_FOR = auto()
+ STATIC_LINK = auto()
+ TEST = auto()
+ TEST_CASE = auto()
+ TEST_DEPENDENCY = auto()
+ TEST_TOOL = auto()
+ TESTED_ON = auto()
+ TRAINED_ON = auto()
+ UNDER_INVESTIGATION_FOR = auto()
+ VARIANT = auto()
+
+
+class RelationshipCompleteness(Enum):
+ INCOMPLETE = auto()
+ COMPLETE = auto()
+ NOASSERTION = auto()
+
+
+@dataclass_with_properties
+class Relationship(Element):
+ # due to the inheritance we need to make all fields non-default in the __annotation__,
+ # the __init__ method still raises an error if required fields are not set
+ from_element: str = None
+ to: List[str] = field(default_factory=list)
+ relationship_type: RelationshipType = None
+ completeness: Optional[RelationshipCompleteness] = None
+ start_time: Optional[datetime] = None
+ end_time: Optional[datetime] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str] = None,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ ):
+ to = [] if to is None else to
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/__init__.py b/src/spdx_tools/spdx3/model/security/__init__.py
new file mode 100644
index 000000000..3407981c8
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/__init__.py
@@ -0,0 +1,18 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from .cvss_v2_vuln_assessment_relationship import CvssV2VulnAssessmentRelationship
+from .cvss_v3_vuln_assessment_relationship import CvssV3VulnAssessmentRelationship
+from .epss_vuln_assessment_relationship import EpssVulnAssessmentRelationship
+from .exploit_catalog_vuln_assessment_relationship import ExploitCatalogVulnAssessmentRelationship, ExploitCatalogType
+from .ssvc_vuln_assessment_relationship import SsvcVulnAssessmentRelationship, SsvcDecisionType
+from .vex_affected_vuln_assessment_relationship import VexAffectedVulnAssessmentRelationship
+from .vex_fixed_vuln_assessment_relationship import VexFixedVulnAssessmentRelationship
+from .vex_not_affected_vuln_assessment_relationship import (
+ VexNotAffectedVulnAssessmentRelationship,
+ VexJustificationType,
+)
+from .vex_under_investigation_vuln_assessment_relationship import VexUnderInvestigationVulnAssessmentRelationship
+from .vex_vuln_assessment_relationship import VexVulnAssessmentRelationship
+from .vuln_assessment_relationship import VulnAssessmentRelationship
+from .vulnerability import Vulnerability
diff --git a/src/spdx_tools/spdx3/model/security/cvss_v2_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/cvss_v2_vuln_assessment_relationship.py
new file mode 100644
index 000000000..c686f9dfc
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/cvss_v2_vuln_assessment_relationship.py
@@ -0,0 +1,57 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vuln_assessment_relationship import VulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+@dataclass_with_properties
+class CvssV2VulnAssessmentRelationship(VulnAssessmentRelationship):
+ score: str = None
+ severity: Optional[str] = None
+ vector: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ score: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ severity: Optional[str] = None,
+ vector: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/cvss_v3_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/cvss_v3_vuln_assessment_relationship.py
new file mode 100644
index 000000000..ab8a803af
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/cvss_v3_vuln_assessment_relationship.py
@@ -0,0 +1,57 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vuln_assessment_relationship import VulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+@dataclass_with_properties
+class CvssV3VulnAssessmentRelationship(VulnAssessmentRelationship):
+ score: str = None
+ severity: Optional[str] = None
+ vector: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ to: List[str],
+ relationship_type: RelationshipType,
+ score: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ severity: Optional[str] = None,
+ vector: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/epss_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/epss_vuln_assessment_relationship.py
new file mode 100644
index 000000000..f5001a92d
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/epss_vuln_assessment_relationship.py
@@ -0,0 +1,55 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vuln_assessment_relationship import VulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+@dataclass_with_properties
+class EpssVulnAssessmentRelationship(VulnAssessmentRelationship):
+ probability: int = None
+ severity: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ probability: int,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ severity: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/exploit_catalog_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/exploit_catalog_vuln_assessment_relationship.py
new file mode 100644
index 000000000..a7a67ac68
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/exploit_catalog_vuln_assessment_relationship.py
@@ -0,0 +1,63 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vuln_assessment_relationship import VulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+class ExploitCatalogType(Enum):
+ KEV = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class ExploitCatalogVulnAssessmentRelationship(VulnAssessmentRelationship):
+ catalog_type: ExploitCatalogType = None
+ exploited: bool = None
+ locator: str = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ catalog_type: ExploitCatalogType,
+ exploited: bool,
+ locator: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/ssvc_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/ssvc_vuln_assessment_relationship.py
new file mode 100644
index 000000000..d98803874
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/ssvc_vuln_assessment_relationship.py
@@ -0,0 +1,61 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vuln_assessment_relationship import VulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+class SsvcDecisionType(Enum):
+ ACT = auto()
+ ATTEND = auto()
+ TRACK = auto()
+ TRACK_STAR = auto()
+
+
+@dataclass_with_properties
+class SsvcVulnAssessmentRelationship(VulnAssessmentRelationship):
+ decision_type: SsvcDecisionType = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ decision_type: SsvcDecisionType,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/vex_affected_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/vex_affected_vuln_assessment_relationship.py
new file mode 100644
index 000000000..2dc242273
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/vex_affected_vuln_assessment_relationship.py
@@ -0,0 +1,59 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vex_vuln_assessment_relationship import VexVulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+@dataclass_with_properties
+class VexAffectedVulnAssessmentRelationship(VexVulnAssessmentRelationship):
+ action_statement: Optional[str] = None
+ action_statement_time: List[datetime] = field(default_factory=list)
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ vex_version: Optional[str] = None,
+ status_notes: Optional[str] = None,
+ action_statement: Optional[str] = None,
+ action_statement_time: List[datetime] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ action_statement_time = [] if action_statement_time is None else action_statement_time
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/vex_fixed_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/vex_fixed_vuln_assessment_relationship.py
new file mode 100644
index 000000000..c8bdc2b38
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/vex_fixed_vuln_assessment_relationship.py
@@ -0,0 +1,52 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vex_vuln_assessment_relationship import VexVulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+@dataclass_with_properties
+class VexFixedVulnAssessmentRelationship(VexVulnAssessmentRelationship):
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ vex_version: Optional[str] = None,
+ status_notes: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/vex_not_affected_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/vex_not_affected_vuln_assessment_relationship.py
new file mode 100644
index 000000000..4c019a973
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/vex_not_affected_vuln_assessment_relationship.py
@@ -0,0 +1,68 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vex_vuln_assessment_relationship import VexVulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+class VexJustificationType(Enum):
+ COMPONENT_NOT_PRESENT = auto()
+ VULNERABLE_CODE_NOT_PRESENT = auto()
+ VULNERABLE_CODE_CANNOT_BE_CONTROLLED_BY_ADVERSARY = auto()
+ VULNERABLE_CODE_NOT_IN_EXECUTE_PATH = auto()
+ INLINE_MITIGATIONS_ALREADY_EXIST = auto()
+
+
+@dataclass_with_properties
+class VexNotAffectedVulnAssessmentRelationship(VexVulnAssessmentRelationship):
+ justification_type: Optional[VexJustificationType] = None
+ impact_statement: Optional[str] = None
+ impact_statement_time: Optional[datetime] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ vex_version: Optional[str] = None,
+ status_notes: Optional[str] = None,
+ justification_type: Optional[VexJustificationType] = None,
+ impact_statement: Optional[str] = None,
+ impact_statement_time: Optional[datetime] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/vex_under_investigation_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/vex_under_investigation_vuln_assessment_relationship.py
new file mode 100644
index 000000000..ba63480bc
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/vex_under_investigation_vuln_assessment_relationship.py
@@ -0,0 +1,52 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ RelationshipCompleteness,
+)
+from spdx_tools.spdx3.model.security.vex_vuln_assessment_relationship import VexVulnAssessmentRelationship
+from spdx_tools.spdx.model import RelationshipType
+
+
+@dataclass_with_properties
+class VexUnderInvestigationVulnAssessmentRelationship(VexVulnAssessmentRelationship):
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ assessed_element: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ supplied_by: Optional[str] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ vex_version: Optional[str] = None,
+ status_notes: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/security/vex_vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/vex_vuln_assessment_relationship.py
new file mode 100644
index 000000000..8b5c0fc68
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/vex_vuln_assessment_relationship.py
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import abstractmethod
+
+from beartype.typing import Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.spdx3.model.security.vuln_assessment_relationship import VulnAssessmentRelationship
+
+
+@dataclass_with_properties
+class VexVulnAssessmentRelationship(VulnAssessmentRelationship):
+ vex_version: Optional[str] = None
+ status_notes: Optional[str] = None
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/security/vuln_assessment_relationship.py b/src/spdx_tools/spdx3/model/security/vuln_assessment_relationship.py
new file mode 100644
index 000000000..f20303743
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/vuln_assessment_relationship.py
@@ -0,0 +1,23 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import abstractmethod
+from datetime import datetime
+
+from beartype.typing import Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.spdx3.model import Relationship
+
+
+@dataclass_with_properties
+class VulnAssessmentRelationship(Relationship):
+ assessed_element: Optional[str] = None # id of the element
+ published_time: Optional[datetime] = None
+ supplied_by: Optional[str] = None
+ modified_time: Optional[datetime] = None
+ withdrawn_time: Optional[datetime] = None
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/security/vulnerability.py b/src/spdx_tools/spdx3/model/security/vulnerability.py
new file mode 100644
index 000000000..a137b1cb7
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/security/vulnerability.py
@@ -0,0 +1,38 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, Element, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+@dataclass_with_properties
+class Vulnerability(Element):
+ published_time: Optional[datetime] = None
+ modified_time: Optional[datetime] = None
+ withdrawn_time: Optional[datetime] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ published_time: Optional[datetime] = None,
+ modified_time: Optional[datetime] = None,
+ withdrawn_time: Optional[datetime] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/software/__init__.py b/src/spdx_tools/spdx3/model/software/__init__.py
new file mode 100644
index 000000000..f3b157024
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/__init__.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from spdx_tools.spdx3.model.software.software_purpose import SoftwarePurpose
+from spdx_tools.spdx3.model.software.file import File
+from spdx_tools.spdx3.model.software.package import Package
+from spdx_tools.spdx3.model.software.snippet import Snippet
+from spdx_tools.spdx3.model.software.sbom import Sbom, SBOMType
+from spdx_tools.spdx3.model.software.software_dependency_relationship import (
+ SoftwareDependencyRelationship,
+ SoftwareDependencyLinkType,
+ DependencyConditionalityType,
+)
diff --git a/src/spdx_tools/spdx3/model/software/file.py b/src/spdx_tools/spdx3/model/software/file.py
new file mode 100644
index 000000000..c962b4dbd
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/file.py
@@ -0,0 +1,54 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+from spdx_tools.spdx3.model.licensing import LicenseField
+from spdx_tools.spdx3.model.software import SoftwarePurpose
+from spdx_tools.spdx3.model.software.software_artifact import SoftwareArtifact
+
+
+@dataclass_with_properties
+class File(SoftwareArtifact):
+ content_type: Optional[str] = None # placeholder for MediaType
+
+ def __init__(
+ self,
+ spdx_id: str,
+ name: str,
+ creation_info: Optional[CreationInfo] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ originated_by: List[str] = None,
+ supplied_by: List[str] = None,
+ built_time: Optional[datetime] = None,
+ release_time: Optional[datetime] = None,
+ valid_until_time: Optional[datetime] = None,
+ standard: List[str] = None,
+ content_identifier: Optional[str] = None,
+ primary_purpose: Optional[SoftwarePurpose] = None,
+ additional_purpose: List[SoftwarePurpose] = None,
+ concluded_license: Optional[LicenseField] = None,
+ declared_license: Optional[LicenseField] = None,
+ copyright_text: Optional[str] = None,
+ attribution_text: Optional[str] = None,
+ content_type: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ originated_by = [] if originated_by is None else originated_by
+ supplied_by = [] if supplied_by is None else supplied_by
+ standard = [] if standard is None else standard
+ additional_purpose = [] if additional_purpose is None else additional_purpose
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/software/package.py b/src/spdx_tools/spdx3/model/software/package.py
new file mode 100644
index 000000000..0c249cc1b
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/package.py
@@ -0,0 +1,62 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+from spdx_tools.spdx3.model.licensing import LicenseField
+from spdx_tools.spdx3.model.software import SoftwarePurpose
+from spdx_tools.spdx3.model.software.software_artifact import SoftwareArtifact
+
+
+@dataclass_with_properties
+class Package(SoftwareArtifact):
+ package_version: Optional[str] = None
+ download_location: Optional[str] = None # anyURI
+ package_url: Optional[str] = None # anyURI
+ homepage: Optional[str] = None # anyURI
+ source_info: Optional[str] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ name: str,
+ creation_info: Optional[CreationInfo] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ originated_by: List[str] = None,
+ supplied_by: List[str] = None,
+ built_time: Optional[datetime] = None,
+ release_time: Optional[datetime] = None,
+ valid_until_time: Optional[datetime] = None,
+ standard: List[str] = None,
+ content_identifier: Optional[str] = None,
+ primary_purpose: Optional[SoftwarePurpose] = None,
+ additional_purpose: List[SoftwarePurpose] = None,
+ concluded_license: Optional[LicenseField] = None,
+ declared_license: Optional[LicenseField] = None,
+ copyright_text: Optional[str] = None,
+ attribution_text: Optional[str] = None,
+ package_version: Optional[str] = None,
+ download_location: Optional[str] = None,
+ package_url: Optional[str] = None,
+ homepage: Optional[str] = None,
+ source_info: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ originated_by = [] if originated_by is None else originated_by
+ supplied_by = [] if supplied_by is None else supplied_by
+ standard = [] if standard is None else standard
+ additional_purpose = [] if additional_purpose is None else additional_purpose
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/software/sbom.py b/src/spdx_tools/spdx3/model/software/sbom.py
new file mode 100644
index 000000000..1fb06f615
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/sbom.py
@@ -0,0 +1,62 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from dataclasses import field
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ Bom,
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalMap,
+ ExternalReference,
+ IntegrityMethod,
+ NamespaceMap,
+)
+
+
+class SBOMType(Enum):
+ DESIGN = auto()
+ SOURCE = auto()
+ BUILD = auto()
+ DEPLOYED = auto()
+ RUNTIME = auto()
+ ANALYZED = auto()
+
+
+@dataclass_with_properties
+class Sbom(Bom):
+ sbom_type: List[SBOMType] = field(default_factory=list)
+
+ # We overwrite the super-__init__ as check_types_and_set_values()
+ # takes care of all fields (including inherited ones).
+ def __init__(
+ self,
+ spdx_id: str,
+ element: List[str],
+ root_element: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ namespaces: List[NamespaceMap] = None,
+ imports: List[ExternalMap] = None,
+ context: Optional[str] = None,
+ sbom_type: List[SBOMType] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ namespaces = [] if namespaces is None else namespaces
+ imports = [] if imports is None else imports
+ sbom_type = [] if sbom_type is None else sbom_type
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/software/snippet.py b/src/spdx_tools/spdx3/model/software/snippet.py
new file mode 100644
index 000000000..b3ab61396
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/snippet.py
@@ -0,0 +1,57 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+from spdx_tools.spdx3.model.licensing import LicenseField
+from spdx_tools.spdx3.model.positive_integer_range import PositiveIntegerRange
+from spdx_tools.spdx3.model.software import SoftwarePurpose
+from spdx_tools.spdx3.model.software.software_artifact import SoftwareArtifact
+
+
+@dataclass_with_properties
+class Snippet(SoftwareArtifact):
+ byte_range: Optional[PositiveIntegerRange] = None
+ line_range: Optional[PositiveIntegerRange] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ originated_by: List[str] = None,
+ supplied_by: List[str] = None,
+ built_time: Optional[datetime] = None,
+ release_time: Optional[datetime] = None,
+ valid_until_time: Optional[datetime] = None,
+ standard: List[str] = None,
+ content_identifier: Optional[str] = None,
+ primary_purpose: Optional[SoftwarePurpose] = None,
+ additional_purpose: List[SoftwarePurpose] = None,
+ concluded_license: Optional[LicenseField] = None,
+ declared_license: Optional[LicenseField] = None,
+ copyright_text: Optional[str] = None,
+ attribution_text: Optional[str] = None,
+ byte_range: Optional[PositiveIntegerRange] = None,
+ line_range: Optional[PositiveIntegerRange] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ originated_by = [] if originated_by is None else originated_by
+ supplied_by = [] if supplied_by is None else supplied_by
+ standard = [] if standard is None else standard
+ additional_purpose = [] if additional_purpose is None else additional_purpose
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/software/software_artifact.py b/src/spdx_tools/spdx3/model/software/software_artifact.py
new file mode 100644
index 000000000..afc2b7ff3
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/software_artifact.py
@@ -0,0 +1,27 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import abstractmethod
+from dataclasses import field
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.spdx3.model import Artifact
+from spdx_tools.spdx3.model.licensing import LicenseField
+from spdx_tools.spdx3.model.software import SoftwarePurpose
+
+
+@dataclass_with_properties
+class SoftwareArtifact(Artifact):
+ content_identifier: Optional[str] = None
+ primary_purpose: Optional[SoftwarePurpose] = None
+ additional_purpose: List[SoftwarePurpose] = field(default_factory=list)
+ concluded_license: Optional[LicenseField] = None
+ declared_license: Optional[LicenseField] = None
+ copyright_text: Optional[str] = None
+ attribution_text: Optional[str] = None
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/software/software_dependency_relationship.py b/src/spdx_tools/spdx3/model/software/software_dependency_relationship.py
new file mode 100644
index 000000000..c6751ecaa
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/software_dependency_relationship.py
@@ -0,0 +1,69 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+from enum import Enum, auto
+
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalReference,
+ IntegrityMethod,
+ LifecycleScopedRelationship,
+ LifecycleScopeType,
+ RelationshipCompleteness,
+ RelationshipType,
+)
+
+
+class SoftwareDependencyLinkType(Enum):
+ STATIC = auto()
+ DYNAMIC = auto()
+ TOOL = auto()
+ OTHER = auto()
+
+
+class DependencyConditionalityType(Enum):
+ OPTIONAL = auto()
+ REQUIRED = auto()
+ PROVIDED = auto()
+ PREREQUISITE = auto()
+ OTHER = auto()
+
+
+@dataclass_with_properties
+class SoftwareDependencyRelationship(LifecycleScopedRelationship):
+ software_linkage: Optional[SoftwareDependencyLinkType] = None
+ conditionality: Optional[DependencyConditionalityType] = None
+
+ def __init__(
+ self,
+ spdx_id: str,
+ from_element: str,
+ relationship_type: RelationshipType,
+ to: List[str] = None,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ completeness: Optional[RelationshipCompleteness] = None,
+ start_time: Optional[datetime] = None,
+ end_time: Optional[datetime] = None,
+ scope: Optional[LifecycleScopeType] = None,
+ software_linkage: Optional[SoftwareDependencyLinkType] = None,
+ conditionality: Optional[DependencyConditionalityType] = None,
+ ):
+ to = [] if to is None else to
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/software/software_purpose.py b/src/spdx_tools/spdx3/model/software/software_purpose.py
new file mode 100644
index 000000000..e3a4d48cf
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software/software_purpose.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum, auto
+
+
+class SoftwarePurpose(Enum):
+ APPLICATION = auto()
+ ARCHIVE = auto()
+ BOM = auto()
+ CONFIGURATION = auto()
+ CONTAINER = auto()
+ DATA = auto()
+ DEVICE = auto()
+ DOCUMENTATION = auto()
+ EXECUTABLE = auto()
+ FILE = auto()
+ FIRMWARE = auto()
+ FRAMEWORK = auto()
+ INSTALL = auto()
+ LIBRARY = auto()
+ MODEL = auto()
+ MODULE = auto()
+ OPERATING_SYSTEM = auto()
+ OTHER = auto()
+ PATCH = auto()
+ REQUIREMENT = auto()
+ SOURCE = auto()
+ TEST = auto()
diff --git a/src/spdx_tools/spdx3/model/software_agent.py b/src/spdx_tools/spdx3/model/software_agent.py
new file mode 100644
index 000000000..28e4b33a2
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/software_agent.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import Agent, CreationInfo, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+@dataclass_with_properties
+class SoftwareAgent(Agent):
+ def __init__(
+ self,
+ spdx_id: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/spdx_collection.py b/src/spdx_tools/spdx3/model/spdx_collection.py
new file mode 100644
index 000000000..65c28951a
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/spdx_collection.py
@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from abc import abstractmethod
+from dataclasses import field
+
+from beartype.typing import List
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.spdx3.model import Element, ExternalMap, NamespaceMap
+
+
+@dataclass_with_properties
+class ElementCollection(Element):
+ # due to the inheritance we need to make all fields non-default in the __annotation__,
+ # the __init__ method still raises an error if required fields are not set
+ element: List[str] = field(default_factory=list)
+ root_element: List[str] = field(default_factory=list)
+ namespaces: List[NamespaceMap] = field(default_factory=list)
+ imports: List[ExternalMap] = field(default_factory=list)
+
+ @abstractmethod
+ def __init__(self):
+ pass
diff --git a/src/spdx_tools/spdx3/model/spdx_document.py b/src/spdx_tools/spdx3/model/spdx_document.py
new file mode 100644
index 000000000..d9c70401c
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/spdx_document.py
@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import (
+ Bundle,
+ CreationInfo,
+ ExternalIdentifier,
+ ExternalMap,
+ ExternalReference,
+ IntegrityMethod,
+ NamespaceMap,
+)
+
+
+@dataclass_with_properties
+class SpdxDocument(Bundle):
+ # The inherited field "name" is required for a SpdxDocument, no longer optional.
+ # We overwrite the super-__init__ as check_types_and_set_values()
+ # takes care of all fields (including inherited ones).
+ def __init__(
+ self,
+ spdx_id: str,
+ name: str,
+ element: List[str],
+ root_element: List[str],
+ creation_info: Optional[CreationInfo] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ namespaces: List[NamespaceMap] = None,
+ imports: List[ExternalMap] = None,
+ context: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ namespaces = [] if namespaces is None else namespaces
+ imports = [] if imports is None else imports
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/model/tool.py b/src/spdx_tools/spdx3/model/tool.py
new file mode 100644
index 000000000..b4ba72cf3
--- /dev/null
+++ b/src/spdx_tools/spdx3/model/tool.py
@@ -0,0 +1,29 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import List, Optional
+
+from spdx_tools.common.typing.dataclass_with_properties import dataclass_with_properties
+from spdx_tools.common.typing.type_checks import check_types_and_set_values
+from spdx_tools.spdx3.model import CreationInfo, Element, ExternalIdentifier, ExternalReference, IntegrityMethod
+
+
+@dataclass_with_properties
+class Tool(Element):
+ def __init__(
+ self,
+ spdx_id: str,
+ creation_info: Optional[CreationInfo] = None,
+ name: Optional[str] = None,
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ comment: Optional[str] = None,
+ verified_using: List[IntegrityMethod] = None,
+ external_reference: List[ExternalReference] = None,
+ external_identifier: List[ExternalIdentifier] = None,
+ extension: Optional[str] = None,
+ ):
+ verified_using = [] if verified_using is None else verified_using
+ external_reference = [] if external_reference is None else external_reference
+ external_identifier = [] if external_identifier is None else external_identifier
+ check_types_and_set_values(self, locals())
diff --git a/src/spdx_tools/spdx3/payload.py b/src/spdx_tools/spdx3/payload.py
new file mode 100644
index 000000000..17bc78c91
--- /dev/null
+++ b/src/spdx_tools/spdx3/payload.py
@@ -0,0 +1,22 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Dict
+
+from spdx_tools.spdx3.model import Element
+
+
+class Payload:
+ _spdx_id_map: Dict[str, Element]
+
+ def __init__(self, spdx_id_map: Dict[str, Element] = None):
+ self._spdx_id_map = spdx_id_map if spdx_id_map else {}
+
+ def add_element(self, element: Element):
+ self._spdx_id_map[element.spdx_id] = element
+
+ def get_element(self, spdx_id: str) -> Element:
+ return self._spdx_id_map[spdx_id]
+
+ def get_full_map(self) -> Dict[str, Element]:
+ return self._spdx_id_map
diff --git a/src/spdx_tools/spdx3/validation/__init__.py b/src/spdx_tools/spdx3/validation/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/validation/json_ld/__init__.py b/src/spdx_tools/spdx3/validation/json_ld/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/validation/json_ld/shacl_validation.py b/src/spdx_tools/spdx3/validation/json_ld/shacl_validation.py
new file mode 100644
index 000000000..e7b3c8776
--- /dev/null
+++ b/src/spdx_tools/spdx3/validation/json_ld/shacl_validation.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import Optional
+from pyshacl import validate
+from rdflib import Graph
+
+
+def validate_against_shacl_from_file(
+ data_file: str, shacl_file: str, data_format: Optional[str] = "json-ld", shacl_format: Optional[str] = "ttl"
+):
+ data_graph = Graph()
+ with open(data_file) as file:
+ data_graph.parse(file, format=data_format)
+
+ shacl_graph = Graph()
+ with open(shacl_file) as file:
+ shacl_graph.parse(file, format=shacl_format)
+
+ return validate(data_graph=data_graph, shacl_graph=shacl_graph, ont_graph=shacl_graph)
diff --git a/src/spdx_tools/spdx3/writer/__init__.py b/src/spdx_tools/spdx3/writer/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/writer/console/__init__.py b/src/spdx_tools/spdx3/writer/console/__init__.py
new file mode 100644
index 000000000..46eabf875
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/__init__.py
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+"""This is a temporary package to write the implemented model of spdx_tools.spdx3.0 to console. As soon as
+serialization formats are properly defined this package can be deleted."""
diff --git a/src/spdx_tools/spdx3/writer/console/agent_writer.py b/src/spdx_tools/spdx3/writer/console/agent_writer.py
new file mode 100644
index 000000000..a1c12a9e0
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/agent_writer.py
@@ -0,0 +1,18 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Agent, Organization, Person, SoftwareAgent
+from spdx_tools.spdx3.writer.console.element_writer import write_element_properties
+
+
+def write_agent(agent: Agent, text_output: TextIO, heading: bool = True):
+ if heading:
+ if isinstance(agent, Person):
+ text_output.write("## Person\n")
+ if isinstance(agent, Organization):
+ text_output.write("## Organization\n")
+ if isinstance(agent, SoftwareAgent):
+ text_output.write("## SoftwareAgent\n")
+ write_element_properties(agent, text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/ai/__init__.py b/src/spdx_tools/spdx3/writer/console/ai/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/writer/console/ai/ai_package_writer.py b/src/spdx_tools/spdx3/writer/console/ai/ai_package_writer.py
new file mode 100644
index 000000000..025ae613a
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/ai/ai_package_writer.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.ai import AIPackage
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.software.package_writer import write_package
+
+
+def write_ai_package(ai_package: AIPackage, text_output: TextIO):
+ text_output.write("## AI Package\n")
+ write_package(ai_package, text_output, False)
+
+ for property_name in AIPackage.__annotations__.keys():
+ write_value(property_name, getattr(ai_package, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/annotation_writer.py b/src/spdx_tools/spdx3/writer/console/annotation_writer.py
new file mode 100644
index 000000000..3261a69bd
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/annotation_writer.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Annotation
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.element_writer import write_element_properties
+
+
+def write_annotation(annotation: Annotation, text_output: TextIO):
+ text_output.write("## Annotation\n")
+ write_element_properties(annotation, text_output)
+
+ for property_name in Annotation.__annotations__.keys():
+ write_value(property_name, getattr(annotation, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/artifact_writer.py b/src/spdx_tools/spdx3/writer/console/artifact_writer.py
new file mode 100644
index 000000000..f55d29e05
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/artifact_writer.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Artifact
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.element_writer import write_element_properties
+
+
+def write_artifact_properties(artifact: Artifact, text_output: TextIO):
+ write_element_properties(artifact, text_output)
+
+ for property_name in Artifact.__annotations__.keys():
+ write_value(property_name, getattr(artifact, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/bom_writer.py b/src/spdx_tools/spdx3/writer/console/bom_writer.py
new file mode 100644
index 000000000..04fcb283d
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/bom_writer.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Bom
+from spdx_tools.spdx3.writer.console.bundle_writer import write_bundle
+
+
+def write_bom(bom: Bom, text_output: TextIO, heading: bool = True):
+ if heading:
+ text_output.write("## Bom\n")
+ write_bundle(bom, text_output, False)
diff --git a/src/spdx_tools/spdx3/writer/console/build/__init__.py b/src/spdx_tools/spdx3/writer/console/build/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/writer/console/build/build_writer.py b/src/spdx_tools/spdx3/writer/console/build/build_writer.py
new file mode 100644
index 000000000..3edc9c6fb
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/build/build_writer.py
@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.build import Build
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.element_writer import write_element_properties
+from spdx_tools.spdx3.writer.console.hash_writer import write_hash
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_optional_heading
+
+
+def write_build(build: Build, text_output: TextIO):
+ text_output.write("## Build\n")
+ write_element_properties(build, text_output)
+
+ for property_name in Build.__annotations__.keys():
+ if property_name == "config_source_digest":
+ write_optional_heading(build.config_source_digest, "config_source_digest", text_output)
+ for digest_hash in build.config_source_digest:
+ write_hash(digest_hash, text_output, heading=False)
+ continue
+
+ write_value(property_name, getattr(build, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/bundle_writer.py b/src/spdx_tools/spdx3/writer/console/bundle_writer.py
new file mode 100644
index 000000000..5930db5ee
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/bundle_writer.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Bundle
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.spdx_collection_writer import write_collection
+
+
+def write_bundle(bundle: Bundle, text_output: TextIO, heading: bool = True):
+ if heading:
+ text_output.write("## Bundle\n")
+ write_collection(bundle, text_output)
+ write_value("context", bundle.context, text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/console.py b/src/spdx_tools/spdx3/writer/console/console.py
new file mode 100644
index 000000000..28b5f9cfa
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/console.py
@@ -0,0 +1,27 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from enum import Enum
+
+from beartype.typing import Optional, TextIO, Union
+
+
+def write_value(tag: str, value: Optional[Union[bool, str, dict, list, Enum]], out: TextIO, indent: bool = False):
+ """This function is duplicated from spdx_tools.spdx.writer.tagvalue.tag_value_writer_helper_functions
+ and slightly adapted to make indentation of output possible."""
+ if not value:
+ return
+ if isinstance(value, dict):
+ value = ", ".join([f"{tag}: ({key}: {val})" for key, val in value.items()])
+ if isinstance(value, list):
+ value = ", ".join([entry for entry in value])
+ if isinstance(value, Enum):
+ value = value.name
+ write_and_possibly_indent(f"{tag}: {value}", indent, out)
+
+
+def write_and_possibly_indent(text: str, indent: bool, out: TextIO):
+ if indent:
+ out.write(f" {text}\n")
+ else:
+ out.write(f"{text}\n")
diff --git a/src/spdx_tools/spdx3/writer/console/creation_info_writer.py b/src/spdx_tools/spdx3/writer/console/creation_info_writer.py
new file mode 100644
index 000000000..c91e6781d
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/creation_info_writer.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import CreationInfo
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+
+
+def write_creation_info(creation_info: CreationInfo, text_output: TextIO, indent: bool = True):
+ text_output.write("# Creation Information\n")
+ write_value("specVersion", str(creation_info.spec_version), text_output, indent)
+ write_value("created", datetime_to_iso_string(creation_info.created), text_output, indent)
+ for created_by in creation_info.created_by:
+ write_value("created by", created_by, text_output, indent)
+ for created_using in creation_info.created_using:
+ write_value("created using", created_using, text_output, indent)
+ write_value("profile", [profile.name for profile in creation_info.profile], text_output, indent)
+ write_value("data license", creation_info.data_license, text_output, indent)
+ write_value("comment", creation_info.comment, text_output, indent)
diff --git a/src/spdx_tools/spdx3/writer/console/dataset/__init__.py b/src/spdx_tools/spdx3/writer/console/dataset/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/writer/console/dataset/dataset_writer.py b/src/spdx_tools/spdx3/writer/console/dataset/dataset_writer.py
new file mode 100644
index 000000000..91131240a
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/dataset/dataset_writer.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.dataset import Dataset
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.software.package_writer import write_package
+
+
+def write_dataset(dataset: Dataset, text_output: TextIO):
+ text_output.write("## Dataset\n")
+ write_package(dataset, text_output, False)
+
+ for property_name in Dataset.__annotations__.keys():
+ write_value(property_name, getattr(dataset, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/element_writer.py b/src/spdx_tools/spdx3/writer/console/element_writer.py
new file mode 100644
index 000000000..61eb72ecd
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/element_writer.py
@@ -0,0 +1,34 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Element
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.creation_info_writer import write_creation_info
+from spdx_tools.spdx3.writer.console.external_identifier_writer import write_external_identifier
+from spdx_tools.spdx3.writer.console.external_reference_writer import write_external_reference
+from spdx_tools.spdx3.writer.console.hash_writer import write_hash
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_optional_heading
+
+
+def write_element_properties(element: Element, text_output: TextIO):
+ write_value("SPDXID", element.spdx_id, text_output)
+ write_value("name", element.name, text_output)
+ if element.creation_info:
+ write_creation_info(element.creation_info, text_output, True)
+ write_value("summary", element.summary, text_output)
+ write_value("description", element.description, text_output)
+ write_value("comment", element.comment, text_output)
+ write_optional_heading(element.verified_using, "verified using:\n", text_output)
+ for integrity_method in element.verified_using:
+ # for now Hash is the only child class of the abstract class IntegrityMethod,
+ # as soon as there are more inherited classes we need to implement a logic
+ # that determines the correct write function for the "integrity_method" object
+ write_hash(integrity_method, text_output, heading=False)
+ write_optional_heading(element.external_reference, "External Reference\n", text_output)
+ for external_reference in element.external_reference:
+ write_external_reference(external_reference, text_output)
+ write_optional_heading(element.external_identifier, "External Identifier\n", text_output)
+ for external_identifier in element.external_identifier:
+ write_external_identifier(external_identifier, text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/external_identifier_writer.py b/src/spdx_tools/spdx3/writer/console/external_identifier_writer.py
new file mode 100644
index 000000000..40f2d0e97
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/external_identifier_writer.py
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import ExternalIdentifier
+from spdx_tools.spdx3.writer.console.console import write_value
+
+
+def write_external_identifier(external_identifier: ExternalIdentifier, text_output: TextIO):
+ for property_name in ExternalIdentifier.__annotations__.keys():
+ write_value(property_name, getattr(external_identifier, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/external_map_writer.py b/src/spdx_tools/spdx3/writer/console/external_map_writer.py
new file mode 100644
index 000000000..41f59dc5d
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/external_map_writer.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import ExternalMap
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.hash_writer import write_hash
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_optional_heading
+
+
+def write_external_map(external_map: ExternalMap, text_output: TextIO):
+ write_value("external_id", external_map.external_id, text_output)
+ write_optional_heading(external_map.verified_using, "verified using\n", text_output)
+ for integrity_method in external_map.verified_using:
+ # for now Hash is the only child class of the abstract class IntegrityMethod,
+ # as soon as there are more inherited classes we need to implement a logic
+ # that determines the correct write function for the "integrity_method" object
+ write_hash(integrity_method, text_output, heading=False)
+ write_value("location_hint", external_map.location_hint, text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/external_reference_writer.py b/src/spdx_tools/spdx3/writer/console/external_reference_writer.py
new file mode 100644
index 000000000..fa6cc79eb
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/external_reference_writer.py
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import ExternalReference
+from spdx_tools.spdx3.writer.console.console import write_value
+
+
+def write_external_reference(external_reference: ExternalReference, text_output: TextIO):
+ for property_name in ExternalReference.__annotations__.keys():
+ write_value(property_name, getattr(external_reference, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/hash_writer.py b/src/spdx_tools/spdx3/writer/console/hash_writer.py
new file mode 100644
index 000000000..970a49b56
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/hash_writer.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Hash
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.integrity_method_writer import write_integrity_method
+
+
+def write_hash(hash_object: Hash, text_output: TextIO, heading: bool, indent: bool = True):
+ if heading:
+ text_output.write("## Hash\n")
+ write_value("algorithm", hash_object.algorithm, text_output, indent)
+ write_value("hash_value", hash_object.hash_value, text_output, indent)
+ write_integrity_method(hash_object, text_output, indent)
diff --git a/src/spdx_tools/spdx3/writer/console/integrity_method_writer.py b/src/spdx_tools/spdx3/writer/console/integrity_method_writer.py
new file mode 100644
index 000000000..df233c997
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/integrity_method_writer.py
@@ -0,0 +1,11 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import IntegrityMethod
+from spdx_tools.spdx3.writer.console.console import write_value
+
+
+def write_integrity_method(integrity_method: IntegrityMethod, text_output: TextIO, indent: bool = True):
+ write_value("comment", integrity_method.comment, text_output, indent)
diff --git a/src/spdx_tools/spdx3/writer/console/lifecycle_scoped_relationship_writer.py b/src/spdx_tools/spdx3/writer/console/lifecycle_scoped_relationship_writer.py
new file mode 100644
index 000000000..5710ae6e8
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/lifecycle_scoped_relationship_writer.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import LifecycleScopedRelationship
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.relationship_writer import write_relationship
+
+
+def write_lifecycle_scoped_relationship(
+ relationship: LifecycleScopedRelationship, text_output: TextIO, heading: bool = True
+):
+ if heading:
+ text_output.write("## LifecycleScopedRelationship\n")
+ write_relationship(relationship, text_output, heading=False)
+
+ for property_name in LifecycleScopedRelationship.__annotations__.keys():
+ write_value(property_name, getattr(relationship, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/namespace_map_writer.py b/src/spdx_tools/spdx3/writer/console/namespace_map_writer.py
new file mode 100644
index 000000000..d83ccb05a
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/namespace_map_writer.py
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import NamespaceMap
+from spdx_tools.spdx3.writer.console.console import write_value
+
+
+def write_namespace_map(namespace_map: NamespaceMap, text_output: TextIO):
+ for property_name in NamespaceMap.__annotations__.keys():
+ write_value(property_name, getattr(namespace_map, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/payload_writer.py b/src/spdx_tools/spdx3/writer/console/payload_writer.py
new file mode 100644
index 000000000..34532f364
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/payload_writer.py
@@ -0,0 +1,65 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import (
+ Annotation,
+ Bom,
+ Bundle,
+ Organization,
+ Person,
+ Relationship,
+ SoftwareAgent,
+ SpdxDocument,
+ Tool,
+)
+from spdx_tools.spdx3.model.ai import AIPackage
+from spdx_tools.spdx3.model.build import Build
+from spdx_tools.spdx3.model.dataset import Dataset
+from spdx_tools.spdx3.model.software import File, Package, Sbom, Snippet, SoftwareDependencyRelationship
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx3.writer.console.agent_writer import write_agent
+from spdx_tools.spdx3.writer.console.ai.ai_package_writer import write_ai_package
+from spdx_tools.spdx3.writer.console.annotation_writer import write_annotation
+from spdx_tools.spdx3.writer.console.bom_writer import write_bom
+from spdx_tools.spdx3.writer.console.build.build_writer import write_build
+from spdx_tools.spdx3.writer.console.bundle_writer import write_bundle
+from spdx_tools.spdx3.writer.console.dataset.dataset_writer import write_dataset
+from spdx_tools.spdx3.writer.console.relationship_writer import write_relationship
+from spdx_tools.spdx3.writer.console.software.file_writer import write_file
+from spdx_tools.spdx3.writer.console.software.package_writer import write_package
+from spdx_tools.spdx3.writer.console.software.sbom_writer import write_sbom
+from spdx_tools.spdx3.writer.console.software.snippet_writer import write_snippet
+from spdx_tools.spdx3.writer.console.software.software_dependency_relationship_writer import (
+ write_software_dependency_relationship,
+)
+from spdx_tools.spdx3.writer.console.spdx_document_writer import write_spdx_document
+from spdx_tools.spdx3.writer.console.tool_writer import write_tool
+
+MAP_CLASS_TO_WRITE_METHOD = {
+ Annotation: write_annotation,
+ Relationship: write_relationship,
+ SoftwareDependencyRelationship: write_software_dependency_relationship,
+ Bundle: write_bundle,
+ SpdxDocument: write_spdx_document,
+ Bom: write_bom,
+ File: write_file,
+ Package: write_package,
+ Snippet: write_snippet,
+ Sbom: write_sbom,
+ Person: write_agent,
+ Organization: write_agent,
+ SoftwareAgent: write_agent,
+ Tool: write_tool,
+ AIPackage: write_ai_package,
+ Dataset: write_dataset,
+ Build: write_build,
+}
+
+
+def write_payload(payload: Payload, text_output: TextIO):
+ for element in payload.get_full_map().values():
+ write_method = MAP_CLASS_TO_WRITE_METHOD[type(element)]
+ write_method(element, text_output)
+ text_output.write("\n")
diff --git a/src/spdx_tools/spdx3/writer/console/relationship_writer.py b/src/spdx_tools/spdx3/writer/console/relationship_writer.py
new file mode 100644
index 000000000..9f61e5214
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/relationship_writer.py
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Relationship
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.element_writer import write_element_properties
+
+
+def write_relationship(relationship: Relationship, text_output: TextIO, heading: bool = True):
+ if heading:
+ text_output.write("## Relationship\n")
+ write_element_properties(relationship, text_output)
+ for property_name in Relationship.__annotations__.keys():
+ write_value(property_name, getattr(relationship, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/software/__init__.py b/src/spdx_tools/spdx3/writer/console/software/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/writer/console/software/file_writer.py b/src/spdx_tools/spdx3/writer/console/software/file_writer.py
new file mode 100644
index 000000000..ec631f024
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/software/file_writer.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.software import File
+from spdx_tools.spdx3.writer.console.artifact_writer import write_artifact_properties
+from spdx_tools.spdx3.writer.console.console import write_value
+
+
+def write_file(file: File, text_output: TextIO):
+ text_output.write("## File\n")
+ write_artifact_properties(file, text_output)
+
+ for property_name in File.__annotations__.keys():
+ if property_name == "file_purpose":
+ write_value(
+ property_name, ", ".join([purpose.name for purpose in getattr(file, property_name)]), text_output
+ )
+ continue
+ write_value(property_name, getattr(file, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/software/package_writer.py b/src/spdx_tools/spdx3/writer/console/software/package_writer.py
new file mode 100644
index 000000000..1f66f989b
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/software/package_writer.py
@@ -0,0 +1,22 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.software import Package
+from spdx_tools.spdx3.writer.console.artifact_writer import write_artifact_properties
+from spdx_tools.spdx3.writer.console.console import write_value
+
+
+def write_package(package: Package, text_output: TextIO, heading: bool = True):
+ if heading:
+ text_output.write("## Package\n")
+ write_artifact_properties(package, text_output)
+
+ for property_name in Package.__annotations__.keys():
+ if property_name == "package_purpose":
+ write_value(
+ property_name, ", ".join([purpose.name for purpose in getattr(package, property_name)]), text_output
+ )
+ continue
+ write_value(property_name, getattr(package, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/software/sbom_writer.py b/src/spdx_tools/spdx3/writer/console/software/sbom_writer.py
new file mode 100644
index 000000000..2e34a6b00
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/software/sbom_writer.py
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.software import Sbom
+from spdx_tools.spdx3.writer.console.bom_writer import write_bom
+
+
+def write_sbom(sbom: Sbom, text_output: TextIO):
+ text_output.write("## Sbom\n")
+ write_bom(sbom, text_output, False)
diff --git a/src/spdx_tools/spdx3/writer/console/software/snippet_writer.py b/src/spdx_tools/spdx3/writer/console/software/snippet_writer.py
new file mode 100644
index 000000000..b0ea7bbc7
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/software/snippet_writer.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.software import Snippet
+from spdx_tools.spdx3.writer.console.artifact_writer import write_artifact_properties
+from spdx_tools.spdx3.writer.console.console import write_value
+
+
+def write_snippet(snippet: Snippet, text_output: TextIO):
+ text_output.write("## Snippet\n")
+ write_artifact_properties(snippet, text_output)
+
+ for property_name in Snippet.__annotations__.keys():
+ if property_name == "snippet_purpose":
+ write_value(
+ property_name, ", ".join([purpose.name for purpose in getattr(snippet, property_name)]), text_output
+ )
+ continue
+ write_value(property_name, getattr(snippet, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/software/software_dependency_relationship_writer.py b/src/spdx_tools/spdx3/writer/console/software/software_dependency_relationship_writer.py
new file mode 100644
index 000000000..8064c76a1
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/software/software_dependency_relationship_writer.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model.software import SoftwareDependencyRelationship
+from spdx_tools.spdx3.writer.console.console import write_value
+from spdx_tools.spdx3.writer.console.lifecycle_scoped_relationship_writer import write_lifecycle_scoped_relationship
+
+
+def write_software_dependency_relationship(
+ relationship: SoftwareDependencyRelationship, text_output: TextIO, heading: bool = True
+):
+ if heading:
+ text_output.write("## SoftwareDependencyRelationship\n")
+ write_lifecycle_scoped_relationship(relationship, text_output, heading=False)
+
+ for property_name in SoftwareDependencyRelationship.__annotations__.keys():
+ write_value(property_name, getattr(relationship, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/spdx_collection_writer.py b/src/spdx_tools/spdx3/writer/console/spdx_collection_writer.py
new file mode 100644
index 000000000..7654329b2
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/spdx_collection_writer.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import ElementCollection
+from spdx_tools.spdx3.writer.console.element_writer import write_element_properties
+from spdx_tools.spdx3.writer.console.external_map_writer import write_external_map
+from spdx_tools.spdx3.writer.console.namespace_map_writer import write_namespace_map
+from spdx_tools.spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_optional_heading
+
+
+def write_collection(collection: ElementCollection, text_output: TextIO):
+ write_element_properties(collection, text_output)
+ text_output.write(f"elements: {', '.join(collection.element)}\n")
+ write_optional_heading(collection.namespaces, "# Namespaces\n", text_output)
+ for namespace_map in collection.namespaces:
+ write_namespace_map(namespace_map, text_output)
+ write_optional_heading(collection.imports, "# Imports\n", text_output)
+ for external_map in collection.imports:
+ write_external_map(external_map, text_output)
diff --git a/src/spdx_tools/spdx3/writer/console/spdx_document_writer.py b/src/spdx_tools/spdx3/writer/console/spdx_document_writer.py
new file mode 100644
index 000000000..8c2cdf649
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/spdx_document_writer.py
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import SpdxDocument
+from spdx_tools.spdx3.writer.console.bundle_writer import write_bundle
+
+
+def write_spdx_document(spdx_document: SpdxDocument, text_output: TextIO):
+ text_output.write("## SPDX Document\n")
+ write_bundle(spdx_document, text_output, False)
diff --git a/src/spdx_tools/spdx3/writer/console/tool_writer.py b/src/spdx_tools/spdx3/writer/console/tool_writer.py
new file mode 100644
index 000000000..1873263bc
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/console/tool_writer.py
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2023 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from beartype.typing import TextIO
+
+from spdx_tools.spdx3.model import Tool
+from spdx_tools.spdx3.writer.console.element_writer import write_element_properties
+
+
+def write_tool(tool: Tool, text_output: TextIO, heading: bool = True):
+ if heading:
+ text_output.write("## Tool\n")
+ write_element_properties(tool, text_output)
diff --git a/src/spdx_tools/spdx3/writer/json_ld/SPDX_OWL.json b/src/spdx_tools/spdx3/writer/json_ld/SPDX_OWL.json
new file mode 100644
index 000000000..96e744e79
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/json_ld/SPDX_OWL.json
@@ -0,0 +1,5448 @@
+{
+ "@context": {
+ "ai": "https://spdx.org/rdf/AI/",
+ "build": "https://spdx.org/rdf/Build/",
+ "core": "https://spdx.org/rdf/Core/",
+ "dataset": "https://spdx.org/rdf/Dataset/",
+ "licensing": "https://spdx.org/rdf/Licensing/",
+ "ns0": "http://www.w3.org/2003/06/sw-vocab-status/ns#",
+ "owl": "http://www.w3.org/2002/07/owl#",
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+ "security": "https://spdx.org/rdf/Security/",
+ "sh": "http://www.w3.org/ns/shacl#",
+ "software": "https://spdx.org/rdf/Software/",
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
+ },
+ "@graph": [
+ {
+ "@id": "ai:AIPackage",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "Metadata information that can be added to a package to describe an AI application or trained AI model.\nExternal property restriction on /Core/Artifact/suppliedBy: minCount: 1\nExternal property restriction on /Software/Package/downloadLocation: minCount: 1\nExternal property restriction on /Software/Package/packageVersion: minCount: 1\nExternal property restriction on /Software/SoftwareArtifact/purpose: minCount: 1\nExternal property restriction on /Core/Artifact/releaseTime: minCount: 1",
+ "rdfs:subClassOf": {
+ "@id": "software:Package"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:DictionaryEntry"
+ },
+ "sh:name": "metric",
+ "sh:path": {
+ "@id": "ai:metric"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "modelExplainability",
+ "sh:path": {
+ "@id": "ai:modelExplainability"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "domain",
+ "sh:path": {
+ "@id": "ai:domain"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "standardCompliance",
+ "sh:path": {
+ "@id": "ai:standardCompliance"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:DictionaryEntry"
+ },
+ "sh:name": "hyperparameter",
+ "sh:path": {
+ "@id": "ai:hyperparameter"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "energyConsumption",
+ "sh:path": {
+ "@id": "ai:energyConsumption"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "limitation",
+ "sh:path": {
+ "@id": "ai:limitation"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "ai:SafetyRiskAssessmentType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "safetyRiskAssessment",
+ "sh:path": {
+ "@id": "ai:safetyRiskAssessment"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "modelDataPreprocessing",
+ "sh:path": {
+ "@id": "ai:modelDataPreprocessing"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "ai:PresenceType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "sensitivePersonalInformation",
+ "sh:path": {
+ "@id": "ai:sensitivePersonalInformation"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "informationAboutTraining",
+ "sh:path": {
+ "@id": "ai:informationAboutTraining"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "informationAboutApplication",
+ "sh:path": {
+ "@id": "ai:informationAboutApplication"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "typeOfModel",
+ "sh:path": {
+ "@id": "ai:typeOfModel"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "ai:PresenceType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "autonomyType",
+ "sh:path": {
+ "@id": "ai:autonomyType"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:DictionaryEntry"
+ },
+ "sh:name": "metricDecisionThreshold",
+ "sh:path": {
+ "@id": "ai:metricDecisionThreshold"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/AI/PresenceType/no",
+ "@type": [
+ "owl:NamedIndividual",
+ "ai:PresenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/AI/PresenceType/noAssertion",
+ "@type": [
+ "owl:NamedIndividual",
+ "ai:PresenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/AI/PresenceType/yes",
+ "@type": [
+ "owl:NamedIndividual",
+ "ai:PresenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/AI/SafetyRiskAssessmentType/high",
+ "@type": [
+ "owl:NamedIndividual",
+ "ai:SafetyRiskAssessmentType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/AI/SafetyRiskAssessmentType/low",
+ "@type": [
+ "owl:NamedIndividual",
+ "ai:SafetyRiskAssessmentType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/AI/SafetyRiskAssessmentType/medium",
+ "@type": [
+ "owl:NamedIndividual",
+ "ai:SafetyRiskAssessmentType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/AI/SafetyRiskAssessmentType/serious",
+ "@type": [
+ "owl:NamedIndividual",
+ "ai:SafetyRiskAssessmentType"
+ ]
+ },
+ {
+ "@id": "build:Build",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A build is a representation of the process in which a piece of software or artifact is built. It encapsulates information related to a build process and\nprovides an element from which relationships can be created to describe the build's inputs, outputs, and related entities (e.g. builders, identities, etc.).\n\nDefinitions of \"BuildType\", \"ConfigSource\", \"Parameters\" and \"Environment\" follow\nthose defined in [SLSA provenance](https://slsa.dev/provenance/v0.2).\n\nExternalIdentifier of type \"urlScheme\" may be used to identify build logs. In this case, the comment of the ExternalIdentifier should be \"LogReference\".\n\nNote that buildStart and buildEnd are optional, and may be omitted to simplify creating reproducible builds.",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:Hash"
+ },
+ "sh:name": "configSourceDigest",
+ "sh:path": {
+ "@id": "build:configSourceDigest"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:name": "configSourceUri",
+ "sh:path": {
+ "@id": "build:configSourceUri"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "buildId",
+ "sh:path": {
+ "@id": "build:buildId"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:DictionaryEntry"
+ },
+ "sh:name": "parameters",
+ "sh:path": {
+ "@id": "build:parameters"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "buildEndTime",
+ "sh:path": {
+ "@id": "build:buildEndTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "buildStartTime",
+ "sh:path": {
+ "@id": "build:buildStartTime"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:DictionaryEntry"
+ },
+ "sh:name": "environment",
+ "sh:path": {
+ "@id": "build:environment"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "buildType",
+ "sh:path": {
+ "@id": "build:buildType"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "configSourceEntrypoint",
+ "sh:path": {
+ "@id": "build:configSourceEntrypoint"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:Annotation",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An Annotation is an assertion made in relation to one or more elements.",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "statement",
+ "sh:path": {
+ "@id": "core:statement"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Element"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "subject",
+ "sh:path": {
+ "@id": "core:subject"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:MediaType"
+ },
+ "sh:name": "contentType",
+ "sh:path": {
+ "@id": "core:contentType"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:AnnotationType"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "annotationType",
+ "sh:path": {
+ "@id": "core:annotationType"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/AnnotationType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:AnnotationType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/AnnotationType/review",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:AnnotationType"
+ ]
+ },
+ {
+ "@id": "core:AnonymousPayload",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "TODO",
+ "rdfs:subClassOf": {
+ "@id": "core:Payload"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:CreationInfo"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "creationInfo",
+ "sh:path": {
+ "@id": "core:creationInfo"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:NamespaceMap"
+ },
+ "sh:name": "namespaces",
+ "sh:path": {
+ "@id": "core:namespaces"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:ExternalMap"
+ },
+ "sh:name": "imports",
+ "sh:path": {
+ "@id": "core:imports"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/cpe22",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/cpe23",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/cve",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/email",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/gitoid",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/pkgUrl",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/securityOther",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/swhid",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/swid",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalIdentifierType/urlScheme",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/altDownloadLocation",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/altWebPage",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/binaryArtifact",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/buildMeta",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/buildSystem",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/chat",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/documentation",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/funding",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/issueTracker",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/license",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/mailingList",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/metrics",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/releaseHistory",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/releaseNotes",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/securityAdvisory",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/securityFix",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/securityOther",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/socialMedia",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/sourceArtifact",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/support",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ExternalReferenceType/vcs",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ExternalReferenceType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/blake2b256",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/blake2b384",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/blake2b512",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/blake3",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/crystalsDilithium",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/crystalsKyber",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/falcon",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/md2",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/md4",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/md5",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/md6",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha1",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha224",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha256",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha384",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha3_224",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha3_256",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha3_384",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha3_512",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sha512",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/spdxPvcSha1",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/spdxPvcSha256",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/HashAlgorithm/sphincsPlus",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:HashAlgorithm"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/LifecycleScopeType/build",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:LifecycleScopeType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/LifecycleScopeType/design",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:LifecycleScopeType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/LifecycleScopeType/development",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:LifecycleScopeType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/LifecycleScopeType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:LifecycleScopeType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/LifecycleScopeType/runtime",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:LifecycleScopeType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/LifecycleScopeType/test",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:LifecycleScopeType"
+ ]
+ },
+ {
+ "@id": "core:Organization",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An Organization is a group of people who work together in an organized way for a shared purpose.",
+ "rdfs:subClassOf": {
+ "@id": "core:Agent"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:Person",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A Person is an individual human being.",
+ "rdfs:subClassOf": {
+ "@id": "core:Agent"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/ai",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/build",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/core",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/dataset",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/extension",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/licensing",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/security",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/software",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/ProfileIdentifierType/usage",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:ProfileIdentifierType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipCompleteness/complete",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipCompleteness"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipCompleteness/incomplete",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipCompleteness"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipCompleteness/noAssertion",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipCompleteness"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/affects",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/amends",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/ancestor",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/availableFrom",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildConfigOf",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildDependency",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildHostOf",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildInputOf",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildInvokedBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildOnBehalfOf",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildOutputOf",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/buildTool",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/contains",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/coordinatedBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/copy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/dataFile",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/dependencyManifest",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/dependsOn",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/descendant",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/describes",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/devDependency",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/devTool",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/distributionArtifact",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/documentation",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/doesNotAffect",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/dynamicLink",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/example",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/expandedFromArchive",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/exploitCreatedBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/fileAdded",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/fileDeleted",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/fileModified",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/fixedBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/fixedIn",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/foundBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/generates",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/hasAssessmentFor",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/hasAssociatedVulnerability",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/metafile",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/optionalComponent",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/optionalDependency",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/packages",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/patch",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/prerequisite",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/providedDependency",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/publishedBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/reportedBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/republishedBy",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/requirementFor",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/runtimeDependency",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/specificationFor",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/staticLink",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/test",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/testCase",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/testDependency",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/testTool",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/underInvestigationFor",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Core/RelationshipType/variant",
+ "@type": [
+ "owl:NamedIndividual",
+ "core:RelationshipType"
+ ]
+ },
+ {
+ "@id": "core:SoftwareAgent",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A SoftwareAgent is a software program that is given the authority (similar to a user's authority) to act on a system.",
+ "rdfs:subClassOf": {
+ "@id": "core:Agent"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:SpdxDocument",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An SpdxDocument assembles a collection of Elements under a common string, the name of the document.\nCommonly used when representing a unit of transfer of SPDX Elements.\nExternal property restriction on /Core/Element/name: minCount: 1",
+ "rdfs:subClassOf": {
+ "@id": "core:Bundle"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "name",
+ "sh:path": {
+ "@id": "core:name"
+ }
+ }
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/ConfidentialityLevelType/Amber",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:ConfidentialityLevelType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/ConfidentialityLevelType/Clear",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:ConfidentialityLevelType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/ConfidentialityLevelType/Green",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:ConfidentialityLevelType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/ConfidentialityLevelType/Red",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:ConfidentialityLevelType"
+ ]
+ },
+ {
+ "@id": "dataset:Dataset",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "Metadata information that can be added to a dataset that may be used in a software or to train/test an AI package.\nExternal property restriction on /Core/Artifact/originatedBy: minCount: 1\nExternal property restriction on /Software/Package/downloadLocation: minCount: 1\nExternal property restriction on /Software/SoftwareArtifact/purpose: minCount: 1\nExternal property restriction on /Core/Artifact/releaseTime: minCount: 1\nExternal property restriction on /Core/Artifact/builtTime: minCount: 1",
+ "rdfs:subClassOf": {
+ "@id": "software:Package"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "dataset:DatasetAvailabilityType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "datasetAvailability",
+ "sh:path": {
+ "@id": "dataset:datasetAvailability"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "dataset:ConfidentialityLevelType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "confidentialityLevel",
+ "sh:path": {
+ "@id": "dataset:confidentialityLevel"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "dataCollectionProcess",
+ "sh:path": {
+ "@id": "dataset:dataCollectionProcess"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "datasetUpdateMechanism",
+ "sh:path": {
+ "@id": "dataset:datasetUpdateMechanism"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "knownBias",
+ "sh:path": {
+ "@id": "dataset:knownBias"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:nonNegativeInteger"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "datasetSize",
+ "sh:path": {
+ "@id": "dataset:datasetSize"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "intendedUse",
+ "sh:path": {
+ "@id": "dataset:intendedUse"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "dataset:PresenceType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "sensitivePersonalInformation",
+ "sh:path": {
+ "@id": "dataset:sensitivePersonalInformation"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "dataPreprocessing",
+ "sh:path": {
+ "@id": "dataset:dataPreprocessing"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "datasetNoise",
+ "sh:path": {
+ "@id": "dataset:datasetNoise"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:DictionaryEntry"
+ },
+ "sh:name": "sensor",
+ "sh:path": {
+ "@id": "dataset:sensor"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "datasetType",
+ "sh:path": {
+ "@id": "dataset:datasetType"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "anonymizationMethodUsed",
+ "sh:path": {
+ "@id": "dataset:anonymizationMethodUsed"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/DatasetAvailabilityType/Clickthrough",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:DatasetAvailabilityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/DatasetAvailabilityType/Direct-Download",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:DatasetAvailabilityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/DatasetAvailabilityType/Query",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:DatasetAvailabilityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/DatasetAvailabilityType/Registration",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:DatasetAvailabilityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Dataset/DatasetAvailabilityType/Scraping-Script",
+ "@type": [
+ "owl:NamedIndividual",
+ "dataset:DatasetAvailabilityType"
+ ]
+ },
+ {
+ "@id": "licensing:ConjunctiveLicenseSet",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A ConjunctiveLicenseSet indicates that _each_ of its subsidiary\nAnyLicenseInfos apply. In other words, a ConjunctiveLicenseSet of two or\nmore licenses represents a licensing situation where _all_ of the specified\nlicenses are to be complied with. It is represented in the SPDX License\nExpression Syntax by the `AND` operator.\n\nIt is syntactically correct to specify a ConjunctiveLicenseSet where the\nsubsidiary AnyLicenseInfos may be \"incompatible\" according to a particular\ninterpretation of the corresponding Licenses. The SPDX License Expression\nSyntax does not take into account interpretation of license texts, which is\nleft to the consumer of SPDX data to determine for themselves.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:class": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "sh:minCount": 2,
+ "sh:name": "member",
+ "sh:path": {
+ "@id": "licensing:member"
+ }
+ }
+ },
+ {
+ "@id": "licensing:CustomLicense",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A CustomLicense represents a License that is not listed on the SPDX License\nList at https://spdx.org/licenses, and is therefore defined by an SPDX data\ncreator.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:License"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:CustomLicenseAddition",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A CustomLicenseAddition represents an addition to a License that is not listed\non the SPDX Exceptions List at https://spdx.org/licenses/exceptions-index.html,\nand is therefore defined by an SPDX data creator.\n\nIt is intended to represent additional language which is meant to be added to\na License, but which is not itself a standalone License.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:LicenseAddition"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:DisjunctiveLicenseSet",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A DisjunctiveLicenseSet indicates that _only one_ of its subsidiary\nAnyLicenseInfos is required to apply. In other words, a\nDisjunctiveLicenseSet of two or more licenses represents a licensing\nsituation where _only one_ of the specified licenses are to be complied with.\nA consumer of SPDX data would typically understand this to permit the recipient\nof the licensed content to choose which of the corresponding license they\nwould prefer to use. It is represented in the SPDX License Expression Syntax\nby the `OR` operator.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:class": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "sh:minCount": 2,
+ "sh:name": "member",
+ "sh:path": {
+ "@id": "licensing:member"
+ }
+ }
+ },
+ {
+ "@id": "licensing:ListedLicense",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A ListedLicense represents a License that is listed on the SPDX License List\nat https://spdx.org/licenses.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:License"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "deprecatedVersion",
+ "sh:path": {
+ "@id": "licensing:deprecatedVersion"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "listVersionAdded",
+ "sh:path": {
+ "@id": "licensing:listVersionAdded"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "licensing:ListedLicenseException",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A ListedLicenseException represents an exception to a License (in other words,\nan exception to a license condition or an additional permission beyond those\ngranted in a License) which is listed on the SPDX Exceptions List at\nhttps://spdx.org/licenses/exceptions-index.html.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:LicenseAddition"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "deprecatedVersion",
+ "sh:path": {
+ "@id": "licensing:deprecatedVersion"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "listVersionAdded",
+ "sh:path": {
+ "@id": "licensing:listVersionAdded"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "licensing:NoAssertionLicense",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A NoAssertionLicense is the primary value that is used by a concludedLicense\nor declaredLicense field that indicates that the SPDX data creator is making\nno assertion about the license information for the corresponding software\nPackage, File or Snippet.\n\nThe specific meaning of NoAssertionLicense in the context of a\nconcludedLicense or declaredLicense field is more fully set forth in the\nProperty definitions for those fields.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:LicenseField"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:NoneLicense",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A NoneLicense is the primary value that is used by a concludedLicense or\ndeclaredLicense field that indicates the absence of license information from\nthe corresponding software Package, File or Snippet.\n\nThe specific meaning of NoneLicense in the context of a concludedLicense or\ndeclaredLicense field is more fully set forth in the Property definitions for\nthose fields.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:LicenseField"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:OrLaterOperator",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An OrLaterOperator indicates that this portion of the AnyLicenseInfo\nrepresents either (1) the specified version of the corresponding License, or\n(2) any later version of that License. It is represented in the SPDX License\nExpression Syntax by the `+` operator.\n\nIt is context-dependent, and unspecified by SPDX, as to what constitutes a\n\"later version\" of any particular License. Some Licenses may not be versioned,\nor may not have clearly-defined ordering for versions. The consumer of SPDX\ndata will need to determine for themselves what meaning to attribute to a\n\"later version\" operator for a particular License.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:class": {
+ "@id": "licensing:License"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "subjectLicense",
+ "sh:path": {
+ "@id": "licensing:subjectLicense"
+ }
+ }
+ },
+ {
+ "@id": "licensing:WithAdditionOperator",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A WithAdditionOperator indicates that the designated License is subject to the\ndesignated LicenseAddition, which might be a license exception on the SPDX\nExceptions List (ListedLicenseException) or may be other additional text\n(CustomLicenseAddition). It is represented in the SPDX License Expression\nSyntax by the `WITH` operator.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "licensing:License"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "subjectLicense",
+ "sh:path": {
+ "@id": "licensing:subjectLicense"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "licensing:LicenseAddition"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "subjectAddition",
+ "sh:path": {
+ "@id": "licensing:subjectAddition"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "security:CvssV2VulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A CvssV2VulnAssessmentRelationship relationship describes the determined score and vector of a vulnerability using version 2.0 of the Common Vulnerability Scoring System\n(CVSS) as defined on [https://www.first.org/cvss/v2/guide](https://www.first.org/cvss/v2/guide). It is intented to communicate the results of using a CVSS calculator.\n\n**Constraints**\n\n- The value of severity must be one of 'low', 'medium' or 'high'\n- The relationship type must be set to hasAssessmentFor.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"CvssV2VulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:cvssv2-cve-2020-28498\",\n \"relationshipType\": \"hasAssessmentFor\",\n \"score\": 4.3,\n \"vector\": \"(AV:N/AC:M/Au:N/C:P/I:N/A:N)\",\n \"severity\": \"low\",\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"assessedElement\": \"urn:npm-elliptic-6.5.2\",\n \"externalReferences\": [\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityAdvisory\",\n \"locator\": \"https://nvd.nist.gov/vuln/detail/CVE-2020-28498\"\n },\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityAdvisory\",\n \"locator\": \"https://snyk.io/vuln/SNYK-JS-ELLIPTIC-1064899\"\n },\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityFix\",\n \"locator\": \"https://github.com/indutny/elliptic/commit/441b742\"\n }\n ],\n \"suppliedBy\": [\"urn:spdx.dev:agent-my-security-vendor\"],\n \"publishedTime\": \"2023-05-06T10:06:13Z\"\n},\n{\n \"@type\": \"Relationship\",\n \"@id\": \"urn:spdx.dev:vulnAgentRel-1\", \n \"relationshipType\": \"publishedBy\", \n \"from\": \"urn:spdx.dev:cvssv2-cve-2020-28498\",\n \"to\": [\"urn:spdx.dev:agent-snyk\"],\n \"startTime\": \"2021-03-08T16:06:50Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:decimal"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "score",
+ "sh:path": {
+ "@id": "security:score"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "severity",
+ "sh:path": {
+ "@id": "security:severity"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "vector",
+ "sh:path": {
+ "@id": "security:vector"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "security:CvssV3VulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A CvssV3VulnAssessmentRelationship relationship describes the determined score,\nseverity, and vector of a vulnerability using version 3.1 of the Common\nVulnerability Scoring System (CVSS) as defined on \n[https://www.first.org/cvss/v3.1/specification-document](https://www.first.org/cvss/v3.1/specification-document). It is intented to communicate the results of using a CVSS calculator.\n\n**Constraints**\n\n- The value of severity must be one of 'none', 'low', 'medium', 'high' or 'critical'.\n- Absence of the property shall be interpreted as 'none'.\n- The relationship type must be set to hasAssessmentFor.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"CvssV3VulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:cvssv3-cve-2020-28498\",\n \"relationshipType\": \"hasAssessmentFor\",\n \"severity\": \"medium\",\n \"score\": 6.8,\n \"vector\": \"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N\",\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"assessedElement\": \"urn:npm-elliptic-6.5.2\",\n \"externalReferences\": [\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityAdvisory\",\n \"locator\": \"https://nvd.nist.gov/vuln/detail/CVE-2020-28498\"\n },\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityAdvisory\",\n \"locator\": \"https://snyk.io/vuln/SNYK-JS-ELLIPTIC-1064899\"\n },\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityFix\",\n \"locator\": \"https://github.com/indutny/elliptic/commit/441b742\"\n }\n ],\n \"suppliedBy\": [\"urn:spdx.dev:agent-my-security-vendor\"],\n \"publishedTime\": \"2023-05-06T10:06:13Z\"\n},\n{\n \"@type\": \"Relationship\",\n \"@id\": \"urn:spdx.dev:vulnAgentRel-1\",\n \"relationshipType\": \"publishedBy\",\n \"from\": \"urn:spdx.dev:cvssv3-cve-2020-28498\",\n \"to\": \"urn:spdx.dev:agent-snyk\",\n \"startTime\": \"2021-03-08T16:06:50Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:decimal"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "score",
+ "sh:path": {
+ "@id": "security:score"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "vector",
+ "sh:path": {
+ "@id": "security:vector"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "severity",
+ "sh:path": {
+ "@id": "security:severity"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "security:EpssVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An EpssVulnAssessmentRelationship relationship describes the likelihood or\nprobability that a vulnerability will be exploited in the wild using the Exploit\nPrediction Scoring System (EPSS) as defined on \n[https://www.first.org/epss/model](https://www.first.org/epss/model).\n\n**Constraints**\n\n- The relationship type must be set to hasAssessmentFor.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"EpssVulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:epss-1\",\n \"relationshipType\": \"hasAssessmentFor\",\n \"probability\": 80,\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"suppliedBy\": [\"urn:spdx.dev:agent-jane-doe\"],\n \"publishedTime\": \"2021-03-09T11:04:53Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:nonNegativeInteger"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "probability",
+ "sh:path": {
+ "@id": "security:probability"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "severity",
+ "sh:path": {
+ "@id": "security:severity"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/ExploitCatalogType/kev",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:ExploitCatalogType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/ExploitCatalogType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:ExploitCatalogType"
+ ]
+ },
+ {
+ "@id": "security:ExploitCatalogVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An ExploitCatalogVulnAssessmentRelationship describes if a vulnerability is\nlisted in any exploit catalog such as the CISA Known Exploited Vulnerabilities\nCatalog (KEV) \n[https://www.cisa.gov/known-exploited-vulnerabilities-catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog).\n\n**Constraints**\n\n- The relationship type must be set to hasAssessmentFor.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"ExploitCatalogVulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:exploit-catalog-1\",\n \"relationshipType\": \"hasAssessmentFor\",\n \"catalogType\": \"kev\",\n \"locator\": \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n \"exploited\": \"true\",\n \"from\": \"urn:spdx.dev:vuln-cve-2023-2136\",\n \"to\": [\"urn:product-google-chrome-112.0.5615.136\"],\n \"suppliedBy\": [\"urn:spdx.dev:agent-jane-doe\"],\n \"publishedTime\": \"2021-03-09T11:04:53Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "locator",
+ "sh:path": {
+ "@id": "security:locator"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "security:ExploitCatalogType"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "catalogType",
+ "sh:path": {
+ "@id": "security:catalogType"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:boolean"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "exploited",
+ "sh:path": {
+ "@id": "security:exploited"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/SsvcDecisionType/act",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:SsvcDecisionType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/SsvcDecisionType/attend",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:SsvcDecisionType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/SsvcDecisionType/track",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:SsvcDecisionType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/SsvcDecisionType/trackStar",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:SsvcDecisionType"
+ ]
+ },
+ {
+ "@id": "security:SsvcVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An SsvcVulnAssessmentRelationship describes the decision made using the\nStakeholder-Specific Vulnerability Categorization (SSVC) decision tree as\ndefined on [https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc](https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc).\nIt is intended to communicate the results of using the CISA SSVC Calculator.\n\n**Constraints**\n\n- The relationship type must be set to hasAssessmentFor.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"SsvcVulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:ssvc-1\",\n \"relationshipType\": \"hasAssessmentFor\",\n \"decisionType\": \"act\",\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"assessedElement\": \"urn:npm-elliptic-6.5.2\",\n \"suppliedBy\": [\"urn:spdx.dev:agent-jane-doe\"],\n \"publishedTime\": \"2021-03-09T11:04:53Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:class": {
+ "@id": "security:SsvcDecisionType"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "decisionType",
+ "sh:path": {
+ "@id": "security:decisionType"
+ }
+ }
+ },
+ {
+ "@id": "security:VexAffectedVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "VexAffectedVulnAssessmentRelationship connects a vulnerability and a number\nof elements. The relationship marks these elements as products affected by the\nvulnerability. This relationship corresponds to the VEX affected status.\n\n**Constraints**\n\nWhen linking elements using a VexAffectedVulnAssessmentRelationship, the\nfollowing requirements must be observed:\n\n- Elements linked with a VulnVexAffectedAssessmentRelationship are constrained\nto the affects relationship type.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"VexAffectedVulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:vex-affected-1\",\n \"relationshipType\": \"affects\",\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"assessedElement\": \"urn:npm-elliptic-6.5.2\",\n \"actionStatement\": \"Upgrade to version 1.4 of ACME application.\",\n \"suppliedBy\": [\"urn:spdx.dev:agent-jane-doe\"],\n \"publishedTime\": \"2021-03-09T11:04:53Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VexVulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:name": "actionStatementTime",
+ "sh:path": {
+ "@id": "security:actionStatementTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "actionStatement",
+ "sh:path": {
+ "@id": "security:actionStatement"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "security:VexFixedVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "VexFixedVulnAssessmentRelationship links a vulnerability to a number of elements\nrepresenting VEX products where a vulnerability has been fixed and are no longer\naffected. It represents the VEX fixed status.\n\n**Constraints**\n\nWhen linking elements using a VexFixedVulnAssessmentRelationship, the following\nrequirements must be observed:\n\n- Elements linked with a VulnVexFixedAssessmentRelationship are constrained to\nusing the fixedIn relationship type.\n- The from: end of the relationship must ve a /Security/Vulnerability classed\nelement.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"VexFixedVulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:vex-fixed-in-1\",\n \"relationshipType\": \"fixedIn\",\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"assessedElement\": \"urn:npm-elliptic-6.5.4\",\n \"suppliedBy\": [\"urn:spdx.dev:agent-jane-doe\"],\n \"publishedTime\": \"2021-03-09T11:04:53Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VexVulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/VexJustificationType/componentNotPresent",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:VexJustificationType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/VexJustificationType/inlineMitigationsAlreadyExist",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:VexJustificationType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/VexJustificationType/vulnerableCodeCannotBeControlledByAdversary",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:VexJustificationType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/VexJustificationType/vulnerableCodeNotInExecutePath",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:VexJustificationType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Security/VexJustificationType/vulnerableCodeNotPresent",
+ "@type": [
+ "owl:NamedIndividual",
+ "security:VexJustificationType"
+ ]
+ },
+ {
+ "@id": "security:VexNotAffectedVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "VexNotAffectedVulnAssessmentRelationship connects a vulnerability and a number\nof elements designating them as products not affected by the vulnerability.\nThis relationship corresponds to the VEX not_affected status.\n\n**Constraints**\n\nWhen linking elements using a VexNotVulnAffectedAssessmentRelationship, the\nfollowing requirements must be observed:\n\n* Relating elements with a VexNotAffectedVulnAssessmentRelationship is restricted\nto the doesNotAffect relationship type.\n* The from: end of the relationship must be a /Security/Vulnerability classed\nelement.\n* Both impactStatement and justificationType properties have a cardinality of\n0..1 making them optional. Nevertheless, to produce a valid VEX not_affected\nstatement, one of them MUST be defined. This is specified in the Minimum Elements\nfor VEX.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"VexNotAffectedVulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:vex-not-affected-1\",\n \"relationshipType\": \"doesNotAffect\",\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"assessedElement\": \"urn:npm-elliptic-6.5.2\",\n \"justificationType\": \"componentNotPresent\",\n \"impactStatement\": \"Not using this vulnerable part of this library.\",\n \"suppliedBy\": [\"urn:spdx.dev:agent-jane-doe\"],\n \"publishedTime\": \"2021-03-09T11:04:53Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VexVulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "impactStatementTime",
+ "sh:path": {
+ "@id": "security:impactStatementTime"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "security:VexJustificationType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "justificationType",
+ "sh:path": {
+ "@id": "security:justificationType"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "impactStatement",
+ "sh:path": {
+ "@id": "security:impactStatement"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "security:VexUnderInvestigationVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "VexUnderInvestigationVulnAssessmentRelationship links a vulnerability to a\nnumber of products stating the vulnerability's impact on them is being\ninvestigated. It represents the VEX under_investigation status.\n\n**Constraints**\n\nWhen linking elements using a VexUnderInvestigationVulnAssessmentRelationship\nthe following requirements must be observed:\n\n- Elements linked with a VexUnderInvestigationVulnAssessmentRelationship are\nconstrained to using the underInvestigationFor relationship type.\n- The from: end of the relationship must ve a /Security/Vulnerability classed\nelement.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"VexUnderInvestigationVulnAssessmentRelationship\",\n \"@id\": \"urn:spdx.dev:vex-underInvestigation-1\",\n \"relationshipType\": \"underInvestigationFor\",\n \"from\": \"urn:spdx.dev:vuln-cve-2020-28498\",\n \"to\": [\"urn:product-acme-application-1.3\"],\n \"assessedElement\": \"urn:npm-elliptic-6.5.2\",\n \"suppliedBy\": [\"urn:spdx.dev:agent-jane-doe\"],\n \"publishedTime\": \"2021-03-09T11:04:53Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "security:VexVulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:Vulnerability",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "Specifies a vulnerability and its associated information.\n\n**Syntax**\n\n```json\n{\n \"@type\": \"Vulnerability\",\n \"@id\": \"urn:spdx.dev:vuln-1\",\n \"summary\": \"Use of a Broken or Risky Cryptographic Algorithm\",\n \"description\": \"The npm package `elliptic` before version 6.5.4 are vulnerable to Cryptographic Issues via the secp256k1 implementation in elliptic/ec/key.js. There is no check to confirm that the public key point passed into the derive function actually exists on the secp256k1 curve. This results in the potential for the private key used in this implementation to be revealed after a number of ECDH operations are performed.\", \n \"modified\": \"2021-03-08T16:02:43Z\",\n \"published\": \"2021-03-08T16:06:50Z\",\n \"externalIdentifiers\": [\n {\n \"@type\": \"ExternalIdentifier\",\n \"externalIdentifierType\": \"cve\",\n \"identifier\": \"CVE-2020-2849\",\n \"identifierLocator\": [\n \"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-28498\",\n \"https://www.cve.org/CVERecord?id=CVE-2020-28498\"\n ],\n \"issuingAuthority\": \"urn:spdx.dev:agent-cve.org\"\n },\n {\n \"type\": \"ExternalIdentifier\",\n \"externalIdentifierType\": \"securityOther\",\n \"identifier\": \"GHSA-r9p9-mrjm-926w\",\n \"identifierLocator\": \"https://github.com/advisories/GHSA-r9p9-mrjm-926w\"\n },\n {\n \"type\": \"ExternalIdentifier\",\n \"externalIdentifierType\": \"securityOther\",\n \"identifier\": \"SNYK-JS-ELLIPTIC-1064899\",\n \"identifierLocator\": \"https://security.snyk.io/vuln/SNYK-JS-ELLIPTIC-1064899\"\n }\n ],\n \"externalReferences\": [\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityAdvisory\",\n \"locator\": \"https://nvd.nist.gov/vuln/detail/CVE-2020-28498\"\n },\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityAdvisory\",\n \"locator\": \"https://ubuntu.com/security/CVE-2020-28498\"\n },\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityOther\",\n \"locator\": \"https://github.com/indutny/elliptic/pull/244/commits\"\n },\n {\n \"@type\": \"ExternalReference\",\n \"externalReferenceType\": \"securityOther\",\n \"locator\": \"https://github.com/christianlundkvist/blog/blob/master/2020_05_26_secp256k1_twist_attacks/secp256k1_twist_attacks.md\"\n }\n ]\n},\n{\n \"@type\": \"Relationship\",\n \"@id\": \"urn:spdx.dev:vulnRelationship-1\",\n \"relationshipType\": \"hasAssociatedVulnerability\",\n \"from\": \"urn:npm-elliptic-6.5.2\",\n \"to\": [\"urn:spdx.dev:vuln-1\"],\n \"startTime\": \"2021-03-08T16:06:50Z\"\n},\n{\n \"@type\": \"Relationship\",\n \"@id\": \"urn:spdx.dev:vulnAgentRel-1\", \n \"relationshipType\": \"publishedBy\", \n \"from\": \"urn:spdx.dev:vuln-1\",\n \"to\": [\"urn:spdx.dev:agent-snyk\"],\n \"startTime\": \"2021-03-08T16:06:50Z\"\n}\n```",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "publishedTime",
+ "sh:path": {
+ "@id": "security:publishedTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "modifiedTime",
+ "sh:path": {
+ "@id": "security:modifiedTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "withdrawnTime",
+ "sh:path": {
+ "@id": "security:withdrawnTime"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/DependencyConditionalityType/optional",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:DependencyConditionalityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/DependencyConditionalityType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:DependencyConditionalityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/DependencyConditionalityType/prerequisite",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:DependencyConditionalityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/DependencyConditionalityType/provided",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:DependencyConditionalityType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/DependencyConditionalityType/required",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:DependencyConditionalityType"
+ ]
+ },
+ {
+ "@id": "software:File",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "Refers to any object that stores content on a computer.\nThe type of content can optionally be provided in the contentType property.\nExternal property restriction on /Core/Element/name: minCount: 1",
+ "rdfs:subClassOf": {
+ "@id": "software:SoftwareArtifact"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:datatype": {
+ "@id": "core:MediaType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "contentType",
+ "sh:path": {
+ "@id": "software:contentType"
+ }
+ }
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SBOMType/analyzed",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SBOMType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SBOMType/build",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SBOMType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SBOMType/deployed",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SBOMType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SBOMType/design",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SBOMType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SBOMType/runtime",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SBOMType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SBOMType/source",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SBOMType"
+ ]
+ },
+ {
+ "@id": "software:Sbom",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A Software Bill of Materials (SBOM) is a collection of SPDX Elements describing a single package.\nThis could include details of the content and composition of the product,\nprovenance details of the product and/or\nits composition, licensing information, known quality or security issues, etc.",
+ "rdfs:subClassOf": {
+ "@id": "core:Bom"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:class": {
+ "@id": "software:SBOMType"
+ },
+ "sh:name": "sbomType",
+ "sh:path": {
+ "@id": "software:sbomType"
+ }
+ }
+ },
+ {
+ "@id": "software:Snippet",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A Snippet describes a certain part of a file and can be used when the file is known to have some content\nthat has been included from another original source. Snippets are useful for denoting when part of a file\nmay have been originally created under another license or copied from a place with a known vulnerability.",
+ "rdfs:subClassOf": {
+ "@id": "software:SoftwareArtifact"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:PositiveIntegerRange"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "byteRange",
+ "sh:path": {
+ "@id": "software:byteRange"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:PositiveIntegerRange"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "lineRange",
+ "sh:path": {
+ "@id": "software:lineRange"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwareDependencyLinkType/dynamic",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwareDependencyLinkType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwareDependencyLinkType/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwareDependencyLinkType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwareDependencyLinkType/static",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwareDependencyLinkType"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwareDependencyLinkType/tool",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwareDependencyLinkType"
+ ]
+ },
+ {
+ "@id": "software:SoftwareDependencyRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "TODO",
+ "rdfs:subClassOf": {
+ "@id": "core:LifecycleScopedRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "software:SoftwareDependencyLinkType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "softwareLinkage",
+ "sh:path": {
+ "@id": "software:softwareLinkage"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "software:DependencyConditionalityType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "conditionality",
+ "sh:path": {
+ "@id": "software:conditionality"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/application",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/archive",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/bom",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/configuration",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/container",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/data",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/device",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/documentation",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/evidence",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/executable",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/file",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/firmware",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/framework",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/install",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/library",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/manifest",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/module",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/operatingSystem",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/other",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/patch",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/requirement",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/source",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "https://spdx.org/rdf/Software/SoftwarePurpose/specification",
+ "@type": [
+ "owl:NamedIndividual",
+ "software:SoftwarePurpose"
+ ]
+ },
+ {
+ "@id": "ai:autonomyType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "AutonomyType indicates if a human is involved in any of the decisions of the AI software\nor if that software is fully automatic.",
+ "rdfs:range": {
+ "@id": "ai:PresenceType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:domain",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Domain describes the domain in which the AI model contained in the AI software\ncan be expected to operate successfully. Examples include computer vision, natural language etc.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:energyConsumption",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "EnergyConsumption captures the amount of energy needed to train and operate the AI model. \nThis value is also known as training energy consumption or inference energy consumption.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:hyperparameter",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field records a hyperparameter value.\nHyperparameters are parameters of the machine learning model that are used to control the learning process,\nfor example the optimization and learning rate used during the training of the model.",
+ "rdfs:range": {
+ "@id": "core:DictionaryEntry"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:informationAboutApplication",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "InformationAboutApplication describes any relevant information in free form text about \nhow the AI model is used inside the software, as well as any relevant pre-processing steps, third party APIs etc.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:informationAboutTraining",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "InformationAboutTraining describes the specific steps involved in the training of the AI model.\nFor example, it can be specified whether supervised fine-tuning \nor active learning is used as part of training the model.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:limitation",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Limitation captures a limitation of the AI Package (or of the AI models present in the AI package),\nexpressed as free form text. Note that this is not guaranteed to be exhaustive.\nFor instance, a limitation might be that the AI package cannot be used on datasets from a certain demography.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:metric",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Metric records the measurement with which the AI model was evaluated. \nThis makes statements about the prediction quality including uncertainty,\naccuracy, characteristics of the tested population, quality, fairness, explainability, robustness etc.",
+ "rdfs:range": {
+ "@id": "core:DictionaryEntry"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:metricDecisionThreshold",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Each metric might be computed based on a decision threshold. \nFor instance, precision or recall is typically computed by checking\nif the probability of the outcome is larger than 0.5.\nEach decision threshold should match with a metric field defined in the AI Package.",
+ "rdfs:range": {
+ "@id": "core:DictionaryEntry"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:modelDataPreprocessing",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "ModelDataPreprocessing is a free form text that describes the preprocessing steps\napplied to the training data before training of the model(s) contained in the AI software.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:modelExplainability",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "ModelExplainability is a free form text that lists the different explainability mechanisms\n(such as SHAP, or other model specific explainability mechanisms) that can be used to explain the model.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:safetyRiskAssessment",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "SafetyRiskAssessment categorizes the safety risk impact of the AI software\nin accordance with Article 20 of [EC Regulation No 765/2008](https://ec.europa.eu/docsroom/documents/17107/attachments/1/translations/en/renditions/pdf).",
+ "rdfs:range": {
+ "@id": "ai:SafetyRiskAssessmentType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:sensitivePersonalInformation",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "SensitivePersonalInformation notes if sensitive personal information\nis used in the training or inference of the AI models.\nThis might include biometric data, addresses or other data that can be used to infer a person's identity.",
+ "rdfs:range": {
+ "@id": "ai:PresenceType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:standardCompliance",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "StandardCompliance captures a standard that the AI software complies with. \nThis includes both published and unpublished standards, for example ISO, IEEE, ETSI etc. \nThe standard could (but not necessarily have to) be used to satisfy a legal or regulatory requirement.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:typeOfModel",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "TypeOfModel records the type of the AI model(s) used in the software. \nFor instance, if it is a supervised model, unsupervised model, reinforcement learning model or a combination of those.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:buildEndTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "buildEndTime describes the time at which a build stops or finishes. This value is typically recorded by the builder.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:buildId",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A buildId is a locally unique identifier to identify a unique instance of a build. This identifier differs based on build toolchain, platform, or naming convention used by an organization or standard.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:buildStartTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "buildStartTime is the time at which a build is triggered. The builder typically records this value.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:buildType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A buildType is a URI expressing the toolchain, platform, or infrastructure that the build was invoked on. For example, if the build was invoked on GitHub's CI platform using github actions, the buildType can be expressed as `https://github.com/actions`. In contrast, if the build was invoked on a local machine, the buildType can be expressed as `file://username@host/path/to/build`.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:configSourceDigest",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "configSourceDigest is the checksum of the build configuration file used by a builder to execute a build. This Property uses the Core model's [Hash](../../Core/Classes/Hash.md) class.",
+ "rdfs:range": {
+ "@id": "core:Hash"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:configSourceEntrypoint",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A build entrypoint is the invoked executable of a build which always runs when the build is triggered. For example, when a build is triggered by running a shell script, the entrypoint is `script.sh`. In terms of a declared build, the entrypoint is the position in a configuration file or a build declaration which is always run when the build is triggered. For example, in the following configuration file, the entrypoint of the build is `publish`.\n\n```\nname: Publish packages to PyPI\n\non:\ncreate:\ntags: \"*\"\n\njobs:\npublish:\nruns-on: ubuntu-latest\nif: startsWith(github.ref, 'refs/tags/')\nsteps:\n\n...\n```",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:configSourceUri",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "If a build configuration exists for the toolchain or platform performing the build, the configSourceUri of a build is the URI of that build configuration. For example, a build triggered by a GitHub action is defined by a build configuration YAML file. In this case, the configSourceUri is the URL of that YAML file. \nm",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:environment",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "environment is a map of environment variables and values that are set during a build session. This is different from the [parameters](parameters.md) property in that it describes the environment variables set before a build is invoked rather than the variables provided to the builder.",
+ "rdfs:range": {
+ "@id": "core:DictionaryEntry"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "build:parameters",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "parameters is a key-value map of all build parameters and their values that were provided to the builder for a build instance. This is different from the [environment](environment.md) property in that the keys and values are provided as command line arguments or a configuration file to the builder.",
+ "rdfs:range": {
+ "@id": "core:DictionaryEntry"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:Artifact",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An artifact is a distinct article or unit within the digital domain,\nsuch as an electronic file, a software package, a device or an element of data.",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "validUntilTime",
+ "sh:path": {
+ "@id": "core:validUntilTime"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Agent"
+ },
+ "sh:name": "originatedBy",
+ "sh:path": {
+ "@id": "core:originatedBy"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Agent"
+ },
+ "sh:name": "suppliedBy",
+ "sh:path": {
+ "@id": "core:suppliedBy"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "builtTime",
+ "sh:path": {
+ "@id": "core:builtTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "releaseTime",
+ "sh:path": {
+ "@id": "core:releaseTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "standard",
+ "sh:path": {
+ "@id": "core:standard"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:Bom",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A Bill Of Materials (BOM) is a container for a grouping of SPDX-3.0 content\ncharacterizing details about a product.\nThis could include details of the content and composition of the product,\nprovenence details of the product and/or\nits composition, licensing information, known quality or security issues, etc.",
+ "rdfs:subClassOf": {
+ "@id": "core:Bundle"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:ElementCollection",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An SpdxCollection is a collection of Elements, not necessarily with unifying context.",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:ExternalMap"
+ },
+ "sh:name": "imports",
+ "sh:path": {
+ "@id": "core:imports"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Element"
+ },
+ "sh:minCount": 1,
+ "sh:name": "rootElement",
+ "sh:path": {
+ "@id": "core:rootElement"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:NamespaceMap"
+ },
+ "sh:name": "namespaces",
+ "sh:path": {
+ "@id": "core:namespaces"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Element"
+ },
+ "sh:minCount": 1,
+ "sh:name": "element",
+ "sh:path": {
+ "@id": "core:element"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:LifecycleScopedRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "TODO",
+ "rdfs:subClassOf": {
+ "@id": "core:Relationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:class": {
+ "@id": "core:LifecycleScopeType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "scope",
+ "sh:path": {
+ "@id": "core:scope"
+ }
+ }
+ },
+ {
+ "@id": "core:algorithm",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An algorithm specifies the algorithm that was used for calculating the hash value.",
+ "rdfs:range": {
+ "@id": "core:HashAlgorithm"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:annotationType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An annotationType describes the type of an annotation.",
+ "rdfs:range": {
+ "@id": "core:AnnotationType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:begin",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "begin is a positive integer that defines the beginning of a range.",
+ "rdfs:range": {
+ "@id": "xsd:positiveInteger"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:builtTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A builtTime specifies the time an artifact was built.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:completeness",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Completeness gives information about whether the provided relationships are\ncomplete, known to be incomplete or if no assertion is made either way.",
+ "rdfs:range": {
+ "@id": "core:RelationshipCompleteness"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:context",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A context gives information about the circumstances or unifying properties\nthat Elements of the bundle have been assembled under.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:created",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Created is a date that identifies when the Element was originally created.\nThe time stamp can serve as an indication as to whether the analysis needs to be updated. This is often the date of last change (e.g., a git commit date), not the date when the SPDX data was created, as doing so supports reproducible builds.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:createdBy",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "CreatedBy identifies who or what created the Element.\nThe generation method will assist the recipient of the Element in assessing\nthe general reliability/accuracy of the analysis information.",
+ "rdfs:range": {
+ "@id": "core:Agent"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:createdUsing",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "CreatedUsing identifies the tooling that was used during the creation of the Element.\nThe generation method will assist the recipient of the Element in assessing\nthe general reliability/accuracy of the analysis information.",
+ "rdfs:range": {
+ "@id": "core:Tool"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:dataLicense",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "The data license provides the license under which the SPDX documentation of the Element can be used.\nThis is to alleviate any concern that content (the data or database) in an SPDX file\nis subject to any form of intellectual property right that could restrict the re-use\nof the information or the creation of another SPDX file for the same project(s).\nThis approach avoids intellectual property and related restrictions over the SPDX file,\nhowever individuals can still contract with each other to restrict release\nof specific collections of SPDX files (which map to software bill of materials)\nand the identification of the supplier of SPDX files.\nCompliance with this document includes populating the SPDX fields therein\nwith data related to such fields (\"SPDX-Metadata\"). \nThis document contains numerous fields where an SPDX file creator may provide\nrelevant explanatory text in SPDX-Metadata. Without opining on the lawfulness\nof \"database rights\" (in jurisdictions where applicable),\nsuch explanatory text is copyrightable subject matter in most Berne Convention countries.\nBy using the SPDX specification, or any portion hereof,\nyou hereby agree that any copyright rights (as determined by your jurisdiction)\nin any SPDX-Metadata, including without limitation explanatory text,\nshall be subject to the terms of the Creative Commons CC0 1.0 Universal license. \nFor SPDX-Metadata not containing any copyright rights, \nyou hereby agree and acknowledge that the SPDX-Metadata is provided to you “as-is”\nand without any representations or warranties of any kind concerning the SPDX-Metadata,\nexpress, implied, statutory or otherwise, including without limitation warranties\nof title, merchantability, fitness for a particular purpose, non-infringement,\nor the absence of latent or other defects, accuracy, or the presence or absence of errors,\nwhether or not discoverable, all to the greatest extent permissible under applicable law.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:definingDocument",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A definingDocument property is used to link an Element identifier to an SpdxDocument which contains the definition for the Element.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:description",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field is a detailed description of the Element. It may also be extracted from the Element itself.\nThe intent is to provide recipients of the SPDX file with a detailed technical explanation\nof the functionality, anticipated use, and anticipated implementation of the Element.\nThis field may also include a description of improvements over prior versions of the Element.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:element",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "This field refers to one or more Elements that are part of an ElementCollection.",
+ "rdfs:range": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:end",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "end is a positive integer that defines the end of a range.",
+ "rdfs:range": {
+ "@id": "xsd:positiveInteger"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:endTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A endTime specifies the time from which element is no applicable / valid.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:extension",
+ "rdfs:comment": "TODO",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:externalId",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "ExternalId identifies an external Element used within a Document but defined external to that Document.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:externalIdentifier",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "ExternalIdentifier points to a resource outside the scope of SPDX-3.0 content\nthat uniquely identifies an Element.",
+ "rdfs:range": {
+ "@id": "core:ExternalIdentifier"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:externalIdentifierType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An externalIdentifierType specifies the type of the external identifier.",
+ "rdfs:range": {
+ "@id": "core:ExternalIdentifierType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:externalReference",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "This field points to a resource outside the scope of the SPDX-3.0 content\nthat provides additional characteristics of an Element.",
+ "rdfs:range": {
+ "@id": "core:ExternalReference"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:externalReferenceType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An externalReferenceType specifies the type of the external reference.",
+ "rdfs:range": {
+ "@id": "core:ExternalReferenceType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:from",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "This field references the Element on the left-hand side of a relationship.",
+ "rdfs:range": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:hashValue",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "HashValue is the result of applying a hash algorithm to an Element.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:identifier",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An identifier uniquely identifies an external element.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:identifierLocator",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A identifierLocator is TODO",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:issuingAuthority",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A issuingAuthority is TODO",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:key",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A key used in generic a key-value pair.\nA key-value pair can be used to implement a dictionary which associates a key with a value.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:locationHint",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A locationHint provides an indication of where to retrieve an external Element.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:locator",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A locator provides the location of an external reference.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:namespace",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A namespace provides an unambiguous mechanism for other documents to reference Elements within this document.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:originatedBy",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "OriginatedBy identifies from where or whom the Element originally came.",
+ "rdfs:range": {
+ "@id": "core:Agent"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:prefix",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A prefix is a substitute for a URI.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:profile",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field provides information about which profiles the Element belongs to.",
+ "rdfs:range": {
+ "@id": "core:ProfileIdentifierType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:relationshipType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field provides information about the relationship between two Elements.\nFor example, you can represent a relationship between two different Files,\nbetween a Package and a File, between two Packages, or between one SPDXDocument and another SPDXDocument.",
+ "rdfs:range": {
+ "@id": "core:RelationshipType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:releaseTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A releaseTime specifies the time an artifact was released.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:rootElement",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A rootElement of a collection is the top level Element from which all other Elements are reached via relationships.",
+ "rdfs:range": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:scope",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A scope is TODO",
+ "rdfs:range": {
+ "@id": "core:LifecycleScopeType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:specVersion",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "The specVersion provides a reference number that can be used to understand how to parse and interpret an Element.\nIt will enable both future changes to the specification and to support backward compatibility.\nThe major version number shall be incremented when incompatible changes between versions are made\n(one or more sections are created, modified or deleted).\nThe minor version number shall be incremented when backwards compatible changes are made.\n\nHere, parties exchanging information in accordance with the SPDX specification need to provide \n100% transparency as to which SPDX specification version such information is conforming to.",
+ "rdfs:range": {
+ "@id": "core:SemVer"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:standard",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Various standards may be relevant to useful to capture for specific artifacts.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:startTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A startTime specifies the time from which element is applicable / valid.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:statement",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A statement is a commentary on an assertion that an annotator has made.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:subject",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A subject is an Element an annotator has made an assertion about.",
+ "rdfs:range": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:summary",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A summary is a short description of an Element. Here, the intent is to allow the Element creator to \nprovide concise information about the function or use of the Element.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:suppliedBy",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "Identify the actual distribution source for the Artifact being referenced.\nThis might or might not be different from the originating distribution source for the artifact.",
+ "rdfs:range": {
+ "@id": "core:Agent"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:to",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "This field references an Element on the right-hand side of a relationship.",
+ "rdfs:range": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:validUntilTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A validUntilTime specifies until when the artifact can be used before its usage needs to be reassessed.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:value",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A value used in a generic key-value pair.\nA key-value pair can be used to implement a dictionary which associates a key with a value.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:anonymizationMethodUsed",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "AnonymizationMethodUsed describes the methods used to anonymize the dataset (of fields in the dataset).",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:confidentialityLevel",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "ConfidentialityLevel describes the levels of confidentiality of the data points contained in the dataset.",
+ "rdfs:range": {
+ "@id": "dataset:ConfidentialityLevelType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:dataCollectionProcess",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "DataCollectionProcess describes how a dataset was collected.\nExamples include the sources from which a dataset was scrapped or\nthe interview protocol that was used for data collection.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:dataPreprocessing",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "DataPreprocessing describes the various preprocessing steps\nthat were applied to the raw data to create the dataset.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:datasetAvailability",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Some datasets are publicly available and can be downloaded directly. Others are only accessible behind a clickthrough, or after filling a registration form. This field will describe the dataset availability from that perspective.",
+ "rdfs:range": {
+ "@id": "dataset:DatasetAvailabilityType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:datasetNoise",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "DatasetNoise describes what kinds of noises a dataset might encompass.\nThe field uses free form text to specify the fields or the samples that might be noisy.\nAlternatively, it can also be used to describe various noises that could impact the whole dataset.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:datasetSize",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "DatasetSize Captures how large a dataset is.\nThe size is to be measured in bytes.",
+ "rdfs:range": {
+ "@id": "xsd:nonNegativeInteger"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:datasetType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Type describes the datatype contained in the dataset. For example a dataset can be a image dataset or a text dataset or sometimes a multimodal dataset that contains multiple types of data",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:datasetUpdateMechanism",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "DatasetUpdateMechanism describes a mechanism to update the dataset.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:intendedUse",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "IntendedUse describes what the given dataset should be used for.\nSome datasets are collected to be used only for particular purposes. \nFor example, medical data collected from a specific demography might only be applicable\nfor training machine learning models to make predictions for that demography.\nIn such a case, the intendedUse field would capture this information.\nSimilarly, if a dataset is collected for building a facial recognition model,\nthe intendedUse field would specify that.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:knownBias",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "KnownBias is a free form text field that describes the different biases that the dataset encompasses.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:sensitivePersonalInformation",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "SensitivePersonalInformation indicates the presence of sensitive personal data\nor information that allows drawing conclusions about a person's identity.",
+ "rdfs:range": {
+ "@id": "dataset:PresenceType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:sensor",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Sensor describes a sensor that was used for collecting the data\nand its calibration value as a key-value pair.",
+ "rdfs:range": {
+ "@id": "core:DictionaryEntry"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:additionComment",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An additionComment for a LicenseAddition describes general factual information\nabout the LicenseAddition. It should not contain information (or links to\ninformation) that includes any kind of interpretation about the meaning or\neffect of the License, even if written by the license addition's author.\n\nExamples of information for an additionComment may include the following:\n\n* If the LicenseAddition's identifier is deprecated, it may briefly explain the\n reason for deprecation.\n* It may include the date of release, if identified, for LicenseAdditions with\n multiple versions.\n* It may include links to other official language translations for the\n LicenseAddition.\n* It may include a reference to the License(s) with which this LicenseAddition\n is typically used.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:additionId",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An additionId contains a human-readable, short-form identifier for a\nLicenseAddition. It may only include letters, numbers, period (\".\") and\nhyphen (\"-\") characters.\n\nFor a ListedLicenseException, the licenseId will be as specified on the\n[SPDX Exceptions List](https://spdx.org/licenses/exceptions-index.html) for the\nparticular exception.\n\nFor a CustomLicenseAddition, the short-form identifier must begin with the\nprefix `AdditionRef-` and must be unique within the applicable SPDX namespace.\nThe short-form identifier may be preceded by an SPDX namespace or a\nfully-qualified URI prefix.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:additionName",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An additionName contains the full name of a LicenseAddition, preferably using\nthe title found in the applicable license addition text or file, or as\notherwise specified by the LicenseAddition's author or steward.\n\nWhen no such title is specified, using a name from another well-known source or list\nof licenses additions (such as OSI or Fedora) is suggested.\n\nIf no official or common name is known, any name may be used to aid in\ndistinguishing the LicenseAddition from other LicenseAdditions.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:additionText",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An additionText contains the plain text of the LicenseAddition, without\ntemplating or other similar markup.\n\nUsers of the additionText for a License can apply the SPDX Matching Guidelines\nwhen comparing it to another text for matching purposes.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:isDeprecatedAdditionId",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "The isDeprecatedAdditionId property specifies whether an identifier for a\nLicenseAddition has been marked as deprecated. If the property is not defined,\nthen it is presumed to be false (i.e., not deprecated).\n\nIf the LicenseAddition is included on the SPDX Exceptions List, then\nthe `deprecatedVersion` property indicates on which version release of the\nExceptions List it was first marked as deprecated.\n\n\"Deprecated\" in this context refers to deprecating the use of the\n_identifier_, not the underlying license addition. In other words, even if a\nLicenseAddition's author or steward has stated that a particular\nLicenseAddition generally should not be used, that would _not_ mean that the\nLicenseAddition's identifier is \"deprecated.\" Rather, a LicenseAddition\noperator is typically marked as \"deprecated\" when it is determined that use of\nanother identifier is preferable.",
+ "rdfs:range": {
+ "@id": "xsd:boolean"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:isDeprecatedLicenseId",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "The isDeprecatedLicenseId property specifies whether an identifier for a\nLicense or LicenseAddition has been marked as deprecated. If the property\nis not defined, then it is presumed to be false (i.e., not deprecated).\n\nIf the License or LicenseAddition is included on the SPDX License List, then\nthe `deprecatedVersion` property indicates on which version release of the\nLicense List it was first marked as deprecated.\n\n\"Deprecated\" in this context refers to deprecating the use of the\n_identifier_, not the underlying license. In other words, even if a License's\nauthor or steward has stated that a particular License generally should not be\nused, that would _not_ mean that the License's identifier is \"deprecated.\"\nRather, a License or LicenseAddition operator is typically marked as\n\"deprecated\" when it is determined that use of another identifier is\npreferable.",
+ "rdfs:range": {
+ "@id": "xsd:boolean"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:isFsfLibre",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "isFsfLibre specifies whether the [Free Software Foundation FSF](https://fsf.org)\nhas listed this License as \"free\" in their commentary on licenses, located at\nthe time of this writing at https://www.gnu.org/licenses/license-list.en.html.\n\nA value of \"true\" indicates that the FSF has listed this License as _free_.\n\nA value of \"false\" indicates that the FSF has listed this License as _not free_.\n\nIf the isFsfLibre field is not specified, the SPDX data creator makes no\nassertions about whether the License is listed in the FSF's commentary.",
+ "rdfs:range": {
+ "@id": "xsd:boolean"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:isOsiApproved",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "isOsiApproved specifies whether the [Open Source Initiative (OSI)](https://opensource.org)\nhas listed this License as \"approved\" in their list of OSI Approved Licenses,\nlocated at the time of this writing at https://opensource.org/licenses/.\n\nA value of \"true\" indicates that the OSI has listed this License as approved.\n\nA value of \"false\" indicates that the OSI has not listed this License as\napproved.\n\nIf the isOsiApproved field is not specified, the SPDX data creator makes no\nassertions about whether the License is approved by the OSI.",
+ "rdfs:range": {
+ "@id": "xsd:boolean"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:licenseComment",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A licenseComment describes general factual information about the License. It\nshould not contain information (or links to information) that includes any kind\nof interpretation about the meaning or effect of the License, even if written\nby the license's author.\n\nExamples of information for a licenseComment may include the following:\n\n* If the License's identifier is deprecated, it may briefly explain the reason\n for deprecation.\n* It may include the date of release, if identified, for Licenses with multiple\n versions.\n* It may include links to other official language translations for the License.\n* For LicenseAdditions, it may include a reference to the License(s) with\n which this additional text is typically used.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:licenseId",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A licenseId contains a human-readable, short-form license identifier for a\nLicense. It may only include letters, numbers, period (\".\") and hyphen (\"-\")\ncharacters.\n\nFor a ListedLicense, the licenseId will be as specified on the\n[SPDX License List](https://spdx.org/licenses) for the particular license.\n\nFor a CustomLicense, the short-form license identifer must begin with the\nprefix `LicenseRef-` and must be unique within the applicable SPDX namespace.\nThe short-form license ID may be preceded by an SPDX namespace or a\nfully-qualified URI prefix.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:licenseName",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A licenseName contains the full name of a License, preferably using the title found\nin the applicable license text or file, or as otherwise specified by the\nLicense's author or steward.\n\nWhen no such title is specified, using a name from another well-known source or list\nof licenses (such as OSI or Fedora) is suggested.\n\nIf no official or common name is known, any name may be used to aid in\ndistinguishing the License from other Licenses.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:licenseText",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A licenseText contains the plain text of the License, without templating\nor other similar markup.\n\nUsers of the licenseText for a License can apply the SPDX Matching Guidelines\nwhen comparing it to another text for matching purposes.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:standardAdditionTemplate",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A standardAdditionTemplate contains a license addition template which describes\nsections of the LicenseAddition text which can be varied. See the Legacy Text\nTemplate format section of the SPDX specification for format information.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:standardLicenseHeader",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A standardLicenseHeader contains the plain text of the License author's\npreferred wording to be used, typically in a source code file's header\ncomments or similar location, to indicate that the file is subject to\nthe specified License.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:standardLicenseTemplate",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A standardLicenseTemplate contains a license template which describes\nsections of the License text which can be varied. See the Legacy Text Template\nformat section of the SPDX specification for format information.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:subjectAddition",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A subjectAddition is a LicenseAddition which is subject to a 'with additional\ntext' effect (WithAdditionOperator).",
+ "rdfs:range": {
+ "@id": "licensing:LicenseAddition"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:actionStatement",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "When an element is referenced with a VexAffectedVulnAssessmentRelationship,\nthe relationship MUST include one actionStatement that SHOULD describe actions\nto remediate or mitigate the vulnerability.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:actionStatementTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "TODO",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:assessedElement",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "Specifies subpackages, files or snippets referenced by a security assessment\nto specify the precise location where a vulnerability was found.",
+ "rdfs:range": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:catalogType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A catalogType is a mandatory value and must select one of the two entries in the `ExploitCatalogType.md` vocabulary.",
+ "rdfs:range": {
+ "@id": "security:ExploitCatalogType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:decisionType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A decisionType is a mandatory value and must select one of the four entries in the `SsvcDecisionType.md` vocabulary.",
+ "rdfs:range": {
+ "@id": "security:SsvcDecisionType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:exploited",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field is set when a CVE is listed in an exploit catalog.",
+ "rdfs:range": {
+ "@id": "xsd:boolean"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:impactStatement",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "When a VEX product element is related with a VexNotAffectedVulnAssessmentRelationship\nand a machine readable justification label is not provided, then an impactStatement\nthat further explains how or why the prouct(s) are not affected by the vulnerability\nmust be provided.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:impactStatementTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "TODO",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:justificationType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "When stating that an element is not affected by a vulnerability, the\nVexNotAffectedVulnAssessmentRelationship must include a justification from the\nmachine-readable labels catalog informing the reason the element is not impacted.\n\nimpactStatement which is a string with English prose can be used instead or as\ncomplementary to the justification label, but one of both MUST be defined.",
+ "rdfs:range": {
+ "@id": "security:VexJustificationType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:locator",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A locator provides the location of an exploit catalog.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:probability",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "The probability score between 0 and 1 (0 and 100%) estimating the likelihood\nthat a vulnerability will be exploited in the next 12 months.",
+ "rdfs:range": {
+ "@id": "xsd:decimal"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:statusNotes",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "TODO",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:suppliedBy",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "Identify the actual distribution source for the vulnerability assessment relationship being referenced.",
+ "rdfs:range": {
+ "@id": "core:Agent"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:vexVersion",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "TODO",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:additionalPurpose",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Additional purpose provides information about the additional purposes of the software artifact in addition to the primaryPurpose.",
+ "rdfs:range": {
+ "@id": "software:SoftwarePurpose"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:attributionText",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An attributionText for a software Package, File or Snippet provides a consumer\nof SPDX data with acknowledgement content, to assist redistributors of the\nPackage, File or Snippet with reproducing those acknowledgements.\n\nFor example, this field may include a statement that is required by a\nparticular license to be reproduced in end-user documentation, advertising\nmaterials, or another form.\n\nThis field may describe where, or in which contexts, the acknowledgements\nneed to be reproduced, but it is not required to do so. The SPDX data creator\nmay also explain elsewhere (such as in a licenseComment field) how they intend\nfor data in this field to be used.\n\nAn attributionText is is not meant to include the software Package, File or\nSnippet’s actual complete license text (see concludedLicense to identify the\ncorresponding license).",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:byteRange",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field defines the byte range in the original host file that the snippet information applies to.\nA range of bytes is independent of various formatting concerns, and the most accurate way \nof referring to the differences. The choice was made to start the numbering of \nthe byte range at 1 to be consistent with the W3C pointer method vocabulary.",
+ "rdfs:range": {
+ "@id": "core:PositiveIntegerRange"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:concludedLicense",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A concludedLicense is the license identified by the SPDX data creator,\nbased on analyzing the license information in the software Package, File\nor Snippet and other information to arrive at a reasonably objective\nconclusion as to what license governs it.\n\nIf a concludedLicense has a NONE value (NoneLicense), this indicates that the\nSPDX data creator has looked and did not find any license information for this\nsoftware Package, File or Snippet.\n\nIf a concludedLicense has a NOASSERTION value (NoAssertionLicense), this\nindicates that one of the following applies:\n* the SPDX data creator has attempted to but cannot reach a reasonable\n objective determination;\n* the SPDX data creator has made no attempt to determine this field; or\n* the SPDX data creator has intentionally provided no information (no\n meaning should be implied by doing so).\n\nA written explanation of a NOASSERTION value (NoAssertionLicense) MAY be\nprovided in the licenseComment field.\n\nIf the concludedLicense for a software Package, File or Snippet is not the\nsame as its declaredLicense, a written explanation SHOULD be provided in\nthe licenseComment field.\n\nIf the declaredLicense for a software Package, File or Snippet is a choice\nof more than one license (e.g. a license expression combining two licenses\nthrough use of the `OR` operator), then the concludedLicense may either\nretain the license choice or identify which license was chosen.",
+ "rdfs:range": {
+ "@id": "licensing:LicenseField"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:conditionality",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A conditionality is TODO",
+ "rdfs:range": {
+ "@id": "software:DependencyConditionalityType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:contentIdentifier",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A contentIdentifier is TODO",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:contentType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field is a reasonable estimation of the content type of the Element, from a creator perspective.\nContent type is intrinsic to the Element, independent of how the Element is being used.",
+ "rdfs:range": {
+ "@id": "core:MediaType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:copyrightText",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A copyrightText consists of the text(s) of the copyright notice(s) found\nfor a software Package, File or Snippet, if any.\n\nIf a copyrightText contains text, then it may contain any text related to\none or more copyright notices (even if not complete) for that software\nPackage, File or Snippet.\n\nIf a copyrightText has a \"NONE\" value, this indicates that the software\nPackage, File or Snippet contains no copyright notice whatsoever.\n\nIf a copyrightText has a \"NOASSERTION\" value, this indicates that one of the\nfollowing applies:\n* the SPDX data creator has attempted to but cannot reach a reasonable\n objective determination;\n* the SPDX data creator has made no attempt to determine this field; or\n* the SPDX data creator has intentionally provided no information (no\n meaning should be implied by doing so).",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:declaredLicense",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A declaredLicense is the license identified in text in the software package,\nfile or snippet as the license declared by its authors.\n\nThis field is not intended to capture license information obtained from an\nexternal source, such as a package's website. Such information can be\nincluded, as needed, in a concludedLicense field.\n\nA declaredLicense may be expressed differently in practice for different\ntypes of artifacts. For example:\n\n* for Packages:\n * would include license info describing the license of the Package as a\n whole, when it is found in the Package itself (e.g., LICENSE file,\n README file, metadata in the repository, etc.)\n * would not include any license information that is not in the Package\n itself (e.g., license information from the project’s website or from a\n third party repository or website)\n* for Files:\n * would include license info found in the File itself (e.g., license\n header or notice, comments, SPDX-License-Identifier expression)\n * would not include license info found in a different file (e.g., LICENSE\n file in the top directory of a repository)\n* for Snippets:\n * would include license info found in the Snippet itself (e.g., license\n notice, comments, SPDX-License-Identifier expression)\n * would not include license info found elsewhere in the File or in a\n different File (e.g., comment at top of File if it is not within the\n Snippet, LICENSE file in the top directory of a repository)\n\nIf a declaredLicense has a NONE value (NoneLicense), this indicates that the\ncorresponding Package, File or Snippet contains no license information\nwhatsoever.\n\nIf a declaredLicense has a NOASSERTION value (NoAssertionLicense), this\nindicates that one of the following applies:\n* the SPDX data creator has attempted to but cannot reach a reasonable\n objective determination;\n* the SPDX data creator has made no attempt to determine this field; or\n* the SPDX data creator has intentionally provided no information (no meaning\n should be implied by doing so).",
+ "rdfs:range": {
+ "@id": "licensing:LicenseField"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:downloadLocation",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "DownloadLocation identifies the download Uniform Resource Identifier \nfor the package at the time that the document was created.\nWhere and how to download the exact package being referenced \nis critical for verification and tracking data.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:homePage",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "HomePage is a place for the SPDX document creator to record a website that serves as the package's home page.\nThis saves the recipient of the SPDX document who is looking for more info from\nhaving to search for and verify a match between the package and the associated project home page.\nThis link can also be used to reference further information about the package\nreferenced by the SPDX document creator.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:lineRange",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field defines the line range in the original host file that the snippet information applies to.\nIf there is a disagreement between the byte range and line range, the byte range values will take precedence.\nA range of lines is a convenient reference for those files where there is a known line delimiter. \nThe choice was made to start the numbering of the lines at 1 to be consistent with the W3C pointer method vocabulary.",
+ "rdfs:range": {
+ "@id": "core:PositiveIntegerRange"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:packageUrl",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A packageUrl is TODO",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:packageVersion",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A packageVersion is useful for identification purposes and for indicating later changes of the package version.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:primaryPurpose",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "primaryPurpose provides information about the primary purpose of the software artifact.",
+ "rdfs:range": {
+ "@id": "software:SoftwarePurpose"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:sbomType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field is a reasonable estimation of the type of SBOM created from a creator perspective.\nIt is intended to be used to give guidance on the elements that may be contained within it.\nAligning with the guidance produced in [Types of Software Bill of Material (SBOM) Documents](https://www.cisa.gov/sites/default/files/2023-04/sbom-types-document-508c.pdf).",
+ "rdfs:range": {
+ "@id": "software:SBOMType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:softwareLinkage",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A softwareLinkage is TODO",
+ "rdfs:range": {
+ "@id": "software:SoftwareDependencyLinkType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:sourceInfo",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "SourceInfo records any relevant background information or additional comments\nabout the origin of the package. For example, this field might include comments \nindicating whether the package was pulled from a source code management system \nor has been repackaged. The creator can provide additional information to describe\nany anomalies or discoveries in the determination of the origin of the package.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:Bundle",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A bundle is a collection of Elements that have a shared context.",
+ "rdfs:subClassOf": {
+ "@id": "core:ElementCollection"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "context",
+ "sh:path": {
+ "@id": "core:context"
+ }
+ }
+ },
+ {
+ "@id": "core:ExternalIdentifier",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An ExternalIdentifier is a reference to a resource outside the scope of SPDX-3.0 content\nthat uniquely identifies an Element.",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "identifier",
+ "sh:path": {
+ "@id": "core:identifier"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "comment",
+ "sh:path": {
+ "@id": "core:comment"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:name": "identifierLocator",
+ "sh:path": {
+ "@id": "core:identifierLocator"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "issuingAuthority",
+ "sh:path": {
+ "@id": "core:issuingAuthority"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:ExternalIdentifierType"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "externalIdentifierType",
+ "sh:path": {
+ "@id": "core:externalIdentifierType"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:ExternalReference",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An External Reference points to a resource outside the scope of the SPDX-3.0 content\nthat provides additional characteristics of an Element.",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:ExternalReferenceType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "externalReferenceType",
+ "sh:path": {
+ "@id": "core:externalReferenceType"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:name": "locator",
+ "sh:path": {
+ "@id": "core:locator"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "comment",
+ "sh:path": {
+ "@id": "core:comment"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:MediaType"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "contentType",
+ "sh:path": {
+ "@id": "core:contentType"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:Hash",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A hash is a grouping of characteristics unique to the result\nof applying a mathematical algorithm\nthat maps data of arbitrary size to a bit string (the hash)\nand is a one-way function, that is,\na function which is practically infeasible to invert.\nThis is commonly used for integrity checking of data.",
+ "rdfs:subClassOf": {
+ "@id": "core:IntegrityMethod"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:HashAlgorithm"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "algorithm",
+ "sh:path": {
+ "@id": "core:algorithm"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "hashValue",
+ "sh:path": {
+ "@id": "core:hashValue"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:Payload",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "TODO",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:NamespaceMap"
+ },
+ "sh:name": "namespaces",
+ "sh:path": {
+ "@id": "core:namespaces"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:ExternalMap"
+ },
+ "sh:name": "imports",
+ "sh:path": {
+ "@id": "core:imports"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:CreationInfo"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "creationInfo",
+ "sh:path": {
+ "@id": "core:creationInfo"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:Relationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A Relationship is a grouping of characteristics unique to an assertion\nthat one Element is related to one or more other Elements in some way.",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "endTime",
+ "sh:path": {
+ "@id": "core:endTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "startTime",
+ "sh:path": {
+ "@id": "core:startTime"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Element"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "from",
+ "sh:path": {
+ "@id": "core:from"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Element"
+ },
+ "sh:name": "to",
+ "sh:path": {
+ "@id": "core:to"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:RelationshipCompleteness"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "completeness",
+ "sh:path": {
+ "@id": "core:completeness"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:RelationshipType"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "relationshipType",
+ "sh:path": {
+ "@id": "core:relationshipType"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:SemVer",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "The semantic version is a string\nthat is following the specification of [Semantic Versioning 2.0.0](https://semver.org/).\nFormat restriction: pattern: ^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$",
+ "rdfs:subClassOf": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:Tool",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A Tool is an element of hardware and/or software utilized to carry out a particular function.",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:contentType",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "ContentType specifies the media type of an Element.",
+ "rdfs:range": {
+ "@id": "core:MediaType"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:name",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field identifies the name of an Element as designated by the creator. \nThe name of an Element is an important convention and easier to refer to than the URI.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:verifiedUsing",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "VerifiedUsing provides an IntegrityMethod with which the integrity of an Element can be asserted.",
+ "rdfs:range": {
+ "@id": "core:IntegrityMethod"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:deprecatedVersion",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A deprecatedVersion for a ListedLicense or ListedLicenseException on the SPDX\nLicense List specifies which version release of the License List was the first\none in which it was marked as deprecated.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:listVersionAdded",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A listVersionAdded for a ListedLicense or ListedLicenseException on the SPDX\nLicense List specifies which version release of the License List was the first\none in which it was included.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:member",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A member is a license expression participating in a conjuctive (of type\nConjunctiveLicenseSet) or a disjunctive (of type DisjunctiveLicenseSet)\nlicense set.",
+ "rdfs:range": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:obsoletedBy",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "An obsoletedBy value for a deprecated License or LicenseAddition specifies\nthe licenseId of the replacement License or LicenseAddition that is preferred\nto be used in its place. It should use the same format as specified for a\nlicenseId.\n\nThe License's or LicenseAddition's comment value may include more information\nabout the reason why the licenseId specified in the obsoletedBy value is\npreferred.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:seeAlso",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A seeAlso defines a cross-reference with a URL where the License or\nLicenseAddition can be found in use by one or a few projects.\n\nIf applicable, it should include a URL where the license text is posted by\nthe license steward, particularly if the license steward has made available a\n\"canonical\" primary URL for the license text.\n\nIf the license is OSI approved, a seeAlso should be included with the URL for\nthe license's listing on the OSI website.\n\nThe seeAlso URL may refer to a previously-available URL for the License or\nLicenseAddition which is no longer active.\n\nWhere applicable, the seeAlso URL should include the license text in its\nnative language. seeAlso URLs to English or other translations may be included\nwhere multiple, equivalent official translations exist.",
+ "rdfs:range": {
+ "@id": "xsd:anyURI"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:subjectLicense",
+ "@type": "owl:ObjectProperty",
+ "rdfs:comment": "A subjectLicense is a License which is subject to either an 'or later' effect\n(OrLaterOperator) or a 'with additional text' effect (WithAdditionOperator).",
+ "rdfs:range": {
+ "@id": "licensing:License"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:modifiedTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Specifies a time when a vulnerability assessment was last modified.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:publishedTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Specifies the time when a vulnerability was first published.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:score",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "The score provides information on the severity of a vulnerability per the\nCommon Vulnerability Scoring System as defined on [https://www.first.org/cvss](https://www.first.org/cvss/).",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:vector",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Sepcifies the vector string of a vulnerability, a string combining metrics\nfrom an assessment of its severity.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:withdrawnTime",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Specified the time and date when a vulnerability was withdrawn.",
+ "rdfs:range": {
+ "@id": "core:DateTime"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:Package",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A package refers to any unit of content that can be associated with a distribution of software.\nTypically, a package is composed of one or more files. \nAny of the following non-limiting examples may be (but are not required to be) represented in SPDX as a package:\n\n - a tarball, zip file or other archive\n - a directory or sub-directory\n - a separately distributed piece of software which another Package or File uses or depends upon (e.g., a Python package, a Go module, ...)\n - a container image, and/or each image layer within a container image\n - a collection of one or more sub-packages\n - a Git repository snapshot from a particular point in time\n\nNote that some of these could be represented in SPDX as a file as well.\nExternal property restriction on /Core/Element/name: minCount: 1",
+ "rdfs:subClassOf": {
+ "@id": "software:SoftwareArtifact"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "homePage",
+ "sh:path": {
+ "@id": "software:homePage"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "packageVersion",
+ "sh:path": {
+ "@id": "software:packageVersion"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "packageUrl",
+ "sh:path": {
+ "@id": "software:packageUrl"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "sourceInfo",
+ "sh:path": {
+ "@id": "software:sourceInfo"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "downloadLocation",
+ "sh:path": {
+ "@id": "software:downloadLocation"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:creationInfo",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "CreationInfo provides information about the creation of the Element.",
+ "rdfs:range": {
+ "@id": "core:CreationInfo"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:imports",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "Imports provides an ExternalMap of Element identifiers that are used within a document\nbut defined external to that document.",
+ "rdfs:range": {
+ "@id": "core:ExternalMap"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:namespaces",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "This field provides a NamespaceMap applicable to an ElementCollection.",
+ "rdfs:range": {
+ "@id": "core:NamespaceMap"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:severity",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "The severity field provides a human readable string, a label that can be used\nas an English adjective that qualifies its numerical score.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:SoftwareArtifact",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A software artifact is a distinct article or unit related to software\nsuch as a package, a file, or a snippet.",
+ "rdfs:subClassOf": {
+ "@id": "core:Artifact"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "contentIdentifier",
+ "sh:path": {
+ "@id": "software:contentIdentifier"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "attributionText",
+ "sh:path": {
+ "@id": "software:attributionText"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "licensing:LicenseField"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "declaredLicense",
+ "sh:path": {
+ "@id": "software:declaredLicense"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "copyrightText",
+ "sh:path": {
+ "@id": "software:copyrightText"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "software:SoftwarePurpose"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "primaryPurpose",
+ "sh:path": {
+ "@id": "software:primaryPurpose"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "licensing:LicenseField"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "concludedLicense",
+ "sh:path": {
+ "@id": "software:concludedLicense"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "software:SoftwarePurpose"
+ },
+ "sh:name": "additionalPurpose",
+ "sh:path": {
+ "@id": "software:additionalPurpose"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:AnnotationType",
+ "@type": "owl:Class",
+ "rdfs:comment": "AnnotationType specifies the type of an annotation.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:CreationInfo",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "The CreationInfo provides information about who created the Element, and when and how it was created. \n\nThe dateTime created is often the date of last change (e.g., a git commit date), not the date when the SPDX data was created, as doing so supports reproducible builds.",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "comment",
+ "sh:path": {
+ "@id": "core:comment"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Tool"
+ },
+ "sh:name": "createdUsing",
+ "sh:path": {
+ "@id": "core:createdUsing"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Agent"
+ },
+ "sh:minCount": 1,
+ "sh:name": "createdBy",
+ "sh:path": {
+ "@id": "core:createdBy"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:name": "created",
+ "sh:path": {
+ "@id": "core:created"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:SemVer"
+ },
+ "sh:name": "specVersion",
+ "sh:path": {
+ "@id": "core:specVersion"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:ProfileIdentifierType"
+ },
+ "sh:minCount": 1,
+ "sh:name": "profile",
+ "sh:path": {
+ "@id": "core:profile"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:name": "dataLicense",
+ "sh:path": {
+ "@id": "core:dataLicense"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:ExternalMap",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An External Map is a map of Element identifiers that are used within a Document\nbut defined external to that Document.\nThe external map provides details about the externally-defined Element\nsuch as its provenance, where to retrieve it, and how to verify its integrity.",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:class": {
+ "@id": "core:IntegrityMethod"
+ },
+ "sh:name": "verifiedUsing",
+ "sh:path": {
+ "@id": "core:verifiedUsing"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "locationHint",
+ "sh:path": {
+ "@id": "core:locationHint"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "definingDocument",
+ "sh:path": {
+ "@id": "core:definingDocument"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "externalId",
+ "sh:path": {
+ "@id": "core:externalId"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:IntegrityMethod",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An IntegrityMethod provides an independently reproducible mechanism that permits verification\nof a specific Element that correlates to the data in this SPDX document. This identifier enables\na recipient to determine if anything in the original Element has been changed and eliminates\nconfusion over which version or modification of a specific Element is referenced.",
+ "ns0:term_status": "Stable",
+ "sh:property": {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "comment",
+ "sh:path": {
+ "@id": "core:comment"
+ }
+ }
+ },
+ {
+ "@id": "core:NamespaceMap",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A namespace map allows the creator of a collection of Elements to use\nshorter identifiers (\"prefixes\") instead of URIs to provide a more\nhuman-readable and smaller serialized representation of the Elements.",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "prefix",
+ "sh:path": {
+ "@id": "core:prefix"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "namespace",
+ "sh:path": {
+ "@id": "core:namespace"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:PositiveIntegerRange",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "PositiveIntegerRange is a tuple of two positive integers that define a range.\n\"begin\" must be less than or equal to \"end\".",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:positiveInteger"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "begin",
+ "sh:path": {
+ "@id": "core:begin"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:positiveInteger"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "end",
+ "sh:path": {
+ "@id": "core:end"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "licensing:LicenseAddition",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A LicenseAddition represents text which is intended to be added to a License\nas additional text, but which is not itself intended to be a standalone\nLicense.\n\nIt may be an exception which is listed on the SPDX Exceptions List\n(ListedLicenseException), or may be any other additional text (as an exception\nor otherwise) which is defined by an SPDX data creator (CustomLicenseAddition).",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:boolean"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "isDeprecatedAdditionId",
+ "sh:path": {
+ "@id": "licensing:isDeprecatedAdditionId"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "additionText",
+ "sh:path": {
+ "@id": "licensing:additionText"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "additionId",
+ "sh:path": {
+ "@id": "licensing:additionId"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:name": "seeAlso",
+ "sh:path": {
+ "@id": "licensing:seeAlso"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "obsoletedBy",
+ "sh:path": {
+ "@id": "licensing:obsoletedBy"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "standardAdditionTemplate",
+ "sh:path": {
+ "@id": "licensing:standardAdditionTemplate"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "additionName",
+ "sh:path": {
+ "@id": "licensing:additionName"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "additionComment",
+ "sh:path": {
+ "@id": "licensing:additionComment"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "security:ExploitCatalogType",
+ "@type": "owl:Class",
+ "rdfs:comment": "ExploitCatalogType specifies the type of exploit catalog that a vulnerability is listed in.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:VexVulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "VexVulnAssessmentRelationship is an abstract subclass that defined the common\nproperties shared by all the SPDX-VEX status relationships. \n\n**Constraints**\n\nWhen linking elements using a VexVulnAssessmentRelationship, the following\nrequirements must be observed:\n\n- The from: end must be a /Security/Vulnerability classed element\n- The to: end must point to elements representing the VEX _products_. To\nspecify a different element where the vulnerability was detected, the VEX\nrelationship can optionally specify _subcomponents_ using the assessedElement\nproperty.\n\nVEX inherits information from the document level down to its statements. When a\nstatement is missing information it can be completed by reading the equivalent \nfield from the containing document. For example, if a VEX relationship is\nmissing data in its createdBy property, tools must consider the entity\nlisted in the CreationInfo section of the document as the VEX author.\nIn the same way, when a VEX relationship does not have a created property,\nthe document's date must be considered as authoritative.",
+ "rdfs:subClassOf": {
+ "@id": "security:VulnAssessmentRelationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "vexVersion",
+ "sh:path": {
+ "@id": "security:vexVersion"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "statusNotes",
+ "sh:path": {
+ "@id": "security:statusNotes"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:MediaType",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "The MediaType is a String constrained to the RFC 2046 specification. It provides a standardized\nway of indicating the type of content of an Element.\nA list of all possible media types is available at https://www.iana.org/assignments/media-types/media-types.xhtml.",
+ "rdfs:subClassOf": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:RelationshipCompleteness",
+ "@type": "owl:Class",
+ "rdfs:comment": "RelationshipCompleteness indicates whether a relationship is complete or \nknown to be incomplete or if there is made no assertion either way.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:comment",
+ "@type": "owl:DatatypeProperty",
+ "rdfs:comment": "A comment is an optional field for creators of the Element to provide comments\nto the readers/reviewers of the document.",
+ "rdfs:range": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:License",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A License represents a license text, whether listed on the SPDX License List\n(ListedLicense) or defined by an SPDX data creator (CustomLicense).",
+ "rdfs:subClassOf": {
+ "@id": "licensing:AnyLicenseInfo"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "obsoletedBy",
+ "sh:path": {
+ "@id": "licensing:obsoletedBy"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "standardLicenseTemplate",
+ "sh:path": {
+ "@id": "licensing:standardLicenseTemplate"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "licenseText",
+ "sh:path": {
+ "@id": "licensing:licenseText"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:boolean"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "isDeprecatedLicenseId",
+ "sh:path": {
+ "@id": "licensing:isDeprecatedLicenseId"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:boolean"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "isFsfLibre",
+ "sh:path": {
+ "@id": "licensing:isFsfLibre"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "standardLicenseHeader",
+ "sh:path": {
+ "@id": "licensing:standardLicenseHeader"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "licenseId",
+ "sh:path": {
+ "@id": "licensing:licenseId"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "licenseName",
+ "sh:path": {
+ "@id": "licensing:licenseName"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:boolean"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "isOsiApproved",
+ "sh:path": {
+ "@id": "licensing:isOsiApproved"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "licenseComment",
+ "sh:path": {
+ "@id": "licensing:licenseComment"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:anyURI"
+ },
+ "sh:name": "seeAlso",
+ "sh:path": {
+ "@id": "licensing:seeAlso"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "ai:SafetyRiskAssessmentType",
+ "@type": "owl:Class",
+ "rdfs:comment": "Lists the different safety risk type values that can be used to describe the safety risk of AI software\naccording to [Article 20 of Regulation 765/2008/EC](https://ec.europa.eu/docsroom/documents/17107/attachments/1/translations/en/renditions/pdf).",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:ConfidentialityLevelType",
+ "@type": "owl:Class",
+ "rdfs:comment": "Describes the different confidentiality levels as given by the [Traffic Light Protocol](https://en.wikipedia.org/wiki/Traffic_Light_Protocol).",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:SsvcDecisionType",
+ "@type": "owl:Class",
+ "rdfs:comment": "SsvcDecisionType specifies the type of decision that's been made according to the Stakeholder-Specific Vulnerability Categorization (SSVC) system [https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc](https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc)",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:VulnAssessmentRelationship",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "VulnAssessmentRelationship is the ancestor class common to all vulnerability\nassessment relationships. It factors out the common properties shared by them.\nExternal property restriction on /Core/Relationship/to: minCount: 1",
+ "rdfs:subClassOf": {
+ "@id": "core:Relationship"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "withdrawnTime",
+ "sh:path": {
+ "@id": "security:withdrawnTime"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Element"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "assessedElement",
+ "sh:path": {
+ "@id": "security:assessedElement"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:Agent"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "suppliedBy",
+ "sh:path": {
+ "@id": "security:suppliedBy"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "publishedTime",
+ "sh:path": {
+ "@id": "security:publishedTime"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:DateTime"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "modifiedTime",
+ "sh:path": {
+ "@id": "security:modifiedTime"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "software:SoftwareDependencyLinkType",
+ "@type": "owl:Class",
+ "rdfs:comment": "TODO",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "ai:PresenceType",
+ "@type": "owl:Class",
+ "rdfs:comment": "This type is used to indicate if a given field is present or absent or unknown.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "dataset:DatasetAvailabilityType",
+ "@type": "owl:Class",
+ "rdfs:comment": "Describes the possible types of availability of a dataset, indicating whether the dataset can be directly downloaded, can be assembled using a script for scraping the data, is only available after a clickthrough or a registration form.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:LicenseField",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A LicenseField is the primary value that is used by a licensing field for a\nsoftware Package, File or Snippet. It represents either a license expression,\nor the values NOASSERTION or NONE. The specific meanings of NOASSERTION or\nNONE for the particular licensing field are defined in the corresponding\nproperty description.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "security:VexJustificationType",
+ "@type": "owl:Class",
+ "rdfs:comment": "VexJustificationType specifies the type of Vulnerability Exploitability eXchange (VEX) justification.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:DependencyConditionalityType",
+ "@type": "owl:Class",
+ "rdfs:comment": "TODO",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:LifecycleScopeType",
+ "@type": "owl:Class",
+ "rdfs:comment": "TODO",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "licensing:AnyLicenseInfo",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An AnyLicenseInfo is used by a licensing field for a software package,\nfile or snippet when its value is not NOASSERTION or NONE. It can be a\nsingle license (either on the SPDX License List or a custom-defined license);\na single license with an \"or later\" operator applied; the foregoing with\nadditional text applied; or a set of licenses combined by applying \"AND\" and\n\"OR\" operators recursively.",
+ "rdfs:subClassOf": {
+ "@id": "licensing:LicenseField"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:SBOMType",
+ "@type": "owl:Class",
+ "rdfs:comment": "The set of SBOM types with definitions as defined in [Types of Software Bill of Material (SBOM) Documents](https://www.cisa.gov/sites/default/files/2023-04/sbom-types-document-508c.pdf), published on April 21, 2023. \nAn SBOM type describes the most likely type of an SBOM from the producer perspective, so that consumers can draw conclusions about the data inside an SBOM. A single SBOM can have multiple SBOM document types associated with it.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:Agent",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "The Agent class represents anything that has the potential to act on a system. This could be a person, organization, software agent, etc. This is not to be confused with tools that are used to perform tasks.",
+ "rdfs:subClassOf": {
+ "@id": "core:Element"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:ProfileIdentifierType",
+ "@type": "owl:Class",
+ "rdfs:comment": "There are a set of profiles that have been defined to be valid for a specific release This file enumerates the values that have been agreed on, and may be applied to the creation information for an an element.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:DictionaryEntry",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "The class used for implementing a generic string mapping (also known as associative array, dictionary, or hash map) in SPDX. Each DictionaryEntry contains a key-value pair which maps the key to its associated value. To implement a dictionary, this class is to be used in a collection with unique keys.",
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:minCount": 1,
+ "sh:name": "key",
+ "sh:path": {
+ "@id": "core:key"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "value",
+ "sh:path": {
+ "@id": "core:value"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:ExternalIdentifierType",
+ "@type": "owl:Class",
+ "rdfs:comment": "ExteralIdentifierType specifies the type of an external identifier.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:Element",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "An Element is a representation of a fundamental concept either directly inherent\nto the Bill of Materials (BOM) domain or indirectly related to the BOM domain\nand necessary for contextually characterizing BOM concepts and relationships.\nWithin SPDX-3.0 structure this is the base class acting as a consistent,\nunifying, and interoperable foundation for all explicit\nand inter-relatable content objects.",
+ "rdfs:subClassOf": {
+ "@id": "core:Payload"
+ },
+ "ns0:term_status": "Stable",
+ "sh:property": [
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "summary",
+ "sh:path": {
+ "@id": "core:summary"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "description",
+ "sh:path": {
+ "@id": "core:description"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "core:Extension"
+ },
+ "sh:name": "extension",
+ "sh:path": {
+ "@id": "core:extension"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:ExternalIdentifier"
+ },
+ "sh:name": "externalIdentifier",
+ "sh:path": {
+ "@id": "core:externalIdentifier"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "name",
+ "sh:path": {
+ "@id": "core:name"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:ExternalReference"
+ },
+ "sh:name": "externalReference",
+ "sh:path": {
+ "@id": "core:externalReference"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:IntegrityMethod"
+ },
+ "sh:name": "verifiedUsing",
+ "sh:path": {
+ "@id": "core:verifiedUsing"
+ }
+ },
+ {
+ "sh:class": {
+ "@id": "core:CreationInfo"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "creationInfo",
+ "sh:path": {
+ "@id": "core:creationInfo"
+ }
+ },
+ {
+ "sh:datatype": {
+ "@id": "xsd:string"
+ },
+ "sh:maxCount": 1,
+ "sh:name": "comment",
+ "sh:path": {
+ "@id": "core:comment"
+ }
+ }
+ ]
+ },
+ {
+ "@id": "core:ExternalReferenceType",
+ "@type": "owl:Class",
+ "rdfs:comment": "ExteralReferenceType specifies the type of an external reference.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:HashAlgorithm",
+ "@type": "owl:Class",
+ "rdfs:comment": "A HashAlgorithm is a mathematical algorithm that maps data of arbitrary size to a bit string (the hash)\nand is a one-way function, that is, a function which is practically infeasible to invert.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "software:SoftwarePurpose",
+ "@type": "owl:Class",
+ "rdfs:comment": "This field provides information about the primary purpose of an Element.\nSoftware Purpose is intrinsic to how the Element is being used rather than the content of the Element.\nThis field is a reasonable estimate of the most likely usage of the Element\nfrom the producer and consumer perspective from which both parties can draw conclusions\nabout the context in which the Element exists.",
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:DateTime",
+ "@type": [
+ "owl:Class",
+ "sh:NodeShape"
+ ],
+ "rdfs:comment": "A Datetime is a string representation of a specific date and time.\nIt has resolution of seconds and is always expressed in UTC timezone.\nThe specific format is one of the most commonly used ISO-8601 formats.\nFormat restriction: pattern: ^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\dZ$",
+ "rdfs:subClassOf": {
+ "@id": "xsd:string"
+ },
+ "ns0:term_status": "Stable"
+ },
+ {
+ "@id": "core:RelationshipType",
+ "@type": "owl:Class",
+ "rdfs:comment": "Provides information about the relationship between two Elements.\nFor example, you can represent a relationship between two different Files,\nbetween a Package and a File, between two Packages, or between one SPDXDocument and another SPDXDocument.\n\nBuild Profile specific RelationshipType descriptions can be found [here](https://github.com/spdx/spdx-3-build-profile/blob/main/model/relationships.md)",
+ "ns0:term_status": "Stable"
+ }
+ ]
+}
diff --git a/src/spdx_tools/spdx3/writer/json_ld/__init__.py b/src/spdx_tools/spdx3/writer/json_ld/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/spdx_tools/spdx3/writer/json_ld/context.json b/src/spdx_tools/spdx3/writer/json_ld/context.json
new file mode 100644
index 000000000..94cb2b5e6
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/json_ld/context.json
@@ -0,0 +1,742 @@
+{
+ "ai": "https://spdx.org/rdf/AI/",
+ "build": "https://spdx.org/rdf/Build/",
+ "core": "https://spdx.org/rdf/Core/",
+ "dataset": "https://spdx.org/rdf/Dataset/",
+ "licensing": "https://spdx.org/rdf/Licensing/",
+ "ns0": "http://www.w3.org/2003/06/sw-vocab-status/ns#",
+ "owl": "http://www.w3.org/2002/07/owl#",
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+ "security": "https://spdx.org/rdf/Security/",
+ "sh": "http://www.w3.org/ns/shacl#",
+ "software": "https://spdx.org/rdf/Software/",
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
+ "AIPackage": "ai:AIPackage",
+ "Build": "build:Build",
+ "Annotation": "core:Annotation",
+ "AnonymousPayload": "core:AnonymousPayload",
+ "Organization": "core:Organization",
+ "Person": "core:Person",
+ "SoftwareAgent": "core:SoftwareAgent",
+ "SpdxDocument": "core:SpdxDocument",
+ "Dataset": "dataset:Dataset",
+ "ConjunctiveLicenseSet": "licensing:ConjunctiveLicenseSet",
+ "CustomLicense": "licensing:CustomLicense",
+ "CustomLicenseAddition": "licensing:CustomLicenseAddition",
+ "DisjunctiveLicenseSet": "licensing:DisjunctiveLicenseSet",
+ "ListedLicense": "licensing:ListedLicense",
+ "ListedLicenseException": "licensing:ListedLicenseException",
+ "NoAssertionLicense": "licensing:NoAssertionLicense",
+ "NoneLicense": "licensing:NoneLicense",
+ "OrLaterOperator": "licensing:OrLaterOperator",
+ "WithAdditionOperator": "licensing:WithAdditionOperator",
+ "CvssV2VulnAssessmentRelationship": "security:CvssV2VulnAssessmentRelationship",
+ "CvssV3VulnAssessmentRelationship": "security:CvssV3VulnAssessmentRelationship",
+ "EpssVulnAssessmentRelationship": "security:EpssVulnAssessmentRelationship",
+ "ExploitCatalogVulnAssessmentRelationship": "security:ExploitCatalogVulnAssessmentRelationship",
+ "SsvcVulnAssessmentRelationship": "security:SsvcVulnAssessmentRelationship",
+ "VexAffectedVulnAssessmentRelationship": "security:VexAffectedVulnAssessmentRelationship",
+ "VexFixedVulnAssessmentRelationship": "security:VexFixedVulnAssessmentRelationship",
+ "VexNotAffectedVulnAssessmentRelationship": "security:VexNotAffectedVulnAssessmentRelationship",
+ "VexUnderInvestigationVulnAssessmentRelationship": "security:VexUnderInvestigationVulnAssessmentRelationship",
+ "Vulnerability": "security:Vulnerability",
+ "File": "software:File",
+ "Sbom": "software:Sbom",
+ "Snippet": "software:Snippet",
+ "SoftwareDependencyRelationship": "software:SoftwareDependencyRelationship",
+ "autonomyType": {
+ "@id": "ai:autonomyType",
+ "@type": "ai:PresenceType"
+ },
+ "domain": {
+ "@id": "ai:domain",
+ "@type": "xsd:string"
+ },
+ "energyConsumption": {
+ "@id": "ai:energyConsumption",
+ "@type": "xsd:string"
+ },
+ "hyperparameter": {
+ "@id": "ai:hyperparameter",
+ "@type": "core:DictionaryEntry"
+ },
+ "informationAboutApplication": {
+ "@id": "ai:informationAboutApplication",
+ "@type": "xsd:string"
+ },
+ "informationAboutTraining": {
+ "@id": "ai:informationAboutTraining",
+ "@type": "xsd:string"
+ },
+ "limitation": {
+ "@id": "ai:limitation",
+ "@type": "xsd:string"
+ },
+ "metric": {
+ "@id": "ai:metric",
+ "@type": "core:DictionaryEntry"
+ },
+ "metricDecisionThreshold": {
+ "@id": "ai:metricDecisionThreshold",
+ "@type": "core:DictionaryEntry"
+ },
+ "modelDataPreprocessing": {
+ "@id": "ai:modelDataPreprocessing",
+ "@type": "xsd:string"
+ },
+ "modelExplainability": {
+ "@id": "ai:modelExplainability",
+ "@type": "xsd:string"
+ },
+ "safetyRiskAssessment": {
+ "@id": "ai:safetyRiskAssessment",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "ai:SafetyRiskAssessmentType/"
+ }
+ },
+ "sensitivePersonalInformation": {
+ "@id": "dataset:sensitivePersonalInformation",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "dataset:PresenceType/"
+ }
+ },
+ "standardCompliance": {
+ "@id": "ai:standardCompliance",
+ "@type": "xsd:string"
+ },
+ "typeOfModel": {
+ "@id": "ai:typeOfModel",
+ "@type": "xsd:string"
+ },
+ "buildEndTime": {
+ "@id": "build:buildEndTime",
+ "@type": "core:DateTime"
+ },
+ "buildId": {
+ "@id": "build:buildId",
+ "@type": "xsd:string"
+ },
+ "buildStartTime": {
+ "@id": "build:buildStartTime",
+ "@type": "core:DateTime"
+ },
+ "buildType": {
+ "@id": "build:buildType",
+ "@type": "xsd:anyURI"
+ },
+ "configSourceDigest": {
+ "@id": "build:configSourceDigest",
+ "@type": "core:Hash"
+ },
+ "configSourceEntrypoint": {
+ "@id": "build:configSourceEntrypoint",
+ "@type": "xsd:string"
+ },
+ "configSourceUri": {
+ "@id": "build:configSourceUri",
+ "@type": "xsd:anyURI"
+ },
+ "environment": {
+ "@id": "build:environment",
+ "@type": "core:DictionaryEntry"
+ },
+ "parameters": {
+ "@id": "build:parameters",
+ "@type": "core:DictionaryEntry"
+ },
+ "Artifact": "core:Artifact",
+ "Bom": "core:Bom",
+ "ElementCollection": "core:ElementCollection",
+ "LifecycleScopedRelationship": "core:LifecycleScopedRelationship",
+ "algorithm": {
+ "@id": "core:algorithm",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "core:HashAlgorithm/"
+ }
+ },
+ "annotationType": {
+ "@id": "core:annotationType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "core:AnnotationType/"
+ }
+ },
+ "begin": {
+ "@id": "core:begin",
+ "@type": "xsd:positiveInteger"
+ },
+ "builtTime": {
+ "@id": "core:builtTime",
+ "@type": "core:DateTime"
+ },
+ "completeness": {
+ "@id": "core:completeness",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "core:RelationshipCompleteness/"
+ }
+ },
+ "context": {
+ "@id": "core:context",
+ "@type": "xsd:string"
+ },
+ "created": {
+ "@id": "core:created",
+ "@type": "core:DateTime"
+ },
+ "createdBy": {
+ "@id": "core:createdBy",
+ "@type": "@id"
+ },
+ "createdUsing": {
+ "@id": "core:createdUsing",
+ "@type": "core:Tool"
+ },
+ "dataLicense": {
+ "@id": "core:dataLicense",
+ "@type": "xsd:string"
+ },
+ "definingDocument": {
+ "@id": "core:definingDocument",
+ "@type": "xsd:anyURI"
+ },
+ "description": {
+ "@id": "core:description",
+ "@type": "xsd:string"
+ },
+ "element": {
+ "@id": "core:element",
+ "@type": "@id"
+ },
+ "end": {
+ "@id": "core:end",
+ "@type": "xsd:positiveInteger"
+ },
+ "endTime": {
+ "@id": "core:endTime",
+ "@type": "core:DateTime"
+ },
+ "externalId": {
+ "@id": "core:externalId",
+ "@type": "xsd:anyURI"
+ },
+ "externalIdentifier": {
+ "@id": "core:externalIdentifier",
+ "@type": "core:ExternalIdentifier"
+ },
+ "externalIdentifierType": {
+ "@id": "core:externalIdentifierType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "core:ExternalIdentifierType/"
+ }
+ },
+ "externalReference": {
+ "@id": "core:externalReference",
+ "@type": "core:ExternalReference"
+ },
+ "externalReferenceType": {
+ "@id": "core:externalReferenceType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "core:ExternalReferenceType/"
+ }
+ },
+ "from": {
+ "@id": "core:from",
+ "@type": "@id"
+ },
+ "hashValue": {
+ "@id": "core:hashValue",
+ "@type": "xsd:string"
+ },
+ "identifier": {
+ "@id": "core:identifier",
+ "@type": "xsd:string"
+ },
+ "identifierLocator": {
+ "@id": "core:identifierLocator",
+ "@type": "xsd:anyURI"
+ },
+ "issuingAuthority": {
+ "@id": "core:issuingAuthority",
+ "@type": "xsd:anyURI"
+ },
+ "key": {
+ "@id": "core:key",
+ "@type": "xsd:string"
+ },
+ "locationHint": {
+ "@id": "core:locationHint",
+ "@type": "xsd:anyURI"
+ },
+ "locator": {
+ "@id": "core:locator",
+ "@type": "xsd:anyURI"
+ },
+ "namespace": {
+ "@id": "core:namespace",
+ "@type": "xsd:anyURI"
+ },
+ "originatedBy": {
+ "@id": "core:originatedBy",
+ "@type": "@id"
+ },
+ "prefix": {
+ "@id": "core:prefix",
+ "@type": "xsd:string"
+ },
+ "profile": {
+ "@id": "core:profile",
+ "@type": "@vocab",
+ "@context": {
+ "core": "https://spdx.org/rdf/Core/ProfileIdentifierType/core",
+ "software": "https://spdx.org/rdf/Core/ProfileIdentifierType/software",
+ "licensing": "https://spdx.org/rdf/Core/ProfileIdentifierType/licensing",
+ "security": "https://spdx.org/rdf/Core/ProfileIdentifierType/security",
+ "build": "https://spdx.org/rdf/Core/ProfileIdentifierType/build",
+ "ai": "https://spdx.org/rdf/Core/ProfileIdentifierType/ai",
+ "dataset": "https://spdx.org/rdf/Core/ProfileIdentifierType/dataset",
+ "usage": "https://spdx.org/rdf/Core/ProfileIdentifierType/usage",
+ "extension": "https://spdx.org/rdf/Core/ProfileIdentifierType/extension"
+ }
+ },
+ "relationshipType": {
+ "@id": "core:relationshipType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "core:RelationshipType/"
+ }
+ },
+ "releaseTime": {
+ "@id": "core:releaseTime",
+ "@type": "core:DateTime"
+ },
+ "rootElement": {
+ "@id": "core:rootElement",
+ "@type": "@id"
+ },
+ "scope": {
+ "@id": "core:scope",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "core:LifecycleScopeType/"
+ }
+ },
+ "specVersion": {
+ "@id": "core:specVersion",
+ "@type": "core:SemVer"
+ },
+ "standard": {
+ "@id": "core:standard",
+ "@type": "xsd:string"
+ },
+ "startTime": {
+ "@id": "core:startTime",
+ "@type": "core:DateTime"
+ },
+ "statement": {
+ "@id": "core:statement",
+ "@type": "xsd:string"
+ },
+ "subject": {
+ "@id": "core:subject",
+ "@type": "@id"
+ },
+ "summary": {
+ "@id": "core:summary",
+ "@type": "xsd:string"
+ },
+ "suppliedBy": {
+ "@id": "core:suppliedBy",
+ "@type": "@id"
+ },
+ "to": {
+ "@id": "core:to",
+ "@type": "@id"
+ },
+ "validUntilTime": {
+ "@id": "core:validUntilTime",
+ "@type": "core:DateTime"
+ },
+ "value": {
+ "@id": "core:value",
+ "@type": "xsd:string"
+ },
+ "anonymizationMethodUsed": {
+ "@id": "dataset:anonymizationMethodUsed",
+ "@type": "xsd:string"
+ },
+ "confidentialityLevel": {
+ "@id": "dataset:confidentialityLevel",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "dataset:ConfidentialityLevelType/"
+ }
+ },
+ "dataCollectionProcess": {
+ "@id": "dataset:dataCollectionProcess",
+ "@type": "xsd:string"
+ },
+ "dataPreprocessing": {
+ "@id": "dataset:dataPreprocessing",
+ "@type": "xsd:string"
+ },
+ "datasetAvailability": {
+ "@id": "dataset:datasetAvailability",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "dataset:DatasetAvailabilityType/"
+ }
+ },
+ "datasetNoise": {
+ "@id": "dataset:datasetNoise",
+ "@type": "xsd:string"
+ },
+ "datasetSize": {
+ "@id": "dataset:datasetSize",
+ "@type": "xsd:nonNegativeInteger"
+ },
+ "datasetType": {
+ "@id": "dataset:datasetType",
+ "@type": "xsd:string"
+ },
+ "datasetUpdateMechanism": {
+ "@id": "dataset:datasetUpdateMechanism",
+ "@type": "xsd:string"
+ },
+ "intendedUse": {
+ "@id": "dataset:intendedUse",
+ "@type": "xsd:string"
+ },
+ "knownBias": {
+ "@id": "dataset:knownBias",
+ "@type": "xsd:string"
+ },
+ "sensor": {
+ "@id": "dataset:sensor",
+ "@type": "core:DictionaryEntry"
+ },
+ "additionComment": {
+ "@id": "licensing:additionComment",
+ "@type": "xsd:string"
+ },
+ "additionId": {
+ "@id": "licensing:additionId",
+ "@type": "xsd:string"
+ },
+ "additionName": {
+ "@id": "licensing:additionName",
+ "@type": "xsd:string"
+ },
+ "additionText": {
+ "@id": "licensing:additionText",
+ "@type": "xsd:string"
+ },
+ "isDeprecatedAdditionId": {
+ "@id": "licensing:isDeprecatedAdditionId",
+ "@type": "xsd:boolean"
+ },
+ "isDeprecatedLicenseId": {
+ "@id": "licensing:isDeprecatedLicenseId",
+ "@type": "xsd:boolean"
+ },
+ "isFsfLibre": {
+ "@id": "licensing:isFsfLibre",
+ "@type": "xsd:boolean"
+ },
+ "isOsiApproved": {
+ "@id": "licensing:isOsiApproved",
+ "@type": "xsd:boolean"
+ },
+ "licenseComment": {
+ "@id": "licensing:licenseComment",
+ "@type": "xsd:string"
+ },
+ "licenseId": {
+ "@id": "licensing:licenseId",
+ "@type": "xsd:string"
+ },
+ "licenseName": {
+ "@id": "licensing:licenseName",
+ "@type": "xsd:string"
+ },
+ "licenseText": {
+ "@id": "licensing:licenseText",
+ "@type": "xsd:string"
+ },
+ "standardAdditionTemplate": {
+ "@id": "licensing:standardAdditionTemplate",
+ "@type": "xsd:string"
+ },
+ "standardLicenseHeader": {
+ "@id": "licensing:standardLicenseHeader",
+ "@type": "xsd:string"
+ },
+ "standardLicenseTemplate": {
+ "@id": "licensing:standardLicenseTemplate",
+ "@type": "xsd:string"
+ },
+ "subjectAddition": {
+ "@id": "licensing:subjectAddition",
+ "@type": "licensing:LicenseAddition"
+ },
+ "actionStatement": {
+ "@id": "security:actionStatement",
+ "@type": "xsd:string"
+ },
+ "actionStatementTime": {
+ "@id": "security:actionStatementTime",
+ "@type": "core:DateTime"
+ },
+ "assessedElement": {
+ "@id": "security:assessedElement",
+ "@type": "@id"
+ },
+ "catalogType": {
+ "@id": "security:catalogType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "security:ExploitCatalogType/"
+ }
+ },
+ "decisionType": {
+ "@id": "security:decisionType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "security:SsvcDecisionType/"
+ }
+ },
+ "exploited": {
+ "@id": "security:exploited",
+ "@type": "xsd:boolean"
+ },
+ "impactStatement": {
+ "@id": "security:impactStatement",
+ "@type": "xsd:string"
+ },
+ "impactStatementTime": {
+ "@id": "security:impactStatementTime",
+ "@type": "core:DateTime"
+ },
+ "justificationType": {
+ "@id": "security:justificationType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "security:VexJustificationType/"
+ }
+ },
+ "probability": {
+ "@id": "security:probability",
+ "@type": "xsd:decimal"
+ },
+ "statusNotes": {
+ "@id": "security:statusNotes",
+ "@type": "xsd:string"
+ },
+ "vexVersion": {
+ "@id": "security:vexVersion",
+ "@type": "xsd:string"
+ },
+ "additionalPurpose": {
+ "@id": "software:additionalPurpose",
+ "@type": "software:SoftwarePurpose"
+ },
+ "attributionText": {
+ "@id": "software:attributionText",
+ "@type": "xsd:string"
+ },
+ "byteRange": {
+ "@id": "software:byteRange",
+ "@type": "core:PositiveIntegerRange"
+ },
+ "concludedLicense": {
+ "@id": "software:concludedLicense",
+ "@type": "licensing:LicenseField"
+ },
+ "conditionality": {
+ "@id": "software:conditionality",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "software:DependencyConditionalityType/"
+ }
+ },
+ "contentIdentifier": {
+ "@id": "software:contentIdentifier",
+ "@type": "xsd:anyURI"
+ },
+ "contentType": {
+ "@id": "core:contentType",
+ "@type": "core:MediaType"
+ },
+ "copyrightText": {
+ "@id": "software:copyrightText",
+ "@type": "xsd:string"
+ },
+ "declaredLicense": {
+ "@id": "software:declaredLicense",
+ "@type": "licensing:LicenseField"
+ },
+ "downloadLocation": {
+ "@id": "software:downloadLocation",
+ "@type": "xsd:anyURI"
+ },
+ "homePage": {
+ "@id": "software:homePage",
+ "@type": "xsd:anyURI"
+ },
+ "lineRange": {
+ "@id": "software:lineRange",
+ "@type": "core:PositiveIntegerRange"
+ },
+ "packageUrl": {
+ "@id": "software:packageUrl",
+ "@type": "xsd:anyURI"
+ },
+ "packageVersion": {
+ "@id": "software:packageVersion",
+ "@type": "xsd:string"
+ },
+ "primaryPurpose": {
+ "@id": "software:primaryPurpose",
+ "@type": "software:SoftwarePurpose"
+ },
+ "sbomType": {
+ "@id": "software:sbomType",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "software:SBOMType/"
+ }
+ },
+ "softwareLinkage": {
+ "@id": "software:softwareLinkage",
+ "@type": "@vocab",
+ "@context": {
+ "@vocab": "software:SoftwareDependencyLinkType/"
+ }
+ },
+ "sourceInfo": {
+ "@id": "software:sourceInfo",
+ "@type": "xsd:string"
+ },
+ "Bundle": "core:Bundle",
+ "ExternalIdentifier": "core:ExternalIdentifier",
+ "ExternalReference": "core:ExternalReference",
+ "Hash": "core:Hash",
+ "Payload": "core:Payload",
+ "Relationship": "core:Relationship",
+ "SemVer": "core:SemVer",
+ "Tool": "core:Tool",
+ "name": {
+ "@id": "core:name",
+ "@type": "xsd:string"
+ },
+ "verifiedUsing": {
+ "@id": "core:verifiedUsing",
+ "@type": "core:IntegrityMethod"
+ },
+ "deprecatedVersion": {
+ "@id": "licensing:deprecatedVersion",
+ "@type": "xsd:string"
+ },
+ "listVersionAdded": {
+ "@id": "licensing:listVersionAdded",
+ "@type": "xsd:string"
+ },
+ "member": {
+ "@id": "licensing:member",
+ "@type": "licensing:AnyLicenseInfo"
+ },
+ "obsoletedBy": {
+ "@id": "licensing:obsoletedBy",
+ "@type": "xsd:string"
+ },
+ "seeAlso": {
+ "@id": "licensing:seeAlso",
+ "@type": "xsd:anyURI"
+ },
+ "subjectLicense": {
+ "@id": "licensing:subjectLicense",
+ "@type": "licensing:License"
+ },
+ "modifiedTime": {
+ "@id": "security:modifiedTime",
+ "@type": "core:DateTime"
+ },
+ "publishedTime": {
+ "@id": "security:publishedTime",
+ "@type": "core:DateTime"
+ },
+ "score": {
+ "@id": "security:score",
+ "@type": "xsd:string"
+ },
+ "vector": {
+ "@id": "security:vector",
+ "@type": "xsd:string"
+ },
+ "withdrawnTime": {
+ "@id": "security:withdrawnTime",
+ "@type": "core:DateTime"
+ },
+ "Package": "software:Package",
+ "creationInfo": {
+ "@id": "core:creationInfo",
+ "@type": "core:CreationInfo"
+ },
+ "imports": {
+ "@id": "core:imports",
+ "@type": "core:ExternalMap"
+ },
+ "namespaces": {
+ "@id": "core:namespaces",
+ "@type": "core:NamespaceMap"
+ },
+ "severity": {
+ "@id": "security:severity",
+ "@type": "xsd:string"
+ },
+ "SoftwareArtifact": "software:SoftwareArtifact",
+ "AnnotationType": "core:AnnotationType",
+ "CreationInfo": "core:CreationInfo",
+ "ExternalMap": "core:ExternalMap",
+ "IntegrityMethod": "core:IntegrityMethod",
+ "NamespaceMap": "core:NamespaceMap",
+ "PositiveIntegerRange": "core:PositiveIntegerRange",
+ "LicenseAddition": "licensing:LicenseAddition",
+ "ExploitCatalogType": "security:ExploitCatalogType",
+ "VexVulnAssessmentRelationship": "security:VexVulnAssessmentRelationship",
+ "MediaType": "core:MediaType",
+ "RelationshipCompleteness": "core:RelationshipCompleteness",
+ "comment": {
+ "@id": "core:comment",
+ "@type": "xsd:string"
+ },
+ "License": "licensing:License",
+ "SafetyRiskAssessmentType": "ai:SafetyRiskAssessmentType",
+ "ConfidentialityLevelType": "dataset:ConfidentialityLevelType",
+ "SsvcDecisionType": "security:SsvcDecisionType",
+ "VulnAssessmentRelationship": "security:VulnAssessmentRelationship",
+ "SoftwareDependencyLinkType": "software:SoftwareDependencyLinkType",
+ "PresenceType": "ai:PresenceType",
+ "DatasetAvailabilityType": "dataset:DatasetAvailabilityType",
+ "LicenseField": "licensing:LicenseField",
+ "VexJustificationType": "security:VexJustificationType",
+ "DependencyConditionalityType": "software:DependencyConditionalityType",
+ "LifecycleScopeType": "core:LifecycleScopeType",
+ "AnyLicenseInfo": "licensing:AnyLicenseInfo",
+ "SBOMType": "software:SBOMType",
+ "Agent": "core:Agent",
+ "ProfileIdentifierType": "core:ProfileIdentifierType",
+ "DictionaryEntry": "core:DictionaryEntry",
+ "ExternalIdentifierType": "core:ExternalIdentifierType",
+ "Element": "core:Element",
+ "ExternalReferenceType": "core:ExternalReferenceType",
+ "HashAlgorithm": "core:HashAlgorithm",
+ "SoftwarePurpose": "software:SoftwarePurpose",
+ "DateTime": "core:DateTime",
+ "RelationshipType": "core:RelationshipType"
+}
diff --git a/src/spdx_tools/spdx3/writer/json_ld/json_ld_converter.py b/src/spdx_tools/spdx3/writer/json_ld/json_ld_converter.py
new file mode 100644
index 000000000..865053b71
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/json_ld/json_ld_converter.py
@@ -0,0 +1,79 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+from datetime import datetime
+from enum import Enum
+
+from beartype.typing import Any, List
+from semantic_version import Version
+
+from spdx_tools.spdx3.model.creation_info import CreationInfo
+from spdx_tools.spdx3.model.hash import Hash
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx.casing_tools import snake_case_to_camel_case
+from spdx_tools.spdx.datetime_conversions import datetime_to_iso_string
+
+
+def convert_payload_to_json_ld_list_of_elements(payload: Payload) -> List:
+ element_list = []
+
+ for element in payload.get_full_map().values():
+ element_dict = _convert_to_json_ld_dict(element)
+ element_list.append(element_dict)
+
+ return element_list
+
+
+def _convert_to_json_ld_dict(element: Any, alt_creation_info=False, alt_hash=False):
+ if not element:
+ return None
+
+ if isinstance(element, (str, int, tuple)):
+ return element
+
+ if isinstance(element, Version):
+ return str(element)
+
+ if isinstance(element, datetime):
+ return datetime_to_iso_string(element)
+
+ if isinstance(element, Enum):
+ return snake_case_to_camel_case(element.name)
+
+ if isinstance(element, list):
+ return [_convert_to_json_ld_dict(item) for item in element if item]
+
+ if alt_hash and isinstance(element, Hash):
+ hash_dict = {element.algorithm.name: element.hash_value}
+ if element.comment:
+ hash_dict["comment"] = element.comment
+ return hash_dict
+
+ # if issubclass(element.__class__, Element):
+ # element_dict = {"@type": element.__class__.__name__}
+ # else:
+ # element_dict = {} # typing of non-Element classes should be handled by the @context, I think
+
+ element_dict = {"@type": element.__class__.__name__}
+
+ for attribute_name in vars(element):
+ attribute_value = getattr(element, attribute_name)
+
+ if alt_creation_info and isinstance(attribute_value, CreationInfo):
+ for creation_info_attr_name in vars(attribute_value):
+ creation_info_attr_value = getattr(attribute_value, creation_info_attr_name)
+ element_dict[snake_case_to_camel_case(creation_info_attr_name)] = _convert_to_json_ld_dict(
+ creation_info_attr_value
+ )
+
+ elif attribute_value:
+ if attribute_name == "_spdx_id":
+ attribute_name = "@id"
+ elif attribute_name == "_from_element":
+ attribute_name = "from"
+ else:
+ attribute_name = snake_case_to_camel_case(attribute_name)
+
+ element_dict[attribute_name] = _convert_to_json_ld_dict(attribute_value)
+
+ return element_dict
diff --git a/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py b/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py
new file mode 100644
index 000000000..3c3e0819a
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py
@@ -0,0 +1,25 @@
+# SPDX-FileCopyrightText: 2023 spdx contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+import json
+from importlib import resources
+
+from spdx_tools.spdx3.payload import Payload
+from spdx_tools.spdx3.writer.json_ld.json_ld_converter import (
+ convert_payload_to_json_ld_list_of_elements,
+)
+
+
+def write_payload(payload: Payload, file_name: str):
+ element_list = convert_payload_to_json_ld_list_of_elements(payload)
+
+ # this will be obsolete as soon as the context is publicly available under some URI
+ # Note: 3.0.1 context is now available at
+ # https://spdx.org/rdf/3.0.1/spdx-context.jsonld
+ with resources.files("spdx_tools.spdx3.writer.json_ld").joinpath("context.json").open("r") as infile:
+ context = json.load(infile)
+
+ complete_dict = {"@context": context, "@graph": element_list}
+
+ with open(file_name + ".jsonld", "w", encoding="utf-8") as out:
+ json.dump(complete_dict, out, indent=2)
diff --git a/src/spdx_tools/spdx3/writer/json_ld/model.ttl b/src/spdx_tools/spdx3/writer/json_ld/model.ttl
new file mode 100644
index 000000000..04b407217
--- /dev/null
+++ b/src/spdx_tools/spdx3/writer/json_ld/model.ttl
@@ -0,0 +1,3441 @@
+@prefix ai: .
+@prefix build: .
+@prefix core: .
+@prefix dataset: .
+@prefix licensing: .
+@prefix ns0: .
+@prefix owl: .
+@prefix rdfs: .
+@prefix security: .
+@prefix sh: .
+@prefix software: .
+@prefix xsd: .
+
+ai:AIPackage a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """Metadata information that can be added to a package to describe an AI application or trained AI model.
+External property restriction on /Core/Artifact/suppliedBy: minCount: 1
+External property restriction on /Software/Package/downloadLocation: minCount: 1
+External property restriction on /Software/Package/packageVersion: minCount: 1
+External property restriction on /Software/SoftwareArtifact/purpose: minCount: 1
+External property restriction on /Core/Artifact/releaseTime: minCount: 1""" ;
+ rdfs:subClassOf software:Package ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class core:DictionaryEntry ;
+ sh:name "metric" ;
+ sh:path ai:metric ],
+ [ sh:datatype xsd:string ;
+ sh:name "modelExplainability" ;
+ sh:path ai:modelExplainability ],
+ [ sh:datatype xsd:string ;
+ sh:name "domain" ;
+ sh:path ai:domain ],
+ [ sh:datatype xsd:string ;
+ sh:name "standardCompliance" ;
+ sh:path ai:standardCompliance ],
+ [ sh:class core:DictionaryEntry ;
+ sh:name "hyperparameter" ;
+ sh:path ai:hyperparameter ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "energyConsumption" ;
+ sh:path ai:energyConsumption ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "limitation" ;
+ sh:path ai:limitation ],
+ [ sh:class ai:SafetyRiskAssessmentType ;
+ sh:maxCount 1 ;
+ sh:name "safetyRiskAssessment" ;
+ sh:path ai:safetyRiskAssessment ],
+ [ sh:datatype xsd:string ;
+ sh:name "modelDataPreprocessing" ;
+ sh:path ai:modelDataPreprocessing ],
+ [ sh:class ai:PresenceType ;
+ sh:maxCount 1 ;
+ sh:name "sensitivePersonalInformation" ;
+ sh:path ai:sensitivePersonalInformation ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "informationAboutTraining" ;
+ sh:path ai:informationAboutTraining ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "informationAboutApplication" ;
+ sh:path ai:informationAboutApplication ],
+ [ sh:datatype xsd:string ;
+ sh:name "typeOfModel" ;
+ sh:path ai:typeOfModel ],
+ [ sh:class ai:PresenceType ;
+ sh:maxCount 1 ;
+ sh:name "autonomyType" ;
+ sh:path ai:autonomyType ],
+ [ sh:class core:DictionaryEntry ;
+ sh:name "metricDecisionThreshold" ;
+ sh:path ai:metricDecisionThreshold ] .
+
+ a owl:NamedIndividual,
+ ai:PresenceType .
+
+ a owl:NamedIndividual,
+ ai:PresenceType .
+
+ a owl:NamedIndividual,
+ ai:PresenceType .
+
+ a owl:NamedIndividual,
+ ai:SafetyRiskAssessmentType .
+
+ a owl:NamedIndividual,
+ ai:SafetyRiskAssessmentType .
+
+ a owl:NamedIndividual,
+ ai:SafetyRiskAssessmentType .
+
+ a owl:NamedIndividual,
+ ai:SafetyRiskAssessmentType .
+
+build:Build a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A build is a representation of the process in which a piece of software or artifact is built. It encapsulates information related to a build process and
+provides an element from which relationships can be created to describe the build's inputs, outputs, and related entities (e.g. builders, identities, etc.).
+
+Definitions of "BuildType", "ConfigSource", "Parameters" and "Environment" follow
+those defined in [SLSA provenance](https://slsa.dev/provenance/v0.2).
+
+ExternalIdentifier of type "urlScheme" may be used to identify build logs. In this case, the comment of the ExternalIdentifier should be "LogReference".
+
+Note that buildStart and buildEnd are optional, and may be omitted to simplify creating reproducible builds.""" ;
+ rdfs:subClassOf core:Element ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class core:Hash ;
+ sh:name "configSourceDigest" ;
+ sh:path build:configSourceDigest ],
+ [ sh:datatype xsd:anyURI ;
+ sh:name "configSourceUri" ;
+ sh:path build:configSourceUri ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "buildId" ;
+ sh:path build:buildId ],
+ [ sh:class core:DictionaryEntry ;
+ sh:name "parameters" ;
+ sh:path build:parameters ],
+ [ sh:datatype core:DateTime ;
+ sh:maxCount 1 ;
+ sh:name "buildEndTime" ;
+ sh:path build:buildEndTime ],
+ [ sh:datatype core:DateTime ;
+ sh:maxCount 1 ;
+ sh:name "buildStartTime" ;
+ sh:path build:buildStartTime ],
+ [ sh:class core:DictionaryEntry ;
+ sh:name "environment" ;
+ sh:path build:environment ],
+ [ sh:datatype xsd:anyURI ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "buildType" ;
+ sh:path build:buildType ],
+ [ sh:datatype xsd:string ;
+ sh:name "configSourceEntrypoint" ;
+ sh:path build:configSourceEntrypoint ] .
+
+core:Annotation a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment "An Annotation is an assertion made in relation to one or more elements." ;
+ rdfs:subClassOf core:Element ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "statement" ;
+ sh:path core:statement ],
+ [ sh:class core:Element ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "subject" ;
+ sh:path core:subject ],
+ [ sh:datatype core:MediaType ;
+ sh:name "contentType" ;
+ sh:path core:contentType ],
+ [ sh:class core:AnnotationType ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "annotationType" ;
+ sh:path core:annotationType ] .
+
+ a owl:NamedIndividual,
+ core:AnnotationType .
+
+ a owl:NamedIndividual,
+ core:AnnotationType .
+
+core:AnonymousPayload a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment "TODO" ;
+ rdfs:subClassOf core:Payload ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class core:CreationInfo ;
+ sh:maxCount 1 ;
+ sh:name "creationInfo" ;
+ sh:path core:creationInfo ],
+ [ sh:class core:NamespaceMap ;
+ sh:name "namespaces" ;
+ sh:path core:namespaces ],
+ [ sh:class core:ExternalMap ;
+ sh:name "imports" ;
+ sh:path core:imports ] .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:ExternalReferenceType .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:HashAlgorithm .
+
+ a owl:NamedIndividual,
+ core:LifecycleScopeType .
+
+ a owl:NamedIndividual,
+ core:LifecycleScopeType .
+
+ a owl:NamedIndividual,
+ core:LifecycleScopeType .
+
+ a owl:NamedIndividual,
+ core:LifecycleScopeType .
+
+ a owl:NamedIndividual,
+ core:LifecycleScopeType .
+
+ a owl:NamedIndividual,
+ core:LifecycleScopeType .
+
+core:Organization a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment "An Organization is a group of people who work together in an organized way for a shared purpose." ;
+ rdfs:subClassOf core:Agent ;
+ ns0:term_status "Stable" .
+
+core:Person a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment "A Person is an individual human being." ;
+ rdfs:subClassOf core:Agent ;
+ ns0:term_status "Stable" .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:ProfileIdentifierType .
+
+ a owl:NamedIndividual,
+ core:RelationshipCompleteness .
+
+ a owl:NamedIndividual,
+ core:RelationshipCompleteness .
+
+ a owl:NamedIndividual,
+ core:RelationshipCompleteness .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+ a owl:NamedIndividual,
+ core:RelationshipType .
+
+core:SoftwareAgent a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment "A SoftwareAgent is a software program that is given the authority (similar to a user's authority) to act on a system." ;
+ rdfs:subClassOf core:Agent ;
+ ns0:term_status "Stable" .
+
+core:SpdxDocument a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """An SpdxDocument assembles a collection of Elements under a common string, the name of the document.
+Commonly used when representing a unit of transfer of SPDX Elements.
+External property restriction on /Core/Element/name: minCount: 1""" ;
+ rdfs:subClassOf core:Bundle ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "name" ;
+ sh:path core:name ] .
+
+ a owl:NamedIndividual,
+ dataset:ConfidentialityLevelType .
+
+ a owl:NamedIndividual,
+ dataset:ConfidentialityLevelType .
+
+ a owl:NamedIndividual,
+ dataset:ConfidentialityLevelType .
+
+ a owl:NamedIndividual,
+ dataset:ConfidentialityLevelType .
+
+dataset:Dataset a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """Metadata information that can be added to a dataset that may be used in a software or to train/test an AI package.
+External property restriction on /Core/Artifact/originatedBy: minCount: 1
+External property restriction on /Software/Package/downloadLocation: minCount: 1
+External property restriction on /Software/SoftwareArtifact/purpose: minCount: 1
+External property restriction on /Core/Artifact/releaseTime: minCount: 1
+External property restriction on /Core/Artifact/builtTime: minCount: 1""" ;
+ rdfs:subClassOf software:Package ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class dataset:DatasetAvailabilityType ;
+ sh:maxCount 1 ;
+ sh:name "datasetAvailability" ;
+ sh:path dataset:datasetAvailability ],
+ [ sh:class dataset:ConfidentialityLevelType ;
+ sh:maxCount 1 ;
+ sh:name "confidentialityLevel" ;
+ sh:path dataset:confidentialityLevel ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "dataCollectionProcess" ;
+ sh:path dataset:dataCollectionProcess ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "datasetUpdateMechanism" ;
+ sh:path dataset:datasetUpdateMechanism ],
+ [ sh:datatype xsd:string ;
+ sh:name "knownBias" ;
+ sh:path dataset:knownBias ],
+ [ sh:datatype xsd:nonNegativeInteger ;
+ sh:maxCount 1 ;
+ sh:name "datasetSize" ;
+ sh:path dataset:datasetSize ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "intendedUse" ;
+ sh:path dataset:intendedUse ],
+ [ sh:datatype dataset:PresenceType ;
+ sh:maxCount 1 ;
+ sh:name "sensitivePersonalInformation" ;
+ sh:path dataset:sensitivePersonalInformation ],
+ [ sh:datatype xsd:string ;
+ sh:name "dataPreprocessing" ;
+ sh:path dataset:dataPreprocessing ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "datasetNoise" ;
+ sh:path dataset:datasetNoise ],
+ [ sh:class core:DictionaryEntry ;
+ sh:name "sensor" ;
+ sh:path dataset:sensor ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "datasetType" ;
+ sh:path dataset:datasetType ],
+ [ sh:datatype xsd:string ;
+ sh:name "anonymizationMethodUsed" ;
+ sh:path dataset:anonymizationMethodUsed ] .
+
+ a owl:NamedIndividual,
+ dataset:DatasetAvailabilityType .
+
+ a owl:NamedIndividual,
+ dataset:DatasetAvailabilityType .
+
+ a owl:NamedIndividual,
+ dataset:DatasetAvailabilityType .
+
+ a owl:NamedIndividual,
+ dataset:DatasetAvailabilityType .
+
+ a owl:NamedIndividual,
+ dataset:DatasetAvailabilityType .
+
+licensing:ConjunctiveLicenseSet a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A ConjunctiveLicenseSet indicates that _each_ of its subsidiary
+AnyLicenseInfos apply. In other words, a ConjunctiveLicenseSet of two or
+more licenses represents a licensing situation where _all_ of the specified
+licenses are to be complied with. It is represented in the SPDX License
+Expression Syntax by the `AND` operator.
+
+It is syntactically correct to specify a ConjunctiveLicenseSet where the
+subsidiary AnyLicenseInfos may be "incompatible" according to a particular
+interpretation of the corresponding Licenses. The SPDX License Expression
+Syntax does not take into account interpretation of license texts, which is
+left to the consumer of SPDX data to determine for themselves.""" ;
+ rdfs:subClassOf licensing:AnyLicenseInfo ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class licensing:AnyLicenseInfo ;
+ sh:minCount 2 ;
+ sh:name "member" ;
+ sh:path licensing:member ] .
+
+licensing:CustomLicense a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A CustomLicense represents a License that is not listed on the SPDX License
+List at https://spdx.org/licenses, and is therefore defined by an SPDX data
+creator.""" ;
+ rdfs:subClassOf licensing:License ;
+ ns0:term_status "Stable" .
+
+licensing:CustomLicenseAddition a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A CustomLicenseAddition represents an addition to a License that is not listed
+on the SPDX Exceptions List at https://spdx.org/licenses/exceptions-index.html,
+and is therefore defined by an SPDX data creator.
+
+It is intended to represent additional language which is meant to be added to
+a License, but which is not itself a standalone License.""" ;
+ rdfs:subClassOf licensing:LicenseAddition ;
+ ns0:term_status "Stable" .
+
+licensing:DisjunctiveLicenseSet a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A DisjunctiveLicenseSet indicates that _only one_ of its subsidiary
+AnyLicenseInfos is required to apply. In other words, a
+DisjunctiveLicenseSet of two or more licenses represents a licensing
+situation where _only one_ of the specified licenses are to be complied with.
+A consumer of SPDX data would typically understand this to permit the recipient
+of the licensed content to choose which of the corresponding license they
+would prefer to use. It is represented in the SPDX License Expression Syntax
+by the `OR` operator.""" ;
+ rdfs:subClassOf licensing:AnyLicenseInfo ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class licensing:AnyLicenseInfo ;
+ sh:minCount 2 ;
+ sh:name "member" ;
+ sh:path licensing:member ] .
+
+licensing:ListedLicense a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A ListedLicense represents a License that is listed on the SPDX License List
+at https://spdx.org/licenses.""" ;
+ rdfs:subClassOf licensing:License ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "deprecatedVersion" ;
+ sh:path licensing:deprecatedVersion ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "listVersionAdded" ;
+ sh:path licensing:listVersionAdded ] .
+
+licensing:ListedLicenseException a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A ListedLicenseException represents an exception to a License (in other words,
+an exception to a license condition or an additional permission beyond those
+granted in a License) which is listed on the SPDX Exceptions List at
+https://spdx.org/licenses/exceptions-index.html.""" ;
+ rdfs:subClassOf licensing:LicenseAddition ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "deprecatedVersion" ;
+ sh:path licensing:deprecatedVersion ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "listVersionAdded" ;
+ sh:path licensing:listVersionAdded ] .
+
+licensing:NoAssertionLicense a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A NoAssertionLicense is the primary value that is used by a concludedLicense
+or declaredLicense field that indicates that the SPDX data creator is making
+no assertion about the license information for the corresponding software
+Package, File or Snippet.
+
+The specific meaning of NoAssertionLicense in the context of a
+concludedLicense or declaredLicense field is more fully set forth in the
+Property definitions for those fields.""" ;
+ rdfs:subClassOf licensing:LicenseField ;
+ ns0:term_status "Stable" .
+
+licensing:NoneLicense a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A NoneLicense is the primary value that is used by a concludedLicense or
+declaredLicense field that indicates the absence of license information from
+the corresponding software Package, File or Snippet.
+
+The specific meaning of NoneLicense in the context of a concludedLicense or
+declaredLicense field is more fully set forth in the Property definitions for
+those fields.""" ;
+ rdfs:subClassOf licensing:LicenseField ;
+ ns0:term_status "Stable" .
+
+licensing:OrLaterOperator a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """An OrLaterOperator indicates that this portion of the AnyLicenseInfo
+represents either (1) the specified version of the corresponding License, or
+(2) any later version of that License. It is represented in the SPDX License
+Expression Syntax by the `+` operator.
+
+It is context-dependent, and unspecified by SPDX, as to what constitutes a
+"later version" of any particular License. Some Licenses may not be versioned,
+or may not have clearly-defined ordering for versions. The consumer of SPDX
+data will need to determine for themselves what meaning to attribute to a
+"later version" operator for a particular License.""" ;
+ rdfs:subClassOf licensing:AnyLicenseInfo ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class licensing:License ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "subjectLicense" ;
+ sh:path licensing:subjectLicense ] .
+
+licensing:WithAdditionOperator a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A WithAdditionOperator indicates that the designated License is subject to the
+designated LicenseAddition, which might be a license exception on the SPDX
+Exceptions List (ListedLicenseException) or may be other additional text
+(CustomLicenseAddition). It is represented in the SPDX License Expression
+Syntax by the `WITH` operator.""" ;
+ rdfs:subClassOf licensing:AnyLicenseInfo ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class licensing:License ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "subjectLicense" ;
+ sh:path licensing:subjectLicense ],
+ [ sh:class licensing:LicenseAddition ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "subjectAddition" ;
+ sh:path licensing:subjectAddition ] .
+
+security:CvssV2VulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A CvssV2VulnAssessmentRelationship relationship describes the determined score and vector of a vulnerability using version 2.0 of the Common Vulnerability Scoring System
+(CVSS) as defined on [https://www.first.org/cvss/v2/guide](https://www.first.org/cvss/v2/guide). It is intented to communicate the results of using a CVSS calculator.
+
+**Constraints**
+
+- The value of severity must be one of 'low', 'medium' or 'high'
+- The relationship type must be set to hasAssessmentFor.
+
+**Syntax**
+
+```json
+{
+ "@type": "CvssV2VulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:cvssv2-cve-2020-28498",
+ "relationshipType": "hasAssessmentFor",
+ "score": 4.3,
+ "vector": "(AV:N/AC:M/Au:N/C:P/I:N/A:N)",
+ "severity": "low",
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "assessedElement": "urn:npm-elliptic-6.5.2",
+ "externalReferences": [
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityAdvisory",
+ "locator": "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"
+ },
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityAdvisory",
+ "locator": "https://snyk.io/vuln/SNYK-JS-ELLIPTIC-1064899"
+ },
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityFix",
+ "locator": "https://github.com/indutny/elliptic/commit/441b742"
+ }
+ ],
+ "suppliedBy": ["urn:spdx.dev:agent-my-security-vendor"],
+ "publishedTime": "2023-05-06T10:06:13Z"
+},
+{
+ "@type": "Relationship",
+ "@id": "urn:spdx.dev:vulnAgentRel-1",
+ "relationshipType": "publishedBy",
+ "from": "urn:spdx.dev:cvssv2-cve-2020-28498",
+ "to": ["urn:spdx.dev:agent-snyk"],
+ "startTime": "2021-03-08T16:06:50Z"
+}
+```""" ;
+ rdfs:subClassOf security:VulnAssessmentRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:decimal ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "score" ;
+ sh:path security:score ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "severity" ;
+ sh:path security:severity ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "vector" ;
+ sh:path security:vector ] .
+
+security:CvssV3VulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A CvssV3VulnAssessmentRelationship relationship describes the determined score,
+severity, and vector of a vulnerability using version 3.1 of the Common
+Vulnerability Scoring System (CVSS) as defined on
+[https://www.first.org/cvss/v3.1/specification-document](https://www.first.org/cvss/v3.1/specification-document). It is intented to communicate the results of using a CVSS calculator.
+
+**Constraints**
+
+- The value of severity must be one of 'none', 'low', 'medium', 'high' or 'critical'.
+- Absence of the property shall be interpreted as 'none'.
+- The relationship type must be set to hasAssessmentFor.
+
+**Syntax**
+
+```json
+{
+ "@type": "CvssV3VulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:cvssv3-cve-2020-28498",
+ "relationshipType": "hasAssessmentFor",
+ "severity": "medium",
+ "score": 6.8,
+ "vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N",
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "assessedElement": "urn:npm-elliptic-6.5.2",
+ "externalReferences": [
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityAdvisory",
+ "locator": "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"
+ },
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityAdvisory",
+ "locator": "https://snyk.io/vuln/SNYK-JS-ELLIPTIC-1064899"
+ },
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityFix",
+ "locator": "https://github.com/indutny/elliptic/commit/441b742"
+ }
+ ],
+ "suppliedBy": ["urn:spdx.dev:agent-my-security-vendor"],
+ "publishedTime": "2023-05-06T10:06:13Z"
+},
+{
+ "@type": "Relationship",
+ "@id": "urn:spdx.dev:vulnAgentRel-1",
+ "relationshipType": "publishedBy",
+ "from": "urn:spdx.dev:cvssv3-cve-2020-28498",
+ "to": "urn:spdx.dev:agent-snyk",
+ "startTime": "2021-03-08T16:06:50Z"
+}
+```""" ;
+ rdfs:subClassOf security:VulnAssessmentRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:decimal ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "score" ;
+ sh:path security:score ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "vector" ;
+ sh:path security:vector ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "severity" ;
+ sh:path security:severity ] .
+
+security:EpssVulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """An EpssVulnAssessmentRelationship relationship describes the likelihood or
+probability that a vulnerability will be exploited in the wild using the Exploit
+Prediction Scoring System (EPSS) as defined on
+[https://www.first.org/epss/model](https://www.first.org/epss/model).
+
+**Constraints**
+
+- The relationship type must be set to hasAssessmentFor.
+
+**Syntax**
+
+```json
+{
+ "@type": "EpssVulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:epss-1",
+ "relationshipType": "hasAssessmentFor",
+ "probability": 80,
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "suppliedBy": ["urn:spdx.dev:agent-jane-doe"],
+ "publishedTime": "2021-03-09T11:04:53Z"
+}
+```""" ;
+ rdfs:subClassOf security:VulnAssessmentRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:nonNegativeInteger ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "probability" ;
+ sh:path security:probability ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "severity" ;
+ sh:path security:severity ] .
+
+ a owl:NamedIndividual,
+ security:ExploitCatalogType .
+
+ a owl:NamedIndividual,
+ security:ExploitCatalogType .
+
+security:ExploitCatalogVulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """An ExploitCatalogVulnAssessmentRelationship describes if a vulnerability is
+listed in any exploit catalog such as the CISA Known Exploited Vulnerabilities
+Catalog (KEV)
+[https://www.cisa.gov/known-exploited-vulnerabilities-catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog).
+
+**Constraints**
+
+- The relationship type must be set to hasAssessmentFor.
+
+**Syntax**
+
+```json
+{
+ "@type": "ExploitCatalogVulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:exploit-catalog-1",
+ "relationshipType": "hasAssessmentFor",
+ "catalogType": "kev",
+ "locator": "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
+ "exploited": "true",
+ "from": "urn:spdx.dev:vuln-cve-2023-2136",
+ "to": ["urn:product-google-chrome-112.0.5615.136"],
+ "suppliedBy": ["urn:spdx.dev:agent-jane-doe"],
+ "publishedTime": "2021-03-09T11:04:53Z"
+}
+```""" ;
+ rdfs:subClassOf security:VulnAssessmentRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype xsd:anyURI ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "locator" ;
+ sh:path security:locator ],
+ [ sh:class security:ExploitCatalogType ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "catalogType" ;
+ sh:path security:catalogType ],
+ [ sh:datatype xsd:boolean ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "exploited" ;
+ sh:path security:exploited ] .
+
+ a owl:NamedIndividual,
+ security:SsvcDecisionType .
+
+ a owl:NamedIndividual,
+ security:SsvcDecisionType .
+
+ a owl:NamedIndividual,
+ security:SsvcDecisionType .
+
+ a owl:NamedIndividual,
+ security:SsvcDecisionType .
+
+security:SsvcVulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """An SsvcVulnAssessmentRelationship describes the decision made using the
+Stakeholder-Specific Vulnerability Categorization (SSVC) decision tree as
+defined on [https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc](https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc).
+It is intended to communicate the results of using the CISA SSVC Calculator.
+
+**Constraints**
+
+- The relationship type must be set to hasAssessmentFor.
+
+**Syntax**
+
+```json
+{
+ "@type": "SsvcVulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:ssvc-1",
+ "relationshipType": "hasAssessmentFor",
+ "decisionType": "act",
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "assessedElement": "urn:npm-elliptic-6.5.2",
+ "suppliedBy": ["urn:spdx.dev:agent-jane-doe"],
+ "publishedTime": "2021-03-09T11:04:53Z"
+}
+```""" ;
+ rdfs:subClassOf security:VulnAssessmentRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class security:SsvcDecisionType ;
+ sh:maxCount 1 ;
+ sh:minCount 1 ;
+ sh:name "decisionType" ;
+ sh:path security:decisionType ] .
+
+security:VexAffectedVulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """VexAffectedVulnAssessmentRelationship connects a vulnerability and a number
+of elements. The relationship marks these elements as products affected by the
+vulnerability. This relationship corresponds to the VEX affected status.
+
+**Constraints**
+
+When linking elements using a VexAffectedVulnAssessmentRelationship, the
+following requirements must be observed:
+
+- Elements linked with a VulnVexAffectedAssessmentRelationship are constrained
+to the affects relationship type.
+
+**Syntax**
+
+```json
+{
+ "@type": "VexAffectedVulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:vex-affected-1",
+ "relationshipType": "affects",
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "assessedElement": "urn:npm-elliptic-6.5.2",
+ "actionStatement": "Upgrade to version 1.4 of ACME application.",
+ "suppliedBy": ["urn:spdx.dev:agent-jane-doe"],
+ "publishedTime": "2021-03-09T11:04:53Z"
+}
+```""" ;
+ rdfs:subClassOf security:VexVulnAssessmentRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype core:DateTime ;
+ sh:name "actionStatementTime" ;
+ sh:path security:actionStatementTime ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "actionStatement" ;
+ sh:path security:actionStatement ] .
+
+security:VexFixedVulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """VexFixedVulnAssessmentRelationship links a vulnerability to a number of elements
+representing VEX products where a vulnerability has been fixed and are no longer
+affected. It represents the VEX fixed status.
+
+**Constraints**
+
+When linking elements using a VexFixedVulnAssessmentRelationship, the following
+requirements must be observed:
+
+- Elements linked with a VulnVexFixedAssessmentRelationship are constrained to
+using the fixedIn relationship type.
+- The from: end of the relationship must ve a /Security/Vulnerability classed
+element.
+
+**Syntax**
+
+```json
+{
+ "@type": "VexFixedVulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:vex-fixed-in-1",
+ "relationshipType": "fixedIn",
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "assessedElement": "urn:npm-elliptic-6.5.4",
+ "suppliedBy": ["urn:spdx.dev:agent-jane-doe"],
+ "publishedTime": "2021-03-09T11:04:53Z"
+}
+```""" ;
+ rdfs:subClassOf security:VexVulnAssessmentRelationship ;
+ ns0:term_status "Stable" .
+
+ a owl:NamedIndividual,
+ security:VexJustificationType .
+
+ a owl:NamedIndividual,
+ security:VexJustificationType .
+
+ a owl:NamedIndividual,
+ security:VexJustificationType .
+
+ a owl:NamedIndividual,
+ security:VexJustificationType .
+
+ a owl:NamedIndividual,
+ security:VexJustificationType .
+
+security:VexNotAffectedVulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """VexNotAffectedVulnAssessmentRelationship connects a vulnerability and a number
+of elements designating them as products not affected by the vulnerability.
+This relationship corresponds to the VEX not_affected status.
+
+**Constraints**
+
+When linking elements using a VexNotVulnAffectedAssessmentRelationship, the
+following requirements must be observed:
+
+* Relating elements with a VexNotAffectedVulnAssessmentRelationship is restricted
+to the doesNotAffect relationship type.
+* The from: end of the relationship must be a /Security/Vulnerability classed
+element.
+* Both impactStatement and justificationType properties have a cardinality of
+0..1 making them optional. Nevertheless, to produce a valid VEX not_affected
+statement, one of them MUST be defined. This is specified in the Minimum Elements
+for VEX.
+
+**Syntax**
+
+```json
+{
+ "@type": "VexNotAffectedVulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:vex-not-affected-1",
+ "relationshipType": "doesNotAffect",
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "assessedElement": "urn:npm-elliptic-6.5.2",
+ "justificationType": "componentNotPresent",
+ "impactStatement": "Not using this vulnerable part of this library.",
+ "suppliedBy": ["urn:spdx.dev:agent-jane-doe"],
+ "publishedTime": "2021-03-09T11:04:53Z"
+}
+```""" ;
+ rdfs:subClassOf security:VexVulnAssessmentRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype core:DateTime ;
+ sh:maxCount 1 ;
+ sh:name "impactStatementTime" ;
+ sh:path security:impactStatementTime ],
+ [ sh:class security:VexJustificationType ;
+ sh:maxCount 1 ;
+ sh:name "justificationType" ;
+ sh:path security:justificationType ],
+ [ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ sh:name "impactStatement" ;
+ sh:path security:impactStatement ] .
+
+security:VexUnderInvestigationVulnAssessmentRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """VexUnderInvestigationVulnAssessmentRelationship links a vulnerability to a
+number of products stating the vulnerability's impact on them is being
+investigated. It represents the VEX under_investigation status.
+
+**Constraints**
+
+When linking elements using a VexUnderInvestigationVulnAssessmentRelationship
+the following requirements must be observed:
+
+- Elements linked with a VexUnderInvestigationVulnAssessmentRelationship are
+constrained to using the underInvestigationFor relationship type.
+- The from: end of the relationship must ve a /Security/Vulnerability classed
+element.
+
+**Syntax**
+
+```json
+{
+ "@type": "VexUnderInvestigationVulnAssessmentRelationship",
+ "@id": "urn:spdx.dev:vex-underInvestigation-1",
+ "relationshipType": "underInvestigationFor",
+ "from": "urn:spdx.dev:vuln-cve-2020-28498",
+ "to": ["urn:product-acme-application-1.3"],
+ "assessedElement": "urn:npm-elliptic-6.5.2",
+ "suppliedBy": ["urn:spdx.dev:agent-jane-doe"],
+ "publishedTime": "2021-03-09T11:04:53Z"
+}
+```""" ;
+ rdfs:subClassOf security:VexVulnAssessmentRelationship ;
+ ns0:term_status "Stable" .
+
+security:Vulnerability a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """Specifies a vulnerability and its associated information.
+
+**Syntax**
+
+```json
+{
+ "@type": "Vulnerability",
+ "@id": "urn:spdx.dev:vuln-1",
+ "summary": "Use of a Broken or Risky Cryptographic Algorithm",
+ "description": "The npm package `elliptic` before version 6.5.4 are vulnerable to Cryptographic Issues via the secp256k1 implementation in elliptic/ec/key.js. There is no check to confirm that the public key point passed into the derive function actually exists on the secp256k1 curve. This results in the potential for the private key used in this implementation to be revealed after a number of ECDH operations are performed.",
+ "modified": "2021-03-08T16:02:43Z",
+ "published": "2021-03-08T16:06:50Z",
+ "externalIdentifiers": [
+ {
+ "@type": "ExternalIdentifier",
+ "externalIdentifierType": "cve",
+ "identifier": "CVE-2020-2849",
+ "identifierLocator": [
+ "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-28498",
+ "https://www.cve.org/CVERecord?id=CVE-2020-28498"
+ ],
+ "issuingAuthority": "urn:spdx.dev:agent-cve.org"
+ },
+ {
+ "type": "ExternalIdentifier",
+ "externalIdentifierType": "securityOther",
+ "identifier": "GHSA-r9p9-mrjm-926w",
+ "identifierLocator": "https://github.com/advisories/GHSA-r9p9-mrjm-926w"
+ },
+ {
+ "type": "ExternalIdentifier",
+ "externalIdentifierType": "securityOther",
+ "identifier": "SNYK-JS-ELLIPTIC-1064899",
+ "identifierLocator": "https://security.snyk.io/vuln/SNYK-JS-ELLIPTIC-1064899"
+ }
+ ],
+ "externalReferences": [
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityAdvisory",
+ "locator": "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"
+ },
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityAdvisory",
+ "locator": "https://ubuntu.com/security/CVE-2020-28498"
+ },
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityOther",
+ "locator": "https://github.com/indutny/elliptic/pull/244/commits"
+ },
+ {
+ "@type": "ExternalReference",
+ "externalReferenceType": "securityOther",
+ "locator": "https://github.com/christianlundkvist/blog/blob/master/2020_05_26_secp256k1_twist_attacks/secp256k1_twist_attacks.md"
+ }
+ ]
+},
+{
+ "@type": "Relationship",
+ "@id": "urn:spdx.dev:vulnRelationship-1",
+ "relationshipType": "hasAssociatedVulnerability",
+ "from": "urn:npm-elliptic-6.5.2",
+ "to": ["urn:spdx.dev:vuln-1"],
+ "startTime": "2021-03-08T16:06:50Z"
+},
+{
+ "@type": "Relationship",
+ "@id": "urn:spdx.dev:vulnAgentRel-1",
+ "relationshipType": "publishedBy",
+ "from": "urn:spdx.dev:vuln-1",
+ "to": ["urn:spdx.dev:agent-snyk"],
+ "startTime": "2021-03-08T16:06:50Z"
+}
+```""" ;
+ rdfs:subClassOf core:Element ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype core:DateTime ;
+ sh:maxCount 1 ;
+ sh:name "publishedTime" ;
+ sh:path security:publishedTime ],
+ [ sh:datatype core:DateTime ;
+ sh:maxCount 1 ;
+ sh:name "modifiedTime" ;
+ sh:path security:modifiedTime ],
+ [ sh:datatype core:DateTime ;
+ sh:maxCount 1 ;
+ sh:name "withdrawnTime" ;
+ sh:path security:withdrawnTime ] .
+
+ a owl:NamedIndividual,
+ software:DependencyConditionalityType .
+
+ a owl:NamedIndividual,
+ software:DependencyConditionalityType .
+
+ a owl:NamedIndividual,
+ software:DependencyConditionalityType .
+
+ a owl:NamedIndividual,
+ software:DependencyConditionalityType .
+
+ a owl:NamedIndividual,
+ software:DependencyConditionalityType .
+
+software:File a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """Refers to any object that stores content on a computer.
+The type of content can optionally be provided in the contentType property.
+External property restriction on /Core/Element/name: minCount: 1""" ;
+ rdfs:subClassOf software:SoftwareArtifact ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:datatype core:MediaType ;
+ sh:maxCount 1 ;
+ sh:name "contentType" ;
+ sh:path software:contentType ] .
+
+ a owl:NamedIndividual,
+ software:SBOMType .
+
+ a owl:NamedIndividual,
+ software:SBOMType .
+
+ a owl:NamedIndividual,
+ software:SBOMType .
+
+ a owl:NamedIndividual,
+ software:SBOMType .
+
+ a owl:NamedIndividual,
+ software:SBOMType .
+
+ a owl:NamedIndividual,
+ software:SBOMType .
+
+software:Sbom a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A Software Bill of Materials (SBOM) is a collection of SPDX Elements describing a single package.
+This could include details of the content and composition of the product,
+provenance details of the product and/or
+its composition, licensing information, known quality or security issues, etc.""" ;
+ rdfs:subClassOf core:Bom ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class software:SBOMType ;
+ sh:name "sbomType" ;
+ sh:path software:sbomType ] .
+
+software:Snippet a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment """A Snippet describes a certain part of a file and can be used when the file is known to have some content
+that has been included from another original source. Snippets are useful for denoting when part of a file
+may have been originally created under another license or copied from a place with a known vulnerability.""" ;
+ rdfs:subClassOf software:SoftwareArtifact ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class core:PositiveIntegerRange ;
+ sh:maxCount 1 ;
+ sh:name "byteRange" ;
+ sh:path software:byteRange ],
+ [ sh:class core:PositiveIntegerRange ;
+ sh:maxCount 1 ;
+ sh:name "lineRange" ;
+ sh:path software:lineRange ] .
+
+ a owl:NamedIndividual,
+ software:SoftwareDependencyLinkType .
+
+ a owl:NamedIndividual,
+ software:SoftwareDependencyLinkType .
+
+ a owl:NamedIndividual,
+ software:SoftwareDependencyLinkType .
+
+ a owl:NamedIndividual,
+ software:SoftwareDependencyLinkType .
+
+software:SoftwareDependencyRelationship a owl:Class,
+ sh:NodeShape ;
+ rdfs:comment "TODO" ;
+ rdfs:subClassOf core:LifecycleScopedRelationship ;
+ ns0:term_status "Stable" ;
+ sh:property [ sh:class software:SoftwareDependencyLinkType ;
+ sh:maxCount 1 ;
+ sh:name "softwareLinkage" ;
+ sh:path software:softwareLinkage ],
+ [ sh:class software:DependencyConditionalityType ;
+ sh:maxCount 1 ;
+ sh:name "conditionality" ;
+ sh:path software:conditionality ] .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+ a owl:NamedIndividual,
+ software:SoftwarePurpose .
+
+