Skip to content

Commit 9f07a61

Browse files
committed
1 parent 2e3e992 commit 9f07a61

File tree

1 file changed

+184
-184
lines changed

1 file changed

+184
-184
lines changed

spec/vulnerabilities.spec.js

Lines changed: 184 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,207 +1453,207 @@ describe('(GHSA-gqpp-xgvh-9h7h) SQL Injection via dot-notation sub-key name in I
14531453
const verify = await new Parse.Query('SubKeyTest').get(obj.id);
14541454
expect(verify.get('stats').counter).toBe(7);
14551455
});
1456+
});
14561457

1457-
describe('(GHSA-r2m8-pxm9-9c4g) Protected fields WHERE clause bypass via dot-notation on object-type fields', () => {
1458-
let obj;
1459-
1460-
beforeEach(async () => {
1461-
const schema = new Parse.Schema('SecretClass');
1462-
schema.addObject('secretObj');
1463-
schema.addString('publicField');
1464-
schema.setCLP({
1465-
find: { '*': true },
1466-
get: { '*': true },
1467-
create: { '*': true },
1468-
update: { '*': true },
1469-
delete: { '*': true },
1470-
addField: {},
1471-
protectedFields: { '*': ['secretObj'] },
1472-
});
1473-
await schema.save();
1458+
describe('(GHSA-r2m8-pxm9-9c4g) Protected fields WHERE clause bypass via dot-notation on object-type fields', () => {
1459+
let obj;
14741460

1475-
obj = new Parse.Object('SecretClass');
1476-
obj.set('secretObj', { apiKey: 'SENSITIVE_KEY_123', score: 42 });
1477-
obj.set('publicField', 'visible');
1478-
await obj.save(null, { useMasterKey: true });
1461+
beforeEach(async () => {
1462+
const schema = new Parse.Schema('SecretClass');
1463+
schema.addObject('secretObj');
1464+
schema.addString('publicField');
1465+
schema.setCLP({
1466+
find: { '*': true },
1467+
get: { '*': true },
1468+
create: { '*': true },
1469+
update: { '*': true },
1470+
delete: { '*': true },
1471+
addField: {},
1472+
protectedFields: { '*': ['secretObj'] },
14791473
});
1474+
await schema.save();
14801475

1481-
it('should deny query with dot-notation on protected field in where clause', async () => {
1482-
const res = await request({
1483-
method: 'GET',
1484-
url: `${Parse.serverURL}/classes/SecretClass`,
1485-
headers: {
1486-
'X-Parse-Application-Id': Parse.applicationId,
1487-
'X-Parse-REST-API-Key': 'rest',
1488-
},
1489-
qs: { where: JSON.stringify({ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }) },
1490-
}).catch(e => e);
1491-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1492-
expect(res.data.error).toBe('Permission denied');
1493-
});
1476+
obj = new Parse.Object('SecretClass');
1477+
obj.set('secretObj', { apiKey: 'SENSITIVE_KEY_123', score: 42 });
1478+
obj.set('publicField', 'visible');
1479+
await obj.save(null, { useMasterKey: true });
1480+
});
14941481

1495-
it('should deny query with dot-notation on protected field in $or', async () => {
1496-
const res = await request({
1497-
method: 'GET',
1498-
url: `${Parse.serverURL}/classes/SecretClass`,
1499-
headers: {
1500-
'X-Parse-Application-Id': Parse.applicationId,
1501-
'X-Parse-REST-API-Key': 'rest',
1502-
},
1503-
qs: {
1504-
where: JSON.stringify({
1505-
$or: [{ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, { 'secretObj.apiKey': 'other' }],
1506-
}),
1507-
},
1508-
}).catch(e => e);
1509-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1510-
expect(res.data.error).toBe('Permission denied');
1511-
});
1482+
it('should deny query with dot-notation on protected field in where clause', async () => {
1483+
const res = await request({
1484+
method: 'GET',
1485+
url: `${Parse.serverURL}/classes/SecretClass`,
1486+
headers: {
1487+
'X-Parse-Application-Id': Parse.applicationId,
1488+
'X-Parse-REST-API-Key': 'rest',
1489+
},
1490+
qs: { where: JSON.stringify({ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }) },
1491+
}).catch(e => e);
1492+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1493+
expect(res.data.error).toBe('Permission denied');
1494+
});
15121495

1513-
it('should deny query with dot-notation on protected field in $and', async () => {
1514-
const res = await request({
1515-
method: 'GET',
1516-
url: `${Parse.serverURL}/classes/SecretClass`,
1517-
headers: {
1518-
'X-Parse-Application-Id': Parse.applicationId,
1519-
'X-Parse-REST-API-Key': 'rest',
1520-
},
1521-
qs: {
1522-
where: JSON.stringify({
1523-
$and: [{ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, { publicField: 'visible' }],
1524-
}),
1525-
},
1526-
}).catch(e => e);
1527-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1528-
expect(res.data.error).toBe('Permission denied');
1529-
});
1496+
it('should deny query with dot-notation on protected field in $or', async () => {
1497+
const res = await request({
1498+
method: 'GET',
1499+
url: `${Parse.serverURL}/classes/SecretClass`,
1500+
headers: {
1501+
'X-Parse-Application-Id': Parse.applicationId,
1502+
'X-Parse-REST-API-Key': 'rest',
1503+
},
1504+
qs: {
1505+
where: JSON.stringify({
1506+
$or: [{ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, { 'secretObj.apiKey': 'other' }],
1507+
}),
1508+
},
1509+
}).catch(e => e);
1510+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1511+
expect(res.data.error).toBe('Permission denied');
1512+
});
15301513

1531-
it('should deny query with dot-notation on protected field in $nor', async () => {
1532-
const res = await request({
1533-
method: 'GET',
1534-
url: `${Parse.serverURL}/classes/SecretClass`,
1535-
headers: {
1536-
'X-Parse-Application-Id': Parse.applicationId,
1537-
'X-Parse-REST-API-Key': 'rest',
1538-
},
1539-
qs: {
1540-
where: JSON.stringify({
1541-
$nor: [{ 'secretObj.apiKey': 'WRONG' }],
1542-
}),
1543-
},
1544-
}).catch(e => e);
1545-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1546-
expect(res.data.error).toBe('Permission denied');
1547-
});
1514+
it('should deny query with dot-notation on protected field in $and', async () => {
1515+
const res = await request({
1516+
method: 'GET',
1517+
url: `${Parse.serverURL}/classes/SecretClass`,
1518+
headers: {
1519+
'X-Parse-Application-Id': Parse.applicationId,
1520+
'X-Parse-REST-API-Key': 'rest',
1521+
},
1522+
qs: {
1523+
where: JSON.stringify({
1524+
$and: [{ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, { publicField: 'visible' }],
1525+
}),
1526+
},
1527+
}).catch(e => e);
1528+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1529+
expect(res.data.error).toBe('Permission denied');
1530+
});
15481531

1549-
it('should deny query with deeply nested dot-notation on protected field', async () => {
1550-
const res = await request({
1551-
method: 'GET',
1552-
url: `${Parse.serverURL}/classes/SecretClass`,
1553-
headers: {
1554-
'X-Parse-Application-Id': Parse.applicationId,
1555-
'X-Parse-REST-API-Key': 'rest',
1556-
},
1557-
qs: { where: JSON.stringify({ 'secretObj.nested.deep.key': 'value' }) },
1558-
}).catch(e => e);
1559-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1560-
expect(res.data.error).toBe('Permission denied');
1561-
});
1532+
it('should deny query with dot-notation on protected field in $nor', async () => {
1533+
const res = await request({
1534+
method: 'GET',
1535+
url: `${Parse.serverURL}/classes/SecretClass`,
1536+
headers: {
1537+
'X-Parse-Application-Id': Parse.applicationId,
1538+
'X-Parse-REST-API-Key': 'rest',
1539+
},
1540+
qs: {
1541+
where: JSON.stringify({
1542+
$nor: [{ 'secretObj.apiKey': 'WRONG' }],
1543+
}),
1544+
},
1545+
}).catch(e => e);
1546+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1547+
expect(res.data.error).toBe('Permission denied');
1548+
});
15621549

1563-
it('should deny sort on protected field via dot-notation', async () => {
1564-
const res = await request({
1565-
method: 'GET',
1566-
url: `${Parse.serverURL}/classes/SecretClass`,
1567-
headers: {
1568-
'X-Parse-Application-Id': Parse.applicationId,
1569-
'X-Parse-REST-API-Key': 'rest',
1570-
},
1571-
qs: { order: 'secretObj.score' },
1572-
}).catch(e => e);
1573-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1574-
expect(res.data.error).toBe('Permission denied');
1575-
});
1550+
it('should deny query with deeply nested dot-notation on protected field', async () => {
1551+
const res = await request({
1552+
method: 'GET',
1553+
url: `${Parse.serverURL}/classes/SecretClass`,
1554+
headers: {
1555+
'X-Parse-Application-Id': Parse.applicationId,
1556+
'X-Parse-REST-API-Key': 'rest',
1557+
},
1558+
qs: { where: JSON.stringify({ 'secretObj.nested.deep.key': 'value' }) },
1559+
}).catch(e => e);
1560+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1561+
expect(res.data.error).toBe('Permission denied');
1562+
});
15761563

1577-
it('should deny sort on protected field directly', async () => {
1578-
const res = await request({
1579-
method: 'GET',
1580-
url: `${Parse.serverURL}/classes/SecretClass`,
1581-
headers: {
1582-
'X-Parse-Application-Id': Parse.applicationId,
1583-
'X-Parse-REST-API-Key': 'rest',
1584-
},
1585-
qs: { order: 'secretObj' },
1586-
}).catch(e => e);
1587-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1588-
expect(res.data.error).toBe('Permission denied');
1589-
});
1564+
it('should deny sort on protected field via dot-notation', async () => {
1565+
const res = await request({
1566+
method: 'GET',
1567+
url: `${Parse.serverURL}/classes/SecretClass`,
1568+
headers: {
1569+
'X-Parse-Application-Id': Parse.applicationId,
1570+
'X-Parse-REST-API-Key': 'rest',
1571+
},
1572+
qs: { order: 'secretObj.score' },
1573+
}).catch(e => e);
1574+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1575+
expect(res.data.error).toBe('Permission denied');
1576+
});
15901577

1591-
it('should deny descending sort on protected field via dot-notation', async () => {
1592-
const res = await request({
1593-
method: 'GET',
1594-
url: `${Parse.serverURL}/classes/SecretClass`,
1595-
headers: {
1596-
'X-Parse-Application-Id': Parse.applicationId,
1597-
'X-Parse-REST-API-Key': 'rest',
1598-
},
1599-
qs: { order: '-secretObj.score' },
1600-
}).catch(e => e);
1601-
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1602-
expect(res.data.error).toBe('Permission denied');
1603-
});
1578+
it('should deny sort on protected field directly', async () => {
1579+
const res = await request({
1580+
method: 'GET',
1581+
url: `${Parse.serverURL}/classes/SecretClass`,
1582+
headers: {
1583+
'X-Parse-Application-Id': Parse.applicationId,
1584+
'X-Parse-REST-API-Key': 'rest',
1585+
},
1586+
qs: { order: 'secretObj' },
1587+
}).catch(e => e);
1588+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1589+
expect(res.data.error).toBe('Permission denied');
1590+
});
16041591

1605-
it('should still allow queries on non-protected fields', async () => {
1606-
const response = await request({
1607-
method: 'GET',
1608-
url: `${Parse.serverURL}/classes/SecretClass`,
1609-
headers: {
1610-
'X-Parse-Application-Id': Parse.applicationId,
1611-
'X-Parse-REST-API-Key': 'rest',
1612-
},
1613-
qs: { where: JSON.stringify({ publicField: 'visible' }) },
1614-
});
1615-
expect(response.data.results.length).toBe(1);
1616-
expect(response.data.results[0].publicField).toBe('visible');
1617-
expect(response.data.results[0].secretObj).toBeUndefined();
1618-
});
1592+
it('should deny descending sort on protected field via dot-notation', async () => {
1593+
const res = await request({
1594+
method: 'GET',
1595+
url: `${Parse.serverURL}/classes/SecretClass`,
1596+
headers: {
1597+
'X-Parse-Application-Id': Parse.applicationId,
1598+
'X-Parse-REST-API-Key': 'rest',
1599+
},
1600+
qs: { order: '-secretObj.score' },
1601+
}).catch(e => e);
1602+
expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
1603+
expect(res.data.error).toBe('Permission denied');
1604+
});
16191605

1620-
it('should still allow sort on non-protected fields', async () => {
1621-
const response = await request({
1622-
method: 'GET',
1623-
url: `${Parse.serverURL}/classes/SecretClass`,
1624-
headers: {
1625-
'X-Parse-Application-Id': Parse.applicationId,
1626-
'X-Parse-REST-API-Key': 'rest',
1627-
},
1628-
qs: { order: 'publicField' },
1629-
});
1630-
expect(response.data.results.length).toBe(1);
1606+
it('should still allow queries on non-protected fields', async () => {
1607+
const response = await request({
1608+
method: 'GET',
1609+
url: `${Parse.serverURL}/classes/SecretClass`,
1610+
headers: {
1611+
'X-Parse-Application-Id': Parse.applicationId,
1612+
'X-Parse-REST-API-Key': 'rest',
1613+
},
1614+
qs: { where: JSON.stringify({ publicField: 'visible' }) },
16311615
});
1616+
expect(response.data.results.length).toBe(1);
1617+
expect(response.data.results[0].publicField).toBe('visible');
1618+
expect(response.data.results[0].secretObj).toBeUndefined();
1619+
});
16321620

1633-
it('should still allow master key to query protected fields with dot-notation', async () => {
1634-
const response = await request({
1635-
method: 'GET',
1636-
url: `${Parse.serverURL}/classes/SecretClass`,
1637-
headers: {
1638-
'X-Parse-Application-Id': Parse.applicationId,
1639-
'X-Parse-Master-Key': Parse.masterKey,
1640-
},
1641-
qs: { where: JSON.stringify({ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }) },
1642-
});
1643-
expect(response.data.results.length).toBe(1);
1621+
it('should still allow sort on non-protected fields', async () => {
1622+
const response = await request({
1623+
method: 'GET',
1624+
url: `${Parse.serverURL}/classes/SecretClass`,
1625+
headers: {
1626+
'X-Parse-Application-Id': Parse.applicationId,
1627+
'X-Parse-REST-API-Key': 'rest',
1628+
},
1629+
qs: { order: 'publicField' },
16441630
});
1631+
expect(response.data.results.length).toBe(1);
1632+
});
16451633

1646-
it('should still block direct query on protected field (existing behavior)', async () => {
1647-
const res = await request({
1648-
method: 'GET',
1649-
url: `${Parse.serverURL}/classes/SecretClass`,
1650-
headers: {
1651-
'X-Parse-Application-Id': Parse.applicationId,
1652-
'X-Parse-REST-API-Key': 'rest',
1653-
},
1654-
qs: { where: JSON.stringify({ secretObj: { apiKey: 'SENSITIVE_KEY_123' } }) },
1655-
}).catch(e => e);
1656-
expect(res.status).toBe(400);
1634+
it('should still allow master key to query protected fields with dot-notation', async () => {
1635+
const response = await request({
1636+
method: 'GET',
1637+
url: `${Parse.serverURL}/classes/SecretClass`,
1638+
headers: {
1639+
'X-Parse-Application-Id': Parse.applicationId,
1640+
'X-Parse-Master-Key': Parse.masterKey,
1641+
},
1642+
qs: { where: JSON.stringify({ 'secretObj.apiKey': 'SENSITIVE_KEY_123' }) },
16571643
});
1644+
expect(response.data.results.length).toBe(1);
1645+
});
1646+
1647+
it('should still block direct query on protected field (existing behavior)', async () => {
1648+
const res = await request({
1649+
method: 'GET',
1650+
url: `${Parse.serverURL}/classes/SecretClass`,
1651+
headers: {
1652+
'X-Parse-Application-Id': Parse.applicationId,
1653+
'X-Parse-REST-API-Key': 'rest',
1654+
},
1655+
qs: { where: JSON.stringify({ secretObj: { apiKey: 'SENSITIVE_KEY_123' } }) },
1656+
}).catch(e => e);
1657+
expect(res.status).toBe(400);
16581658
});
16591659
});

0 commit comments

Comments
 (0)