Skip to content

Commit f98df08

Browse files
authored
This fixes GHSA-wcg2-648h-mhxq, fix panic on negative shared string index (#2366)
- Update unit test and docs
1 parent c904c92 commit f98df08

7 files changed

Lines changed: 71 additions & 27 deletions

File tree

cell.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,11 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
619619
if c.V != "" {
620620
xlsxSI, _ := strconv.Atoi(strings.TrimSpace(c.V))
621621
if _, ok := f.tempFiles.Load(defaultXMLPathSharedStrings); ok {
622-
return f.formattedValue(&xlsxC{S: c.S, V: f.getFromStringItem(xlsxSI)}, raw, CellTypeSharedString)
622+
val, err := f.getFromStringItem(xlsxSI)
623+
if err != nil {
624+
return "", err
625+
}
626+
return f.formattedValue(&xlsxC{S: c.S, V: val}, raw, CellTypeSharedString)
623627
}
624628
d.mu.Lock()
625629
defer d.mu.Unlock()

cell_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,16 @@ func TestGetValueFrom(t *testing.T) {
574574
value, err = c.getValueFrom(f, &xlsxSST{Count: 1, SI: []xlsxSI{{}, {T: &xlsxT{Val: "s"}}}}, false)
575575
assert.Equal(t, newInvalidSharedStringIndex(-1), err)
576576
assert.Empty(t, value)
577+
578+
f.tempFiles.Store(defaultXMLPathSharedStrings, "")
579+
f.sharedStringTemp, err = os.CreateTemp(os.TempDir(), "excelize-")
580+
assert.NoError(t, err)
581+
c = xlsxC{T: "s", V: "-1"}
582+
value, err = c.getValueFrom(f, sst, false)
583+
assert.Equal(t, newInvalidSharedStringIndex(-1), err)
584+
assert.Empty(t, value)
585+
assert.NoError(t, f.sharedStringTemp.Close())
586+
assert.NoError(t, os.Remove(f.sharedStringTemp.Name()))
577587
}
578588

579589
func TestGetCellFormula(t *testing.T) {
@@ -1199,10 +1209,14 @@ func TestSharedStringsError(t *testing.T) {
11991209
tempFile, ok := f.tempFiles.Load(defaultXMLPathSharedStrings)
12001210
assert.True(t, ok)
12011211
f.tempFiles.Store(defaultXMLPathSharedStrings, "")
1202-
assert.Equal(t, "1", f.getFromStringItem(1))
1212+
value, err := f.getFromStringItem(1)
1213+
assert.Equal(t, newInvalidSharedStringIndex(1), err)
1214+
assert.Empty(t, value)
12031215
// Test get from string item with invalid offset range
12041216
f.sharedStringItem = [][]uint{{0}}
1205-
assert.Equal(t, "0", f.getFromStringItem(0))
1217+
value, err = f.getFromStringItem(0)
1218+
assert.NoError(t, err)
1219+
assert.Equal(t, "0", value)
12061220
// Cleanup undelete temporary files
12071221
assert.NoError(t, os.Remove(tempFile.(string)))
12081222
// Test reload the file error on set cell value and rich text. The error message was different between macOS and Windows
@@ -1227,7 +1241,9 @@ func TestSharedStringsError(t *testing.T) {
12271241
assert.NoError(t, err)
12281242
// Test get cell value from string item with invalid offset
12291243
f.sharedStringItem[1] = []uint{maxUint16 - 1, maxUint16}
1230-
assert.Equal(t, "1", f.getFromStringItem(1))
1244+
value, err := f.getFromStringItem(1)
1245+
assert.NoError(t, err)
1246+
assert.Equal(t, "1", value)
12311247
break
12321248
}
12331249
}

errors.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,11 @@ var (
128128
// length.
129129
ErrPasswordLengthInvalid = errors.New("password length invalid")
130130
// ErrPivotTableShowValuesAsBaseField defined the error message on enable
131-
// this kind of show value as type requires a base field.
132-
ErrPivotTableShowValuesAsBaseField = errors.New("this kind of show value as type requires a base field")
131+
// this kind of "show values as" type requires a base field.
132+
ErrPivotTableShowValuesAsBaseField = errors.New("this kind of show values as type requires a base field")
133133
// ErrPivotTableShowValuesAsBaseItem defined the error message on enable
134-
// this kind of show value as type and base field requires a base item.
135-
ErrPivotTableShowValuesAsBaseItem = errors.New("this kind of show value as type and base field requires a base item")
134+
// this kind of "show values as" type and base field requires a base item.
135+
ErrPivotTableShowValuesAsBaseItem = errors.New("this kind of show values as type and base field requires a base item")
136136
// ErrPivotTableClassicLayout defined the error message on enable
137137
// ClassicLayout and CompactData in the same time.
138138
ErrPivotTableClassicLayout = errors.New("cannot enable ClassicLayout and CompactData in the same time")
@@ -199,8 +199,8 @@ var (
199199
// number format expression.
200200
ErrUnsupportedNumberFormat = errors.New("unsupported number format token")
201201
// ErrUnsupportedPivotTableShowValuesAsType defined the error message on
202-
// receiving the unsupported pivot table show value as type.
203-
ErrUnsupportedPivotTableShowValuesAsType = errors.New("unsupported pivot table show value as type")
202+
// receiving the unsupported pivot table "show values as" type.
203+
ErrUnsupportedPivotTableShowValuesAsType = errors.New("unsupported pivot table show values as type")
204204
// ErrWorkbookFileFormat defined the error message on receive an
205205
// unsupported workbook file format.
206206
ErrWorkbookFileFormat = errors.New("unsupported workbook file format")
@@ -376,7 +376,7 @@ func newPivotTableRangeError(msg string) error {
376376
}
377377

378378
// newPivotTableShowValuesAsBaseFieldError defined the error message on receiving
379-
// the invalid pivot table show value as base field.
379+
// the invalid pivot table "show values as" base field.
380380
func newPivotTableShowValuesAsBaseFieldError(field string) error {
381381
return fmt.Errorf("base field %s does not exist in shared items", field)
382382
}

pivotTable.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const (
8989
PivotTableShowValuesAsIndex
9090
)
9191

92-
// PivotTableShowValuesAs directly maps the show value as settings of the pivot
92+
// PivotTableShowValuesAs directly maps the show values as settings of the pivot
9393
// table.
9494
type PivotTableShowValuesAs struct {
9595
Type PivotTableShowValuesAsType
@@ -896,7 +896,7 @@ func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opts *PivotTable
896896
return err
897897
}
898898

899-
// setPivotTableShowValuesAs provides a method to set show value as for pivot
899+
// setPivotTableShowValuesAs provides a method to set show values as for pivot
900900
// table data field by given pivot table options and data field index.
901901
func (df *xlsxDataField) setPivotTableShowValuesAs(idx int, order []string, opts *PivotTableOptions) error {
902902
showValuesAsType := opts.Data[idx].ShowValuesAs.Type
@@ -1453,7 +1453,7 @@ func (f *File) extractPivotTableFields(pt *xlsxPivotTableDefinition, pc *xlsxPiv
14531453
}
14541454
}
14551455

1456-
// extractPivotTableShowValuesAs provides a function to extract show value as
1456+
// extractPivotTableShowValuesAs provides a function to extract show values as
14571457
// settings for pivot table data field.
14581458
func (f *File) extractPivotTableShowValuesAs(pc *xlsxPivotCacheDefinition, df *xlsxDataField, dataField *PivotTableField) {
14591459
order := pc.getPivotCacheFieldsName()

pivotTable_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func TestPivotTable(t *testing.T) {
100100
ShowColHeaders: true,
101101
ShowLastColumn: true,
102102
}))
103-
// Test get pivot table with show value as with base field and base item
103+
// Test get pivot table with show values as with base field and base item
104104
pivotTables, err = f.GetPivotTables("Sheet1")
105105
assert.NoError(t, err)
106106
assert.Len(t, pivotTables, 4)
@@ -331,7 +331,7 @@ func TestPivotTable(t *testing.T) {
331331
Data: []PivotTableField{{Data: "Revenue"}},
332332
Filter: []PivotTableField{{Data: "Month"}},
333333
}))
334-
// Test with unsupported pivot table data field show value as type
334+
// Test with unsupported pivot table data field show values as type
335335
assert.Equal(t, ErrUnsupportedPivotTableShowValuesAsType, f.AddPivotTable(&PivotTableOptions{
336336
DataRange: "Sheet1!A1:E31",
337337
PivotTableRange: "Sheet1!G2:M34",
@@ -340,7 +340,7 @@ func TestPivotTable(t *testing.T) {
340340
Data: []PivotTableField{{Data: "Revenue", ShowValuesAs: PivotTableShowValuesAs{Type: 15}}},
341341
Filter: []PivotTableField{{Data: "Month"}},
342342
}))
343-
// Test set pivot table show value as type without required base field
343+
// Test set pivot table show values as type without required base field
344344
assert.Equal(t, ErrPivotTableShowValuesAsBaseField, f.AddPivotTable(&PivotTableOptions{
345345
DataRange: "Sheet1!A1:E31",
346346
PivotTableRange: "Sheet1!G2:M34",
@@ -349,7 +349,7 @@ func TestPivotTable(t *testing.T) {
349349
Data: []PivotTableField{{Data: "Revenue", ShowValuesAs: PivotTableShowValuesAs{Type: PivotTableShowValuesAsRunningTotalIn}}},
350350
Filter: []PivotTableField{{Data: "Month"}},
351351
}))
352-
// Test set pivot table show value as type without required base item
352+
// Test set pivot table show values as type without required base item
353353
assert.Equal(t, ErrPivotTableShowValuesAsBaseItem, f.AddPivotTable(&PivotTableOptions{
354354
DataRange: "Sheet1!A1:E31",
355355
PivotTableRange: "Sheet1!G2:M34",
@@ -358,7 +358,7 @@ func TestPivotTable(t *testing.T) {
358358
Data: []PivotTableField{{Data: "Revenue", ShowValuesAs: PivotTableShowValuesAs{Type: PivotTableShowValuesAsPercentOf, BaseField: "Month"}}},
359359
Filter: []PivotTableField{{Data: "Month"}},
360360
}))
361-
// Test with invalid pivot table show value as base field
361+
// Test with invalid pivot table show values as base field
362362
assert.Equal(t, newPivotTableShowValuesAsBaseFieldError("x"), f.AddPivotTable(&PivotTableOptions{
363363
DataRange: "Sheet1!A1:E31",
364364
PivotTableRange: "Sheet1!G2:M34",
@@ -367,7 +367,7 @@ func TestPivotTable(t *testing.T) {
367367
Data: []PivotTableField{{Data: "Revenue", ShowValuesAs: PivotTableShowValuesAs{Type: PivotTableShowValuesAsRunningTotalIn, BaseField: "x"}}},
368368
Filter: []PivotTableField{{Data: "Month"}},
369369
}))
370-
// Test with invalid pivot table show value as base item
370+
// Test with invalid pivot table show values as base item
371371
assert.Equal(t, newPivotTableSelectedItemError("x", "Month"), f.AddPivotTable(&PivotTableOptions{
372372
DataRange: "Sheet1!A1:E31",
373373
PivotTableRange: "Sheet1!G2:M34",

rows.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -353,21 +353,23 @@ func (f *File) Rows(sheet string) (*Rows, error) {
353353
}
354354

355355
// getFromStringItem build shared string item offset list from system temporary
356-
// file at one time, and return value by given to string index.
357-
func (f *File) getFromStringItem(index int) string {
356+
// file at one time, and return value by given to string index. An index outside
357+
// the shared string table returns an error, matching the in-memory path in
358+
// xlsxC.getValueFrom.
359+
func (f *File) getFromStringItem(index int) (string, error) {
358360
if f.sharedStringTemp != nil {
359-
if len(f.sharedStringItem) <= index {
360-
return strconv.Itoa(index)
361+
if index < 0 || len(f.sharedStringItem) <= index {
362+
return "", newInvalidSharedStringIndex(index)
361363
}
362364
offsetRange := f.sharedStringItem[index]
363365
if len(offsetRange) != 2 || offsetRange[0] > offsetRange[1] {
364-
return strconv.Itoa(index)
366+
return strconv.Itoa(index), nil
365367
}
366368
buf := make([]byte, offsetRange[1]-offsetRange[0])
367369
if _, err := f.sharedStringTemp.ReadAt(buf, int64(offsetRange[0])); err != nil {
368-
return strconv.Itoa(index)
370+
return strconv.Itoa(index), nil
369371
}
370-
return string(buf)
372+
return string(buf), nil
371373
}
372374
needClose, decoder, tempFile, err := f.xmlDecoder(defaultXMLPathSharedStrings)
373375
if needClose && err == nil {

rows_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"encoding/xml"
66
"fmt"
7+
"os"
78
"path/filepath"
89
"strconv"
910
"sync"
@@ -255,6 +256,27 @@ func TestColumns(t *testing.T) {
255256
assert.NoError(t, err)
256257
}
257258

259+
func TestGetFromStringItem(t *testing.T) {
260+
f := NewFile()
261+
// Test get shared string item by a negative index on the streaming path,
262+
// which the in-memory path already rejects in xlsxC.getValueFrom
263+
tempFile, err := os.CreateTemp(f.options.TmpDir, "excelize-")
264+
assert.NoError(t, err)
265+
f.sharedStringTemp = tempFile
266+
f.sharedStringItem = [][]uint{{0, 0}}
267+
value, err := f.getFromStringItem(-1)
268+
assert.Equal(t, newInvalidSharedStringIndex(-1), err)
269+
assert.Empty(t, value)
270+
value, err = f.getFromStringItem(1)
271+
assert.Equal(t, newInvalidSharedStringIndex(1), err)
272+
assert.Empty(t, value)
273+
value, err = f.getFromStringItem(0)
274+
assert.NoError(t, err)
275+
assert.Empty(t, value)
276+
assert.NoError(t, tempFile.Close())
277+
assert.NoError(t, os.Remove(tempFile.Name()))
278+
}
279+
258280
func TestSharedStringsReader(t *testing.T) {
259281
f := NewFile()
260282
// Test read shared string with unsupported charset

0 commit comments

Comments
 (0)