Skip to content

Commit 3c49e9e

Browse files
committed
fix: revert breaking change in results creation
1 parent 6dccf55 commit 3c49e9e

File tree

6 files changed

+89
-59
lines changed

6 files changed

+89
-59
lines changed

lib/helpers.js

+37-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
function srcEscape(str) {
1818
return JSON.stringify({
19-
[str]: 1
19+
[str]: 1,
2020
}).slice(1, -3);
2121
}
2222

@@ -29,7 +29,7 @@ try {
2929
const REQUIRE_TERMINATOR = '';
3030
highlightFn = require(`cardinal${REQUIRE_TERMINATOR}`).highlight;
3131
} catch (err) {
32-
highlightFn = text => {
32+
highlightFn = (text) => {
3333
if (!cardinalRecommended) {
3434
// eslint-disable-next-line no-console
3535
console.log('For nicer debug output consider install cardinal@^2.0.0');
@@ -56,10 +56,44 @@ exports.printDebugWithCode = printDebugWithCode;
5656
*/
5757
function typeMatch(type, list, Types) {
5858
if (Array.isArray(list)) {
59-
return list.some(t => type === Types[t]);
59+
return list.some((t) => type === Types[t]);
6060
}
6161

6262
return !!list;
6363
}
6464

6565
exports.typeMatch = typeMatch;
66+
67+
function createSafeObject() {
68+
const nativeProps = ['hasOwnProperty', 'toString', 'valueOf'];
69+
70+
const handler = {
71+
get(_, prop, receiver) {
72+
const isNativeProp = nativeProps.includes(prop);
73+
74+
if (isNativeProp) {
75+
return (...args) => Object.prototype[prop].apply(receiver, args);
76+
}
77+
78+
if (prop === '__proto__') {
79+
return Object.prototype;
80+
}
81+
82+
return Reflect.get(...arguments);
83+
},
84+
set(_, prop) {
85+
const isNativeProp = nativeProps.includes(prop);
86+
87+
if (isNativeProp || prop === '__proto__') {
88+
return false;
89+
}
90+
91+
return Reflect.set(...arguments);
92+
},
93+
};
94+
95+
const safePrototype = Object.create(Object.prototype);
96+
return new Proxy(safePrototype, handler);
97+
}
98+
99+
exports.createSafeObject = createSafeObject;

lib/parsers/binary_parser.js

+6-9
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,7 @@ function compile(fields, options, config) {
122122
if (options.rowsAsArray) {
123123
parserFn(`const result = new Array(${fields.length});`);
124124
} else {
125-
parserFn('const result = Object.create(null);');
126-
parserFn(`Object.defineProperty(result, "constructor", {
127-
value: Object.create(null),
128-
writable: false,
129-
configurable: false,
130-
enumerable: false
131-
});`);
125+
parserFn('const result = createSafeObject();');
132126
}
133127

134128
// Global typeCast
@@ -161,7 +155,7 @@ function compile(fields, options, config) {
161155
} else if (options.nestTables === true) {
162156
tableName = helpers.srcEscape(fields[i].table);
163157
parserFn(
164-
`if (!result[${tableName}]) result[${tableName}] = Object.create(null);`,
158+
`if (!result[${tableName}]) result[${tableName}] = createSafeObject();`,
165159
);
166160
lvalue = `result[${tableName}][${fieldName}]`;
167161
} else if (options.rowsAsArray) {
@@ -207,7 +201,10 @@ function compile(fields, options, config) {
207201
parserFn.toString(),
208202
);
209203
}
210-
return parserFn.toFunction({ wrap });
204+
return parserFn.toFunction({
205+
wrap,
206+
createSafeObject: helpers.createSafeObject,
207+
});
211208
}
212209

213210
function getBinaryParser(fields, options, config) {

lib/parsers/text_parser.js

+7-10
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,7 @@ function compile(fields, options, config) {
131131
if (options.rowsAsArray) {
132132
parserFn(`const result = new Array(${fields.length});`);
133133
} else {
134-
parserFn('const result = Object.create(null);');
135-
parserFn(`Object.defineProperty(result, "constructor", {
136-
value: Object.create(null),
137-
writable: false,
138-
configurable: false,
139-
enumerable: false
140-
});`);
134+
parserFn('const result = createSafeObject();');
141135
}
142136

143137
const resultTables = {};
@@ -150,7 +144,7 @@ function compile(fields, options, config) {
150144
resultTablesArray = Object.keys(resultTables);
151145
for (let i = 0; i < resultTablesArray.length; i++) {
152146
parserFn(
153-
`result[${helpers.srcEscape(resultTablesArray[i])}] = Object.create(null);`,
147+
`result[${helpers.srcEscape(resultTablesArray[i])}] = createSafeObject();`,
154148
);
155149
}
156150
}
@@ -203,9 +197,12 @@ function compile(fields, options, config) {
203197
);
204198
}
205199
if (typeof options.typeCast === 'function') {
206-
return parserFn.toFunction({ wrap });
200+
return parserFn.toFunction({
201+
wrap,
202+
createSafeObject: helpers.createSafeObject,
203+
});
207204
}
208-
return parserFn.toFunction();
205+
return parserFn.toFunction({ createSafeObject: helpers.createSafeObject });
209206
}
210207

211208
function getTextParser(fields, options, config) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, assert } from 'poku';
2+
import { describeOptions } from '../../../common.test.cjs';
3+
import { createSafeObject } from '../../../../lib/helpers.js';
4+
5+
describe('Creating a safe object', describeOptions);
6+
7+
const stdObj = {};
8+
const safeObj = createSafeObject();
9+
10+
assert.deepStrictEqual(stdObj, safeObj, 'Ensure __proto__ is immutable');
11+
assert.throws(() => {
12+
safeObj.__proto__ = { toString: () => true };
13+
}, 'Ensure safeObject is valid');
14+
15+
stdObj.__proto__ = { toString: () => true };
16+
assert.notDeepStrictEqual(
17+
stdObj,
18+
safeObj,
19+
'Ensure that the object is not the same as the poisoned __proto__',
20+
);

test/esm/unit/parsers/prototype-binary-results.test.mjs

+9-18
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,23 @@ describe('Binary Parser: Prototype Sanitization', describeOptions);
77

88
Promise.all([
99
test(async () => {
10-
const expected = [{}];
11-
expected[0].test = 2;
12-
13-
const [results] = await connection.query('SELECT 1+1 AS `test`');
14-
15-
assert.notDeepStrictEqual(
16-
results,
17-
expected,
18-
`Ensure "results" doesn't contain a standard object ({})`,
19-
);
20-
}),
21-
test(async () => {
22-
const expected = [Object.create(null)];
23-
expected[0].test = 2;
10+
const expected = [
11+
{
12+
test: 2,
13+
},
14+
];
2415

2516
const [results] = await connection.execute('SELECT 1+1 AS `test`');
2617

2718
assert.deepStrictEqual(results, expected, 'Ensure clean object "results"');
28-
assert.strictEqual(
29-
Object.getPrototypeOf(results[0]),
30-
null,
19+
assert.deepStrictEqual(
20+
JSON.stringify(Object.getPrototypeOf(results[0])),
21+
JSON.stringify({}),
3122
'Ensure clean properties in results items',
3223
);
3324
assert.strictEqual(
3425
typeof results[0].toString,
35-
'undefined',
26+
'function',
3627
'Re-check prototypes (manually) in results columns',
3728
);
3829
assert.strictEqual(

test/esm/unit/parsers/prototype-text-results.test.mjs

+10-19
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,27 @@ import { createConnection, describeOptions } from '../../../common.test.cjs';
33

44
const connection = createConnection().promise();
55

6-
describe('Text Parser: Prototype Sanitization', describeOptions);
6+
describe('Binary Parser: Prototype Sanitization', describeOptions);
77

88
Promise.all([
99
test(async () => {
10-
const expected = [{}];
11-
expected[0].test = 2;
12-
13-
const [results] = await connection.query('SELECT 1+1 AS `test`');
14-
15-
assert.notDeepStrictEqual(
16-
results,
17-
expected,
18-
`Ensure "results" doesn't contain a standard object ({})`,
19-
);
20-
}),
21-
test(async () => {
22-
const expected = [Object.create(null)];
23-
expected[0].test = 2;
10+
const expected = [
11+
{
12+
test: 2,
13+
},
14+
];
2415

2516
const [results] = await connection.query('SELECT 1+1 AS `test`');
2617

2718
assert.deepStrictEqual(results, expected, 'Ensure clean object "results"');
28-
assert.strictEqual(
29-
Object.getPrototypeOf(results[0]),
30-
null,
19+
assert.deepStrictEqual(
20+
JSON.stringify(Object.getPrototypeOf(results[0])),
21+
JSON.stringify({}),
3122
'Ensure clean properties in results items',
3223
);
3324
assert.strictEqual(
3425
typeof results[0].toString,
35-
'undefined',
26+
'function',
3627
'Re-check prototypes (manually) in results columns',
3728
);
3829
assert.strictEqual(

0 commit comments

Comments
 (0)