@goodandready/dsh-moa 0.2.49 → 0.2.50

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/lib/history.js CHANGED
@@ -53,7 +53,7 @@ export function invalidateHistoryCache() {
53
53
 
54
54
  function getOrInitCache(filePath, stat) {
55
55
  if (!_historyCache || _historyCache.filePath !== filePath || _historyCache.mtimeMs !== stat.mtimeMs || _historyCache.size !== stat.size) {
56
- _historyCache = { filePath, mtimeMs: stat.mtimeMs, size: stat.size, leaderboards: new Map(), runsById: new Map(), totalCount: null }
56
+ _historyCache = { filePath, mtimeMs: stat.mtimeMs, size: stat.size, leaderboards: new Map(), runsById: new Map(), totalCount: null, fullyIndexed: false }
57
57
  }
58
58
  if (!_historyCache.runsById) _historyCache.runsById = new Map()
59
59
  return _historyCache
@@ -473,19 +473,20 @@ export function getMoaRunById(runId, filePath = getDefaultHistoryFile()) {
473
473
  const stat = fs.statSync(filePath)
474
474
  const cache = getOrInitCache(filePath, stat)
475
475
  if (cache.runsById.has(runId)) return cache.runsById.get(runId)
476
+ if (cache.fullyIndexed) return null
476
477
 
477
478
  if (stat.size <= 65536) {
478
479
  const raw = fs.readFileSync(filePath, 'utf8')
479
- const lines = raw.trim().split('\n').filter(Boolean)
480
+ const lines = raw.trim().split(String.fromCharCode(10)).filter(Boolean)
480
481
  for (let i = lines.length - 1; i >= 0; i--) {
481
482
  try {
482
483
  const item = JSON.parse(lines[i])
483
- if (item?.id) {
484
- cache.runsById.set(item.id, item)
485
- if (item.id === runId) return item
486
- }
484
+ if (item?.id) cache.runsById.set(item.id, item)
487
485
  } catch { /* skip corrupt */ }
488
486
  }
487
+ cache.fullyIndexed = true
488
+ if (cache.runsById.has(runId)) return cache.runsById.get(runId)
489
+ cache.runsById.set(runId, null)
489
490
  return null
490
491
  }
491
492
 
@@ -501,16 +502,16 @@ export function getMoaRunById(runId, filePath = getDefaultHistoryFile()) {
501
502
  }
502
503
 
503
504
  const raw = fs.readFileSync(filePath, 'utf8')
504
- const lines = raw.trim().split('\n').filter(Boolean)
505
+ const lines = raw.trim().split(String.fromCharCode(10)).filter(Boolean)
505
506
  for (let i = lines.length - 1; i >= 0; i--) {
506
507
  try {
507
508
  const item = JSON.parse(lines[i])
508
- if (item?.id) {
509
- cache.runsById.set(item.id, item)
510
- if (item.id === runId) return item
511
- }
509
+ if (item?.id) cache.runsById.set(item.id, item)
512
510
  } catch { /* skip corrupt */ }
513
511
  }
512
+ cache.fullyIndexed = true
513
+ if (cache.runsById.has(runId)) return cache.runsById.get(runId)
514
+ cache.runsById.set(runId, null)
514
515
  return null
515
516
  } catch {
516
517
  return null
@@ -45,21 +45,12 @@ export function resolveCandidateDir(baseDir, candidateIndex, options = {}) {
45
45
 
46
46
  export function decodeEscapedStrings(str) {
47
47
  if (!str || typeof str !== 'string') return ''
48
- let res = str.replace(/\\+/g, '\\').replace(/\\(?:([0-7]{1,3})|x([0-9a-fA-F]{2}))/g, (m, oct, hex) =>
48
+ return str.replace(/\\+/g, '\\').replace(/\\(?:([0-7]{1,3})|x([0-9a-fA-F]{2}))/g, (m, oct, hex) =>
49
49
  oct ? String.fromCharCode(parseInt(oct, 8)) : hex ? String.fromCharCode(parseInt(hex, 16)) : m
50
50
  )
51
- return res.replace(/(?<![A-Za-z0-9+/=])(?:(?:[A-Za-z0-9+/]{4}){2,}|(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=))(?![A-Za-z0-9+/=])/g, (m) => {
52
- try {
53
- const dec = Buffer.from(m, 'base64').toString('utf8')
54
- return /^[\x20-\x7E\r\n\t]+$/.test(dec) ? dec : m
55
- } catch (err) {
56
- logger.debug('[dsh-moa:testGate] base64 decode skip:', err?.message || err)
57
- return m
58
- }
59
- })
60
51
  }
61
52
 
62
- const COMPUTED_SHELL_EVASION = /(?:base64\s+(?:-d|--decode|-D)[^|\n;]*\|\s*(?:ba|z|da)?sh|\beval\s+["\']?\$|\bxxd\s+-r[^|\n;]*\|\s*(?:ba|z|da)?sh|\bprintf\s+[^|\n]+\|\s*(?:ba|z|da)?sh|\bopenssl\s+enc\s+-d[^|\n;]*\|\s*(?:ba|z|da)?sh)/i
53
+ const COMPUTED_SHELL_EVASION = /(?:base64\s+(?:-d|--decode|-D)[^|\n;]*\|\s*(?:ba|z|da)?sh|\beval\s+["']?\$|\bxxd\s+-r[^|\n;]*\|\s*(?:ba|z|da)?sh|\bprintf\s+[^|\n]+\|\s*(?:ba|z|da)?sh|\bopenssl\s+enc\s+-d[^|\n;]*\|\s*(?:ba|z|da)?sh|\bprintf\s+(?:%s|["']%s["'])\s+.*["'](?:\/|mnt|home|tmp|etc|var|usr|root|opt)["']|\$\(\s*(?:printf|echo)\s+.*["'](?:\/|mnt|home|tmp|etc|var|usr|root|opt)["']|\x60\s*(?:printf|echo)\s+.*["'](?:\/|mnt|home|tmp|etc|var|usr|root|opt)["']|\b(?:cat|head|tail|source|\.|touch|rm|cp|mv)\s+["']?\$[A-Za-z_]|>\s*["']?\$[A-Za-z_]|>>\s*["']?\$[A-Za-z_])/i
63
54
 
64
55
  const UNIX_ROOT_PREFIX = /^\/(?:home|root|etc|var|tmp|opt|mnt|srv|media|proc|sys|dev|usr|bin|lib|lib64|run\/(?:user\/\d+|systemd|lock|credentials|secrets|udev|initramfs|mount|shm|dbus|sshd|motd)|sbin|boot|Users\/(?:[a-zA-Z0-9_\-.]+)|Applications|Library|System|Volumes|private)(?:\/|$)/
65
56
 
@@ -82,6 +73,12 @@ function scanPathTokensForLeak(text, allowedRoots, executionDir) {
82
73
  }
83
74
  const rawLeak = checkText(text)
84
75
  if (rawLeak) return rawLeak
76
+ const segmented = text.match(/\bprintf\s+(?:%s|['"]%s['"])\s+((?:['"][^'"]*['"]\s*)+)/i)
77
+ if (segmented) {
78
+ const assembled = (segmented[1].match(/['"]([^'"]*)['"]/g) || []).map((s) => s.slice(1, -1)).join('')
79
+ const assembledLeak = checkText(assembled)
80
+ if (assembledLeak) return assembledLeak
81
+ }
85
82
  const decoded = decodeEscapedStrings(text)
86
83
  return decoded !== text ? checkText(decoded) : null
87
84
  }
@@ -166,6 +163,18 @@ export async function runCandidateTestGate({
166
163
  stderr = customRes?.stderr || ''
167
164
  } else {
168
165
  const allowedRoots = [executionDir, cwd, ...(candidateDir && candidateDir !== executionDir ? [candidateDir] : [])]
166
+ if (COMPUTED_SHELL_EVASION.test(resolvedCmd)) {
167
+ const durationMs = Date.now() - startTime
168
+ return {
169
+ candidateIndex,
170
+ passed: false,
171
+ exitCode: 1,
172
+ durationMs,
173
+ command: resolvedCmd,
174
+ output: 'Access denied: test command contains obfuscated or computed command execution',
175
+ summary: `FAIL (code 1, ${durationMs}ms)`,
176
+ }
177
+ }
169
178
  const cmdTokens = resolvedCmd.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []
170
179
 
171
180
  // 1. Check command tokens for absolute path leaks
@@ -310,7 +319,7 @@ function decodeEscaped(str) {
310
319
  return m;
311
320
  });
312
321
  }
313
- const COMPUTED_SHELL_EVASION = /(?:base64\\s+(?:-d|--decode|-D)[^|\\n;]*\\|\\s*(?:ba|z|da)?sh|\\beval\\s+["\']?\\$|\\bxxd\\s+-r[^|\\n;]*\\|\\s*(?:ba|z|da)?sh|\\bprintf\\s+[^|\\n]+\\|\\s*(?:ba|z|da)?sh|\\bopenssl\\s+enc\\s+-d[^|\\n;]*\\|\\s*(?:ba|z|da)?sh)/i;
322
+ const COMPUTED_SHELL_EVASION = /(?:base64\\s+(?:-d|--decode|-D)[^|\\n;]*\\|\\s*(?:ba|z|da)?sh|\\beval\\s+["']?\\$|\\bxxd\\s+-r[^|\\n;]*\\|\\s*(?:ba|z|da)?sh|\\bprintf\\s+[^|\\n]+\\|\\s*(?:ba|z|da)?sh|\\bopenssl\\s+enc\\s+-d[^|\\n;]*\\|\\s*(?:ba|z|da)?sh|\\bprintf\\s+(?:%s|["']%s["'])\\s+.*["'](?:\\/|mnt|home|tmp|etc|var|usr|root|opt)["']|\\$\\(\\s*(?:printf|echo)\\s+.*["'](?:\\/|mnt|home|tmp|etc|var|usr|root|opt)["']|\\x60\\s*(?:printf|echo)\\s+.*["'](?:\\/|mnt|home|tmp|etc|var|usr|root|opt)["']|\\b(?:cat|head|tail|source|\\.|touch|rm|cp|mv)\\s+["']?\\$[A-Za-z_]|>\\s*["']?\\$[A-Za-z_]|>>\\s*["']?\\$[A-Za-z_])/i;
314
323
  const UNIX_ROOT_PREFIX = /^\\/(?:home|root|etc|var|tmp|opt|mnt|srv|media|proc|sys|dev|usr|bin|lib|lib64|run\\/(?:user\\/\\d+|systemd|lock|credentials|secrets|udev|initramfs|mount|shm|dbus|sshd|motd)|sbin|boot|Users\\/(?:[a-zA-Z0-9_\\-.]+)|Applications|Library|System|Volumes|private)(?:\\/|$)/;
315
324
  function checkSinglePath(p) {
316
325
  if (!p || typeof p !== 'string') return true;
@@ -355,36 +364,12 @@ function guardArgs(cmd, args) {
355
364
  }
356
365
  }
357
366
  }
358
- const origSpawn = cp.spawn;
359
- cp.spawn = function(cmd, args, opts) {
360
- guardArgs(cmd, args);
361
- return origSpawn.apply(this, arguments);
362
- };
363
- const origSpawnSync = cp.spawnSync;
364
- cp.spawnSync = function(cmd, args, opts) {
365
- guardArgs(cmd, args);
366
- return origSpawnSync.apply(this, arguments);
367
- };
368
- const origExecFile = cp.execFile;
369
- cp.execFile = function(file, args, opts, cb) {
370
- guardArgs(file, args);
371
- return origExecFile.apply(this, arguments);
372
- };
373
- const origExecFileSync = cp.execFileSync;
374
- cp.execFileSync = function(file, args, opts) {
375
- guardArgs(file, args);
376
- return origExecFileSync.apply(this, arguments);
377
- };
378
- const origExec = cp.exec;
379
- cp.exec = function(cmd, opts, cb) {
380
- guardArgs(cmd, [cmd]);
381
- return origExec.apply(this, arguments);
382
- };
383
- const origExecSync = cp.execSync;
384
- cp.execSync = function(cmd, opts) {
385
- guardArgs(cmd, [cmd]);
386
- return origExecSync.apply(this, arguments);
387
- };
367
+ const origSpawn = cp.spawn; cp.spawn = function(cmd, args, opts) { guardArgs(cmd, args); return origSpawn.apply(this, arguments); };
368
+ const origSpawnSync = cp.spawnSync; cp.spawnSync = function(cmd, args, opts) { guardArgs(cmd, args); return origSpawnSync.apply(this, arguments); };
369
+ const origExecFile = cp.execFile; cp.execFile = function(file, args, opts, cb) { guardArgs(file, args); return origExecFile.apply(this, arguments); };
370
+ const origExecFileSync = cp.execFileSync; cp.execFileSync = function(file, args, opts) { guardArgs(file, args); return origExecFileSync.apply(this, arguments); };
371
+ const origExec = cp.exec; cp.exec = function(cmd, opts, cb) { guardArgs(cmd, [cmd]); return origExec.apply(this, arguments); };
372
+ const origExecSync = cp.execSync; cp.execSync = function(cmd, opts) { guardArgs(cmd, [cmd]); return origExecSync.apply(this, arguments); };
388
373
  const fsMod = require('node:fs');
389
374
  function guardFs(p, isWrite) {
390
375
  if (!p || typeof p !== 'string') return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-moa",
3
- "version": "0.2.49",
3
+ "version": "0.2.50",
4
4
  "description": "Mixture of Agents (MoA) plugin for DeepSeek Harness with /moa slash command",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",