Skip to content

Commit 2e86f91

Browse files
committed
Add LimitNumTags and LimitTagSize
With some senbible defaults. This is a safeguard against abnormally large image metadata. Note that there's already a Timeout option that can be set if you want to have the processing fail if it takes too long.
1 parent 454e871 commit 2e86f91

File tree

6 files changed

+49
-3
lines changed

6 files changed

+49
-3
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[{
2+
"SourceFile": "../testdata/images/largeexif.png",
3+
"ExifTool": {
4+
"ExifToolVersion": 12.76,
5+
"Warning": "Processing TIFF-like data after unknown 16-byte header"
6+
},
7+
"File": {
8+
"FileName": "largeexif.png",
9+
"Directory": "../testdata/images",
10+
"FileSize": 1310710,
11+
"FilePermissions": 100644,
12+
"ExifByteOrder": "MM"
13+
}
14+
}]

‎imagemeta.go‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,28 @@ func Decode(opts Options) (err error) {
113113
}
114114
}
115115

116+
const (
117+
defaultLimitNumTags = 5000
118+
defaultLimitTagSize = 10000
119+
)
120+
121+
if opts.LimitNumTags == 0 {
122+
opts.LimitNumTags = defaultLimitNumTags
123+
}
124+
if opts.LimitTagSize == 0 {
125+
opts.LimitTagSize = defaultLimitTagSize
126+
}
127+
128+
var tagCount uint32
129+
shouldHandleTag := opts.ShouldHandleTag
130+
opts.ShouldHandleTag = func(ti TagInfo) bool {
131+
tagCount++
132+
if tagCount > opts.LimitNumTags {
133+
panic(ErrStopWalking)
134+
}
135+
return shouldHandleTag(ti)
136+
}
137+
116138
if opts.HandleTag == nil {
117139
opts.HandleTag = func(TagInfo) error { return nil }
118140
}
@@ -246,6 +268,16 @@ type Options struct {
246268
// Mostly useful for testing.
247269
// If set to 0, the decoder will not time out.
248270
Timeout time.Duration
271+
272+
// LimitNumTags is the maximum number of tags to read.
273+
// Default value is 5000.
274+
LimitNumTags uint32
275+
276+
// LimitTagSize is the maximum size in bytes of a tag value to read.
277+
// Tag values larger than this will be skipped without notice.
278+
// Note that this limit is not relevant for the XMP source.
279+
// Default value is 10000.
280+
LimitTagSize uint32
249281
}
250282

251283
// TagInfo contains information about a tag.

‎imagemeta_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"errors"
99
"fmt"
1010
"io"
11+
"maps"
1112
"math"
1213
"math/rand"
1314
"os"
@@ -23,7 +24,6 @@ import (
2324

2425
qt "github.com/frankban/quicktest"
2526
"github.com/google/go-cmp/cmp"
26-
"maps"
2727
)
2828

2929
func TestDecodeAllImageFormats(t *testing.T) {

‎metadecoder_exif.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ func (e *metaDecoderEXIF) decodeTag(namespace string) error {
383383
}
384384

385385
// Below is EXIF
386-
if !e.opts.Sources.Has(EXIF) {
386+
if !e.opts.Sources.Has(EXIF) || valLen > e.opts.LimitTagSize {
387387
e.skip(4)
388388
return nil
389389
}

‎metadecoder_iptc.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ func (e *metaDecoderIPTC) decodeRecord(stringSlices map[TagInfo][]string) error
273273
Namespace: recordDef.RecordName,
274274
}
275275

276-
if !e.opts.ShouldHandleTag(ti) {
276+
if recordSize > uint16(e.opts.LimitTagSize) || !e.opts.ShouldHandleTag(ti) {
277277
e.skip(int64(recordSize))
278278
return nil
279279
}

‎testdata/images/largeexif.png‎

1.25 MB
Loading

0 commit comments

Comments
 (0)