veryfront 0.1.241 → 0.1.243
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/esm/cli/templates/manifest.js +77 -77
- package/esm/deno.js +1 -1
- package/esm/src/agent/conversation-root-run-context.d.ts.map +1 -1
- package/esm/src/agent/conversation-root-run-context.js +2 -0
- package/esm/src/agent/conversation-run-context.d.ts +2 -0
- package/esm/src/agent/conversation-run-context.d.ts.map +1 -1
- package/esm/src/agent/durable.d.ts +23 -0
- package/esm/src/agent/durable.d.ts.map +1 -1
- package/esm/src/agent/durable.js +39 -0
- package/esm/src/agent/index.d.ts +1 -1
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +1 -1
- package/esm/src/oauth/handlers/callback-handler.d.ts +2 -2
- package/esm/src/oauth/handlers/callback-handler.d.ts.map +1 -1
- package/esm/src/oauth/handlers/callback-handler.js +17 -5
- package/esm/src/oauth/handlers/init-handler.d.ts +24 -4
- package/esm/src/oauth/handlers/init-handler.d.ts.map +1 -1
- package/esm/src/oauth/handlers/init-handler.js +47 -10
- package/esm/src/oauth/providers/base.d.ts +9 -2
- package/esm/src/oauth/providers/base.d.ts.map +1 -1
- package/esm/src/oauth/providers/base.js +12 -5
- package/esm/src/oauth/token-store/index.d.ts +1 -1
- package/esm/src/oauth/token-store/index.d.ts.map +1 -1
- package/esm/src/oauth/token-store/memory.d.ts +21 -9
- package/esm/src/oauth/token-store/memory.d.ts.map +1 -1
- package/esm/src/oauth/token-store/memory.js +42 -28
- package/esm/src/oauth/types.d.ts +33 -7
- package/esm/src/oauth/types.d.ts.map +1 -1
- package/esm/src/platform/compat/framework-source-resolver.d.ts.map +1 -1
- package/esm/src/platform/compat/framework-source-resolver.js +34 -0
- package/esm/src/routing/api/module-loader/loader.d.ts +11 -0
- package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/loader.js +18 -2
- package/esm/src/server/handlers/dev/dashboard/api.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/dashboard/api.js +34 -13
- package/esm/src/server/handlers/dev/files/esbuild-plugins.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/files/esbuild-plugins.js +45 -4
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/cli/templates/manifest.js +77 -77
- package/src/deno.js +1 -1
- package/src/src/agent/conversation-root-run-context.ts +2 -0
- package/src/src/agent/durable.ts +60 -0
- package/src/src/agent/index.ts +3 -0
- package/src/src/oauth/handlers/callback-handler.ts +25 -8
- package/src/src/oauth/handlers/init-handler.ts +83 -15
- package/src/src/oauth/providers/base.ts +12 -5
- package/src/src/oauth/token-store/index.ts +1 -1
- package/src/src/oauth/token-store/memory.ts +48 -35
- package/src/src/oauth/types.ts +34 -7
- package/src/src/platform/compat/framework-source-resolver.ts +32 -0
- package/src/src/routing/api/module-loader/loader.ts +18 -2
- package/src/src/server/handlers/dev/dashboard/api.ts +32 -10
- package/src/src/server/handlers/dev/files/esbuild-plugins.ts +54 -5
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -91,7 +91,7 @@ async function readProjectDependencies(
|
|
|
91
91
|
* `new Function` to evaluate them in a proper CJS wrapper with require,
|
|
92
92
|
* exports, module, __filename, and __dirname bindings.
|
|
93
93
|
*/
|
|
94
|
-
function generateCompiledBinaryRequireShim(projectDir: string): string {
|
|
94
|
+
export function generateCompiledBinaryRequireShim(projectDir: string): string {
|
|
95
95
|
const builtinSet = JSON.stringify(NODE_BUILTINS);
|
|
96
96
|
const safeProjectDir = JSON.stringify(projectDir + "/package.json");
|
|
97
97
|
const safeProjectRoot = JSON.stringify(pathHelper.resolve(projectDir));
|
|
@@ -102,6 +102,12 @@ import { dirname as __vf_dirname, resolve as __vf_resolve } from "node:path";
|
|
|
102
102
|
var __vf_builtinRequire = __vf_createRequire(${safeProjectDir});
|
|
103
103
|
var __vf_builtinSet = new Set(${builtinSet});
|
|
104
104
|
var __vf_projectRoot = ${safeProjectRoot};
|
|
105
|
+
// VULN-FS-5: Canonicalize the project root so containment checks using
|
|
106
|
+
// Deno.realPathSync(resolved) compare canonical-vs-canonical. Without this,
|
|
107
|
+
// when the project itself is opened via a symlink, the realpath'd resolved
|
|
108
|
+
// module path has a different prefix than the non-canonical projectRoot and
|
|
109
|
+
// legitimate dependencies would be rejected.
|
|
110
|
+
try { __vf_projectRoot = Deno.realPathSync(__vf_projectRoot); } catch (_) { /* expected: projectRoot may not exist at shim init in some environments */ }
|
|
105
111
|
var __vf_cache = Object.create(null);
|
|
106
112
|
function __vf_assertContained(resolved) {
|
|
107
113
|
var norm = __vf_resolve(resolved).replace(/\\\\/g, "/");
|
|
@@ -124,10 +130,20 @@ function __vf_loadCjs(id, parentDir) {
|
|
|
124
130
|
try { Deno.statSync(resolved + exts[i]); resolved += exts[i]; break; } catch (_) { /* expected: probing file extensions */ }
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
|
-
__vf_assertContained(resolved);
|
|
128
133
|
} else {
|
|
129
134
|
resolved = __vf_builtinRequire.resolve(id);
|
|
130
135
|
}
|
|
136
|
+
// VULN-FS-5: Always assert containment after resolution (both branches),
|
|
137
|
+
// then re-canonicalize via realPathSync to resist symlinked node_modules
|
|
138
|
+
// entries that could point outside the project root.
|
|
139
|
+
__vf_assertContained(resolved);
|
|
140
|
+
try {
|
|
141
|
+
var real = Deno.realPathSync(resolved);
|
|
142
|
+
__vf_assertContained(real);
|
|
143
|
+
resolved = real;
|
|
144
|
+
} catch (_) {
|
|
145
|
+
/* expected: realPathSync fails for non-existent paths — assertContained above already held */
|
|
146
|
+
}
|
|
131
147
|
if (resolved in __vf_cache) return __vf_cache[resolved];
|
|
132
148
|
var code = Deno.readTextFileSync(resolved);
|
|
133
149
|
if (resolved.endsWith(".json")) {
|
|
@@ -24,17 +24,31 @@ import { isRSCEnabled } from "../../../../utils/feature-flags.js";
|
|
|
24
24
|
import { getEnvironmentConfig } from "../../../../config/environment-config.js";
|
|
25
25
|
import { getErrorCollector } from "../../../../observability/error-collector.js";
|
|
26
26
|
import { getLogBuffer } from "../../../../observability/log-buffer.js";
|
|
27
|
+
import { validatePathSync } from "../../../../security/index.js";
|
|
27
28
|
import { ReloadNotifier } from "../../../reload-notifier.js";
|
|
28
29
|
import type { HandlerContext } from "../../types.js";
|
|
29
30
|
|
|
30
31
|
const WORKFLOW_EXECUTION_TIMEOUT_MS = 30_000;
|
|
31
32
|
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Validate a relative path against the project directory.
|
|
35
|
+
*
|
|
36
|
+
* Uses `validatePathSync` in strict mode (rejects absolute paths, null bytes,
|
|
37
|
+
* `..` traversal, and any resolved path that escapes `baseDir`).
|
|
38
|
+
*
|
|
39
|
+
* Note: `searchParams.get()` already percent-decodes; no extra decoding needed
|
|
40
|
+
* (double-decoding would itself be a vulnerability).
|
|
41
|
+
*
|
|
42
|
+
* Returns the canonicalized absolute path on success, or `null` when invalid.
|
|
43
|
+
*/
|
|
44
|
+
function validateRelativePath(path: string, projectDir: string): string | null {
|
|
45
|
+
const result = validatePathSync(path, {
|
|
46
|
+
baseDir: projectDir,
|
|
47
|
+
allowAbsolute: false,
|
|
48
|
+
level: "strict",
|
|
49
|
+
});
|
|
50
|
+
if (!result.valid || !result.canonicalPath) return null;
|
|
51
|
+
return result.canonicalPath;
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
const JSON_HEADERS = {
|
|
@@ -397,9 +411,15 @@ async function handleListFiles(req: Request, ctx: HandlerContext): Promise<Respo
|
|
|
397
411
|
if (!projectDir) return errorResponse("No project directory configured", 500);
|
|
398
412
|
|
|
399
413
|
const relativePath = new URL(req.url).searchParams.get("path") ?? "";
|
|
400
|
-
if (!isValidRelativePath(relativePath)) return errorResponse("Invalid path", 400);
|
|
401
414
|
|
|
402
|
-
|
|
415
|
+
let fullPath: string;
|
|
416
|
+
if (relativePath === "") {
|
|
417
|
+
fullPath = projectDir;
|
|
418
|
+
} else {
|
|
419
|
+
const canonical = validateRelativePath(relativePath, projectDir);
|
|
420
|
+
if (canonical === null) return errorResponse("Invalid path", 400);
|
|
421
|
+
fullPath = canonical;
|
|
422
|
+
}
|
|
403
423
|
|
|
404
424
|
try {
|
|
405
425
|
const files: Array<{ name: string; type: "file" | "directory"; path: string }> = [];
|
|
@@ -433,10 +453,12 @@ async function handleReadFileContent(req: Request, ctx: HandlerContext): Promise
|
|
|
433
453
|
|
|
434
454
|
const relativePath = new URL(req.url).searchParams.get("path") ?? "";
|
|
435
455
|
if (!relativePath) return errorResponse("path parameter is required", 400);
|
|
436
|
-
|
|
456
|
+
|
|
457
|
+
const canonical = validateRelativePath(relativePath, projectDir);
|
|
458
|
+
if (canonical === null) return errorResponse("Invalid path", 400);
|
|
437
459
|
|
|
438
460
|
try {
|
|
439
|
-
const content = await adapter.fs.readFile(
|
|
461
|
+
const content = await adapter.fs.readFile(canonical);
|
|
440
462
|
const extension = relativePath.split(".").pop() ?? "";
|
|
441
463
|
|
|
442
464
|
if (!TEXT_EXTENSIONS.has(extension.toLowerCase())) {
|
|
@@ -2,7 +2,12 @@ import type { OnLoadArgs, OnResolveArgs, Plugin, PluginBuild } from "esbuild";
|
|
|
2
2
|
import { NETWORK_ERROR } from "../../../../errors/index.js";
|
|
3
3
|
// Direct import from base.ts to avoid circular dependency through barrel
|
|
4
4
|
import type { RuntimeAdapter } from "../../../../platform/adapters/base.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getDirectory,
|
|
7
|
+
isWithinDirectory,
|
|
8
|
+
joinPath,
|
|
9
|
+
normalizePath,
|
|
10
|
+
} from "../../../../utils/path-utils.js";
|
|
6
11
|
|
|
7
12
|
import {
|
|
8
13
|
computeIntegrity,
|
|
@@ -34,16 +39,43 @@ export function createRelativeFsPlugin(projectDir: string, adapter: RuntimeAdapt
|
|
|
34
39
|
const exts = [".tsx", ".ts", ".jsx", ".js", ".mjs"];
|
|
35
40
|
|
|
36
41
|
build.onResolve({ filter: /^(\.?\.?\/|\/)\/*/ }, async (args: OnResolveArgs) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
// VULN-FS-6: NUL bytes are never legitimate in module paths.
|
|
43
|
+
if (args.path.includes("\0")) {
|
|
44
|
+
return {
|
|
45
|
+
errors: [{ text: `Import path contains NUL byte: ${args.path}`, location: null }],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const basedir = args.resolveDir ||
|
|
50
|
+
(args.importer ? getDirectory(args.importer) : projectDir);
|
|
51
|
+
// normalizePath collapses `./` and `foo/../` segments produced by
|
|
52
|
+
// `joinPath` so downstream `adapter.fs.stat` lookups match the file
|
|
53
|
+
// system's canonical key. Still inside the containment check below.
|
|
54
|
+
const candidate = normalizePath(
|
|
55
|
+
args.path.startsWith("/")
|
|
56
|
+
? joinPath(projectDir, args.path)
|
|
57
|
+
: joinPath(basedir, args.path),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// VULN-FS-6: refuse anything that, after joining, escapes the project
|
|
61
|
+
// root. esbuild plugins fire per-import; an entry file with
|
|
62
|
+
// `import "../../../../etc/hostname"` would otherwise embed the file.
|
|
63
|
+
if (!isWithinDirectory(projectDir, candidate)) {
|
|
64
|
+
return {
|
|
65
|
+
errors: [{
|
|
66
|
+
text: `Import escapes project directory: ${args.path}`,
|
|
67
|
+
location: null,
|
|
68
|
+
}],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
41
71
|
|
|
42
72
|
const candidates: string[] = [candidate];
|
|
43
73
|
for (const ext of exts) candidates.push(candidate + ext);
|
|
44
74
|
for (const ext of exts) candidates.push(joinPath(candidate, `index${ext}`));
|
|
45
75
|
|
|
46
76
|
for (const f of candidates) {
|
|
77
|
+
// Defence in depth: each extension probe must also stay inside.
|
|
78
|
+
if (!isWithinDirectory(projectDir, f)) continue;
|
|
47
79
|
try {
|
|
48
80
|
const st = await adapter.fs.stat(f);
|
|
49
81
|
if (st.isFile) return { path: f };
|
|
@@ -56,6 +88,23 @@ export function createRelativeFsPlugin(projectDir: string, adapter: RuntimeAdapt
|
|
|
56
88
|
});
|
|
57
89
|
|
|
58
90
|
build.onLoad({ filter: /\.(tsx?|jsx?|mjs)$/ }, async (args: OnLoadArgs) => {
|
|
91
|
+
// VULN-FS-6: belt-and-braces — reject any onLoad call whose path
|
|
92
|
+
// escapes the project root or carries a NUL byte. onResolve already
|
|
93
|
+
// gates this, but esbuild can call onLoad with paths produced by
|
|
94
|
+
// other plugins or namespaces.
|
|
95
|
+
if (args.path.includes("\0")) {
|
|
96
|
+
return {
|
|
97
|
+
errors: [{ text: `Load path contains NUL byte: ${args.path}`, location: null }],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (!isWithinDirectory(projectDir, args.path)) {
|
|
101
|
+
return {
|
|
102
|
+
errors: [{
|
|
103
|
+
text: `Load path escapes project directory: ${args.path}`,
|
|
104
|
+
location: null,
|
|
105
|
+
}],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
59
108
|
try {
|
|
60
109
|
const contents = await adapter.fs.readFile(args.path);
|
|
61
110
|
return { contents, loader: getLoaderForPath(args.path) };
|