Skip to content

Conversation

@Churro
Copy link
Contributor

@Churro Churro commented Nov 2, 2025

part of #903

Signed-off-by: Johannes Feichtner <johannes@web-wack.at>
@Churro Churro requested a review from a team as a code owner November 2, 2025 20:35
Signed-off-by: Johannes Feichtner <johannes@web-wack.at>
@jkowalleck jkowalleck added enhancement New feature or request schema1.7 labels Nov 3, 2025
@jkowalleck
Copy link
Member

before i look into the details of the implementation, please explain what the goal is.
special questions form my side:

  • How should the normalized/serialized data look like?
  • what rendered as CycloneDX 1.6 - is any data "backported", or is the information simply lost?

Signed-off-by: Johannes Feichtner <johannes@web-wack.at>
@Churro
Copy link
Contributor Author

Churro commented Nov 3, 2025

before i look into the details of the implementation, please explain what the goal is.

The goal is to add XML and JSON serialization/deserialization support for license expression details.

* How should the normalized/serialized data look like?

I used the following schema test data with license expression details as a reference on how it should look like:

The implementation mimics the structure of LicenseExpressionDetailed in bom-1.7.proto.

* what rendered as CycloneDX 1.6 - is any data "backported", or is the information simply lost?

A “skipped” warning is issued when attempting to serialize this data type for CDX < 1.7. No “backport”-like functionality.

@jkowalleck
Copy link
Member

I would really love to have minimal backport capabilities in the following sense:
if normalized/serialized to CycloneDX < 1.7, then

  • the BOM result has no expression details -> if there were some, then a warning is issued
  • the BOM result has the expression

so this

{
  "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
  "bomFormat": "CycloneDX",
  "specVersion": "1.7",
  "serialNumber": "urn:uuid:8ad91ceb-1741-4d58-8d22-4488a0f68dbe",
  "version": 1,
  "components": [
    {
      "type": "application",
      "name": "my-application",
      "version": "1.33.7",
      "description": "This application is composed of multiple things, and therefore has multiple licenses applied:\n* custom code - custom license\n* component A - EPL or GPL\n* component B - MIT\n* component C - MIT",
      "licenses": [
        {
          "bom-ref": "my-application-license",
          "acknowledgement": "declared",
          "expression": "LicenseRef-my-custom-license AND (EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0) AND MIT",
          "expressionDetails": [
            {
              "licenseIdentifier": "LicenseRef-my-custom-license",
              "text": {
                "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
              },
              "url": "https://my-application.example.com/license.txt"
            },
            {
              "licenseIdentifier": "EPL-2.0",
              "text": {
                "content": "Eclipse Public License - v 2.0\n\n    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE\n    PUBLIC LICENSE (\"AGREEMENT\"). ANY USE, REPRODUCTION OR DISTRIBUTION\n    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT..."
              }
            },
            {
              "licenseIdentifier": "GPL-2.0 WITH Classpath-exception-2.0",
              "text": {
                "contentType": "text/plain",
                "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed...\n\n...\n\nLinking this library statically or dynamically with other modules is making a combined work based on this library..."
              }
            },
            {
              "licenseIdentifier": "MIT",
              "bom-ref": "LicenseDetails-component-B",
              "text": {
                "content": "MIT License\n\nCopyright (c) 1996 Component-B-Creators Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"),...\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software..."
              }
            },
            {
              "licenseIdentifier": "MIT",
              "bom-ref": "LicenseDetails-component-C",
              "text": {
                "content": "MIT License\n\nCopyright (c) 2001 Component-C-Creators Org\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"),...\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software..."
              }
            }
          ]
        }
      ]
    }
  ]
}

would become

{
  "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "serialNumber": "urn:uuid:8ad91ceb-1741-4d58-8d22-4488a0f68dbe",
  "version": 1,
  "components": [
    {
      "type": "application",
      "name": "my-application",
      "version": "1.33.7",
      "description": "This application is composed of multiple things, and therefore has multiple licenses applied:\n* custom code - custom license\n* component A - EPL or GPL\n* component B - MIT\n* component C - MIT",
      "licenses": [
        {
          "bom-ref": "my-application-license",
          "acknowledgement": "declared",
          "expression": "LicenseRef-my-custom-license AND (EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0) AND MIT"
        }
      ]
    }
  ]
}

this "transformation" might be tricky - and might be added as a capability in a later iteration as a followup.

do you think this is possible?

@jkowalleck jkowalleck requested a review from Copilot November 5, 2025 12:53
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for LicenseExpressionDetailed and ExpressionDetails classes to handle detailed license expression information introduced in CycloneDX v1.7. The changes include new model classes, serialization/deserialization logic, and comprehensive test coverage.

  • Introduces LicenseExpressionDetailed class to represent detailed license expressions with expression details and properties
  • Introduces ExpressionDetails class to represent individual license identifier details within an expression
  • Updates the License type alias to include the new LicenseExpressionDetailed type

Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

File Description
cyclonedx/model/license.py Adds new ExpressionDetails and LicenseExpressionDetailed classes with full serialization support for CycloneDX v1.7, updates License type union, and implements comparison logic for proper sorting
tests/test_model_license.py Adds comprehensive test coverage for the new LicenseExpressionDetailed and ExpressionDetails classes including creation, update, equality, and sorting tests
tests/_data/models.py Adds test data for components with expression details to validate serialization across different schema versions
tests/_data/snapshots/*.bin Updates snapshot files for all CycloneDX versions (1.0-1.7) to include the new test component with expression details

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Churro and others added 2 commits November 5, 2025 20:09
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Johannes Feichtner <343448+Churro@users.noreply.github.com>
Signed-off-by: Johannes Feichtner <johannes@web-wack.at>
@Churro
Copy link
Contributor Author

Churro commented Nov 5, 2025

do you think this is possible?

In ebe9f2e, I've now added a pragmatic transformation that maps LicenseExpressionDetailed to LicenseExpression for CDX < 1.7. Please let me know if you had something like this in mind or otherwise we can revert it.

With JSON serialization, it's comparably easy because expressionDetails can just be ignored like other elements that are only available in certain CDX versions. The culprit is XML, where the element itself is named differently (<expression> vs <expression-detailed>) and the expression value is stored differently (element value vs expression="..". attribute).

@jkowalleck
Copy link
Member

jkowalleck commented Nov 6, 2025

From a user's perspective, I would find it more appealing to have no new data model, but use the existing LicenseExpression.

The (de)normalization is non-user-facing, non-public, and custom-made in the _LicenseRepositorySerializationHelper already ... so we could detect ala if self.expression_has_details(expression) and spec_verson < (1, 7): warn('some data will belost'); ....

I mean, this should be feasible, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request schema1.7

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants