0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00
ghost/test/unit/data/exporter/index_spec.js
Naz aaa54c603c Refactored exporter to use "allowlist" table filtering
refs https://github.com/TryGhost/Team/issues/555

- Previous blocklist approach was resulting in adding every single new table into an export automatically. Which creates possibility to leak sensitive data if not used porperly. Allowlist approach gives better control over what is exported, makes this information explicit, and version-control friendlier
2021-03-25 16:46:56 +13:00

182 lines
7.1 KiB
JavaScript

const should = require('should');
const sinon = require('sinon');
const rewire = require('rewire');
const Promise = require('bluebird');
const errors = require('@tryghost/errors');
const db = require('../../../../core/server/data/db');
const exporter = rewire('../../../../core/server/data/exporter');
const schema = require('../../../../core/server/data/schema');
const models = require('../../../../core/server/models');
const schemaTables = Object.keys(schema.tables);
describe('Exporter', function () {
let tablesStub;
let queryMock;
let knexMock;
before(function () {
models.init();
});
afterEach(function () {
sinon.restore();
});
describe('doExport', function () {
beforeEach(function () {
exporter.__set__('ghostVersion', {
full: '2.0.0'
});
tablesStub = sinon.stub(schema.commands, 'getTables').returns(schemaTables);
queryMock = {
whereNot: sinon.stub(),
select: sinon.stub()
};
knexMock = sinon.stub().returns(queryMock);
sinon.stub(db, 'knex').get(function () {
return knexMock;
});
});
it('should try to export all the correct tables (without excluded)', function (done) {
// Execute
exporter.doExport().then(function (exportData) {
// No tables, less the number of excluded tables
const expectedCallCount = schemaTables.length - exporter.BACKUP_TABLES.length;
should.exist(exportData);
//TODO: Update when 3.0.0 is released
exportData.meta.version.should.eql('2.0.0');
tablesStub.calledOnce.should.be.true();
db.knex.called.should.be.true();
knexMock.callCount.should.eql(expectedCallCount);
queryMock.select.callCount.should.have.eql(expectedCallCount);
knexMock.getCall(0).args[0].should.eql('posts');
knexMock.getCall(1).args[0].should.eql('posts_meta');
knexMock.getCall(2).args[0].should.eql('users');
knexMock.getCall(3).args[0].should.eql('posts_authors');
knexMock.getCall(4).args[0].should.eql('roles');
knexMock.getCall(5).args[0].should.eql('roles_users');
knexMock.getCall(6).args[0].should.eql('permissions');
knexMock.getCall(7).args[0].should.eql('permissions_users');
knexMock.getCall(8).args[0].should.eql('permissions_roles');
knexMock.getCall(9).args[0].should.eql('settings');
knexMock.getCall(10).args[0].should.eql('tags');
knexMock.getCall(11).args[0].should.eql('posts_tags');
done();
}).catch(done);
});
// SKIPPED: the "extra" clients and client_trusted_domains tables no longer exist
it.skip('should try to export all the correct tables with extra tables', function (done) {
// Setup for success
queryMock.select.returns(new Promise.resolve({}));
// Execute
exporter.doExport({include: ['clients', 'client_trusted_domains']}).then(function (exportData) {
// all tables, except of the tokes and sessions
const expectedCallCount = schemaTables.length - (exporter.BACKUP_TABLES.length - 2);
should.exist(exportData);
exportData.meta.version.should.eql('2.0.0');
tablesStub.calledOnce.should.be.true();
db.knex.called.should.be.true();
queryMock.select.called.should.be.true();
knexMock.callCount.should.eql(expectedCallCount);
queryMock.select.callCount.should.have.eql(expectedCallCount);
knexMock.getCall(0).args[0].should.eql('posts');
knexMock.getCall(1).args[0].should.eql('posts_meta');
knexMock.getCall(2).args[0].should.eql('users');
knexMock.getCall(3).args[0].should.eql('posts_authors');
knexMock.getCall(4).args[0].should.eql('roles');
knexMock.getCall(5).args[0].should.eql('roles_users');
knexMock.getCall(6).args[0].should.eql('permissions');
knexMock.getCall(7).args[0].should.eql('permissions_users');
knexMock.getCall(8).args[0].should.eql('permissions_roles');
knexMock.getCall(9).args[0].should.eql('settings');
knexMock.getCall(10).args[0].should.eql('tags');
knexMock.getCall(11).args[0].should.eql('posts_tags');
knexMock.getCall(12).args[0].should.eql('clients');
knexMock.getCall(13).args[0].should.eql('client_trusted_domains');
done();
}).catch(done);
});
it('should catch and log any errors', function (done) {
// Setup for failure
queryMock.select.returns(Promise.reject({}));
// Execute
exporter.doExport()
.then(function () {
done(new Error('expected error for export'));
})
.catch(function (err) {
(err instanceof errors.DataExportError).should.eql(true);
done();
});
});
});
describe('exportFileName', function () {
it('should return a correctly structured filename', function (done) {
const settingsStub = sinon.stub(models.Settings, 'findOne').returns(
new Promise.resolve({
get: function () {
return 'testblog';
}
})
);
exporter.fileName().then(function (result) {
should.exist(result);
settingsStub.calledOnce.should.be.true();
result.should.match(/^testblog\.ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\.json$/);
done();
}).catch(done);
});
it('should return a correctly structured filename if settings is empty', function (done) {
const settingsStub = sinon.stub(models.Settings, 'findOne').returns(
new Promise.resolve()
);
exporter.fileName().then(function (result) {
should.exist(result);
settingsStub.calledOnce.should.be.true();
result.should.match(/^ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\.json$/);
done();
}).catch(done);
});
it('should return a correctly structured filename if settings errors', function (done) {
const settingsStub = sinon.stub(models.Settings, 'findOne').returns(
new Promise.reject()
);
exporter.fileName().then(function (result) {
should.exist(result);
settingsStub.calledOnce.should.be.true();
result.should.match(/^ghost\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\.json$/);
done();
}).catch(done);
});
});
});