From dc7a61a2971f172efd94a3147c3ec08e1458d86e Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 8 Sep 2026 09:18:43 +0200 Subject: [PATCH 1/7] formulation environmentVars as key-value Signed-off-by: Jan Kowalleck --- .../cyclonedx-formulation-2.0.schema.json | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json index 997dc6d9..7d88138c 100644 --- a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json @@ -686,21 +686,8 @@ }, "environmentVars": { "title": "Environment variables", - "description": "Inputs that have the form of parameters with names and values.", - "type": "array", - "uniqueItems": true, - "items": { - "oneOf": [ - { - "$ref": "cyclonedx-common-2.0.schema.json#/$defs/property" - }, - { - "type": "string", - "title": "String-Based Environment Variables", - "description": "In addition to the more common key–value pair format, some environment variables may consist of a single string without an explicit value assignment. These string-based environment variables typically act as flags or signals to software, indicating that a feature should be enabled, a mode should be activated, or a specific condition is present. Their presence alone conveys meaning." - } - ] - } + "description": "Inputs that have the form of environment variables.", + "$ref": "#/$defs/environmentVars" }, "data": { "title": "Data", @@ -772,20 +759,7 @@ "environmentVars": { "title": "Environment variables", "description": "Outputs that have the form of environment variables.", - "type": "array", - "items": { - "oneOf": [ - { - "$ref": "cyclonedx-common-2.0.schema.json#/$defs/property" - }, - { - "type": "string", - "title": "String-Based Environment Variables", - "description": "In addition to the more common key–value pair format, some environment variables may consist of a single string without an explicit value assignment. These string-based environment variables typically act as flags or signals to software, indicating that a feature should be enabled, a mode should be activated, or a specific condition is present. Their presence alone conveys meaning." - } - ] - }, - "uniqueItems": true + "$ref": "#/$defs/environmentVars" }, "properties": { "$ref": "cyclonedx-common-2.0.schema.json#/$defs/properties" @@ -895,6 +869,17 @@ "type": "string" } } + }, + "environmentVars": { + "title": "Environment variables", + "description": "A map of environment variable names to their values. Each value must be a `string` or `null`. Only `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", + "type": "object", + "additionalProperties": { + "type": ["string", "null"] + }, + "examples": [ + { "LC_ALL": null, "LANG": "en_US.UTF-8", "0TRUST": "true" } + ] } } } From 05d137fefb8bc438102f84f69043fdd3f2f0c745 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 8 Sep 2026 09:50:18 +0200 Subject: [PATCH 2/7] tests Signed-off-by: Jan Kowalleck --- .../resources/2.0/valid-formulation-2.0.json | 51 +++++-------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/tools/src/test/resources/2.0/valid-formulation-2.0.json b/tools/src/test/resources/2.0/valid-formulation-2.0.json index 5038abba..cdba18f6 100644 --- a/tools/src/test/resources/2.0/valid-formulation-2.0.json +++ b/tools/src/test/resources/2.0/valid-formulation-2.0.json @@ -210,50 +210,23 @@ ], "inputs": [ { - "environmentVars": [ - { - "name": "Foo", - "value": "Bar" - } - ] - }, - { - "environmentVars": [ - "FooBar" - ] - }, - { - "environmentVars": [ - { - "name": "Foo", - "value": "Bar" - }, - "FooBar" - ] + "environmentVars": { + "LC_ALL": null, + "LANG": "en_US.UTF-8", + "0TRUST": "true" + } } ], "outputs": [ { - "environmentVars": [ - { - "name": "Foo", - "value": "Bar" - } - ] - }, - { - "environmentVars": [ - "FooBar" - ] + "environmentVars": { + "Foo": "Bar" + } }, { - "environmentVars": [ - { - "name": "Foo", - "value": "Bar" - }, - "FooBar" - ] + "environmentVars": { + "FooBar": "" + } } ], "timeStart": "2023-01-01T00:00:00+00:00", @@ -303,4 +276,4 @@ ] } ] -} \ No newline at end of file +} From b00247ccbcc676d66fb60eaad010ac05fc3d52c9 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 8 Sep 2026 10:06:40 +0200 Subject: [PATCH 3/7] examples Signed-off-by: Jan Kowalleck --- .../cyclonedx-formulation-2.0.schema.json | 4 +- .../2.0/invalid-formulation-envVars-2.0.json | 276 ++++++++++++++++++ .../resources/2.0/valid-formulation-2.0.json | 3 +- 3 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 tools/src/test/resources/2.0/invalid-formulation-envVars-2.0.json diff --git a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json index 7d88138c..a22a6f39 100644 --- a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json @@ -878,7 +878,9 @@ "type": ["string", "null"] }, "examples": [ - { "LC_ALL": null, "LANG": "en_US.UTF-8", "0TRUST": "true" } + { "LC_ALL": null, "LANG": "en_US.UTF-8" }, + { "0TRUST": "true" }, + { "MY_FLAG": "" } ] } } diff --git a/tools/src/test/resources/2.0/invalid-formulation-envVars-2.0.json b/tools/src/test/resources/2.0/invalid-formulation-envVars-2.0.json new file mode 100644 index 00000000..c34a2138 --- /dev/null +++ b/tools/src/test/resources/2.0/invalid-formulation-envVars-2.0.json @@ -0,0 +1,276 @@ +{ + "$schema": "https://cyclonedx.org/schema/2.0/cyclonedx-2.0.schema.json", + "specFormat": "CycloneDX", + "specVersion": "2.0", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "library", + "parties": [ + { + "roles": [ { "role": "publisher" } ], + "organization": { "name": "Acme Inc" } + } + ], + "group": "org.example", + "name": "mylibrary", + "version": "1.0.0" + } + ], + "formulation": [ + { + "bom-ref": "formula-1", + "components": [ + { + "bom-ref": "component-1", + "type": "platform", + "name": "Pipeline controller image", + "version": "v0.47.0" + } + ], + "workflows": [ + { + "inputs": [ + { + "environmentVars": { + "LC": { "ALL": null }, + "AMOUNT": 2, + "0TRUST": true + } + }, + { + "environmentVars": "LANG='en_US.UTF-8' 0TRUST='true' MY_FLAG=''" + }, + { + "environmentVars": "$env:LANG=\"en_US.UTF-8\"; Set-Item Env:0TRUST \"true\"; $env:MY_FLAG=\"\"" + }, + { + "environmentVars": "set \"LANG=en_US.UTF-8\" && set \"0TRUST=true\" && set \"MY_FLAG=\"" + } + ], + "bom-ref": "workflow-1", + "uid": "8edb2b08-e2c7-11ed-b5ea-0242ac120002", + "name": "My workflow", + "description": "Workflow description here", + "resourceReferences": [ + { + "ref": "component-a" + } + ], + "tasks": [ + { + "bom-ref": "task-1", + "uid": "task-uid-1", + "name": "fetch-repository", + "description": "Description here", + "resourceReferences": [ + { + "ref": "component-a" + } + ], + "taskTypes": [ + "clone", + "build" + ], + "trigger": { + "bom-ref": "trigger-1", + "uid": "trigger-1", + "type": "api" + }, + "steps": [ + { + "name": "My step" + } + ], + "inputs": [ + { + "resource": { + "ref": "component-a" + } + } + ], + "outputs": [ + { + "resource": { + "ref": "component-b" + } + } + ], + "timeStart": "2023-01-01T00:00:00+00:00", + "timeEnd": "2023-01-01T00:00:00+00:00", + "workspaces": [ + { + "bom-ref": "workspace-1", + "uid": "workspace-uid-1", + "name": "workspace" + } + ], + "runtimeTopology": [ + { + "ref": "task-1", + "dependsOn": ["task-2"] + } + ] + } + ], + "taskDependencies": [ + { + "ref": "task-1", + "dependsOn": ["task-2"] + } + ], + "taskTypes": [ + "clone", + "build" + ], + "trigger": { + "bom-ref": "trigger-2", + "uid": "trigger-uid-2", + "name": "My trigger", + "description": "Description here", + "resourceReferences": [ + { + "ref": "component-a" + } + ], + "type": "api", + "event": { + "uid": "event-1", + "description": "Description here", + "timeReceived": "2023-01-01T00:00:00+00:00", + "data": { + "mediaType": "text/plain", + "content": "Foo/Bar" + }, + "source": { + "ref": "component-g" + }, + "target": { + "ref": "component-h" + }, + "properties": [ + { + "name": "Foo", + "value": "Bar" + } + ] + }, + "conditions": [ + { + "description": "Description here", + "expression": "1 == 1", + "properties": [ + { + "name": "Foo", + "value": "Bar" + } + ] + } + ], + "timeActivated": "2023-01-01T00:00:00+00:00", + "inputs": [ + { + "resource": { + "ref": "component-10" + }, + "source": { + "ref": "component-11" + }, + "target": { + "ref": "component-12" + } + } + ], + "outputs": [ + { + "resource": { + "ref": "component-14" + }, + "type": "artifact", + "source": { + "ref": "component-15" + }, + "target": { + "ref": "component-16" + } + } + ], + "properties": [ + { + "name": "Foo", + "value": "Bar" + } + ] + }, + "steps": [ + { + "name": "My step", + "description": "Description here", + "commands": [ + { + "executed": "ls -las", + "properties": [ + { + "name": "Foo", + "value": "Bar" + } + ] + } + ], + "properties": [ + { + "name": "Foo", + "value": "Bar" + } + ] + } + ], + "timeStart": "2023-01-01T00:00:00+00:00", + "timeEnd": "2023-01-01T00:00:10+00:00", + "workspaces": [ + { + "bom-ref": "workspace-2", + "uid": "workspace-1", + "name": "My workspace", + "aliases": ["default-workspace"], + "description": "Description here", + "resourceReferences": [ + { + "ref": "component-t" + } + ], + "accessMode": "read-write", + "mountPath": "/tmp/workspace", + "managedDataType": "ConfigMap", + "volumeRequest": "requestedVolumeClaim", + "volume": { + "uid": "volume-1", + "name": "My volume", + "mode": "filesystem", + "path": "/", + "sizeAllocated": "10GB", + "persistent": true, + "remote": false + } + } + ], + "runtimeTopology": [ + { + "ref": "component-s", + "dependsOn": [ + "component-r" + ] + } + ], + "properties": [ + { + "name": "Foo", + "value": "Bar" + } + ] + } + ] + } + ] +} diff --git a/tools/src/test/resources/2.0/valid-formulation-2.0.json b/tools/src/test/resources/2.0/valid-formulation-2.0.json index cdba18f6..16e2f9ae 100644 --- a/tools/src/test/resources/2.0/valid-formulation-2.0.json +++ b/tools/src/test/resources/2.0/valid-formulation-2.0.json @@ -213,7 +213,8 @@ "environmentVars": { "LC_ALL": null, "LANG": "en_US.UTF-8", - "0TRUST": "true" + "0TRUST": "true", + "MY_FLAG": "" } } ], From 2da0c2c996a40d1074104d63485c78145156adf2 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 8 Sep 2026 13:31:24 +0200 Subject: [PATCH 4/7] docs Signed-off-by: Jan Kowalleck --- schema/2.0/model/cyclonedx-formulation-2.0.schema.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json index a22a6f39..6287aa5f 100644 --- a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json @@ -872,9 +872,10 @@ }, "environmentVars": { "title": "Environment variables", - "description": "A map of environment variable names to their values. Each value must be a `string` or `null`. Only `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", + "description": "A map of environment variable names to their values. \nEach value must be a `string` or `null`. Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", "type": "object", "additionalProperties": { + "description": "Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", "type": ["string", "null"] }, "examples": [ From 4c2b8e809445cb3305e4b14f3d66461caa5d4a04 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 8 Sep 2026 13:43:24 +0200 Subject: [PATCH 5/7] docs Signed-off-by: Jan Kowalleck --- schema/2.0/model/cyclonedx-formulation-2.0.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json index 6287aa5f..3df0812a 100644 --- a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json @@ -872,7 +872,7 @@ }, "environmentVars": { "title": "Environment variables", - "description": "A map of environment variable names to their values. \nEach value must be a `string` or `null`. Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", + "description": "A map of environment variable names to their values.", "type": "object", "additionalProperties": { "description": "Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", From 0ebfa3951e5cf0976e16debcf14c0ef8687fb4ef Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 8 Sep 2026 13:45:45 +0200 Subject: [PATCH 6/7] docs Signed-off-by: Jan Kowalleck --- schema/2.0/model/cyclonedx-formulation-2.0.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json index 3df0812a..6287aa5f 100644 --- a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json @@ -872,7 +872,7 @@ }, "environmentVars": { "title": "Environment variables", - "description": "A map of environment variable names to their values.", + "description": "A map of environment variable names to their values. \nEach value must be a `string` or `null`. Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", "type": "object", "additionalProperties": { "description": "Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", From 427ef2e23a23ec6d3462c6a4de160f322769ea7a Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Wed, 9 Sep 2026 13:10:17 +0200 Subject: [PATCH 7/7] remarks on name and no constraints, added examples Signed-off-by: Jan Kowalleck --- schema/2.0/model/cyclonedx-formulation-2.0.schema.json | 8 +++++--- tools/src/test/resources/2.0/valid-formulation-2.0.json | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json index 6287aa5f..2272c3e5 100644 --- a/schema/2.0/model/cyclonedx-formulation-2.0.schema.json +++ b/schema/2.0/model/cyclonedx-formulation-2.0.schema.json @@ -872,16 +872,18 @@ }, "environmentVars": { "title": "Environment variables", - "description": "A map of environment variable names to their values. \nEach value must be a `string` or `null`. Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", + "description": "A map of environment variable names to values.\n\nKeys are environment variable names and are intentionally unconstrained by this schema (including names that may be empty or start with non-alphabetic characters).\n\nValues must be either a `string` or `null`:\n- `null` means the variable is absent/unset.\n- `\"\"` (empty string) is a valid, explicit value and does not mean absent.", + "$comment": "While some systems may have restrictions on env var names, others may have not. See this test: `env '=empty-name' '0trust=starts-with-number' python -c 'import os;print(repr(os.environ))'`", "type": "object", "additionalProperties": { - "description": "Only a value `null` means the variable is absent or unset; an empty string (`\"\"`) is a valid value and does not indicate absence.", + "description": "Values must be either a `string` or `null`:\n- `null` means the variable is absent/unset.\n- `\"\"` (empty string) is a valid, explicit value and does not mean absent.", "type": ["string", "null"] }, "examples": [ { "LC_ALL": null, "LANG": "en_US.UTF-8" }, + { "MY_FLAG": "1" }, { "0TRUST": "true" }, - { "MY_FLAG": "" } + { "": "" } ] } } diff --git a/tools/src/test/resources/2.0/valid-formulation-2.0.json b/tools/src/test/resources/2.0/valid-formulation-2.0.json index 16e2f9ae..cf0504dc 100644 --- a/tools/src/test/resources/2.0/valid-formulation-2.0.json +++ b/tools/src/test/resources/2.0/valid-formulation-2.0.json @@ -213,8 +213,9 @@ "environmentVars": { "LC_ALL": null, "LANG": "en_US.UTF-8", + "MY_FLAG": "1", "0TRUST": "true", - "MY_FLAG": "" + "": "" } } ],