mcp-test-timebox 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 +21 -0
- package/README.md +192 -0
- package/dist/executor/index.d.ts +8 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +8 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/process-executor.d.ts +93 -0
- package/dist/executor/process-executor.d.ts.map +1 -0
- package/dist/executor/process-executor.js +161 -0
- package/dist/executor/process-executor.js.map +1 -0
- package/dist/executor/timebox-controller.d.ts +109 -0
- package/dist/executor/timebox-controller.d.ts.map +1 -0
- package/dist/executor/timebox-controller.js +140 -0
- package/dist/executor/timebox-controller.js.map +1 -0
- package/dist/report/index.d.ts +8 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +12 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/log-extractor.d.ts +125 -0
- package/dist/report/log-extractor.d.ts.map +1 -0
- package/dist/report/log-extractor.js +213 -0
- package/dist/report/log-extractor.js.map +1 -0
- package/dist/report/report-generator.d.ts +148 -0
- package/dist/report/report-generator.d.ts.map +1 -0
- package/dist/report/report-generator.js +261 -0
- package/dist/report/report-generator.js.map +1 -0
- package/dist/server.d.ts +27 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +150 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/run-test.d.ts +80 -0
- package/dist/tools/run-test.d.ts.map +1 -0
- package/dist/tools/run-test.js +178 -0
- package/dist/tools/run-test.js.map +1 -0
- package/dist/utils/path-validator.d.ts +52 -0
- package/dist/utils/path-validator.d.ts.map +1 -0
- package/dist/utils/path-validator.js +128 -0
- package/dist/utils/path-validator.js.map +1 -0
- package/dist/validation/command-builder.d.ts +52 -0
- package/dist/validation/command-builder.d.ts.map +1 -0
- package/dist/validation/command-builder.js +143 -0
- package/dist/validation/command-builder.js.map +1 -0
- package/dist/validation/index.d.ts +8 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +12 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/input-schema.d.ts +85 -0
- package/dist/validation/input-schema.d.ts.map +1 -0
- package/dist/validation/input-schema.js +113 -0
- package/dist/validation/input-schema.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunTestTool - テスト実行ツール
|
|
3
|
+
*
|
|
4
|
+
* MCPサーバが公開する唯一のツール。
|
|
5
|
+
* 入力バリデーション、コマンド構築、プロセス実行、レポート生成を統合する。
|
|
6
|
+
*
|
|
7
|
+
* Requirements:
|
|
8
|
+
* - 2.1-2.6: テスト実行コマンドの制限
|
|
9
|
+
* - 6.1-6.5: MCPレスポンスの必須フィールド
|
|
10
|
+
*/
|
|
11
|
+
import { validateRunTestInput } from '../validation/input-schema.js';
|
|
12
|
+
import { buildCommand } from '../validation/command-builder.js';
|
|
13
|
+
import { createProcessExecutor } from '../executor/process-executor.js';
|
|
14
|
+
import { createReportGenerator } from '../report/report-generator.js';
|
|
15
|
+
import { createLogExtractor, formatExtractedBlocks } from '../report/log-extractor.js';
|
|
16
|
+
import { normalizePath } from '../utils/path-validator.js';
|
|
17
|
+
/**
|
|
18
|
+
* デフォルトの末尾行数
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_TAIL_LINE_COUNT = 20;
|
|
21
|
+
/**
|
|
22
|
+
* ログエントリを結合してログ文字列を生成する
|
|
23
|
+
*
|
|
24
|
+
* @param logs - ログエントリの配列
|
|
25
|
+
* @returns 結合されたログ文字列
|
|
26
|
+
*/
|
|
27
|
+
function combineLogEntries(logs) {
|
|
28
|
+
return logs.map(entry => entry.data).join('');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* RunTestToolクラス
|
|
32
|
+
*
|
|
33
|
+
* テスト実行の全体フローを管理する
|
|
34
|
+
*/
|
|
35
|
+
export class RunTestTool {
|
|
36
|
+
repoRoot;
|
|
37
|
+
tailLineCount;
|
|
38
|
+
/**
|
|
39
|
+
* @param options - ツールオプション
|
|
40
|
+
*/
|
|
41
|
+
constructor(options = {}) {
|
|
42
|
+
this.repoRoot = options.repoRoot || process.cwd();
|
|
43
|
+
this.tailLineCount = options.tailLineCount || DEFAULT_TAIL_LINE_COUNT;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* テストを実行する
|
|
47
|
+
*
|
|
48
|
+
* @param input - 入力パラメータ(未検証)
|
|
49
|
+
* @returns 実行結果
|
|
50
|
+
*/
|
|
51
|
+
async execute(input) {
|
|
52
|
+
const startTime = Date.now();
|
|
53
|
+
// 1. 入力バリデーション
|
|
54
|
+
const validationResult = validateRunTestInput(input);
|
|
55
|
+
if (!validationResult.success || !validationResult.data) {
|
|
56
|
+
return this.createErrorResponse(startTime, `入力バリデーションエラー: ${validationResult.errors?.join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
const validInput = validationResult.data;
|
|
59
|
+
// 2. targetパスの検証(scope が file/pattern の場合)
|
|
60
|
+
if (validInput.target) {
|
|
61
|
+
const pathResult = normalizePath(validInput.target, this.repoRoot);
|
|
62
|
+
if (!pathResult.valid) {
|
|
63
|
+
return this.createErrorResponse(startTime, `パス検証エラー: ${pathResult.error}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// 3. report_dirパスの検証(指定されている場合)
|
|
67
|
+
let reportDirBase;
|
|
68
|
+
if (validInput.report_dir) {
|
|
69
|
+
const pathResult = normalizePath(validInput.report_dir, this.repoRoot);
|
|
70
|
+
if (!pathResult.valid) {
|
|
71
|
+
return this.createErrorResponse(startTime, `レポートディレクトリパス検証エラー: ${pathResult.error}`);
|
|
72
|
+
}
|
|
73
|
+
reportDirBase = validInput.report_dir;
|
|
74
|
+
}
|
|
75
|
+
// 4. コマンド構築
|
|
76
|
+
let commandResult;
|
|
77
|
+
try {
|
|
78
|
+
commandResult = buildCommand(validInput.runner, validInput.scope, validInput.target);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
82
|
+
return this.createErrorResponse(startTime, `コマンド構築エラー: ${message}`);
|
|
83
|
+
}
|
|
84
|
+
// 5. プロセス実行
|
|
85
|
+
const executor = createProcessExecutor();
|
|
86
|
+
let processResult;
|
|
87
|
+
try {
|
|
88
|
+
processResult = await executor.execute(commandResult.command, commandResult.args, {
|
|
89
|
+
cwd: this.repoRoot,
|
|
90
|
+
timeoutMs: validInput.timeout_ms,
|
|
91
|
+
noOutputTimeoutMs: validInput.no_output_timeout_ms,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
96
|
+
return this.createErrorResponse(startTime, `プロセス実行エラー: ${message}`);
|
|
97
|
+
}
|
|
98
|
+
// 6. レポート生成
|
|
99
|
+
const reportGenerator = createReportGenerator();
|
|
100
|
+
let reportDir;
|
|
101
|
+
let artifacts;
|
|
102
|
+
try {
|
|
103
|
+
// レポートディレクトリ作成
|
|
104
|
+
reportDir = await reportGenerator.createReportDir(reportDirBase);
|
|
105
|
+
// ログ抽出
|
|
106
|
+
const logExtractor = createLogExtractor();
|
|
107
|
+
const combinedLog = combineLogEntries(processResult.logs);
|
|
108
|
+
const extractedBlocks = logExtractor.extractImportantLines(combinedLog, validInput.max_output_bytes);
|
|
109
|
+
const tailLines = logExtractor.getTailLines(combinedLog, this.tailLineCount);
|
|
110
|
+
// 要約情報を構築
|
|
111
|
+
const summary = {
|
|
112
|
+
command: commandResult.command,
|
|
113
|
+
args: commandResult.args,
|
|
114
|
+
status: processResult.status,
|
|
115
|
+
exitCode: processResult.exitCode,
|
|
116
|
+
durationMs: processResult.durationMs,
|
|
117
|
+
excerpts: extractedBlocks.map(block => block.matchedLine),
|
|
118
|
+
tailLines,
|
|
119
|
+
};
|
|
120
|
+
// 成果物を生成
|
|
121
|
+
artifacts = await reportGenerator.writeAll(reportDir, processResult.logs, summary);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
125
|
+
return this.createErrorResponse(startTime, `レポート生成エラー: ${message}`);
|
|
126
|
+
}
|
|
127
|
+
// 7. 抜粋を生成
|
|
128
|
+
const logExtractor = createLogExtractor();
|
|
129
|
+
const combinedLog = combineLogEntries(processResult.logs);
|
|
130
|
+
const extractedBlocks = logExtractor.extractImportantLines(combinedLog, validInput.max_output_bytes);
|
|
131
|
+
const excerpt = formatExtractedBlocks(extractedBlocks);
|
|
132
|
+
// 8. レスポンス生成
|
|
133
|
+
return {
|
|
134
|
+
status: processResult.status,
|
|
135
|
+
exit_code: processResult.exitCode,
|
|
136
|
+
duration_ms: processResult.durationMs,
|
|
137
|
+
report_dir: reportDir,
|
|
138
|
+
artifacts: {
|
|
139
|
+
raw_log: artifacts.rawLog,
|
|
140
|
+
summary_md: artifacts.summaryMd,
|
|
141
|
+
summary_json: artifacts.summaryJson,
|
|
142
|
+
},
|
|
143
|
+
excerpt,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* エラーレスポンスを生成する
|
|
148
|
+
*
|
|
149
|
+
* @param startTime - 開始時刻
|
|
150
|
+
* @param errorMessage - エラーメッセージ
|
|
151
|
+
* @returns エラーレスポンス
|
|
152
|
+
*/
|
|
153
|
+
createErrorResponse(startTime, errorMessage) {
|
|
154
|
+
return {
|
|
155
|
+
status: 'error',
|
|
156
|
+
exit_code: null,
|
|
157
|
+
duration_ms: Date.now() - startTime,
|
|
158
|
+
report_dir: '',
|
|
159
|
+
artifacts: {
|
|
160
|
+
raw_log: '',
|
|
161
|
+
summary_md: '',
|
|
162
|
+
summary_json: '',
|
|
163
|
+
},
|
|
164
|
+
excerpt: '',
|
|
165
|
+
error_message: errorMessage,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* RunTestToolのファクトリ関数
|
|
171
|
+
*
|
|
172
|
+
* @param options - ツールオプション
|
|
173
|
+
* @returns 新しいRunTestToolインスタンス
|
|
174
|
+
*/
|
|
175
|
+
export function createRunTestTool(options) {
|
|
176
|
+
return new RunTestTool(options);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=run-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-test.js","sourceRoot":"","sources":["../../src/tools/run-test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* パス検証結果
|
|
3
|
+
*/
|
|
4
|
+
export interface PathValidationResult {
|
|
5
|
+
/** 検証が成功したかどうか */
|
|
6
|
+
valid: boolean;
|
|
7
|
+
/** 正規化されたパス(validがtrueの場合のみ有効) */
|
|
8
|
+
normalizedPath?: string;
|
|
9
|
+
/** エラーメッセージ(validがfalseの場合のみ有効) */
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* パスがリポジトリ配下かを検証する
|
|
14
|
+
*
|
|
15
|
+
* @param targetPath - 検証対象のパス
|
|
16
|
+
* @param repoRoot - リポジトリのルートパス(絶対パス)
|
|
17
|
+
* @returns 検証結果
|
|
18
|
+
*/
|
|
19
|
+
export declare function isUnderRepo(targetPath: string, repoRoot: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* パスを正規化し、リポジトリ配下であることを検証する
|
|
22
|
+
*
|
|
23
|
+
* @param targetPath - 正規化対象のパス
|
|
24
|
+
* @param repoRoot - リポジトリのルートパス(絶対パス)
|
|
25
|
+
* @returns 検証結果(正規化されたパスまたはエラー)
|
|
26
|
+
*/
|
|
27
|
+
export declare function normalizePath(targetPath: string, repoRoot: string): PathValidationResult;
|
|
28
|
+
/**
|
|
29
|
+
* PathValidatorクラス(オブジェクト指向インターフェース)
|
|
30
|
+
*
|
|
31
|
+
* リポジトリルートを保持し、パス検証を行う
|
|
32
|
+
*/
|
|
33
|
+
export declare class PathValidator {
|
|
34
|
+
private readonly repoRoot;
|
|
35
|
+
/**
|
|
36
|
+
* @param repoRoot - リポジトリのルートパス
|
|
37
|
+
*/
|
|
38
|
+
constructor(repoRoot: string);
|
|
39
|
+
/**
|
|
40
|
+
* パスがリポジトリ配下かを検証する
|
|
41
|
+
*/
|
|
42
|
+
isUnderRepo(targetPath: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* パスを正規化し、リポジトリ配下であることを検証する
|
|
45
|
+
*/
|
|
46
|
+
normalizePath(targetPath: string): PathValidationResult;
|
|
47
|
+
/**
|
|
48
|
+
* リポジトリルートを取得する
|
|
49
|
+
*/
|
|
50
|
+
getRepoRoot(): string;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=path-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-validator.d.ts","sourceRoot":"","sources":["../../src/utils/path-validator.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,kBAAkB;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAqCzE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,oBAAoB,CA6CxF;AAED;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC;;OAEG;gBACS,QAAQ,EAAE,MAAM;IAI5B;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIxC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB;IAIvD;;OAEG;IACH,WAAW,IAAI,MAAM;CAGtB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PathValidator - パス検証ユーティリティ
|
|
3
|
+
*
|
|
4
|
+
* パスがリポジトリ配下かを検証し、パストラバーサル攻撃を防ぐ。
|
|
5
|
+
*
|
|
6
|
+
* Requirements:
|
|
7
|
+
* - 2.6: targetパラメータがリポジトリ配下であることを検証
|
|
8
|
+
* - 4.6: report_dirがリポジトリ配下であることを検証
|
|
9
|
+
* - 4.7: リポジトリ外を指すパスはエラーを返す
|
|
10
|
+
*/
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
/**
|
|
13
|
+
* パスがリポジトリ配下かを検証する
|
|
14
|
+
*
|
|
15
|
+
* @param targetPath - 検証対象のパス
|
|
16
|
+
* @param repoRoot - リポジトリのルートパス(絶対パス)
|
|
17
|
+
* @returns 検証結果
|
|
18
|
+
*/
|
|
19
|
+
export function isUnderRepo(targetPath, repoRoot) {
|
|
20
|
+
// 空文字列チェック
|
|
21
|
+
if (!targetPath || !repoRoot) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
// repoRootを正規化(絶対パスに変換)
|
|
25
|
+
const normalizedRepoRoot = path.resolve(repoRoot);
|
|
26
|
+
// targetPathを正規化
|
|
27
|
+
// 相対パスの場合はrepoRootを基準に解決
|
|
28
|
+
const absoluteTargetPath = path.isAbsolute(targetPath)
|
|
29
|
+
? path.resolve(targetPath)
|
|
30
|
+
: path.resolve(normalizedRepoRoot, targetPath);
|
|
31
|
+
// 正規化されたパスがrepoRoot配下かチェック
|
|
32
|
+
// path.relative()を使用して相対パスを計算し、
|
|
33
|
+
// 「..」で始まる場合はリポジトリ外
|
|
34
|
+
const relativePath = path.relative(normalizedRepoRoot, absoluteTargetPath);
|
|
35
|
+
// 相対パスが空(同じディレクトリ)または「..」で始まらない場合は配下
|
|
36
|
+
if (relativePath === '') {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
// 「..」で始まる場合はリポジトリ外
|
|
40
|
+
if (relativePath.startsWith('..')) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
// Windowsの場合、絶対パスが異なるドライブを指している可能性
|
|
44
|
+
// path.relative()は異なるドライブの場合、絶対パスを返す
|
|
45
|
+
if (path.isAbsolute(relativePath)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* パスを正規化し、リポジトリ配下であることを検証する
|
|
52
|
+
*
|
|
53
|
+
* @param targetPath - 正規化対象のパス
|
|
54
|
+
* @param repoRoot - リポジトリのルートパス(絶対パス)
|
|
55
|
+
* @returns 検証結果(正規化されたパスまたはエラー)
|
|
56
|
+
*/
|
|
57
|
+
export function normalizePath(targetPath, repoRoot) {
|
|
58
|
+
// 空文字列チェック
|
|
59
|
+
if (!targetPath) {
|
|
60
|
+
return {
|
|
61
|
+
valid: false,
|
|
62
|
+
error: 'パスが空です',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (!repoRoot) {
|
|
66
|
+
return {
|
|
67
|
+
valid: false,
|
|
68
|
+
error: 'リポジトリルートが指定されていません',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// repoRootを正規化
|
|
72
|
+
const normalizedRepoRoot = path.resolve(repoRoot);
|
|
73
|
+
// 絶対パスの場合は拒否(相対パスのみ許可)
|
|
74
|
+
if (path.isAbsolute(targetPath)) {
|
|
75
|
+
return {
|
|
76
|
+
valid: false,
|
|
77
|
+
error: `絶対パスは許可されていません: ${targetPath}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// パスを正規化
|
|
81
|
+
const absolutePath = path.resolve(normalizedRepoRoot, targetPath);
|
|
82
|
+
// リポジトリ配下かチェック
|
|
83
|
+
if (!isUnderRepo(absolutePath, normalizedRepoRoot)) {
|
|
84
|
+
return {
|
|
85
|
+
valid: false,
|
|
86
|
+
error: `パスがリポジトリ外を指しています: ${targetPath}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// リポジトリルートからの相対パスを返す
|
|
90
|
+
const relativePath = path.relative(normalizedRepoRoot, absolutePath);
|
|
91
|
+
return {
|
|
92
|
+
valid: true,
|
|
93
|
+
normalizedPath: relativePath || '.',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* PathValidatorクラス(オブジェクト指向インターフェース)
|
|
98
|
+
*
|
|
99
|
+
* リポジトリルートを保持し、パス検証を行う
|
|
100
|
+
*/
|
|
101
|
+
export class PathValidator {
|
|
102
|
+
repoRoot;
|
|
103
|
+
/**
|
|
104
|
+
* @param repoRoot - リポジトリのルートパス
|
|
105
|
+
*/
|
|
106
|
+
constructor(repoRoot) {
|
|
107
|
+
this.repoRoot = path.resolve(repoRoot);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* パスがリポジトリ配下かを検証する
|
|
111
|
+
*/
|
|
112
|
+
isUnderRepo(targetPath) {
|
|
113
|
+
return isUnderRepo(targetPath, this.repoRoot);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* パスを正規化し、リポジトリ配下であることを検証する
|
|
117
|
+
*/
|
|
118
|
+
normalizePath(targetPath) {
|
|
119
|
+
return normalizePath(targetPath, this.repoRoot);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* リポジトリルートを取得する
|
|
123
|
+
*/
|
|
124
|
+
getRepoRoot() {
|
|
125
|
+
return this.repoRoot;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=path-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-validator.js","sourceRoot":"","sources":["../../src/utils/path-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* コマンドビルダー
|
|
3
|
+
*
|
|
4
|
+
* 入力パラメータから安全なコマンドと引数を生成する。
|
|
5
|
+
* 固定テンプレートのみを許可し、任意コマンド実行を防ぐ。
|
|
6
|
+
*
|
|
7
|
+
* Requirements:
|
|
8
|
+
* - 2.1: 固定テンプレート(flutter test)のみを実行
|
|
9
|
+
* - 2.3: scope が all の場合、flutter test を実行
|
|
10
|
+
* - 2.4: scope が file の場合、flutter test <target> を実行
|
|
11
|
+
* - 2.5: scope が pattern の場合、flutter test --name <target> を実行
|
|
12
|
+
*/
|
|
13
|
+
import type { Runner, Scope } from './input-schema.js';
|
|
14
|
+
/**
|
|
15
|
+
* コマンド構築結果
|
|
16
|
+
*/
|
|
17
|
+
export interface CommandResult {
|
|
18
|
+
/** 実行するコマンド */
|
|
19
|
+
command: string;
|
|
20
|
+
/** コマンド引数 */
|
|
21
|
+
args: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 入力パラメータからコマンドと引数を構築する
|
|
25
|
+
*
|
|
26
|
+
* 固定テンプレートのみを使用し、任意コマンド実行を防ぐ。
|
|
27
|
+
*
|
|
28
|
+
* @param runner - テストランナー
|
|
29
|
+
* @param scope - テスト実行スコープ
|
|
30
|
+
* @param target - テスト対象(scope が file/pattern の場合)
|
|
31
|
+
* @returns コマンド構築結果
|
|
32
|
+
* @throws Error - 不正なパラメータの場合
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildCommand(runner: Runner, scope: Scope, target?: string): CommandResult;
|
|
35
|
+
/**
|
|
36
|
+
* コマンド文字列を生成する(表示用)
|
|
37
|
+
*
|
|
38
|
+
* @param result - コマンド構築結果
|
|
39
|
+
* @returns コマンド文字列
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatCommand(result: CommandResult): string;
|
|
42
|
+
/**
|
|
43
|
+
* 許可されたコマンドパターンかどうかを検証する
|
|
44
|
+
*
|
|
45
|
+
* 生成されたコマンドが固定テンプレートに一致するかチェック
|
|
46
|
+
*
|
|
47
|
+
* @param command - コマンド
|
|
48
|
+
* @param args - 引数
|
|
49
|
+
* @returns 許可されたパターンの場合 true
|
|
50
|
+
*/
|
|
51
|
+
export declare function isAllowedCommandPattern(command: string, args: string[]): boolean;
|
|
52
|
+
//# sourceMappingURL=command-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-builder.d.ts","sourceRoot":"","sources":["../../src/validation/command-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AA2CD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,aAAa,CAqCf;AA+BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAmChF"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 許可されたコマンドテンプレート
|
|
3
|
+
*
|
|
4
|
+
* runner -> scope -> template のマッピング
|
|
5
|
+
*/
|
|
6
|
+
const COMMAND_TEMPLATES = {
|
|
7
|
+
flutter: {
|
|
8
|
+
all: {
|
|
9
|
+
command: 'flutter',
|
|
10
|
+
baseArgs: ['test'],
|
|
11
|
+
},
|
|
12
|
+
file: {
|
|
13
|
+
command: 'flutter',
|
|
14
|
+
baseArgs: ['test'],
|
|
15
|
+
targetArgFormat: 'positional',
|
|
16
|
+
},
|
|
17
|
+
pattern: {
|
|
18
|
+
command: 'flutter',
|
|
19
|
+
baseArgs: ['test'],
|
|
20
|
+
targetArgFormat: 'named',
|
|
21
|
+
targetFlag: '--name',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* 入力パラメータからコマンドと引数を構築する
|
|
27
|
+
*
|
|
28
|
+
* 固定テンプレートのみを使用し、任意コマンド実行を防ぐ。
|
|
29
|
+
*
|
|
30
|
+
* @param runner - テストランナー
|
|
31
|
+
* @param scope - テスト実行スコープ
|
|
32
|
+
* @param target - テスト対象(scope が file/pattern の場合)
|
|
33
|
+
* @returns コマンド構築結果
|
|
34
|
+
* @throws Error - 不正なパラメータの場合
|
|
35
|
+
*/
|
|
36
|
+
export function buildCommand(runner, scope, target) {
|
|
37
|
+
// テンプレートを取得
|
|
38
|
+
const runnerTemplates = COMMAND_TEMPLATES[runner];
|
|
39
|
+
if (!runnerTemplates) {
|
|
40
|
+
throw new Error(`許可されていないランナー: ${runner}`);
|
|
41
|
+
}
|
|
42
|
+
const template = runnerTemplates[scope];
|
|
43
|
+
if (!template) {
|
|
44
|
+
throw new Error(`許可されていないスコープ: ${scope}`);
|
|
45
|
+
}
|
|
46
|
+
// 引数を構築
|
|
47
|
+
const args = [...template.baseArgs];
|
|
48
|
+
// targetが必要な場合
|
|
49
|
+
if (template.targetArgFormat) {
|
|
50
|
+
if (!target) {
|
|
51
|
+
throw new Error(`scope '${scope}' の場合、target は必須です`);
|
|
52
|
+
}
|
|
53
|
+
// targetの安全性チェック(シェルインジェクション防止)
|
|
54
|
+
validateTarget(target);
|
|
55
|
+
if (template.targetArgFormat === 'positional') {
|
|
56
|
+
// 位置引数として追加
|
|
57
|
+
args.push(target);
|
|
58
|
+
}
|
|
59
|
+
else if (template.targetArgFormat === 'named' && template.targetFlag) {
|
|
60
|
+
// 名前付き引数として追加
|
|
61
|
+
args.push(template.targetFlag, target);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
command: template.command,
|
|
66
|
+
args,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* targetの安全性を検証する
|
|
71
|
+
*
|
|
72
|
+
* シェルインジェクションを防ぐため、危険な文字をチェック
|
|
73
|
+
*
|
|
74
|
+
* @param target - 検証対象のtarget
|
|
75
|
+
* @throws Error - 危険な文字が含まれる場合
|
|
76
|
+
*/
|
|
77
|
+
function validateTarget(target) {
|
|
78
|
+
// 空文字列チェック
|
|
79
|
+
if (!target || target.trim() === '') {
|
|
80
|
+
throw new Error('target は空にできません');
|
|
81
|
+
}
|
|
82
|
+
// 危険な文字のパターン
|
|
83
|
+
// シェルメタ文字やコマンド区切り文字を禁止
|
|
84
|
+
const dangerousPatterns = [
|
|
85
|
+
/[;&|`$(){}[\]<>]/, // シェルメタ文字
|
|
86
|
+
/\n|\r/, // 改行文字
|
|
87
|
+
/^\s*-/, // 先頭のハイフン(オプション注入防止)
|
|
88
|
+
];
|
|
89
|
+
for (const pattern of dangerousPatterns) {
|
|
90
|
+
if (pattern.test(target)) {
|
|
91
|
+
throw new Error(`target に不正な文字が含まれています: ${target}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* コマンド文字列を生成する(表示用)
|
|
97
|
+
*
|
|
98
|
+
* @param result - コマンド構築結果
|
|
99
|
+
* @returns コマンド文字列
|
|
100
|
+
*/
|
|
101
|
+
export function formatCommand(result) {
|
|
102
|
+
return `${result.command} ${result.args.join(' ')}`.trim();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 許可されたコマンドパターンかどうかを検証する
|
|
106
|
+
*
|
|
107
|
+
* 生成されたコマンドが固定テンプレートに一致するかチェック
|
|
108
|
+
*
|
|
109
|
+
* @param command - コマンド
|
|
110
|
+
* @param args - 引数
|
|
111
|
+
* @returns 許可されたパターンの場合 true
|
|
112
|
+
*/
|
|
113
|
+
export function isAllowedCommandPattern(command, args) {
|
|
114
|
+
// flutter test パターンのみ許可
|
|
115
|
+
if (command !== 'flutter') {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (args.length === 0 || args[0] !== 'test') {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
// 許可されたパターン:
|
|
122
|
+
// 1. flutter test
|
|
123
|
+
// 2. flutter test <target>
|
|
124
|
+
// 3. flutter test --name <target>
|
|
125
|
+
if (args.length === 1) {
|
|
126
|
+
// flutter test
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
if (args.length === 2) {
|
|
130
|
+
// flutter test <target>
|
|
131
|
+
// targetが--で始まらないこと
|
|
132
|
+
const arg1 = args[1];
|
|
133
|
+
return arg1 !== undefined && !arg1.startsWith('--');
|
|
134
|
+
}
|
|
135
|
+
if (args.length === 3) {
|
|
136
|
+
// flutter test --name <target>
|
|
137
|
+
const arg1 = args[1];
|
|
138
|
+
const arg2 = args[2];
|
|
139
|
+
return arg1 === '--name' && arg2 !== undefined && !arg2.startsWith('--');
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=command-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-builder.js","sourceRoot":"","sources":["../../src/validation/command-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* バリデーションモジュール
|
|
3
|
+
*
|
|
4
|
+
* 入力スキーマとコマンドビルダーをエクスポート
|
|
5
|
+
*/
|
|
6
|
+
export { runTestInputSchema, validateRunTestInput, isAllowedRunner, isAllowedScope, ALLOWED_RUNNERS, ALLOWED_SCOPES, type Runner, type Scope, type RunTestInput, type ValidationResult, } from './input-schema.js';
|
|
7
|
+
export { buildCommand, formatCommand, isAllowedCommandPattern, type CommandResult, } from './command-builder.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAEL,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,eAAe,EACf,cAAc,EACd,KAAK,MAAM,EACX,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAEL,YAAY,EACZ,aAAa,EACb,uBAAuB,EACvB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* バリデーションモジュール
|
|
3
|
+
*
|
|
4
|
+
* 入力スキーマとコマンドビルダーをエクスポート
|
|
5
|
+
*/
|
|
6
|
+
export {
|
|
7
|
+
// 入力スキーマ
|
|
8
|
+
runTestInputSchema, validateRunTestInput, isAllowedRunner, isAllowedScope, ALLOWED_RUNNERS, ALLOWED_SCOPES, } from './input-schema.js';
|
|
9
|
+
export {
|
|
10
|
+
// コマンドビルダー
|
|
11
|
+
buildCommand, formatCommand, isAllowedCommandPattern, } from './command-builder.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO;AACL,SAAS;AACT,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,eAAe,EACf,cAAc,GAKf,MAAM,mBAAmB,CAAC;AAE3B,OAAO;AACL,WAAW;AACX,YAAY,EACZ,aAAa,EACb,uBAAuB,GAExB,MAAM,sBAAsB,CAAC"}
|