pax_global_header 0000666 0000000 0000000 00000000064 15067437333 0014525 g ustar 00root root 0000000 0000000 52 comment=72e4629d580624c7d6bd815e2d209a0a62d08047
cyclonedx-go-0.9.3/ 0000775 0000000 0000000 00000000000 15067437333 0014131 5 ustar 00root root 0000000 0000000 cyclonedx-go-0.9.3/.github/ 0000775 0000000 0000000 00000000000 15067437333 0015471 5 ustar 00root root 0000000 0000000 cyclonedx-go-0.9.3/.github/dependabot.yml 0000664 0000000 0000000 00000001066 15067437333 0020324 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
reviewers:
- "CycloneDX/go-maintainers"
schedule:
# We only have one Dockerfile for the Gitpod workspace
# right now, and it's not critical to be super up-to-date.
interval: "monthly"
- package-ecosystem: "gomod"
directory: "/"
reviewers:
- "CycloneDX/go-maintainers"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
reviewers:
- "CycloneDX/go-maintainers"
schedule:
interval: "daily"
cyclonedx-go-0.9.3/.github/workflows/ 0000775 0000000 0000000 00000000000 15067437333 0017526 5 ustar 00root root 0000000 0000000 cyclonedx-go-0.9.3/.github/workflows/ci.yml 0000664 0000000 0000000 00000003061 15067437333 0020644 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
permissions: { }
jobs:
licensecheck:
name: License Check
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
- name: Check license headers
uses: apache/skywalking-eyes@5c5b974209f0de5d905f37deb69369068ebfc15c # tag=v0.7.0
with:
config: .licenserc.yml
lint:
name: Lint
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
with:
go-version: "1.22"
check-latest: true
cache: false
- name: Run golangci-lint
uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # tag=v6.2.0
with:
version: latest
args: --verbose
test:
name: Test
timeout-minutes: 5
runs-on: ubuntu-latest
strategy:
matrix:
go:
- "1.20"
- "1.21"
- "1.22"
steps:
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
with:
go-version: ${{ matrix.go }}
check-latest: true
- name: Checkout Repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
- name: Test
run: make test
cyclonedx-go-0.9.3/.github/workflows/goreleaser.yml 0000664 0000000 0000000 00000001660 15067437333 0022404 0 ustar 00root root 0000000 0000000 name: GoReleaser
on:
push:
tags:
- 'v*'
permissions: { }
jobs:
goreleaser:
name: Release
timeout-minutes: 5
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
with:
go-version: "1.22"
check-latest: true
- name: Install cyclonedx-gomod
uses: CycloneDX/gh-gomod-generate-sbom@efc74245d6802c8cefd925620515442756c70d8f # tag=v2.0.0
with:
version: v1
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # tag=v6.3.0
with:
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
cyclonedx-go-0.9.3/.gitignore 0000664 0000000 0000000 00000000560 15067437333 0016122 0 ustar 00root root 0000000 0000000 # Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# IntelliJ / GoLand
.idea/*
!.idea/icon.svg
*.iml
# SBOMs generated during CI
/bom.json
/bom.xml
cyclonedx-go-0.9.3/.gitpod.yml 0000664 0000000 0000000 00000000144 15067437333 0016217 0 ustar 00root root 0000000 0000000 image:
file: Dockerfile.gitpod
tasks:
- init: make build
vscode:
extensions:
- golang.Go cyclonedx-go-0.9.3/.golangci.yml 0000664 0000000 0000000 00000000137 15067437333 0016516 0 ustar 00root root 0000000 0000000 linters:
enable:
- asciicheck
- errorlint
- gofmt
- gosec
- wastedassign
cyclonedx-go-0.9.3/.goreleaser.yml 0000664 0000000 0000000 00000001507 15067437333 0017065 0 ustar 00root root 0000000 0000000 builds:
# This is a library project, we don't want to build any binaries.
# Building and testing is performed in the CI workflow
- skip: true
release:
prerelease: auto
source:
enabled: true
sboms:
- artifacts: source
documents:
- "${artifact}.cdx.sbom"
cmd: cyclonedx-gomod
args: [ "mod", "-licenses", "-json", "-output", "$document", "./.." ]
milestones:
- name_template: "{{ .Tag }}"
close: true
changelog:
use: github
sort: asc
groups:
- title: Features
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: Fixes
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: Building and Packaging
regexp: "^.*build[(\\w)]*:+.*$"
order: 2
- title: Documentation
regexp: "^.*docs[(\\w)]*:+.*$"
order: 3
- title: Others
order: 999
filters:
exclude:
- '^test:'
- '^Merge ' cyclonedx-go-0.9.3/.idea/ 0000775 0000000 0000000 00000000000 15067437333 0015111 5 ustar 00root root 0000000 0000000 cyclonedx-go-0.9.3/.idea/icon.svg 0000664 0000000 0000000 00000004021 15067437333 0016557 0 ustar 00root root 0000000 0000000
cyclonedx-go-0.9.3/.licenserc.yml 0000664 0000000 0000000 00000002224 15067437333 0016701 0 ustar 00root root 0000000 0000000 header:
license:
spdx-id: Apache-2.0
copyright-owner: OWASP Foundation
content: |
This file is part of CycloneDX Go
Licensed under the Apache License, Version 2.0 (the “License”);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
Copyright (c) OWASP Foundation. All Rights Reserved.
paths-ignore:
- "**/*.md"
- "**/go.mod"
- "**/go.sum"
- "**/testdata/**"
- ".github/**"
- ".idea/**"
- ".gitignore"
- ".gitpod.yml"
- ".golangci.yml"
- ".goreleaser.yml"
- ".licenserc.yml"
- "CODEOWNERS"
- "LICENSE"
- "Makefile"
- "NOTICE"
- "cyclonedx_string.go"
- "schema/**" cyclonedx-go-0.9.3/CODEOWNERS 0000664 0000000 0000000 00000000035 15067437333 0015522 0 ustar 00root root 0000000 0000000 * @CycloneDX/go-maintainers
cyclonedx-go-0.9.3/Dockerfile.gitpod 0000664 0000000 0000000 00000001426 15067437333 0017413 0 ustar 00root root 0000000 0000000 # This file is part of CycloneDX Go
#
# Licensed under the Apache License, Version 2.0 (the “License”);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an “AS IS” BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.
FROM gitpod/workspace-go:latest@sha256:8985eb7cf5f155eb83f07294e9bd1a7e8066f969711f51a166ef60d17d409eb0
cyclonedx-go-0.9.3/LICENSE 0000664 0000000 0000000 00000026115 15067437333 0015143 0 ustar 00root root 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright OWASP Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
cyclonedx-go-0.9.3/Makefile 0000664 0000000 0000000 00000000334 15067437333 0015571 0 ustar 00root root 0000000 0000000 build:
go build -v
.PHONY: build
test:
go test -v -cover
.PHONY: test
clean:
go clean
.PHONY: clean
generate:
go generate
.PHONY: generate
lint:
golangci-lint run
.PHONY: lint
all: clean build test
.PHONY: all
cyclonedx-go-0.9.3/NOTICE 0000664 0000000 0000000 00000000212 15067437333 0015030 0 ustar 00root root 0000000 0000000 CycloneDX Go
Copyright (c) OWASP Foundation
This product includes software developed by the
CycloneDX community (https://cyclonedx.org/). cyclonedx-go-0.9.3/README.md 0000664 0000000 0000000 00000006546 15067437333 0015423 0 ustar 00root root 0000000 0000000 # cyclonedx-go
[](https://github.com/CycloneDX/cyclonedx-go/actions/workflows/ci.yml)
[](https://goreportcard.com/report/github.com/CycloneDX/cyclonedx-go)
[](https://pkg.go.dev/github.com/CycloneDX/cyclonedx-go)
[](LICENSE)
[](https://cyclonedx.org/)
[](https://cyclonedx.org/slack/invite)
[](https://groups.io/g/CycloneDX)
[](https://twitter.com/CycloneDX_Spec)
*cyclonedx-go is a Go library to consume and produce CycloneDX Software Bill of Materials (SBOM)*
> If you just want to create BOMs for your Go projects, see [*cyclonedx-gomod*](https://github.com/CycloneDX/cyclonedx-gomod)
## Installation
```
go get github.com/CycloneDX/cyclonedx-go
```
## Usage
Please refer to the module's [documentation](https://pkg.go.dev/github.com/CycloneDX/cyclonedx-go#section-documentation).
Also, checkout the [`examples`](./example_test.go) to get an idea of how this library may be used.
## Compatibility
| cyclonedx-go versions | Supported Go versions | Supported CycloneDX spec |
|:---------------------:|:---------------------:|:------------------------:|
| < v0.4.0 | 1.14+ | 1.2 |
| == v0.4.0 | 1.14+ | 1.3 |
| >= v0.5.0, < v0.7.0 | 1.15+ | 1.4 |
| >= v0.7.0, < v0.8.0 | 1.17+ | 1.0-1.4 |
| == v0.8.0 | 1.18+ | 1.0-1.5 |
| >= v0.9.0 | 1.20+ | 1.0-1.6 |
We're aiming to support all [officially supported](https://golang.org/doc/devel/release.html#policy) Go versions, plus
an additional older version.
Prior to v0.7.0, this library only supported the latest version of the CycloneDX specification. While it is generally
possible to *read* BOMs of an older spec, *writing* would exclusively produce BOMs conforming to the latest supported spec.
Starting with v0.7.0, writing BOMs conforming to all previous version of the spec is also possible.
## Copyright & License
CycloneDX Go is Copyright (c) OWASP Foundation. All Rights Reserved.
Permission to modify and redistribute is granted under the terms of the Apache 2.0 license.
See the [LICENSE](./LICENSE) file for the full license.
## Contributing
[](https://gitpod.io/#https://github.com/CycloneDX/cyclonedx-go)
Pull requests are welcome. But please read the
[CycloneDX contributing guidelines](https://github.com/CycloneDX/.github/blob/master/CONTRIBUTING.md) first.
It is generally expected that pull requests will include relevant tests. Tests are automatically run against all
supported Go versions (see [Compatibility](#compatibility)) for every pull request.
cyclonedx-go-0.9.3/convert.go 0000664 0000000 0000000 00000041203 15067437333 0016140 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import "fmt"
// copyAndConvert returns a converted copy of the BOM, adhering to a given SpecVersion.
func (b BOM) copyAndConvert(specVersion SpecVersion) (*BOM, error) {
var bomCopy BOM
err := b.copy(&bomCopy)
if err != nil {
return nil, fmt.Errorf("failed to copy bom: %w", err)
}
bomCopy.convert(specVersion)
return &bomCopy, nil
}
// convert modifies the BOM such that it adheres to a given SpecVersion.
func (b *BOM) convert(specVersion SpecVersion) {
if specVersion < SpecVersion1_1 {
b.SerialNumber = ""
b.ExternalReferences = nil
}
if specVersion < SpecVersion1_2 {
b.Dependencies = nil
b.Metadata = nil
b.Services = nil
}
if specVersion < SpecVersion1_3 {
b.Compositions = nil
}
if specVersion < SpecVersion1_4 {
b.Vulnerabilities = nil
}
if specVersion < SpecVersion1_5 {
b.Annotations = nil
b.Formulation = nil
}
if specVersion < SpecVersion1_6 {
b.Declarations = nil
b.Definitions = nil
}
if b.Metadata != nil {
if specVersion < SpecVersion1_3 {
b.Metadata.Licenses = nil
b.Metadata.Properties = nil
}
if specVersion < SpecVersion1_5 {
b.Metadata.Lifecycles = nil
}
if specVersion < SpecVersion1_6 {
b.Metadata.Manufacturer = nil
}
recurseComponent(b.Metadata.Component, componentConverter(specVersion))
convertLicenses(b.Metadata.Licenses, specVersion)
convertTools(b.Metadata.Tools, specVersion)
convertOrganizationalEntity(b.Metadata.Manufacture, specVersion)
convertOrganizationalEntity(b.Metadata.Supplier, specVersion)
if b.Metadata.Authors != nil {
for i := range *b.Metadata.Authors {
convertOrganizationalContact(&(*b.Metadata.Authors)[i], specVersion)
}
}
}
if b.Components != nil {
for i := range *b.Components {
recurseComponent(&(*b.Components)[i], componentConverter(specVersion))
}
}
if b.Services != nil {
for i := range *b.Services {
recurseService(&(*b.Services)[i], serviceConverter(specVersion))
}
}
if b.Vulnerabilities != nil {
convertVulnerabilities(b.Vulnerabilities, specVersion)
}
if b.Compositions != nil {
convertCompositions(b.Compositions, specVersion)
}
if b.ExternalReferences != nil {
convertExternalReferences(b.ExternalReferences, specVersion)
}
if b.Annotations != nil {
convertAnnotations(b.Annotations, specVersion)
}
b.SpecVersion = specVersion
b.XMLNS = xmlNamespaces[specVersion]
b.JSONSchema = jsonSchemas[specVersion]
}
// componentConverter modifies a Component such that it adheres to a given SpecVersion.
func componentConverter(specVersion SpecVersion) func(*Component) {
return func(c *Component) {
if specVersion < SpecVersion1_1 {
c.BOMRef = ""
c.ExternalReferences = nil
if c.Modified == nil {
c.Modified = Bool(false)
}
c.Pedigree = nil
}
if specVersion < SpecVersion1_2 {
c.Author = ""
c.MIMEType = ""
if c.Pedigree != nil {
c.Pedigree.Patches = nil
}
c.Supplier = nil
c.SWID = nil
}
if specVersion < SpecVersion1_3 {
c.Properties = nil
}
if specVersion < SpecVersion1_4 {
c.ReleaseNotes = nil
if c.Version == "" {
c.Version = "0.0.0"
}
}
if specVersion < SpecVersion1_5 {
c.ModelCard = nil
c.Data = nil
}
if specVersion < SpecVersion1_6 {
c.SWHID = nil
c.OmniborID = nil
c.Manufacturer = nil
c.Authors = nil
}
if !specVersion.supportsComponentType(c.Type) {
c.Type = ComponentTypeApplication
}
convertExternalReferences(c.ExternalReferences, specVersion)
convertHashes(c.Hashes, specVersion)
convertLicenses(c.Licenses, specVersion)
convertEvidence(c, specVersion)
convertModelCard(c, specVersion)
if !specVersion.supportsScope(c.Scope) {
c.Scope = ""
}
}
}
func convertEvidence(c *Component, specVersion SpecVersion) {
if c.Evidence == nil {
return
}
if specVersion < SpecVersion1_3 {
c.Evidence = nil
return
}
if specVersion < SpecVersion1_5 {
c.Evidence.Identity = nil
c.Evidence.Occurrences = nil
c.Evidence.Callstack = nil
return
}
if specVersion < SpecVersion1_6 {
// Spec version 1.5 uses only one Identity.
// cf. https://cyclonedx.org/docs/1.5/json/#components_items_evidence_identity
if c.Evidence.Identity != nil {
ids := *c.Evidence.Identity
ids = ids[:1]
c.Evidence.Identity = &ids
}
if c.Evidence.Occurrences != nil {
for i := range *c.Evidence.Occurrences {
occ := &(*c.Evidence.Occurrences)[i]
occ.Line = nil
occ.Offset = nil
occ.Symbol = ""
occ.AdditionalContext = ""
}
}
}
convertLicenses(c.Evidence.Licenses, specVersion)
}
func convertCompositions(comps *[]Composition, specVersion SpecVersion) {
if comps == nil {
return
}
for i := range *comps {
comp := &(*comps)[i]
if !specVersion.supportsCompositionAggregate(comp.Aggregate) {
comp.Aggregate = CompositionAggregateUnknown
}
}
}
// convertExternalReferences modifies an ExternalReference slice such that it adheres to a given SpecVersion.
func convertExternalReferences(extRefs *[]ExternalReference, specVersion SpecVersion) {
if extRefs == nil {
return
}
for i := range *extRefs {
extRef := &(*extRefs)[i]
if !specVersion.supportsExternalReferenceType(extRef.Type) {
extRef.Type = ERTypeOther
}
if specVersion < SpecVersion1_3 {
extRef.Hashes = nil
}
}
}
// convertHashes modifies a Hash slice such that it adheres to a given SpecVersion.
// If after the conversion no valid hashes are left in the slice, it will be nilled.
func convertHashes(hashes *[]Hash, specVersion SpecVersion) {
if hashes == nil {
return
}
converted := make([]Hash, 0)
for i := range *hashes {
hash := (*hashes)[i]
if specVersion.supportsHashAlgorithm(hash.Algorithm) {
converted = append(converted, hash)
}
}
if len(converted) == 0 {
*hashes = nil
} else {
*hashes = converted
}
}
// convertLicenses modifies a Licenses slice such that it adheres to a given SpecVersion.
// If after the conversion no valid licenses are left in the slice, it will be nilled.
func convertLicenses(licenses *Licenses, specVersion SpecVersion) {
if licenses == nil {
return
}
if specVersion < SpecVersion1_1 {
converted := make(Licenses, 0)
for i := range *licenses {
choice := &(*licenses)[i]
if choice.License != nil {
if choice.License.ID == "" && choice.License.Name == "" {
choice.License = nil
} else {
choice.License.Text = nil
choice.License.URL = ""
}
}
choice.Expression = ""
if choice.License != nil {
converted = append(converted, *choice)
}
}
if len(converted) == 0 {
*licenses = nil
} else {
*licenses = converted
}
}
if specVersion < SpecVersion1_5 {
for i := range *licenses {
choice := &(*licenses)[i]
if choice.License != nil {
choice.License.BOMRef = ""
choice.License.Licensing = nil
choice.License.Properties = nil
}
}
}
if specVersion < SpecVersion1_6 {
for i := range *licenses {
choice := &(*licenses)[i]
if choice.License == nil {
continue
}
choice.License.Acknowledgement = ""
if choice.License.Licensing == nil {
continue
}
if choice.License.Licensing.Licensor != nil {
convertOrganizationalEntity(choice.License.Licensing.Licensor.Organization, specVersion)
}
if choice.License.Licensing.Licensee != nil {
convertOrganizationalEntity(choice.License.Licensing.Licensee.Organization, specVersion)
}
if choice.License.Licensing.Purchaser != nil {
convertOrganizationalEntity(choice.License.Licensing.Purchaser.Organization, specVersion)
}
}
}
}
func convertOrganizationalEntity(org *OrganizationalEntity, specVersion SpecVersion) {
if org == nil {
return
}
if specVersion < SpecVersion1_5 {
org.BOMRef = ""
if org.Contact != nil {
for i := range *org.Contact {
convertOrganizationalContact(&(*org.Contact)[i], specVersion)
}
}
}
if specVersion < SpecVersion1_6 {
org.Address = nil
}
}
func convertOrganizationalContact(c *OrganizationalContact, specVersion SpecVersion) {
if c == nil {
return
}
if specVersion < SpecVersion1_5 {
c.BOMRef = ""
}
}
func convertModelCard(c *Component, specVersion SpecVersion) {
if c.ModelCard == nil {
return
}
if specVersion < SpecVersion1_6 {
if c.ModelCard.Considerations != nil {
c.ModelCard.Considerations.EnvironmentalConsiderations = nil
}
}
}
func convertVulnerabilities(vulns *[]Vulnerability, specVersion SpecVersion) {
if vulns == nil {
return
}
for i := range *vulns {
vuln := &(*vulns)[i]
convertTools(vuln.Tools, specVersion)
if specVersion < SpecVersion1_5 {
vuln.ProofOfConcept = nil
vuln.Rejected = ""
vuln.Workaround = ""
}
if specVersion < SpecVersion1_6 {
if vuln.Credits != nil {
if vuln.Credits.Organizations != nil {
for i := range *vuln.Credits.Organizations {
convertOrganizationalEntity(&(*vuln.Credits.Organizations)[i], specVersion)
}
}
if vuln.Credits.Individuals != nil {
for i := range *vuln.Credits.Individuals {
convertOrganizationalContact(&(*vuln.Credits.Individuals)[i], specVersion)
}
}
}
}
if vuln.Ratings != nil {
for j := range *vuln.Ratings {
rating := &(*vuln.Ratings)[j]
if !specVersion.supportsScoringMethod(rating.Method) {
rating.Method = ScoringMethodOther
}
}
}
}
}
func convertAnnotations(annotations *[]Annotation, specVersion SpecVersion) {
if annotations == nil {
return
}
if specVersion < SpecVersion1_6 {
for i := range *annotations {
ann := (*annotations)[i]
if ann.Annotator == nil {
continue
}
convertOrganizationalEntity(ann.Annotator.Organization, specVersion)
recurseService(ann.Annotator.Service, serviceConverter(specVersion))
}
}
}
// serviceConverter modifies a Service such that it adheres to a given SpecVersion.
func serviceConverter(specVersion SpecVersion) func(*Service) {
return func(s *Service) {
if specVersion < SpecVersion1_3 {
s.Properties = nil
}
if specVersion < SpecVersion1_4 {
s.ReleaseNotes = nil
}
convertOrganizationalEntity(s.Provider, specVersion)
convertExternalReferences(s.ExternalReferences, specVersion)
}
}
// convertTools modifies a ToolsChoice such that it adheres to a given SpecVersion.
func convertTools(tools *ToolsChoice, specVersion SpecVersion) {
if tools == nil {
return
}
if specVersion < SpecVersion1_5 {
convertedTools := make([]Tool, 0)
if tools.Components != nil {
for i := range *tools.Components {
tool := convertComponentToTool((*tools.Components)[i], specVersion)
if tool != nil {
convertedTools = append(convertedTools, *tool)
}
}
tools.Components = nil
}
if tools.Services != nil {
for i := range *tools.Services {
tool := convertServiceToTool((*tools.Services)[i], specVersion)
if tool != nil {
convertedTools = append(convertedTools, *tool)
}
}
tools.Services = nil
}
if len(convertedTools) > 0 {
if tools.Tools == nil {
tools.Tools = &convertedTools
} else {
*tools.Tools = append(*tools.Tools, convertedTools...)
}
}
}
if tools.Services != nil {
for i := range *tools.Services {
convertOrganizationalEntity((*tools.Services)[i].Provider, specVersion)
}
}
if tools.Tools != nil {
for i := range *tools.Tools {
convertTool(&(*tools.Tools)[i], specVersion)
}
}
}
// convertTool modifies a Tool such that it adheres to a given SpecVersion.
func convertTool(tool *Tool, specVersion SpecVersion) {
if tool == nil {
return
}
if specVersion < SpecVersion1_4 {
tool.ExternalReferences = nil
}
convertExternalReferences(tool.ExternalReferences, specVersion)
convertHashes(tool.Hashes, specVersion)
}
// convertComponentToTool converts a Component to a Tool for use in ToolsChoice.Tools.
func convertComponentToTool(component Component, _ SpecVersion) *Tool {
tool := Tool{
Vendor: component.Author,
Name: component.Name,
Version: component.Version,
Hashes: component.Hashes,
ExternalReferences: component.ExternalReferences,
}
if component.Supplier != nil {
// There is no perfect 1:1 mapping for the Vendor field, but Supplier comes closest.
// https://github.com/CycloneDX/cyclonedx-go/issues/115#issuecomment-1688710539
tool.Vendor = component.Supplier.Name
}
return &tool
}
// convertServiceToTool converts a Service to a Tool for use in ToolsChoice.Tools.
func convertServiceToTool(service Service, _ SpecVersion) *Tool {
tool := Tool{
Name: service.Name,
Version: service.Version,
ExternalReferences: service.ExternalReferences,
}
if service.Provider != nil {
tool.Vendor = service.Provider.Name
}
return &tool
}
func recurseComponent(component *Component, f func(c *Component)) {
if component == nil {
return
}
f(component)
if component.Components != nil {
for i := range *component.Components {
recurseComponent(&(*component.Components)[i], f)
}
}
if component.Pedigree != nil {
if component.Pedigree.Ancestors != nil {
for i := range *component.Pedigree.Ancestors {
recurseComponent(&(*component.Pedigree.Ancestors)[i], f)
}
}
if component.Pedigree.Descendants != nil {
for i := range *component.Pedigree.Descendants {
recurseComponent(&(*component.Pedigree.Descendants)[i], f)
}
}
if component.Pedigree.Variants != nil {
for i := range *component.Pedigree.Variants {
recurseComponent(&(*component.Pedigree.Variants)[i], f)
}
}
}
}
func recurseService(service *Service, f func(s *Service)) {
if service == nil {
return
}
f(service)
if service.Services != nil {
for i := range *service.Services {
recurseService(&(*service.Services)[i], f)
}
}
}
func (sv SpecVersion) supportsComponentType(cType ComponentType) bool {
switch cType {
case ComponentTypeApplication, ComponentTypeDevice, ComponentTypeFramework, ComponentTypeLibrary, ComponentTypeOS:
return sv >= SpecVersion1_0
case ComponentTypeFile:
return sv >= SpecVersion1_1
case ComponentTypeContainer, ComponentTypeFirmware:
return sv >= SpecVersion1_2
case ComponentTypeData, ComponentTypeDeviceDriver, ComponentTypeMachineLearningModel, ComponentTypePlatform:
return sv >= SpecVersion1_5
}
return false
}
func (sv SpecVersion) supportsCompositionAggregate(ca CompositionAggregate) bool {
switch ca {
case CompositionAggregateIncompleteFirstPartyOpenSourceOnly, CompositionAggregateIncompleteFirstPartyProprietaryOnly,
CompositionAggregateIncompleteThirdPartyOpenSourceOnly, CompositionAggregateIncompleteThirdPartyProprietaryOnly:
return sv >= SpecVersion1_5
}
return sv >= SpecVersion1_3
}
func (sv SpecVersion) supportsExternalReferenceType(ert ExternalReferenceType) bool {
switch ert {
case ERTypeAdversaryModel,
ERTypeAttestation,
ERTypeCertificationReport,
ERTypeCodifiedInfrastructure,
ERTypeComponentAnalysisReport,
ERTypeConfiguration,
ERTypeDistributionIntake,
ERTypeDynamicAnalysisReport,
ERTypeEvidence,
ERTypeExploitabilityStatement,
ERTypeFormulation,
ERTypeLog,
ERTypeMaturityReport,
ERTypeModelCard,
ERTypePentestReport,
ERTypeQualityMetrics,
ERTypeRiskAssessment,
ERTypeRuntimeAnalysisReport,
ERTypeStaticAnalysisReport,
ERTypeThreatModel,
ERTypeVulnerabilityAssertion:
return sv >= SpecVersion1_5
}
return sv >= SpecVersion1_1
}
func (sv SpecVersion) supportsHashAlgorithm(algo HashAlgorithm) bool {
switch algo {
case HashAlgoMD5, HashAlgoSHA1, HashAlgoSHA256, HashAlgoSHA384, HashAlgoSHA512, HashAlgoSHA3_256, HashAlgoSHA3_512:
return sv >= SpecVersion1_0
case HashAlgoSHA3_384, HashAlgoBlake2b_256, HashAlgoBlake2b_384, HashAlgoBlake2b_512, HashAlgoBlake3:
return sv >= SpecVersion1_2
}
return false
}
func (sv SpecVersion) supportsScope(scope Scope) bool {
switch scope {
case ScopeRequired, ScopeOptional:
return sv >= SpecVersion1_0
case ScopeExcluded:
return sv >= SpecVersion1_2
}
return false
}
func (sv SpecVersion) supportsScoringMethod(method ScoringMethod) bool {
switch method {
case ScoringMethodCVSSv2, ScoringMethodCVSSv3, ScoringMethodCVSSv31, ScoringMethodOWASP, ScoringMethodOther:
return sv >= SpecVersion1_4
case ScoringMethodCVSSv4, ScoringMethodSSVC:
return sv >= SpecVersion1_5
}
return false
}
cyclonedx-go-0.9.3/convert_test.go 0000664 0000000 0000000 00000014207 15067437333 0017203 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_componentConverter_convertEvidence(t *testing.T) {
t.Run("spec 1.2 and lower", func(t *testing.T) {
convert := componentConverter(SpecVersion1_2)
comp := Component{
Evidence: &Evidence{},
}
convert(&comp)
assert.Nil(t, comp.Evidence)
})
t.Run("spec 1.4 and lower", func(t *testing.T) {
convert := componentConverter(SpecVersion1_4)
comp := Component{
Evidence: &Evidence{
Identity: &[]EvidenceIdentity{},
Occurrences: &[]EvidenceOccurrence{},
Callstack: &Callstack{},
Copyright: &[]Copyright{{Text: "foo"}},
},
}
convert(&comp)
assert.Nil(t, comp.Evidence.Identity)
assert.Nil(t, comp.Evidence.Occurrences)
assert.Nil(t, comp.Evidence.Callstack)
assert.NotNil(t, comp.Evidence.Copyright)
})
t.Run("spec 1.5 and lower", func(t *testing.T) {
convert := componentConverter(SpecVersion1_5)
var val int = 42
comp := Component{
Evidence: &Evidence{
Identity: &[]EvidenceIdentity{
{
Field: EvidenceIdentityFieldTypePURL,
},
{
Field: EvidenceIdentityFieldTypeName,
},
},
Occurrences: &[]EvidenceOccurrence{
{
BOMRef: "foo",
Location: "bar",
Line: &val,
Offset: &val,
Symbol: "asdf",
AdditionalContext: "quux",
},
},
},
}
convert(&comp)
require.Len(t, *comp.Evidence.Identity, 1)
require.Len(t, *comp.Evidence.Occurrences, 1)
occ := (*comp.Evidence.Occurrences)[0]
assert.Nil(t, occ.Line)
assert.Nil(t, occ.Offset)
assert.Zero(t, occ.Symbol)
assert.Zero(t, occ.AdditionalContext)
})
}
func Test_convertLicenses(t *testing.T) {
t.Run("spec 1.5 and lower", func(t *testing.T) {
bom := NewBOM()
bom.Metadata = &Metadata{
Licenses: &Licenses{
{License: &License{Name: "Apache License 2.0", Acknowledgement: LicenseAcknowledgementDeclared}},
},
}
bom.Components = &[]Component{
{
Name: "foo",
Licenses: &Licenses{
{License: &License{Name: "Apache License 2.0", Acknowledgement: LicenseAcknowledgementConcluded}},
},
},
}
bom.convert(SpecVersion1_5)
assert.Zero(t, (*bom.Metadata.Licenses)[0].License.Acknowledgement)
assert.Zero(t, (*(*bom.Components)[0].Licenses)[0].License.Acknowledgement)
})
}
func Test_convertTools_OrganizationalEntity(t *testing.T) {
t.Run("spec 1.5 and lower", func(t *testing.T) {
orgStub := func() *OrganizationalEntity {
t.Helper()
return &OrganizationalEntity{
Name: "Acme Corp",
Address: &PostalAddress{},
}
}
bom := NewBOM()
bom.Metadata = &Metadata{
Manufacture: orgStub(),
Supplier: orgStub(),
Tools: &ToolsChoice{
Services: &[]Service{{Provider: orgStub()}},
},
Licenses: &Licenses{
{
License: &License{
Licensing: &Licensing{
Licensor: &OrganizationalEntityOrContact{Organization: orgStub()},
Licensee: &OrganizationalEntityOrContact{Organization: orgStub()},
Purchaser: &OrganizationalEntityOrContact{Organization: orgStub()},
},
},
},
},
}
bom.Vulnerabilities = &[]Vulnerability{
{
ID: "some-vuln",
Credits: &Credits{
Organizations: &[]OrganizationalEntity{*orgStub()},
},
},
}
bom.Annotations = &[]Annotation{
{
Annotator: &Annotator{
Organization: orgStub(),
Service: &Service{Provider: orgStub()},
},
},
}
bom.convert(SpecVersion1_5)
assert.Nil(t, bom.Metadata.Manufacture.Address)
assert.Nil(t, bom.Metadata.Supplier.Address)
assert.Nil(t, (*bom.Metadata.Tools.Services)[0].Provider.Address)
assert.Nil(t, (*bom.Metadata.Licenses)[0].License.Licensing.Licensor.Organization.Address)
assert.Nil(t, (*bom.Metadata.Licenses)[0].License.Licensing.Licensee.Organization.Address)
assert.Nil(t, (*bom.Metadata.Licenses)[0].License.Licensing.Purchaser.Organization.Address)
assert.Nil(t, (*(*bom.Vulnerabilities)[0].Credits.Organizations)[0].Address)
assert.Nil(t, (*bom.Annotations)[0].Annotator.Organization.Address)
assert.Nil(t, (*bom.Annotations)[0].Annotator.Service.Provider.Address)
})
}
func Test_convertModelCard(t *testing.T) {
t.Run("spec 1.5 and lower", func(t *testing.T) {
bom := NewBOM()
bom.Metadata = &Metadata{
Component: &Component{
ModelCard: &MLModelCard{
Considerations: &MLModelCardConsiderations{
EnvironmentalConsiderations: &MLModelCardEnvironmentalConsiderations{},
},
},
},
}
bom.convert(SpecVersion1_5)
assert.Nil(t, bom.Metadata.Component.ModelCard.Considerations.EnvironmentalConsiderations)
})
}
func Test_convertManufacturer(t *testing.T) {
t.Run("spec 1.5 and lower", func(t *testing.T) {
bom := NewBOM()
bom.Metadata = &Metadata{
Manufacturer: &OrganizationalEntity{
Name: "Acme, Inc.",
},
}
bom.Components = &[]Component{
{
Name: "foo",
Manufacturer: &OrganizationalEntity{
Name: "Acme, Inc.",
},
},
}
bom.convert(SpecVersion1_5)
assert.Nil(t, bom.Metadata.Manufacturer)
assert.Nil(t, (*bom.Components)[0].Manufacturer)
})
}
func Test_convertAuthors(t *testing.T) {
t.Run("spec 1.5 and lower", func(t *testing.T) {
bom := NewBOM()
bom.Components = &[]Component{
{
Name: "foo",
Authors: &[]OrganizationalContact{
{
Name: "Acme Professional Services",
},
},
},
}
bom.convert(SpecVersion1_5)
assert.Nil(t, (*bom.Components)[0].Authors)
})
}
cyclonedx-go-0.9.3/copy.go 0000664 0000000 0000000 00000002443 15067437333 0015435 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"bytes"
"encoding/gob"
"fmt"
)
// copy creates a deep copy of the BOM in a given destination.
// Copying is currently done by encoding and decoding the BOM struct using gob.
// In the future we may choose to switch to a more efficient strategy,
// and consider to export this API.
func (b BOM) copy(dst *BOM) error {
buf := bytes.Buffer{}
err := gob.NewEncoder(&buf).Encode(b)
if err != nil {
return fmt.Errorf("failed to encode bom: %w", err)
}
err = gob.NewDecoder(&buf).Decode(dst)
if err != nil {
return fmt.Errorf("failed to decode bom: %w", err)
}
return nil
}
cyclonedx-go-0.9.3/cyclonedx.go 0000664 0000000 0000000 00000254433 15067437333 0016463 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"encoding/xml"
"errors"
"fmt"
"regexp"
)
//go:generate stringer -linecomment -output cyclonedx_string.go -type MediaType,SpecVersion
const (
BOMFormat = "CycloneDX"
)
var ErrInvalidSpecVersion = errors.New("invalid specification version")
type Advisory struct {
Title string `json:"title,omitempty" xml:"title,omitempty"`
URL string `json:"url" xml:"url"`
}
type AffectedVersions struct {
Version string `json:"version,omitempty" xml:"version,omitempty"`
Range string `json:"range,omitempty" xml:"range,omitempty"`
Status VulnerabilityStatus `json:"status" xml:"status"`
}
type Affects struct {
Ref string `json:"ref" xml:"ref"`
Range *[]AffectedVersions `json:"versions,omitempty" xml:"versions>version,omitempty"`
}
type Annotation struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Subjects *[]BOMReference `json:"subjects,omitempty" xml:"subjects>subject,omitempty"`
Annotator *Annotator `json:"annotator,omitempty" xml:"annotator,omitempty"`
Timestamp string `json:"timestamp,omitempty" xml:"timestamp,omitempty"`
Text string `json:"text,omitempty" xml:"text,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type Annotator struct {
Organization *OrganizationalEntity `json:"organization,omitempty" xml:"organization,omitempty"`
Individual *OrganizationalContact `json:"individual,omitempty" xml:"individual,omitempty"`
Component *Component `json:"component,omitempty" xml:"component,omitempty"`
Service *Service `json:"service,omitempty" xml:"service,omitempty"`
}
type Assessor struct {
BOMRef BOMReference `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
ThirdParty bool `json:"thirdParty,omitempty" xml:"thirdParty,omitempty"`
Organization *OrganizationalEntity `json:"organization,omitempty" xml:"organization,omitempty"`
}
type AttachedText struct {
Content string `json:"content" xml:",chardata"`
ContentType string `json:"contentType,omitempty" xml:"content-type,attr,omitempty"`
Encoding string `json:"encoding,omitempty" xml:"encoding,attr,omitempty"`
}
type Attestation struct {
Summary string `json:"summary,omitempty" xml:"summary,omitempty"`
Assessor BOMReference `json:"assessor,omitempty" xml:"assessor,omitempty"`
Map *[]AttestationMap `json:"map,omitempty" xml:"map,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type AttestationMap struct {
Requirement string `json:"requirement,omitempty" xml:"requirement,omitempty"`
Claims *[]BOMReference `json:"claims,omitempty" xml:"claims>claim,omitempty"`
CounterClaims *[]BOMReference `json:"counterClaims,omitempty" xml:"counterClaims>counterClaim,omitempty"`
Conformance *AttestationConformance `json:"conformance,omitempty" xml:"conformance,omitempty"`
Confidence *AttestationConfidence `json:"confidence,omitempty" xml:"confidence,omitempty"`
}
type AttestationConformance struct {
Score *float64 `json:"score,omitempty" xml:"score,omitempty"`
Rationale string `json:"rationale,omitempty" xml:"rationale,omitempty"`
MitigationStrategies *[]BOMReference `json:"mitigationStrategies,omitempty" xml:"mitigationStrategies>mitigationStrategy,omitempty"`
}
type AttestationConfidence struct {
Score *float64 `json:"score,omitempty" xml:"score,omitempty"`
Rationale string `json:"rationale,omitempty" xml:"rationale,omitempty"`
}
type BOM struct {
// XML specific fields
XMLName xml.Name `json:"-" xml:"bom"`
XMLNS string `json:"-" xml:"xmlns,attr"`
// JSON specific fields
JSONSchema string `json:"$schema,omitempty" xml:"-"`
BOMFormat string `json:"bomFormat" xml:"-"`
SpecVersion SpecVersion `json:"specVersion" xml:"-"`
SerialNumber string `json:"serialNumber,omitempty" xml:"serialNumber,attr,omitempty"`
Version int `json:"version" xml:"version,attr"`
Metadata *Metadata `json:"metadata,omitempty" xml:"metadata,omitempty"`
Components *[]Component `json:"components,omitempty" xml:"components>component,omitempty"`
Services *[]Service `json:"services,omitempty" xml:"services>service,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
Dependencies *[]Dependency `json:"dependencies,omitempty" xml:"dependencies>dependency,omitempty"`
Compositions *[]Composition `json:"compositions,omitempty" xml:"compositions>composition,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
Vulnerabilities *[]Vulnerability `json:"vulnerabilities,omitempty" xml:"vulnerabilities>vulnerability,omitempty"`
Annotations *[]Annotation `json:"annotations,omitempty" xml:"annotations>annotation,omitempty"`
Formulation *[]Formula `json:"formulation,omitempty" xml:"formulation>formula,omitempty"`
Declarations *Declarations `json:"declarations,omitempty" xml:"declarations,omitempty"`
Definitions *Definitions `json:"definitions,omitempty" xml:"definitions,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
func NewBOM() *BOM {
return &BOM{
JSONSchema: jsonSchemas[SpecVersion1_6],
XMLNS: xmlNamespaces[SpecVersion1_6],
BOMFormat: BOMFormat,
SpecVersion: SpecVersion1_6,
Version: 1,
}
}
type BOMFileFormat int
const (
BOMFileFormatXML BOMFileFormat = iota
BOMFileFormatJSON
)
// Bool is a convenience function to transform a value of the primitive type bool to a pointer of bool
func Bool(value bool) *bool {
return &value
}
type BOMReference string
type Callstack struct {
Frames *[]CallstackFrame `json:"frames,omitempty" xml:"frames>frame,omitempty"`
}
type CallstackFrame struct {
Package string `json:"package,omitempty" xml:"package,omitempty"`
Module string `json:"module,omitempty" xml:"module,omitempty"`
Function string `json:"function,omitempty" xml:"function,omitempty"`
Parameters *[]string `json:"parameters,omitempty" xml:"parameters>parameter,omitempty"`
Line *int `json:"line,omitempty" xml:"line,omitempty"`
Column *int `json:"column,omitempty" xml:"column,omitempty"`
FullFilename string `json:"fullFilename,omitempty" xml:"fullFilename,omitempty"`
}
type CertificateProperties struct {
SubjectName string `json:"subjectName,omitempty" xml:"subjectName,omitempty"`
IssuerName string `json:"issuerName,omitempty" xml:"issuerName,omitempty"`
NotValidBefore string `json:"notValidBefore,omitempty" xml:"notValidBefore,omitempty"`
NotValidAfter string `json:"notValidAfter,omitempty" xml:"notValidAfter,omitempty"`
SignatureAlgorithmRef BOMReference `json:"signatureAlgorithmRef,omitempty" xml:"signatureAlgorithmRef,omitempty"`
SubjectPublicKeyRef BOMReference `json:"subjectPublicKeyRef,omitempty" xml:"subjectPublicKeyRef,omitempty"`
CertificateFormat string `json:"certificateFormat,omitempty" xml:"certificateFormat,omitempty"`
CertificateExtension string `json:"certificateExtension,omitempty" xml:"certificateExtension,omitempty"`
}
type Claim struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Target BOMReference `json:"target,omitempty" xml:"target,omitempty"`
Predicate string `json:"predicate,omitempty" xml:"predicate,omitempty"`
MitigationStrategies *[]BOMReference `json:"mitigationStrategies,omitempty" xml:"mitigationStrategies>mitigationStrategy,omitempty"`
Reasoning string `json:"reasoning,omitempty" xml:"reasoning,omitempty"`
Evidence *[]BOMReference `json:"evidence,omitempty" xml:"evidence,omitempty"`
CounterEvidence *[]BOMReference `json:"counterEvidence,omitempty" xml:"counterEvidence,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type CipherSuite struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
Algorithms *[]BOMReference `json:"algorithms,omitempty" xml:"algorithms,omitempty"`
Identifiers *[]string `json:"identifiers,omitempty" xml:"identifiers,omitempty"`
}
type ComponentType string
const (
ComponentTypeApplication ComponentType = "application"
ComponentTypeContainer ComponentType = "container"
ComponentTypeCryptographicAsset ComponentType = "cryptographic-asset"
ComponentTypeData ComponentType = "data"
ComponentTypeDevice ComponentType = "device"
ComponentTypeDeviceDriver ComponentType = "device-driver"
ComponentTypeFile ComponentType = "file"
ComponentTypeFirmware ComponentType = "firmware"
ComponentTypeFramework ComponentType = "framework"
ComponentTypeLibrary ComponentType = "library"
ComponentTypeMachineLearningModel ComponentType = "machine-learning-model"
ComponentTypeOS ComponentType = "operating-system"
ComponentTypePlatform ComponentType = "platform"
)
type Commit struct {
UID string `json:"uid,omitempty" xml:"uid,omitempty"`
URL string `json:"url,omitempty" xml:"url,omitempty"`
Author *IdentifiableAction `json:"author,omitempty" xml:"author,omitempty"`
Committer *IdentifiableAction `json:"committer,omitempty" xml:"committer,omitempty"`
Message string `json:"message,omitempty" xml:"message,omitempty"`
}
type Component struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
MIMEType string `json:"mime-type,omitempty" xml:"mime-type,attr,omitempty"`
Type ComponentType `json:"type" xml:"type,attr"`
Supplier *OrganizationalEntity `json:"supplier,omitempty" xml:"supplier,omitempty"`
Manufacturer *OrganizationalEntity `json:"manufacturer,omitempty" xml:"manufacturer,omitempty"`
Author string `json:"author,omitempty" xml:"author,omitempty"` // Deprecated: Use authors or manufacturer instead.
Authors *[]OrganizationalContact `json:"authors,omitempty" xml:"authors>author,omitempty"`
Publisher string `json:"publisher,omitempty" xml:"publisher,omitempty"`
Group string `json:"group,omitempty" xml:"group,omitempty"`
Name string `json:"name" xml:"name"`
Version string `json:"version,omitempty" xml:"version,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Scope Scope `json:"scope,omitempty" xml:"scope,omitempty"`
Hashes *[]Hash `json:"hashes,omitempty" xml:"hashes>hash,omitempty"`
Licenses *Licenses `json:"licenses,omitempty" xml:"licenses,omitempty"`
Copyright string `json:"copyright,omitempty" xml:"copyright,omitempty"`
CPE string `json:"cpe,omitempty" xml:"cpe,omitempty"`
PackageURL string `json:"purl,omitempty" xml:"purl,omitempty"`
OmniborID *[]string `json:"omniborId,omitempty" xml:"omniborId,omitempty"`
SWHID *[]string `json:"swhid,omitempty" xml:"swhid,omitempty"`
SWID *SWID `json:"swid,omitempty" xml:"swid,omitempty"`
Modified *bool `json:"modified,omitempty" xml:"modified,omitempty"`
Pedigree *Pedigree `json:"pedigree,omitempty" xml:"pedigree,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
Components *[]Component `json:"components,omitempty" xml:"components>component,omitempty"`
Evidence *Evidence `json:"evidence,omitempty" xml:"evidence,omitempty"`
ReleaseNotes *ReleaseNotes `json:"releaseNotes,omitempty" xml:"releaseNotes,omitempty"`
ModelCard *MLModelCard `json:"modelCard,omitempty" xml:"modelCard,omitempty"`
Data *[]ComponentData `json:"data,omitempty" xml:"data,omitempty"`
CryptoProperties *CryptoProperties `json:"cryptoProperties,omitempty" xml:"cryptoProperties,omitempty"`
Tags *[]string `json:"tags,omitempty" xml:"tags>tag,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type ComponentData struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Type ComponentDataType `json:"type,omitempty" xml:"type,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Contents *ComponentDataContents `json:"contents,omitempty" xml:"contents,omitempty"`
Classification string `json:"classification,omitempty" xml:"classification,omitempty"`
SensitiveData *[]string `json:"sensitiveData,omitempty" xml:"sensitiveData,omitempty"`
Graphics *ComponentDataGraphics `json:"graphics,omitempty" xml:"graphics,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Governance *DataGovernance `json:"governance,omitempty" xml:"governance,omitempty"`
}
type ComponentDataContents struct {
Attachment *AttachedText `json:"attachment,omitempty" xml:"attachment,omitempty"`
URL string `json:"url,omitempty" xml:"url,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties,omitempty"`
}
type ComponentDataGovernanceResponsibleParty struct {
Organization *OrganizationalEntity `json:"organization,omitempty" xml:"organization,omitempty"`
Contact *OrganizationalContact `json:"contact,omitempty" xml:"contact,omitempty"`
}
type ComponentDataGraphic struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
Image *AttachedText `json:"image,omitempty" xml:"image,omitempty"`
}
type ComponentDataGraphics struct {
Description string `json:"description,omitempty" xml:"description,omitempty"`
Collection *[]ComponentDataGraphic `json:"collection,omitempty" xml:"collection>graphic,omitempty"`
}
type ComponentDataType string
const (
ComponentDataTypeConfiguration ComponentDataType = "configuration"
ComponentDataTypeDataset ComponentDataType = "dataset"
ComponentDataTypeDefinition ComponentDataType = "definition"
ComponentDataTypeOther ComponentDataType = "other"
ComponentDataTypeSourceCode ComponentDataType = "source-code"
)
type Composition struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Aggregate CompositionAggregate `json:"aggregate" xml:"aggregate"`
Assemblies *[]BOMReference `json:"assemblies,omitempty" xml:"assemblies>assembly,omitempty"`
Dependencies *[]BOMReference `json:"dependencies,omitempty" xml:"dependencies>dependency,omitempty"`
Vulnerabilities *[]BOMReference `json:"vulnerabilities,omitempty" xml:"vulnerabilities>vulnerability,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type CompositionAggregate string
const (
CompositionAggregateComplete CompositionAggregate = "complete"
CompositionAggregateIncomplete CompositionAggregate = "incomplete"
CompositionAggregateIncompleteFirstPartyOnly CompositionAggregate = "incomplete_first_party_only"
CompositionAggregateIncompleteFirstPartyOpenSourceOnly CompositionAggregate = "incomplete_first_party_opensource_only"
CompositionAggregateIncompleteFirstPartyProprietaryOnly CompositionAggregate = "incomplete_first_party_proprietary_only"
CompositionAggregateIncompleteThirdPartyOnly CompositionAggregate = "incomplete_third_party_only"
CompositionAggregateIncompleteThirdPartyOpenSourceOnly CompositionAggregate = "incomplete_third_party_opensource_only"
CompositionAggregateIncompleteThirdPartyProprietaryOnly CompositionAggregate = "incomplete_third_party_proprietary_only"
CompositionAggregateNotSpecified CompositionAggregate = "not_specified"
CompositionAggregateUnknown CompositionAggregate = "unknown"
)
type Copyright struct {
Text string `json:"text" xml:"-"`
}
type Credits struct {
Organizations *[]OrganizationalEntity `json:"organizations,omitempty" xml:"organizations>organization,omitempty"`
Individuals *[]OrganizationalContact `json:"individuals,omitempty" xml:"individuals>individual,omitempty"`
}
type CryptoAlgorithmMode string
const (
CryptoAlgorithmModeCBC CryptoAlgorithmMode = "cbc"
CryptoAlgorithmModeECB CryptoAlgorithmMode = "ecb"
CryptoAlgorithmModeCCM CryptoAlgorithmMode = "ccm"
CryptoAlgorithmModeGCM CryptoAlgorithmMode = "gcm"
CryptoAlgorithmModeCFB CryptoAlgorithmMode = "cfb"
CryptoAlgorithmModeOFB CryptoAlgorithmMode = "ofb"
CryptoAlgorithmModeCTR CryptoAlgorithmMode = "ctr"
CryptoAlgorithmModeOther CryptoAlgorithmMode = "other"
CryptoAlgorithmModeUnknown CryptoAlgorithmMode = "unknown"
)
type CryptoAlgorithmProperties struct {
Primitive CryptoPrimitive `json:"primitive,omitempty" xml:"primitive,omitempty"`
ParameterSetIdentifier string `json:"parameterSetIdentifier,omitempty" xml:"parameterSetIdentifier,omitempty"`
Curve string `json:"curve,omitempty" xml:"curve,omitempty"`
ExecutionEnvironment CryptoExecutionEnvironment `json:"executionEnvironment,omitempty" xml:"executionEnvironment,omitempty"`
ImplementationPlatform ImplementationPlatform `json:"implementationPlatform,omitempty" xml:"implementationPlatform,omitempty"`
CertificationLevel *[]CryptoCertificationLevel `json:"certificationLevel,omitempty" xml:"certificationLevel,omitempty"`
Mode CryptoAlgorithmMode `json:"mode,omitempty" xml:"mode,omitempty"`
Padding CryptoPadding `json:"padding,omitempty" xml:"padding,omitempty"`
CryptoFunctions *[]CryptoFunction `json:"cryptoFunctions,omitempty" xml:"cryptoFunctions>cryptoFunction,omitempty"`
ClassicalSecurityLevel *int `json:"classicalSecurityLevel,omitempty" xml:"classicalSecurityLevel,omitempty"`
NistQuantumSecurityLevel *int `json:"nistQuantumSecurityLevel,omitempty" xml:"nistQuantumSecurityLevel,omitempty"`
}
type CryptoAssetType string
const (
CryptoAssetTypeAlgorithm CryptoAssetType = "algorithm"
CryptoAssetTypeCertificate CryptoAssetType = "certificate"
CryptoAssetTypeProtocol CryptoAssetType = "protocol"
CryptoAssetTypeRelatedCryptoMaterial CryptoAssetType = "related-crypto-material"
)
type CryptoCertificationLevel string
const (
CryptoCertificationLevelNone CryptoCertificationLevel = "none"
CryptoCertificationLevelFIPS140_1_L1 CryptoCertificationLevel = "fips140-1-l1"
CryptoCertificationLevelFIPS140_1_L2 CryptoCertificationLevel = "fips140-1-l2"
CryptoCertificationLevelFIPS140_1_L3 CryptoCertificationLevel = "fips140-1-l3"
CryptoCertificationLevelFIPS140_1_L4 CryptoCertificationLevel = "fips140-1-l4"
CryptoCertificationLevelFIPS140_2_L1 CryptoCertificationLevel = "fips140-2-l1"
CryptoCertificationLevelFIPS140_2_L2 CryptoCertificationLevel = "fips140-2-l2"
CryptoCertificationLevelFIPS140_2_L3 CryptoCertificationLevel = "fips140-2-l3"
CryptoCertificationLevelFIPS140_2_L4 CryptoCertificationLevel = "fips140-2-l4"
CryptoCertificationLevelFIPS140_3_L1 CryptoCertificationLevel = "fips140-3-l1"
CryptoCertificationLevelFIPS140_3_L2 CryptoCertificationLevel = "fips140-3-l2"
CryptoCertificationLevelFIPS140_3_L3 CryptoCertificationLevel = "fips140-3-l3"
CryptoCertificationLevelFIPS140_3_L4 CryptoCertificationLevel = "fips140-3-l4"
CryptoCertificationLevelCCEAL1 CryptoCertificationLevel = "cc-eal1"
CryptoCertificationLevelCCEAL1Plus CryptoCertificationLevel = "cc-eal1+"
CryptoCertificationLevelCCEAL2 CryptoCertificationLevel = "cc-eal2"
CryptoCertificationLevelCCEAL2Plus CryptoCertificationLevel = "cc-eal2+"
CryptoCertificationLevelCCEAL3 CryptoCertificationLevel = "cc-eal3"
CryptoCertificationLevelCCEAL3Plus CryptoCertificationLevel = "cc-eal3+"
CryptoCertificationLevelCCEAL4 CryptoCertificationLevel = "cc-eal4"
CryptoCertificationLevelCCEAL4Plus CryptoCertificationLevel = "cc-eal4+"
CryptoCertificationLevelCCEAL5 CryptoCertificationLevel = "cc-eal5"
CryptoCertificationLevelCCEAL5Plus CryptoCertificationLevel = "cc-eal5+"
CryptoCertificationLevelCCEAL6 CryptoCertificationLevel = "cc-eal6"
CryptoCertificationLevelCCEAL6Plus CryptoCertificationLevel = "cc-eal6+"
CryptoCertificationLevelCCEAL7 CryptoCertificationLevel = "cc-eal7"
CryptoCertificationLevelCCEAL7Plus CryptoCertificationLevel = "cc-eal7+"
CryptoCertificationLevelOther CryptoCertificationLevel = "other"
CryptoCertificationLevelUnknown CryptoCertificationLevel = "unknown"
)
type CryptoExecutionEnvironment string
const (
CryptoExecutionEnvironmentSoftwarePlainRAM CryptoExecutionEnvironment = "software-plain-ram"
CryptoExecutionEnvironmentSoftwareEncryptedRAM CryptoExecutionEnvironment = "software-encrypted-ram"
CryptoExecutionEnvironmentSoftwareTEE CryptoExecutionEnvironment = "software-tee"
CryptoExecutionEnvironmentHardware CryptoExecutionEnvironment = "hardware"
CryptoExecutionEnvironmentOther CryptoExecutionEnvironment = "other"
CryptoExecutionEnvironmentUnknown CryptoExecutionEnvironment = "unknown"
)
type CryptoFunction string
const (
CryptoFunctionGenerate CryptoFunction = "generate"
CryptoFunctionKeygen CryptoFunction = "keygen"
CryptoFunctionEncrypt CryptoFunction = "encrypt"
CryptoFunctionDecrypt CryptoFunction = "decrypt"
CryptoFunctionDigest CryptoFunction = "digest"
CryptoFunctionTag CryptoFunction = "tag"
CryptoFunctionKeyderive CryptoFunction = "keyderive"
CryptoFunctionSign CryptoFunction = "sign"
CryptoFunctionVerify CryptoFunction = "verify"
CryptoFunctionEncapsulate CryptoFunction = "encapsulate"
CryptoFunctionDecapsulate CryptoFunction = "decapsulate"
CryptoFunctionOther CryptoFunction = "other"
CryptoFunctionUnknown CryptoFunction = "unknown"
)
type CryptoKeyState string
const (
CryptoKeyStatePreActivation CryptoKeyState = "pre-activation"
CryptoKeyStateActive CryptoKeyState = "active"
CryptoKeyStateSuspended CryptoKeyState = "suspended"
CryptoKeyStateDeactivated CryptoKeyState = "deactivated"
CryptoKeyStateCompromised CryptoKeyState = "compromised"
CryptoKeyStateDestroyed CryptoKeyState = "destroyed"
)
type CryptoPadding string
const (
CryptoPaddingPKCS5 CryptoPadding = "pkcs5"
CryptoPaddingPKCS7 CryptoPadding = "pkcs7"
CryptoPaddingPKCS1v15 CryptoPadding = "pkcs1v15"
CryptoPaddingOAEP CryptoPadding = "oaep"
CryptoPaddingRaw CryptoPadding = "raw"
CryptoPaddingOther CryptoPadding = "other"
CryptoPaddingUnknown CryptoPadding = "unknown"
)
type CryptoPrimitive string
const (
CryptoPrimitiveDRBG CryptoPrimitive = "drbg"
CryptoPrimitiveMAC CryptoPrimitive = "mac"
CryptoPrimitiveBlockCipher CryptoPrimitive = "block-cipher"
CryptoPrimitiveStreamCipher CryptoPrimitive = "stream-cipher"
CryptoPrimitiveSignature CryptoPrimitive = "signature"
CryptoPrimitiveHash CryptoPrimitive = "hash"
CryptoPrimitivePKE CryptoPrimitive = "pke"
CryptoPrimitiveXOF CryptoPrimitive = "xof"
CryptoPrimitiveKDF CryptoPrimitive = "kdf"
CryptoPrimitiveKeyAgree CryptoPrimitive = "key-agree"
CryptoPrimitiveKEM CryptoPrimitive = "kem"
CryptoPrimitiveAE CryptoPrimitive = "ae"
CryptoPrimitiveCombiner CryptoPrimitive = "combiner"
CryptoPrimitiveOther CryptoPrimitive = "other"
CryptoPrimitiveUnknown CryptoPrimitive = "unknown"
)
type CryptoProperties struct {
AssetType CryptoAssetType `json:"assetType" xml:"assetType"`
AlgorithmProperties *CryptoAlgorithmProperties `json:"algorithmProperties,omitempty" xml:"algorithmProperties,omitempty"`
CertificateProperties *CertificateProperties `json:"certificateProperties,omitempty" xml:"certificateProperties,omitempty"`
RelatedCryptoMaterialProperties *RelatedCryptoMaterialProperties `json:"relatedCryptoMaterialProperties,omitempty" xml:"relatedCryptoMaterialProperties,omitempty"`
ProtocolProperties *CryptoProtocolProperties `json:"protocolProperties,omitempty" xml:"protocolProperties,omitempty"`
OID string `json:"oid,omitempty" xml:"oid,omitempty"`
}
type CryptoProtocolProperties struct {
Type CryptoProtocolType `json:"type,omitempty" xml:"type,omitempty"`
Version string `json:"version,omitempty" xml:"version,omitempty"`
CipherSuites *[]CipherSuite `json:"cipherSuites,omitempty" xml:"cipherSuites,omitempty"`
IKEv2TransformTypes *IKEv2TransformTypes `json:"ikev2TransformTypes,omitempty" xml:"ikev2TransformTypes,omitempty"`
CryptoRefArray *[]BOMReference `json:"cryptoRefArray,omitempty" xml:"cryptoRefArray,omitempty"`
}
type CryptoProtocolType string
const (
CryptoProtocolTypeTLS CryptoProtocolType = "tls"
CryptoProtocolTypeSSH CryptoProtocolType = "ssh"
CryptoProtocolTypeIPSec CryptoProtocolType = "ipsec"
CryptoProtocolTypeIKE CryptoProtocolType = "ike"
CryptoProtocolTypeSSTP CryptoProtocolType = "sstp"
CryptoProtocolTypeWPA CryptoProtocolType = "wpa"
CryptoProtocolTypeOther CryptoProtocolType = "other"
CryptoProtocolTypeUnknown CryptoProtocolType = "unknown"
)
type IKEv2TransformTypes struct {
Encr *[]BOMReference `json:"encr,omitempty" xml:"encr,omitempty"`
PRF *[]BOMReference `json:"prf,omitempty" xml:"prf,omitempty"`
Integ *[]BOMReference `json:"integ,omitempty" xml:"integ,omitempty"`
KE *[]BOMReference `json:"ke,omitempty" xml:"ke,omitempty"`
ESN bool `json:"esn" xml:"esn"`
Auth *[]BOMReference `json:"auth,omitempty" xml:"auth,omitempty"`
}
type SecuredBy struct {
Mechanism string `json:"mechanism,omitempty" xml:"mechanism,omitempty"`
AlgorithmRef BOMReference `json:"algorithmRef,omitempty" xml:"algorithmRef,omitempty"`
}
type DataClassification struct {
Flow DataFlow `json:"flow" xml:"flow,attr"`
Classification string `json:"classification" xml:",chardata"`
}
type DataFlow string
const (
DataFlowBidirectional DataFlow = "bi-directional"
DataFlowInbound DataFlow = "inbound"
DataFlowOutbound DataFlow = "outbound"
DataFlowUnknown DataFlow = "unknown"
)
type DataGovernance struct {
Custodians *[]ComponentDataGovernanceResponsibleParty `json:"custodians,omitempty" xml:"custodians>custodian,omitempty"`
Stewards *[]ComponentDataGovernanceResponsibleParty `json:"stewards,omitempty" xml:"stewards>steward,omitempty"`
Owners *[]ComponentDataGovernanceResponsibleParty `json:"owners,omitempty" xml:"owners>owner,omitempty"`
}
type Declarations struct {
Assessors *[]Assessor `json:"assessors,omitempty" xml:"assessors>assessor,omitempty"`
Attestations *[]Attestation `json:"attestations,omitempty" xml:"attestations>attestation,omitempty"`
Claims *[]Claim `json:"claims,omitempty" xml:"claims>claim,omitempty"`
Evidence *[]DeclarationEvidence `json:"evidence,omitempty" xml:"evidence>evidence,omitempty"`
Targets *Targets `json:"targets,omitempty" xml:"targets,omitempty"`
Affirmation *Affirmation `json:"affirmation,omitempty" xml:"affirmation,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type DeclarationEvidence struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
PropertyName string `json:"propertyName,omitempty" xml:"propertyName,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Data *[]EvidenceData `json:"data,omitempty" xml:"data,omitempty"`
Created string `json:"created,omitempty" xml:"created,omitempty"`
Expires string `json:"expires,omitempty" xml:"expires,omitempty"`
Author *OrganizationalContact `json:"author,omitempty" xml:"author,omitempty"`
Reviewer *OrganizationalContact `json:"reviewer,omitempty" xml:"reviewer,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type Definitions struct {
Standards *[]StandardDefinition `json:"standards,omitempty" xml:"standards>standard,omitempty"`
}
type EvidenceData struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
Contents *EvidenceDataContents `json:"contents,omitempty" xml:"contents,omitempty"`
Classification *DataClassification `json:"classification,omitempty" xml:"data>classification,omitempty"`
SensitiveData *[]string `json:"sensitiveData,omitempty" xml:"sensitiveData,omitempty"`
Governance *DataGovernance `json:"governance,omitempty" xml:"governance,omitempty"`
}
type EvidenceDataContents struct {
Attachment *AttachedText `json:"attachment,omitempty" xml:"attachment,omitempty"`
URL string `json:"url,omitempty" xml:"url,omitempty"`
}
type Targets struct {
Organizations *[]OrganizationalEntity `json:"organizations,omitempty" xml:"organizations>organization,omitempty"`
Components *[]Component `json:"components,omitempty" xml:"components>component,omitempty"`
Services *[]Service `json:"services,omitempty" xml:"services>service,omitempty"`
}
type Affirmation struct {
Statement string `json:"statement,omitempty" xml:"statement,omitempty"`
Signatories *[]Signatory `json:"signatories,omitempty" xml:"signatories>signatory,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type Signatory struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
Role string `json:"role,omitempty" xml:"role,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
Organization *OrganizationalEntity `json:"organization,omitempty" xml:"organization,omitempty"`
ExternalReference *ExternalReference `json:"externalReference,omitempty" xml:"externalReference,omitempty"`
}
type Dependency struct {
Ref string `json:"ref"`
Dependencies *[]string `json:"dependsOn,omitempty"`
}
type Diff struct {
Text *AttachedText `json:"text,omitempty" xml:"text,omitempty"`
URL string `json:"url,omitempty" xml:"url,omitempty"`
}
type EnvironmentVariables []EnvironmentVariableChoice
type EnvironmentVariableChoice struct {
Property *Property `json:"-" xml:"-"`
Value string `json:"-" xml:"-"`
}
type Event struct {
UID string `json:"uid,omitempty" xml:"uid,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
TimeReceived string `json:"timeReceived,omitempty" xml:"timeReceived,omitempty"`
Data *AttachedText `json:"data,omitempty" xml:"data,omitempty"`
Source *ResourceReferenceChoice `json:"source,omitempty" xml:"source,omitempty"`
Target *ResourceReferenceChoice `json:"target,omitempty" xml:"target,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type Evidence struct {
Identity *[]EvidenceIdentity `json:"identity,omitempty" xml:"-"`
Occurrences *[]EvidenceOccurrence `json:"occurrences,omitempty" xml:"-"`
Callstack *Callstack `json:"callstack,omitempty" xml:"-"`
Licenses *Licenses `json:"licenses,omitempty" xml:"-"`
Copyright *[]Copyright `json:"copyright,omitempty" xml:"-"`
}
type EvidenceIdentity struct {
Field EvidenceIdentityFieldType `json:"field,omitempty" xml:"field,omitempty"`
Confidence *float32 `json:"confidence,omitempty" xml:"confidence,omitempty"`
Methods *[]EvidenceIdentityMethod `json:"methods,omitempty" xml:"methods>method,omitempty"`
Tools *[]BOMReference `json:"tools,omitempty" xml:"tools>tool,omitempty"`
}
type EvidenceIdentityFieldType string
const (
EvidenceIdentityFieldTypeCPE EvidenceIdentityFieldType = "cpe"
EvidenceIdentityFieldTypeGroup EvidenceIdentityFieldType = "group"
EvidenceIdentityFieldTypeHash EvidenceIdentityFieldType = "hash"
EvidenceIdentityFieldTypeName EvidenceIdentityFieldType = "name"
EvidenceIdentityFieldTypePURL EvidenceIdentityFieldType = "purl"
EvidenceIdentityFieldTypeOmniborID EvidenceIdentityFieldType = "omniborId"
EvidenceIdentityFieldTypeSWHID EvidenceIdentityFieldType = "swhid"
EvidenceIdentityFieldTypeSWID EvidenceIdentityFieldType = "swid"
EvidenceIdentityFieldTypeVersion EvidenceIdentityFieldType = "version"
)
type EvidenceIdentityMethod struct {
Technique EvidenceIdentityTechnique `json:"technique,omitempty" xml:"technique,omitempty"`
Confidence *float32 `json:"confidence,omitempty" xml:"confidence,omitempty"`
Value string `json:"value,omitempty" xml:"value,omitempty"`
}
type EvidenceIdentityTechnique string
const (
EvidenceIdentityTechniqueASTFingerprint EvidenceIdentityTechnique = "ast-fingerprint"
EvidenceIdentityTechniqueAttestation EvidenceIdentityTechnique = "attestation"
EvidenceIdentityTechniqueBinaryAnalysis EvidenceIdentityTechnique = "binary-analysis"
EvidenceIdentityTechniqueDynamicAnalysis EvidenceIdentityTechnique = "dynamic-analysis"
EvidenceIdentityTechniqueFilename EvidenceIdentityTechnique = "filename"
EvidenceIdentityTechniqueHashComparison EvidenceIdentityTechnique = "hash-comparison"
EvidenceIdentityTechniqueInstrumentation EvidenceIdentityTechnique = "instrumentation"
EvidenceIdentityTechniqueManifestAnalysis EvidenceIdentityTechnique = "manifest-analysis"
EvidenceIdentityTechniqueOther EvidenceIdentityTechnique = "other"
EvidenceIdentityTechniqueSourceCodeAnalysis EvidenceIdentityTechnique = "source-code-analysis"
)
type EvidenceOccurrence struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Location string `json:"location,omitempty" xml:"location,omitempty"`
Line *int `json:"line,omitempty" xml:"line,attr,omitempty"`
Offset *int `json:"offset,omitempty" xml:"offset,attr,omitempty"`
Symbol string `json:"symbol,omitempty" xml:"symbol,attr,omitempty"`
AdditionalContext string `json:"additionalContext,omitempty" xml:"additionalContext,attr,omitempty"`
}
type ExternalReference struct {
URL string `json:"url" xml:"url"`
Comment string `json:"comment,omitempty" xml:"comment,omitempty"`
Hashes *[]Hash `json:"hashes,omitempty" xml:"hashes>hash,omitempty"`
Type ExternalReferenceType `json:"type" xml:"type,attr"`
}
type ExternalReferenceType string
const (
ERTypeAdversaryModel ExternalReferenceType = "adversary-model"
ERTypeAdvisories ExternalReferenceType = "advisories"
ERTypeAttestation ExternalReferenceType = "attestation"
ERTypeBOM ExternalReferenceType = "bom"
ERTypeBuildMeta ExternalReferenceType = "build-meta"
ERTypeBuildSystem ExternalReferenceType = "build-system"
ERTypeCertificationReport ExternalReferenceType = "certification-report"
ERTypeChat ExternalReferenceType = "chat"
ERTypeConfiguration ExternalReferenceType = "configuration"
ERTypeCodifiedInfrastructure ExternalReferenceType = "codified-infrastructure"
ERTypeComponentAnalysisReport ExternalReferenceType = "component-analysis-report"
ERTypeDistribution ExternalReferenceType = "distribution"
ERTypeDistributionIntake ExternalReferenceType = "distribution-intake"
ERTypeDocumentation ExternalReferenceType = "documentation"
ERTypeDynamicAnalysisReport ExternalReferenceType = "dynamic-analysis-report"
ERTypeEvidence ExternalReferenceType = "evidence"
ERTypeExploitabilityStatement ExternalReferenceType = "exploitability-statement"
ERTypeFormulation ExternalReferenceType = "formulation"
ERTypeIssueTracker ExternalReferenceType = "issue-tracker"
ERTypeLicense ExternalReferenceType = "license"
ERTypeLog ExternalReferenceType = "log"
ERTypeMailingList ExternalReferenceType = "mailing-list"
ERTypeMaturityReport ExternalReferenceType = "maturity-report"
ERTypeModelCard ExternalReferenceType = "model-card"
ERTypeOther ExternalReferenceType = "other"
ERTypePentestReport ExternalReferenceType = "pentest-report"
ERTypeQualityMetrics ExternalReferenceType = "quality-metrics"
ERTypeReleaseNotes ExternalReferenceType = "release-notes"
ERTypeRiskAssessment ExternalReferenceType = "risk-assessment"
ERTypeRuntimeAnalysisReport ExternalReferenceType = "runtime-analysis-report"
ERTypeSecurityContact ExternalReferenceType = "security-contact"
ERTypeSocial ExternalReferenceType = "social"
ERTypeStaticAnalysisReport ExternalReferenceType = "static-analysis-report"
ERTypeSupport ExternalReferenceType = "support"
ERTypeThreatModel ExternalReferenceType = "threat-model"
ERTypeVCS ExternalReferenceType = "vcs"
ERTypeVulnerabilityAssertion ExternalReferenceType = "vulnerability-assertion"
ERTypeWebsite ExternalReferenceType = "website"
)
type Formula struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Components *[]Component `json:"components,omitempty" xml:"components>component,omitempty"`
Services *[]Service `json:"services,omitempty" xml:"services>service,omitempty"`
Workflows *[]Workflow `json:"workflows,omitempty" xml:"workflows>workflow,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type Hash struct {
Algorithm HashAlgorithm `json:"alg" xml:"alg,attr"`
Value string `json:"content" xml:",chardata"`
}
type HashAlgorithm string
const (
HashAlgoMD5 HashAlgorithm = "MD5"
HashAlgoSHA1 HashAlgorithm = "SHA-1"
HashAlgoSHA256 HashAlgorithm = "SHA-256"
HashAlgoSHA384 HashAlgorithm = "SHA-384"
HashAlgoSHA512 HashAlgorithm = "SHA-512"
HashAlgoSHA3_256 HashAlgorithm = "SHA3-256"
HashAlgoSHA3_384 HashAlgorithm = "SHA3-384"
HashAlgoSHA3_512 HashAlgorithm = "SHA3-512"
HashAlgoBlake2b_256 HashAlgorithm = "BLAKE2b-256"
HashAlgoBlake2b_384 HashAlgorithm = "BLAKE2b-384"
HashAlgoBlake2b_512 HashAlgorithm = "BLAKE2b-512"
HashAlgoBlake3 HashAlgorithm = "BLAKE3"
)
type IdentifiableAction struct {
Timestamp string `json:"timestamp,omitempty" xml:"timestamp,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Email string `json:"email,omitempty" xml:"email,omitempty"`
}
type ImpactAnalysisJustification string
const (
IAJCodeNotPresent ImpactAnalysisJustification = "code_not_present"
IAJCodeNotReachable ImpactAnalysisJustification = "code_not_reachable"
IAJRequiresConfiguration ImpactAnalysisJustification = "requires_configuration"
IAJRequiresDependency ImpactAnalysisJustification = "requires_dependency"
IAJRequiresEnvironment ImpactAnalysisJustification = "requires_environment"
IAJProtectedByCompiler ImpactAnalysisJustification = "protected_by_compiler"
IAJProtectedAtRuntime ImpactAnalysisJustification = "protected_at_runtime"
IAJProtectedAtPerimeter ImpactAnalysisJustification = "protected_at_perimeter"
IAJProtectedByMitigatingControl ImpactAnalysisJustification = "protected_by_mitigating_control"
)
type ImpactAnalysisResponse string
const (
IARCanNotFix ImpactAnalysisResponse = "can_not_fix"
IARWillNotFix ImpactAnalysisResponse = "will_not_fix"
IARUpdate ImpactAnalysisResponse = "update"
IARRollback ImpactAnalysisResponse = "rollback"
IARWorkaroundAvailable ImpactAnalysisResponse = "workaround_available"
)
type ImpactAnalysisState string
const (
IASResolved ImpactAnalysisState = "resolved"
IASResolvedWithPedigree ImpactAnalysisState = "resolved_with_pedigree"
IASExploitable ImpactAnalysisState = "exploitable"
IASInTriage ImpactAnalysisState = "in_triage"
IASFalsePositive ImpactAnalysisState = "false_positive"
IASNotAffected ImpactAnalysisState = "not_affected"
)
type ImplementationPlatform string
const (
ImplementationPlatformGeneric ImplementationPlatform = "generic"
ImplementationPlatformX86_32 ImplementationPlatform = "x86_32"
ImplementationPlatformX86_64 ImplementationPlatform = "x86_64"
ImplementationPlatformARMv7A ImplementationPlatform = "armv7-a"
ImplementationPlatformARMv7M ImplementationPlatform = "armv7-m"
ImplementationPlatformARMv8A ImplementationPlatform = "armv8-a"
ImplementationPlatformARMv8M ImplementationPlatform = "armv8-m"
ImplementationPlatformARMv9A ImplementationPlatform = "armv9-a"
ImplementationPlatformARMv9M ImplementationPlatform = "armv9-m"
ImplementationPlatformS390x ImplementationPlatform = "s390x"
ImplementationPlatformPPC64 ImplementationPlatform = "ppc64"
ImplementationPlatformPPC64LE ImplementationPlatform = "ppc64le"
ImplementationPlatformOther ImplementationPlatform = "other"
ImplementationPlatformUnknown ImplementationPlatform = "unknown"
)
type Issue struct {
ID string `json:"id" xml:"id"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Description string `json:"description" xml:"description"`
Source *Source `json:"source,omitempty" xml:"source,omitempty"`
References *[]string `json:"references,omitempty" xml:"references>url,omitempty"`
Type IssueType `json:"type" xml:"type,attr"`
}
type IssueType string
const (
IssueTypeDefect IssueType = "defect"
IssueTypeEnhancement IssueType = "enhancement"
IssueTypeSecurity IssueType = "security"
)
type JSFSignature struct {
*JSFSigner `json:"-" xml:"-"`
Signers *[]JSFSigner `json:"signers,omitempty" xml:"-"`
Chain *[]JSFSigner `json:"chain,omitempty" xml:"-"`
}
type JSFSigner struct {
Algorithm string `json:"algorithm" xml:"-"`
KeyID string `json:"keyId,omitempty" xml:"-"`
PublicKey JSFPublicKey `json:"publicKey,omitempty" xml:"-"`
CertificatePath *[]string `json:"certificatePath,omitempty" xml:"-"`
Excludes *[]string `json:"excludes,omitempty" xml:"-"`
Value string `json:"value" xml:"-"`
}
type JSFPublicKey struct {
KTY string `json:"kty,omitempty" xml:"-"`
CRV string `json:"crv,omitempty" xml:"-"`
X string `json:"x,omitempty" xml:"-"`
Y string `json:"y,omitempty" xml:"-"`
N string `json:"n,omitempty" xml:"-"`
E string `json:"e,omitempty" xml:"-"`
}
type License struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
ID string `json:"id,omitempty" xml:"id,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Acknowledgement LicenseAcknowledgement `json:"acknowledgement,omitempty" xml:"acknowledgement,attr,omitempty"`
Text *AttachedText `json:"text,omitempty" xml:"text,omitempty"`
URL string `json:"url,omitempty" xml:"url,omitempty"`
Licensing *Licensing `json:"licensing,omitempty" xml:"licensing,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type LicenseAcknowledgement string
const (
LicenseAcknowledgementDeclared LicenseAcknowledgement = "declared"
LicenseAcknowledgementConcluded LicenseAcknowledgement = "concluded"
)
type Licenses []LicenseChoice
type LicenseChoice struct {
License *License `json:"license,omitempty" xml:"-"`
Expression string `json:"expression,omitempty" xml:"-"`
}
type LicenseType string
const (
LicenseTypeAcademic LicenseType = "academic"
LicenseTypeAppliance LicenseType = "appliance"
LicenseTypeClientAccess LicenseType = "client-access"
LicenseTypeConcurrentUser LicenseType = "concurrent-user"
LicenseTypeCorePoints LicenseType = "core-points"
LicenseTypeCustomMetric LicenseType = "custom-metric"
LicenseTypeDevice LicenseType = "device"
LicenseTypeEvaluation LicenseType = "evaluation"
LicenseTypeNamedUser LicenseType = "named-user"
LicenseTypeNodeLocked LicenseType = "node-locked"
LicenseTypeOEM LicenseType = "oem"
LicenseTypeOther LicenseType = "other"
LicenseTypePerpetual LicenseType = "perpetual"
LicenseTypeProcessorPoints LicenseType = "processor-points"
LicenseTypeSubscription LicenseType = "subscription"
LicenseTypeUser LicenseType = "user"
)
type Licensing struct {
AltIDs *[]string `json:"altIds,omitempty" xml:"altIds>altId,omitempty"`
Licensor *OrganizationalEntityOrContact `json:"licensor,omitempty" xml:"licensor,omitempty"`
Licensee *OrganizationalEntityOrContact `json:"licensee,omitempty" xml:"licensee,omitempty"`
Purchaser *OrganizationalEntityOrContact `json:"purchaser,omitempty" xml:"purchaser,omitempty"`
PurchaseOrder string `json:"purchaseOrder,omitempty" xml:"purchaseOrder,omitempty"`
LicenseTypes *[]LicenseType `json:"licenseTypes,omitempty" xml:"licenseTypes>licenseType,omitempty"`
LastRenewal string `json:"lastRenewal,omitempty" xml:"lastRenewal,omitempty"`
Expiration string `json:"expiration,omitempty" xml:"expiration,omitempty"`
}
type Lifecycle struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
Phase LifecyclePhase `json:"phase,omitempty" xml:"phase,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
}
type LifecyclePhase string
const (
LifecyclePhaseBuild LifecyclePhase = "build"
LifecyclePhaseDecommission LifecyclePhase = "decommission"
LifecyclePhaseDesign LifecyclePhase = "design"
LifecyclePhaseDiscovery LifecyclePhase = "discovery"
LifecyclePhaseOperations LifecyclePhase = "operations"
LifecyclePhasePostBuild LifecyclePhase = "post-build"
LifecyclePhasePreBuild LifecyclePhase = "pre-build"
)
// MediaType defines the official media types for CycloneDX BOMs.
// See https://cyclonedx.org/specification/overview/#registered-media-types
type MediaType int
const (
MediaTypeJSON MediaType = iota + 1 // application/vnd.cyclonedx+json
MediaTypeXML // application/vnd.cyclonedx+xml
MediaTypeProtobuf // application/x.vnd.cyclonedx+protobuf
)
func (mt MediaType) WithVersion(specVersion SpecVersion) (string, error) {
if mt == MediaTypeJSON && specVersion < SpecVersion1_2 {
return "", fmt.Errorf("json format is not supported for specification versions lower than %s", SpecVersion1_2)
}
return fmt.Sprintf("%s; version=%s", mt, specVersion), nil
}
type Metadata struct {
Timestamp string `json:"timestamp,omitempty" xml:"timestamp,omitempty"`
Lifecycles *[]Lifecycle `json:"lifecycles,omitempty" xml:"lifecycles>lifecycle,omitempty"`
Tools *ToolsChoice `json:"tools,omitempty" xml:"tools,omitempty"`
Authors *[]OrganizationalContact `json:"authors,omitempty" xml:"authors>author,omitempty"`
Component *Component `json:"component,omitempty" xml:"component,omitempty"`
Manufacture *OrganizationalEntity `json:"manufacture,omitempty" xml:"manufacture,omitempty"` // Deprecated: Use Component Manufacturer instead.
Manufacturer *OrganizationalEntity `json:"manufacturer,omitempty" xml:"manufacturer,omitempty"`
Supplier *OrganizationalEntity `json:"supplier,omitempty" xml:"supplier,omitempty"`
Licenses *Licenses `json:"licenses,omitempty" xml:"licenses,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type MLDatasetChoice struct {
Ref string `json:"-" xml:"-"`
ComponentData *ComponentData `json:"-" xml:"-"`
}
type MLInputOutputParameters struct {
Format string `json:"format,omitempty" xml:"format,omitempty"`
}
type MLModelCard struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
ModelParameters *MLModelParameters `json:"modelParameters,omitempty" xml:"modelParameters,omitempty"`
QuantitativeAnalysis *MLQuantitativeAnalysis `json:"quantitativeAnalysis,omitempty" xml:"quantitativeAnalysis,omitempty"`
Considerations *MLModelCardConsiderations `json:"considerations,omitempty" xml:"considerations,omitempty"`
}
type MLModelCardConsiderations struct {
Users *[]string `json:"users,omitempty" xml:"users>user,omitempty"`
UseCases *[]string `json:"useCases,omitempty" xml:"useCases>useCase,omitempty"`
TechnicalLimitations *[]string `json:"technicalLimitations,omitempty" xml:"technicalLimitations>technicalLimitation,omitempty"`
PerformanceTradeoffs *[]string `json:"performanceTradeoffs,omitempty" xml:"performanceTradeoffs>performanceTradeoff,omitempty"`
EthicalConsiderations *[]MLModelCardEthicalConsideration `json:"ethicalConsiderations,omitempty" xml:"ethicalConsiderations>ethicalConsideration,omitempty"`
EnvironmentalConsiderations *MLModelCardEnvironmentalConsiderations `json:"environmentalConsiderations,omitempty" xml:"environmentalConsiderations,omitempty"`
FairnessAssessments *[]MLModelCardFairnessAssessment `json:"fairnessAssessments,omitempty" xml:"fairnessAssessments>fairnessAssessment,omitempty"`
}
type MLModelCardEnvironmentalConsiderations struct {
EnergyConsumptions *[]MLModelEnergyConsumption `json:"energyConsumptions,omitempty" xml:"energyConsumptions>energyConsumption,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type MLModelCardEthicalConsideration struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
MitigationStrategy string `json:"mitigationStrategy,omitempty" xml:"mitigationStrategy,omitempty"`
}
type MLModelCardFairnessAssessment struct {
GroupAtRisk string `json:"groupAtRisk,omitempty" xml:"groupAtRisk,omitempty"`
Benefits string `json:"benefits,omitempty" xml:"benefits,omitempty"`
Harms string `json:"harms,omitempty" xml:"harms,omitempty"`
MitigationStrategy string `json:"mitigationStrategy,omitempty" xml:"mitigationStrategy,omitempty"`
}
type MLModelCO2Measure struct {
Value float32 `json:"value" xml:"value"`
Unit MLModelCO2Unit `json:"unit" xml:"unit"`
}
type MLModelCO2Unit string
const MLModelCO2UnitTCO2Eq MLModelCO2Unit = "tCO2eq"
type MLModelEnergyConsumption struct {
Activity MLModelEnergyConsumptionActivity `json:"activity" xml:"activity"`
EnergyProviders *[]MLModelEnergyProvider `json:"energyProviders" xml:"energyProviders"`
ActivityEnergyCost MLModelEnergyMeasure `json:"activityEnergyCost" xml:"activityEnergyCost"`
CO2CostEquivalent *MLModelCO2Measure `json:"co2CostEquivalent,omitempty" xml:"co2CostEquivalent,omitempty"`
CO2CostOffset *MLModelCO2Measure `json:"co2CostOffset,omitempty" xml:"co2CostOffset,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type MLModelEnergyConsumptionActivity string
const (
MLModelEnergyConsumptionActivityDesign MLModelEnergyConsumptionActivity = "design"
MLModelEnergyConsumptionActivityDataCollection MLModelEnergyConsumptionActivity = "data-collection"
MLModelEnergyConsumptionActivityDataPreparation MLModelEnergyConsumptionActivity = "data-preparation"
MLModelEnergyConsumptionActivityTraining MLModelEnergyConsumptionActivity = "training"
MLModelEnergyConsumptionActivityFineTuning MLModelEnergyConsumptionActivity = "fine-tuning"
MLModelEnergyConsumptionActivityValidation MLModelEnergyConsumptionActivity = "validation"
MLModelEnergyConsumptionActivityDeployment MLModelEnergyConsumptionActivity = "deployment"
MLModelEnergyConsumptionActivityInference MLModelEnergyConsumptionActivity = "inference"
MLModelEnergyConsumptionActivityOther MLModelEnergyConsumptionActivity = "other"
)
type MLModelEnergyMeasure struct {
Value float32 `json:"value" xml:"value"`
Unit MLModelEnergyUnit `json:"unit" xml:"unit"`
}
type MLModelEnergyProvider struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Organization *OrganizationalEntity `json:"organization" xml:"organization"`
EnergySource MLModelEnergySource `json:"energySource" xml:"energySource"`
EnergyProvided *MLModelEnergyMeasure `json:"energyProvided" xml:"energyProvided"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
}
type MLModelEnergySource string
const (
MLModelEnergySourceCoal MLModelEnergySource = "coal"
MLModelEnergySourceOil MLModelEnergySource = "oil"
MLModelEnergySourceNaturalGas MLModelEnergySource = "natural-gas"
MLModelEnergySourceNuclear MLModelEnergySource = "nuclear"
MLModelEnergySourceWind MLModelEnergySource = "wind"
MLModelEnergySourceSolar MLModelEnergySource = "solar"
MLModelEnergySourceGeothermal MLModelEnergySource = "geothermal"
MLModelEnergySourceHydropower MLModelEnergySource = "hydropower"
MLModelEnergySourceBiofuel MLModelEnergySource = "biofuel"
MLModelEnergySourceUnknown MLModelEnergySource = "unknown"
MLModelEnergySourceOther MLModelEnergySource = "other"
)
type MLModelEnergyUnit string
const MLModelEnergyUnitKWH MLModelEnergyUnit = "kWh"
type MLModelParameters struct {
Approach *MLModelParametersApproach `json:"approach,omitempty" xml:"approach,omitempty"`
Task string `json:"task,omitempty" xml:"task,omitempty"`
ArchitectureFamily string `json:"architectureFamily,omitempty" xml:"architectureFamily,omitempty"`
ModelArchitecture string `json:"modelArchitecture,omitempty" xml:"modelArchitecture,omitempty"`
Datasets *[]MLDatasetChoice `json:"datasets,omitempty" xml:"datasets>dataset,omitempty"`
Inputs *[]MLInputOutputParameters `json:"inputs,omitempty" xml:"inputs>input,omitempty"`
Outputs *[]MLInputOutputParameters `json:"outputs,omitempty" xml:"outputs>output,omitempty"`
}
type MLModelParametersApproach struct {
Type MLModelParametersApproachType `json:"type,omitempty" xml:"type,omitempty"`
}
type MLModelParametersApproachType string
const (
MLModelParametersApproachTypeSupervised MLModelParametersApproachType = "supervised"
MLModelParametersApproachTypeUnsupervised MLModelParametersApproachType = "unsupervised"
MLModelParametersApproachTypeReinforcementLearning MLModelParametersApproachType = "reinforcement-learning"
MLModelParametersApproachTypeSemiSupervised MLModelParametersApproachType = "semi-supervised"
MLModelParametersApproachTypeSelfSupervised MLModelParametersApproachType = "self-supervised"
)
type MLQuantitativeAnalysis struct {
PerformanceMetrics *[]MLPerformanceMetric `json:"performanceMetrics,omitempty" xml:"performanceMetrics>performanceMetric,omitempty"`
Graphics *ComponentDataGraphics `json:"graphics,omitempty" xml:"graphics,omitempty"`
}
type MLPerformanceMetric struct {
Type string `json:"type,omitempty" xml:"type,omitempty"`
Value string `json:"value,omitempty" xml:"value,omitempty"`
Slice string `json:"slice,omitempty" xml:"slice,omitempty"`
ConfidenceInterval *MLPerformanceMetricConfidenceInterval `json:"confidenceInterval,omitempty" xml:"confidenceInterval,omitempty"`
}
type MLPerformanceMetricConfidenceInterval struct {
LowerBound string `json:"lowerBound,omitempty" xml:"lowerBound,omitempty"`
UpperBound string `json:"upperBound,omitempty" xml:"upperBound,omitempty"`
}
type Note struct {
Locale string `json:"locale,omitempty" xml:"locale,omitempty"`
Text AttachedText `json:"text" xml:"text"`
}
type OrganizationalContact struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Email string `json:"email,omitempty" xml:"email,omitempty"`
Phone string `json:"phone,omitempty" xml:"phone,omitempty"`
}
type OrganizationalEntity struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Name string `json:"name" xml:"name"`
Address *PostalAddress `json:"address,omitempty" xml:"address,omitempty"`
URL *[]string `json:"url,omitempty" xml:"url,omitempty"`
Contact *[]OrganizationalContact `json:"contact,omitempty" xml:"contact,omitempty"`
}
type OrganizationalEntityOrContact struct {
Organization *OrganizationalEntity `json:"organization,omitempty" xml:"organization,omitempty"`
Individual *OrganizationalContact `json:"individual,omitempty" xml:"individual,omitempty"`
}
type Parameter struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
Value string `json:"value,omitempty" xml:"value,omitempty"`
DataType string `json:"dataType,omitempty" xml:"dataType,omitempty"`
}
type Patch struct {
Diff *Diff `json:"diff,omitempty" xml:"diff,omitempty"`
Resolves *[]Issue `json:"resolves,omitempty" xml:"resolves>issue,omitempty"`
Type PatchType `json:"type" xml:"type,attr"`
}
type PatchType string
const (
PatchTypeBackport PatchType = "backport"
PatchTypeCherryPick PatchType = "cherry-pick"
PatchTypeMonkey PatchType = "monkey"
PatchTypeUnofficial PatchType = "unofficial"
)
type Pedigree struct {
Ancestors *[]Component `json:"ancestors,omitempty" xml:"ancestors>component,omitempty"`
Descendants *[]Component `json:"descendants,omitempty" xml:"descendants>component,omitempty"`
Variants *[]Component `json:"variants,omitempty" xml:"variants>component,omitempty"`
Commits *[]Commit `json:"commits,omitempty" xml:"commits>commit,omitempty"`
Patches *[]Patch `json:"patches,omitempty" xml:"patches>patch,omitempty"`
Notes string `json:"notes,omitempty" xml:"notes,omitempty"`
}
type PostalAddress struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Country string `json:"country,omitempty" xml:"country,omitempty"`
Region string `json:"region,omitempty" xml:"region,omitempty"`
Locality string `json:"locality,omitempty" xml:"locality,omitempty"`
PostOfficeBoxNumber string `json:"postOfficeBoxNumber,omitempty" xml:"postOfficeBoxNumber,omitempty"`
PostalCode string `json:"postalCode,omitempty" xml:"postalCode,omitempty"`
StreetAddress string `json:"streetAddress,omitempty" xml:"streetAddress,omitempty"`
}
type ProofOfConcept struct {
ReproductionSteps string `json:"reproductionSteps,omitempty" xml:"reproductionSteps,omitempty"`
Environment string `json:"environment,omitempty" xml:"environment,omitempty"`
SupportingMaterial *[]AttachedText `json:"supportingMaterial,omitempty" xml:"supportingMaterial>attachment,omitempty"`
}
type Property struct {
Name string `json:"name" xml:"name,attr"`
Value string `json:"value" xml:",chardata"`
}
type RelatedCryptoMaterialProperties struct {
Type RelatedCryptoMaterialType `json:"type,omitempty" xml:"type,omitempty"`
ID string `json:"id,omitempty" xml:"id,omitempty"`
State CryptoKeyState `json:"state,omitempty" xml:"state,omitempty"`
AlgorithmRef BOMReference `json:"algorithmRef,omitempty" xml:"algorithmRef,omitempty"`
CreationDate string `json:"creationDate,omitempty" xml:"creationDate,omitempty"`
ActivationDate string `json:"activationDate,omitempty" xml:"activationDate,omitempty"`
UpdateDate string `json:"updateDate,omitempty" xml:"updateDate,omitempty"`
ExpirationDate string `json:"expirationDate,omitempty" xml:"expirationDate,omitempty"`
Value string `json:"value,omitempty" xml:"value,omitempty"`
Size *int `json:"size,omitempty" xml:"size,omitempty"`
Format string `json:"format,omitempty" xml:"format,omitempty"`
SecuredBy *SecuredBy `json:"securedBy,omitempty" xml:"securedBy,omitempty"`
}
type RelatedCryptoMaterialType string
const (
RelatedCryptoMaterialTypePrivateKey RelatedCryptoMaterialType = "private-key"
RelatedCryptoMaterialTypePublicKey RelatedCryptoMaterialType = "public-key"
RelatedCryptoMaterialTypeSecretKey RelatedCryptoMaterialType = "secret-key"
RelatedCryptoMaterialTypeKey RelatedCryptoMaterialType = "key"
RelatedCryptoMaterialTypeCiphertext RelatedCryptoMaterialType = "ciphertext"
RelatedCryptoMaterialTypeSignature RelatedCryptoMaterialType = "signature"
RelatedCryptoMaterialTypeDigest RelatedCryptoMaterialType = "digest"
RelatedCryptoMaterialTypeInitializationVector RelatedCryptoMaterialType = "initialization-vector"
RelatedCryptoMaterialTypeNonce RelatedCryptoMaterialType = "nonce"
RelatedCryptoMaterialTypeSeed RelatedCryptoMaterialType = "seed"
RelatedCryptoMaterialTypeSalt RelatedCryptoMaterialType = "salt"
RelatedCryptoMaterialTypeSharedSecret RelatedCryptoMaterialType = "shared-secret"
RelatedCryptoMaterialTypeTag RelatedCryptoMaterialType = "tag"
RelatedCryptoMaterialTypeAdditionalData RelatedCryptoMaterialType = "additional-data"
RelatedCryptoMaterialTypePassword RelatedCryptoMaterialType = "password"
RelatedCryptoMaterialTypeCredential RelatedCryptoMaterialType = "credential"
RelatedCryptoMaterialTypeToken RelatedCryptoMaterialType = "token"
RelatedCryptoMaterialTypeOther RelatedCryptoMaterialType = "other"
RelatedCryptoMaterialTypeUnknown RelatedCryptoMaterialType = "unknown"
)
type ReleaseNotes struct {
Type string `json:"type" xml:"type"`
Title string `json:"title,omitempty" xml:"title,omitempty"`
FeaturedImage string `json:"featuredImage,omitempty" xml:"featuredImage,omitempty"`
SocialImage string `json:"socialImage,omitempty" xml:"socialImage,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Timestamp string `json:"timestamp,omitempty" xml:"timestamp,omitempty"`
Aliases *[]string `json:"aliases,omitempty" xml:"aliases>alias,omitempty"`
Tags *[]string `json:"tags,omitempty" xml:"tags>tag,omitempty"`
Resolves *[]Issue `json:"resolves,omitempty" xml:"resolves>issue,omitempty"`
Notes *[]Note `json:"notes,omitempty" xml:"notes>note,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type ResourceReferenceChoice struct {
Ref string `json:"ref,omitempty" xml:"ref,omitempty"`
ExternalReference *ExternalReference `json:"externalReference,omitempty" xml:"externalReference,omitempty"`
}
type Scope string
const (
ScopeExcluded Scope = "excluded"
ScopeOptional Scope = "optional"
ScopeRequired Scope = "required"
)
type ScoringMethod string
const (
ScoringMethodOther ScoringMethod = "other"
ScoringMethodCVSSv2 ScoringMethod = "CVSSv2"
ScoringMethodCVSSv3 ScoringMethod = "CVSSv3"
ScoringMethodCVSSv31 ScoringMethod = "CVSSv31"
ScoringMethodCVSSv4 ScoringMethod = "CVSSv4"
ScoringMethodOWASP ScoringMethod = "OWASP"
ScoringMethodSSVC ScoringMethod = "SSVC"
)
type Service struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Provider *OrganizationalEntity `json:"provider,omitempty" xml:"provider,omitempty"`
Group string `json:"group,omitempty" xml:"group,omitempty"`
Name string `json:"name" xml:"name"`
Version string `json:"version,omitempty" xml:"version,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Endpoints *[]string `json:"endpoints,omitempty" xml:"endpoints>endpoint,omitempty"`
Authenticated *bool `json:"authenticated,omitempty" xml:"authenticated,omitempty"`
CrossesTrustBoundary *bool `json:"x-trust-boundary,omitempty" xml:"x-trust-boundary,omitempty"`
Data *[]DataClassification `json:"data,omitempty" xml:"data>classification,omitempty"`
Licenses *Licenses `json:"licenses,omitempty" xml:"licenses,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
Services *[]Service `json:"services,omitempty" xml:"services>service,omitempty"`
ReleaseNotes *ReleaseNotes `json:"releaseNotes,omitempty" xml:"releaseNotes,omitempty"`
Tags *[]string `json:"tags,omitempty" xml:"tags>tag,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type Severity string
const (
SeverityUnknown Severity = "unknown"
SeverityNone Severity = "none"
SeverityInfo Severity = "info"
SeverityLow Severity = "low"
SeverityMedium Severity = "medium"
SeverityHigh Severity = "high"
SeverityCritical Severity = "critical"
)
var serialNumberRegex = regexp.MustCompile(`^urn:uuid:[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$`)
type Source struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
URL string `json:"url,omitempty" xml:"url,omitempty"`
}
type SpecVersion int
const (
SpecVersion1_0 SpecVersion = iota + 1 // 1.0
SpecVersion1_1 // 1.1
SpecVersion1_2 // 1.2
SpecVersion1_3 // 1.3
SpecVersion1_4 // 1.4
SpecVersion1_5 // 1.5
SpecVersion1_6 // 1.6
)
type StandardDefinition struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Version string `json:"version,omitempty" xml:"version,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Owner string `json:"owner,omitempty" xml:"owner,omitempty"`
Requirements *[]StandardRequirement `json:"requirements,omitempty" xml:"requirements>requirement,omitempty"`
Levels *[]StandardLevel `json:"levels,omitempty" xml:"levels>level,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
Signature *JSFSignature `json:"signature,omitempty" xml:"-"`
}
type StandardRequirement struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Identifier string `json:"identifier,omitempty" xml:"identifier,omitempty"`
Title string `json:"title,omitempty" xml:"title,omitempty"`
Text string `json:"text,omitempty" xml:"text,omitempty"`
Descriptions *[]string `json:"descriptions,omitempty" xml:"descriptions>description,omitempty"`
OpenCRE *[]string `json:"openCre,omitempty" xml:"openCre,omitempty"`
Parent string `json:"parent,omitempty" xml:"parent,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
}
type StandardLevel struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Identifier string `json:"identifier,omitempty" xml:"identifier,omitempty"`
Title string `json:"title,omitempty" xml:"title,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Requirements *[]string `json:"requirements,omitempty" xml:"requirements>requirement,omitempty"`
}
type SWID struct {
Text *AttachedText `json:"text,omitempty" xml:"text,omitempty"`
URL string `json:"url,omitempty" xml:"url,attr,omitempty"`
TagID string `json:"tagId" xml:"tagId,attr"`
Name string `json:"name" xml:"name,attr"`
Version string `json:"version,omitempty" xml:"version,attr,omitempty"`
TagVersion *int `json:"tagVersion,omitempty" xml:"tagVersion,attr,omitempty"`
Patch *bool `json:"patch,omitempty" xml:"patch,attr,omitempty"`
}
type Task struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
UID string `json:"uid,omitempty" xml:"uid,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
ResourceReferences *[]ResourceReferenceChoice `json:"resourceReferences,omitempty" xml:"resourceReferences>resourceReference,omitempty"`
TaskTypes *[]TaskType `json:"taskTypes,omitempty" xml:"taskTypes>taskType,omitempty"`
Trigger *TaskTrigger `json:"trigger,omitempty" xml:"trigger,omitempty"`
Steps *[]TaskStep `json:"steps,omitempty" xml:"steps>step,omitempty"`
Inputs *[]TaskInput `json:"inputs,omitempty" xml:"inputs>input,omitempty"`
Outputs *[]TaskOutput `json:"outputs,omitempty" xml:"outputs>output,omitempty"`
TimeStart string `json:"timeStart,omitempty" xml:"timeStart,omitempty"`
TimeEnd string `json:"timeEnd,omitempty" xml:"timeEnd,omitempty"`
Workspaces *[]TaskWorkspace `json:"workspaces,omitempty" xml:"workspaces>workspace,omitempty"`
RuntimeTopology *[]Dependency `json:"runtimeTopology,omitempty" xml:"runtimeTopology>dependency,omitempty"`
}
type TaskCommand struct {
Executed string `json:"executed,omitempty" xml:"executed,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type TaskInput struct {
Resource *ResourceReferenceChoice `json:"resource,omitempty" xml:"resource,omitempty"`
Parameters *[]Parameter `json:"parameters,omitempty" xml:"parameters>parameter,omitempty"`
EnvironmentVars *EnvironmentVariables `json:"environmentVars,omitempty" xml:"environmentVars,omitempty"`
Data *AttachedText `json:"data,omitempty" xml:"data,omitempty"`
Source *ResourceReferenceChoice `json:"source,omitempty" xml:"source,omitempty"`
Target *ResourceReferenceChoice `json:"target,omitempty" xml:"target,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type TaskOutput struct {
Resource *ResourceReferenceChoice `json:"resource,omitempty" xml:"resource,omitempty"`
Parameters *[]Parameter `json:"parameters,omitempty" xml:"parameters>parameter,omitempty"`
EnvironmentVars *EnvironmentVariables `json:"environmentVars,omitempty" xml:"environmentVars,omitempty"`
Data *AttachedText `json:"data,omitempty" xml:"data,omitempty"`
Type TaskOutputType `json:"type,omitempty" xml:"type,omitempty"`
Source *ResourceReferenceChoice `json:"source,omitempty" xml:"source,omitempty"`
Target *ResourceReferenceChoice `json:"target,omitempty" xml:"target,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type TaskOutputType string
const (
TaskOutputTypeArtifact TaskOutputType = "artifact"
TaskOutputTypeAttestation TaskOutputType = "attestation"
TaskOutputTypeEvidence TaskOutputType = "evidence"
TaskOutputTypeLog TaskOutputType = "log"
TaskOutputTypeMetrics TaskOutputType = "metrics"
TaskOutputTypeOther TaskOutputType = "other"
)
type TaskStep struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Commands *[]TaskCommand `json:"commands,omitempty" xml:"commands>command,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type TaskTrigger struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
UID string `json:"uid,omitempty" xml:"uid,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
ResourceReferences *[]ResourceReferenceChoice `json:"resourceReferences,omitempty" xml:"resourceReferences>resourceReference,omitempty"`
Type TaskTriggerType `json:"type,omitempty" xml:"type,omitempty"`
Event *Event `json:"event,omitempty" xml:"event,omitempty"`
Conditions *[]TaskTriggerCondition `json:"conditions,omitempty" xml:"conditions>condition,omitempty"`
TimeActivated string `json:"timeActivated,omitempty" xml:"timeActivated,omitempty"`
Inputs *[]TaskInput `json:"inputs,omitempty" xml:"inputs>input,omitempty"`
Outputs *[]TaskOutput `json:"outputs,omitempty" xml:"outputs>output,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type TaskTriggerCondition struct {
Description string `json:"description,omitempty" xml:"description,omitempty"`
Expression string `json:"expression,omitempty" xml:"expression,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type TaskTriggerType string
const (
TaskTriggerTypeAPI TaskTriggerType = "api"
TaskTriggerTypeManual TaskTriggerType = "manual"
TaskTriggerTypeScheduled TaskTriggerType = "scheduled"
TaskTriggerTypeWebhook TaskTriggerType = "webhook"
)
type TaskType string
const (
TaskTypeBuild TaskType = "build"
TaskTypeClean TaskType = "clean"
TaskTypeClone TaskType = "clone"
TaskTypeCopy TaskType = "copy"
TaskTypeDeliver TaskType = "deliver"
TaskTypeDeploy TaskType = "deploy"
TaskTypeLint TaskType = "lint"
TaskTypeMerge TaskType = "merge"
TaskTypeOther TaskType = "other"
TaskTypeRelease TaskType = "release"
TaskTypeScan TaskType = "scan"
TaskTypeTest TaskType = "test"
)
type TaskWorkspace struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
UID string `json:"uid,omitempty" xml:"uid,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Aliases *[]string `json:"aliases,omitempty" xml:"aliases>alias,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
ResourceReferences *[]ResourceReferenceChoice `json:"resourceReferences,omitempty" xml:"resourceReferences>resourceReference,omitempty"`
AccessMode TaskWorkspaceAccessMode `json:"accessMode,omitempty" xml:"accessMode,omitempty"`
MountPath string `json:"mountPath,omitempty" xml:"mountPath,omitempty"`
ManagedDataType string `json:"managedDataType,omitempty" xml:"managedDataType,omitempty"`
VolumeRequest string `json:"volumeRequest,omitempty" xml:"volumeRequest,omitempty"`
Volume *Volume `json:"volume,omitempty" xml:"volume,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type TaskWorkspaceAccessMode string
const (
TaskWorkspaceAccessModeReadOnly TaskWorkspaceAccessMode = "read-only"
TaskWorkspaceAccessModeReadWrite TaskWorkspaceAccessMode = "read-write"
TaskWorkspaceAccessModeReadWriteOnce TaskWorkspaceAccessMode = "read-write-once"
TaskWorkspaceAccessModeWriteOnce TaskWorkspaceAccessMode = "write-once"
TaskWorkspaceAccessModeWriteOnly TaskWorkspaceAccessMode = "write-only"
)
// Deprecated: Use Component or Service instead.
type Tool struct {
Vendor string `json:"vendor,omitempty" xml:"vendor,omitempty"`
Name string `json:"name" xml:"name"`
Version string `json:"version,omitempty" xml:"version,omitempty"`
Hashes *[]Hash `json:"hashes,omitempty" xml:"hashes>hash,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
}
// ToolsChoice represents a union of either Tools (deprecated as of CycloneDX v1.5), and Components or Services.
//
// Encoding or decoding a ToolsChoice with both options present will raise an error.
// When encoding to a SpecVersion lower than SpecVersion1_5, and Components or Services are set,
// they will be automatically converted to legacy Tools.
//
// It is strongly recommended to use Components and Services. However, when consuming BOMs,
// applications should still expect legacy Tools to be present, and handle them accordingly.
type ToolsChoice struct {
Tools *[]Tool `json:"-" xml:"-"` // Deprecated: Use Components and Services instead.
Components *[]Component `json:"-" xml:"-"`
Services *[]Service `json:"-" xml:"-"`
}
type Volume struct {
UID string `json:"uid,omitempty" xml:"uid,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Mode VolumeMode `json:"mode,omitempty" xml:"mode,omitempty"`
Path string `json:"path,omitempty" xml:"path,omitempty"`
SizeAllocated string `json:"sizeAllocated,omitempty" xml:"sizeAllocated,omitempty"`
Persistent *bool `json:"persistent,omitempty" xml:"persistent,omitempty"`
Remote *bool `json:"remote,omitempty" xml:"remote,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type VolumeMode string
const (
VolumeModeBlock VolumeMode = "block"
VolumeModeFilesystem VolumeMode = "file-system"
)
type Vulnerability struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
ID string `json:"id" xml:"id"`
Source *Source `json:"source,omitempty" xml:"source,omitempty"`
References *[]VulnerabilityReference `json:"references,omitempty" xml:"references>reference,omitempty"`
Ratings *[]VulnerabilityRating `json:"ratings,omitempty" xml:"ratings>rating,omitempty"`
CWEs *[]int `json:"cwes,omitempty" xml:"cwes>cwe,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
Detail string `json:"detail,omitempty" xml:"detail,omitempty"`
Recommendation string `json:"recommendation,omitempty" xml:"recommendation,omitempty"`
Workaround string `json:"workaround,omitempty" xml:"workaround,omitempty"`
ProofOfConcept *ProofOfConcept `json:"proofOfConcept,omitempty" xml:"proofOfConcept,omitempty"`
Advisories *[]Advisory `json:"advisories,omitempty" xml:"advisories>advisory,omitempty"`
Created string `json:"created,omitempty" xml:"created,omitempty"`
Published string `json:"published,omitempty" xml:"published,omitempty"`
Updated string `json:"updated,omitempty" xml:"updated,omitempty"`
Rejected string `json:"rejected,omitempty" xml:"rejected,omitempty"`
Credits *Credits `json:"credits,omitempty" xml:"credits,omitempty"`
Tools *ToolsChoice `json:"tools,omitempty" xml:"tools,omitempty"`
Analysis *VulnerabilityAnalysis `json:"analysis,omitempty" xml:"analysis,omitempty"`
Affects *[]Affects `json:"affects,omitempty" xml:"affects>target,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
type VulnerabilityAnalysis struct {
State ImpactAnalysisState `json:"state,omitempty" xml:"state,omitempty"`
Justification ImpactAnalysisJustification `json:"justification,omitempty" xml:"justification,omitempty"`
Response *[]ImpactAnalysisResponse `json:"response,omitempty" xml:"responses>response,omitempty"`
Detail string `json:"detail,omitempty" xml:"detail,omitempty"`
FirstIssued string `json:"firstIssued,omitempty" xml:"firstIssued,omitempty"`
LastUpdated string `json:"lastUpdated,omitempty" xml:"lastUpdated,omitempty"`
}
type VulnerabilityRating struct {
Source *Source `json:"source,omitempty" xml:"source,omitempty"`
Score *float64 `json:"score,omitempty" xml:"score,omitempty"`
Severity Severity `json:"severity,omitempty" xml:"severity,omitempty"`
Method ScoringMethod `json:"method,omitempty" xml:"method,omitempty"`
Vector string `json:"vector,omitempty" xml:"vector,omitempty"`
Justification string `json:"justification,omitempty" xml:"justification,omitempty"`
}
type VulnerabilityReference struct {
ID string `json:"id,omitempty" xml:"id,omitempty"`
Source *Source `json:"source,omitempty" xml:"source,omitempty"`
}
type VulnerabilityStatus string
const (
VulnerabilityStatusUnknown VulnerabilityStatus = "unknown"
VulnerabilityStatusAffected VulnerabilityStatus = "affected"
VulnerabilityStatusNotAffected VulnerabilityStatus = "unaffected"
)
type Workflow struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
UID string `json:"uid,omitempty" xml:"uid,omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
Description string `json:"description,omitempty" xml:"description,omitempty"`
ResourceReferences *[]ResourceReferenceChoice `json:"resourceReferences,omitempty" xml:"resourceReferences>resourceReference,omitempty"`
Tasks *[]Task `json:"tasks,omitempty" xml:"tasks>task,omitempty"`
TaskDependencies *[]Dependency `json:"taskDependencies,omitempty" xml:"taskDependencies>dependency"`
TaskTypes *[]TaskType `json:"taskTypes,omitempty" xml:"taskTypes>taskType,omitempty"`
Trigger *TaskTrigger `json:"trigger,omitempty" xml:"trigger,omitempty"`
Steps *[]TaskStep `json:"steps,omitempty" xml:"steps>step,omitempty"`
Inputs *[]TaskInput `json:"inputs,omitempty" xml:"inputs>input,omitempty"`
Outputs *[]TaskOutput `json:"outputs,omitempty" xml:"outputs>output,omitempty"`
TimeStart string `json:"timeStart,omitempty" xml:"timeStart,omitempty"`
TimeEnd string `json:"timeEnd,omitempty" xml:"timeEnd,omitempty"`
Workspaces *[]TaskWorkspace `json:"workspaces,omitempty" xml:"workspaces>workspace,omitempty"`
RuntimeTopology *[]Dependency `json:"runtimeTopology,omitempty" xml:"runtimeTopology>dependency,omitempty"`
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}
cyclonedx-go-0.9.3/cyclonedx_json.go 0000664 0000000 0000000 00000013317 15067437333 0017506 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"encoding/json"
"errors"
"fmt"
)
func (ev EnvironmentVariableChoice) MarshalJSON() ([]byte, error) {
if ev.Property != nil && *ev.Property != (Property{}) {
return json.Marshal(ev.Property)
} else if ev.Value != "" {
return json.Marshal(ev.Value)
}
return []byte("{}"), nil
}
func (ev *EnvironmentVariableChoice) UnmarshalJSON(bytes []byte) error {
var property Property
err := json.Unmarshal(bytes, &property)
if err != nil {
var ute *json.UnmarshalTypeError
if !errors.As(err, &ute) || ute.Value != "string" {
return err
}
}
if property != (Property{}) {
ev.Property = &property
return nil
}
var value string
err = json.Unmarshal(bytes, &value)
if err != nil {
var ute *json.UnmarshalTypeError
if !errors.As(err, &ute) || ute.Value != "object" {
return err
}
}
ev.Value = value
return nil
}
type mlDatasetChoiceRefJSON struct {
Ref string `json:"ref" xml:"-"`
}
func (dc MLDatasetChoice) MarshalJSON() ([]byte, error) {
if dc.Ref != "" {
return json.Marshal(mlDatasetChoiceRefJSON{Ref: dc.Ref})
} else if dc.ComponentData != nil {
return json.Marshal(dc.ComponentData)
}
return []byte("{}"), nil
}
func (dc *MLDatasetChoice) UnmarshalJSON(bytes []byte) error {
var refObj mlDatasetChoiceRefJSON
err := json.Unmarshal(bytes, &refObj)
if err != nil {
return err
}
if refObj.Ref != "" {
dc.Ref = refObj.Ref
return nil
}
var componentData ComponentData
err = json.Unmarshal(bytes, &componentData)
if err != nil {
return err
}
if componentData != (ComponentData{}) {
dc.ComponentData = &componentData
}
return nil
}
func (sv SpecVersion) MarshalJSON() ([]byte, error) {
return json.Marshal(sv.String())
}
func (sv *SpecVersion) UnmarshalJSON(bytes []byte) error {
var v string
err := json.Unmarshal(bytes, &v)
if err != nil {
return err
}
switch v {
case SpecVersion1_0.String():
*sv = SpecVersion1_0
case SpecVersion1_1.String():
*sv = SpecVersion1_1
case SpecVersion1_2.String():
*sv = SpecVersion1_2
case SpecVersion1_3.String():
*sv = SpecVersion1_3
case SpecVersion1_4.String():
*sv = SpecVersion1_4
case SpecVersion1_5.String():
*sv = SpecVersion1_5
case SpecVersion1_6.String():
*sv = SpecVersion1_6
default:
return ErrInvalidSpecVersion
}
return nil
}
type toolsChoiceJSON struct {
Components *[]Component `json:"components,omitempty" xml:"-"`
Services *[]Service `json:"services,omitempty" xml:"-"`
}
func (tc ToolsChoice) MarshalJSON() ([]byte, error) {
if tc.Tools != nil && (tc.Components != nil || tc.Services != nil) {
return nil, fmt.Errorf("either a list of tools, or an object holding components and services can be used, but not both")
}
if tc.Tools != nil {
return json.Marshal(tc.Tools)
}
choiceJSON := toolsChoiceJSON{
Components: tc.Components,
Services: tc.Services,
}
if choiceJSON.Components != nil || choiceJSON.Services != nil {
return json.Marshal(choiceJSON)
}
return []byte(nil), nil
}
func (tc *ToolsChoice) UnmarshalJSON(bytes []byte) error {
var choiceJSON toolsChoiceJSON
err := json.Unmarshal(bytes, &choiceJSON)
if err != nil {
var typeErr *json.UnmarshalTypeError
if !errors.As(err, &typeErr) || typeErr.Value != "array" {
return err
}
var legacyTools []Tool
err = json.Unmarshal(bytes, &legacyTools)
if err != nil {
return err
}
*tc = ToolsChoice{Tools: &legacyTools}
return nil
}
if choiceJSON.Components != nil || choiceJSON.Services != nil {
*tc = ToolsChoice{
Components: choiceJSON.Components,
Services: choiceJSON.Services,
}
}
return nil
}
func (ev *Evidence) UnmarshalJSON(bytes []byte) error {
type Alias Evidence
var aux = &struct {
Identity json.RawMessage `json:"identity"`
*Alias
}{
Alias: (*Alias)(ev),
}
if err := json.Unmarshal(bytes, &aux); err != nil {
return err
}
// Try to unmarshal Identity field as struct
if aux.Identity != nil {
var identity EvidenceIdentity
err := json.Unmarshal(aux.Identity, &identity)
if err != nil {
var typeErr *json.UnmarshalTypeError
if !errors.As(err, &typeErr) || typeErr.Value != "array" {
return err
}
// Try to unmarshal Identity field as array
var identities []EvidenceIdentity
err = json.Unmarshal(aux.Identity, &identities)
if err != nil {
return err
}
if len(identities) > 0 {
ev.Identity = &identities
}
return nil
}
// The field is required, so we can check for emptiness using this field.
// cf. https://cyclonedx.org/docs/1.6/json/#metadata_component_evidence_identity_oneOf_i0_items_field
if identity.Field != "" {
ev.Identity = &[]EvidenceIdentity{
identity,
}
}
}
return nil
}
var jsonSchemas = map[SpecVersion]string{
SpecVersion1_0: "",
SpecVersion1_1: "",
SpecVersion1_2: "http://cyclonedx.org/schema/bom-1.2.schema.json",
SpecVersion1_3: "http://cyclonedx.org/schema/bom-1.3.schema.json",
SpecVersion1_4: "http://cyclonedx.org/schema/bom-1.4.schema.json",
SpecVersion1_5: "http://cyclonedx.org/schema/bom-1.5.schema.json",
SpecVersion1_6: "http://cyclonedx.org/schema/bom-1.6.schema.json",
}
cyclonedx-go-0.9.3/cyclonedx_json_test.go 0000664 0000000 0000000 00000022375 15067437333 0020551 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func TestEnvironmentVariableChoice_MarshalJSON(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
choice := EnvironmentVariableChoice{}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, "{}", string(jsonBytes))
})
t.Run("WithProperty", func(t *testing.T) {
choice := EnvironmentVariableChoice{
Property: &Property{
Name: "foo",
Value: "bar",
},
}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `{"name":"foo","value":"bar"}`, string(jsonBytes))
})
t.Run("WithValue", func(t *testing.T) {
choice := EnvironmentVariableChoice{Value: "foo"}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `"foo"`, string(jsonBytes))
})
t.Run("WithPropertyAndValue", func(t *testing.T) {
choice := EnvironmentVariableChoice{
Property: &Property{
Name: "foo",
Value: "bar",
},
Value: "baz",
}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `{"name":"foo","value":"bar"}`, string(jsonBytes))
})
}
func TestEnvironmentVariableChoice_UnmarshalJSON(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
var choice EnvironmentVariableChoice
err := json.Unmarshal([]byte(`{}`), &choice)
require.NoError(t, err)
require.Equal(t, EnvironmentVariableChoice{}, choice)
})
t.Run("WithProperty", func(t *testing.T) {
var choice EnvironmentVariableChoice
err := json.Unmarshal([]byte(`{"name":"foo","value":"bar"}`), &choice)
require.NoError(t, err)
require.NotNil(t, choice.Property)
require.Equal(t, "foo", choice.Property.Name)
require.Equal(t, "bar", choice.Property.Value)
require.Empty(t, choice.Value)
})
t.Run("WithValue", func(t *testing.T) {
var choice EnvironmentVariableChoice
err := json.Unmarshal([]byte(`"foo"`), &choice)
require.NoError(t, err)
require.Nil(t, choice.Property)
require.Equal(t, "foo", choice.Value)
})
}
func TestMLDatasetChoice_MarshalJSON(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
choice := MLDatasetChoice{}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, "{}", string(jsonBytes))
})
t.Run("WithRef", func(t *testing.T) {
choice := MLDatasetChoice{Ref: "foo"}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `{"ref":"foo"}`, string(jsonBytes))
})
t.Run("WithComponentData", func(t *testing.T) {
choice := MLDatasetChoice{
ComponentData: &ComponentData{
BOMRef: "foo",
Name: "bar",
},
}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `{"bom-ref":"foo","name":"bar"}`, string(jsonBytes))
})
t.Run("WithRefAndComponentData", func(t *testing.T) {
choice := MLDatasetChoice{
Ref: "foo",
ComponentData: &ComponentData{
BOMRef: "bar",
Name: "baz",
},
}
jsonBytes, err := json.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `{"ref":"foo"}`, string(jsonBytes))
})
}
func TestMLDatasetChoice_UnmarshalJSON(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
var choice MLDatasetChoice
err := json.Unmarshal([]byte(`{}`), &choice)
require.NoError(t, err)
require.Equal(t, MLDatasetChoice{}, choice)
})
t.Run("WithRef", func(t *testing.T) {
var choice MLDatasetChoice
err := json.Unmarshal([]byte(`{"ref":"foo"}`), &choice)
require.NoError(t, err)
require.Equal(t, "foo", choice.Ref)
require.Nil(t, choice.ComponentData)
})
t.Run("WithComponentData", func(t *testing.T) {
var choice MLDatasetChoice
err := json.Unmarshal([]byte(`{"bom-ref":"foo","name":"bar"}`), &choice)
require.NoError(t, err)
require.Empty(t, choice.Ref)
require.NotNil(t, choice.ComponentData)
require.Equal(t, "foo", choice.ComponentData.BOMRef)
require.Equal(t, "bar", choice.ComponentData.Name)
})
t.Run("WithRefAndComponentData", func(t *testing.T) {
var choice MLDatasetChoice
err := json.Unmarshal([]byte(`{"ref":"foo","bom-ref":"bar","name":"baz"}`), &choice)
require.NoError(t, err)
require.Equal(t, "foo", choice.Ref)
require.Nil(t, choice.ComponentData)
})
}
func TestEvidence_MarshalJSON(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
evidence := Evidence{}
jsonBytes, err := json.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, "{}", string(jsonBytes))
})
t.Run("WithOccurrences", func(t *testing.T) {
evidence := Evidence{
Occurrences: &[]EvidenceOccurrence{
{
BOMRef: "d6bf237e-4e11-4713-9f62-56d18d5e2079",
Location: "/path/to/component",
},
{
BOMRef: "b574d5d1-e3cf-4dcd-9ba5-f3507eb1b175",
Location: "/another/path/to/component",
},
},
}
jsonBytes, err := json.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, `{"occurrences":[{"bom-ref":"d6bf237e-4e11-4713-9f62-56d18d5e2079","location":"/path/to/component"},{"bom-ref":"b574d5d1-e3cf-4dcd-9ba5-f3507eb1b175","location":"/another/path/to/component"}]}`, string(jsonBytes))
})
t.Run("WithIdentify", func(t *testing.T) {
evidence := Evidence{
Identity: &[]EvidenceIdentity{
{
Field: EvidenceIdentityFieldTypePURL,
Confidence: toPointer(t, float32(1)),
Methods: &[]EvidenceIdentityMethod{
{
Technique: "filename",
Confidence: toPointer(t, float32(0.1)),
Value: "findbugs-project-3.0.0.jar",
},
{
Technique: "ast-fingerprint",
Confidence: toPointer(t, float32(0.9)),
Value: "61e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab",
},
},
Tools: &[]BOMReference{
"bom-ref-of-tool-that-performed-analysis",
},
},
},
}
jsonBytes, err := json.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, `{"identity":[{"field":"purl","confidence":1,"methods":[{"technique":"filename","confidence":0.1,"value":"findbugs-project-3.0.0.jar"},{"technique":"ast-fingerprint","confidence":0.9,"value":"61e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab"}],"tools":["bom-ref-of-tool-that-performed-analysis"]}]}`, string(jsonBytes))
})
}
func TestEvidence_UnmarshalJSON(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
var evidence Evidence
err := json.Unmarshal([]byte(`{}`), &evidence)
require.NoError(t, err)
require.Equal(t, Evidence{}, evidence)
})
t.Run("WithOccurrences", func(t *testing.T) {
var evidence Evidence
err := json.Unmarshal([]byte(`{
"occurrences": [
{
"bom-ref": "d6bf237e-4e11-4713-9f62-56d18d5e2079",
"location": "/path/to/component"
},
{
"bom-ref": "b574d5d1-e3cf-4dcd-9ba5-f3507eb1b175",
"location": "/another/path/to/component"
}
]}`), &evidence)
require.NoError(t, err)
require.Equal(t, &[]EvidenceOccurrence{
{
BOMRef: "d6bf237e-4e11-4713-9f62-56d18d5e2079",
Location: "/path/to/component",
},
{
BOMRef: "b574d5d1-e3cf-4dcd-9ba5-f3507eb1b175",
Location: "/another/path/to/component",
},
}, evidence.Occurrences)
})
t.Run("WithIdentityAsStruct", func(t *testing.T) {
var evidence Evidence
err := json.Unmarshal([]byte(`{
"identity": {
"field": "purl",
"confidence": 1,
"methods": [
{
"technique": "filename",
"confidence": 0.1,
"value": "findbugs-project-3.0.0.jar"
},
{
"technique": "ast-fingerprint",
"confidence": 0.9,
"value": "61e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab"
}
],
"tools": [
"bom-ref-of-tool-that-performed-analysis"
]
}}`), &evidence)
require.NoError(t, err)
require.Equal(t, &[]EvidenceIdentity{
{
Field: EvidenceIdentityFieldTypePURL,
Confidence: toPointer(t, float32(1)),
Methods: &[]EvidenceIdentityMethod{
{
Technique: "filename",
Confidence: toPointer(t, float32(0.1)),
Value: "findbugs-project-3.0.0.jar",
},
{
Technique: "ast-fingerprint",
Confidence: toPointer(t, float32(0.9)),
Value: "61e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab",
},
},
Tools: &[]BOMReference{
"bom-ref-of-tool-that-performed-analysis",
},
},
}, evidence.Identity)
})
t.Run("WithIdentityAsArray", func(t *testing.T) {
var evidence Evidence
err := json.Unmarshal([]byte(`{
"identity": [
{
"field": "purl",
"confidence": 1
},
{
"field": "name",
"confidence": 0.1
}
]}`), &evidence)
require.NoError(t, err)
require.Equal(t, &[]EvidenceIdentity{
{
Field: EvidenceIdentityFieldTypePURL,
Confidence: toPointer(t, float32(1)),
},
{
Field: EvidenceIdentityFieldTypeName,
Confidence: toPointer(t, float32(0.1)),
},
}, evidence.Identity)
})
}
cyclonedx-go-0.9.3/cyclonedx_string.go 0000664 0000000 0000000 00000003006 15067437333 0020035 0 ustar 00root root 0000000 0000000 // Code generated by "stringer -linecomment -output cyclonedx_string.go -type MediaType,SpecVersion"; DO NOT EDIT.
package cyclonedx
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[MediaTypeJSON-1]
_ = x[MediaTypeXML-2]
_ = x[MediaTypeProtobuf-3]
}
const _MediaType_name = "application/vnd.cyclonedx+jsonapplication/vnd.cyclonedx+xmlapplication/x.vnd.cyclonedx+protobuf"
var _MediaType_index = [...]uint8{0, 30, 59, 95}
func (i MediaType) String() string {
i -= 1
if i < 0 || i >= MediaType(len(_MediaType_index)-1) {
return "MediaType(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _MediaType_name[_MediaType_index[i]:_MediaType_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[SpecVersion1_0-1]
_ = x[SpecVersion1_1-2]
_ = x[SpecVersion1_2-3]
_ = x[SpecVersion1_3-4]
_ = x[SpecVersion1_4-5]
_ = x[SpecVersion1_5-6]
_ = x[SpecVersion1_6-7]
}
const _SpecVersion_name = "1.01.11.21.31.41.51.6"
var _SpecVersion_index = [...]uint8{0, 3, 6, 9, 12, 15, 18, 21}
func (i SpecVersion) String() string {
i -= 1
if i < 0 || i >= SpecVersion(len(_SpecVersion_index)-1) {
return "SpecVersion(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _SpecVersion_name[_SpecVersion_index[i]:_SpecVersion_index[i+1]]
}
cyclonedx-go-0.9.3/cyclonedx_test.go 0000664 0000000 0000000 00000003676 15067437333 0017523 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"testing"
"github.com/bradleyjkemp/cupaloy/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var snapShooter = cupaloy.NewDefaultConfig().
WithOptions(cupaloy.SnapshotSubdirectory("./testdata/snapshots"))
func TestBool(t *testing.T) {
assert.Equal(t, true, *Bool(true))
assert.Equal(t, false, *Bool(false))
}
func TestMediaType_WithVersion(t *testing.T) {
t.Run("ShouldReturnVersionedMediaType", func(t *testing.T) {
res, err := MediaTypeJSON.WithVersion(SpecVersion1_2)
require.NoError(t, err)
require.Equal(t, "application/vnd.cyclonedx+json; version=1.2", res)
})
t.Run("ShouldReturnErrorForSpecLowerThan1.2AndJSON", func(t *testing.T) {
_, err := MediaTypeJSON.WithVersion(SpecVersion1_1)
require.Error(t, err)
})
}
func TestVulnerability_Properties(t *testing.T) {
// GIVEN
properties := []Property{}
vuln := Vulnerability{
Properties: &properties,
}
// EXPECT
assert.Equal(t, 0, len(*vuln.Properties))
}
func assertValidBOM(t *testing.T, bomBytes []byte, format BOMFileFormat, version SpecVersion) {
var v validator
if format == BOMFileFormatJSON {
v = newJSONValidator()
} else {
v = newXMLValidator()
}
err := v.Validate(bomBytes, version)
require.NoError(t, err)
}
cyclonedx-go-0.9.3/cyclonedx_xml.go 0000664 0000000 0000000 00000033452 15067437333 0017337 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"encoding/xml"
"errors"
"fmt"
"io"
)
// bomReferenceXML is temporarily used for marshalling and unmarshalling
// BOMReference instances to and from XML.
type bomReferenceXML struct {
Ref string `json:"-" xml:"ref,attr"`
}
func (b BOMReference) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(bomReferenceXML{Ref: string(b)}, start)
}
func (b *BOMReference) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
bXML := bomReferenceXML{}
if err := d.DecodeElement(&bXML, &start); err != nil {
return err
}
*b = BOMReference(bXML.Ref)
return nil
}
func (c Copyright) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(c.Text, start)
}
func (c *Copyright) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var text string
if err := d.DecodeElement(&text, &start); err != nil {
return err
}
c.Text = text
return nil
}
// dependencyXML is temporarily used for marshalling and unmarshalling
// Dependency instances to and from XML.
type dependencyXML struct {
Ref string `xml:"ref,attr"`
Dependencies *[]dependencyXML `xml:"dependency,omitempty"`
}
func (d Dependency) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
xmlDep := dependencyXML{Ref: d.Ref}
if d.Dependencies != nil && len(*d.Dependencies) > 0 {
xmlDeps := make([]dependencyXML, len(*d.Dependencies))
for i := range *d.Dependencies {
xmlDeps[i] = dependencyXML{Ref: (*d.Dependencies)[i]}
}
xmlDep.Dependencies = &xmlDeps
}
return e.EncodeElement(xmlDep, start)
}
func (d *Dependency) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error {
xmlDep := dependencyXML{}
err := dec.DecodeElement(&xmlDep, &start)
if err != nil {
return err
}
dep := Dependency{Ref: xmlDep.Ref}
if xmlDep.Dependencies != nil && len(*xmlDep.Dependencies) > 0 {
deps := make([]string, len(*xmlDep.Dependencies))
for i := range *xmlDep.Dependencies {
deps[i] = (*xmlDep.Dependencies)[i].Ref
}
dep.Dependencies = &deps
}
*d = dep
return nil
}
func (ev EnvironmentVariables) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if len(ev) == 0 {
return nil
}
err := e.EncodeToken(start)
if err != nil {
return err
}
for _, choice := range ev {
if choice.Property != nil && choice.Value != "" {
return fmt.Errorf("either property or value must be set, but not both")
}
if choice.Property != nil {
err = e.EncodeElement(choice.Property, xml.StartElement{Name: xml.Name{Local: "environmentVar"}})
if err != nil {
return err
}
} else if choice.Value != "" {
err = e.EncodeElement(choice.Value, xml.StartElement{Name: xml.Name{Local: "value"}})
if err != nil {
return err
}
}
}
return e.EncodeToken(start.End())
}
func (ev *EnvironmentVariables) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error {
envVars := make([]EnvironmentVariableChoice, 0)
for {
token, err := d.Token()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
switch tokenType := token.(type) {
case xml.StartElement:
if tokenType.Name.Local == "value" {
var value string
err = d.DecodeElement(&value, &tokenType)
if err != nil {
return err
}
envVars = append(envVars, EnvironmentVariableChoice{Value: value})
} else if tokenType.Name.Local == "environmentVar" {
var property Property
err = d.DecodeElement(&property, &tokenType)
if err != nil {
return err
}
envVars = append(envVars, EnvironmentVariableChoice{Property: &property})
} else {
return fmt.Errorf("unknown element: %s", tokenType.Name.Local)
}
}
}
*ev = envVars
return nil
}
func (l Licenses) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if len(l) == 0 {
return nil
}
if err := e.EncodeToken(start); err != nil {
return err
}
for _, choice := range l {
if choice.License != nil && choice.Expression != "" {
return fmt.Errorf("either license or expression must be set, but not both")
}
if choice.License != nil {
if err := e.EncodeElement(choice.License, xml.StartElement{Name: xml.Name{Local: "license"}}); err != nil {
return err
}
} else if choice.Expression != "" {
if err := e.EncodeElement(choice.Expression, xml.StartElement{Name: xml.Name{Local: "expression"}}); err != nil {
return err
}
}
}
return e.EncodeToken(start.End())
}
func (l *Licenses) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error {
licenses := make([]LicenseChoice, 0)
for {
token, err := d.Token()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
switch tokenType := token.(type) {
case xml.StartElement:
if tokenType.Name.Local == "expression" {
var expression string
if err = d.DecodeElement(&expression, &tokenType); err != nil {
return err
}
licenses = append(licenses, LicenseChoice{Expression: expression})
} else if tokenType.Name.Local == "license" {
var license License
if err = d.DecodeElement(&license, &tokenType); err != nil {
return err
}
licenses = append(licenses, LicenseChoice{License: &license})
} else {
return fmt.Errorf("unknown element: %s", tokenType.Name.Local)
}
}
}
*l = licenses
return nil
}
type mlDatasetChoiceRefXML struct {
Ref string `json:"-" xml:"ref"`
}
type mlDatasetChoiceXML struct {
Ref string `json:"-" xml:"ref"`
ComponentData
}
func (dc MLDatasetChoice) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if dc.Ref != "" {
return e.EncodeElement(mlDatasetChoiceRefXML{Ref: dc.Ref}, start)
} else if dc.ComponentData != nil {
return e.EncodeElement(dc.ComponentData, start)
}
return nil
}
func (dc *MLDatasetChoice) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var choice mlDatasetChoiceXML
err := d.DecodeElement(&choice, &start)
if err != nil {
return err
}
if choice.Ref != "" {
dc.Ref = choice.Ref
return nil
}
if choice.ComponentData != (ComponentData{}) {
dc.ComponentData = &choice.ComponentData
}
return nil
}
func (sv SpecVersion) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(sv.String(), start)
}
func (sv *SpecVersion) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var v string
err := d.DecodeElement(&v, &start)
if err != nil {
return err
}
switch v {
case SpecVersion1_0.String():
*sv = SpecVersion1_0
case SpecVersion1_1.String():
*sv = SpecVersion1_1
case SpecVersion1_2.String():
*sv = SpecVersion1_2
case SpecVersion1_3.String():
*sv = SpecVersion1_3
case SpecVersion1_4.String():
*sv = SpecVersion1_4
case SpecVersion1_5.String():
*sv = SpecVersion1_5
case SpecVersion1_6.String():
*sv = SpecVersion1_6
default:
return ErrInvalidSpecVersion
}
return nil
}
// toolsChoiceMarshalXML is a helper struct for marshalling ToolsChoice.
type toolsChoiceMarshalXML struct {
LegacyTools *[]Tool `json:"-" xml:"tool,omitempty"`
Components *[]Component `json:"-" xml:"components>component,omitempty"`
Services *[]Service `json:"-" xml:"services>service,omitempty"`
}
// toolsChoiceUnmarshalXML is a helper struct for unmarshalling tools represented
// as components and / or services. It is intended to be used with the streaming XML API.
//
// <-- cursor should be here when unmarshalling this!
//
// foo
//
//
type toolsChoiceUnmarshalXML struct {
Components *[]Component `json:"-" xml:"component,omitempty"`
Services *[]Service `json:"-" xml:"service,omitempty"`
}
func (tc ToolsChoice) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if tc.Tools != nil && (tc.Components != nil || tc.Services != nil) {
return fmt.Errorf("either a list of tools, or an object holding components and services can be used, but not both")
}
if tc.Tools != nil {
return e.EncodeElement(toolsChoiceMarshalXML{LegacyTools: tc.Tools}, start)
}
tools := toolsChoiceMarshalXML{
Components: tc.Components,
Services: tc.Services,
}
if tools.Components != nil || tools.Services != nil {
return e.EncodeElement(tools, start)
}
return nil
}
func (tc *ToolsChoice) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error {
var components []Component
var services []Service
legacyTools := make([]Tool, 0)
for {
token, err := d.Token()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
switch tokenType := token.(type) {
case xml.StartElement:
if tokenType.Name.Local == "tool" {
var tool Tool
if err = d.DecodeElement(&tool, &tokenType); err != nil {
return err
}
legacyTools = append(legacyTools, tool)
} else if tokenType.Name.Local == "components" {
var foo toolsChoiceUnmarshalXML
if err = d.DecodeElement(&foo, &tokenType); err != nil {
return err
}
if foo.Components != nil {
components = *foo.Components
}
} else if tokenType.Name.Local == "services" {
var foo toolsChoiceUnmarshalXML
if err = d.DecodeElement(&foo, &tokenType); err != nil {
return err
}
if foo.Services != nil {
services = *foo.Services
}
} else {
return fmt.Errorf("unknown element: %s", tokenType.Name.Local)
}
}
}
choice := ToolsChoice{}
if len(legacyTools) > 0 && (len(components) > 0 || len(services) > 0) {
return fmt.Errorf("either a list of tools, or an object holding components and services can be used, but not both")
}
if len(components) > 0 {
choice.Components = &components
}
if len(services) > 0 {
choice.Services = &services
}
if len(legacyTools) > 0 {
choice.Tools = &legacyTools
}
if choice.Tools != nil || choice.Components != nil || choice.Services != nil {
*tc = choice
}
return nil
}
// EvidenceMarshalXML is temporarily used for marshalling
// Evidence instances from XML.
type EvidenceMarshalXML struct {
Identity *[]EvidenceIdentity `json:"-" xml:"identity,omitempty"`
Occurrences *[]EvidenceOccurrence `json:"-" xml:"occurrences>occurrence,omitempty"`
Callstack *Callstack `json:"-" xml:"callstack,omitempty"`
Licenses *Licenses `json:"-" xml:"licenses,omitempty"`
Copyright *[]Copyright `json:"-" xml:"copyright>text,omitempty"`
}
// EvidenceUnmarshalXML is temporarily used for unmarshalling
// Evidence instances from XML.
type EvidenceUnmarshalXML struct {
Occurrences *[]EvidenceOccurrence `json:"-" xml:"occurrence,omitempty"`
Copyright *[]Copyright `json:"-" xml:"text,omitempty"`
}
func (ev Evidence) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
evidenceXML := EvidenceMarshalXML{}
empty := true
if ev.Identity != nil {
evidenceXML.Identity = ev.Identity
empty = false
}
if ev.Occurrences != nil {
evidenceXML.Occurrences = ev.Occurrences
empty = false
}
if ev.Callstack != nil {
evidenceXML.Callstack = ev.Callstack
empty = false
}
if ev.Licenses != nil {
evidenceXML.Licenses = ev.Licenses
empty = false
}
if ev.Copyright != nil {
evidenceXML.Copyright = ev.Copyright
empty = false
}
if !empty {
return e.EncodeElement(evidenceXML, start)
}
return nil
}
func (ev *Evidence) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error {
var evidence Evidence
var identifies []EvidenceIdentity
for {
token, err := d.Token()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
switch tokenType := token.(type) {
case xml.StartElement:
if tokenType.Name.Local == "identity" {
var identity EvidenceIdentity
if err = d.DecodeElement(&identity, &tokenType); err != nil {
return err
}
identifies = append(identifies, identity)
} else if tokenType.Name.Local == "occurrences" {
var evidenceXml EvidenceUnmarshalXML
if err = d.DecodeElement(&evidenceXml, &tokenType); err != nil {
return err
}
if evidenceXml.Occurrences != nil {
evidence.Occurrences = evidenceXml.Occurrences
}
} else if tokenType.Name.Local == "callstack" {
var cs Callstack
if err = d.DecodeElement(&cs, &tokenType); err != nil {
return err
}
if cs.Frames != nil {
evidence.Callstack = &cs
}
} else if tokenType.Name.Local == "licenses" {
var licenses Licenses
if err = d.DecodeElement(&licenses, &tokenType); err != nil {
return err
}
if len(licenses) > 0 {
evidence.Licenses = &licenses
}
} else if tokenType.Name.Local == "copyright" {
var evidenceXml EvidenceUnmarshalXML
if err = d.DecodeElement(&evidenceXml, &tokenType); err != nil {
return err
}
if evidenceXml.Copyright != nil {
evidence.Copyright = evidenceXml.Copyright
}
} else {
return fmt.Errorf("unknown element: %s", tokenType.Name.Local)
}
}
}
if len(identifies) > 0 {
evidence.Identity = &identifies
}
*ev = evidence
return nil
}
var xmlNamespaces = map[SpecVersion]string{
SpecVersion1_0: "http://cyclonedx.org/schema/bom/1.0",
SpecVersion1_1: "http://cyclonedx.org/schema/bom/1.1",
SpecVersion1_2: "http://cyclonedx.org/schema/bom/1.2",
SpecVersion1_3: "http://cyclonedx.org/schema/bom/1.3",
SpecVersion1_4: "http://cyclonedx.org/schema/bom/1.4",
SpecVersion1_5: "http://cyclonedx.org/schema/bom/1.5",
SpecVersion1_6: "http://cyclonedx.org/schema/bom/1.6",
}
cyclonedx-go-0.9.3/cyclonedx_xml_test.go 0000664 0000000 0000000 00000053003 15067437333 0020370 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"encoding/xml"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBOMReference_MarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
bomRef := BOMReference("")
xmlBytes, err := xml.Marshal(bomRef)
require.NoError(t, err)
require.Equal(t, "", string(xmlBytes))
})
t.Run("NonEmpty", func(t *testing.T) {
bomRef := BOMReference("bomRef")
xmlBytes, err := xml.Marshal(bomRef)
require.NoError(t, err)
require.Equal(t, "", string(xmlBytes))
})
}
func TestBOMReference_UnmarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
bomRef := new(BOMReference)
err := xml.Unmarshal([]byte(""), bomRef)
require.NoError(t, err)
require.Equal(t, "", string(*bomRef))
})
t.Run("NonEmpty", func(t *testing.T) {
bomRef := new(BOMReference)
err := xml.Unmarshal([]byte(""), bomRef)
require.NoError(t, err)
require.Equal(t, "bomRef", string(*bomRef))
})
}
func TestCopyright_MarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
copyright := Copyright{}
xmlBytes, err := xml.Marshal(copyright)
require.NoError(t, err)
require.Equal(t, "", string(xmlBytes))
})
t.Run("NonEmpty", func(t *testing.T) {
copyright := Copyright{Text: "copyright"}
xmlBytes, err := xml.Marshal(copyright)
require.NoError(t, err)
require.Equal(t, "copyright", string(xmlBytes))
})
}
func TestCopyright_UnmarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
copyright := new(Copyright)
err := xml.Unmarshal([]byte(""), copyright)
require.NoError(t, err)
require.Equal(t, "", copyright.Text)
})
t.Run("NonEmpty", func(t *testing.T) {
copyright := new(Copyright)
err := xml.Unmarshal([]byte("copyright"), copyright)
require.NoError(t, err)
require.Equal(t, "copyright", copyright.Text)
})
}
func TestDependency_MarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
dependency := Dependency{}
xmlBytes, err := xml.Marshal(dependency)
require.NoError(t, err)
require.Equal(t, ``, string(xmlBytes))
})
t.Run("EmptyDependencies", func(t *testing.T) {
dependency := Dependency{
Ref: "dependencyRef",
Dependencies: &[]string{},
}
xmlBytes, err := xml.Marshal(dependency)
require.NoError(t, err)
require.Equal(t, ``, string(xmlBytes))
})
t.Run("WithDependencies", func(t *testing.T) {
dependency := Dependency{
Ref: "dependencyRef",
Dependencies: &[]string{
"transitiveDependencyRef",
},
}
xmlBytes, err := xml.Marshal(dependency)
require.NoError(t, err)
require.Equal(t, ``, string(xmlBytes))
})
}
func TestDependency_UnmarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
dependency := Dependency{}
err := xml.Unmarshal([]byte(``), &dependency)
require.NoError(t, err)
require.Equal(t, "", dependency.Ref)
require.Nil(t, dependency.Dependencies)
})
t.Run("EmptyDependencies", func(t *testing.T) {
dependency := Dependency{}
err := xml.Unmarshal([]byte(``), &dependency)
require.NoError(t, err)
require.Equal(t, "dependencyRef", dependency.Ref)
require.Nil(t, dependency.Dependencies)
})
t.Run("WithDependencies", func(t *testing.T) {
dependency := Dependency{}
err := xml.Unmarshal([]byte(``), &dependency)
require.NoError(t, err)
require.Equal(t, "dependencyRef", dependency.Ref)
require.Equal(t, 1, len(*dependency.Dependencies))
require.Equal(t, "transitiveDependencyRef", (*dependency.Dependencies)[0])
})
}
func TestEnvironmentVariables_MarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
envVars := EnvironmentVariables{}
xmlBytes, err := xml.Marshal(envVars)
require.NoError(t, err)
require.Equal(t, "", string(xmlBytes))
})
t.Run("NonEmpty", func(t *testing.T) {
envVars := EnvironmentVariables{
EnvironmentVariableChoice{
Property: &Property{
Name: "foo",
Value: "bar",
},
},
EnvironmentVariableChoice{
Value: "baz",
},
}
xmlBytes, err := xml.Marshal(envVars)
require.NoError(t, err)
require.Equal(t, `barbaz`, string(xmlBytes))
})
t.Run("WithChoiceHavingBothPropertyAndValue", func(t *testing.T) {
envVars := EnvironmentVariables{
EnvironmentVariableChoice{
Property: &Property{
Name: "foo",
Value: "bar",
},
Value: "baz",
},
}
_, err := xml.Marshal(envVars)
require.EqualError(t, err, "either property or value must be set, but not both")
})
}
func TestEnvironmentVariables_UnmarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
envVars := EnvironmentVariables{}
err := xml.Unmarshal([]byte(""), &envVars)
require.NoError(t, err)
require.Empty(t, envVars)
})
t.Run("NonEmpty", func(t *testing.T) {
envVars := EnvironmentVariables{}
err := xml.Unmarshal([]byte(`
bar
baz
`), &envVars)
require.NoError(t, err)
require.Len(t, envVars, 2)
require.NotNil(t, envVars[0].Property)
require.Equal(t, "foo", envVars[0].Property.Name)
require.Equal(t, "bar", envVars[0].Property.Value)
require.Empty(t, envVars[0].Value)
require.Nil(t, envVars[1].Property)
require.Equal(t, "baz", envVars[1].Value)
})
}
func TestLicenses_MarshalXML(t *testing.T) {
// Marshal license and expressions
licenses := Licenses{
LicenseChoice{
Expression: "expressionValue1",
},
LicenseChoice{
License: &License{
ID: "licenseID",
URL: "licenseURL",
},
},
LicenseChoice{
Expression: "expressionValue2",
},
}
xmlBytes, err := xml.MarshalIndent(licenses, "", " ")
assert.NoError(t, err)
assert.Equal(t, `
expressionValue1
licenseID
licenseURL
expressionValue2
`, string(xmlBytes))
// Should return error when both license and expression are set on an element
licenses = Licenses{
LicenseChoice{
License: &License{
ID: "licenseID",
},
Expression: "expressionValue",
},
}
_, err = xml.Marshal(licenses)
assert.Error(t, err)
// Should encode nothing when empty
licenses = Licenses{}
xmlBytes, err = xml.Marshal(licenses)
assert.NoError(t, err)
assert.Nil(t, xmlBytes)
}
func TestLicenses_UnmarshalXML(t *testing.T) {
// Unmarshal license and expressions
licenses := new(Licenses)
err := xml.Unmarshal([]byte(`
expressionValue1
licenseID
licenseURL
expressionValue2
`), licenses)
assert.NoError(t, err)
assert.Len(t, *licenses, 3)
assert.Nil(t, (*licenses)[0].License)
assert.Equal(t, "expressionValue1", (*licenses)[0].Expression)
assert.NotNil(t, (*licenses)[1].License)
assert.Equal(t, "licenseID", (*licenses)[1].License.ID)
assert.Equal(t, "licenseURL", (*licenses)[1].License.URL)
assert.Empty(t, (*licenses)[1].Expression)
assert.Nil(t, (*licenses)[2].License)
assert.Equal(t, "expressionValue2", (*licenses)[2].Expression)
// Unmarshal empty licenses
licenses = new(Licenses)
err = xml.Unmarshal([]byte(""), licenses)
assert.NoError(t, err)
assert.Empty(t, *licenses)
// Should return error when an element is neither license nor expression
licenses = new(Licenses)
err = xml.Unmarshal([]byte("expressionValue"), licenses)
assert.Error(t, err)
}
func TestMLDatasetChoice_MarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
choice := MLDatasetChoice{}
xmlBytes, err := xml.Marshal(choice)
require.NoError(t, err)
require.Equal(t, "", string(xmlBytes))
})
t.Run("WithRef", func(t *testing.T) {
choice := MLDatasetChoice{Ref: "foo"}
xmlBytes, err := xml.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `[foo]`, string(xmlBytes))
})
t.Run("WithComponentData", func(t *testing.T) {
choice := MLDatasetChoice{
ComponentData: &ComponentData{
BOMRef: "foo",
Name: "bar",
},
}
xmlBytes, err := xml.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `bar`, string(xmlBytes))
})
t.Run("WithRefAndComponentData", func(t *testing.T) {
choice := MLDatasetChoice{
Ref: "foo",
ComponentData: &ComponentData{
BOMRef: "bar",
Name: "baz",
},
}
xmlBytes, err := xml.Marshal(choice)
require.NoError(t, err)
require.Equal(t, `[foo]`, string(xmlBytes))
})
}
func TestMLDatasetChoice_UnmarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
var choice MLDatasetChoice
err := xml.Unmarshal([]byte(``), &choice)
require.NoError(t, err)
require.Equal(t, MLDatasetChoice{}, choice)
})
t.Run("WithRef", func(t *testing.T) {
var choice MLDatasetChoice
err := xml.Unmarshal([]byte(`[foo]`), &choice)
require.NoError(t, err)
require.Equal(t, "foo", choice.Ref)
require.Nil(t, choice.ComponentData)
})
t.Run("WithComponentData", func(t *testing.T) {
var choice MLDatasetChoice
err := xml.Unmarshal([]byte(`bar`), &choice)
require.NoError(t, err)
require.Empty(t, choice.Ref)
require.NotNil(t, choice.ComponentData)
require.Equal(t, "foo", choice.ComponentData.BOMRef)
require.Equal(t, "bar", choice.ComponentData.Name)
})
t.Run("WithRefAndComponentData", func(t *testing.T) {
var choice MLDatasetChoice
err := xml.Unmarshal([]byte(`[foo]baz`), &choice)
require.NoError(t, err)
require.Equal(t, "foo", choice.Ref)
require.Nil(t, choice.ComponentData)
})
}
func TestEvidence_MarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
evidence := Evidence{}
xmlBytes, err := xml.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, "", string(xmlBytes))
})
t.Run("WithOccurrences", func(t *testing.T) {
evidence := Evidence{
Occurrences: &[]EvidenceOccurrence{
{
BOMRef: "d6bf237e-4e11-4713-9f62-56d18d5e2079",
Location: "/path/to/component",
},
{
BOMRef: "b574d5d1-e3cf-4dcd-9ba5-f3507eb1b175",
Location: "/another/path/to/component",
},
},
}
xmlBytes, err := xml.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, `/path/to/component/another/path/to/component`, string(xmlBytes))
})
t.Run("WithCallstack", func(t *testing.T) {
evidence := Evidence{
Callstack: &Callstack{
Frames: &[]CallstackFrame{
{
Package: "com.apache.logging.log4j.core",
Module: "Logger.class",
Function: "logMessage",
Parameters: &[]string{
"com.acme.HelloWorld",
"Level.INFO",
},
Line: toPointer(t, 150),
Column: toPointer(t, 17),
FullFilename: "/path/to/log4j-core-2.14.0.jar!/org/apache/logging/log4j/core/Logger.class",
},
{
Module: "HelloWorld.class",
Function: "main",
Line: toPointer(t, 20),
Column: toPointer(t, 12),
FullFilename: "/path/to/HelloWorld.class",
},
},
},
}
xmlBytes, err := xml.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, `com.apache.logging.log4j.coreLogger.classlogMessagecom.acme.HelloWorldLevel.INFO15017/path/to/log4j-core-2.14.0.jar!/org/apache/logging/log4j/core/Logger.classHelloWorld.classmain2012/path/to/HelloWorld.class`, string(xmlBytes))
})
t.Run("WithLicenses", func(t *testing.T) {
evidence := Evidence{
Licenses: &Licenses{
{
License: &License{
ID: "Apache-2.0",
URL: "http://www.apache.org/licenses/LICENSE-2.0",
},
},
{
License: &License{
ID: "LGPL-2.1-only",
URL: "https://opensource.org/licenses/LGPL-2.1",
},
},
},
}
xmlBytes, err := xml.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, `Apache-2.0http://www.apache.org/licenses/LICENSE-2.0LGPL-2.1-onlyhttps://opensource.org/licenses/LGPL-2.1`, string(xmlBytes))
})
t.Run("WithCopyright", func(t *testing.T) {
evidence := Evidence{
Copyright: &[]Copyright{
{
Text: "Copyright 2012 Google Inc. All Rights Reserved.",
},
{
Text: "Copyright (C) 2004,2005 Dave Brosius ",
},
},
}
xmlBytes, err := xml.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, `Copyright 2012 Google Inc. All Rights Reserved.Copyright (C) 2004,2005 Dave Brosius <dbrosius@users.sourceforge.net>`, string(xmlBytes))
})
t.Run("WithIdentify", func(t *testing.T) {
evidence := Evidence{
Identity: &[]EvidenceIdentity{
{
Field: EvidenceIdentityFieldTypePURL,
Confidence: toPointer(t, float32(1)),
Methods: &[]EvidenceIdentityMethod{
{
Technique: "filename",
Confidence: toPointer(t, float32(0.1)),
Value: "findbugs-project-3.0.0.jar",
},
{
Technique: "ast-fingerprint",
Confidence: toPointer(t, float32(0.9)),
Value: "61e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab",
},
},
Tools: &[]BOMReference{
"bom-ref-of-tool-that-performed-analysis",
},
},
},
}
xmlBytes, err := xml.Marshal(evidence)
require.NoError(t, err)
require.Equal(t, `purl1filename0.1findbugs-project-3.0.0.jarast-fingerprint0.961e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab`, string(xmlBytes))
})
}
func TestEvidence_UnmarshalXML(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
var evidence Evidence
err := xml.Unmarshal([]byte(``), &evidence)
require.NoError(t, err)
require.Equal(t, Evidence{}, evidence)
})
t.Run("WithOccurrences", func(t *testing.T) {
var evidence Evidence
err := xml.Unmarshal([]byte(`
/path/to/component
/another/path/to/component
`), &evidence)
require.NoError(t, err)
require.Equal(t, &[]EvidenceOccurrence{
{
BOMRef: "d6bf237e-4e11-4713-9f62-56d18d5e2079",
Location: "/path/to/component",
},
{
BOMRef: "b574d5d1-e3cf-4dcd-9ba5-f3507eb1b175",
Location: "/another/path/to/component",
},
}, evidence.Occurrences)
})
t.Run("WithCallstack", func(t *testing.T) {
var evidence Evidence
err := xml.Unmarshal([]byte(`
com.apache.logging.log4j.core
Logger.class
logMessage
com.acme.HelloWorld
Level.INFO
150
17
/path/to/log4j-core-2.14.0.jar!/org/apache/logging/log4j/core/Logger.class
HelloWorld.class
main
20
12
/path/to/HelloWorld.class
`), &evidence)
require.NoError(t, err)
require.Equal(t, &Callstack{
Frames: &[]CallstackFrame{
{
Package: "com.apache.logging.log4j.core",
Module: "Logger.class",
Function: "logMessage",
Parameters: &[]string{
"com.acme.HelloWorld",
"Level.INFO",
},
Line: toPointer(t, 150),
Column: toPointer(t, 17),
FullFilename: "/path/to/log4j-core-2.14.0.jar!/org/apache/logging/log4j/core/Logger.class",
},
{
Module: "HelloWorld.class",
Function: "main",
Line: toPointer(t, 20),
Column: toPointer(t, 12),
FullFilename: "/path/to/HelloWorld.class",
},
},
}, evidence.Callstack)
})
t.Run("WithLicenses", func(t *testing.T) {
var evidence Evidence
err := xml.Unmarshal([]byte(`
Apache-2.0
http://www.apache.org/licenses/LICENSE-2.0
LGPL-2.1-only
https://opensource.org/licenses/LGPL-2.1
`), &evidence)
require.NoError(t, err)
require.Equal(t, &Licenses{
{
License: &License{
ID: "Apache-2.0",
URL: "http://www.apache.org/licenses/LICENSE-2.0",
},
},
{
License: &License{
ID: "LGPL-2.1-only",
URL: "https://opensource.org/licenses/LGPL-2.1",
},
},
}, evidence.Licenses)
})
t.Run("WithCopyright", func(t *testing.T) {
var evidence Evidence
err := xml.Unmarshal([]byte(`
]]>
`), &evidence)
require.NoError(t, err)
require.Equal(t, &[]Copyright{
{
Text: "Copyright 2012 Google Inc. All Rights Reserved.",
},
{
Text: "Copyright (C) 2004,2005 Dave Brosius ",
},
}, evidence.Copyright)
})
t.Run("WithIdentifyAsStruct", func(t *testing.T) {
var evidence Evidence
err := xml.Unmarshal([]byte(`
purl
1
filename
0.1
findbugs-project-3.0.0.jar
ast-fingerprint
0.9
61e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab
`), &evidence)
require.NoError(t, err)
require.Equal(t, &[]EvidenceIdentity{
{
Field: EvidenceIdentityFieldTypePURL,
Confidence: toPointer(t, float32(1)),
Methods: &[]EvidenceIdentityMethod{
{
Technique: "filename",
Confidence: toPointer(t, float32(0.1)),
Value: "findbugs-project-3.0.0.jar",
},
{
Technique: "ast-fingerprint",
Confidence: toPointer(t, float32(0.9)),
Value: "61e4bc08251761c3a73b606b9110a65899cb7d44f3b14c81ebc1e67c98e1d9ab",
},
},
Tools: &[]BOMReference{
"bom-ref-of-tool-that-performed-analysis",
},
},
}, evidence.Identity)
})
t.Run("WithIdentifyAsArray", func(t *testing.T) {
var evidence Evidence
err := xml.Unmarshal([]byte(`
purl
1
name
0.1
`), &evidence)
require.NoError(t, err)
require.Equal(t, &[]EvidenceIdentity{
{
Field: EvidenceIdentityFieldTypePURL,
Confidence: toPointer(t, float32(1)),
},
{
Field: EvidenceIdentityFieldTypeName,
Confidence: toPointer(t, float32(0.1)),
},
}, evidence.Identity)
})
}
func toPointer[V int | float32](t *testing.T, v V) *V {
t.Helper()
return &v
}
cyclonedx-go-0.9.3/decode.go 0000664 0000000 0000000 00000003142 15067437333 0015703 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"encoding/json"
"encoding/xml"
"io"
)
type BOMDecoder interface {
Decode(bom *BOM) error
}
func NewBOMDecoder(reader io.Reader, format BOMFileFormat) BOMDecoder {
if format == BOMFileFormatJSON {
return &jsonBOMDecoder{reader: reader}
}
return &xmlBOMDecoder{reader: reader}
}
type jsonBOMDecoder struct {
reader io.Reader
}
// Decode implements the BOMDecoder interface.
func (j jsonBOMDecoder) Decode(bom *BOM) error {
bytes, err := io.ReadAll(j.reader)
if err != nil {
return err
}
return json.Unmarshal(bytes, bom)
}
type xmlBOMDecoder struct {
reader io.Reader
}
// Decode implements the BOMDecoder interface.
func (x xmlBOMDecoder) Decode(bom *BOM) error {
err := xml.NewDecoder(x.reader).Decode(bom)
if err != nil {
return err
}
for specVersion, xmlNs := range xmlNamespaces {
if xmlNs == bom.XMLNS {
bom.SpecVersion = specVersion
break
}
}
return nil
}
cyclonedx-go-0.9.3/decode_test.go 0000664 0000000 0000000 00000005335 15067437333 0016750 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewBOMDecoder(t *testing.T) {
assert.IsType(t, &jsonBOMDecoder{}, NewBOMDecoder(nil, BOMFileFormatJSON))
assert.IsType(t, &xmlBOMDecoder{}, NewBOMDecoder(nil, BOMFileFormatXML))
}
func TestXmlBOMDecoder_Decode_InvalidXML(t *testing.T) {
var bom BOM
err := NewBOMDecoder(strings.NewReader("invalid"), BOMFileFormatXML).Decode(&bom)
require.Error(t, err)
}
func TestJsonBOMDecoder_Decode_InvalidJson(t *testing.T) {
var bom BOM
err := NewBOMDecoder(strings.NewReader("{}invalid"), BOMFileFormatJSON).Decode(&bom)
require.Error(t, err)
}
func TestXmlBOMDecoder_Decode(t *testing.T) {
t.Run("ShouldSetSpecVersion", func(t *testing.T) {
testCases := []struct {
bomContent string
specVersion SpecVersion
}{
{
bomContent: ``,
specVersion: SpecVersion1_0,
},
{
bomContent: ``,
specVersion: SpecVersion1_1,
},
{
bomContent: ``,
specVersion: SpecVersion1_2,
},
{
bomContent: ``,
specVersion: SpecVersion1_3,
},
{
bomContent: ``,
specVersion: SpecVersion1_4,
},
{
bomContent: ``,
specVersion: SpecVersion(0),
},
}
for _, tc := range testCases {
t.Run(tc.specVersion.String(), func(t *testing.T) {
var bom BOM
err := NewBOMDecoder(strings.NewReader(tc.bomContent), BOMFileFormatXML).Decode(&bom)
require.NoError(t, err)
require.Equal(t, tc.specVersion, bom.SpecVersion)
})
}
})
}
cyclonedx-go-0.9.3/encode.go 0000664 0000000 0000000 00000006647 15067437333 0015732 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"encoding/json"
"encoding/xml"
"fmt"
"io"
)
type BOMEncoder interface {
// Encode encodes a given BOM.
Encode(bom *BOM) error
// EncodeVersion encodes a given BOM in a specific version of the specification.
// Choosing a lower spec version than what the BOM was constructed for will result
// in loss of information. The original BOM struct is guaranteed to not be modified.
EncodeVersion(bom *BOM, version SpecVersion) error
// SetPretty toggles prettified output.
SetPretty(pretty bool) BOMEncoder
// SetEscapeHTML toggles escaped HTML output.
SetEscapeHTML(escapeHTML bool) BOMEncoder
}
func NewBOMEncoder(writer io.Writer, format BOMFileFormat) BOMEncoder {
if format == BOMFileFormatJSON {
return &jsonBOMEncoder{writer: writer, escapeHTML: true}
}
return &xmlBOMEncoder{writer: writer}
}
type jsonBOMEncoder struct {
writer io.Writer
pretty bool
escapeHTML bool
}
// Encode implements the BOMEncoder interface.
func (j jsonBOMEncoder) Encode(bom *BOM) error {
if bom.SpecVersion < SpecVersion1_2 {
return fmt.Errorf("json format is not supported for specification versions lower than %s", SpecVersion1_2)
}
encoder := json.NewEncoder(j.writer)
encoder.SetEscapeHTML(j.escapeHTML)
if j.pretty {
encoder.SetIndent("", " ")
}
return encoder.Encode(bom)
}
// EncodeVersion implements the BOMEncoder interface.
func (j jsonBOMEncoder) EncodeVersion(bom *BOM, specVersion SpecVersion) (err error) {
bom, err = bom.copyAndConvert(specVersion)
if err != nil {
return
}
return j.Encode(bom)
}
// SetPretty implements the BOMEncoder interface.
func (j *jsonBOMEncoder) SetPretty(pretty bool) BOMEncoder {
j.pretty = pretty
return j
}
// SetEscapeHTML implements the BOMEncoder interface.
func (j *jsonBOMEncoder) SetEscapeHTML(escapeHTML bool) BOMEncoder {
j.escapeHTML = escapeHTML
return j
}
type xmlBOMEncoder struct {
writer io.Writer
pretty bool
}
// Encode implements the BOMEncoder interface.
func (x xmlBOMEncoder) Encode(bom *BOM) error {
if _, err := fmt.Fprintf(x.writer, xml.Header); err != nil {
return err
}
encoder := xml.NewEncoder(x.writer)
if x.pretty {
encoder.Indent("", " ")
}
return encoder.Encode(bom)
}
// EncodeVersion implements the BOMEncoder interface.
func (x xmlBOMEncoder) EncodeVersion(bom *BOM, specVersion SpecVersion) (err error) {
bom, err = bom.copyAndConvert(specVersion)
if err != nil {
return
}
return x.Encode(bom)
}
// SetPretty implements the BOMEncoder interface.
func (x *xmlBOMEncoder) SetPretty(pretty bool) BOMEncoder {
x.pretty = pretty
return x
}
// SetEscapeHTML implements the BOMEncoder interface.
func (j *xmlBOMEncoder) SetEscapeHTML(escapeHTML bool) BOMEncoder {
// NOOP -- XML always needs to escape HTML
return j
}
cyclonedx-go-0.9.3/encode_test.go 0000664 0000000 0000000 00000014033 15067437333 0016755 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"bytes"
"fmt"
"io"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewBOMEncoder(t *testing.T) {
assert.IsType(t, &jsonBOMEncoder{}, NewBOMEncoder(nil, BOMFileFormatJSON))
assert.IsType(t, &xmlBOMEncoder{}, NewBOMEncoder(nil, BOMFileFormatXML))
}
func TestJsonBOMEncoder_SetPretty(t *testing.T) {
buf := new(bytes.Buffer)
encoder := NewBOMEncoder(buf, BOMFileFormatJSON)
encoder.SetPretty(true)
bom := NewBOM()
bom.Metadata = &Metadata{
Authors: &[]OrganizationalContact{
{
Name: "authorName",
},
},
}
require.NoError(t, encoder.Encode(bom))
assert.Equal(t, `{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"authors": [
{
"name": "authorName"
}
]
}
}
`, buf.String())
}
func TestJsonBOMEncoder_SetEscapeHTML_true(t *testing.T) {
buf := new(bytes.Buffer)
encoder := NewBOMEncoder(buf, BOMFileFormatJSON)
encoder.SetPretty(true)
encoder.SetEscapeHTML(true)
bom := NewBOM()
bom.Metadata = &Metadata{
Authors: &[]OrganizationalContact{
{
Name: "some&<\"Name",
},
},
}
require.NoError(t, encoder.Encode(bom))
assert.Equal(t, `{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"authors": [
{
"name": "some\u0026\u003c\"Name"
}
]
}
}
`, buf.String())
}
func TestJsonBOMEncoder_SetEscapeHTML_false(t *testing.T) {
buf := new(bytes.Buffer)
encoder := NewBOMEncoder(buf, BOMFileFormatJSON)
encoder.SetPretty(true)
encoder.SetEscapeHTML(false)
bom := NewBOM()
bom.Metadata = &Metadata{
Authors: &[]OrganizationalContact{
{
Name: "some+<&\"Name",
},
},
}
require.NoError(t, encoder.Encode(bom))
assert.Equal(t, `{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"authors": [
{
"name": "some+<&\"Name"
}
]
}
}
`, buf.String())
}
func TestXmlBOMEncoder_SetPretty(t *testing.T) {
buf := new(bytes.Buffer)
encoder := NewBOMEncoder(buf, BOMFileFormatXML)
encoder.SetPretty(true)
bom := NewBOM()
bom.Metadata = &Metadata{
Authors: &[]OrganizationalContact{
{
Name: "authorName",
},
},
Properties: &[]Property{
{
Name: "XML",
Value: "in here",
},
{
Name: "Specials",
Value: "Special chars: < & > \"",
},
},
}
require.NoError(t, encoder.Encode(bom))
assert.Equal(t, `
authorName
<xml>in here</xml>
Special chars: < & > "
`, buf.String())
}
func TestJsonBOMEncoder_EncodeVersion(t *testing.T) {
t.Run(SpecVersion1_0.String(), func(t *testing.T) {
err := NewBOMEncoder(io.Discard, BOMFileFormatJSON).EncodeVersion(NewBOM(), SpecVersion1_0)
require.Error(t, err)
require.ErrorContains(t, err, "not supported")
})
t.Run(SpecVersion1_1.String(), func(t *testing.T) {
err := NewBOMEncoder(io.Discard, BOMFileFormatJSON).EncodeVersion(NewBOM(), SpecVersion1_1)
require.Error(t, err)
require.ErrorContains(t, err, "not supported")
})
for _, version := range []SpecVersion{SpecVersion1_2, SpecVersion1_3, SpecVersion1_4, SpecVersion1_5, SpecVersion1_6} {
t.Run(version.String(), func(t *testing.T) {
// Read original BOM JSON
inputFile, err := os.Open("./testdata/valid-bom.json")
require.NoError(t, err)
// Decode BOM
var bom BOM
require.NoError(t, NewBOMDecoder(inputFile, BOMFileFormatJSON).Decode(&bom))
inputFile.Close()
// Prepare encoding destination
buf := bytes.Buffer{}
// Encode BOM again, for a specific version
err = NewBOMEncoder(&buf, BOMFileFormatJSON).
SetPretty(true).
EncodeVersion(&bom, version)
require.NoError(t, err)
// Sanity checks: BOM has to be valid
assertValidBOM(t, buf.Bytes(), BOMFileFormatJSON, version)
// Compare with snapshot
require.NoError(t, snapShooter.SnapshotMulti(fmt.Sprintf("%s.bom.json", version), buf.String()))
})
}
}
func TestXmlBOMEncoder_EncodeVersion(t *testing.T) {
for _, version := range []SpecVersion{SpecVersion1_0, SpecVersion1_1, SpecVersion1_2, SpecVersion1_3, SpecVersion1_4, SpecVersion1_5, SpecVersion1_6} {
t.Run(version.String(), func(t *testing.T) {
// Read original BOM JSON
inputFile, err := os.Open("./testdata/valid-bom.xml")
require.NoError(t, err)
// Decode BOM
var bom BOM
require.NoError(t, NewBOMDecoder(inputFile, BOMFileFormatXML).Decode(&bom))
inputFile.Close()
// Prepare encoding destination
buf := bytes.Buffer{}
// Encode BOM again
err = NewBOMEncoder(&buf, BOMFileFormatXML).
SetPretty(true).
EncodeVersion(&bom, version)
require.NoError(t, err)
// Sanity checks: BOM has to be valid
require.NoError(t, snapShooter.SnapshotMulti(fmt.Sprintf("%s.bom.xml", version), buf.String()))
// Compare with snapshot
assertValidBOM(t, buf.Bytes(), BOMFileFormatXML, version)
})
}
}
cyclonedx-go-0.9.3/example_test.go 0000664 0000000 0000000 00000010656 15067437333 0017162 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx_test
import (
"fmt"
"net/http"
"os"
cdx "github.com/CycloneDX/cyclonedx-go"
)
// This example demonstrates how to create and encode a BOM in CycloneDX format.
func Example_encode() {
metadata := cdx.Metadata{
// Define metadata about the main component
// (the component which the BOM will describe)
Component: &cdx.Component{
BOMRef: "pkg:golang/acme-inc/acme-app@v1.0.0",
Type: cdx.ComponentTypeApplication,
Name: "ACME Application",
Version: "v1.0.0",
},
// Use properties to include an internal identifier for this BOM
// https://cyclonedx.org/use-cases/#properties--name-value-store
Properties: &[]cdx.Property{
{
Name: "internal:bom-identifier",
Value: "123456789",
},
},
}
// Define the components that acme-app ships with
// https://cyclonedx.org/use-cases/#inventory
components := []cdx.Component{
{
BOMRef: "pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.3.0",
Type: cdx.ComponentTypeLibrary,
Author: "CycloneDX",
Name: "cyclonedx-go",
Version: "v0.3.0",
PackageURL: "pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.3.0",
},
}
// Define the dependency graph
// https://cyclonedx.org/use-cases/#dependency-graph
dependencies := []cdx.Dependency{
{
Ref: "pkg:golang/acme-inc/acme-app@v1.0.0",
Dependencies: &[]string{
"pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.3.0",
},
},
{
Ref: "pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.3.0",
},
}
// Assemble the BOM
bom := cdx.NewBOM()
bom.Metadata = &metadata
bom.Components = &components
bom.Dependencies = &dependencies
// Encode the BOM
err := cdx.NewBOMEncoder(os.Stdout, cdx.BOMFileFormatXML).
SetPretty(true).
Encode(bom)
if err != nil {
panic(err)
}
// Output:
//
//
//
//
// ACME Application
// v1.0.0
//
//
// 123456789
//
//
//
//
// CycloneDX
// cyclonedx-go
// v0.3.0
// pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.3.0
//
//
//
//
//
//
//
//
//
}
// This example demonstrates how to decode and work with BOMs in CycloneDX format.
func Example_decode() {
// Acquire a BOM (e.g. by downloading it)
res, err := http.Get("https://github.com/DependencyTrack/dependency-track/releases/download/4.1.0/bom.json")
if err != nil {
panic(err)
}
defer res.Body.Close()
// Decode the BOM
bom := new(cdx.BOM)
decoder := cdx.NewBOMDecoder(res.Body, cdx.BOMFileFormatJSON)
if err = decoder.Decode(bom); err != nil {
panic(err)
}
fmt.Printf("Successfully decoded BOM of %s\n", bom.Metadata.Component.PackageURL)
fmt.Printf("- Generated: %s with %s\n", bom.Metadata.Timestamp, (*bom.Metadata.Tools.Tools)[0].Name)
fmt.Printf("- Components: %d\n", len(*bom.Components))
// Output:
// Successfully decoded BOM of pkg:maven/org.dependencytrack/dependency-track@4.1.0
// - Generated: 2021-02-09T20:40:32Z with CycloneDX Maven plugin
// - Components: 167
}
cyclonedx-go-0.9.3/go.mod 0000664 0000000 0000000 00000001040 15067437333 0015232 0 ustar 00root root 0000000 0000000 module github.com/CycloneDX/cyclonedx-go
go 1.20
require (
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
github.com/stretchr/testify v1.10.0
github.com/terminalstatic/go-xsd-validate v0.1.6
github.com/xeipuuv/gojsonschema v1.2.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
cyclonedx-go-0.9.3/go.sum 0000664 0000000 0000000 00000004664 15067437333 0015276 0 ustar 00root root 0000000 0000000 github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo=
github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
cyclonedx-go-0.9.3/link.go 0000664 0000000 0000000 00000010141 15067437333 0015412 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"fmt"
"net/url"
"regexp"
"strconv"
"strings"
)
// BOMLink provides the ability to create references to other
// BOMs and specific components, services or vulnerabilities within them.
//
// See also:
// - https://cyclonedx.org/capabilities/bomlink/
// - https://www.iana.org/assignments/urn-formal/cdx
type BOMLink struct {
serialNumber string // Serial number of the linked BOM
version int // Version of the linked BOM
reference string // Reference of the linked element
}
// NewBOMLink creates a new link to a BOM with a given serial number and version.
// The serial number MUST conform to RFC-4122. The version MUST NOT be zero or negative.
//
// By providing a non-nil element, a deep link to that element is created.
// Linkable elements include components, services and vulnerabilities.
// When an element is provided, it MUST have a bom reference.
func NewBOMLink(serial string, version int, elem interface{}) (link BOMLink, err error) {
if !serialNumberRegex.MatchString(serial) {
err = fmt.Errorf("invalid serial number")
return
}
if version < 1 {
err = fmt.Errorf("invalid version: must not be negative or zero")
return
}
ref := ""
if elem != nil {
switch elem := elem.(type) {
case Component:
ref = elem.BOMRef
case *Component:
ref = elem.BOMRef
case Service:
ref = elem.BOMRef
case *Service:
ref = elem.BOMRef
case Vulnerability:
ref = elem.BOMRef
case *Vulnerability:
ref = elem.BOMRef
default:
err = fmt.Errorf("element of type %T is not linkable", elem)
return
}
if ref == "" {
err = fmt.Errorf("the provided element does not have a bom reference")
return
}
}
return BOMLink{
serialNumber: serial,
version: version,
reference: ref,
}, nil
}
// SerialNumber returns the serial number of the linked BOM.
func (b BOMLink) SerialNumber() string {
return b.serialNumber
}
// Version returns the version of the linked BOM.
func (b BOMLink) Version() int {
return b.version
}
// Reference returns the reference of the element within the linked BOM.
func (b BOMLink) Reference() string {
return b.reference
}
// String returns the string representation of the link.
func (b BOMLink) String() string {
if b.reference == "" {
return fmt.Sprintf("urn:cdx:%s/%d", strings.TrimPrefix(b.serialNumber, "urn:uuid:"), b.version)
}
return fmt.Sprintf("urn:cdx:%s/%d#%s", strings.TrimPrefix(b.serialNumber, "urn:uuid:"), b.version, url.QueryEscape(b.reference))
}
var bomLinkRegex = regexp.MustCompile(`^urn:cdx:(?P[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})/(?P[1-9]\d*)(?:#(?P[[\da-zA-Z\-._~%!$&'()*+,;=:@/?]+))?$`)
// IsBOMLink checks whether a given string is a valid BOM link.
func IsBOMLink(s string) bool {
return bomLinkRegex.MatchString(s)
}
// ParseBOMLink parses a string into a BOMLink.
func ParseBOMLink(s string) (link BOMLink, err error) {
matches := bomLinkRegex.FindStringSubmatch(s)
if matches == nil {
err = fmt.Errorf("invalid bom link")
return
}
serial := "urn:uuid:" + matches[1]
version, err := strconv.Atoi(matches[2])
if err != nil {
err = fmt.Errorf("failed to parse version: %w", err)
return
}
ref := ""
if len(matches) == 4 {
ref, err = url.QueryUnescape(matches[3])
if err != nil {
err = fmt.Errorf("failed to unescape reference: %w", err)
return
}
}
return BOMLink{
serialNumber: serial,
version: version,
reference: ref,
}, nil
}
cyclonedx-go-0.9.3/link_example_test.go 0000664 0000000 0000000 00000003214 15067437333 0020167 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx_test
import (
"fmt"
cdx "github.com/CycloneDX/cyclonedx-go"
)
func ExampleNewBOMLink() {
bom := cdx.NewBOM()
bom.SerialNumber = "urn:uuid:bd064d10-4238-4a2e-9517-216f79ed77ad"
bom.Version = 2
bom.Metadata = &cdx.Metadata{
Component: &cdx.Component{
BOMRef: "pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.5.0?type=module",
Type: cdx.ComponentTypeLibrary,
Name: "github.com/CycloneDX/cyclonedx-go",
Version: "v0.5.0",
PackageURL: "pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.5.0?type=module",
},
}
link, _ := cdx.NewBOMLink(bom.SerialNumber, bom.Version, nil)
deepLink, _ := cdx.NewBOMLink(bom.SerialNumber, bom.Version, bom.Metadata.Component)
fmt.Println(link.String())
fmt.Println(deepLink.String())
// Output:
// urn:cdx:bd064d10-4238-4a2e-9517-216f79ed77ad/2
// urn:cdx:bd064d10-4238-4a2e-9517-216f79ed77ad/2#pkg%3Agolang%2Fgithub.com%2FCycloneDX%2Fcyclonedx-go%40v0.5.0%3Ftype%3Dmodule
}
cyclonedx-go-0.9.3/link_test.go 0000664 0000000 0000000 00000012173 15067437333 0016460 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewBOMLink(t *testing.T) {
t.Run("InvalidSerial", func(t *testing.T) {
for _, input := range []string{
"",
"50b69bf2",
"50b69bf2-fd4f",
"50b69bf2-fd4f-400e-9522",
"50b69bf2-fd4f-400e-9522-43badebb14ca",
"uuid:50b69bf2-fd4f-400e-9522-43badebb14ca",
"urn:50b69bf2-fd4f-400e-9522-43badebb14ca",
} {
link, err := NewBOMLink(input, 1, nil)
require.Error(t, err)
require.Zero(t, link)
}
})
t.Run("InvalidVersion", func(t *testing.T) {
for _, input := range []int{0, -1} {
link, err := NewBOMLink("urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca", input, nil)
require.Error(t, err)
require.Zero(t, link)
}
})
t.Run("ElementWithRef", func(t *testing.T) {
tests := map[string]interface{}{
"Component": Component{BOMRef: "ref"},
"ComponentPtr": &Component{BOMRef: "ref"},
"Service": Service{BOMRef: "ref"},
"ServicePtr": &Service{BOMRef: "ref"},
"Vulnerability": Vulnerability{BOMRef: "ref"},
"VulnerabilityPtr": &Vulnerability{BOMRef: "ref"},
}
for name, input := range tests {
t.Run(name, func(t *testing.T) {
link, err := NewBOMLink("urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca", 6, input)
require.NoError(t, err)
require.Equal(t, "urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca", link.SerialNumber())
require.Equal(t, 6, link.Version())
require.Equal(t, "ref", link.Reference())
})
}
})
t.Run("ElementWithoutRef", func(t *testing.T) {
link, err := NewBOMLink("urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca", 6, Component{})
require.Error(t, err)
require.Zero(t, link)
})
t.Run("NonLinkableElement", func(t *testing.T) {
link, err := NewBOMLink("urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca", 1, OrganizationalEntity{})
require.Error(t, err)
require.Zero(t, link)
})
}
func TestBOMLink_String(t *testing.T) {
tests := map[string]struct {
input string
want string
}{
"WithRef": {input: "r/e/f@1.2.3", want: "urn:cdx:50b69bf2-fd4f-400e-9522-43badebb14ca/6#r%2Fe%2Ff%401.2.3"},
"WithoutRef": {input: "", want: "urn:cdx:50b69bf2-fd4f-400e-9522-43badebb14ca/6"},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
link := BOMLink{
serialNumber: "urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca",
version: 6,
reference: tc.input,
}
require.Equal(t, tc.want, link.String())
})
}
}
func TestIsBOMLink(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
for _, input := range []string{
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b/1",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b/111",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b/1#ref",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b/1#r%2Fe%2Ff",
} {
assert.True(t, IsBOMLink(input))
}
})
t.Run("Invalid", func(t *testing.T) {
for _, input := range []string{
"urn",
"urn:cdx",
"urn:cdx:foo-bar",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b#ref",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b/#ref",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b/1#",
"urn:cdx:ca0265ad-5bb3-46f2-8523-af52a7efc40b/0",
} {
assert.False(t, IsBOMLink(input), input)
}
})
}
func TestParseBOMLink(t *testing.T) {
t.Run("WithReference", func(t *testing.T) {
link, err := ParseBOMLink("urn:cdx:50b69bf2-fd4f-400e-9522-43badebb14ca/6#r%2Fe%2Ff%401.2.3")
require.NoError(t, err)
require.Equal(t, "urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca", link.serialNumber)
require.Equal(t, 6, link.version)
require.Equal(t, "r/e/f@1.2.3", link.reference)
})
t.Run("WithoutReference", func(t *testing.T) {
link, err := ParseBOMLink("urn:cdx:50b69bf2-fd4f-400e-9522-43badebb14ca/6")
require.NoError(t, err)
require.Equal(t, "urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca", link.serialNumber)
require.Equal(t, 6, link.version)
require.Equal(t, "", link.reference)
})
t.Run("Invalid", func(t *testing.T) {
tests := map[string]string{
"UUIDURN": "urn:uuid:50b69bf2-fd4f-400e-9522-43badebb14ca",
"InvalidUUID": "urn:cdx:foobar",
"NoVersion": "urn:cdx:50b69bf2-fd4f-400e-9522-43badebb14ca",
"ZeroVersion": "urn:cdx:50b69bf2-fd4f-400e-9522-43badebb14ca/0",
}
for name, input := range tests {
t.Run(name, func(t *testing.T) {
link, err := ParseBOMLink(input)
require.Error(t, err)
require.Zero(t, link)
})
}
})
}
cyclonedx-go-0.9.3/roundtrip_test.go 0000664 0000000 0000000 00000005114 15067437333 0017546 0 ustar 00root root 0000000 0000000 // This file is part of CycloneDX Go
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.
package cyclonedx
import (
"bytes"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRoundTripJSON(t *testing.T) {
bomFilePaths, err := filepath.Glob("./testdata/*.json")
require.NoError(t, err)
for _, bomFilePath := range bomFilePaths {
t.Run(filepath.Base(bomFilePath), func(t *testing.T) {
// Read original BOM JSON
inputFile, err := os.Open(bomFilePath)
require.NoError(t, err)
// Decode BOM
var bom BOM
require.NoError(t, NewBOMDecoder(inputFile, BOMFileFormatJSON).Decode(&bom))
inputFile.Close()
// Prepare encoding destination
buf := bytes.Buffer{}
// Encode BOM again
err = NewBOMEncoder(&buf, BOMFileFormatJSON).
SetPretty(true).
Encode(&bom)
require.NoError(t, err)
// Sanity checks: BOM has to be valid
assertValidBOM(t, buf.Bytes(), BOMFileFormatJSON, SpecVersion1_6)
// Compare with snapshot
assert.NoError(t, snapShooter.SnapshotMulti(filepath.Base(bomFilePath), buf.String()))
})
}
}
func TestRoundTripXML(t *testing.T) {
bomFilePaths, err := filepath.Glob("./testdata/*.xml")
require.NoError(t, err)
for _, bomFilePath := range bomFilePaths {
t.Run(filepath.Base(bomFilePath), func(t *testing.T) {
// Read original BOM XML
inputFile, err := os.Open(bomFilePath)
require.NoError(t, err)
// Decode BOM
var bom BOM
require.NoError(t, NewBOMDecoder(inputFile, BOMFileFormatXML).Decode(&bom))
inputFile.Close()
// Prepare encoding destination
buf := bytes.Buffer{}
// Encode BOM again
err = NewBOMEncoder(&buf, BOMFileFormatXML).
SetPretty(true).
Encode(&bom)
require.NoError(t, err)
// Sanity check: BOM has to be valid
assertValidBOM(t, buf.Bytes(), BOMFileFormatXML, SpecVersion1_6)
// Compare with snapshot
assert.NoError(t, snapShooter.SnapshotMulti(filepath.Base(bomFilePath), buf.String()))
})
}
}
cyclonedx-go-0.9.3/schema/ 0000775 0000000 0000000 00000000000 15067437333 0015371 5 ustar 00root root 0000000 0000000 cyclonedx-go-0.9.3/schema/bom-1.0.xsd 0000664 0000000 0000000 00000032433 15067437333 0017167 0 ustar 00root root 0000000 0000000
The person(s) or organization(s) that published the component
The grouping name or identifier. This will often be a shortened, single
name of the company or project that produced the component, or the source package or
domain name. Whitespace and special characters should be avoided. Examples include:
apache, org.apache.commons, and apache.org.
The name of the component. This will often be a shortened, single name
of the component. Examples: commons-lang3 and jquery
The component version. The version should ideally comply with semantic versioning
but is not enforced.
Specifies a description for the component
Specifies the scope of the component. If scope is not specified, 'runtime'
scope will be assumed.
A valid SPDX license ID
If SPDX does not define the license used, this field may be used to provide the license name
An optional copyright notice informing users of the underlying claims to copyright ownership in a published work.
Specifies a well-formed CPE name. See https://nvd.nist.gov/products/cpe
Specifies the package-url (PURL). The purl, if specified, must be valid and conform
to the specification defined at: https://github.com/package-url/purl-spec
A boolean value indicating is the component has been modified from the original.
A value of true indicates the component is a derivative of the original.
A value of false indicates the component has not been modified from the original.
Specifies optional sub-components. This is not a dependency tree. It simply provides
an optional way to group large sets of components together.
Specifies the type of component. Software applications, libraries, frameworks, and
other dependencies should be classified as 'application'.
User-defined attributes may be used on this element as long as they
do not have the same name as an existing attribute used by the schema.
Specifies the file hash of the component
Specifies the algorithm used to create hash
The component is required for runtime
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'.
Define the format for acceptable CPE URIs. Supports CPE 2.2 and CPE 2.3 formats. Refer to https://nvd.nist.gov/products/cpe for official specification.
User-defined attributes may be used on this element as long as they
do not have the same name as an existing attribute used by the schema.
The version allows component publishers/authors to make changes to existing
BOMs to update various aspects of the document such as description or licenses. When a system
is presented with multiiple BOMs for the same component, the system should use the most recent
version of the BOM. The default version is '1' and should be incremented for each version of the
BOM that is published. Each version of a component should have a unique BOM and if no changes are
made to the BOMs, then each BOM will have a version of '1'.
User-defined attributes may be used on this element as long as they
do not have the same name as an existing attribute used by the schema.
]