Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions spec/vulnerabilities.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1352,3 +1352,50 @@ describe('(GHSA-q3vj-96h2-gwvg) SQL Injection via Increment amount on nested Obj
expect(verify.get('stats').counter).toBe(8);
});
});

describe('(GHSA-gqpp-xgvh-9h7h) SQL Injection via dot-notation sub-key name in Increment operation', () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};

it_only_db('postgres')('does not execute injected SQL via single quote in sub-key name', async () => {
const obj = new Parse.Object('SubKeyTest');
obj.set('stats', { counter: 0 });
await obj.save();

const start = Date.now();
await request({
method: 'PUT',
url: `http://localhost:8378/1/classes/SubKeyTest/${obj.id}`,
headers,
body: JSON.stringify({
"stats.x' || (SELECT pg_sleep(3))::text || '": { __op: 'Increment', amount: 1 },
}),
}).catch(() => {});
const elapsed = Date.now() - start;

// If injection succeeded, query would take >= 3 seconds
expect(elapsed).toBeLessThan(3000);
});

it_only_db('postgres')('allows valid Increment on nested object field with normal sub-key', async () => {
const obj = new Parse.Object('SubKeyTest');
obj.set('stats', { counter: 5 });
await obj.save();

const response = await request({
method: 'PUT',
url: `http://localhost:8378/1/classes/SubKeyTest/${obj.id}`,
headers,
body: JSON.stringify({
'stats.counter': { __op: 'Increment', amount: 2 },
}),
});

expect(response.status).toBe(200);
const verify = await new Parse.Query('SubKeyTest').get(obj.id);
expect(verify.get('stats').counter).toBe(7);
});
});
7 changes: 5 additions & 2 deletions src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ const handleDotFields = object => {
return object;
};

const escapeSqlString = value => value.replace(/'/g, "''");

Comment thread
mtrezza marked this conversation as resolved.
const transformDotFieldToComponents = fieldName => {
return fieldName.split('.').map((cmpt, index) => {
if (index === 0) {
Expand All @@ -216,7 +218,7 @@ const transformDotFieldToComponents = fieldName => {
if (isArrayIndex(cmpt)) {
return Number(cmpt);
} else {
return `'${cmpt.replace(/'/g, "''")}'`;
return `'${escapeSqlString(cmpt)}'`;
}
});
};
Expand Down Expand Up @@ -1747,7 +1749,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
}
incrementValues.push(amount);
const amountIndex = index + incrementValues.length;
return `CONCAT('{"${c}":', COALESCE($${index}:name->>'${c}','0')::int + $${amountIndex}, '}')::jsonb`;
const safeName = escapeSqlString(c);
return `CONCAT('{"${safeName}":', COALESCE($${index}:name->>'${safeName}','0')::int + $${amountIndex}, '}')::jsonb`;
})
.join(' || ');
// Strip the keys
Expand Down
Loading