Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: documenting external/extraneous dependencies #321

Open
jkowalleck opened this issue Oct 13, 2023 · 17 comments · May be fixed by #586
Open

Feature: documenting external/extraneous dependencies #321

jkowalleck opened this issue Oct 13, 2023 · 17 comments · May be fixed by #586
Assignees
Milestone

Comments

@jkowalleck
Copy link
Member

jkowalleck commented Oct 13, 2023

current state

CycloneDX allows describing components, and the dependency graph.
Each component can have exactly one version, no version range.
Components can be connected in a dependency graph.

feature

Make it possible to describe external dependencies that are not part of a shipped product,
like runtime environments, shared objects and dynamically linked libraries, operating system, hardware (architecture) etc.

use cases

It would enable identifying vendor locks, dependency to outdated external software, and enable DevOps better spec runtime environments.

  • As a customer, I get delivered a software library that includes bundled dependencies. But some external dependencies are not bundled (maybe for legal purposes). These external dependencies need to be provided by the later runtime - by me, the customer.
    The SBOM I get handed out with said library should tell which externals are needed to provide/install, so that I know, how my licensing posture is.
    The extraneous dependencies are not version-locked, they may be in a range of compatible versions.
    — Or, in other words —
    As the author of a software library, I want to provide an SBOM that not only includes the bundled runtime-dependencies, but also the info which runtime-dependencies need to be provided by the user of my library.
  • As a customer, I get delivered a CMS written in PHP. The software delivered contains a bundle of source code and (transitive) dependencies. It does not include the PHP runtime(php-fpm,php-executable, etc), nor required php-extensions and runtime libraries(libssl, etc). see the php/composer spec.
    The SBOM I get handed out with the product should tell which version range of PHP is required to run the software, and which version of required PHP-extensions are required to be installed/enabled. This would help identify risks with the product, that are implied by locks or depending on outdated external software.
  • As a customer, I get delivered a Java program, that can only run properly on a certain range of JavaRuntimeEnvironment.
    The SBOM I get handed out should state this compatibility range.
  • As a customer, I get delivered a binary that is only runnable on Apple's M1/M2 chips.
    The SBOM I get handed out should state the compatible hardware architecture.
  • As a customer, I get delivered a tool, that depends on a certain shared-object or dynamically linked library, which is not part of the delivered package. It needs to be provided by me.
  • As a software manufacturer, I build a product(assembly), which is dynamically linked against libSSL. Therefore, libSSL is a dependency of my product. For licensing reasons, I ship only my product and none of the dynamically linked dependencies.
    My SBOM must express these dependencies (name, version-range, distribution-options(download-URL)), so that my clients know what they need to provide, in order to run the software I've shipped.

possible solution

  • Idea: a new boolean property of Component, to indicate the fact that something is not part of the assembly, but needs to be provided.
    ( "external"? "extraneous"? wording unclear)
    Alternative: add a new value for ComponenScope. Current values: { "required", "optional", "excluded"} -- see Feature: documenting external/extraneous dependencies #321 (comment)
    ❗ see discussion below

  • add an optional property to Component that allowed documenting a version-range, instead of a disjunctive version.
    might utilize existing schema definition for "range", which uses the still unfinished purlspec's VERS: https://github.com/package-url/purl-spec/blob/version-range-spec/VERSION-RANGE-SPEC.rst

  • unchanged: allow components to be vendor-less. Some externals need to be able to describe a product-class, instead of an exact product.

  • document the following, and if possible, then formulate it in schema language:

    • add constraint, that a component's optional version MUST be omitted, if component's is "external"
    • add constraint, that a component's optional version-range MUST be omitted, if component's is not "external"
      reminder: a version-range can also be a value that matches only one exact version - like VERS:1.44.7
  • might utilize new ComponentType "runtime" -- Feature Request: new ComponentType "runtime" #233

Followup

We might add to the authoritative guide, that a BOM for non-bundled/non-shipped MUST NOT have concrete version, but MAY have a version-range(which may match exactly one version), and MUST be flagged as "extraneous".
The same goes to BOMs with the $.metadata.lifecycles[].phase=design.

Discussion

First of, the goal is to allow describing externals, this is not SBOM in the traditional way, this is about the capability of CycloneDX.

howto name?

options:

  • external?
  • extraneous?
  • yet another option?

how mark "externals"

Question: Where to put a marker that signals the fact that something is external?
Is scope the best place to put this? No! What if something is eternal and optional at the same time?!
So it might be better to have a dedicated marker for that purpose.

Question: Which element/object to use?
The existing "Component" seams good, but might be just to broad.
Maybe use "Definitions"' child element, as soon as it is clear? see #321 (comment)

motivation / need

As requested by the community, and canned as tickets

As described by German Cybersecurity Agency BSI

  • Technical Guideline TR-03183 ¶ 5.1

    [orig German] Für eine SBOM, die konform zu dieser Technischen Richtlinie ist, MUSS zumindest für jede zum Lieferumfang gehörende Komponente die rekursive Auflösung von Abhängigkeiten auf jedem Pfad mindestens bis einschließlich der ersten Komponente, die nicht mehr zum Lieferumfang gehört, durchgeführt werden (siehe Abschnitt 6.1.4)
    [transl English] For an SBOM that is compliant with this Technical Guideline, at least for each component that is part of the component included in the scope of supply, the recursive resolution of dependencies on each path at least up to and including the first out-of-scope component, be performed (see 6.1.4)

As a solution for the package registries

Package registries that (are planning to) provide SBOMS for libraries which do not include all their dependencies. These SBOMs pretend/predict the dependencies as "components" but in fact, the components are not packaged with these libraries, but need to be fetched additionally. It these package registries would make unresolved dependencies as "extraneous", that would be great.

@jkowalleck jkowalleck changed the title Enable documenting _external_ dependencies Feature: documenting external dependencies Oct 13, 2023
@jkowalleck jkowalleck changed the title Feature: documenting external dependencies Feature: documenting external/extraneous dependencies Oct 15, 2023
@stevespringett
Copy link
Member

A PR would be good. Or at a minimum, a prototype on this ticket describing a proposal. FYI, v1.6 has a new top-level element called definitions which are not included in the inventory of the BOM but can still be referenced. That may be useful here, although there are likely other ways to achieve this as well.

@jkowalleck
Copy link
Member Author

jkowalleck commented Oct 16, 2023

[...] v1.6 has a new top-level element called definitions which are not included in the inventory of the BOM but can still be referenced.

Did not find "definitions" in the current (draft) state of 1.6-dev nor any of (the work-in-progress) branches that eventually will be merged into 1.6-dev.
As soon as it is there, I will see if it fits the idea. No rush.

[...] although there are likely other ways to achieve this as well.

👍 I might sketch out possible schema changes and new test cases based on "component".
This might not be best solution, but should help visualize the concept.

@stevespringett
Copy link
Member

The definitions property is here: https://github.com/CycloneDX/specification/blob/1.6-dev-attestations/schema/bom-1.6.schema.json#L473

It currently only consists of standards, but can (and likely should) be expanded to include additional objects (e.g. licenses, components, services, etc)

jkowalleck added a commit to jkowalleck/fork_CycloneDX-specification that referenced this issue Oct 22, 2023
jkowalleck added a commit to jkowalleck/fork_CycloneDX-specification that referenced this issue Oct 22, 2023
@jkowalleck
Copy link
Member Author

jkowalleck commented Oct 23, 2023

re: #321 (comment)

A PR would be good.

here is a sketch: #326

@jkowalleck
Copy link
Member Author

read: #293 (comment)

scope is a way to include a component but not assert that that component is delivered or included in the inventory. For example, you may want to include Windows XP Embedded as a component, mark the scope as excluded, as a way to indicate that the entire stack will include the OS, but that the supplier does not provide it with the software. There is ongoing discussions within CISA on how to specify runtime requirements. Expect this capability to expand in the future.

@jkowalleck jkowalleck linked a pull request Dec 27, 2023 that will close this issue
3 tasks
@jkowalleck jkowalleck self-assigned this Jan 16, 2024
jkowalleck added a commit to jkowalleck/fork_CycloneDX-specification that referenced this issue Jan 16, 2024
@mtsfoni
Copy link

mtsfoni commented Jan 29, 2024

read: #293 (comment)

scope is a way to include a component but not assert that that component is delivered or included in the inventory. For example, you may want to include Windows XP Embedded as a component, mark the scope as excluded, as a way to indicate that the entire stack will include the OS, but that the supplier does not provide it with the software. There is ongoing discussions within CISA on how to specify runtime requirements. Expect this capability to expand in the future.

Does this invalidate your change request? Sound like scope was meant exactly as a solution to the depicted problem?

However, scope cannot currently not describe a difference between an extraneous optional and required component.

What about dev dependencies?
In the dotnet tool those currently get marked as external. But they are external for a completely different reason. Maybe there should be a better distinction of why a component is external? (because it can be added, because it must be added/be installed on the target system or because it was once used in the production process but is not part of the scope of delivery?)

@jkowalleck jkowalleck modified the milestones: 1.6, 1.7 Jan 29, 2024
@jkowalleck
Copy link
Member Author

Dropped this issue/request from the 1.6 milestone goals, and moved it to 1.7 for the following reasons: did not finish in time for 1.6, needs further discussion.

@jkowalleck
Copy link
Member Author

@Jerod might have additional ideas how he implemented a process to get around todays limitations ...


@stevespringett proposed to

  • add another dependency-relation like "depends", "provides" -- to identify runtime dependencies.
    We could add components of type "platform" for runtime/os etc ...
    But this idea would be conflicting with the ssl library that is to be provided by the runtime -- which is a library, not a platform ...
  • add an option to allow alternative (oneOf) version ranges. -- possible via PURL/vers -- see above
  • add grouping in "dependencies" to allow alternative (oneOf) component branches ...

need to think about all of this. will come back later. :-)

@jkowalleck
Copy link
Member Author

now that the enum cases for scope are documented, lets rethink possible solutions.

"meta:enum": {
"required": "The component is required for runtime",
"optional": "The component is optional at runtime. Optional components are components that are not capable of being called due to them not be installed or otherwise accessible by any means. Components that are installed but due to configuration or other restrictions are prohibited from being called must be scoped as 'required'.",
"excluded": "Components that are excluded provide the ability to document component usage for test and other non-runtime purposes. Excluded components are not reachable within a call graph at runtime."
},

@vetsin
Copy link

vetsin commented Mar 12, 2024

I've seen a need for this when dealing with Java -- you release a jar or war artifact and it defines dependencies. There is no indication on if they're what you say you need or what's packaged within/distributed with what you're describing.

My thoughts:

  • scope is ambiguous in that different languages/build tools treat the concept differently, but they all do seem to result into the three distinct dx scope's above
  • Currently a dependency or component entry does not state if it is in within the described artifact or not
  • If not within in the artifact itself, the true runtime dependency can differ from the sbom
  • If we have provided a dependency or not does not change the scope of it -- mayhaps they are still optional.
  • Currently the DX format is not making a distinction between what 'we say we need' and 'what we actually have'
  • Using OBOM instead does not seem to solve the problem

That being said I am for a provides-relation instead of any extraneous/intrinsic declaration. The component states what it states, but it does/cannot reflect what the consumer will do with it. If the component is compiled, it seems like it should state the specific version it was compiled with (even if it doesn't provide it). If it's not compiled it seems fair to state what you tested with. Why does it matter if i can use Foo>=1.0 when i tested with 1.0 and when you use me, you provide Foo? Even if we state Foo>=1.0 its disingenuous to think that 2.0 may cause regressions and not work anyway.

I know there's some edge cases in that maybe I tested with python 3.6, 3.8, and 3.11 -- do i list them all as dependencies? I can't attest 3.9 works, but do i include it anyway?

@jkowalleck
Copy link
Member Author

@DarthHater could you check #321 (comment) and see how CDX fits in the Java world when it comes to "externals"?

@sschoeling
Copy link

So, with 1.6 out of the way now, I'd like to revisit this. So far scope doesn't seem to work out and ComponentType "runtime" from #233 doesn't tell whether it's included or not either. @jkowalleck : Following the list you put into cyclonedx-php-composer/#435 I think it comes down to:

  • being able to flag a dependency as "not included" in the distribution
  • being able to either have version ranges or some kind of generic dependency that is implemented by concrete releases (akin to dpkg "provides")

and both of these are orthogonal to scope (whether the component is used for runtime or other purposes) and ComponentType.

@ppkarwasz
Copy link

Note that in the current 1.6 version, it is already possible to differentiate external and embedded dependencies:

This mechanism is very expressive: you can say the foo depends on an external bar, which contains baz. However, since the exact version of bar will only be known at runtime, you can not be sure that bar will contain baz (you can choose a version that does not contain it).

@stevespringett
Copy link
Member

CISA and NTIA before it, had discussed the possibility of documenting runtime requirements. These are external dependencies that must be met, which will add to the dependency graph when installed. For example:

Acme Application requires one of:

  • Windows Server 2019 or higher
  • Redhat Enterprise Linux 9 or higher
    -- and --
  • Postgres SQL v9 or higher
  • Oracle Database 21c or higher
    -- and --
  • Service: http://maps.google.com/

In my mind, these are runtime or environmental requirements. This is very different from scope. This solution would describe what we need, and the OBOM currently describes what we have.

@hboutemy
Copy link

hboutemy commented Jan 20, 2025

I've seen a need for this when dealing with Java -- you release a jar or war artifact and it defines dependencies. There is no indication on if they're what you say you need or what's packaged within/distributed with what you're describing.

in fact, when releasing a war, dependencies are included in WEB-INF/lib, not really "extraneous": what is not described is if a runner is necessary (Java Runtime, servlet runner), which would be "extraneous", not called "dependency" in build tools and in fact not really represented, but "runtime environment prerequisites" in deployment documentation

another typical case in Java is when you build and ship a library: dependencies are not shipped with the library, so not "part of shipped product". Can we really consider them "extraneous"? That's what #578 tried to challenge, that has common aspects to this issue (like some notion of version range), and some differences (extraneous or not extraneous).

I'm sure it's the same when someone builds and ship a Javascript / npm library: typically, in their package.json,

  • they define a range for their dependencies
  • they don't ship their dependencies

Then the SBOM generated at build time does not represent what they ship but what they built with, particularly precise versions of components = the built-time resolution of dependencies version range defined in package.json

@jkowalleck
Copy link
Member Author

jkowalleck commented Jan 20, 2025

another typical case in Java is when you build and ship a library: dependencies are not shipped with the library, so not "part of shipped product".
[...]
I'm sure it's the same when someone builds and ship a Javascript / npm library: typically, in their package.json,

* they define a range for their dependencies
* they don't ship their dependencies

that is literally what the first use case of this ticket describes.

Was this not clear? please help me improve the ticket, could you provide a use-case description that makes this explains this better, so I could replace the existing description?

PS: added the following alt description:

As the author of a software library, I want to provide an SBOM that not only includes my bundled runtime-dependencies, but also the info which dependencies need to be provided by the user of that library.

@jkowalleck
Copy link
Member Author

FYI: i am currently working on this.
see #586

jkowalleck added a commit that referenced this issue Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment