Skip to content

Commit

Permalink
don't accept inexact numbers while parsing date values, closes playfr…
Browse files Browse the repository at this point in the history
  • Loading branch information
OlegYch committed Sep 18, 2018
1 parent 4ae6cc3 commit 839a8ea
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 14 deletions.
21 changes: 9 additions & 12 deletions play-json/jvm/src/main/scala/play/api/libs/json/EnvReads.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ trait EnvReads {
def dateReads(pattern: String, corrector: String => String = identity): Reads[java.util.Date] = new Reads[java.util.Date] {

def reads(json: JsValue): JsResult[java.util.Date] = json match {
case JsNumber(d) => JsSuccess(new java.util.Date(d.toLong))
case n: JsNumber => n.validate[Long].map(l => new java.util.Date(l))
case JsString(s) => parseJDate(pattern, corrector(s)) match {
case Some(d) => JsSuccess(d)
case None => JsError(Seq(JsPath ->
Expand Down Expand Up @@ -95,7 +95,7 @@ trait EnvReads {
val WithTz = """^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[^.]+$""".r

def reads(json: JsValue): JsResult[Date] = json match {
case JsNumber(d) => JsSuccess(new Date(d.toLong))
case n: JsNumber => n.validate[Long].map(l => new Date(l))

case JsString(s) => (s match {
case WithMillisAndTz() => millisAndTz -> parseJDate(millisAndTz, s)
Expand Down Expand Up @@ -227,7 +227,7 @@ trait EnvReads {
epoch: Long => B
) extends Reads[B] {
def reads(json: JsValue): JsResult[B] = json match {
case JsNumber(d) => JsSuccess(epoch(d.toLong))
case n: JsNumber => n.validate[Long].map(epoch)
case JsString(s) => p(parsing).parse(corrector(s)) match {
case Some(d) => JsSuccess(d)
case None => JsError(Seq(JsPath ->
Expand Down Expand Up @@ -354,7 +354,7 @@ trait EnvReads {
def localDateReads[T](parsing: T, corrector: String => String = identity)(implicit p: T => TemporalParser[LocalDate]): Reads[LocalDate] =
new Reads[LocalDate] {
def reads(json: JsValue): JsResult[LocalDate] = json match {
case JsNumber(d) => JsSuccess(epoch(d.toLong))
case n: JsNumber => n.validate[Long].map(epoch)
case JsString(s) => p(parsing).parse(corrector(s)) match {
case Some(d) => JsSuccess(d)
case _ => JsError(Seq(JsPath ->
Expand Down Expand Up @@ -422,7 +422,7 @@ trait EnvReads {
def localTimeReads[T](parsing: T, corrector: String => String = identity)(implicit p: T => TemporalParser[LocalTime]): Reads[LocalTime] =
new Reads[LocalTime] {
def reads(json: JsValue): JsResult[LocalTime] = json match {
case JsNumber(d) => JsSuccess(epoch(d.toLong))
case n: JsNumber => n.validate[Long].map(epoch)
case JsString(s) => p(parsing).parse(corrector(s)) match {
case Some(d) => JsSuccess(d)
case _ => JsError(Seq(JsPath ->
Expand Down Expand Up @@ -514,11 +514,8 @@ trait EnvReads {

private def jdurationNumberReads(unit: TemporalUnit) =
Reads[JDuration] {
case JsNumber(n) if !n.ulp.isValidLong =>
JsError("error.invalid.longDuration")

case JsNumber(n) => JsSuccess(JDuration.of(n.toLong, unit))
case _ => JsError("error.expected.lonDuration")
case n: JsNumber => n.validate[Long].map(l => JDuration.of(l, unit))
case _ => JsError("error.expected.longDuration")
}

/**
Expand Down Expand Up @@ -593,7 +590,7 @@ trait EnvReads {
val df = DateTimeFormat.forPattern(pattern)

def reads(json: JsValue): JsResult[DateTime] = json match {
case JsNumber(d) => JsSuccess(new DateTime(d.toLong))
case n: JsNumber => n.validate[Long].map(l => new DateTime(l))
case JsString(s) => parseDate(corrector(s)) match {
case Some(d) => JsSuccess(d)
case _ => JsError(Seq(JsPath() -> Seq(JsonValidationError("error.expected.jodadate.format", pattern))))
Expand Down Expand Up @@ -635,7 +632,7 @@ trait EnvReads {
val df = if (pattern == "") ISODateTimeFormat.localTimeParser else DateTimeFormat.forPattern(pattern)

def reads(json: JsValue): JsResult[LocalTime] = json match {
case JsNumber(n) => JsSuccess(new LocalTime(n.toLong))
case n: JsNumber => n.validate[Long].map(l => new LocalTime(l))
case JsString(s) => parseTime(corrector(s)) match {
case Some(d) => JsSuccess(d)
case None => JsError(Seq(JsPath() -> Seq(JsonValidationError("error.expected.jodatime.format", pattern))))
Expand Down
32 changes: 31 additions & 1 deletion play-json/jvm/src/test/scala/play/api/libs/json/ReadsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ class ReadsSpec extends org.specs2.mutable.Specification {
))
}

"not be read from invalid number" in {
reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike {
case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok
}
}

"not be read from invalid string" in {
reads(JsString("invalid")) aka "read date" must beLike {
case JsError((_, JsonValidationError(
Expand Down Expand Up @@ -224,6 +230,12 @@ class ReadsSpec extends org.specs2.mutable.Specification {
}
}

"not be read from invalid number" in {
reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike {
case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok
}
}

"be successfully read with default implicit" >> {
"from '2011-12-03T10:15:30+01:00' (with TZ offset)" in {
reads(JsString("2011-12-03T10:15:30+01:00")) aka "read date" must_== (
Expand Down Expand Up @@ -299,6 +311,12 @@ class ReadsSpec extends org.specs2.mutable.Specification {
aka("read date") must_== JsSuccess(d)
}

"not be read from invalid number" in {
reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike {
case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok
}
}

"not be read from invalid string" in {
reads(JsString("invalid")) aka "read date" must beLike {
case JsError((_, JsonValidationError(
Expand Down Expand Up @@ -368,6 +386,12 @@ class ReadsSpec extends org.specs2.mutable.Specification {
aka("read time") must_== JsSuccess(d)
}

"not be read from invalid number" in {
reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike {
case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok
}
}

"not be read from invalid string" in {
reads(JsString("invalid")) aka "read time" must beLike {
case JsError((_, JsonValidationError(
Expand Down Expand Up @@ -432,6 +456,12 @@ class ReadsSpec extends org.specs2.mutable.Specification {
aka("read date") must_== JsSuccess(Instant ofEpochMilli 123L)
}

"not be read from invalid number" in {
reads(JsNumber(BigDecimal("1000000000e1000000000"))) aka "read date" must beLike {
case JsError((_, JsonValidationError("error.expected.long" :: Nil) :: Nil) :: Nil) => ok
}
}

"not be read from invalid string" in {
reads(JsString("invalid")) aka "read date" must beLike {
case JsError((_, JsonValidationError(
Expand Down Expand Up @@ -575,7 +605,7 @@ class ReadsSpec extends org.specs2.mutable.Specification {
JsString("1 seconds") -> JsError("error.invalid.duration"),
JsString("foo") -> JsError("error.invalid.duration"),
JsNumber(BigDecimal(1000L)) -> JsSuccess(oneSec),
JsNumber(BigDecimal(1.234D)) -> JsError("error.invalid.longDuration")
JsNumber(BigDecimal(1.234D)) -> JsError("error.expected.long")
)) {
case (input, result) =>
s"be parsed from ${Json stringify input} as $result" in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ class JsonSharedSpec extends WordSpec
"key3" -> js.arr(1, "tutu")
)

js.prettyPrint(jo) mustEqual ("""{
js.prettyPrint(jo).replaceAllLiterally("\r\n", "\n") mustEqual ("""{
"key1" : "toto",
"key2" : {
"key21" : "tata",
Expand Down

0 comments on commit 839a8ea

Please # to comment.