Kaynağa Gözat

Merge remote-tracking branch 'origin/master'

Erik Johnston 3 yıl önce
ebeveyn
işleme
f46713326f

+ 1 - 0
changelog.d/337.bugfix

@@ -0,0 +1 @@
+Fix a long-standing bug where invalid JSON would be accepted over the HTTP interfaces.

+ 2 - 1
sydent/http/httpclient.py

@@ -27,6 +27,7 @@ from sydent.http.blacklisting_reactor import BlacklistingReactorWrapper
 from sydent.http.matrixfederationagent import MatrixFederationAgent
 from sydent.http.matrixfederationagent import MatrixFederationAgent
 from sydent.http.federation_tls_options import ClientTLSOptionsFactory
 from sydent.http.federation_tls_options import ClientTLSOptionsFactory
 from sydent.http.httpcommon import BodyExceededMaxSize, read_body_with_max_size
 from sydent.http.httpcommon import BodyExceededMaxSize, read_body_with_max_size
+from sydent.util import json_decoder
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -57,7 +58,7 @@ class HTTPClient(object):
         body = yield read_body_with_max_size(response, max_size)
         body = yield read_body_with_max_size(response, max_size)
         try:
         try:
             # json.loads doesn't allow bytes in Python 3.5
             # json.loads doesn't allow bytes in Python 3.5
-            json_body = json.loads(body.decode("UTF-8"))
+            json_body = json_decoder.decode(body.decode("UTF-8"))
         except Exception as e:
         except Exception as e:
             logger.exception("Error parsing JSON from %s", uri)
             logger.exception("Error parsing JSON from %s", uri)
             raise
             raise

+ 3 - 3
sydent/http/matrixfederationagent.py

@@ -14,7 +14,6 @@
 # limitations under the License.
 # limitations under the License.
 from __future__ import absolute_import
 from __future__ import absolute_import
 
 
-import json
 import logging
 import logging
 import random
 import random
 import time
 import time
@@ -33,6 +32,7 @@ from twisted.web.iweb import IAgent
 
 
 from sydent.http.httpcommon import BodyExceededMaxSize, read_body_with_max_size
 from sydent.http.httpcommon import BodyExceededMaxSize, read_body_with_max_size
 from sydent.http.srvresolver import SrvResolver, pick_server_from_list
 from sydent.http.srvresolver import SrvResolver, pick_server_from_list
+from sydent.util import json_decoder
 from sydent.util.ttlcache import TTLCache
 from sydent.util.ttlcache import TTLCache
 
 
 # period to cache .well-known results for by default
 # period to cache .well-known results for by default
@@ -324,7 +324,7 @@ class MatrixFederationAgent(object):
             if response.code != 200:
             if response.code != 200:
                 raise Exception("Non-200 response %s" % (response.code, ))
                 raise Exception("Non-200 response %s" % (response.code, ))
 
 
-            parsed_body = json.loads(body.decode('utf-8'))
+            parsed_body = json_decoder.decode(body.decode('utf-8'))
             logger.info("Response from .well-known: %s", parsed_body)
             logger.info("Response from .well-known: %s", parsed_body)
             if not isinstance(parsed_body, dict):
             if not isinstance(parsed_body, dict):
                 raise Exception("not a dict")
                 raise Exception("not a dict")
@@ -444,4 +444,4 @@ class _RoutingResult(object):
     The port we should route the TCP connection to (the target of the SRV record, or
     The port we should route the TCP connection to (the target of the SRV record, or
     the port from the URL/.well-known, or 8448)
     the port from the URL/.well-known, or 8448)
     :type: int
     :type: int
-    """
+    """

+ 2 - 1
sydent/http/servlets/__init__.py

@@ -22,6 +22,7 @@ import functools
 from twisted.internet import defer
 from twisted.internet import defer
 from twisted.web import server
 from twisted.web import server
 
 
+from sydent.util import json_decoder
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -76,7 +77,7 @@ def get_args(request, args, required=True):
     ):
     ):
         try:
         try:
             # json.loads doesn't allow bytes in Python 3.5
             # json.loads doesn't allow bytes in Python 3.5
-            request_args = json.loads(request.content.read().decode("UTF-8"))
+            request_args = json_decoder.decode(request.content.read().decode("UTF-8"))
         except ValueError:
         except ValueError:
             raise MatrixRestError(400, 'M_BAD_JSON', 'Malformed JSON')
             raise MatrixRestError(400, 'M_BAD_JSON', 'Malformed JSON')
 
 

+ 3 - 3
sydent/http/servlets/lookupservlet.py

@@ -20,10 +20,10 @@ from twisted.web.resource import Resource
 from sydent.db.threepid_associations import GlobalAssociationStore
 from sydent.db.threepid_associations import GlobalAssociationStore
 
 
 import logging
 import logging
-import json
 import signedjson.sign
 import signedjson.sign
 
 
 from sydent.http.servlets import get_args, jsonwrap, send_cors, MatrixRestError
 from sydent.http.servlets import get_args, jsonwrap, send_cors, MatrixRestError
+from sydent.util import json_decoder
 
 
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
@@ -41,7 +41,7 @@ class LookupServlet(Resource):
         Look up an individual threepid.
         Look up an individual threepid.
 
 
         ** DEPRECATED **
         ** DEPRECATED **
-        
+
         Params: 'medium': the medium of the threepid
         Params: 'medium': the medium of the threepid
                 'address': the address of the threepid
                 'address': the address of the threepid
         Returns: A signed association if the threepid has a corresponding mxid, otherwise the empty object.
         Returns: A signed association if the threepid has a corresponding mxid, otherwise the empty object.
@@ -60,7 +60,7 @@ class LookupServlet(Resource):
         if not sgassoc:
         if not sgassoc:
             return {}
             return {}
 
 
-        sgassoc = json.loads(sgassoc)
+        sgassoc = json_decoder.decode(sgassoc)
         if not self.sydent.server_name in sgassoc['signatures']:
         if not self.sydent.server_name in sgassoc['signatures']:
             # We have not yet worked out what the proper trust model should be.
             # We have not yet worked out what the proper trust model should be.
             #
             #

+ 2 - 1
sydent/http/servlets/replication.py

@@ -19,6 +19,7 @@ import twisted.python.log
 from twisted.web.resource import Resource
 from twisted.web.resource import Resource
 from sydent.http.servlets import jsonwrap, MatrixRestError
 from sydent.http.servlets import jsonwrap, MatrixRestError
 from sydent.threepid import threePidAssocFromDict
 from sydent.threepid import threePidAssocFromDict
+from sydent.util import json_decoder
 
 
 from sydent.util.hash import sha256_and_url_safe_base64
 from sydent.util.hash import sha256_and_url_safe_base64
 
 
@@ -60,7 +61,7 @@ class ReplicationPushServlet(Resource):
 
 
         try:
         try:
             # json.loads doesn't allow bytes in Python 3.5
             # json.loads doesn't allow bytes in Python 3.5
-            inJson = json.loads(request.content.read().decode("UTF-8"))
+            inJson = json_decoder.decode(request.content.read().decode("UTF-8"))
         except ValueError:
         except ValueError:
             logger.warn("Peer %s made push connection with malformed JSON", peer.servername)
             logger.warn("Peer %s made push connection with malformed JSON", peer.servername)
             raise MatrixRestError(400, 'M_BAD_JSON', 'Malformed JSON')
             raise MatrixRestError(400, 'M_BAD_JSON', 'Malformed JSON')

+ 2 - 1
sydent/http/servlets/threepidunbindservlet.py

@@ -24,6 +24,7 @@ from signedjson.sign import SignatureVerifyException
 
 
 from sydent.http.servlets import dict_to_json_bytes
 from sydent.http.servlets import dict_to_json_bytes
 from sydent.db.valsession import ThreePidValSessionStore
 from sydent.db.valsession import ThreePidValSessionStore
+from sydent.util import json_decoder
 from sydent.util.stringutils import is_valid_client_secret
 from sydent.util.stringutils import is_valid_client_secret
 from sydent.validators import (
 from sydent.validators import (
     IncorrectClientSecretException,
     IncorrectClientSecretException,
@@ -51,7 +52,7 @@ class ThreePidUnbindServlet(Resource):
         try:
         try:
             try:
             try:
                 # json.loads doesn't allow bytes in Python 3.5
                 # json.loads doesn't allow bytes in Python 3.5
-                body = json.loads(request.content.read().decode("UTF-8"))
+                body = json_decoder.decode(request.content.read().decode("UTF-8"))
             except ValueError:
             except ValueError:
                 request.setResponseCode(400)
                 request.setResponseCode(400)
                 request.write(dict_to_json_bytes({'errcode': 'M_BAD_JSON', 'error': 'Malformed JSON'}))
                 request.write(dict_to_json_bytes({'errcode': 'M_BAD_JSON', 'error': 'Malformed JSON'}))

+ 2 - 1
sydent/replication/peer.py

@@ -22,6 +22,7 @@ from sydent.db.threepid_associations import GlobalAssociationStore
 from sydent.db.hashing_metadata import HashingMetadataStore
 from sydent.db.hashing_metadata import HashingMetadataStore
 from sydent.threepid import threePidAssocFromDict
 from sydent.threepid import threePidAssocFromDict
 from sydent.config import ConfigError
 from sydent.config import ConfigError
+from sydent.util import json_decoder
 from sydent.util.hash import sha256_and_url_safe_base64
 from sydent.util.hash import sha256_and_url_safe_base64
 from unpaddedbase64 import decode_base64
 from unpaddedbase64 import decode_base64
 
 
@@ -241,7 +242,7 @@ class RemotePeer(Peer):
         :param updateDeferred: The deferred to call the error callback of.
         :param updateDeferred: The deferred to call the error callback of.
         :type updateDeferred: twisted.internet.defer.Deferred
         :type updateDeferred: twisted.internet.defer.Deferred
         """
         """
-        errObj = json.loads(body.decode("utf8"))
+        errObj = json_decoder.decode(body.decode("utf8"))
         e = RemotePeerError()
         e = RemotePeerError()
         e.errorDict = errObj
         e.errorDict = errObj
         updateDeferred.errback(e)
         updateDeferred.errback(e)

+ 10 - 1
sydent/util/__init__.py

@@ -14,9 +14,9 @@
 # See the License for the specific language governing permissions and
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # limitations under the License.
 
 
+import json
 import time
 import time
 
 
-
 def time_msec():
 def time_msec():
     """
     """
     Get the current time in milliseconds.
     Get the current time in milliseconds.
@@ -25,3 +25,12 @@ def time_msec():
     :rtype: int
     :rtype: int
     """
     """
     return int(time.time() * 1000)
     return int(time.time() * 1000)
+
+
+def _reject_invalid_json(val):
+    """Do not allow Infinity, -Infinity, or NaN values in JSON."""
+    raise ValueError("Invalid JSON value: '%s'" % val)
+
+
+# a custom JSON decoder which will reject Python extensions to JSON.
+json_decoder = json.JSONDecoder(parse_constant=_reject_invalid_json)