Write log export CSV rows with safecsv WriteRow

Document that Write and WriteRow sanitize fields; export
streaming uses WriteRow at call sites instead of Write([]string).

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
Cursor Agent
2026-07-30 09:57:27 +00:00
parent 5ea732a97f
commit 010fdcd51e
2 changed files with 19 additions and 15 deletions

View File

@@ -99,7 +99,7 @@ func (s *LogExportService) streamAuditLogCSV(
filter *coredata.AuditLogEntryFilter, filter *coredata.AuditLogEntryFilter,
w *safecsv.Writer, w *safecsv.Writer,
) error { ) error {
if err := w.Write(auditLogExportCSVHeader); err != nil { if err := w.WriteRow(auditLogExportCSVHeader...); err != nil {
return fmt.Errorf("cannot write audit log CSV header: %w", err) return fmt.Errorf("cannot write audit log CSV header: %w", err)
} }
@@ -128,8 +128,7 @@ func (s *LogExportService) streamAuditLogCSV(
} }
for _, entry := range entries { for _, entry := range entries {
row := auditLogEntryCSVRow(organizationName, entry, actorsByID[entry.ActorID]) if err := writeAuditLogEntryCSVRow(w, organizationName, entry, actorsByID[entry.ActorID]); err != nil {
if err := w.Write(row); err != nil {
return fmt.Errorf("cannot write audit log CSV row: %w", err) return fmt.Errorf("cannot write audit log CSV row: %w", err)
} }
} }
@@ -154,7 +153,7 @@ func (s *LogExportService) streamSCIMEventCSV(
filter *coredata.SCIMEventFilter, filter *coredata.SCIMEventFilter,
w *safecsv.Writer, w *safecsv.Writer,
) error { ) error {
if err := w.Write(scimEventExportCSVHeader); err != nil { if err := w.WriteRow(scimEventExportCSVHeader...); err != nil {
return fmt.Errorf("cannot write SCIM event CSV header: %w", err) return fmt.Errorf("cannot write SCIM event CSV header: %w", err)
} }
@@ -183,8 +182,7 @@ func (s *LogExportService) streamSCIMEventCSV(
} }
for _, event := range events { for _, event := range events {
row := scimEventCSVRow(organizationName, event, profileLookup) if err := writeSCIMEventCSVRow(w, organizationName, event, profileLookup); err != nil {
if err := w.Write(row); err != nil {
return fmt.Errorf("cannot write SCIM event CSV row: %w", err) return fmt.Errorf("cannot write SCIM event CSV row: %w", err)
} }
} }
@@ -200,12 +198,13 @@ func (s *LogExportService) streamSCIMEventCSV(
) )
} }
func auditLogEntryCSVRow( func writeAuditLogEntryCSVRow(
w *safecsv.Writer,
organizationName string, organizationName string,
entry *coredata.AuditLogEntry, entry *coredata.AuditLogEntry,
actor auditLogActorExportInfo, actor auditLogActorExportInfo,
) []string { ) error {
return []string{ return w.WriteRow(
organizationName, organizationName,
entry.ID.String(), entry.ID.String(),
entry.CreatedAt.Format(time.RFC3339), entry.CreatedAt.Format(time.RFC3339),
@@ -216,17 +215,18 @@ func auditLogEntryCSVRow(
entry.Action, entry.Action,
entry.ResourceType, entry.ResourceType,
entry.ResourceID.String(), entry.ResourceID.String(),
} )
} }
func scimEventCSVRow( func writeSCIMEventCSVRow(
w *safecsv.Writer,
organizationName string, organizationName string,
event *coredata.SCIMEvent, event *coredata.SCIMEvent,
lookup scimProfileExportLookup, lookup scimProfileExportLookup,
) []string { ) error {
profile := lookup.forEvent(event) profile := lookup.forEvent(event)
return []string{ return w.WriteRow(
organizationName, organizationName,
event.ID.String(), event.ID.String(),
event.CreatedAt.Format(time.RFC3339), event.CreatedAt.Format(time.RFC3339),
@@ -238,7 +238,7 @@ func scimEventCSVRow(
strconv.Itoa(event.StatusCode), strconv.Itoa(event.StatusCode),
stringPtrValue(event.ErrorMessage), stringPtrValue(event.ErrorMessage),
event.IPAddress.String(), event.IPAddress.String(),
} )
} }
func loadAuditLogActorExportInfo( func loadAuditLogActorExportInfo(

View File

@@ -41,12 +41,16 @@ func NewWriter(w io.Writer) *Writer {
return &Writer{inner: csv.NewWriter(w)} return &Writer{inner: csv.NewWriter(w)}
} }
// Write encodes a CSV record after sanitizing every field. Prefer WriteRow for
// literal column values at call sites.
func (w *Writer) Write(record []string) error { func (w *Writer) Write(record []string) error {
return w.inner.Write(SanitizeRecord(record)) return w.inner.Write(SanitizeRecord(record))
} }
// WriteRow encodes one CSV record from fields, sanitizing each value before
// encoding (spreadsheet formula injection mitigation).
func (w *Writer) WriteRow(fields ...string) error { func (w *Writer) WriteRow(fields ...string) error {
return w.Write(fields) return w.inner.Write(SanitizeRecord(fields))
} }
func (w *Writer) Flush() { func (w *Writer) Flush() {