From 48c16de8bf74df0fa94a30d29e2e7e3446d48433 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 15 Aug 2021 00:06:40 +0800 Subject: [PATCH] Improve security and simplify code - Make variable name more semantic - Reduce cyclomatic complexities for the formula calculate function - Support specified unzip size limit on open file options, avoid zip bombs vulnerability attack - Typo fix for documentation and error message --- calc.go | 59 +++++++++++++++++++++++++----------------- comment_test.go | 8 +++--- crypt.go | 10 ++++--- crypt_test.go | 8 +++--- datavalidation_test.go | 8 +++--- date_test.go | 2 +- errors.go | 12 ++++++++- errors_test.go | 2 +- excelize.go | 44 +++++++++++++++++++------------ excelize_test.go | 15 ++++++----- file.go | 16 +++++++----- file_test.go | 12 +++------ lib.go | 28 ++++++++++++-------- merge_test.go | 6 ++--- picture.go | 14 +++++----- picture_test.go | 10 +++---- sheet.go | 8 +++--- sheetpr.go | 10 +++---- stream.go | 4 +-- xmlChartSheet.go | 8 +++--- xmlDrawing.go | 1 + 21 files changed, 165 insertions(+), 120 deletions(-) diff --git a/calc.go b/calc.go index a03520b..eb6ff75 100644 --- a/calc.go +++ b/calc.go @@ -6026,6 +6026,39 @@ func (fn *formulaFuncs) DATE(argsList *list.List) formulaArg { return newStringFormulaArg(timeFromExcelTime(daysBetween(excelMinTime1900.Unix(), d)+1, false).String()) } +// calcDateDif is an implementation of the formula function DATEDIF, +// calculation difference between two dates. +func calcDateDif(unit string, diff float64, seq []int, startArg, endArg formulaArg) float64 { + ey, sy, em, sm, ed, sd := seq[0], seq[1], seq[2], seq[3], seq[4], seq[5] + switch unit { + case "d": + diff = endArg.Number - startArg.Number + case "md": + smMD := em + if ed < sd { + smMD-- + } + diff = endArg.Number - daysBetween(excelMinTime1900.Unix(), makeDate(ey, time.Month(smMD), sd)) - 1 + case "ym": + diff = float64(em - sm) + if ed < sd { + diff-- + } + if diff < 0 { + diff += 12 + } + case "yd": + syYD := sy + if em < sm || (em == sm && ed < sd) { + syYD++ + } + s := daysBetween(excelMinTime1900.Unix(), makeDate(syYD, time.Month(em), ed)) + e := daysBetween(excelMinTime1900.Unix(), makeDate(sy, time.Month(sm), sd)) + diff = s - e + } + return diff +} + // DATEDIF function calculates the number of days, months, or years between // two dates. The syntax of the function is: // @@ -6051,8 +6084,6 @@ func (fn *formulaFuncs) DATEDIF(argsList *list.List) formulaArg { ey, emm, ed := endDate.Date() sm, em, diff := int(smm), int(emm), 0.0 switch unit { - case "d": - return newNumberFormulaArg(endArg.Number - startArg.Number) case "y": diff = float64(ey - sy) if em < sm || (em == sm && ed < sd) { @@ -6069,28 +6100,8 @@ func (fn *formulaFuncs) DATEDIF(argsList *list.List) formulaArg { mdiff += 12 } diff = float64(ydiff*12 + mdiff) - case "md": - smMD := em - if ed < sd { - smMD-- - } - diff = endArg.Number - daysBetween(excelMinTime1900.Unix(), makeDate(ey, time.Month(smMD), sd)) - 1 - case "ym": - diff = float64(em - sm) - if ed < sd { - diff-- - } - if diff < 0 { - diff += 12 - } - case "yd": - syYD := sy - if em < sm || (em == sm && ed < sd) { - syYD++ - } - s := daysBetween(excelMinTime1900.Unix(), makeDate(syYD, time.Month(em), ed)) - e := daysBetween(excelMinTime1900.Unix(), makeDate(sy, time.Month(sm), sd)) - diff = s - e + case "d", "md", "ym", "yd": + diff = calcDateDif(unit, diff, []int{ey, sy, em, sm, ed, sd}, startArg, endArg) default: return newErrorFormulaArg(formulaErrorVALUE, "DATEDIF has invalid unit") } diff --git a/comment_test.go b/comment_test.go index f1b60dc..fb36d29 100644 --- a/comment_test.go +++ b/comment_test.go @@ -3,9 +3,11 @@ // the LICENSE file. // // Package excelize providing a set of functions that allow you to write to -// and read from XLSX files. Support reads and writes XLSX file generated by -// Microsoft Excel™ 2007 and later. Support save file without losing original -// charts of XLSX. This library needs Go version 1.15 or later. +// and read from XLSX / XLSM / XLTM files. Supports reading and writing +// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports +// complex components by high compatibility, and provided streaming API for +// generating or reading data from a worksheet with huge amounts of data. This +// library needs Go version 1.15 or later. package excelize diff --git a/crypt.go b/crypt.go index a0096c9..24ac7ec 100644 --- a/crypt.go +++ b/crypt.go @@ -3,9 +3,11 @@ // the LICENSE file. // // Package excelize providing a set of functions that allow you to write to -// and read from XLSX files. Support reads and writes XLSX file generated by -// Microsoft Excel™ 2007 and later. Support save file without losing original -// charts of XLSX. This library needs Go version 1.15 or later. +// and read from XLSX / XLSM / XLTM files. Supports reading and writing +// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports +// complex components by high compatibility, and provided streaming API for +// generating or reading data from a worksheet with huge amounts of data. This +// library needs Go version 1.15 or later. package excelize @@ -15,6 +17,7 @@ import ( "crypto/cipher" "crypto/hmac" "crypto/md5" + "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/sha512" @@ -22,7 +25,6 @@ import ( "encoding/binary" "encoding/xml" "hash" - "math/rand" "reflect" "strings" diff --git a/crypt_test.go b/crypt_test.go index 6a882e5..68ff5b8 100644 --- a/crypt_test.go +++ b/crypt_test.go @@ -3,9 +3,11 @@ // the LICENSE file. // // Package excelize providing a set of functions that allow you to write to -// and read from XLSX files. Support reads and writes XLSX file generated by -// Microsoft Excel™ 2007 and later. Support save file without losing original -// charts of XLSX. This library needs Go version 1.15 or later. +// and read from XLSX / XLSM / XLTM files. Supports reading and writing +// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports +// complex components by high compatibility, and provided streaming API for +// generating or reading data from a worksheet with huge amounts of data. This +// library needs Go version 1.15 or later. package excelize diff --git a/datavalidation_test.go b/datavalidation_test.go index f0afe5f..0cb5929 100644 --- a/datavalidation_test.go +++ b/datavalidation_test.go @@ -3,9 +3,11 @@ // the LICENSE file. // // Package excelize providing a set of functions that allow you to write to -// and read from XLSX files. Support reads and writes XLSX file generated by -// Microsoft Excel™ 2007 and later. Support save file without losing original -// charts of XLSX. This library needs Go version 1.15 or later. +// and read from XLSX / XLSM / XLTM files. Supports reading and writing +// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports +// complex components by high compatibility, and provided streaming API for +// generating or reading data from a worksheet with huge amounts of data. This +// library needs Go version 1.15 or later. package excelize diff --git a/date_test.go b/date_test.go index 38898b0..2addc4a 100644 --- a/date_test.go +++ b/date_test.go @@ -85,5 +85,5 @@ func TestExcelDateToTime(t *testing.T) { } // Check error case _, err := ExcelDateToTime(-1, false) - assert.EqualError(t, err, "invalid date value -1.000000, negative values are not supported supported") + assert.EqualError(t, err, "invalid date value -1.000000, negative values are not supported") } diff --git a/errors.go b/errors.go index 0edb697..aee4420 100644 --- a/errors.go +++ b/errors.go @@ -16,26 +16,36 @@ import ( "fmt" ) +// newInvalidColumnNameError defined the error message on receiving the invalid column name. func newInvalidColumnNameError(col string) error { return fmt.Errorf("invalid column name %q", col) } +// newInvalidRowNumberError defined the error message on receiving the invalid row number. func newInvalidRowNumberError(row int) error { return fmt.Errorf("invalid row number %d", row) } +// newInvalidCellNameError defined the error message on receiving the invalid cell name. func newInvalidCellNameError(cell string) error { return fmt.Errorf("invalid cell name %q", cell) } +// newInvalidExcelDateError defined the error message on receiving the data with negative values. func newInvalidExcelDateError(dateValue float64) error { - return fmt.Errorf("invalid date value %f, negative values are not supported supported", dateValue) + return fmt.Errorf("invalid date value %f, negative values are not supported", dateValue) } +// newUnsupportChartType defined the error message on receiving the chart type are unsupported. func newUnsupportChartType(chartType string) error { return fmt.Errorf("unsupported chart type %s", chartType) } +// newUnzipSizeLimitError defined the error message on unzip size exceeds the limit. +func newUnzipSizeLimitError(unzipSizeLimit int64) error { + return fmt.Errorf("unzip size exceeds the %d bytes limit", unzipSizeLimit) +} + var ( // ErrStreamSetColWidth defined the error message on set column width in // stream writing mode. diff --git a/errors_test.go b/errors_test.go index 207e80a..971802f 100644 --- a/errors_test.go +++ b/errors_test.go @@ -21,5 +21,5 @@ func TestNewInvalidCellNameError(t *testing.T) { } func TestNewInvalidExcelDateError(t *testing.T) { - assert.EqualError(t, newInvalidExcelDateError(-1), "invalid date value -1.000000, negative values are not supported supported") + assert.EqualError(t, newInvalidExcelDateError(-1), "invalid date value -1.000000, negative values are not supported") } diff --git a/excelize.go b/excelize.go index 0091c82..fafa57f 100644 --- a/excelize.go +++ b/excelize.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" "path" + "path/filepath" "strconv" "strings" "sync" @@ -59,21 +60,27 @@ type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, e // Options define the options for open spreadsheet. type Options struct { - Password string + Password string + UnzipSizeLimit int64 } -// OpenFile take the name of an spreadsheet file and returns a populated spreadsheet file struct -// for it. For example, open spreadsheet with password protection: +// OpenFile take the name of an spreadsheet file and returns a populated +// spreadsheet file struct for it. For example, open spreadsheet with +// password protection: // // f, err := excelize.OpenFile("Book1.xlsx", excelize.Options{Password: "password"}) // if err != nil { // return // } // -// Note that the excelize just support decrypt and not support encrypt currently, the spreadsheet -// saved by Save and SaveAs will be without password unprotected. +// Note that the excelize just support decrypt and not support encrypt +// currently, the spreadsheet saved by Save and SaveAs will be without +// password unprotected. +// +// UnzipSizeLimit specified the unzip size limit in bytes on open the +// spreadsheet, the default size limit is 16GB. func OpenFile(filename string, opt ...Options) (*File, error) { - file, err := os.Open(filename) + file, err := os.Open(filepath.Clean(filename)) if err != nil { return nil, err } @@ -89,6 +96,7 @@ func OpenFile(filename string, opt ...Options) (*File, error) { // newFile is object builder func newFile() *File { return &File{ + options: &Options{UnzipSizeLimit: UnzipSizeLimit}, xmlAttr: make(map[string][]xml.Attr), checked: make(map[string]bool), sheetMap: make(map[string]string), @@ -111,10 +119,13 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) { return nil, err } f := newFile() - if bytes.Contains(b, oleIdentifier) && len(opt) > 0 { - for _, o := range opt { - f.options = &o + for i := range opt { + f.options = &opt[i] + if f.options.UnzipSizeLimit == 0 { + f.options.UnzipSizeLimit = UnzipSizeLimit } + } + if bytes.Contains(b, oleIdentifier) { b, err = Decrypt(b, f.options) if err != nil { return nil, fmt.Errorf("decrypted file failed") @@ -124,8 +135,7 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) { if err != nil { return nil, err } - - file, sheetCount, err := ReadZipReader(zr) + file, sheetCount, err := ReadZipReader(zr, f.options) if err != nil { return nil, err } @@ -316,18 +326,18 @@ func (f *File) UpdateLinkedValue() error { // recalculate formulas wb.CalcPr = nil for _, name := range f.GetSheetList() { - xlsx, err := f.workSheetReader(name) + ws, err := f.workSheetReader(name) if err != nil { if err.Error() == fmt.Sprintf("sheet %s is chart sheet", trimSheetName(name)) { continue } return err } - for indexR := range xlsx.SheetData.Row { - for indexC, col := range xlsx.SheetData.Row[indexR].C { + for indexR := range ws.SheetData.Row { + for indexC, col := range ws.SheetData.Row[indexR].C { if col.F != nil && col.V != "" { - xlsx.SheetData.Row[indexR].C[indexC].V = "" - xlsx.SheetData.Row[indexR].C[indexC].T = "" + ws.SheetData.Row[indexR].C[indexC].V = "" + ws.SheetData.Row[indexR].C[indexC].T = "" } } } @@ -381,7 +391,7 @@ func (f *File) AddVBAProject(bin string) error { Type: SourceRelationshipVBAProject, }) } - file, _ := ioutil.ReadFile(bin) + file, _ := ioutil.ReadFile(filepath.Clean(bin)) f.Pkg.Store("xl/vbaProject.bin", file) return err } diff --git a/excelize_test.go b/excelize_test.go index cc3a1b2..918279b 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -184,13 +184,9 @@ func TestSaveFile(t *testing.T) { func TestSaveAsWrongPath(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) - if assert.NoError(t, err) { - // Test write file to not exist directory. - err = f.SaveAs("") - if assert.Error(t, err) { - assert.True(t, os.IsNotExist(err), "Error: %v: Expected os.IsNotExists(err) == true", err) - } - } + assert.NoError(t, err) + // Test write file to not exist directory. + assert.EqualError(t, f.SaveAs(""), "open .: is a directory") } func TestCharsetTranscoder(t *testing.T) { @@ -204,6 +200,10 @@ func TestOpenReader(t *testing.T) { _, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password"}) assert.EqualError(t, err, "decrypted file failed") + // Test open spreadsheet with unzip size limit. + _, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipSizeLimit: 100}) + assert.EqualError(t, err, newUnzipSizeLimitError(100).Error()) + // Test open password protected spreadsheet created by Microsoft Office Excel 2010. f, err := OpenFile(filepath.Join("test", "encryptSHA1.xlsx"), Options{Password: "password"}) assert.NoError(t, err) @@ -1226,6 +1226,7 @@ func TestWorkSheetReader(t *testing.T) { f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset) _, err := f.workSheetReader("Sheet1") assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8") + assert.EqualError(t, f.UpdateLinkedValue(), "xml decode error: XML syntax error on line 1: invalid UTF-8") // Test on no checked worksheet. f = NewFile() diff --git a/file.go b/file.go index abb0305..bfb6abf 100644 --- a/file.go +++ b/file.go @@ -17,6 +17,7 @@ import ( "fmt" "io" "os" + "path/filepath" "sync" ) @@ -69,14 +70,14 @@ func (f *File) SaveAs(name string, opt ...Options) error { return ErrMaxFileNameLength } f.Path = name - file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666) + file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600) if err != nil { return err } defer file.Close() f.options = nil - for _, o := range opt { - f.options = &o + for i := range opt { + f.options = &opt[i] } return f.Write(file) } @@ -102,7 +103,8 @@ func (f *File) WriteTo(w io.Writer) (int64, error) { return 0, nil } -// WriteToBuffer provides a function to get bytes.Buffer from the saved file. And it allocate space in memory. Be careful when the file size is large. +// WriteToBuffer provides a function to get bytes.Buffer from the saved file, +// and it allocates space in memory. Be careful when the file size is large. func (f *File) WriteToBuffer() (*bytes.Buffer, error) { buf := new(bytes.Buffer) zw := zip.NewWriter(buf) @@ -130,7 +132,7 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) { func (f *File) writeDirectToWriter(w io.Writer) error { zw := zip.NewWriter(w) if err := f.writeToZip(zw); err != nil { - zw.Close() + _ = zw.Close() return err } return zw.Close() @@ -157,14 +159,14 @@ func (f *File) writeToZip(zw *zip.Writer) error { var from io.Reader from, err = stream.rawData.Reader() if err != nil { - stream.rawData.Close() + _ = stream.rawData.Close() return err } _, err = io.Copy(fi, from) if err != nil { return err } - stream.rawData.Close() + _ = stream.rawData.Close() } var err error f.Pkg.Range(func(path, content interface{}) bool { diff --git a/file_test.go b/file_test.go index d86ce53..956ff92 100644 --- a/file_test.go +++ b/file_test.go @@ -37,9 +37,7 @@ func BenchmarkWrite(b *testing.B) { func TestWriteTo(t *testing.T) { // Test WriteToBuffer err { - f := File{} - buf := bytes.Buffer{} - f.Pkg = sync.Map{} + f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{} f.Pkg.Store("/d/", []byte("s")) _, err := f.WriteTo(bufio.NewWriter(&buf)) assert.EqualError(t, err, "zip: write to directory") @@ -47,9 +45,7 @@ func TestWriteTo(t *testing.T) { } // Test file path overflow { - f := File{} - buf := bytes.Buffer{} - f.Pkg = sync.Map{} + f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{} const maxUint16 = 1<<16 - 1 f.Pkg.Store(strings.Repeat("s", maxUint16+1), nil) _, err := f.WriteTo(bufio.NewWriter(&buf)) @@ -57,9 +53,7 @@ func TestWriteTo(t *testing.T) { } // Test StreamsWriter err { - f := File{} - buf := bytes.Buffer{} - f.Pkg = sync.Map{} + f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{} f.Pkg.Store("s", nil) f.streams = make(map[string]*StreamWriter) file, _ := os.Open("123") diff --git a/lib.go b/lib.go index 912f738..712576d 100644 --- a/lib.go +++ b/lib.go @@ -26,15 +26,22 @@ import ( // ReadZipReader can be used to read the spreadsheet in memory without touching the // filesystem. -func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) { - var err error - var docPart = map[string]string{ - "[content_types].xml": "[Content_Types].xml", - "xl/sharedstrings.xml": "xl/sharedStrings.xml", - } - fileList := make(map[string][]byte, len(r.File)) - worksheets := 0 +func ReadZipReader(r *zip.Reader, o *Options) (map[string][]byte, int, error) { + var ( + err error + docPart = map[string]string{ + "[content_types].xml": "[Content_Types].xml", + "xl/sharedstrings.xml": "xl/sharedStrings.xml", + } + fileList = make(map[string][]byte, len(r.File)) + worksheets int + unzipSize int64 + ) for _, v := range r.File { + unzipSize += v.FileInfo().Size() + if unzipSize > o.UnzipSizeLimit { + return fileList, worksheets, newUnzipSizeLimitError(o.UnzipSizeLimit) + } fileName := strings.Replace(v.Name, "\\", "/", -1) if partName, ok := docPart[strings.ToLower(fileName)]; ok { fileName = partName @@ -61,7 +68,7 @@ func (f *File) readXML(name string) []byte { } // saveFileList provides a function to update given file content in file list -// of XLSX. +// of spreadsheet. func (f *File) saveFileList(name string, content []byte) { f.Pkg.Store(name, append([]byte(XMLHeader), content...)) } @@ -75,8 +82,7 @@ func readFile(file *zip.File) ([]byte, error) { dat := make([]byte, 0, file.FileInfo().Size()) buff := bytes.NewBuffer(dat) _, _ = io.Copy(buff, rc) - rc.Close() - return buff.Bytes(), nil + return buff.Bytes(), rc.Close() } // SplitCellName splits cell name to column name and row number. diff --git a/merge_test.go b/merge_test.go index a370126..02d92fb 100644 --- a/merge_test.go +++ b/merge_test.go @@ -148,16 +148,16 @@ func TestUnmergeCell(t *testing.T) { } sheet1 := f.GetSheetName(0) - xlsx, err := f.workSheetReader(sheet1) + sheet, err := f.workSheetReader(sheet1) assert.NoError(t, err) - mergeCellNum := len(xlsx.MergeCells.Cells) + mergeCellNum := len(sheet.MergeCells.Cells) assert.EqualError(t, f.UnmergeCell("Sheet1", "A", "A"), `cannot convert cell "A" to coordinates: invalid cell name "A"`) // unmerge the mergecell that contains A1 assert.NoError(t, f.UnmergeCell(sheet1, "A1", "A1")) - if len(xlsx.MergeCells.Cells) != mergeCellNum-1 { + if len(sheet.MergeCells.Cells) != mergeCellNum-1 { t.FailNow() } diff --git a/picture.go b/picture.go index c37899e..e3601dd 100644 --- a/picture.go +++ b/picture.go @@ -94,7 +94,7 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error { if !ok { return ErrImgExt } - file, _ := ioutil.ReadFile(picture) + file, _ := ioutil.ReadFile(filepath.Clean(picture)) _, name := filepath.Split(picture) return f.AddPictureFromBytes(sheet, cell, format, name, ext, file) } @@ -199,8 +199,8 @@ func (f *File) deleteSheetRelationships(sheet, rID string) { // addSheetLegacyDrawing provides a function to add legacy drawing element to // xl/worksheets/sheet%d.xml by given worksheet name and relationship index. func (f *File) addSheetLegacyDrawing(sheet string, rID int) { - xlsx, _ := f.workSheetReader(sheet) - xlsx.LegacyDrawing = &xlsxLegacyDrawing{ + ws, _ := f.workSheetReader(sheet) + ws.LegacyDrawing = &xlsxLegacyDrawing{ RID: "rId" + strconv.Itoa(rID), } } @@ -208,8 +208,8 @@ func (f *File) addSheetLegacyDrawing(sheet string, rID int) { // addSheetDrawing provides a function to add drawing element to // xl/worksheets/sheet%d.xml by given worksheet name and relationship index. func (f *File) addSheetDrawing(sheet string, rID int) { - xlsx, _ := f.workSheetReader(sheet) - xlsx.Drawing = &xlsxDrawing{ + ws, _ := f.workSheetReader(sheet) + ws.Drawing = &xlsxDrawing{ RID: "rId" + strconv.Itoa(rID), } } @@ -217,8 +217,8 @@ func (f *File) addSheetDrawing(sheet string, rID int) { // addSheetPicture provides a function to add picture element to // xl/worksheets/sheet%d.xml by given worksheet name and relationship index. func (f *File) addSheetPicture(sheet string, rID int) { - xlsx, _ := f.workSheetReader(sheet) - xlsx.Picture = &xlsxPicture{ + ws, _ := f.workSheetReader(sheet) + ws.Picture = &xlsxPicture{ RID: "rId" + strconv.Itoa(rID), } } diff --git a/picture_test.go b/picture_test.go index 913ed3d..3e12f5f 100644 --- a/picture_test.go +++ b/picture_test.go @@ -71,24 +71,24 @@ func TestAddPicture(t *testing.T) { } func TestAddPictureErrors(t *testing.T) { - xlsx, err := OpenFile(filepath.Join("test", "Book1.xlsx")) + f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) assert.NoError(t, err) // Test add picture to worksheet with invalid file path. - err = xlsx.AddPicture("Sheet1", "G21", filepath.Join("test", "not_exists_dir", "not_exists.icon"), "") + err = f.AddPicture("Sheet1", "G21", filepath.Join("test", "not_exists_dir", "not_exists.icon"), "") if assert.Error(t, err) { assert.True(t, os.IsNotExist(err), "Expected os.IsNotExist(err) == true") } // Test add picture to worksheet with unsupported file type. - err = xlsx.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), "") + err = f.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), "") assert.EqualError(t, err, ErrImgExt.Error()) - err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1)) + err = f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1)) assert.EqualError(t, err, ErrImgExt.Error()) // Test add picture to worksheet with invalid file data. - err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1)) + err = f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1)) assert.EqualError(t, err, "image: unknown format") } diff --git a/sheet.go b/sheet.go index 1c4b355..7e15bbe 100644 --- a/sheet.go +++ b/sheet.go @@ -480,7 +480,7 @@ func (f *File) SetSheetBackground(sheet, picture string) error { if !ok { return ErrImgExt } - file, _ := ioutil.ReadFile(picture) + file, _ := ioutil.ReadFile(filepath.Clean(picture)) name := f.addMedia(file, ext) sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "") @@ -655,13 +655,13 @@ func (f *File) SetSheetVisible(name string, visible bool) error { } } for k, v := range content.Sheets.Sheet { - xlsx, err := f.workSheetReader(v.Name) + ws, err := f.workSheetReader(v.Name) if err != nil { return err } tabSelected := false - if len(xlsx.SheetViews.SheetView) > 0 { - tabSelected = xlsx.SheetViews.SheetView[0].TabSelected + if len(ws.SheetViews.SheetView) > 0 { + tabSelected = ws.SheetViews.SheetView[0].TabSelected } if v.Name == name && count > 1 && !tabSelected { content.Sheets.Sheet[k].State = "hidden" diff --git a/sheetpr.go b/sheetpr.go index 8bc4bfe..6f46040 100644 --- a/sheetpr.go +++ b/sheetpr.go @@ -182,14 +182,14 @@ func (o *AutoPageBreaks) getSheetPrOption(pr *xlsxSheetPr) { // AutoPageBreaks(bool) // OutlineSummaryBelow(bool) func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error { - sheet, err := f.workSheetReader(name) + ws, err := f.workSheetReader(name) if err != nil { return err } - pr := sheet.SheetPr + pr := ws.SheetPr if pr == nil { pr = new(xlsxSheetPr) - sheet.SheetPr = pr + ws.SheetPr = pr } for _, opt := range opts { @@ -208,11 +208,11 @@ func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error { // AutoPageBreaks(bool) // OutlineSummaryBelow(bool) func (f *File) GetSheetPrOptions(name string, opts ...SheetPrOptionPtr) error { - sheet, err := f.workSheetReader(name) + ws, err := f.workSheetReader(name) if err != nil { return err } - pr := sheet.SheetPr + pr := ws.SheetPr for _, opt := range opts { opt.getSheetPrOption(pr) diff --git a/stream.go b/stream.go index 9939016..125b58c 100644 --- a/stream.go +++ b/stream.go @@ -346,8 +346,8 @@ func (sw *StreamWriter) SetRow(axis string, values []interface{}, opts ...RowOpt // marshalRowAttrs prepare attributes of the row by given options. func marshalRowAttrs(opts ...RowOpts) (attrs string, err error) { var opt *RowOpts - for _, o := range opts { - opt = &o + for i := range opts { + opt = &opts[i] } if opt == nil { return diff --git a/xmlChartSheet.go b/xmlChartSheet.go index 4ef2ded..fcc3443 100644 --- a/xmlChartSheet.go +++ b/xmlChartSheet.go @@ -5,9 +5,11 @@ // struct code generated by github.com/xuri/xgen // // Package excelize providing a set of functions that allow you to write to -// and read from XLSX files. Support reads and writes XLSX file generated by -// Microsoft Excel™ 2007 and later. Support save file without losing original -// charts of XLSX. This library needs Go version 1.15 or later. +// and read from XLSX / XLSM / XLTM files. Supports reading and writing +// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports +// complex components by high compatibility, and provided streaming API for +// generating or reading data from a worksheet with huge amounts of data. This +// library needs Go version 1.15 or later. package excelize diff --git a/xmlDrawing.go b/xmlDrawing.go index 4e35fcf..b49ae9d 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -94,6 +94,7 @@ const ( // Excel specifications and limits const ( + UnzipSizeLimit = 1000 << 24 StreamChunkSize = 1 << 24 MaxFontFamilyLength = 31 MaxFontSize = 409 -- GitLab