source: frontend/node_modules/jsonpath/test/security.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 9.0 KB
RevLine 
[9af201e]1var assert = require('assert');
2var jp = require('../');
3
4suite('security', function() {
5
6 var cleanup = function() {
7 if (Object.prototype.polluted) {
8 delete Object.prototype.polluted;
9 }
10 };
11
12 teardown(function() {
13 cleanup();
14 });
15
16 test('blocks prototype pollution via value()', function() {
17 cleanup();
18 var data = {};
19 assert.throws(function() {
20 jp.value(data, '$.__proto__.polluted', 'yes');
21 }, /Unsafe key/);
22 assert.equal(({}).polluted, undefined);
23 });
24
25 test('blocks prototype pollution via apply()', function() {
26 cleanup();
27 var data = { safe: { ok: true } };
28 assert.throws(function() {
29 jp.apply(data, '$.__proto__.polluted', function() { return 'yes'; });
30 }, /Unsafe key/);
31 assert.equal(({}).polluted, undefined);
32 });
33
34 test('blocks unsafe subscript access', function() {
35 cleanup();
36 var data = {};
37 assert.throws(function() {
38 jp.query(data, '$["__proto__"]["polluted"]');
39 }, /Unsafe key/);
40 assert.equal(({}).polluted, undefined);
41 });
42
43 test('blocks unsafe union access', function() {
44 cleanup();
45 var data = { safe: 1 };
46 assert.throws(function() {
47 jp.nodes(data, "$['safe','__proto__']");
48 }, /Unsafe key/);
49 assert.equal(({}).polluted, undefined);
50 });
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 }, /Unsafe expression/);
59 });
60
61 test('rejects constructor.constructor in filter expression', function() {
62 assert.throws(function() {
63 jp.query(data, '$[?(@.constructor.constructor)]');
64 }, /Unsafe expression/);
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 }, /Unsafe expression/);
71 });
72
73 test('rejects __proto__ access in filter expression', function() {
74 assert.throws(function() {
75 jp.query(data, '$[?(@.__proto__)]');
76 }, /Unsafe expression/);
77 });
78
79 test('rejects function call in filter expression', function() {
80 assert.throws(function() {
81 jp.query(data, '$[?(process.exit(1))]');
82 }, /Unsafe expression/);
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 }, /Unsafe expression/);
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"])]'); }, /Unsafe expression/);
106 });
107
108 test('rejects bracket notation __proto__: @["__proto__"]', function() {
109 assert.throws(function() { jp.query(data, '$[?(@["__proto__"])]'); }, /Unsafe expression/);
110 });
111
112 test('rejects bracket notation prototype: @["prototype"]', function() {
113 assert.throws(function() { jp.query(data, '$[?(@["prototype"])]'); }, /Unsafe expression/);
114 });
115
116 test('rejects ObjectExpression with unsafe key: { "__proto__": @ }', function() {
117 assert.throws(function() { jp.query(data, '$[?({ "__proto__": @ })]'); }, /Unsafe expression|Unexpected token/);
118 });
119
120 test('rejects ObjectExpression with unsafe key: { "constructor": @ }', function() {
121 assert.throws(function() { jp.query(data, '$[?({ "constructor": @ })]'); }, /Unsafe expression|Unexpected token/);
122 });
123
124 test('rejects ObjectExpression with unsafe key: { "prototype": @ }', function() {
125 assert.throws(function() { jp.query(data, '$[?({ "prototype": @ })]'); }, /Unsafe expression|Unexpected token/);
126 });
127
128 test('rejects unicode escape constructor in bracket: @["\\u0063onstructor"]', function() {
129 assert.throws(function() { jp.query(data, '$[?(@["\\u0063onstructor"])]'); }, /Unsafe expression/);
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); }, /Unsafe expression/);
135 });
136
137 test('rejects IIFE: (function(){return 1})()', function() {
138 assert.throws(function() { jp.query(data, '$[?((function(){return 1})())]'); }, /Unsafe expression/);
139 });
140
141 test('rejects direct function call: process.exit(1)', function() {
142 assert.throws(function() { jp.query(data, '$[?(process.exit(1))]'); }, /Unsafe expression/);
143 });
144
145 test('rejects require() call', function() {
146 assert.throws(function() { jp.query(data, '$[?(require("fs"))]'); }, /Unsafe expression/);
147 });
148
149 test('rejects eval() call', function() {
150 assert.throws(function() { jp.query(data, '$[?(eval("1"))]'); }, /Unsafe expression/);
151 });
152
153 test('rejects globalThis / global identifier', function() {
154 assert.throws(function() { jp.query(data, '$[?(globalThis)]'); }, /Unsafe expression/);
155 assert.throws(function() { jp.query(data, '$[?(global)]'); }, /Unsafe expression/);
156 });
157
158 test('rejects NewExpression: new Function("return 1")()', function() {
159 assert.throws(function() { jp.query(data, '$[?(new Function("return 1")())]'); }, /Unsafe expression/);
160 });
161
162 test('rejects JSFuck-style: [] ["filter"]["constructor"]', function() {
163 assert.throws(function() { jp.query(data, '$[?([]["filter"]["constructor"])]'); }, /Unsafe expression/);
164 });
165
166 test('rejects JSFuck-style constructor call (no @)', function() {
167 assert.throws(function() { jp.query(data, '$[?([]["filter"]["constructor"]("return 1")())]'); }, /Unsafe expression/);
168 });
169
170 test('rejects sequence expression: (1, process.exit)(1)', function() {
171 assert.throws(function() { jp.query(data, '$[?((1, process.exit)(1))]'); }, /Unsafe expression/);
172 });
173
174 test('rejects method call on @: @.valueOf()', function() {
175 assert.throws(function() { jp.query(data, '$[?(@.valueOf())]'); }, /Unsafe expression/);
176 });
177
178 test('rejects method call: @.toString()', function() {
179 assert.throws(function() { jp.query(data, '$[?(@.toString())]'); }, /Unsafe expression/);
180 });
181
182 test('rejects template literal in computed: @[`constructor`]', function() {
183 assert.throws(function() { jp.query(data, '$[?(@[`constructor`])]'); }, /Unsafe expression|Unexpected token|ILLEGAL/);
184 });
185
186 test('rejects tagged template (code execution vector)', function() {
187 assert.throws(function() { jp.query(data, '$[?(String.raw`x`)]'); }, /Unsafe expression|Unexpected token|ILLEGAL/);
188 });
189
190 test('rejects ArrowFunctionExpression', function() {
191 assert.throws(function() { jp.query(data, '$[?((()=>1)())]'); }, /Unsafe expression|Unexpected token/);
192 });
193
194 test('rejects ThisExpression (this)', function() {
195 assert.throws(function() { jp.query(data, '$[?(this)]'); }, /Unsafe expression/);
196 });
197
198 test('rejects script expression with constructor', function() {
199 assert.throws(function() { jp.query(data, '$[(@.constructor)]'); }, /Unsafe expression/);
200 });
201
202 test('rejects script expression with call', function() {
203 assert.throws(function() { jp.query(data, '$[((function(){return 0})())]'); }, /Unsafe expression/);
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)]'); }, /Unsafe expression/);
224 });
225
226 test('rejects comma/sequence that could hide call', function() {
227 assert.throws(function() { jp.query(data, '$[?((0, eval)("1"))]'); }, /Unsafe expression/);
228 });
229
230 test('rejects AssignmentExpression', function() {
231 assert.throws(function() { jp.query(data, '$[?((x=1)==1)]'); }, /Unsafe expression/);
232 });
233
234 test('rejects UpdateExpression (++, --)', function() {
235 assert.throws(function() { jp.query(data, '$[?(@.x++)]'); }, /Unsafe expression/);
236 });
237 });
238});
Note: See TracBrowser for help on using the repository browser.