Non authenticated request cannot use pg_ catalogs/functions

This commit is contained in:
Raul Ochoa 2014-08-08 12:48:29 +02:00
parent 9cc7c94406
commit 21b8e6947c
5 changed files with 80 additions and 82 deletions

View File

@ -1,6 +1,9 @@
1.15.0 - 2014-mm-dd 1.14.1 - 2014-mm-dd
------------------- -------------------
Other changes:
* Constraint for pg_ queries if request is non authenticated
1.14.0 - 2014-08-07 1.14.0 - 2014-08-07
------------------- -------------------

View File

@ -276,7 +276,7 @@ function handleQuery(req, res) {
pass: global.settings.db_pubuser_pass pass: global.settings.db_pubuser_pass
}; };
var authenticated; var authenticated = false;
var formatter; var formatter;
@ -325,6 +325,7 @@ function handleQuery(req, res) {
throw err; throw err;
} }
if (_.isBoolean(isAuthenticated) && isAuthenticated) { if (_.isBoolean(isAuthenticated) && isAuthenticated) {
authenticated = isAuthenticated;
dbopts.user = _.template(global.settings.db_user, {user_id: dbParams.dbuser}); dbopts.user = _.template(global.settings.db_user, {user_id: dbParams.dbuser});
if ( global.settings.hasOwnProperty('db_user_pass') ) { if ( global.settings.hasOwnProperty('db_user_pass') ) {
dbopts.pass = _.template(global.settings.db_user_pass, { dbopts.pass = _.template(global.settings.db_user_pass, {
@ -386,6 +387,18 @@ function handleQuery(req, res) {
tableCache.set(sql_md5, tableCacheItem); tableCache.set(sql_md5, tableCacheItem);
} }
if ( !authenticated && tableCacheItem ) {
var affected_tables = tableCacheItem.affected_tables;
for ( var i = 0; i < affected_tables.length; ++i ) {
var t = affected_tables[i];
if ( t.match(/\bpg_/) ) {
var e = new SyntaxError("system tables are forbidden");
e.http_status = 403;
throw(e);
}
}
}
var fClass = formats[format]; var fClass = formats[format];
formatter = new fClass(); formatter = new fClass();
req.formatter = formatter; req.formatter = formatter;

2
npm-shrinkwrap.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "cartodb_sql_api", "name": "cartodb_sql_api",
"version": "1.15.0", "version": "1.14.1",
"dependencies": { "dependencies": {
"underscore": { "underscore": {
"version": "1.3.3" "version": "1.3.3"

View File

@ -5,7 +5,7 @@
"keywords": [ "keywords": [
"cartodb" "cartodb"
], ],
"version": "1.15.0", "version": "1.14.1",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/CartoDB/CartoDB-SQL-API.git" "url": "git://github.com/CartoDB/CartoDB-SQL-API.git"

View File

@ -955,84 +955,66 @@ test('GET /api/v1/sql ensure cross domain set on errors', function(done){
}); });
}); });
test('cannot GET system tables', function(done){ var systemQueriesSuitesToTest = [
var req = { headers: {host: 'vizzuality.cartodb.com'}, {
method: 'GET' }; desc: 'pg_ queries work with api_key and fail otherwise',
var pre = '/api/v1/sql?'; queries: [
Step( 'SELECT * FROM pg_attribute',
function trySysTable1() { 'SELECT * FROM PG_attribute',
req.url = pre + querystring.stringify({q: 'SELECT * FROM pg_attribute'}); 'SELECT * FROM "pg_attribute"',
var next = this; 'SELECT a.* FROM untitle_table_4 a,pg_attribute'
assert.response( app, req, function(res) { next(null, res); } ); ],
}, api_key_works: true,
function chkSysTable1_trySysTable2(err, res) { no_api_key_works: false
if ( err ) throw err; },
var next = this; {
assert.equal(res.statusCode, 200); desc: 'Possible false positive queries will work with api_key and without it',
req.url = pre + querystring.stringify({q: 'SELECT * FROM PG_attribute'}); queries: [
assert.response(app, req, function(res) { next(null, res); }); "SELECT 'pg_'",
}, 'SELECT pg_attribute FROM ( select 1 as pg_attribute ) as f',
function chkSysTable2_trySysTable3(err, res) { 'SELECT * FROM cpg_test'
if ( err ) throw err; ],
var next = this; api_key_works: true,
assert.equal(res.statusCode, 200); no_api_key_works: true
req.url = pre + querystring.stringify({q: 'SELECT * FROM "pg_attribute"'}); },
assert.response(app, req, function(res) { next(null, res); }); {
}, desc: 'Set queries will FAIL for both api_key and no_api_key queries',
function chkSysTable3_trySysTable4(err, res) { queries: [
if ( err ) throw err; ' SET work_mem TO 80000',
var next = this; ' set statement_timeout TO 400'
assert.equal(res.statusCode, 200); ],
req.url = pre + querystring.stringify({q: 'SELECT a.* FROM untitle_table_4 a,pg_attribute'}); api_key_works: false,
assert.response(app, req, function(res) { next(null, res); }); no_api_key_works: false
}, }
function chkSysTable4_tryValidPg1(err, res) { ];
if ( err ) throw err;
var next = this; systemQueriesSuitesToTest.forEach(function(suiteToTest) {
assert.equal(res.statusCode, 200); var apiKeyStatusErrorCode = !!suiteToTest.api_key_works ? 200 : 403;
req.url = pre + querystring.stringify({q: "SELECT 'pg_'"}); testSystemQueries(suiteToTest.desc + ' with api_key', suiteToTest.queries, apiKeyStatusErrorCode, '1234');
assert.response(app, req, function(res) { next(null, res); }); var noApiKeyStatusErrorCode = !!suiteToTest.no_api_key_works ? 200 : 403;
}, testSystemQueries(suiteToTest.desc, suiteToTest.queries, noApiKeyStatusErrorCode);
function chkValidPg1_tryValidPg2(err, res) { });
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 200); function testSystemQueries(description, queries, statusErrorCode, apiKey) {
req.url = pre + querystring.stringify({q: "SELECT pg_attribute FROM ( select 1 as pg_attribute ) as f"}); queries.forEach(function(query) {
assert.response(app, req, function(res) { next(null, res); }); test('[' + description + '] query: ' + query, function(done) {
}, var queryStringParams = {q: query};
// See http://github.com/CartoDB/CartoDB-SQL-API/issues/118 if (!!apiKey) {
function chkValidPg1_tryValidPg3_b(err, res) { queryStringParams.api_key = apiKey;
if ( err ) throw err; }
var next = this; var request = {
assert.equal(res.statusCode, 200, res.statusCode + ':' + res.body); headers: {host: 'vizzuality.cartodb.com'},
req.url = pre + querystring.stringify({q: "SELECT * FROM cpg_test"}); method: 'GET',
assert.response(app, req, function(res) { next(null, res); }); url: '/api/v1/sql?' + querystring.stringify(queryStringParams)
}, };
function chkValidPg2_trySet1(err, res) { assert.response(app, request, function(response) {
if ( err ) throw err; assert.equal(response.statusCode, statusErrorCode);
var next = this; done();
assert.equal(res.statusCode, 200, res.statusCode + ':' + res.body); });
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);
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);
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){
assert.response(app, { assert.response(app, {