@hachej/boring-execution 0.4.7 → 0.4.9
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.
|
@@ -378,14 +378,34 @@ export function createCodeInterpreterEnv(options) {
|
|
|
378
378
|
readTextLines: (path, lines, context) => guarded([[path, true]], file => local.readTextLines(file, lines, context)),
|
|
379
379
|
openTextLineReader: (path, context) => guarded([[path, true]], file => local.openTextLineReader(file, context)),
|
|
380
380
|
openBinaryReader: (path, readerOptions, context) => guarded([[path, !readerOptions?.noFollow]], file => local.openBinaryReader(file, readerOptions, context)),
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
// The reader's entries carry host paths; map them into the workspace namespace like `listDir`.
|
|
382
|
+
openDirReader: async (path, context) => {
|
|
383
|
+
const opened = await guarded([[path, true]], directory => local.openDirReader(directory, context));
|
|
384
|
+
if (!opened.ok)
|
|
385
|
+
return opened;
|
|
386
|
+
const reader = opened.value;
|
|
387
|
+
return ok({
|
|
388
|
+
next: async (maxEntries, readContext) => {
|
|
389
|
+
const page = await reader.next(maxEntries, readContext);
|
|
390
|
+
return page.ok ? ok({ ...page.value, entries: page.value.entries.map(mapInfo) }) : err(mapError(page.error));
|
|
391
|
+
},
|
|
392
|
+
close: readContext => reader.close(readContext),
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
// The mount's own watcher, confined like every file tool, with its paths reported in the workspace's namespace.
|
|
383
396
|
watch: async (targets, onChange, context) => {
|
|
384
397
|
const mapped = [];
|
|
385
398
|
for (const target of targets) {
|
|
386
399
|
const path = toLocal(target.path);
|
|
387
400
|
if (path === undefined)
|
|
388
401
|
return err(outside(target.path));
|
|
402
|
+
try {
|
|
403
|
+
if (!await confined(path, true))
|
|
404
|
+
return err(outside(target.path));
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
return err(new FileError('unknown', errorText(error), namespace(target.path)));
|
|
408
|
+
}
|
|
389
409
|
mapped.push({ ...target, path });
|
|
390
410
|
}
|
|
391
411
|
const watcher = await local.watch(mapped, change => onChange('paths' in change ? { paths: change.paths.map(path => fromLocal(path)) } : 'error' in change ? { error: mapError(change.error) } : change), context);
|
|
@@ -20,6 +20,12 @@ export interface RemoteShellHandlerOptions {
|
|
|
20
20
|
readonly maxRequestBytes?: number;
|
|
21
21
|
readonly maxFrameBytes?: number;
|
|
22
22
|
readonly maxQueuedBytes?: number;
|
|
23
|
+
/**
|
|
24
|
+
* A client-chosen `env` may not set loader or shell-startup variables (`LD_*`, `DYLD_*`, `BASH_ENV`, `ENV`, `NODE_OPTIONS`,
|
|
25
|
+
* `PYTHONSTARTUP`, `PERL5OPT` and a few `GIT_*` command hooks): they run code before the command, which a host that authorizes
|
|
26
|
+
* only the command string would not see. The request is refused (403) before `authorize`. Set true to let clients pass them.
|
|
27
|
+
*/
|
|
28
|
+
readonly allowLoaderEnv?: boolean;
|
|
23
29
|
}
|
|
24
30
|
export declare function createRemoteShellHandler(options: RemoteShellHandlerOptions): (request: Request) => Promise<Response>;
|
|
25
31
|
//# sourceMappingURL=remote-shell-handler.d.ts.map
|
|
@@ -3,6 +3,7 @@ import { ExecutionError, err } from '@earendil-works/pi-durable/env';
|
|
|
3
3
|
import { guardStatus, hasJsonContentType, readJsonBody } from '@hachej/boring-files/request-guard';
|
|
4
4
|
import { windowedOutput } from './remote-shell-window.js';
|
|
5
5
|
import { contentType, identity, nativeVersion, positiveLimit, requestInput, sameIdentity, schema, version, wireResult } from './remote-shell-protocol.js';
|
|
6
|
+
const LOADER_ENV = /^(LD_|DYLD_)|^(BASH_ENV|ENV|NODE_OPTIONS|PYTHONSTARTUP|PERL5OPT|GIT_SSH_COMMAND|GIT_SSH|GIT_ASKPASS|GIT_EXTERNAL_DIFF|GIT_PROXY_COMMAND)$/;
|
|
6
7
|
function failure(status) {
|
|
7
8
|
return new Response(null, { status, headers: { 'cache-control': 'no-store' } });
|
|
8
9
|
}
|
|
@@ -40,6 +41,8 @@ export function createRemoteShellHandler(options) {
|
|
|
40
41
|
return failure(409);
|
|
41
42
|
if (input.options.timeout !== undefined && supports.timeout !== true || input.options.spill !== undefined && supports.spill !== true)
|
|
42
43
|
return failure(422);
|
|
44
|
+
if (!options.allowLoaderEnv && Object.keys(input.options.env ?? {}).some(key => LOADER_ENV.test(key)))
|
|
45
|
+
return failure(403);
|
|
43
46
|
try {
|
|
44
47
|
if (signal?.aborted || await awaitWithContext(Promise.resolve(authorize(input.command, structuredClone(input.options))), context) !== true || signal?.aborted)
|
|
45
48
|
return failure(403);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-execution",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@earendil-works/chord": "~1.2.0",
|
|
57
57
|
"just-bash": "^3.6.0",
|
|
58
58
|
"isomorphic-git": "^1.42.6",
|
|
59
|
-
"@hachej/boring-files": "^0.4.
|
|
59
|
+
"@hachej/boring-files": "^0.4.9",
|
|
60
60
|
"zod": "^4.6.5",
|
|
61
61
|
"@aws-sdk/client-bedrock-agentcore": "^3.1146.0"
|
|
62
62
|
},
|