@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.
- package/lib/file-workspace.js +2 -2
- package/lib/routes.js +36 -6
- package/lib/updater.js +1 -2
- package/package.json +1 -1
package/lib/file-workspace.js
CHANGED
|
@@ -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 =
|
|
406
|
+
const moaRoot = assertPathContained(baseDir, '.moa')
|
|
407
407
|
if (safeRunId) {
|
|
408
|
-
await bestEffort('cleanMoaWorkspaces runId', () => fs.rm(
|
|
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
|
|
7
|
-
import {
|
|
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
|
-
|
|
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:
|
|
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:
|
|
544
|
-
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
|
-
|
|
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;
|