Skip to content

Commit 2f1fb1e

Browse files
authored
feat: support for external components with version-ranges (#586)
As discussed in ticket #321, this PR adds the following abilities: - mark components as **external** > Determine whether this component is external. > An external component is one that is not part of an assembly, but is expected to be provided by the environment, regardless of the component's `@scope`. This setting can be useful for distinguishing which components are bundled with the product and which can be relied upon to be present in the deployment environment. > This may be set to `true` for runtime components only. For `/metadata/component`, it must be set to `false`. - external components may have **version-ranges** instead of a specific version > For an external component, this specifies the accepted version range. > The value must adhere to the Package URL Version Range syntax (vers), as defined at <https://github.com/package-url/purl-spec/blob/master/VERSION-RANGE-SPEC.rst>. > May only be used if `.isExternal` is set to `true`. > Must be used exclusively, either 'version' or 'versionRange', but not both. fixes #321 ---- > [!NOTE] > this one supersedes #326 <-- read there for more background and previous discussions implementing with `components`, because the objects referenced/required are actually used at runtime and therefore are considered a "component". Sketch/proposal for #321 - [x] sketch JSON schema - properties and assert - test cases - [x] sketch XML schema - elements & attributes. no asserts - this would require XSD1.1 which is not broadly implemented, yet. - test cases - [x] sketch ProtoBuff schema - fields - test cases ---- > [!NOTE] > ALL FEEDBACK IS WELCOME! Yes, everything. > but some might not be resolved in this very PR, but in the authoritative guides. See #586 (comment)
2 parents 93c29e5 + ebe66bc commit 2f1fb1e

18 files changed

+440
-7
lines changed

schema/bom-1.7.proto

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,16 @@ message Component {
106106
optional string group = 7;
107107
// The name of the component. This will often be a shortened, single name of the component. Examples: commons-lang3 and jquery
108108
string name = 8;
109-
// The component version. The version should ideally comply with semantic versioning but is not enforced. Version was made optional in v1.4 of the spec. For backward compatibility, it is recommended to use an empty string to represent components without version information.
109+
// The component version. The version should ideally comply with semantic versioning but is not enforced.
110+
// Version was made optional in v1.4 of the spec.
111+
// For backward compatibility, it is recommended to use an empty string to represent components without version information.
112+
// Must be used exclusively, either 'version' or 'versionRange', but not both.
110113
string version = 9;
114+
// For an external component, this specifies the accepted version range.
115+
// The value must adhere to the Package URL Version Range syntax (vers), as defined at https://github.com/package-url/purl-spec/blob/master/VERSION-RANGE-SPEC.rst.
116+
// May only be used if `isExternal` is set to `true`.
117+
// Must be used exclusively, either 'version' or 'versionRange', but not both.
118+
optional string versionRange = 33;
111119
// Specifies a description for the component
112120
optional string description = 10;
113121
// Specifies the scope of the component. If a scope is not specified, SCOPE_REQUIRED scope should be assumed by the consumer of the BOM
@@ -154,6 +162,10 @@ message Component {
154162
repeated string omniborId = 31;
155163
// Specifies the Software Heritage persistent identifier (SWHID). The SWHID, if specified, must be valid and conform to the specification defined at: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html
156164
repeated string swhid = 32;
165+
// Determine whether this component is external.
166+
// An external component is one that is not part of an assembly, but is expected to be provided by the environment, regardless of the component's `scope`. This setting can be useful for distinguishing which components are bundled with the product and which can be relied upon to be present in the deployment environment.
167+
// This may be set to `true` for runtime components only. For `Bom.metadata.component`, it must be set to `false`.
168+
optional bool isExternal = 34; // implicit defaults to `false`
157169
}
158170

159171
// Specifies the data flow.

schema/bom-1.7.schema.json

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,18 @@
923923
"version": {
924924
"$ref": "#/definitions/version",
925925
"title": "Component Version",
926-
"description": "The component version. The version should ideally comply with semantic versioning but is not enforced."
926+
"description": "The component version. The version should ideally comply with semantic versioning but is not enforced.\nMust be used exclusively, either 'version' or 'versionRange', but not both."
927+
},
928+
"versionRange": {
929+
"$ref": "#/definitions/versionRange",
930+
"title": "Component Version Range",
931+
"description": "For an external component, this specifies the accepted version range.\nThe value must adhere to the Package URL Version Range syntax (vers), as defined at <https://github.com/package-url/purl-spec/blob/master/VERSION-RANGE-SPEC.rst>.\nMay only be used if `.isExternal` is set to `true`.\nMust be used exclusively, either 'version' or 'versionRange', but not both."
932+
},
933+
"isExternal": {
934+
"type": "boolean",
935+
"title": "Component Is External",
936+
"description": "Determine whether this component is external.\nAn external component is one that is not part of an assembly, but is expected to be provided by the environment, regardless of the component's `.scope`. This setting can be useful for distinguishing which components are bundled with the product and which can be relied upon to be present in the deployment environment.\nThis may be set to `true` for runtime components only. For `$.metadata.component`, it must be set to `false`.",
937+
"default": false
927938
},
928939
"description": {
929940
"type": "string",
@@ -1096,7 +1107,25 @@
10961107
"title": "Signature",
10971108
"description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)."
10981109
}
1099-
}
1110+
},
1111+
"allOf": [
1112+
{
1113+
"description": "Requirement: ensure that `version` and `versionRange` are not present simultaneously.",
1114+
"not": {
1115+
"required": ["version", "versionRange"]
1116+
}
1117+
},
1118+
{
1119+
"description": "Requirement: 'versionRange' must not be present when 'isExternal' is `false`.",
1120+
"if": {
1121+
"properties": { "isExternal": { "const": false } }
1122+
},
1123+
"then": {
1124+
"not": { "required": ["versionRange"] }
1125+
},
1126+
"else": true
1127+
}
1128+
]
11001129
},
11011130
"swid": {
11021131
"type": "object",

schema/bom-1.7.xsd

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -554,12 +554,26 @@ limitations under the License.
554554
of the component. Examples: commons-lang3 and jquery</xs:documentation>
555555
</xs:annotation>
556556
</xs:element>
557-
<xs:element name="version" type="bom:versionType" minOccurs="0" maxOccurs="1">
557+
<xs:choice minOccurs="0" maxOccurs="1">
558558
<xs:annotation>
559-
<xs:documentation>The component version. The version should ideally comply with semantic versioning
560-
but is not enforced.</xs:documentation>
559+
<xs:documentation>Must be used exclusively, either 'version' or 'versionRange', but not both.</xs:documentation>
561560
</xs:annotation>
562-
</xs:element>
561+
<xs:element name="version" type="bom:versionType">
562+
<xs:annotation>
563+
<xs:documentation>The component version. The version should ideally comply with semantic versioning
564+
but is not enforced.</xs:documentation>
565+
</xs:annotation>
566+
</xs:element>
567+
<xs:element name="versionRange" type="bom:versionRangeType">
568+
<xs:annotation>
569+
<xs:documentation><![CDATA[
570+
For an external component, this specifies the accepted version range.
571+
The value must adhere to the Package URL Version Range syntax (vers), as defined at https://github.com/package-url/purl-spec/blob/master/VERSION-RANGE-SPEC.rst.
572+
May only be used if `@isExternal` is set to `true`.
573+
]]></xs:documentation>
574+
</xs:annotation>
575+
</xs:element>
576+
</xs:choice>
563577
<xs:element name="description" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
564578
<xs:annotation>
565579
<xs:documentation>Specifies a description for the component</xs:documentation>
@@ -745,6 +759,15 @@ limitations under the License.
745759
</xs:documentation>
746760
</xs:annotation>
747761
</xs:attribute>
762+
<xs:attribute name="isExternal" type="xs:boolean" use="optional" default="false">
763+
<xs:annotation>
764+
<xs:documentation>
765+
Determine whether this component is external.
766+
An external component is one that is not part of an assembly, but is expected to be provided by the environment, regardless of the component's `@scope`. This setting can be useful for distinguishing which components are bundled with the product and which can be relied upon to be present in the deployment environment.
767+
This may be set to `true` for runtime components only. For `/metadata/component`, it must be set to `false`.
768+
</xs:documentation>
769+
</xs:annotation>
770+
</xs:attribute>
748771
<xs:attribute name="bom-ref" type="bom:refType">
749772
<xs:annotation>
750773
<xs:documentation>
@@ -759,6 +782,14 @@ limitations under the License.
759782
do not have the same name as an existing attribute used by the schema.</xs:documentation>
760783
</xs:annotation>
761784
</xs:anyAttribute>
785+
<!-- Attention:
786+
This would be formal, if the support for XSD1.1's `assert` was properly implemented in validators and tools digesting XML.
787+
<xs:assert vc:minVersion="1.1"
788+
id="versionRange_requires_isExternal_eq_true"
789+
test="if (versionRange) then (@isExternal eq 'true') else true()">
790+
Child `versionRange` May only be present, if attribute `isExternal`=='true'.
791+
</xs:assert>
792+
-->
762793
</xs:complexType>
763794

764795
<xs:complexType name="licenseType">
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0"?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.7"
3+
serialNumber="urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79" version="1"
4+
>
5+
<!--
6+
this would be formal, if the support for XSD1.1's `assert` was properly implemented
7+
in validators and tools digesting XML.
8+
-->
9+
<components>
10+
<component type="library" isExternal="false">
11+
<name>InvalidVersions</name>
12+
<versionRange><![CDATA[vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1]]></versionRange>
13+
<description>versionRange may only exist on extraneous components, set `isExternal` explicit</description>
14+
</component>
15+
</components>
16+
</bom>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0"?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.7"
3+
serialNumber="urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79" version="1"
4+
>
5+
<!--
6+
this would be formal, if the support for XSD1.1's `assert` was properly implemented
7+
in validators and tools digesting XML.
8+
-->
9+
<components>
10+
<component type="library">
11+
<!-- @isExternal defaults to `false` -->
12+
<name>InvalidVersions</name>
13+
<versionRange><![CDATA[vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1]]></versionRange>
14+
<description>versionRange may only exist on extraneous components, set `isExternal` implicit by default value</description>
15+
</component>
16+
</components>
17+
</bom>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
3+
"bomFormat": "CycloneDX",
4+
"specVersion": "1.7",
5+
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
6+
"version": 1,
7+
"components": [
8+
{
9+
"type": "library",
10+
"name": "InvalidVersions",
11+
"description": "may have `version` or `versionRange`, not both. This one does - it is invalid",
12+
"version": "9.0.14",
13+
"versionRange": "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1",
14+
"isExternal": true
15+
}
16+
]
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0"?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.7"
3+
serialNumber="urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79" version="1"
4+
>
5+
<components>
6+
<component type="library" isExternal="true">
7+
<name>InvalidVersions</name>
8+
<version>9.0.14</version>
9+
<versionRange><![CDATA[vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1]]></versionRange>
10+
<description>may have `version` or `versionRange`, not both. This one does - it is invalid</description>
11+
</component>
12+
</components>
13+
</bom>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
3+
"bomFormat": "CycloneDX",
4+
"specVersion": "1.7",
5+
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
6+
"version": 1,
7+
"components": [
8+
{
9+
"type": "library",
10+
"name": "InvalidVersions",
11+
"description": "versionRange may only exist on extraneous components; set `.isExternal` explicit",
12+
"isExternal": false,
13+
"versionRange": "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1"
14+
}
15+
]
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
3+
"bomFormat": "CycloneDX",
4+
"specVersion": "1.7",
5+
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
6+
"version": 1,
7+
"components": [
8+
{
9+
"type": "library",
10+
"name": "InvalidVersions",
11+
"description": "versionRange may only exist on extraneous components; set `.isExternal` implicit by default value",
12+
"versionRange": "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1"
13+
}
14+
]
15+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
3+
"bomFormat": "CycloneDX",
4+
"specVersion": "1.7",
5+
"serialNumber": "urn:uuid:7dc07ac8-7fb8-4782-bf89-4762394e189d",
6+
"version": 1,
7+
"metadata": {
8+
"component": {
9+
"bom-ref": "my-app",
10+
"type": "application",
11+
"name": "My Application"
12+
}
13+
},
14+
"components": [
15+
{
16+
"bom-ref": "os",
17+
"type": "operating-system",
18+
"name": "Ubuntu",
19+
"version": "24.04",
20+
"description": "Ubuntu 24.04",
21+
"isExternal": true
22+
}
23+
],
24+
"dependencies": [
25+
{
26+
"ref": "my-app",
27+
"dependsOn": [
28+
"os"
29+
]
30+
},
31+
{
32+
"ref": "os"
33+
}
34+
]
35+
}

0 commit comments

Comments
 (0)