cyclonedx / cyclonedx-gomod Goto Github PK
View Code? Open in Web Editor NEWCreates CycloneDX Software Bill of Materials (SBOM) from Go modules
Home Page: https://cyclonedx.org
License: Apache License 2.0
Creates CycloneDX Software Bill of Materials (SBOM) from Go modules
Home Page: https://cyclonedx.org
License: Apache License 2.0
Currently, the program doesn't output anything besides errors or the BOM itself. For debugging and observation purposes, it might be helpful to know what the program is doing.
See golang/go#37475
We're currently only capturing the Go version in app
and mod
.
Ideally we would also include info about the Go compiler, like hashes of go
and most likely more.
We'll need to do some research as to what properties make sense to include in the SBOM.
This was part of the feedback in https://zt.dev/posts/analysis-cyclonedx-gomod-sbom/
cyclonedx-go v0.4.0 introduced support for spec v1.3.
Using the beta 2 of cyclonedx-gomod on my public lxkns Github project seems to show a problem with license determination when the package import path contains version information. Of course, I might be mistaken here and this might not be a problem of cyclonedx-gomod but instead of the license checking dependency.
When running cyclonedx-gomod app -json -output ../lxkns-bom.json -main cmd/lxkns/ -licenses -std -verbose .
on the checked-out lxkns repository, the bom entry for cenkalti's backoff module catches my eye:
{
"bom-ref": "pkg:golang/github.com/cenkalti/backoff/[email protected]",
"type": "library",
"name": "github.com/cenkalti/backoff/v4",
"version": "v4.1.1",
"scope": "required",
"hashes": [
{
"alg": "SHA-256",
"content": "1b61c07c09af9bf19c29a9f6a0e42905739ddad4f61b9ed99d91966b53f13c14"
}
],
"purl": "pkg:golang/github.com/cenkalti/backoff/[email protected]",
"externalReferences": [
{
"url": "https://github.com/cenkalti/backoff",
"type": "vcs"
}
]
},
A check with the repository shows a LICENSE file for branch v4: MIT license.
Is this just a detection problem or instead a versioned import path issue?
The current implementation only considers lightweight Git tags and will use a pseudo version if none were found.
Relates to #55.
Overall, there are three ways in which SBOMs can be generated for Go projects:
go list -m
, go mod why -m
)
go list -deps ./cmd/myapp
)
go version -m ./myapp.exe
)
The CLI should clearly separate these cases, especially since some options will not apply to all ways.
Which way is chosen should be an active decision, as each way has implications that users should be aware of.
go mod graph
apparently isn't really intended for generating an accurate dependency graph (that is, differentiating between direct and transitive dependencies). This becomes painfully obvious when running that command on modules that have go 1.17
in their go.mod
file (golang/go#47648).
The output of go list -deps -json
has a .Deps
field which we could use to build a dependency graph instead. But go list
as of today is still subject to build constraints. That will work for the new app
command, but not for mod
.
There is however an effort of supporting "unconstrained" use of go list
: golang/go#42504
Possible solutions:
/usr/src/acme-module
cmd/acme-app/main.go
pkg:golang/github.com/acme-inc/[email protected]#cmd/acme-app
cdx:gomod:application:name=acme-app
To ease integration into existing GitHub Actions workflows, we could provide an official action similar to https://github.com/CycloneDX/gh-dotnet-generate-sbom
We're currently using go list -json -m all
to get all modules a given module depends on.
However, that doesn't work with vendoring:
$ go mod vendor
...
$ go list -json -m all
go list -m: can't compute 'all' using the vendor directory
Instead, we can use go mod vendor -v
to list all vendored modules. For example, for cyclonedx-go
:
$ go mod vendor -v
# github.com/bradleyjkemp/cupaloy/v2 v2.6.0
## explicit
github.com/bradleyjkemp/cupaloy/v2
github.com/bradleyjkemp/cupaloy/v2/internal
# github.com/davecgh/go-spew v1.1.1
github.com/davecgh/go-spew/spew
# github.com/pmezard/go-difflib v1.0.0
github.com/pmezard/go-difflib/difflib
# github.com/stretchr/testify v1.7.0
## explicit
github.com/stretchr/testify/assert
github.com/stretchr/testify/require
# gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
gopkg.in/yaml.v3
Lines prefixed with #
denote modules as we'd see them with go list
.
Lines following ## explicit
are packages inside those modules, which we don't care about for now.
Because the Go (major) version can influence package/module selection, and we currently only build container images for a single Go version, the images are not as useful as they could be.
We should provide images for each officially supported Go version (that is, for the two latest major versions). The Go version included should be expressed via tags, e.g. v1-go1.16
, v1.1.0-go1.17
and so on. Minor- and bugfix versions of Go are not relevant for this use case.
Depending on which systems consume the BOM, having the v
prefix in the version may or may not be compatible with whatever those systems try to accomplish. Sonatype's OSSIndex for example expects versions without that prefix, and will raise false positives otherwise.
There should be a command line flag that allows for stripping of that prefix.
Relates to #2
The choice made sense for cases where projects would fork or otherwise modify a given module, but replace
is also commonly used for version pinning. Kubernetes does this, for example.
It would be nice if the building block can be importable.
I think if the necessary directories are put into pkg
dir instead of internal
(mostly all of them except cli
), it should be work.
We should have integration tests to verify that generated BOMs
Rough idea:
git clone
a specific tag of any module with a reasonable amount of dependenciesgo run main.go -module /path/to/module -o itest-bom.xml
cyclonedx validate --input-file itest-bom.xml --fail-on-error
#40 causes issues with some SBOM ingestion tools that can't currently deal with evidences (e.g. DependencyTrack/dependency-track#1241).
We should add an -assert-licenses
option that moves detected licenses from components/component/evidence/licenses
back to components/component/licenses
.
https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables
https://docs.drone.io/pipeline/environment/reference/
https://docs.github.com/en/actions/reference/environment-variables#default-environment-variables
https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables
https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
As suggested in this repository, I tried to reach out first via Stackoverflow cyclonedx-gomod "DBG dependency not found ...", produces almost empty BOM but I got no responses there for some time. If I got the wrong part of the SO fleet, then please direct me to the correct part of the SO community. I noticed that there is no tag dedicated to cylconedx.
Anyway: I'm unsuccessfully trying to generate a BOM from a Go module using cyclonedx-gomod. I've downloaded a binary version from the project's repository https://github.com/CycloneDX/cyclonedx-gomod/releases/tag/v1.0.0-alpha.4, for ARM64 to use on a Raspi 4 with Ubuntu.
Now, when I run cyclonedx-gomod app -json -output mymodule-bom.json -main cmd/myapp/main.go -licenses -std -verbose .
while inside my module, I see lots of DBG complaints about dependencies, such as:
dependency not found dependant=github.com/thediveo/[email protected] dependency=github.com/spf13/[email protected]
However, these are public repositories and they have already been used for building, so they're present in the Go module cache. The resulting BOM JSON file consists of maybe four or five entries, but is completely useless because it lacks all dependencies.
What am I doing wrong?
Because running both commands is a prerequisite anyway.
By using go list
, we get to know which packages are actually used, but we currently don't include this information in the SBOM.
Using the info we already have, we can construct a structure according to modules > packages > files
, for example:
{
"bom-ref": "pkg:golang/github.com/ProtonMail/[email protected]",
"type": "library",
"name": "github.com/ProtonMail/go-crypto",
"version": "v0.0.0-20210428141323-04723f9f07d7",
"scope": "required",
"hashes": [
{
"alg": "SHA-256",
"content": "62825b7a72bd0baed29339037e642e5659f4e328078f7be365f77d14bf869904"
}
],
"purl": "pkg:golang/github.com/ProtonMail/[email protected]",
"components": [
{
"bom-ref": "pkg:golang/github.com/ProtonMail/go-crypto#[email protected]",
"type": "library",
"name": "bitcurves",
"version": "v0.0.0-20210428141323-04723f9f07d7",
"scope": "required",
"purl": "pkg:golang/github.com/ProtonMail/go-crypto#[email protected]",
"components": [
{
"type": "file",
"name": "bitcurve.go",
"version": "v0.0.0-2472c8e5f796",
"scope": "required",
"hashes": [
{
"alg": "MD5",
"content": "b4689d8871aa46bb387ef148944f8da8"
},
{
"alg": "SHA-1",
"content": "2472c8e5f796d8463738938fea98398dabba08ad"
}
// ...
]
}
]
}
]
}
Including the packages should probably be enabled per default, but including individual files should still be optional.
It's common to execute tools in CI in a container. Providing an official Docker image would make CI integration a bit easier.
Docker images can be built and pushed with GoReleaser as well, see https://goreleaser.com/customization/docker/
Go binaries are statically linked and include the standard library / runtime. The PURL of the standard library would be pkg:golang/[email protected]
.
For application modules it makes sense to include this in the BOM. For library modules, it doesn't, because no binary is built. This should either be coupled to specific ComponentType
s or an opt-in feature.
The go
directive in go.mod
defines the minimum version required to build the module. An exact version probably needs to be acquired by calling go version
.
Someone on Stack Overflow ran into an issue with v1.0.0-alpha.4
where the SBOM generated by app
contains only a small subset of modules: https://stackoverflow.com/questions/69329242/cyclonedx-gomod-dbg-dependency-not-found-produces-almost-empty-bom
The issue appears to be that the bulk of packages is imported by files in package main
that are not main.go
. All files in cmd/lxkns
are in package main
. Because we only consider a single main file though, we're missing a lot of imports.
Most applications these days have a single main.go
that simply calls a function from another package. Meaning package main
doesn't really contain anything besides func main()
.
We can't just use patterns like cmd/lxkns/...
, because there are cases where this won't yield the desired result either, providing more dependencies than we actually care about (especially if main.go
happens to be in the module's root dir).
We could Walk
the directory containing -main
and select all .go
files with package main
declaration, and pass them to go list
. However, my testing revealed that this then bypasses build constraints (imports by main_windows.go
are loaded even though GOOS
is set to linux
etc.).
I've generated a BOM of an application with both -std
as well as without: with -std
there's only a single Go stdlib component included, without it all included stdlib components seem to be missing from the BOM. I'm using the recent 1.0.0 version, compiled from master only few days ago.
For instance, crypto packages are thus missing from the BOM generated on the basis of the sources, yet scanning the binary reveals that at least some of the crypto packages from the Go stdlib have been bound into the final binary.
Am I doing something wrong or missing some CLI flag that will include stdlib packages en detail into the BOM generated from the sources?
Quoting the README:
There is currently no standard way for developers to declare their module's license.
Detecting licenses based on files in a repository is a non-trivial task, which is why cyclonedx-gomod
uses pkg.go.dev to resolve module licenses (please read their license disclaimer).While pkg.go.dev's license matching may be accurate most of the time, BOMs should state facts.
This is why license resolution is an opt-in feature (using the -licenses flag).
If you are a vendor and legally required to provide 100% accurate BOMs, do not use this feature.
We need to include this disclaimer, because the components/licenses
node we're currently using represents an assertion.
Since v1.3 of the spec, there's now support for license evidence. Given that we perform error-prone license detection (or leverage services that do it for us), we should put detection results into the component/evidence/licenses
node instead.
CycloneDX supports dependency graphs.
Coincidentally, Go's go mod graph
command provides a module graph in pretty much the same structure:
$ go mod graph
github.com/CycloneDX/cyclonedx-gomod github.com/CycloneDX/[email protected]
github.com/CycloneDX/cyclonedx-gomod github.com/google/[email protected]
github.com/CycloneDX/cyclonedx-gomod golang.org/x/[email protected]
github.com/CycloneDX/[email protected] github.com/bradleyjkemp/cupaloy/[email protected]
github.com/CycloneDX/[email protected] github.com/stretchr/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
github.com/bradleyjkemp/cupaloy/[email protected] github.com/davecgh/[email protected]
github.com/bradleyjkemp/cupaloy/[email protected] github.com/pmezard/[email protected]
github.com/bradleyjkemp/cupaloy/[email protected] github.com/stretchr/[email protected]
github.com/bradleyjkemp/cupaloy/[email protected] github.com/stretchr/[email protected]
github.com/stretchr/[email protected] github.com/davecgh/[email protected]
github.com/stretchr/[email protected] github.com/pmezard/[email protected]
github.com/stretchr/[email protected] github.com/stretchr/[email protected]
github.com/stretchr/[email protected] gopkg.in/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
github.com/stretchr/[email protected] github.com/davecgh/[email protected]
github.com/stretchr/[email protected] github.com/pmezard/[email protected]
github.com/stretchr/[email protected] github.com/stretchr/[email protected]
github.com/stretchr/[email protected] gopkg.in/[email protected]
gopkg.in/[email protected] gopkg.in/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
golang.org/x/[email protected] golang.org/x/[email protected]
An MVP implementation could simply take this output, convert <path>@<version>
expressions to package URLs and be done with it.
Will have to figure out how to deal with replacements though. We currently treat replaced modules as ancestors in the replacement component's pedigree. However, the module graph will still reference the replaced module, not the replacement.
Hello ๐
I maintain ko
, a tool for building Go applications into lightweight container images. It's got somewhat basic support today for generating "SBOMs", which is effectively to extract the Go binary and run go version -m
on it, and print that to stdout. It's not pretty but it works.
I'd love to augment this with "real" SBOM support in as many formats as I can get, and have SBOMs generated and uploaded to registries, instead of just extracted from binaries at request-time. (Or maybe some future where we ensure the binary's embedded information matches the pushed SBOM? Unclear.)
Anyway, I'd love to be able to skip writing code to translate the output of go version -m
to CycloneDX SBOM if I can avoid it, and I see that this repo already has that written in an internal package, in LoadModulesFromBinary
:
cyclonedx-gomod/internal/gomod/binary.go
Line 29 in b5f7b63
Is there any interest in moving this to a non-internal
package, where I could vendor it into ko
?
Module hashes take coordinates (path@version
) into consideration.
We trim the +incompatible
suffix and optionally the v
prefix from versions, but we do it before we calculate the hashes. This causes the resulting hashes to be different if the version was modified, even though none of the module's file have changed.
Version normalization really is just cosmetics. cyclonedx-gomod itself doesn't need it, and neither does Go. For this reason, it should happen just before writing the BOM.
Also, maybe we shouldn't trim +incompatible
at all.
Just use raw git
commands instead. go-git
introduces a lot of dependencies we don't otherwise use. Our usage of Git functions is also very shallow, nothing that would justify import such a powerful lib.
See discussion in #100 starting at #100 (comment).
Possible solutions:
app
and mod
. Opting out of license detection is one use-case of manygo mod vendor
)go get
, go mod download
etc. and may still produce noise in SAST toolsgo-license-detector
and don't see a reason to switchgo version -m
can't currently deal with macOS universal binaries.
However, with Go 1.18, we will get the necessary tools to implement support for them ourselves, using buildinfo.Read(io.ReaderAt)
.
Also, Go has had support for reading fat mach-o binaries since 1.3 using macho.OpenFat
.
I tinkered a bit, and it's now almost trivial to get go version -m
results for all embedded binaries:
import (
"debug/buildinfo"
"debug/macho"
"io"
"log"
"os"
)
func LoadBuildInfo118(binaryPath string) error {
ff, err := macho.OpenFat(binaryPath)
if err != nil {
return err
}
ff.Close()
binaryFile, err := os.Open(binaryPath)
if err != nil {
return err
}
defer binaryFile.Close()
for i, arch := range ff.Arches {
header := ff.Arches[i].FatArchHeader
bi, err := buildinfo.Read(io.NewSectionReader(binaryFile, int64(header.Offset), int64(header.Size)))
if err != nil {
return err
}
log.Printf("%s: %s@%s (%s)", arch.Cpu, bi.Main.Path, bi.Main.Version, bi.Main.Sum)
}
return nil
}
Example output for the universal binary of goreleaser:
2022/02/05 12:34:35 CpuAmd64: github.com/goreleaser/[email protected] (h1:gW8sdjDEo2H2ZgcJmWsNZUcaJSD4MLvA/bw7+GYQ8kU=)
2022/02/05 12:34:35 CpuArm64: github.com/goreleaser/[email protected] (h1:gW8sdjDEo2H2ZgcJmWsNZUcaJSD4MLvA/bw7+GYQ8kU=)
Still torn on what the correct output would be though. Two SBOMs? A merged SBOM?
It should be possible to verify that a given module matches the facts stated in the corresponding BOM.
Possible factors to consider when verifying:
Example usage could be:
$ cyclonedx-gomod verify --module . --bom cyclonedx-gomod-v0.3.0.bom.xml
Verification will be supported for Go modules only. No other ecosystems will be considered.
Go embeds module version information in the binaries it builds. go version
can be used to retrieve that information:
usage: go version [-m] [-v] [file ...]
[...]
The -m flag causes go version to print each executable's embedded
module version information, when available. In the output, the module
information consists of multiple lines following the version line, each
indented by a leading tab character.
[...]
See https://golang.org/cmd/go/#hdr-Print_Go_version
We can use this to build a SBOM.
Implement variant 1 from #20 (comment)
v0.8.0 removed the requirement to run go mod download
, because go mod why
downloads all required modules anyway (and doesn't modify go.sum
, as go mod download
does).
However, for some reason, dirhash.HashDir
fails for modules that have been downloaded by go mod why
prior:
2021/06/02 08:43:06 generating sbom
2021/06/02 08:43:06 enumerating modules
go: downloading go.uber.org/zap v1.17.0
go: downloading github.com/hashicorp/go-version v1.3.0
go: downloading github.com/go-chi/chi/v5 v5.0.3
go: downloading github.com/spf13/cobra v1.1.3
go: downloading github.com/spf13/viper v1.7.1
go: downloading gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
go: downloading github.com/stretchr/testify v1.7.0
go: downloading github.com/go-resty/resty/v2 v2.6.0
go: downloading github.com/google/uuid v1.2.0
go: downloading github.com/fsnotify/fsnotify v1.4.7
go: downloading github.com/hashicorp/hcl v1.0.0
go: downloading github.com/magiconair/properties v1.8.1
go: downloading github.com/mitchellh/mapstructure v1.1.2
go: downloading github.com/pelletier/go-toml v1.2.0
go: downloading github.com/spf13/afero v1.1.2
go: downloading github.com/spf13/cast v1.3.0
go: downloading github.com/spf13/jwalterweatherman v1.0.0
go: downloading github.com/spf13/pflag v1.0.5
go: downloading github.com/subosito/gotenv v1.2.0
go: downloading gopkg.in/ini.v1 v1.51.0
go: downloading gopkg.in/yaml.v2 v2.4.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading github.com/inconshreveable/mousetrap v1.0.0
go: downloading go.uber.org/atomic v1.7.0
go: downloading go.uber.org/multierr v1.6.0
go: downloading golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
go: downloading golang.org/x/text v0.3.3
go: downloading golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
2021/06/02 08:43:17 normalizing module versions
2021/06/02 08:43:17 determining version of main module
2021/06/02 08:43:17 converting main module xxx
2021/06/02 08:43:17 converting module xxx
2021/06/02 08:43:17 converting module github.com/fsnotify/[email protected]
2021/06/02 08:43:17 generating sbom failed: failed to convert module github.com/fsnotify/[email protected]: failed to calculate h1 hash: open /pkg/mod/cache/download/cloud.google.com/go/@v/list: no such file or directory
Strangely enough, the file at /pkg/mod/cache/download/cloud.google.com/go/@v/list
exists, and so does github.com/fsnotify/[email protected]
in the module cache. When running cyclonedx-gomod
again, it works.
As of v0.5.0, golang.org/x/mod
now has utility functions to generate pseudo versions that consider the previous versions: https://pkg.go.dev/golang.org/x/[email protected]/module#PseudoVersion
I tried out this tool on an open source repo I maintain called cosign
. The repo is here: https://github.com/sigstore/cosign
I ran the tool at cosign commit 749c7e3e5d80f3fa976f31084317a556718c3e54
.
The cyclonedx-gomod version I used was 35b214b43eabfdf6b47499427b93c23ae1f903aa
.
I ran: $ cyclonedx-gomod -json > gomod.json
The resulting sbom is available here: https://gist.github.com/dlorenc/17c0582acb0560807e561e436cf76a1e
The tool worked, but a quick look at the data shows some packages that are not included in my application. For example:
"components": [
{
"bom-ref": "pkg:golang/bazil.org/[email protected]",
"type": "library",
"name": "bazil.org/fuse",
"version": "v0.0.0-20180421153158-65cc252bf669",
"scope": "required",
"hashes": [
{
"alg": "SHA-256",
"content": "14d091a578aab86d5aa32a9c2165459a94d2295731d9b403dfcb9965e1ad765c"
}
],
"purl": "pkg:golang/bazil.org/[email protected]"
},
bazil.org/fuse does not appear in my application at all, including it's dependency tree. I do not vendor my code normally (I saw the warnings about this tool only partially supporting vendoring). But if I run go mod vendor
, I do not see bazil.org/fuze anywhere in the vendor tree. I can build and run my code without that package, so I think it's a bug that it appears in the SBOM.
You can see a test branch with the vendor directory here: https://github.com/dlorenc/cosign/tree/vendor/vendor/github.com
Note that this is just the first component in the list and the first that I checked. There may be other things missing or incorrect.
Am I misunderstanding the fields? Is there some reason this is included?
Note: This does appear in my go.sum file, but not in the vendor directory and it never makes it into my compiled application, as can be shown by the vendor directory. This means it is used somehow in the dependency constraint calculation by "go mod".
Note that there are
Use the new app
command to generate an SBOM for every binary that is built by the release workflow.
Go currently has no way to define a module's license. pkg.go.dev does display license information, but that's not really accurate and relies on Google's license detection.
Similarly, licenses for modules hosted on GitHub could be retrieved using the GitHub REST API. Responses of the /repos
endpoint includes license information.
Another way would be to use license detection mechanisms like the one provided by go-license-detector. Although the detection appears to be quite good, we can't be 100% sure. The question arises if including licenses in the BOM that possibly aren't accurate is better than not including any at all.
For starters, this could be implemented as opt-in feature.
We don't currently scan the files of a module for licenses.
As pointed out in the article below however, it is totally possible that some files are licensed differently than the module.
A simple suggested solution was to scan the first X lines of a file for the SPDX-License-Identifier
field.
This was part of the feedback in https://zt.dev/posts/analysis-cyclonedx-gomod-sbom/
Once we support a "distribution mode" (for a lack of better terms; see #20 (comment) - variant 1), we need to express for which build constraints the SBOM has been generated.
Constraints could be included via properties of the main component, e.g.
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:5f05aa95-d0d9-43aa-bf11-7c9ee2ce9325" version="1">
<metadata>
<component>
<name>github.com/CycloneDX/cyclonedx-gomod</name>
<version>vX.X.X</version>
<properties>
<property name="cdx:gomod:buildconstraint:goos">linux</property>
<property name="cdx:gomod:buildconstraint:goarch">amd64</property>
<property name="cdx:gomod:buildconstraint:tags">tag1,tag2,tag3</property>
</properties>
</component>
</metadata>
<components></components>
</bom>
Or, alternatively:
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:5f05aa95-d0d9-43aa-bf11-7c9ee2ce9325" version="1">
<metadata>
<component>
<name>github.com/CycloneDX/cyclonedx-gomod</name>
<version>vX.X.X</version>
<properties>
<property name="cdx:gomod:buildconstraint:goos">linux</property>
<property name="cdx:gomod:buildconstraint:goarch">amd64</property>
<property name="cdx:gomod:buildconstraint:tag">tag1</property>
<property name="cdx:gomod:buildconstraint:tag">tag2</property>
<property name="cdx:gomod:buildconstraint:tag">tag3</property>
</properties>
</component>
</metadata>
<components></components>
</bom>
This requires cyclonedx-go
to support v1.3 of the spec (CycloneDX/cyclonedx-go#1).
Additionally, these constraints could be added to the main component's PURL as well, via qualifiers:
pkg:golang/github.com/CycloneDX/[email protected]?goos=windows&goarch=amd64&tags=tag1,tag2,tag3
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.