@@ -48,4 +48,191 @@ suite('security', function() {
4848 } , / U n s a f e k e y / ) ;
4949 assert . equal ( ( { } ) . polluted , undefined ) ;
5050 } ) ;
51+
52+ suite ( 'CVE-2026-1615: blocks code injection in filter/script expressions' , function ( ) {
53+ var data = { a : { } , b : [ 1 , 2 , 3 ] } ;
54+
55+ test ( 'rejects constructor access in filter expression' , function ( ) {
56+ assert . throws ( function ( ) {
57+ jp . query ( data , '$[?(@.constructor)]' ) ;
58+ } , / U n s a f e e x p r e s s i o n / ) ;
59+ } ) ;
60+
61+ test ( 'rejects constructor.constructor in filter expression' , function ( ) {
62+ assert . throws ( function ( ) {
63+ jp . query ( data , '$[?(@.constructor.constructor)]' ) ;
64+ } , / U n s a f e e x p r e s s i o n / ) ;
65+ } ) ;
66+
67+ test ( 'rejects chained constructor.constructor call: @.foo["constructor"]["constructor"](...)()' , function ( ) {
68+ assert . throws ( function ( ) {
69+ jp . query ( data , '$[?(@.foo["constructor"]["constructor"]("return process")())]' ) ;
70+ } , / U n s a f e e x p r e s s i o n / ) ;
71+ } ) ;
72+
73+ test ( 'rejects __proto__ access in filter expression' , function ( ) {
74+ assert . throws ( function ( ) {
75+ jp . query ( data , '$[?(@.__proto__)]' ) ;
76+ } , / U n s a f e e x p r e s s i o n / ) ;
77+ } ) ;
78+
79+ test ( 'rejects function call in filter expression' , function ( ) {
80+ assert . throws ( function ( ) {
81+ jp . query ( data , '$[?(process.exit(1))]' ) ;
82+ } , / U n s a f e e x p r e s s i o n / ) ;
83+ } ) ;
84+
85+ test ( 'rejects constructor access in script expression' , function ( ) {
86+ var scriptData = { a : [ 1 , 2 , 3 ] } ;
87+ assert . throws ( function ( ) {
88+ jp . query ( scriptData , '$[(@.constructor)]' ) ;
89+ } , / U n s a f e e x p r e s s i o n / ) ;
90+ } ) ;
91+
92+ test ( 'allows safe filter expressions' , function ( ) {
93+ var storeData = { store : { book : [ { price : 5 } , { price : 15 } ] } } ;
94+ var results = jp . query ( storeData , '$..book[?(@.price<10)]' ) ;
95+ assert . deepEqual ( results , [ { price : 5 } ] ) ;
96+ } ) ;
97+
98+ test ( 'allows safe script expressions' , function ( ) {
99+ var bookData = { book : [ { id : 1 } , { id : 2 } , { id : 3 } ] } ;
100+ var results = jp . nodes ( bookData , '$..book[(@.length-1)]' ) ;
101+ assert . deepEqual ( results [ 0 ] . value , { id : 3 } ) ;
102+ } ) ;
103+
104+ test ( 'rejects bracket notation constructor: @["constructor"]' , function ( ) {
105+ assert . throws ( function ( ) { jp . query ( data , '$[?(@["constructor"])]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
106+ } ) ;
107+
108+ test ( 'rejects bracket notation __proto__: @["__proto__"]' , function ( ) {
109+ assert . throws ( function ( ) { jp . query ( data , '$[?(@["__proto__"])]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
110+ } ) ;
111+
112+ test ( 'rejects bracket notation prototype: @["prototype"]' , function ( ) {
113+ assert . throws ( function ( ) { jp . query ( data , '$[?(@["prototype"])]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
114+ } ) ;
115+
116+ test ( 'rejects ObjectExpression with unsafe key: { "__proto__": @ }' , function ( ) {
117+ assert . throws ( function ( ) { jp . query ( data , '$[?({ "__proto__": @ })]' ) ; } , / U n s a f e e x p r e s s i o n | U n e x p e c t e d t o k e n / ) ;
118+ } ) ;
119+
120+ test ( 'rejects ObjectExpression with unsafe key: { "constructor": @ }' , function ( ) {
121+ assert . throws ( function ( ) { jp . query ( data , '$[?({ "constructor": @ })]' ) ; } , / U n s a f e e x p r e s s i o n | U n e x p e c t e d t o k e n / ) ;
122+ } ) ;
123+
124+ test ( 'rejects ObjectExpression with unsafe key: { "prototype": @ }' , function ( ) {
125+ assert . throws ( function ( ) { jp . query ( data , '$[?({ "prototype": @ })]' ) ; } , / U n s a f e e x p r e s s i o n | U n e x p e c t e d t o k e n / ) ;
126+ } ) ;
127+
128+ test ( 'rejects unicode escape constructor in bracket: @["\\u0063onstructor"]' , function ( ) {
129+ assert . throws ( function ( ) { jp . query ( data , '$[?(@["\\u0063onstructor"])]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
130+ } ) ;
131+
132+ test ( 'rejects unicode escape __proto__ in bracket' , function ( ) {
133+ var path = '$[?(@["\\u005f\\u005fproto\\u005f\\u005f"])]' ;
134+ assert . throws ( function ( ) { jp . query ( data , path ) ; } , / U n s a f e e x p r e s s i o n / ) ;
135+ } ) ;
136+
137+ test ( 'rejects IIFE: (function(){return 1})()' , function ( ) {
138+ assert . throws ( function ( ) { jp . query ( data , '$[?((function(){return 1})())]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
139+ } ) ;
140+
141+ test ( 'rejects direct function call: process.exit(1)' , function ( ) {
142+ assert . throws ( function ( ) { jp . query ( data , '$[?(process.exit(1))]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
143+ } ) ;
144+
145+ test ( 'rejects require() call' , function ( ) {
146+ assert . throws ( function ( ) { jp . query ( data , '$[?(require("fs"))]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
147+ } ) ;
148+
149+ test ( 'rejects eval() call' , function ( ) {
150+ assert . throws ( function ( ) { jp . query ( data , '$[?(eval("1"))]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
151+ } ) ;
152+
153+ test ( 'rejects globalThis / global identifier' , function ( ) {
154+ assert . throws ( function ( ) { jp . query ( data , '$[?(globalThis)]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
155+ assert . throws ( function ( ) { jp . query ( data , '$[?(global)]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
156+ } ) ;
157+
158+ test ( 'rejects NewExpression: new Function("return 1")()' , function ( ) {
159+ assert . throws ( function ( ) { jp . query ( data , '$[?(new Function("return 1")())]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
160+ } ) ;
161+
162+ test ( 'rejects JSFuck-style: [] ["filter"]["constructor"]' , function ( ) {
163+ assert . throws ( function ( ) { jp . query ( data , '$[?([]["filter"]["constructor"])]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
164+ } ) ;
165+
166+ test ( 'rejects JSFuck-style constructor call (no @)' , function ( ) {
167+ assert . throws ( function ( ) { jp . query ( data , '$[?([]["filter"]["constructor"]("return 1")())]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
168+ } ) ;
169+
170+ test ( 'rejects sequence expression: (1, process.exit)(1)' , function ( ) {
171+ assert . throws ( function ( ) { jp . query ( data , '$[?((1, process.exit)(1))]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
172+ } ) ;
173+
174+ test ( 'rejects method call on @: @.valueOf()' , function ( ) {
175+ assert . throws ( function ( ) { jp . query ( data , '$[?(@.valueOf())]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
176+ } ) ;
177+
178+ test ( 'rejects method call: @.toString()' , function ( ) {
179+ assert . throws ( function ( ) { jp . query ( data , '$[?(@.toString())]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
180+ } ) ;
181+
182+ test ( 'rejects template literal in computed: @[`constructor`]' , function ( ) {
183+ assert . throws ( function ( ) { jp . query ( data , '$[?(@[`constructor`])]' ) ; } , / U n s a f e e x p r e s s i o n | U n e x p e c t e d t o k e n | I L L E G A L / ) ;
184+ } ) ;
185+
186+ test ( 'rejects tagged template (code execution vector)' , function ( ) {
187+ assert . throws ( function ( ) { jp . query ( data , '$[?(String.raw`x`)]' ) ; } , / U n s a f e e x p r e s s i o n | U n e x p e c t e d t o k e n | I L L E G A L / ) ;
188+ } ) ;
189+
190+ test ( 'rejects ArrowFunctionExpression' , function ( ) {
191+ assert . throws ( function ( ) { jp . query ( data , '$[?((()=>1)())]' ) ; } , / U n s a f e e x p r e s s i o n | U n e x p e c t e d t o k e n / ) ;
192+ } ) ;
193+
194+ test ( 'rejects ThisExpression (this)' , function ( ) {
195+ assert . throws ( function ( ) { jp . query ( data , '$[?(this)]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
196+ } ) ;
197+
198+ test ( 'rejects script expression with constructor' , function ( ) {
199+ assert . throws ( function ( ) { jp . query ( data , '$[(@.constructor)]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
200+ } ) ;
201+
202+ test ( 'rejects script expression with call' , function ( ) {
203+ assert . throws ( function ( ) { jp . query ( data , '$[((function(){return 0})())]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
204+ } ) ;
205+
206+ test ( 'allows @.length (no call)' , function ( ) {
207+ var r = jp . query ( data , '$[?(@.length)]' ) ;
208+ assert . ok ( Array . isArray ( r ) ) ;
209+ } ) ;
210+
211+ test ( 'allows bracket with safe key: @["length"]' , function ( ) {
212+ var r = jp . query ( data , '$[?(@["length"])]' ) ;
213+ assert . ok ( Array . isArray ( r ) ) ;
214+ } ) ;
215+
216+ test ( 'allows @["@class"] (existing test pattern)' , function ( ) {
217+ var d = { DIV : [ { '@class' : 'value' , val : 5 } ] } ;
218+ var r = jp . query ( d , '$..DIV[?(@["@class"]=="value")]' ) ;
219+ assert . deepEqual ( r , d . DIV ) ;
220+ } ) ;
221+
222+ test ( 'rejects prototype access in filter' , function ( ) {
223+ assert . throws ( function ( ) { jp . query ( data , '$[?(@.prototype)]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
224+ } ) ;
225+
226+ test ( 'rejects comma/sequence that could hide call' , function ( ) {
227+ assert . throws ( function ( ) { jp . query ( data , '$[?((0, eval)("1"))]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
228+ } ) ;
229+
230+ test ( 'rejects AssignmentExpression' , function ( ) {
231+ assert . throws ( function ( ) { jp . query ( data , '$[?((x=1)==1)]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
232+ } ) ;
233+
234+ test ( 'rejects UpdateExpression (++, --)' , function ( ) {
235+ assert . throws ( function ( ) { jp . query ( data , '$[?(@.x++)]' ) ; } , / U n s a f e e x p r e s s i o n / ) ;
236+ } ) ;
237+ } ) ;
51238} ) ;
0 commit comments