@@ -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