@heimdall-ai/heimdall 0.1.0
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/LICENSE +190 -0
- package/README.md +471 -0
- package/dist/config/constants.d.ts +24 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +70 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/core/bash-manager.d.ts +56 -0
- package/dist/core/bash-manager.d.ts.map +1 -0
- package/dist/core/bash-manager.js +106 -0
- package/dist/core/bash-manager.js.map +1 -0
- package/dist/core/pyodide-manager.d.ts +125 -0
- package/dist/core/pyodide-manager.d.ts.map +1 -0
- package/dist/core/pyodide-manager.js +669 -0
- package/dist/core/pyodide-manager.js.map +1 -0
- package/dist/core/pyodide-worker.d.ts +9 -0
- package/dist/core/pyodide-worker.d.ts.map +1 -0
- package/dist/core/pyodide-worker.js +295 -0
- package/dist/core/pyodide-worker.js.map +1 -0
- package/dist/core/secure-fs.d.ts +101 -0
- package/dist/core/secure-fs.d.ts.map +1 -0
- package/dist/core/secure-fs.js +279 -0
- package/dist/core/secure-fs.js.map +1 -0
- package/dist/integration.test.d.ts +10 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +439 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +13 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/workspace.d.ts +12 -0
- package/dist/resources/workspace.d.ts.map +1 -0
- package/dist/resources/workspace.js +105 -0
- package/dist/resources/workspace.js.map +1 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +51 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/bash-execution.d.ts +13 -0
- package/dist/tools/bash-execution.d.ts.map +1 -0
- package/dist/tools/bash-execution.js +135 -0
- package/dist/tools/bash-execution.js.map +1 -0
- package/dist/tools/filesystem.d.ts +12 -0
- package/dist/tools/filesystem.d.ts.map +1 -0
- package/dist/tools/filesystem.js +104 -0
- package/dist/tools/filesystem.js.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +17 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/python-execution.d.ts +12 -0
- package/dist/tools/python-execution.d.ts.map +1 -0
- package/dist/tools/python-execution.js +77 -0
- package/dist/tools/python-execution.js.map +1 -0
- package/dist/types/index.d.ts +64 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/async-lock.d.ts +35 -0
- package/dist/utils/async-lock.d.ts.map +1 -0
- package/dist/utils/async-lock.js +57 -0
- package/dist/utils/async-lock.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
// Get directory paths
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
/**
|
|
8
|
+
* Host filesystem path to workspace directory
|
|
9
|
+
*/
|
|
10
|
+
export const WORKSPACE_DIR = process.env.HEIMDALL_WORKSPACE || path.join(__dirname, "..", "..", "workspace");
|
|
11
|
+
/**
|
|
12
|
+
* Virtual filesystem path to workspace directory in Pyodide
|
|
13
|
+
*/
|
|
14
|
+
export const VIRTUAL_WORKSPACE = "/workspace";
|
|
15
|
+
/**
|
|
16
|
+
* Parse and validate a size limit from environment variable
|
|
17
|
+
* @param envValue - Environment variable value
|
|
18
|
+
* @param defaultValue - Default value in bytes
|
|
19
|
+
* @param name - Name of the configuration (for error messages)
|
|
20
|
+
* @returns Validated size in bytes
|
|
21
|
+
*/
|
|
22
|
+
function parseSizeLimit(envValue, defaultValue, name) {
|
|
23
|
+
if (!envValue) {
|
|
24
|
+
return defaultValue;
|
|
25
|
+
}
|
|
26
|
+
const parsed = parseInt(envValue, 10);
|
|
27
|
+
if (isNaN(parsed) || parsed <= 0) {
|
|
28
|
+
console.error(`[Config] Invalid ${name}: "${envValue}". Must be a positive number. Using default: ${defaultValue} bytes`);
|
|
29
|
+
return defaultValue;
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse and validate a timeout from environment variable
|
|
35
|
+
* @param envValue - Environment variable value
|
|
36
|
+
* @param defaultValue - Default value in milliseconds
|
|
37
|
+
* @param name - Name of the configuration (for error messages)
|
|
38
|
+
* @returns Validated timeout in milliseconds
|
|
39
|
+
*/
|
|
40
|
+
function parseTimeoutMs(envValue, defaultValue, name) {
|
|
41
|
+
if (!envValue) {
|
|
42
|
+
return defaultValue;
|
|
43
|
+
}
|
|
44
|
+
const parsed = parseInt(envValue, 10);
|
|
45
|
+
if (isNaN(parsed) || parsed <= 0) {
|
|
46
|
+
console.error(`[Config] Invalid ${name}: "${envValue}". Must be a positive number. Using default: ${defaultValue}ms`);
|
|
47
|
+
return defaultValue;
|
|
48
|
+
}
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Maximum size for a single file (default: 10MB)
|
|
53
|
+
* Configure via HEIMDALL_MAX_FILE_SIZE environment variable (in bytes)
|
|
54
|
+
*/
|
|
55
|
+
export const MAX_FILE_SIZE = parseSizeLimit(process.env.HEIMDALL_MAX_FILE_SIZE, 10 * 1024 * 1024, "HEIMDALL_MAX_FILE_SIZE");
|
|
56
|
+
/**
|
|
57
|
+
* Maximum total workspace size (default: 100MB)
|
|
58
|
+
* Configure via HEIMDALL_MAX_WORKSPACE_SIZE environment variable (in bytes)
|
|
59
|
+
*/
|
|
60
|
+
export const MAX_WORKSPACE_SIZE = parseSizeLimit(process.env.HEIMDALL_MAX_WORKSPACE_SIZE, 100 * 1024 * 1024, "HEIMDALL_MAX_WORKSPACE_SIZE");
|
|
61
|
+
/**
|
|
62
|
+
* Maximum execution time for Python code (default: 5000ms)
|
|
63
|
+
* Configure via HEIMDALL_PYTHON_EXECUTION_TIMEOUT_MS environment variable
|
|
64
|
+
*/
|
|
65
|
+
export const PYTHON_EXECUTION_TIMEOUT_MS = parseTimeoutMs(process.env.HEIMDALL_PYTHON_EXECUTION_TIMEOUT_MS, 5000, "HEIMDALL_PYTHON_EXECUTION_TIMEOUT_MS");
|
|
66
|
+
// Ensure workspace exists
|
|
67
|
+
if (!fs.existsSync(WORKSPACE_DIR)) {
|
|
68
|
+
fs.mkdirSync(WORKSPACE_DIR, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,sBAAsB;AACtB,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAElF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE9C;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,QAA4B,EAAE,YAAoB,EAAE,IAAY;IACtF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEtC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,KAAK,CACX,oBAAoB,IAAI,MAAM,QAAQ,gDAAgD,YAAY,QAAQ,CAC3G,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,QAA4B,EAAE,YAAoB,EAAE,IAAY;IACtF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEtC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,KAAK,CACX,oBAAoB,IAAI,MAAM,QAAQ,gDAAgD,YAAY,IAAI,CACvG,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,cAAc,CACzC,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAClC,EAAE,GAAG,IAAI,GAAG,IAAI,EAChB,wBAAwB,CACzB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,cAAc,CAC9C,OAAO,CAAC,GAAG,CAAC,2BAA2B,EACvC,GAAG,GAAG,IAAI,GAAG,IAAI,EACjB,6BAA6B,CAC9B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,cAAc,CACvD,OAAO,CAAC,GAAG,CAAC,oCAAoC,EAChD,IAAI,EACJ,sCAAsC,CACvC,CAAC;AAEF,0BAA0B;AAC1B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;IAClC,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BashManager - Manages bash command execution using just-bash
|
|
3
|
+
*
|
|
4
|
+
* Provides a sandboxed bash environment with an in-memory virtual filesystem.
|
|
5
|
+
* Commands are executed using just-bash, a TypeScript implementation of bash
|
|
6
|
+
* that doesn't spawn real processes.
|
|
7
|
+
*
|
|
8
|
+
* SECURITY: Uses SecureFs wrapper to prevent symlink-based path traversal attacks.
|
|
9
|
+
* All file operations validate that resolved paths stay within the workspace.
|
|
10
|
+
*
|
|
11
|
+
* @see SECURITY-REVIEW.md for security details
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Result of a bash command execution
|
|
15
|
+
*/
|
|
16
|
+
export interface BashExecutionResult {
|
|
17
|
+
stdout: string;
|
|
18
|
+
stderr: string;
|
|
19
|
+
exitCode: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Options for bash execution
|
|
23
|
+
*/
|
|
24
|
+
export interface BashExecutionOptions {
|
|
25
|
+
cwd?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* BashManager provides bash command execution in a sandboxed environment
|
|
29
|
+
*/
|
|
30
|
+
export declare class BashManager {
|
|
31
|
+
private bash;
|
|
32
|
+
private workspaceDir;
|
|
33
|
+
private initialized;
|
|
34
|
+
constructor(workspaceDir?: string);
|
|
35
|
+
/**
|
|
36
|
+
* Initialize the bash manager
|
|
37
|
+
*/
|
|
38
|
+
initialize(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Execute a bash command
|
|
41
|
+
*/
|
|
42
|
+
execute(command: string, options?: BashExecutionOptions): Promise<BashExecutionResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Get the workspace directory
|
|
45
|
+
*/
|
|
46
|
+
getWorkspaceDir(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Check if the manager is initialized
|
|
49
|
+
*/
|
|
50
|
+
isInitialized(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Reset the bash environment (for testing)
|
|
53
|
+
*/
|
|
54
|
+
reset(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=bash-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-manager.d.ts","sourceRoot":"","sources":["../../src/core/bash-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;gBAEhB,YAAY,GAAE,MAAsB;IAIhD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BjC;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAgC5F;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BashManager - Manages bash command execution using just-bash
|
|
3
|
+
*
|
|
4
|
+
* Provides a sandboxed bash environment with an in-memory virtual filesystem.
|
|
5
|
+
* Commands are executed using just-bash, a TypeScript implementation of bash
|
|
6
|
+
* that doesn't spawn real processes.
|
|
7
|
+
*
|
|
8
|
+
* SECURITY: Uses SecureFs wrapper to prevent symlink-based path traversal attacks.
|
|
9
|
+
* All file operations validate that resolved paths stay within the workspace.
|
|
10
|
+
*
|
|
11
|
+
* @see SECURITY-REVIEW.md for security details
|
|
12
|
+
*/
|
|
13
|
+
import { Bash } from "just-bash";
|
|
14
|
+
import { SecureFs } from "./secure-fs.js";
|
|
15
|
+
import { WORKSPACE_DIR } from "../config/constants.js";
|
|
16
|
+
import path from "path";
|
|
17
|
+
/**
|
|
18
|
+
* BashManager provides bash command execution in a sandboxed environment
|
|
19
|
+
*/
|
|
20
|
+
export class BashManager {
|
|
21
|
+
bash = null;
|
|
22
|
+
workspaceDir;
|
|
23
|
+
initialized = false;
|
|
24
|
+
constructor(workspaceDir = WORKSPACE_DIR) {
|
|
25
|
+
this.workspaceDir = path.resolve(workspaceDir);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Initialize the bash manager
|
|
29
|
+
*/
|
|
30
|
+
async initialize() {
|
|
31
|
+
if (this.initialized && this.bash) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// SECURITY: Use SecureFs instead of ReadWriteFs to prevent symlink attacks
|
|
35
|
+
// SecureFs validates all file operations to ensure they don't escape
|
|
36
|
+
// the workspace via symlinks
|
|
37
|
+
const fs = new SecureFs({ root: this.workspaceDir });
|
|
38
|
+
// Initialize bash environment
|
|
39
|
+
this.bash = new Bash({
|
|
40
|
+
fs,
|
|
41
|
+
cwd: "/",
|
|
42
|
+
// Configure execution limits to prevent DoS attacks
|
|
43
|
+
executionLimits: {
|
|
44
|
+
maxLoopIterations: 10000,
|
|
45
|
+
maxCommandCount: 10000,
|
|
46
|
+
maxCallDepth: 100,
|
|
47
|
+
},
|
|
48
|
+
// Network disabled by default for security
|
|
49
|
+
// Can be enabled later if needed with allowlist
|
|
50
|
+
network: undefined,
|
|
51
|
+
});
|
|
52
|
+
this.initialized = true;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Execute a bash command
|
|
56
|
+
*/
|
|
57
|
+
async execute(command, options) {
|
|
58
|
+
if (!this.initialized || !this.bash) {
|
|
59
|
+
await this.initialize();
|
|
60
|
+
}
|
|
61
|
+
if (!this.bash) {
|
|
62
|
+
throw new Error("Bash manager not initialized");
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
// Execute the command
|
|
66
|
+
const result = await this.bash.exec(command, {
|
|
67
|
+
cwd: options?.cwd,
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
stdout: result.stdout,
|
|
71
|
+
stderr: result.stderr,
|
|
72
|
+
exitCode: result.exitCode,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
// Handle execution errors
|
|
77
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
78
|
+
return {
|
|
79
|
+
stdout: "",
|
|
80
|
+
stderr: errorMessage,
|
|
81
|
+
exitCode: 1,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the workspace directory
|
|
87
|
+
*/
|
|
88
|
+
getWorkspaceDir() {
|
|
89
|
+
return this.workspaceDir;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if the manager is initialized
|
|
93
|
+
*/
|
|
94
|
+
isInitialized() {
|
|
95
|
+
return this.initialized;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Reset the bash environment (for testing)
|
|
99
|
+
*/
|
|
100
|
+
async reset() {
|
|
101
|
+
this.bash = null;
|
|
102
|
+
this.initialized = false;
|
|
103
|
+
await this.initialize();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=bash-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-manager.js","sourceRoot":"","sources":["../../src/core/bash-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,IAAI,MAAM,MAAM,CAAC;AAkBxB;;GAEG;AACH,MAAM,OAAO,WAAW;IACd,IAAI,GAAgB,IAAI,CAAC;IACzB,YAAY,CAAS;IACrB,WAAW,GAAG,KAAK,CAAC;IAE5B,YAAY,eAAuB,aAAa;QAC9C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACjD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,2EAA2E;QAC3E,qEAAqE;QACrE,6BAA6B;QAC7B,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAErD,8BAA8B;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC;YACnB,EAAE;YACF,GAAG,EAAE,GAAG;YACR,oDAAoD;YACpD,eAAe,EAAE;gBACf,iBAAiB,EAAE,KAAK;gBACxB,eAAe,EAAE,KAAK;gBACtB,YAAY,EAAE,GAAG;aAClB;YACD,2CAA2C;YAC3C,gDAAgD;YAChD,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,OAAe,EAAE,OAA8B;QAC3D,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,CAAC;YACH,sBAAsB;YACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;gBAC3C,GAAG,EAAE,OAAO,EAAE,GAAG;aAClB,CAAC,CAAC;YAEH,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,0BAA0B;YAC1B,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE5E,OAAO;gBACL,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,YAAY;gBACpB,QAAQ,EAAE,CAAC;aACZ,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PyodideManager - Handles Python runtime lifecycle
|
|
3
|
+
*
|
|
4
|
+
* This class manages the Pyodide WebAssembly Python runtime, including:
|
|
5
|
+
* - Initialization and lifecycle management
|
|
6
|
+
* - Virtual filesystem operations
|
|
7
|
+
* - Host <-> Virtual filesystem synchronization
|
|
8
|
+
* - Python code execution (via worker thread for timeout support)
|
|
9
|
+
* - Package installation
|
|
10
|
+
*
|
|
11
|
+
* Code execution runs in a separate worker thread to enable true timeout
|
|
12
|
+
* enforcement. If Python code blocks indefinitely (infinite loops, etc.),
|
|
13
|
+
* the worker can be terminated to enforce the timeout.
|
|
14
|
+
*/
|
|
15
|
+
import { PyodideInterface } from "pyodide";
|
|
16
|
+
import type { ExecutionResult, FileReadResult, FileWriteResult, FileListResult, FileDeleteResult, PackageInstallResult } from "../types/index.js";
|
|
17
|
+
export declare class PyodideManager {
|
|
18
|
+
private pyodide;
|
|
19
|
+
private initialized;
|
|
20
|
+
private initializationPromise;
|
|
21
|
+
private worker;
|
|
22
|
+
private workerReady;
|
|
23
|
+
private workerInitPromise;
|
|
24
|
+
private writeLock;
|
|
25
|
+
/**
|
|
26
|
+
* Convert a virtual path to a host filesystem path.
|
|
27
|
+
*/
|
|
28
|
+
private virtualToHostPath;
|
|
29
|
+
/**
|
|
30
|
+
* Validate and normalize a file path to prevent directory traversal attacks
|
|
31
|
+
* @param filePath - The file path to validate
|
|
32
|
+
* @returns The normalized virtual filesystem path
|
|
33
|
+
* @throws Error if path is invalid or attempts to escape workspace
|
|
34
|
+
*/
|
|
35
|
+
private validatePath;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve symlinks and validate that the real path is within the workspace.
|
|
38
|
+
* This prevents symlink-based path traversal attacks where an attacker creates
|
|
39
|
+
* a symlink inside the workspace pointing to a location outside.
|
|
40
|
+
*
|
|
41
|
+
* @param hostPath - The host filesystem path to validate
|
|
42
|
+
* @throws Error if the resolved path escapes the workspace
|
|
43
|
+
*/
|
|
44
|
+
private validateHostPathWithSymlinkResolution;
|
|
45
|
+
/**
|
|
46
|
+
* For files that don't exist yet, validate parent directories for symlinks.
|
|
47
|
+
* Walks up the path until we find an existing directory, then validates it.
|
|
48
|
+
*/
|
|
49
|
+
private validateParentPathForSymlinks;
|
|
50
|
+
/**
|
|
51
|
+
* Calculate total workspace size from host filesystem
|
|
52
|
+
* @returns Total size in bytes
|
|
53
|
+
*/
|
|
54
|
+
private getWorkspaceSize;
|
|
55
|
+
/**
|
|
56
|
+
* Validate that writing a file won't exceed workspace size limit
|
|
57
|
+
* @param fileSize - Size of file to be written
|
|
58
|
+
* @throws Error if workspace size limit would be exceeded
|
|
59
|
+
*/
|
|
60
|
+
private checkWorkspaceSize;
|
|
61
|
+
initialize(): Promise<PyodideInterface>;
|
|
62
|
+
private doInitialize;
|
|
63
|
+
/**
|
|
64
|
+
* Sync files from host filesystem to Pyodide virtual FS
|
|
65
|
+
*/
|
|
66
|
+
syncHostToVirtual(hostPath?: string, virtualPath?: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Sync a specific host file or directory into the virtual FS.
|
|
69
|
+
*/
|
|
70
|
+
private syncHostPathToVirtual;
|
|
71
|
+
/**
|
|
72
|
+
* Sync files from Pyodide virtual FS to host filesystem
|
|
73
|
+
*/
|
|
74
|
+
syncVirtualToHost(virtualPath?: string, hostPath?: string): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Sync a specific virtual file or directory into the host filesystem.
|
|
77
|
+
* Includes symlink protection to prevent writing outside workspace.
|
|
78
|
+
*/
|
|
79
|
+
private syncVirtualPathToHost;
|
|
80
|
+
/**
|
|
81
|
+
* Initialize the worker thread for code execution
|
|
82
|
+
*/
|
|
83
|
+
private initializeWorker;
|
|
84
|
+
private doInitializeWorker;
|
|
85
|
+
/**
|
|
86
|
+
* Terminate the worker thread
|
|
87
|
+
*/
|
|
88
|
+
private terminateWorker;
|
|
89
|
+
/**
|
|
90
|
+
* Execute Python code in the sandbox using a worker thread
|
|
91
|
+
*
|
|
92
|
+
* Network access is NOT available - Pyodide runs in WebAssembly which
|
|
93
|
+
* doesn't have network capabilities. This is by design for security.
|
|
94
|
+
*
|
|
95
|
+
* The code runs in a separate worker thread, enabling true timeout
|
|
96
|
+
* enforcement. If the code doesn't complete within the timeout,
|
|
97
|
+
* the worker is terminated.
|
|
98
|
+
*/
|
|
99
|
+
executeCode(code: string, packages?: string[]): Promise<ExecutionResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Install packages via micropip
|
|
102
|
+
*/
|
|
103
|
+
installPackages(packages: string[]): Promise<PackageInstallResult[]>;
|
|
104
|
+
/**
|
|
105
|
+
* Read a file from the virtual filesystem
|
|
106
|
+
*/
|
|
107
|
+
readFile(filePath: string): Promise<FileReadResult>;
|
|
108
|
+
/**
|
|
109
|
+
* Write a file to the virtual filesystem
|
|
110
|
+
*
|
|
111
|
+
* Uses a write lock to prevent TOCTOU race conditions where concurrent
|
|
112
|
+
* writes could bypass workspace size limits. The lock ensures that the
|
|
113
|
+
* size check and write operation are atomic.
|
|
114
|
+
*/
|
|
115
|
+
writeFile(filePath: string, content: string): Promise<FileWriteResult>;
|
|
116
|
+
/**
|
|
117
|
+
* List files in a directory
|
|
118
|
+
*/
|
|
119
|
+
listFiles(dirPath?: string): Promise<FileListResult>;
|
|
120
|
+
/**
|
|
121
|
+
* Delete a file or directory
|
|
122
|
+
*/
|
|
123
|
+
deleteFile(filePath: string): Promise<FileDeleteResult>;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=pyodide-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pyodide-manager.d.ts","sourceRoot":"","sources":["../../src/core/pyodide-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAe,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAYxD,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AA0B3B,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,qBAAqB,CAA0C;IAGvE,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAA8B;IAIvD,OAAO,CAAC,SAAS,CAAmB;IAEpC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAuBpB;;;;;;;OAOG;YACW,qCAAqC;IAuBnD;;;OAGG;YACW,6BAA6B;IAgC3C;;;OAGG;YACW,gBAAgB;IA0B9B;;;;OAIG;YACW,kBAAkB;IAU1B,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;YAsB/B,YAAY;IA4C1B;;OAEG;IACG,iBAAiB,CACrB,QAAQ,SAAgB,EACxB,WAAW,SAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC;IAIhB;;OAEG;YACW,qBAAqB;IAqDnC;;OAEG;IACG,iBAAiB,CACrB,WAAW,SAAoB,EAC/B,QAAQ,SAAgB,GACvB,OAAO,CAAC,IAAI,CAAC;IAIhB;;;OAGG;YACW,qBAAqB;IAqDnC;;OAEG;YACW,gBAAgB;YAmBhB,kBAAkB;IAoEhC;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;;;;;;;;OASG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAyFlF;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAsB1E;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAmBzD;;;;;;OAMG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAqC5E;;OAEG;IACG,SAAS,CAAC,OAAO,SAAK,GAAG,OAAO,CAAC,cAAc,CAAC;IA6BtD;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAsC9D"}
|