@goodandready/dsh-moa 0.2.52 → 0.2.53

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.
@@ -403,9 +403,9 @@ export async function cleanMoaWorkspaces(baseDir, options = {}) {
403
403
  if (!baseDir) return
404
404
  const rawRunId = typeof options === 'string' ? options : options?.runId
405
405
  const safeRunId = rawRunId ? String(rawRunId).trim().replace(/[^a-zA-Z0-9_-]/g, '') : null
406
- const moaRoot = path.join(baseDir, '.moa')
406
+ const moaRoot = assertPathContained(baseDir, '.moa')
407
407
  if (safeRunId) {
408
- await bestEffort('cleanMoaWorkspaces runId', () => fs.rm(path.join(moaRoot, safeRunId), { recursive: true, force: true }))
408
+ await bestEffort('cleanMoaWorkspaces runId', () => fs.rm(assertPathContained(moaRoot, safeRunId), { recursive: true, force: true }))
409
409
  await bestEffort('clean empty moaRoot', async () => {
410
410
  if ((await fs.readdir(moaRoot)).length === 0) await fs.rmdir(moaRoot)
411
411
  })
package/lib/routes.js CHANGED
@@ -3,8 +3,9 @@
3
3
  */
4
4
 
5
5
  import path from 'node:path'
6
- import { getMoaHistory, getMoaLeaderboard, getMoaRunById, exportMoaHistory } from './history.js'
7
- import { promoteCandidateWorkspace, readCandidateFiles, computeLineDiff, isSafeRelativePath, isLoopbackUrl } from './file-workspace.js'
6
+ import os from 'node:os'
7
+ import { getMoaHistory, getMoaLeaderboard, getMoaRunById, exportMoaHistory, isTestEnvironment } from './history.js'
8
+ import { promoteCandidateWorkspace, readCandidateFiles, computeLineDiff, isSafeRelativePath, isLoopbackUrl, assertPathContained } from './file-workspace.js'
8
9
  import { isSafeWriteRequest } from './updater.js'
9
10
  import { runMoAPipeline } from './moa-runner.js'
10
11
  import { generateMoABenchmarkReport } from './moa-report.js'
@@ -30,7 +31,21 @@ function readBody(req) {
30
31
  })
31
32
  }
32
33
 
33
- export function registerMoaRoutes(ctx, { live, callLlm, liveCanvas, saveConfig, Config, plainConfig, webServerPort, timeMachineEngine }) {
34
+ function isAllowedWorkspace(targetCwd, allowedRoots) {
35
+ if (!targetCwd || typeof targetCwd !== 'string') return false
36
+ for (const root of allowedRoots) {
37
+ if (!root) continue
38
+ try {
39
+ assertPathContained(root, targetCwd)
40
+ return true
41
+ } catch {
42
+ // not within root
43
+ }
44
+ }
45
+ return false
46
+ }
47
+
48
+ export function registerMoaRoutes(ctx, { live, callLlm, liveCanvas, saveConfig, Config, plainConfig, webServerPort, timeMachineEngine, workspaceRoot }) {
34
49
 
35
50
  ctx.effect(() => {
36
51
  return ctx.webServer.register({
@@ -535,13 +550,28 @@ ctx.effect(() => {
535
550
  const presetName = payload.preset || cfg.default_preset || 'default'
536
551
  const preset = (cfg.presets || []).find((p) => p.name === presetName) || cfg.presets?.[0]
537
552
 
553
+ const baseWorkspaceRoot = workspaceRoot || ctx.baseDir || ctx.workspaceDir || process.cwd()
554
+ const allowedRoots = [
555
+ baseWorkspaceRoot,
556
+ process.cwd(),
557
+ cfg.workspace_root,
558
+ ...(Array.isArray(cfg.allowed_workspaces) ? cfg.allowed_workspaces : (cfg.allowed_workspaces ? [cfg.allowed_workspaces] : [])),
559
+ ...(isTestEnvironment() ? [os.tmpdir()] : []),
560
+ ].filter(Boolean)
561
+
562
+ const targetCwd = payload.cwd ? path.resolve(payload.cwd) : baseWorkspaceRoot
563
+ if (payload.cwd && !isAllowedWorkspace(targetCwd, allowedRoots)) {
564
+ writeJson(res, 403, { ok: false, error: 'Forbidden: cwd escapes allowed workspace boundary' })
565
+ return
566
+ }
567
+
538
568
  const result = await runMoAPipeline({
539
569
  userPrompt: prompt, messages: payload.messages || [], preset, callLlm,
540
- cwd: payload.cwd || process.cwd(), prices: cfg.prices || {}, liveCanvas,
570
+ cwd: targetCwd, prices: cfg.prices || {}, liveCanvas,
541
571
  webServerPort: webServerPort || ctx.webServer?.port,
542
572
  timeMachineEngine: timeMachineEngine || ctx.reflect?.get?.('timeMachineEngine', false),
543
- timeMachineUrl: payload.timeMachineUrl, force: payload.force,
544
- checkpoint: payload.checkpoint,
573
+ timeMachineUrl: payload.timeMachineUrl, force: false,
574
+ checkpoint: true,
545
575
  runId: payload.runId,
546
576
  })
547
577
 
package/lib/updater.js CHANGED
@@ -44,8 +44,7 @@ export function isTrustedUpdateRequest(request) {
44
44
  /** General safe write check for plugin write endpoints (CSRF / Cross-Site protection, #234) */
45
45
  export function isSafeWriteRequest(request) {
46
46
  const remote = request?.socket?.remoteAddress || request?.connection?.remoteAddress;
47
- const isLoop = !remote || isLoopback(remote);
48
- if (!isLoop) return false;
47
+ if (!remote || !isLoopback(remote)) return false;
49
48
 
50
49
  const site = header(request, 'sec-fetch-site');
51
50
  if (site === 'cross-site') return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-moa",
3
- "version": "0.2.52",
3
+ "version": "0.2.53",
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",