@nahisaho/katashiro-sandbox 0.4.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/README.md +213 -0
- package/dist/docker-executor.d.ts +117 -0
- package/dist/docker-executor.d.ts.map +1 -0
- package/dist/docker-executor.js +557 -0
- package/dist/docker-executor.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/local-executor.d.ts +64 -0
- package/dist/local-executor.d.ts.map +1 -0
- package/dist/local-executor.js +242 -0
- package/dist/local-executor.js.map +1 -0
- package/dist/sandbox.d.ts +104 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +128 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/types.d.ts +228 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +51 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# @nahisaho/katashiro-sandbox
|
|
2
|
+
|
|
3
|
+
コード実行サンドボックスパッケージ。Docker/ローカル環境でbash, Python, JavaScript/TypeScriptを安全に実行します。
|
|
4
|
+
|
|
5
|
+
## 概要
|
|
6
|
+
|
|
7
|
+
KATASHIRO v0.4.0で追加された、コード実行のための隔離環境を提供するパッケージです。
|
|
8
|
+
|
|
9
|
+
### 主な機能
|
|
10
|
+
|
|
11
|
+
- **Docker実行**: Dockerコンテナ内での安全なコード実行
|
|
12
|
+
- **ローカル実行**: 開発/テスト用のローカル環境実行
|
|
13
|
+
- **複数言語サポート**: bash, Python, JavaScript, TypeScript
|
|
14
|
+
- **リソース制限**: メモリ、CPU、タイムアウト制限
|
|
15
|
+
- **セキュリティポリシー**: システムコール制限、ケーパビリティ削除
|
|
16
|
+
|
|
17
|
+
## インストール
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @nahisaho/katashiro-sandbox
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 基本的な使い方
|
|
24
|
+
|
|
25
|
+
### 簡易関数
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { executePython, executeBash, executeJavaScript } from '@nahisaho/katashiro-sandbox';
|
|
29
|
+
|
|
30
|
+
// Pythonコード実行
|
|
31
|
+
const result = await executePython('print("Hello, World!")');
|
|
32
|
+
if (result.isOk()) {
|
|
33
|
+
console.log(result.value.stdout); // "Hello, World!\n"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Bashスクリプト実行
|
|
37
|
+
const bashResult = await executeBash('echo "Hello from bash"');
|
|
38
|
+
|
|
39
|
+
// JavaScript実行
|
|
40
|
+
const jsResult = await executeJavaScript('console.log(1 + 2)');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### SandboxFactoryを使用
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { SandboxFactory } from '@nahisaho/katashiro-sandbox';
|
|
47
|
+
|
|
48
|
+
// Dockerサンドボックスを作成
|
|
49
|
+
const sandbox = SandboxFactory.create({ timeout: 60 }, 'docker');
|
|
50
|
+
|
|
51
|
+
// コード実行
|
|
52
|
+
const result = await sandbox.execute('print(sum(range(100)))', 'python');
|
|
53
|
+
|
|
54
|
+
// イベントリスナー
|
|
55
|
+
sandbox.on('execution:start', (event) => {
|
|
56
|
+
console.log(`開始: ${event.requestId}`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
sandbox.on('execution:complete', (event) => {
|
|
60
|
+
console.log(`完了: ${event.requestId}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// クリーンアップ
|
|
64
|
+
await sandbox.cleanup();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 自動ランタイム検出
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { SandboxFactory } from '@nahisaho/katashiro-sandbox';
|
|
71
|
+
|
|
72
|
+
// Dockerが利用可能ならDocker、そうでなければローカル実行
|
|
73
|
+
const sandbox = await SandboxFactory.createAuto({ timeout: 30 });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 設定
|
|
77
|
+
|
|
78
|
+
### SandboxConfig
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
interface SandboxConfig {
|
|
82
|
+
runtime: 'docker' | 'local' | 'wasm'; // ランタイム
|
|
83
|
+
timeout: number; // タイムアウト(秒)
|
|
84
|
+
memoryLimit: number; // メモリ制限(バイト)
|
|
85
|
+
cpuLimit: number; // CPU制限(0.0-1.0)
|
|
86
|
+
workingDir: string; // 作業ディレクトリ
|
|
87
|
+
networkEnabled: boolean; // ネットワークアクセス
|
|
88
|
+
tmpfsSize: number; // 一時ファイルシステムサイズ
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### デフォルト設定
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const DEFAULT_SANDBOX_CONFIG = {
|
|
96
|
+
runtime: 'docker',
|
|
97
|
+
timeout: 30,
|
|
98
|
+
memoryLimit: 512 * 1024 * 1024, // 512MB
|
|
99
|
+
cpuLimit: 0.5,
|
|
100
|
+
workingDir: '/workspace',
|
|
101
|
+
networkEnabled: false,
|
|
102
|
+
tmpfsSize: 64 * 1024 * 1024, // 64MB
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Docker Executor
|
|
107
|
+
|
|
108
|
+
### 特徴
|
|
109
|
+
|
|
110
|
+
- **完全な隔離**: コンテナ内でコードを実行
|
|
111
|
+
- **リソース制限**: メモリ・CPU制限を強制
|
|
112
|
+
- **セキュリティ**: ケーパビリティ削除、seccompプロファイル
|
|
113
|
+
- **自動クリーンアップ**: 実行後にコンテナを自動削除
|
|
114
|
+
|
|
115
|
+
### 使用するDockerイメージ
|
|
116
|
+
|
|
117
|
+
| 言語 | デフォルトイメージ |
|
|
118
|
+
|------|-------------------|
|
|
119
|
+
| bash | alpine:3.19 |
|
|
120
|
+
| python | python:3.12-slim |
|
|
121
|
+
| javascript | node:22-slim |
|
|
122
|
+
| typescript | node:22-slim |
|
|
123
|
+
|
|
124
|
+
### カスタムイメージの使用
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { DockerExecutor } from '@nahisaho/katashiro-sandbox';
|
|
128
|
+
|
|
129
|
+
const executor = new DockerExecutor(
|
|
130
|
+
{ timeout: 60 },
|
|
131
|
+
{
|
|
132
|
+
images: {
|
|
133
|
+
bash: 'ubuntu:22.04',
|
|
134
|
+
python: 'python:3.11-alpine',
|
|
135
|
+
javascript: 'node:20-alpine',
|
|
136
|
+
typescript: 'node:20-alpine',
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Local Executor
|
|
143
|
+
|
|
144
|
+
### 注意
|
|
145
|
+
|
|
146
|
+
Local Executorはホストシステムで直接コードを実行するため、**本番環境では使用しないでください**。開発とテスト目的専用です。
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { LocalExecutor } from '@nahisaho/katashiro-sandbox';
|
|
150
|
+
|
|
151
|
+
const executor = new LocalExecutor({ timeout: 10 });
|
|
152
|
+
const result = await executor.execute('console.log("test")', 'javascript');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## セキュリティポリシー
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
interface SecurityPolicy {
|
|
159
|
+
blockedSyscalls: string[]; // ブロックするシステムコール
|
|
160
|
+
readOnlyPaths: string[]; // 読み取り専用パス
|
|
161
|
+
writablePaths: string[]; // 書き込み可能パス
|
|
162
|
+
maxProcesses: number; // 最大プロセス数
|
|
163
|
+
maxOpenFiles: number; // 最大ファイルディスクリプタ数
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### デフォルトポリシー
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const DEFAULT_SECURITY_POLICY = {
|
|
171
|
+
blockedSyscalls: ['ptrace', 'mount', 'umount', 'reboot', 'swapon', 'swapoff'],
|
|
172
|
+
readOnlyPaths: ['/etc', '/usr', '/bin', '/lib'],
|
|
173
|
+
writablePaths: ['/workspace', '/tmp'],
|
|
174
|
+
maxProcesses: 10,
|
|
175
|
+
maxOpenFiles: 100,
|
|
176
|
+
};
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## イベント
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
sandbox.on('execution:start', (event) => { ... });
|
|
183
|
+
sandbox.on('execution:output', (event) => { ... });
|
|
184
|
+
sandbox.on('execution:complete', (event) => { ... });
|
|
185
|
+
sandbox.on('execution:error', (event) => { ... });
|
|
186
|
+
sandbox.on('execution:timeout', (event) => { ... });
|
|
187
|
+
sandbox.on('container:create', (event) => { ... });
|
|
188
|
+
sandbox.on('container:start', (event) => { ... });
|
|
189
|
+
sandbox.on('container:stop', (event) => { ... });
|
|
190
|
+
sandbox.on('security:violation', (event) => { ... });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## ExecutionResult
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
interface ExecutionResult {
|
|
197
|
+
requestId: string; // リクエストID
|
|
198
|
+
status: ExecutionStatus; // 'completed' | 'failed' | 'timeout' | ...
|
|
199
|
+
exitCode: number; // 終了コード
|
|
200
|
+
stdout: string; // 標準出力
|
|
201
|
+
stderr: string; // 標準エラー出力
|
|
202
|
+
duration: number; // 実行時間(ミリ秒)
|
|
203
|
+
files: FileOutput[]; // 出力ファイル
|
|
204
|
+
memoryUsed?: number; // メモリ使用量
|
|
205
|
+
cpuTime?: number; // CPU使用時間
|
|
206
|
+
error?: ExecutionError; // エラー詳細
|
|
207
|
+
completedAt: string; // 完了日時
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## ライセンス
|
|
212
|
+
|
|
213
|
+
MIT
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KATASHIRO Sandbox - Docker Executor Implementation
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview REQ-007: Dockerベースのコード実行サンドボックス
|
|
5
|
+
* @module @nahisaho/katashiro-sandbox
|
|
6
|
+
* @since 0.4.0
|
|
7
|
+
*/
|
|
8
|
+
import { EventEmitter } from 'events';
|
|
9
|
+
import { type Result } from '@nahisaho/katashiro-core';
|
|
10
|
+
import type { SandboxConfig, DockerConfig, ExecutionRequest, ExecutionResult, SupportedLanguage, SecurityPolicy, SandboxEventType, SandboxEventListener, ContainerInfo, ResourceStats } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* サンドボックスエラー
|
|
13
|
+
*/
|
|
14
|
+
export declare class SandboxError extends Error {
|
|
15
|
+
readonly code: string;
|
|
16
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
17
|
+
constructor(message: string, code: string, details?: Record<string, unknown> | undefined);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Docker Executor
|
|
21
|
+
*
|
|
22
|
+
* EARS Requirements:
|
|
23
|
+
* - Ubiquitous: The Sandbox shall execute code in an isolated Docker/VM environment
|
|
24
|
+
* - Ubiquitous: The Sandbox shall support bash, Python, and JavaScript/TypeScript execution
|
|
25
|
+
* - Event-Driven: When execution time exceeds timeout, the Sandbox shall terminate
|
|
26
|
+
* - State-Driven: While code is executing, the Sandbox shall enforce CPU and memory limits
|
|
27
|
+
* - Unwanted: If code attempts to access host system, the Sandbox shall block the access
|
|
28
|
+
*/
|
|
29
|
+
export declare class DockerExecutor extends EventEmitter {
|
|
30
|
+
private readonly docker;
|
|
31
|
+
private readonly config;
|
|
32
|
+
private readonly dockerConfig;
|
|
33
|
+
private readonly securityPolicy;
|
|
34
|
+
private readonly activeContainers;
|
|
35
|
+
constructor(config?: Partial<SandboxConfig>, dockerConfig?: Partial<DockerConfig>, securityPolicy?: Partial<SecurityPolicy>);
|
|
36
|
+
/**
|
|
37
|
+
* 型安全なイベントリスナー登録
|
|
38
|
+
*/
|
|
39
|
+
on(event: SandboxEventType, listener: SandboxEventListener): this;
|
|
40
|
+
/**
|
|
41
|
+
* イベント発火
|
|
42
|
+
*/
|
|
43
|
+
private emitEvent;
|
|
44
|
+
/**
|
|
45
|
+
* コードを実行
|
|
46
|
+
*
|
|
47
|
+
* @param code 実行するコード
|
|
48
|
+
* @param language プログラミング言語
|
|
49
|
+
* @param options 追加オプション
|
|
50
|
+
*/
|
|
51
|
+
execute(code: string, language: SupportedLanguage, options?: Partial<Pick<ExecutionRequest, 'stdin' | 'env' | 'timeout'>>): Promise<Result<ExecutionResult, SandboxError>>;
|
|
52
|
+
/**
|
|
53
|
+
* コンテナを作成
|
|
54
|
+
*/
|
|
55
|
+
private createContainer;
|
|
56
|
+
/**
|
|
57
|
+
* 実行コマンドを構築
|
|
58
|
+
*/
|
|
59
|
+
private buildCommand;
|
|
60
|
+
/**
|
|
61
|
+
* 言語に対応するファイル名を取得
|
|
62
|
+
*/
|
|
63
|
+
private getCodeFileName;
|
|
64
|
+
/**
|
|
65
|
+
* 環境変数を構築
|
|
66
|
+
*/
|
|
67
|
+
private buildEnv;
|
|
68
|
+
/**
|
|
69
|
+
* コードをコンテナにコピー
|
|
70
|
+
*/
|
|
71
|
+
private copyCodeToContainer;
|
|
72
|
+
/**
|
|
73
|
+
* tarアーカイブを作成(簡易版)
|
|
74
|
+
*/
|
|
75
|
+
private createTarArchive;
|
|
76
|
+
/**
|
|
77
|
+
* コンテナの完了を待機
|
|
78
|
+
*/
|
|
79
|
+
private waitForCompletion;
|
|
80
|
+
/**
|
|
81
|
+
* Dockerログをパース
|
|
82
|
+
*/
|
|
83
|
+
private parseLogs;
|
|
84
|
+
/**
|
|
85
|
+
* 出力ファイルを抽出
|
|
86
|
+
*/
|
|
87
|
+
private extractOutputFiles;
|
|
88
|
+
/**
|
|
89
|
+
* コンテナをクリーンアップ
|
|
90
|
+
*/
|
|
91
|
+
private cleanupContainer;
|
|
92
|
+
/**
|
|
93
|
+
* エラーをパース
|
|
94
|
+
*/
|
|
95
|
+
private parseError;
|
|
96
|
+
/**
|
|
97
|
+
* アクティブなコンテナ数を取得
|
|
98
|
+
*/
|
|
99
|
+
getActiveContainerCount(): number;
|
|
100
|
+
/**
|
|
101
|
+
* 指定した実行をキャンセル
|
|
102
|
+
*/
|
|
103
|
+
cancel(requestId: string): Promise<boolean>;
|
|
104
|
+
/**
|
|
105
|
+
* 全アクティブコンテナをクリーンアップ
|
|
106
|
+
*/
|
|
107
|
+
cleanup(): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* コンテナ情報を取得
|
|
110
|
+
*/
|
|
111
|
+
getContainerInfo(requestId: string): Promise<ContainerInfo | null>;
|
|
112
|
+
/**
|
|
113
|
+
* リソース使用量を取得
|
|
114
|
+
*/
|
|
115
|
+
getResourceStats(requestId: string): Promise<ResourceStats | null>;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=docker-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-executor.d.ts","sourceRoot":"","sources":["../src/docker-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAW,KAAK,MAAM,EAAc,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAGf,iBAAiB,EACjB,cAAc,EAEd,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACd,MAAM,SAAS,CAAC;AAWjB;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;aAGnB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFjD,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAKpD;AAMD;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuC;gBAGtE,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM,EACnC,YAAY,GAAE,OAAO,CAAC,YAAY,CAAM,EACxC,cAAc,GAAE,OAAO,CAAC,cAAc,CAAM;IAc9C;;OAEG;IACH,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI;IAIjE;;OAEG;IACH,OAAO,CAAC,SAAS;IAYjB;;;;;;OAMG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC,CAAM,GACzE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IAqGjD;;OAEG;YACW,eAAe;IAwD7B;;OAEG;IACH,OAAO,CAAC,YAAY;IA+BpB;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAUhB;;OAEG;YACW,mBAAmB;IAajC;;OAEG;YACW,gBAAgB;IA0D9B;;OAEG;YACW,iBAAiB;IAmC/B;;OAEG;IACH,OAAO,CAAC,SAAS;IAoCjB;;OAEG;YACW,kBAAkB;IAQhC;;OAEG;YACW,gBAAgB;IAuB9B;;OAEG;IACH,OAAO,CAAC,UAAU;IAsBlB;;OAEG;IACH,uBAAuB,IAAI,MAAM;IAIjC;;OAEG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcjD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAoBxE;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;CAkEzE"}
|