From a1f446f4431cb54be9c1815c61b6e76349839085 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Wed, 17 Aug 2022 12:08:38 +0200 Subject: [PATCH] Use fully quoted CSV writer for changes.csv --- cmd/csaf_aggregator/indices.go | 2 +- cmd/csaf_provider/indices.go | 4 +- util/csv.go | 71 ++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 util/csv.go diff --git a/cmd/csaf_aggregator/indices.go b/cmd/csaf_aggregator/indices.go index e2524a4..854bfb8 100644 --- a/cmd/csaf_aggregator/indices.go +++ b/cmd/csaf_aggregator/indices.go @@ -105,7 +105,7 @@ func (w *worker) writeCSV(label string, summaries []summary) error { if err != nil { return err } - out := csv.NewWriter(f) + out := util.NewFullyQuotedCSWWriter(f) record := make([]string, 2) diff --git a/cmd/csaf_provider/indices.go b/cmd/csaf_provider/indices.go index 4ecd534..1b9b19f 100644 --- a/cmd/csaf_provider/indices.go +++ b/cmd/csaf_provider/indices.go @@ -17,6 +17,8 @@ import ( "path/filepath" "sort" "time" + + "github.com/csaf-poc/csaf_distribution/util" ) func updateIndex(dir, fname string) error { @@ -141,7 +143,7 @@ func updateChanges(dir, fname string, releaseDate time.Time) error { if err != nil { return err } - c := csv.NewWriter(o) + c := util.NewFullyQuotedCSWWriter(o) record := make([]string, 2) for _, ch := range chs { record[timeColumn] = ch.time.Format(dateFormat) diff --git a/util/csv.go b/util/csv.go new file mode 100644 index 0000000..aee0e6d --- /dev/null +++ b/util/csv.go @@ -0,0 +1,71 @@ +// This file is Free Software under the MIT License +// without warranty, see README.md and LICENSES/MIT.txt for details. +// +// SPDX-License-Identifier: MIT +// +// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) +// Software-Engineering: 2022 Intevation GmbH + +package util + +import ( + "bufio" + "io" + "strings" +) + +// FullyQuotedCSVWriter implements a CSV writer +// which puts each field in double quotes ("). +type FullyQuotedCSVWriter struct { + // Comma is the separator between fields. Defaults to ','. + Comma rune + // UseCRLF indicates if "\r\n" should be used for line separation. + UseCRLF bool + w *bufio.Writer +} + +// NewFullyQuotedCSWWriter returns a new writer that writes to w. +func NewFullyQuotedCSWWriter(w io.Writer) *FullyQuotedCSVWriter { + return &FullyQuotedCSVWriter{ + Comma: ',', + w: bufio.NewWriter(w), + } +} + +// Write writes a single CSV record to w along with any necessary quoting. +// A record is a slice of strings with each string being one field. +// Writes are buffered, so Flush must eventually be called to ensure +// that the record is written to the underlying io.Writer. +func (fqcw *FullyQuotedCSVWriter) Write(record []string) error { + + for i, field := range record { + if i > 0 { + fqcw.w.WriteRune(fqcw.Comma) + } + fqcw.w.WriteByte('"') + if !fqcw.UseCRLF { + field = strings.ReplaceAll(field, "\r\n", "\n") + } + fqcw.w.WriteString(strings.ReplaceAll(field, `"`, `""`)) + fqcw.w.WriteByte('"') + } + var err error + if fqcw.UseCRLF { + _, err = fqcw.w.WriteString("\r\n") + } else { + err = fqcw.w.WriteByte('\n') + } + return err +} + +// Flush writes any buffered data to the underlying io.Writer. +// To check if an error occurred during the Flush, call Error. +func (fqcw *FullyQuotedCSVWriter) Flush() { + fqcw.w.Flush() +} + +// Error reports any error that has occurred during a previous Write or Flush. +func (fqcw *FullyQuotedCSVWriter) Error() error { + _, err := fqcw.w.Write(nil) + return err +}