From 3de9282fba15dff726e233de4f4b957eddcfc1e2 Mon Sep 17 00:00:00 2001
From: Dhole <dhole@riseup.net>
Date: Tue, 18 May 2021 23:48:48 +0200
Subject: [PATCH] Sanitize float values in EXIF fields

Read exposure as rational in EXIF internal parser
---
 api/scanner/exif/exif_parser_external.go |  34 +++++++++++++++++++++++
 api/scanner/exif/exif_parser_internal.go |   7 +++--
 api/scanner/exif/exif_test.go            |   7 +++++
 api/scanner/exif/test_data/bad-exif.jpg  | Bin 0 -> 10563 bytes
 4 files changed, 45 insertions(+), 3 deletions(-)
 create mode 100644 api/scanner/exif/test_data/bad-exif.jpg

diff --git a/api/scanner/exif/exif_parser_external.go b/api/scanner/exif/exif_parser_external.go
index eb6d4d3..29940fe 100644
--- a/api/scanner/exif/exif_parser_external.go
+++ b/api/scanner/exif/exif_parser_external.go
@@ -2,6 +2,7 @@ package exif
 
 import (
 	"log"
+	"math"
 	"time"
 
 	"github.com/barasher/go-exiftool"
@@ -28,6 +29,38 @@ func newExiftoolParser() (*externalExifParser, error) {
 	}, nil
 }
 
+// isFloatReal returns true when the float value represents a real number
+// (different than +Inf, -Inf or NaN)
+func isFloatReal(v float64) bool {
+	if math.IsInf(v, 1) {
+		return false
+	} else if math.IsInf(v, -1) {
+		return false
+	} else if math.IsNaN(v) {
+		return false
+	}
+	return true
+}
+
+// sanitizeEXIF removes any EXIF float64 field that is not a real number (+Inf,
+// -Inf or Nan)
+func sanitizeEXIF(exif *models.MediaEXIF) {
+	if exif.Exposure != nil && !isFloatReal(*exif.Exposure) {
+		exif.Exposure = nil
+	}
+	if exif.Aperture != nil && !isFloatReal(*exif.Aperture) {
+		exif.Aperture = nil
+	}
+	if exif.FocalLength != nil && !isFloatReal(*exif.FocalLength) {
+		exif.FocalLength = nil
+	}
+	if (exif.GPSLatitude != nil && !isFloatReal(*exif.GPSLatitude)) ||
+		(exif.GPSLongitude != nil && !isFloatReal(*exif.GPSLongitude)) {
+		exif.GPSLatitude = nil
+		exif.GPSLongitude = nil
+	}
+}
+
 func (p *externalExifParser) ParseExif(media_path string) (returnExif *models.MediaEXIF, returnErr error) {
 	// ExifTool - No print conversion mode
 	if p.et == nil {
@@ -148,5 +181,6 @@ func (p *externalExifParser) ParseExif(media_path string) (returnExif *models.Me
 	}
 
 	returnExif = &newExif
+	sanitizeEXIF(returnExif)
 	return
 }
diff --git a/api/scanner/exif/exif_parser_internal.go b/api/scanner/exif/exif_parser_internal.go
index 5897e6e..1cb8a42 100644
--- a/api/scanner/exif/exif_parser_internal.go
+++ b/api/scanner/exif/exif_parser_internal.go
@@ -62,11 +62,12 @@ func (p *internalExifParser) ParseExif(media_path string) (returnExif *models.Me
 		newExif.DateShot = &date_utc
 	}
 
-	exposure, err := exifTags.Get(exif.ExposureTime)
+	exposureTag, err := exifTags.Get(exif.ExposureTime)
 	if err == nil {
-		exposureFloat, err := exposure.Float(0)
+		exposureRat, err := exposureTag.Rat(0)
 		if err == nil {
-			newExif.Exposure = &exposureFloat
+			exposure, _ := exposureRat.Float64()
+			newExif.Exposure = &exposure
 		}
 	}
 
diff --git a/api/scanner/exif/exif_test.go b/api/scanner/exif/exif_test.go
index 538d720..e586c2c 100644
--- a/api/scanner/exif/exif_test.go
+++ b/api/scanner/exif/exif_test.go
@@ -51,6 +51,7 @@ func TestExifParsers(t *testing.T) {
 				assert.EqualValues(t, *exif.Camera, "Canon EOS 600D")
 				assert.EqualValues(t, *exif.Maker, "Canon")
 				assert.WithinDuration(t, *exif.DateShot, time.Unix(1336318784, 0).UTC(), time.Minute)
+				assert.InDelta(t, *exif.Exposure, 1.0/4000.0, 0.0001)
 				assert.EqualValues(t, *exif.Aperture, 6.3)
 				assert.EqualValues(t, *exif.Iso, 800)
 				assert.EqualValues(t, *exif.FocalLength, 300)
@@ -66,6 +67,12 @@ func TestExifParsers(t *testing.T) {
 				assert.Nil(t, exif)
 			},
 		},
+		{
+			path: "./test_data/bad-exif.jpg",
+			assert: func(t *testing.T, exif *models.MediaEXIF) {
+				assert.Nil(t, exif.Exposure)
+			},
+		},
 	}
 
 	for _, p := range parsers {
diff --git a/api/scanner/exif/test_data/bad-exif.jpg b/api/scanner/exif/test_data/bad-exif.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..62e5f900f5846292c3ad60cccc5309d24e43d426
GIT binary patch
