Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add support for cyclonedx spec v1.4 #16

Merged
merged 12 commits into from
Feb 7, 2022
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ jobs:
run: |
mkdir -p "$HOME/.local/bin"
echo "$HOME/.local/bin" >> $GITHUB_PATH
wget -O "$HOME/.local/bin/cyclonedx" https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.15.2/cyclonedx-linux-x64
wget -O "$HOME/.local/bin/cyclonedx" https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.22.0/cyclonedx-linux-x64
echo "ae39404a9dc8b2e7be0a9559781ee9fe3492201d2629de139d702fd4535ffdd6 $HOME/.local/bin/cyclonedx" | sha256sum -c
chmod +x "$HOME/.local/bin/cyclonedx"
- name: Checkout Repository
uses: actions/checkout@v2
Expand Down
167 changes: 159 additions & 8 deletions cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,26 @@ import (
const (
BOMFormat = "CycloneDX"
defaultVersion = 1
SpecVersion = "1.3"
XMLNamespace = "http://cyclonedx.org/schema/bom/1.3"
SpecVersion = "1.4"
XMLNamespace = "http://cyclonedx.org/schema/bom/1.4"
)

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 AttachedText struct {
Content string `json:"content" xml:",innerxml"`
ContentType string `json:"contentType,omitempty" xml:"content-type,attr,omitempty"`
Expand All @@ -56,6 +72,7 @@ type BOM struct {
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"`
}

func NewBOM() *BOM {
Expand Down Expand Up @@ -129,7 +146,7 @@ type Component struct {
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" xml:"version"`
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"`
Expand All @@ -144,6 +161,7 @@ type Component struct {
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"`
}

type Composition struct {
Expand Down Expand Up @@ -180,6 +198,11 @@ func (c *Copyright) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return nil
}

type Credits struct {
Organizations *[]OrganizationalEntity `json:"organizations,omitempty" xml:"organizations>organization,omitempty"`
Individuals *[]OrganizationalContact `json:"individuals,omitempty" xml:"individuals>individual,omitempty"`
}

type DataClassification struct {
Flow DataFlow `json:"flow" xml:"flow,attr"`
Classification string `json:"classification" xml:",innerxml"`
Expand Down Expand Up @@ -276,6 +299,7 @@ const (
ERTypeMailingList ExternalReferenceType = "mailing-list"
ERTypeOther ExternalReferenceType = "other"
ERTypeIssueTracker ExternalReferenceType = "issue-tracker"
ERTypeReleaseNotes ExternalReferenceType = "release-notes"
ERTypeSocial ExternalReferenceType = "social"
ERTypeSupport ExternalReferenceType = "support"
ERTypeVCS ExternalReferenceType = "vcs"
Expand Down Expand Up @@ -309,9 +333,44 @@ type IdentifiableAction struct {
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 Issue struct {
ID string `json:"id" xml:"id"`
Name string `json:"name" xml:"name"`
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"`
Expand Down Expand Up @@ -415,6 +474,11 @@ type Metadata struct {
Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"`
}

type Note struct {
Locale string `json:"locale,omitempty" xml:"locale,omitempty"`
Text AttachedText `json:"text" xml:"text"`
}

type OrganizationalContact struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
EMail string `json:"email,omitempty" xml:"email,omitempty"`
Expand Down Expand Up @@ -456,6 +520,20 @@ type Property struct {
Value string `json:"value" xml:",innerxml"`
}

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 Scope string

const (
Expand All @@ -464,6 +542,16 @@ const (
ScopeRequired Scope = "required"
)

type ScoringMethod string

const (
ScoringMethodOther ScoringMethod = "other"
ScoringMethodCVSSv2 ScoringMethod = "CVSSv2"
ScoringMethodCVSSv3 ScoringMethod = "CVSSv3"
ScoringMethodCVSSv31 ScoringMethod = "CVSSv31"
ScoringMethodOWASP ScoringMethod = "OWASP"
)

type Service struct {
BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
Provider *OrganizationalEntity `json:"provider,omitempty" xml:"provider,omitempty"`
Expand All @@ -479,8 +567,21 @@ type Service struct {
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"`
}

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"
)

type Source struct {
Name string `json:"name,omitempty" xml:"name,omitempty"`
URL string `json:"url,omitempty" xml:"url,omitempty"`
Expand All @@ -497,8 +598,58 @@ type SWID struct {
}

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"`
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"`
}

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"`
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"`
Credits *Credits `json:"credits,omitempty" xml:"credits,omitempty"`
Tools *[]Tool `json:"tools,omitempty" xml:"tools>tool,omitempty"`
Analysis *VulnerabilityAnalysis `json:"analysis,omitempty" xml:"analysis,omitempty"`
Affects *[]Affects `json:"affects,omitempty" xml:"affects>target,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"`
}

type VulnerabilityRating struct {
Source *Source `json:"source,omitempty" xml:"source,omitempty"`
Score float64 `json:"score" xml:"score"`
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"
)
4 changes: 2 additions & 2 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestJsonBOMEncoder_SetPretty(t *testing.T) {

assert.Equal(t, `{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"version": 1,
"metadata": {
"authors": [
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestXmlBOMEncoder_SetPretty(t *testing.T) {
require.NoError(t, encoder.Encode(bom))

assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" version="1">
<metadata>
<authors>
<author>
Expand Down
5 changes: 3 additions & 2 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package cyclonedx_test

import (
"fmt"
cdx "github.com/CycloneDX/cyclonedx-go"
"net/http"
"os"

cdx "github.com/CycloneDX/cyclonedx-go"
)

// This example demonstrates how to create and encode a BOM in CycloneDX format.
Expand Down Expand Up @@ -70,7 +71,7 @@ func Example_encode() {

// Output:
// <?xml version="1.0" encoding="UTF-8"?>
// <bom xmlns="http://cyclonedx.org/schema/bom/1.3" version="1">
// <bom xmlns="http://cyclonedx.org/schema/bom/1.4" version="1">
// <metadata>
// <component bom-ref="pkg:golang/acme-inc/acme-app@v1.0.0" type="application">
// <name>ACME Application</name>
Expand Down
6 changes: 3 additions & 3 deletions roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ func TestRoundTripXML(t *testing.T) {
}

func assertValidBOM(t *testing.T, bomFilePath string) {
inputFormat := "xml_v1_3"
inputFormat := "xml"
if strings.HasSuffix(bomFilePath, ".json") {
inputFormat = "json_v1_3"
inputFormat = "json"
}
valCmd := exec.Command("cyclonedx", "validate", "--input-file", bomFilePath, "--input-format", inputFormat, "--fail-on-errors")
valCmd := exec.Command("cyclonedx", "validate", "--input-file", bomFilePath, "--input-format", inputFormat, "--input-version", "v1_4", "--fail-on-errors")
valOut, err := valCmd.CombinedOutput()
if !assert.NoError(t, err) {
// Provide some context when test is failing
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"components": []
Expand Down
Loading