[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#988957: unblock: pydantic/1.7.4-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: Michael Banck <mbanck@debian.org>

Please unblock package pydantic

pydantic (1.7.4-1) unstable; urgency=medium

  * Team upload.
  * New upstream point release.
    - Fixes CVE-2021-29510: Date and datetime parsing could cause an infinite
      loop by passing either 'infinity' or float('inf') (Closes: #988480)
  * Update watch file to version 4 with current uscan(1) recommended regex.

 -- Stefano Rivera <stefanor@debian.org>  Fri, 21 May 2021 16:05:17 -0400

[ Reason ]
New upstream point release, with (only) a security fix (DoS).

[ Impact ]
Without this patch, pydantic can be DoSed with "infinity" as a
timestamp.

[ Tests ]
Upstream unit test suite runs during the package build.
There are unit tests for the changes in this release.

[ Risks ]
Upstream maintains support branches, and provided this point release. So
we're not relying on any untested patches.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

unblock pydantic/1.7.4-1
diff -Nru pydantic-1.7.3/debian/changelog pydantic-1.7.4/debian/changelog
--- pydantic-1.7.3/debian/changelog	2021-01-08 03:31:43.000000000 -0400
+++ pydantic-1.7.4/debian/changelog	2021-05-21 16:05:17.000000000 -0400
@@ -1,3 +1,13 @@
+pydantic (1.7.4-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream point release.
+    - Fixes CVE-2021-29510: Date and datetime parsing could cause an infinite
+      loop by passing either 'infinity' or float('inf') (Closes: #988480)
+  * Update watch file to version 4 with current uscan(1) recommended regex.
+
+ -- Stefano Rivera <stefanor@debian.org>  Fri, 21 May 2021 16:05:17 -0400
+
 pydantic (1.7.3-1) unstable; urgency=medium
 
   [ Sandro Tosi ]
diff -Nru pydantic-1.7.3/debian/watch pydantic-1.7.4/debian/watch
--- pydantic-1.7.3/debian/watch	2021-01-08 03:31:43.000000000 -0400
+++ pydantic-1.7.4/debian/watch	2021-05-21 16:05:17.000000000 -0400
@@ -1,2 +1,4 @@
-version=3
-https://github.com/samuelcolvin/pydantic/releases .*/archive/v([\d.]+)\.tar\.gz
+version=4
+opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%@PACKAGE@-$1.tar.gz%" \
+    https://github.com/samuelcolvin/pydantic/releases \
+    (?:.*?/)?v?(\d[\d.]*)\.tar\.gz
diff -Nru pydantic-1.7.3/.github/workflows/ci.yml pydantic-1.7.4/.github/workflows/ci.yml
--- pydantic-1.7.3/.github/workflows/ci.yml	2020-11-30 19:33:24.000000000 -0400
+++ pydantic-1.7.4/.github/workflows/ci.yml	2021-05-11 15:04:58.000000000 -0400
@@ -80,20 +80,20 @@
         COMPILED: yes
         DEPS: yes
 
-    - name: uninstall deps
-      run: pip uninstall -y cython email-validator typing-extensions devtools python-dotenv
-
-    - name: test compiled without deps
-      run: make test
-
-    - run: coverage xml
-    - uses: codecov/codecov-action@v1.0.14
-      with:
-        file: ./coverage.xml
-        env_vars: COMPILED,DEPS,PYTHON,OS
-      env:
-        COMPILED: yes
-        DEPS: no
+#    - name: uninstall deps
+#      run: pip uninstall -y cython email-validator typing-extensions devtools python-dotenv
+#
+#    - name: test compiled without deps
+#      run: make test
+#
+#    - run: coverage xml
+#    - uses: codecov/codecov-action@v1.0.14
+#      with:
+#        file: ./coverage.xml
+#        env_vars: COMPILED,DEPS,PYTHON,OS
+#      env:
+#        COMPILED: yes
+#        DEPS: no
 
     - name: remove compiled binaries
       run: |
@@ -159,11 +159,12 @@
       with:
         python-version: '3.7'
 
-    - name: install
-      run: make install-testing
-
-    - name: test
-      run: make test-fastapi
+    - run: echo "skip fastapi for now"
+#    - name: install
+#      run: make install-testing
+#
+#    - name: test
+#      run: make test-fastapi
 
   benchmark:
     name: run benchmarks
diff -Nru pydantic-1.7.3/HISTORY.md pydantic-1.7.4/HISTORY.md
--- pydantic-1.7.3/HISTORY.md	2020-11-30 19:33:24.000000000 -0400
+++ pydantic-1.7.4/HISTORY.md	2021-05-11 15:04:58.000000000 -0400
@@ -1,3 +1,9 @@
+## v1.7.4 (2021-05-11)
+
+* **Security fix:** Fix `date` and `datetime` parsing so passing either `'infinity'` or `float('inf')` 
+  (or their negative values) does not cause an infinite loop,
+  See security advisory [CVE-2021-29510](https://github.com/samuelcolvin/pydantic/security/advisories/GHSA-5jqp-qgf6-3pvh)
+
 ## v1.7.3 (2020-11-30)
 
 Thank you to pydantic's sponsors:
diff -Nru pydantic-1.7.3/pydantic/datetime_parse.py pydantic-1.7.4/pydantic/datetime_parse.py
--- pydantic-1.7.3/pydantic/datetime_parse.py	2020-11-30 19:33:24.000000000 -0400
+++ pydantic-1.7.4/pydantic/datetime_parse.py	2021-05-11 15:04:58.000000000 -0400
@@ -58,6 +58,8 @@
 # if greater than this, the number is in ms, if less than or equal it's in seconds
 # (in seconds this is 11th October 2603, in ms it's 20th August 1970)
 MS_WATERSHED = int(2e10)
+# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
+MAX_NUMBER = int(3e20)
 StrBytesIntFloat = Union[str, bytes, int, float]
 
 
@@ -73,6 +75,11 @@
 
 
 def from_unix_seconds(seconds: Union[int, float]) -> datetime:
+    if seconds > MAX_NUMBER:
+        return datetime.max
+    elif seconds < -MAX_NUMBER:
+        return datetime.min
+
     while abs(seconds) > MS_WATERSHED:
         seconds /= 1000
     dt = EPOCH + timedelta(seconds=seconds)
diff -Nru pydantic-1.7.3/pydantic/version.py pydantic-1.7.4/pydantic/version.py
--- pydantic-1.7.3/pydantic/version.py	2020-11-30 19:33:24.000000000 -0400
+++ pydantic-1.7.4/pydantic/version.py	2021-05-11 15:04:58.000000000 -0400
@@ -1,6 +1,6 @@
 __all__ = 'VERSION', 'version_info'
 
-VERSION = '1.7.3'
+VERSION = '1.7.4'
 
 
 def version_info() -> str:
diff -Nru pydantic-1.7.3/tests/test_datetime_parse.py pydantic-1.7.4/tests/test_datetime_parse.py
--- pydantic-1.7.3/tests/test_datetime_parse.py	2020-11-30 19:33:24.000000000 -0400
+++ pydantic-1.7.4/tests/test_datetime_parse.py	2021-05-11 15:04:58.000000000 -0400
@@ -42,11 +42,20 @@
         (1_549_316_052_104, date(2019, 2, 4)),  # nowish in ms
         (1_549_316_052_104_324, date(2019, 2, 4)),  # nowish in μs
         (1_549_316_052_104_324_096, date(2019, 2, 4)),  # nowish in ns
+        ('infinity', date(9999, 12, 31)),
+        ('inf', date(9999, 12, 31)),
+        (float('inf'), date(9999, 12, 31)),
+        ('infinity ', date(9999, 12, 31)),
+        (int('1' + '0' * 100), date(9999, 12, 31)),
+        (1e1000, date(9999, 12, 31)),
+        ('-infinity', date(1, 1, 1)),
+        ('-inf', date(1, 1, 1)),
+        ('nan', ValueError),
     ],
 )
 def test_date_parsing(value, result):
-    if result == errors.DateError:
-        with pytest.raises(errors.DateError):
+    if type(result) == type and issubclass(result, Exception):
+        with pytest.raises(result):
             parse_date(value)
     else:
         assert parse_date(value) == result
@@ -123,11 +132,19 @@
         (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)),  # nowish in ms
         (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)),  # nowish in μs
         (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)),  # nowish in ns
+        ('infinity', datetime(9999, 12, 31, 23, 59, 59, 999999)),
+        ('inf', datetime(9999, 12, 31, 23, 59, 59, 999999)),
+        ('inf ', datetime(9999, 12, 31, 23, 59, 59, 999999)),
+        (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)),
+        (float('inf'), datetime(9999, 12, 31, 23, 59, 59, 999999)),
+        ('-infinity', datetime(1, 1, 1, 0, 0)),
+        ('-inf', datetime(1, 1, 1, 0, 0)),
+        ('nan', ValueError),
     ],
 )
 def test_datetime_parsing(value, result):
-    if result == errors.DateTimeError:
-        with pytest.raises(errors.DateTimeError):
+    if type(result) == type and issubclass(result, Exception):
+        with pytest.raises(result):
             parse_datetime(value)
     else:
         assert parse_datetime(value) == result
@@ -251,3 +268,24 @@
         'type': 'value_error.unicodedecode',
         'msg': "'utf-8' codec can't decode byte 0x81 in position 0: invalid start byte",
     }
+
+
+def test_nan():
+    class Model(BaseModel):
+        dt: datetime
+        d: date
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(dt='nan', d='nan')
+    assert exc_info.value.errors() == [
+        {
+            'loc': ('dt',),
+            'msg': 'cannot convert float NaN to integer',
+            'type': 'value_error',
+        },
+        {
+            'loc': ('d',),
+            'msg': 'cannot convert float NaN to integer',
+            'type': 'value_error',
+        },
+    ]

Reply to: