Make using SET or querying system catalogues harder

An hack to "prevent" querying system tables already existed but
was pretty weak. This commits makes that a bit stronger. The
filter for SET is new.
This commit is contained in:
Sandro Santilli 2013-04-09 11:49:05 +02:00
parent bc068a5463
commit 1bcffbc68c
3 changed files with 77 additions and 17 deletions

View File

@ -1,5 +1,6 @@
1.3.8 1.3.8
----- -----
* Make using SET or querying system catalogues harder
1.3.7 1.3.7
----- -----

View File

@ -99,15 +99,23 @@ var PSQL = function(user_id, db, limit, offset){
} }
}; };
// throw exception if system table detected // throw exception if illegal operations are detected
// NOTE: this check is weak hack, better database
// permissions should be used instead.
me.sanitize = function(sql, callback){ me.sanitize = function(sql, callback){
if (sql.match(/\s+pg_.+/)){ if (sql.match(/\s+"?pg_.+/i)){
var error = new SyntaxError("system tables are forbidden"); var error = new SyntaxError("system tables are forbidden");
error.http_status = 403; error.http_status = 403;
throw error; callback(error);
} else { return;
callback(null,true);
} }
if (sql.match(/^\s+set\s+/i)){
var error = new SyntaxError("SET command is forbidden");
error.http_status = 403;
callback(error);
return;
}
callback(null,true);
}; };
return me; return me;

View File

@ -22,6 +22,7 @@ var app = require(global.settings.app_root + '/app/controllers/app')
, zipfile = require('zipfile') , zipfile = require('zipfile')
, fs = require('fs') , fs = require('fs')
, libxmljs = require('libxmljs') , libxmljs = require('libxmljs')
, Step = require('step')
; ;
// allow lots of emitters to be set to silence warning // allow lots of emitters to be set to silence warning
@ -621,18 +622,68 @@ test('GET /api/v1/sql ensure cross domain set on errors', function(done){
}); });
test('cannot GET system tables', function(done){ test('cannot GET system tables', function(done){
assert.response(app, { var req = { headers: {host: 'vizzuality.cartodb.com'},
url: '/api/v1/sql?q=SELECT%20*%20FROM%20pg_attribute', method: 'GET' };
headers: {host: 'vizzuality.cartodb.com'}, var pre = '/api/v1/sql?';
method: 'GET' Step(
},{ function trySysTable1() {
status: 403 req.url = pre + querystring.stringify({q: 'SELECT * FROM pg_attribute'});
}, function(res) { var next = this;
assert.response( app, req, function(res) { next(null, res); } );
},
function chkSysTable1_trySysTable2(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 403);
assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(res.headers['content-disposition'], 'inline'); assert.deepEqual(res.headers['content-disposition'], 'inline');
// TODO: check actual error message... // TODO: check actual error message...
done(); req.url = pre + querystring.stringify({q: 'SELECT * FROM PG_attribute'});
}); assert.response(app, req, function(res) { next(null, res); });
},
function chkSysTable2_trySysTable3(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 403);
assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(res.headers['content-disposition'], 'inline');
// TODO: check actual error message...
req.url = pre + querystring.stringify({q: 'SELECT * FROM "pg_attribute"'});
assert.response(app, req, function(res) { next(null, res); });
},
function chkSysTable3_trySet1(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 403);
assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(res.headers['content-disposition'], 'inline');
// TODO: check actual error message...
req.url = pre + querystring.stringify({q: ' set statement_timeout TO 400'});
assert.response(app, req, function(res) { next(null, res); });
},
function chkSet1_trySet2(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 403);
assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(res.headers['content-disposition'], 'inline');
// TODO: check actual error message...
req.url = pre + querystring.stringify({q: ' SET work_mem TO 80000'});
assert.response(app, req, function(res) { next(null, res); });
},
function chkSet2(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 403);
assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(res.headers['content-disposition'], 'inline');
// TODO: check actual error message...
return true;
},
function finish(err) {
done(err);
}
);
}); });
test('GET decent error if domain is incorrect', function(done){ test('GET decent error if domain is incorrect', function(done){