From 7f95b018c0437e5dc27b4174d8fb14b0fb4decf1 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 16 Oct 2023 11:18:22 -0400 Subject: [PATCH] fix: stop escaping HTML (#224) Signed-off-by: Keith Zantow --- Makefile | 5 ++ convert/spdx_document_conversion_test.go | 4 +- json/marshal/json.go | 15 ++++++ json/marshal/json_test.go | 60 ++++++++++++++++++++++++ json/writer_test.go | 6 ++- spdx/v2/common/annotation.go | 5 +- spdx/v2/common/creation_info.go | 5 +- spdx/v2/common/identifier.go | 11 +++-- spdx/v2/common/identifier_test.go | 8 ++-- spdx/v2/common/package.go | 11 +++-- spdx/v2/v2_2/package.go | 3 +- spdx/v2/v2_3/package.go | 3 +- spdxlib/described_elements.go | 6 +-- 13 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 json/marshal/json.go create mode 100644 json/marshal/json_test.go diff --git a/Makefile b/Makefile index ec42b613..064db79c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,11 @@ .PHONY: test test: unit fuzz +.PHONY: format +format: + gofmt -w . + gosimports -w -local github.com/spdx . + .PHONY: unit unit: go test -v -covermode=count -coverprofile=profile.cov ./... diff --git a/convert/spdx_document_conversion_test.go b/convert/spdx_document_conversion_test.go index 23ca44d2..06040bd3 100644 --- a/convert/spdx_document_conversion_test.go +++ b/convert/spdx_document_conversion_test.go @@ -3,12 +3,12 @@ package convert import ( - "encoding/json" "reflect" "testing" "github.com/stretchr/testify/require" + "github.com/spdx/tools-golang/json/marshal" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" "github.com/spdx/tools-golang/spdx/v2/v2_1" @@ -2128,7 +2128,7 @@ func Test_ConvertSPDXDocuments(t *testing.T) { } func toJSON(data interface{}) string { - bytes, err := json.Marshal(data) + bytes, err := marshal.JSON(data) if err != nil { panic(err) } diff --git a/json/marshal/json.go b/json/marshal/json.go new file mode 100644 index 00000000..b5baefec --- /dev/null +++ b/json/marshal/json.go @@ -0,0 +1,15 @@ +package marshal + +import ( + "bytes" + "encoding/json" +) + +// JSON marshals the object _without_ escaping HTML +func JSON(obj interface{}) ([]byte, error) { + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + err := enc.Encode(obj) + return bytes.TrimSpace(buf.Bytes()), err +} diff --git a/json/marshal/json_test.go b/json/marshal/json_test.go new file mode 100644 index 00000000..dbbdedc3 --- /dev/null +++ b/json/marshal/json_test.go @@ -0,0 +1,60 @@ +package marshal + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_MarshalJSON(t *testing.T) { + tests := []struct { + name string + in interface{} + expected string + }{ + { + name: "basic usage", + in: "", + expected: `""`, + }, + { + name: "within MarshalJSON callbacks", + in: s1{ + s2{ + s3{ + Value: "", + }, + }, + }, + expected: `{"S2":{"S3":{"Value":""}}}`, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := JSON(test.in) + require.NoError(t, err) + require.Equal(t, test.expected, string(got)) + }) + } +} + +type s1 struct { + S2 s2 +} + +type s2 struct { + S3 s3 +} + +func (s *s2) MarshalJSON() ([]byte, error) { + return JSON(s.S3) +} + +type s3 struct { + Value string +} + +func (s *s3) MarshalJSON() ([]byte, error) { + return JSON(s.Value) +} diff --git a/json/writer_test.go b/json/writer_test.go index f00f156c..0588d06b 100644 --- a/json/writer_test.go +++ b/json/writer_test.go @@ -2,11 +2,13 @@ package json_test import ( "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/spdx/tools-golang/json" "github.com/spdx/tools-golang/spdx/common" spdx "github.com/spdx/tools-golang/spdx/v2/v2_3" - "github.com/stretchr/testify/assert" - "testing" ) func Test_Write(t *testing.T) { diff --git a/spdx/v2/common/annotation.go b/spdx/v2/common/annotation.go index e77d7b78..645aa924 100644 --- a/spdx/v2/common/annotation.go +++ b/spdx/v2/common/annotation.go @@ -3,9 +3,10 @@ package common import ( - "encoding/json" "fmt" "strings" + + "github.com/spdx/tools-golang/json/marshal" ) type Annotator struct { @@ -37,7 +38,7 @@ func (a *Annotator) UnmarshalJSON(data []byte) error { // This function is also used when marshalling to YAML func (a Annotator) MarshalJSON() ([]byte, error) { if a.Annotator != "" { - return json.Marshal(fmt.Sprintf("%s: %s", a.AnnotatorType, a.Annotator)) + return marshal.JSON(fmt.Sprintf("%s: %s", a.AnnotatorType, a.Annotator)) } return []byte{}, nil diff --git a/spdx/v2/common/creation_info.go b/spdx/v2/common/creation_info.go index c87ae7be..77b24ee0 100644 --- a/spdx/v2/common/creation_info.go +++ b/spdx/v2/common/creation_info.go @@ -3,9 +3,10 @@ package common import ( - "encoding/json" "fmt" "strings" + + "github.com/spdx/tools-golang/json/marshal" ) // Creator is a wrapper around the Creator SPDX field. The SPDX field contains two values, which requires special @@ -37,7 +38,7 @@ func (c *Creator) UnmarshalJSON(data []byte) error { // This function is also used with marshalling to YAML func (c Creator) MarshalJSON() ([]byte, error) { if c.Creator != "" { - return json.Marshal(fmt.Sprintf("%s: %s", c.CreatorType, c.Creator)) + return marshal.JSON(fmt.Sprintf("%s: %s", c.CreatorType, c.Creator)) } return []byte{}, nil diff --git a/spdx/v2/common/identifier.go b/spdx/v2/common/identifier.go index 806a8157..929e42fa 100644 --- a/spdx/v2/common/identifier.go +++ b/spdx/v2/common/identifier.go @@ -3,9 +3,10 @@ package common import ( - "encoding/json" "fmt" "strings" + + "github.com/spdx/tools-golang/json/marshal" ) const ( @@ -21,7 +22,7 @@ type ElementID string // MarshalJSON returns an SPDXRef- prefixed JSON string func (d ElementID) MarshalJSON() ([]byte, error) { - return json.Marshal(prefixElementId(d)) + return marshal.JSON(prefixElementId(d)) } // UnmarshalJSON validates SPDXRef- prefixes and removes them when processing ElementIDs @@ -85,11 +86,11 @@ type DocElementID struct { func (d DocElementID) MarshalJSON() ([]byte, error) { if d.DocumentRefID != "" && d.ElementRefID != "" { idStr := prefixElementId(d.ElementRefID) - return json.Marshal(fmt.Sprintf("%s%s:%s", documentRefPrefix, d.DocumentRefID, idStr)) + return marshal.JSON(fmt.Sprintf("%s%s:%s", documentRefPrefix, d.DocumentRefID, idStr)) } else if d.ElementRefID != "" { - return json.Marshal(prefixElementId(d.ElementRefID)) + return marshal.JSON(prefixElementId(d.ElementRefID)) } else if d.SpecialID != "" { - return json.Marshal(d.SpecialID) + return marshal.JSON(d.SpecialID) } return []byte{}, fmt.Errorf("failed to marshal empty DocElementID") diff --git a/spdx/v2/common/identifier_test.go b/spdx/v2/common/identifier_test.go index 0fbb52e0..5adfd436 100644 --- a/spdx/v2/common/identifier_test.go +++ b/spdx/v2/common/identifier_test.go @@ -8,6 +8,8 @@ import ( "reflect" "strings" "testing" + + "github.com/spdx/tools-golang/json/marshal" ) func Test_DocElementIDEncoding(t *testing.T) { @@ -50,7 +52,7 @@ func Test_DocElementIDEncoding(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - result, err := json.Marshal(test.value) + result, err := marshal.JSON(test.value) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) @@ -167,7 +169,7 @@ func Test_ElementIDEncoding(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - result, err := json.Marshal(test.value) + result, err := marshal.JSON(test.value) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) @@ -255,7 +257,7 @@ func Test_ElementIDStructEncoding(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - result, err := json.Marshal(test.value) + result, err := marshal.JSON(test.value) switch { case !test.err && err != nil: t.Fatalf("unexpected error: %v", err) diff --git a/spdx/v2/common/package.go b/spdx/v2/common/package.go index 349cef25..b9ee2c5c 100644 --- a/spdx/v2/common/package.go +++ b/spdx/v2/common/package.go @@ -3,9 +3,10 @@ package common import ( - "encoding/json" "fmt" "strings" + + "github.com/spdx/tools-golang/json/marshal" ) type Supplier struct { @@ -43,9 +44,9 @@ func (s *Supplier) UnmarshalJSON(data []byte) error { // This function is also used when marshalling to YAML func (s Supplier) MarshalJSON() ([]byte, error) { if s.Supplier == "NOASSERTION" { - return json.Marshal(s.Supplier) + return marshal.JSON(s.Supplier) } else if s.SupplierType != "" && s.Supplier != "" { - return json.Marshal(fmt.Sprintf("%s: %s", s.SupplierType, s.Supplier)) + return marshal.JSON(fmt.Sprintf("%s: %s", s.SupplierType, s.Supplier)) } return []byte{}, fmt.Errorf("failed to marshal invalid Supplier: %+v", s) @@ -84,9 +85,9 @@ func (o *Originator) UnmarshalJSON(data []byte) error { // This function is also used when marshalling to YAML func (o Originator) MarshalJSON() ([]byte, error) { if o.Originator == "NOASSERTION" { - return json.Marshal(o.Originator) + return marshal.JSON(o.Originator) } else if o.Originator != "" { - return json.Marshal(fmt.Sprintf("%s: %s", o.OriginatorType, o.Originator)) + return marshal.JSON(fmt.Sprintf("%s: %s", o.OriginatorType, o.Originator)) } return []byte{}, nil diff --git a/spdx/v2/v2_2/package.go b/spdx/v2/v2_2/package.go index 54de537c..a2ecb3ed 100644 --- a/spdx/v2/v2_2/package.go +++ b/spdx/v2/v2_2/package.go @@ -6,6 +6,7 @@ import ( "encoding/json" "strings" + "github.com/spdx/tools-golang/json/marshal" "github.com/spdx/tools-golang/spdx/v2/common" ) @@ -199,5 +200,5 @@ func (r *PackageExternalReference) MarshalJSON() ([]byte, error) { rr = ref(*r) rr.Category = strings.ReplaceAll(rr.Category, "-", "_") - return json.Marshal(&rr) + return marshal.JSON(&rr) } diff --git a/spdx/v2/v2_3/package.go b/spdx/v2/v2_3/package.go index 0acadc27..6488a7e8 100644 --- a/spdx/v2/v2_3/package.go +++ b/spdx/v2/v2_3/package.go @@ -6,6 +6,7 @@ import ( "encoding/json" "strings" + "github.com/spdx/tools-golang/json/marshal" "github.com/spdx/tools-golang/spdx/v2/common" ) @@ -217,5 +218,5 @@ func (r *PackageExternalReference) MarshalJSON() ([]byte, error) { rr.Category = strings.ReplaceAll(rr.Category, "_", "-") - return json.Marshal(&rr) + return marshal.JSON(&rr) } diff --git a/spdxlib/described_elements.go b/spdxlib/described_elements.go index 048a6ab8..5a727815 100644 --- a/spdxlib/described_elements.go +++ b/spdxlib/described_elements.go @@ -12,9 +12,9 @@ import ( // GetDescribedPackageIDs returns a slice of ElementIDs for all Packages // in this Document that it "describes," according to SPDX rules: -// - If the document has only one Package, its ID is returned. -// - If the document has 2+ Packages, it returns the IDs of those that have -// a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. +// - If the document has only one Package, its ID is returned. +// - If the document has 2+ Packages, it returns the IDs of those that have +// a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. func GetDescribedPackageIDs(doc *spdx.Document) ([]common.ElementID, error) { // if nil Packages map or zero packages in it, return empty slice if doc.Packages == nil {