Security advisory GHSA-5v9h-q3gj-c32x: SQL Injection via GeoJSON in sequelize
Package: sequelize (npm), vulnerable version: 3.23.5, fixed in: 3.23.6
CWE: CWE-89
Files changed by the fix:
lib/dialects/postgres/data-types.js

Fix diff:
diff --git a/changelog.md b/changelog.md
index 83a36bd3c9da..0004f888f2fa 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,7 @@
 - [FIXED] Fixed an issue where custom-named model fields break when offsetting, ordering, and including hasMany simultaneously. [#5985](https://github.com/sequelize/sequelize/issues/5985)
 - [FIXED] Don't remove includes from count queries and unify findAndCount and count queries. [#6123](https://github.com/sequelize/sequelize/issues/6123)
 - [FIXED] `Model.count` don't include attributes [#5057](https://github.com/sequelize/sequelize/issues/5057)
+- [SECURITY] `GEOMETRY` and `GEOGRAPHY` SQL injection attacks [#6194](https://github.com/sequelize/sequelize/issues/6194)
 
 # 3.23.3
 - [FIXED] Pass ResourceLock instead of raw connection in MSSQL disconnect handling
diff --git a/lib/dialects/postgres/data-types.js b/lib/dialects/postgres/data-types.js
index 5a53ecb9cebe..3401aedc6cf9 100644
--- a/lib/dialects/postgres/data-types.js
+++ b/lib/dialects/postgres/data-types.js
@@ -266,8 +266,8 @@ module.exports = function (BaseTypes) {
     return wkx.Geometry.parse(b).toGeoJSON();
   };
 
-  GEOMETRY.prototype.$stringify = function (value) {
-    return 'ST_GeomFromGeoJSON(\'' + JSON.stringify(value) + '\')';
+  GEOMETRY.prototype.$stringify = function (value, options) {
+    return 'ST_GeomFromGeoJSON(' + options.escape(JSON.stringify(value)) + ')';
   };
 
   var GEOGRAPHY = BaseTypes.GEOGRAPHY.inherits();
@@ -293,13 +293,13 @@ module.exports = function (BaseTypes) {
     array_oids: []
   };
 
-  GEOGRAPHY.parse = GEOGRAPHY.prototype.parse = function(value) {
+  GEOGRAPHY.parse = GEOGRAPHY.prototype.parse = function(value, options) {
     var b = new Buffer(value, 'hex');
     return wkx.Geometry.parse(b).toGeoJSON();
   };
 
-  GEOGRAPHY.prototype.$stringify = function (value) {
-    return 'ST_GeomFromGeoJSON(\'' + JSON.stringify(value) + '\')';
+  GEOGRAPHY.prototype.$stringify = function (value, options) {
+    return 'ST_GeomFromGeoJSON(' + options.escape(JSON.stringify(value)) + ')';
   };
 
   var hstore;
diff --git a/test/integration/model/findAll/order.test.js b/test/integration/model/findAll/order.test.js
index 7173dc30959c..72cb9e048fbc 100644
--- a/test/integration/model/findAll/order.test.js
+++ b/test/integration/model/findAll/order.test.js
@@ -59,7 +59,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
             }, function (err) {
               throw err;
             });
-          });        
+          });
        });
 
       describe('Sequelize.literal()', function () {
diff --git a/test/integration/model/geography.test.js b/test/integration/model/geography.test.js
index aac8fe113174..ead88c4b4d83 100644
--- a/test/integration/model/geography.test.js
+++ b/test/integration/model/geography.test.js
@@ -177,5 +177,26 @@ describe(Support.getTestDialectTeaser('Model'), function() {
         });
       });
     });
+
+    describe('sql injection attacks', function () {
+      beforeEach(function() {
+        this.Model = this.sequelize.define('Model', {
+          location: DataTypes.GEOGRAPHY
+        });
+        return this.sequelize.sync({ force: true });
+      });
+
+      it('should properly escape the single quotes', function () {
+        return this.Model.create({
+          location: {
+            type: "Point",
+            properties: {
+              exploit: "'); DELETE YOLO INJECTIONS; -- "
+            },
+            coordinates: [39.807222,-76.984722]
+          }
+        });
+      });
+    });
   }
 });
diff --git a/test/integration/model/geometry.test.js b/test/integration/model/geometry.test.js
index 3242c29e6fdb..2731d306e94b 100644
--- a/test/integration/model/geometry.test.js
+++ b/test/integration/model/geometry.test.js
@@ -177,5 +177,26 @@ describe(Support.getTestDialectTeaser('Model'), function() {
         });
       });
     });
+
+    describe('sql injection attacks', function () {
+      beforeEach(function() {
+        this.Model = this.sequelize.define('Model', {
+          location: DataTypes.GEOMETRY
+        });
+        return this.sequelize.sync({ force: true });
+      });
+
+      it('should properly escape the single quotes', function () {
+        return this.Model.create({
+          location: {
+            type: "Point",
+            properties: {
+              exploit: "'); DELETE YOLO INJECTIONS; -- "
+            },
+            coordinates: [39.807222,-76.984722]
+          }
+        });
+      });
+    });
   }
 });