@yemi33/minions 0.1.1949 → 0.1.1950
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/engine/copilot-models.json +1 -1
- package/engine/shared.js +35 -16
- package/package.json +1 -1
package/engine/shared.js
CHANGED
|
@@ -2335,6 +2335,11 @@ function sanitizePath(file, baseDir) {
|
|
|
2335
2335
|
|
|
2336
2336
|
const _DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
2337
2337
|
|
|
2338
|
+
// DoS caps for the recursive walk. Reaching either limit is treated as
|
|
2339
|
+
// "dangerous" — see hasDangerousKey() for rationale (P-e8b1d3a6 / F6).
|
|
2340
|
+
const HAS_DANGEROUS_KEY_MAX_DEPTH = 64;
|
|
2341
|
+
const HAS_DANGEROUS_KEY_MAX_NODES = 10000;
|
|
2342
|
+
|
|
2338
2343
|
/**
|
|
2339
2344
|
* Detect the presence of prototype-pollution attack keys in a JSON-decoded payload.
|
|
2340
2345
|
*
|
|
@@ -2345,43 +2350,55 @@ const _DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
|
2345
2350
|
* but downstream code that shallow-merges the payload into a target object
|
|
2346
2351
|
* CAN elevate it into a prototype write.
|
|
2347
2352
|
*
|
|
2348
|
-
* Contract is **rejection, not sanitization**: we
|
|
2349
|
-
*
|
|
2350
|
-
*
|
|
2353
|
+
* Contract is **rejection, not sanitization**: we walk the full tree and
|
|
2354
|
+
* return a boolean. To avoid DoS via deeply-nested or pathologically-wide
|
|
2355
|
+
* inputs (stack exhaustion, unbounded CPU), the walk is capped:
|
|
2356
|
+
*
|
|
2357
|
+
* - Recursion depth > {@link HAS_DANGEROUS_KEY_MAX_DEPTH} (64) → return true.
|
|
2358
|
+
* - Total visited nodes > {@link HAS_DANGEROUS_KEY_MAX_NODES} (10000) → return true.
|
|
2359
|
+
* Every recursive call counts (including primitive leaves and array elements),
|
|
2360
|
+
* so a pathologically wide payload trips the cap even if no key is dangerous.
|
|
2361
|
+
*
|
|
2362
|
+
* Returning `true` on overflow is the safe-by-default policy: the sole
|
|
2363
|
+
* caller (dashboard.js request-body guard) rejects on `true`, so degrading
|
|
2364
|
+
* to rejection is conservative. The caps also protect against cyclic
|
|
2365
|
+
* objects, since each visit increments the node counter.
|
|
2351
2366
|
*
|
|
2352
2367
|
* - Null / undefined / primitives → false.
|
|
2353
2368
|
* - Arrays are transparent: each element is checked at the same depth as the
|
|
2354
|
-
* array itself (an array does NOT consume a depth level)
|
|
2355
|
-
*
|
|
2356
|
-
* are intentionally NOT flagged.
|
|
2369
|
+
* array itself (an array does NOT consume a depth level), but each element
|
|
2370
|
+
* visit increments the node counter.
|
|
2357
2371
|
* - Never mutates the input.
|
|
2358
2372
|
*
|
|
2359
2373
|
* @param {*} obj - any JSON-decoded value
|
|
2360
2374
|
* @param {number} [_depth=0] - internal recursion counter; do not pass externally
|
|
2361
|
-
* @
|
|
2375
|
+
* @param {{n:number}} [_nodeCount={n:0}] - internal mutable node counter; do not pass externally
|
|
2376
|
+
* @returns {boolean} true if any forbidden key is present, or if the depth/node cap is exceeded
|
|
2362
2377
|
*/
|
|
2363
|
-
function hasDangerousKey(obj, _depth = 0) {
|
|
2378
|
+
function hasDangerousKey(obj, _depth = 0, _nodeCount = { n: 0 }) {
|
|
2379
|
+
// DoS caps: count EVERY visited node (including primitive leaves and
|
|
2380
|
+
// array elements per F6 contract), and bail conservatively on either cap.
|
|
2381
|
+
// Returning `true` on overflow is the safe-by-default policy since the
|
|
2382
|
+
// sole caller (dashboard.js request-body guard) rejects on `true`.
|
|
2383
|
+
if (++_nodeCount.n > HAS_DANGEROUS_KEY_MAX_NODES) return true;
|
|
2384
|
+
if (_depth > HAS_DANGEROUS_KEY_MAX_DEPTH) return true;
|
|
2385
|
+
|
|
2364
2386
|
if (obj === null || obj === undefined || typeof obj !== 'object') return false;
|
|
2365
2387
|
|
|
2366
2388
|
// Arrays are transparent — preserve depth when recursing into elements.
|
|
2367
2389
|
if (Array.isArray(obj)) {
|
|
2368
2390
|
for (const elt of obj) {
|
|
2369
|
-
if (hasDangerousKey(elt, _depth)) return true;
|
|
2391
|
+
if (hasDangerousKey(elt, _depth, _nodeCount)) return true;
|
|
2370
2392
|
}
|
|
2371
2393
|
return false;
|
|
2372
2394
|
}
|
|
2373
2395
|
|
|
2374
|
-
// Object: check own keys at the current depth.
|
|
2396
|
+
// Object: check own keys at the current depth, then recurse into values.
|
|
2375
2397
|
for (const key of Object.keys(obj)) {
|
|
2376
2398
|
if (_DANGEROUS_KEYS.has(key)) return true;
|
|
2377
2399
|
}
|
|
2378
|
-
|
|
2379
|
-
// Stop after one level of object nesting. Deeper recursion is an explicit
|
|
2380
|
-
// non-goal (see DoS note in the header).
|
|
2381
|
-
if (_depth >= 1) return false;
|
|
2382
|
-
|
|
2383
2400
|
for (const v of Object.values(obj)) {
|
|
2384
|
-
if (hasDangerousKey(v, _depth + 1)) return true;
|
|
2401
|
+
if (hasDangerousKey(v, _depth + 1, _nodeCount)) return true;
|
|
2385
2402
|
}
|
|
2386
2403
|
return false;
|
|
2387
2404
|
}
|
|
@@ -3822,6 +3839,8 @@ module.exports = {
|
|
|
3822
3839
|
isAllowedOrigin,
|
|
3823
3840
|
buildSecurityHeaders,
|
|
3824
3841
|
hasDangerousKey,
|
|
3842
|
+
HAS_DANGEROUS_KEY_MAX_DEPTH,
|
|
3843
|
+
HAS_DANGEROUS_KEY_MAX_NODES,
|
|
3825
3844
|
validateProjectName,
|
|
3826
3845
|
validateProjectPath,
|
|
3827
3846
|
validatePid,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1950",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|