Skip to content

Commit

Permalink
Merge pull request #1343 from tediousjs/arthur/prevent-prototype-poll…
Browse files Browse the repository at this point in the history
…ution

fix: prevent `Object` prototype pollution to affect rows or column metadata objects
  • Loading branch information
arthurschreiber committed Sep 19, 2021
2 parents 4196d85 + e112237 commit 7aea4fa
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2074,7 +2074,7 @@ class Connection extends EventEmitter {
if (request) {
if (!request.canceled) {
if (this.config.options.useColumnNames) {
const columns: { [key: string]: ColumnMetadata } = {};
const columns: { [key: string]: ColumnMetadata } = Object.create(null);

for (let j = 0, len = token.columns.length; j < len; j++) {
const col = token.columns[j];
Expand Down
2 changes: 1 addition & 1 deletion src/token/row-token-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function rowParser(parser: Parser): Promise<RowToken> {
}

if (parser.options.useColumnNames) {
const columnsMap: { [key: string]: Column } = {};
const columnsMap: { [key: string]: Column } = Object.create(null);

columns.forEach((column) => {
const colName = column.metadata.colName;
Expand Down
151 changes: 93 additions & 58 deletions test/integration/connection-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -591,86 +591,121 @@ describe('Insertion Tests', function() {
});
});

it('should numeric column name', function(done) {
const config = getConfig();
config.options.useColumnNames = true;
describe('when `useColumnNames` is `true`', function() {
it('should support numeric column names', function(done) {
const config = getConfig();
config.options.useColumnNames = true;

const request = new Request('select 8 as [123]', function(err, rowCount) {
assert.ifError(err);
assert.strictEqual(rowCount, 1);
const connection = new Connection(config);
connection.connect((err) => {
if (err) {
return done(err);
}

connection.close();
});
const request = new Request('select 8 as [123]', (err, rowCount) => {
assert.ifError(err);
assert.strictEqual(rowCount, 1);

request.on('columnMetadata', function(columnsMetadata) {
assert.strictEqual(Object.keys(columnsMetadata).length, 1);
});
connection.close();
});

request.on('row', function(columns) {
assert.strictEqual(Object.keys(columns).length, 1);
assert.strictEqual(columns[123].value, 8);
});
request.on('columnMetadata', (columnsMetadata) => {
assert.strictEqual(Object.keys(columnsMetadata).length, 1);
});

let connection = new Connection(config);
request.on('row', (columns) => {
assert.strictEqual(Object.keys(columns).length, 1);
assert.strictEqual(columns[123].value, 8);
});

connection.connect(function(err) {
connection.execSql(request);
});
connection.execSql(request);
});

connection.on('end', function() {
done();
connection.on('end', () => {
done();
});
});

connection.on('infoMessage', function(info) {
// console.log("#{info.number} : #{info.message}")
});
it('supports duplicate column names', function(done) {
const config = getConfig();
config.options.useColumnNames = true;

connection.on('debug', function(text) {
// console.log(text)
});
});
const connection = new Connection(config);

it('should duplicate column name', function(done) {
const config = getConfig();
config.options.useColumnNames = true;
connection.connect((err) => {
if (err) {
return done(err);
}

const request = new Request("select 1 as abc, 2 as xyz, '3' as abc", function(
err,
rowCount
) {
assert.ifError(err);
assert.strictEqual(rowCount, 1);
const request = new Request("select 1 as abc, 2 as xyz, '3' as abc", (err, rowCount) => {
assert.ifError(err);
assert.strictEqual(rowCount, 1);

connection.close();
});
connection.close();
});

request.on('columnMetadata', function(columnsMetadata) {
assert.strictEqual(Object.keys(columnsMetadata).length, 2);
});
request.on('columnMetadata', (columnsMetadata) => {
assert.strictEqual(Object.keys(columnsMetadata).length, 2);
});

request.on('row', function(columns) {
assert.strictEqual(Object.keys(columns).length, 2);
request.on('row', (columns) => {
assert.strictEqual(Object.keys(columns).length, 2);

assert.strictEqual(columns.abc.value, 1);
assert.strictEqual(columns.xyz.value, 2);
});
assert.strictEqual(columns.abc.value, 1);
assert.strictEqual(columns.xyz.value, 2);
});

let connection = new Connection(config);
connection.execSql(request);
});

connection.connect(function(err) {
connection.execSql(request);
connection.on('end', () => {
done();
});
});

connection.on('end', function() {
done();
});
describe('with a polluted `Object` prototype', function() {
beforeEach(function() {
({}).constructor.prototype.foo = 'bar';
});

connection.on('infoMessage', function(info) {
// console.log("#{info.number} : #{info.message}")
});
afterEach(function() {
delete ({}).constructor.prototype.foo;
});

connection.on('debug', function(text) {
// console.log(text)
it('should not have column metadata or rows be affected by the pollution', function(done) {
const config = getConfig();
config.options.useColumnNames = true;

const connection = new Connection(config);
connection.connect((err) => {
if (err) {
return done(err);
}

const request = new Request('select 1 as abc', (err, rowCount) => {
assert.ifError(err);
assert.strictEqual(rowCount, 1);

connection.close();
});

request.on('columnMetadata', (columnsMetadata) => {
assert.property(columnsMetadata, 'abc');
assert.notProperty(columnsMetadata, 'foo');
});

request.on('row', (columns) => {
assert.property(columns, 'abc');
assert.notProperty(columns, 'foo');
});

connection.execSql(request);
});

connection.on('end', () => {
done();
});
});
});
});

Expand Down

0 comments on commit 7aea4fa

Please sign in to comment.