From 4bd160dbf1048e9bc318201eef3848f2086297ab Mon Sep 17 00:00:00 2001 From: Pavel Shukhman Date: Fri, 12 Jun 2026 09:34:35 -0400 Subject: [PATCH 1/3] feat: add origin region for hardware provenance Signed-off-by: Pavel Shukhman --- .../model/cyclonedx-common-2.0.schema.json | 47 +++++++++++++++++++ .../model/cyclonedx-component-2.0.schema.json | 5 ++ 2 files changed, 52 insertions(+) diff --git a/schema/2.0/model/cyclonedx-common-2.0.schema.json b/schema/2.0/model/cyclonedx-common-2.0.schema.json index b93946c47..9a4e54082 100644 --- a/schema/2.0/model/cyclonedx-common-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-common-2.0.schema.json @@ -371,6 +371,11 @@ "title": "Organizational Contact", "description": "A contact at the organization. Multiple contacts are allowed.", "items": {"$ref": "#/$defs/organizationalContact"} + }, + "origin": { + "$ref": "#/$defs/origin", + "title": "Organization Origin", + "description": "The geographic origin associated with the organization, such as the country or countries where its goods or services originate." } } }, @@ -407,6 +412,48 @@ }, "organizationalEntityOrContact": { + }, + "origin": { + "type": "object", + "title": "Origin", + "description": "Describes the geographic origin of the subject (for example, a component or party), expressed as a distribution across one or more countries or subdivisions.", + "additionalProperties": false, + "properties": { + "basis": { + "type": "string", + "title": "Origin Basis", + "description": "A free-form description of the methodology or unit on which the origin distribution is calculated.", + "examples": [ "number of parts", "manufacturing cost", "net weight" ] + }, + "origins": { + "type": "array", + "title": "Origins", + "description": "The list of regions of origin and their respective share of the whole.", + "items": { "$ref": "#/$defs/originRegion" } + } + } + }, + "originRegion": { + "type": "object", + "title": "Origin Region", + "description": "A single region of origin and its share of the whole.", + "additionalProperties": false, + "required": [ "originCode" ], + "properties": { + "originCode": { + "type": "string", + "title": "Origin Code", + "description": "The country or subdivision of origin, expressed as an ISO 3166-1 alpha-2 country code (for example, 'CA' for Canada) or an ISO 3166-2 subdivision code (for example, 'US-CA' for California, United States).", + "examples": [ "CA", "US-CA" ] + }, + "percentage": { + "type": "number", + "title": "Percentage", + "description": "The share attributable to this region, expressed as a fraction between 0 and 1 inclusive.", + "minimum": 0, + "maximum": 1 + } + } }, "properties": { "type": "array", diff --git a/schema/2.0/model/cyclonedx-component-2.0.schema.json b/schema/2.0/model/cyclonedx-component-2.0.schema.json index d2e8053d2..fba0f6a9f 100644 --- a/schema/2.0/model/cyclonedx-component-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-component-2.0.schema.json @@ -90,6 +90,11 @@ "description": "The person(s) or organization(s) that published the component", "examples": ["Acme Inc"] }, + "origin": { + "$ref": "cyclonedx-common-2.0.schema.json#/$defs/origin", + "title": "Component Origin", + "description": "The geographic origin of the component, expressed as a distribution across one or more countries or subdivisions." + }, "group": { "type": "string", "title": "Component Group", From 63682cc29716d645f7cdefbdcdf0abb3d905ad83 Mon Sep 17 00:00:00 2001 From: Pavel Shukhman Date: Sun, 12 Jul 2026 14:42:20 -0400 Subject: [PATCH 2/3] fix: multiple origins allowed per subject and origins terminology change Based on HBOM group meeting from 2026-07-07. Signed-off-by: Pavel Shukhman --- .../model/cyclonedx-common-2.0.schema.json | 31 ++++++++++--------- .../model/cyclonedx-component-2.0.schema.json | 9 +++--- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/schema/2.0/model/cyclonedx-common-2.0.schema.json b/schema/2.0/model/cyclonedx-common-2.0.schema.json index 9a4e54082..d1626363d 100644 --- a/schema/2.0/model/cyclonedx-common-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-common-2.0.schema.json @@ -372,10 +372,11 @@ "description": "A contact at the organization. Multiple contacts are allowed.", "items": {"$ref": "#/$defs/organizationalContact"} }, - "origin": { - "$ref": "#/$defs/origin", - "title": "Organization Origin", - "description": "The geographic origin associated with the organization, such as the country or countries where its goods or services originate." + "origins": { + "type": "array", + "title": "Organization Origins", + "description": "The geographic origins associated with the organization, such as the country or countries where its goods or services originate. Multiple origins are allowed, each calculated on a different basis.", + "items": {"$ref": "#/$defs/origin"} } } }, @@ -416,19 +417,21 @@ "origin": { "type": "object", "title": "Origin", - "description": "Describes the geographic origin of the subject (for example, a component or party), expressed as a distribution across one or more countries or subdivisions.", + "description": "Describes the geographic origin of the subject (for example, a component or party), expressed as a distribution across one or more countries or subdivisions, calculated on a stated basis.", "additionalProperties": false, + "required": [ "basis", "distribution" ], "properties": { "basis": { "type": "string", "title": "Origin Basis", - "description": "A free-form description of the methodology or unit on which the origin distribution is calculated.", + "description": "A free-form description of the methodology or unit on which the origin distribution is calculated. Each origin of the same subject must use a distinct basis.", "examples": [ "number of parts", "manufacturing cost", "net weight" ] }, - "origins": { + "distribution": { "type": "array", - "title": "Origins", - "description": "The list of regions of origin and their respective share of the whole.", + "title": "Origin Distribution", + "description": "The distribution of the origin across regions. The percentages of all entries must total 100.", + "minItems": 1, "items": { "$ref": "#/$defs/originRegion" } } } @@ -438,20 +441,20 @@ "title": "Origin Region", "description": "A single region of origin and its share of the whole.", "additionalProperties": false, - "required": [ "originCode" ], + "required": [ "isoCode", "percentage" ], "properties": { - "originCode": { + "isoCode": { "type": "string", - "title": "Origin Code", + "title": "ISO Code", "description": "The country or subdivision of origin, expressed as an ISO 3166-1 alpha-2 country code (for example, 'CA' for Canada) or an ISO 3166-2 subdivision code (for example, 'US-CA' for California, United States).", "examples": [ "CA", "US-CA" ] }, "percentage": { "type": "number", "title": "Percentage", - "description": "The share attributable to this region, expressed as a fraction between 0 and 1 inclusive.", + "description": "The percentage of the whole attributable to this region. The percentages of all entries in a distribution must total 100.", "minimum": 0, - "maximum": 1 + "maximum": 100 } } }, diff --git a/schema/2.0/model/cyclonedx-component-2.0.schema.json b/schema/2.0/model/cyclonedx-component-2.0.schema.json index fba0f6a9f..d6b98cb03 100644 --- a/schema/2.0/model/cyclonedx-component-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-component-2.0.schema.json @@ -90,10 +90,11 @@ "description": "The person(s) or organization(s) that published the component", "examples": ["Acme Inc"] }, - "origin": { - "$ref": "cyclonedx-common-2.0.schema.json#/$defs/origin", - "title": "Component Origin", - "description": "The geographic origin of the component, expressed as a distribution across one or more countries or subdivisions." + "origins": { + "type": "array", + "title": "Component Origins", + "description": "The geographic origins of the component, each expressed as a distribution across one or more countries or subdivisions. Multiple origins are allowed, each calculated on a different basis.", + "items": { "$ref": "cyclonedx-common-2.0.schema.json#/$defs/origin" } }, "group": { "type": "string", From 30c0c023e550ab2d1ac63ba565157cd5037e78db Mon Sep 17 00:00:00 2001 From: Pavel Shukhman Date: Sun, 12 Jul 2026 15:02:13 -0400 Subject: [PATCH 3/3] fix: add validations and clarify descriptions for origins Signed-off-by: Pavel Shukhman --- schema/2.0/model/cyclonedx-common-2.0.schema.json | 7 +++++-- schema/2.0/model/cyclonedx-component-2.0.schema.json | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/schema/2.0/model/cyclonedx-common-2.0.schema.json b/schema/2.0/model/cyclonedx-common-2.0.schema.json index d1626363d..48d9085d3 100644 --- a/schema/2.0/model/cyclonedx-common-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-common-2.0.schema.json @@ -376,6 +376,7 @@ "type": "array", "title": "Organization Origins", "description": "The geographic origins associated with the organization, such as the country or countries where its goods or services originate. Multiple origins are allowed, each calculated on a different basis.", + "minItems": 1, "items": {"$ref": "#/$defs/origin"} } } @@ -425,12 +426,13 @@ "type": "string", "title": "Origin Basis", "description": "A free-form description of the methodology or unit on which the origin distribution is calculated. Each origin of the same subject must use a distinct basis.", + "minLength": 1, "examples": [ "number of parts", "manufacturing cost", "net weight" ] }, "distribution": { "type": "array", "title": "Origin Distribution", - "description": "The distribution of the origin across regions. The percentages of all entries must total 100.", + "description": "The distribution of the origin across regions. The percentages of all entries must total 100, within rounding of the stated precision.", "minItems": 1, "items": { "$ref": "#/$defs/originRegion" } } @@ -447,12 +449,13 @@ "type": "string", "title": "ISO Code", "description": "The country or subdivision of origin, expressed as an ISO 3166-1 alpha-2 country code (for example, 'CA' for Canada) or an ISO 3166-2 subdivision code (for example, 'US-CA' for California, United States).", + "pattern": "^[A-Z]{2}(-[A-Z0-9]{1,3})?$", "examples": [ "CA", "US-CA" ] }, "percentage": { "type": "number", "title": "Percentage", - "description": "The percentage of the whole attributable to this region. The percentages of all entries in a distribution must total 100.", + "description": "The percentage of the whole attributable to this region. The percentages of all entries in a distribution must total 100, within rounding of the stated precision.", "minimum": 0, "maximum": 100 } diff --git a/schema/2.0/model/cyclonedx-component-2.0.schema.json b/schema/2.0/model/cyclonedx-component-2.0.schema.json index d6b98cb03..80b3fdcbf 100644 --- a/schema/2.0/model/cyclonedx-component-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-component-2.0.schema.json @@ -94,6 +94,7 @@ "type": "array", "title": "Component Origins", "description": "The geographic origins of the component, each expressed as a distribution across one or more countries or subdivisions. Multiple origins are allowed, each calculated on a different basis.", + "minItems": 1, "items": { "$ref": "cyclonedx-common-2.0.schema.json#/$defs/origin" } }, "group": {