@nahisaho/katashiro-security 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/package.json +45 -0
- package/src/action-logger.ts +327 -0
- package/src/index.ts +42 -0
- package/src/security-analyzer.ts +311 -0
- package/src/types.ts +351 -0
- package/tests/action-logger.test.ts +372 -0
- package/tests/security-analyzer.test.ts +364 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecurityAnalyzer - セキュリティ分析クラス
|
|
3
|
+
*
|
|
4
|
+
* @requirement REQ-012
|
|
5
|
+
* @design REQ-012-01 リスクレベル評価
|
|
6
|
+
* @design REQ-012-02 確認プロンプト判定
|
|
7
|
+
* @design REQ-012-03 拒否パターンブロック
|
|
8
|
+
* @design REQ-012-04 許可パターン判定
|
|
9
|
+
* @design REQ-012-06 ファイル削除=高リスク
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import micromatch from 'micromatch';
|
|
13
|
+
import {
|
|
14
|
+
Action,
|
|
15
|
+
SecurityPolicy,
|
|
16
|
+
SecurityAnalysis,
|
|
17
|
+
RiskLevel,
|
|
18
|
+
RiskRule,
|
|
19
|
+
PatternRule,
|
|
20
|
+
BUILTIN_RISK_RULES,
|
|
21
|
+
DEFAULT_SECURITY_POLICY,
|
|
22
|
+
isRiskLevelAtLeast,
|
|
23
|
+
RISK_LEVEL_ORDER,
|
|
24
|
+
SecurityError,
|
|
25
|
+
} from './types';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* SecurityAnalyzerオプション
|
|
29
|
+
*/
|
|
30
|
+
export interface SecurityAnalyzerOptions {
|
|
31
|
+
/** カスタムポリシー */
|
|
32
|
+
policy?: Partial<SecurityPolicy>;
|
|
33
|
+
/** ビルトインルールを使用するか */
|
|
34
|
+
useBuiltinRules?: boolean;
|
|
35
|
+
/** 追加のリスクルール */
|
|
36
|
+
additionalRules?: RiskRule[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* セキュリティ分析器
|
|
41
|
+
*/
|
|
42
|
+
export class SecurityAnalyzer {
|
|
43
|
+
private readonly policy: SecurityPolicy;
|
|
44
|
+
private readonly riskRules: RiskRule[];
|
|
45
|
+
|
|
46
|
+
constructor(options: SecurityAnalyzerOptions = {}) {
|
|
47
|
+
// ポリシーをマージ
|
|
48
|
+
this.policy = {
|
|
49
|
+
...DEFAULT_SECURITY_POLICY,
|
|
50
|
+
...options.policy,
|
|
51
|
+
allowPatterns: [
|
|
52
|
+
...(options.policy?.allowPatterns ?? DEFAULT_SECURITY_POLICY.allowPatterns),
|
|
53
|
+
],
|
|
54
|
+
denyPatterns: [
|
|
55
|
+
...(options.policy?.denyPatterns ?? DEFAULT_SECURITY_POLICY.denyPatterns),
|
|
56
|
+
],
|
|
57
|
+
requireConfirmation:
|
|
58
|
+
options.policy?.requireConfirmation ?? DEFAULT_SECURITY_POLICY.requireConfirmation,
|
|
59
|
+
customRiskRules: [
|
|
60
|
+
...(DEFAULT_SECURITY_POLICY.customRiskRules ?? []),
|
|
61
|
+
...(options.policy?.customRiskRules ?? []),
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// リスクルールを構築
|
|
66
|
+
const builtinRules = options.useBuiltinRules !== false ? BUILTIN_RISK_RULES : [];
|
|
67
|
+
this.riskRules = [
|
|
68
|
+
...builtinRules,
|
|
69
|
+
...(this.policy.customRiskRules ?? []),
|
|
70
|
+
...(options.additionalRules ?? []),
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* アクションを分析(REQ-012-01)
|
|
76
|
+
*/
|
|
77
|
+
analyze(action: Action): SecurityAnalysis {
|
|
78
|
+
const matchedRules: string[] = [];
|
|
79
|
+
const reasons: string[] = [];
|
|
80
|
+
|
|
81
|
+
// 1. 拒否パターンチェック(REQ-012-03)
|
|
82
|
+
const denyMatch = this.checkDenyPatterns(action);
|
|
83
|
+
if (denyMatch) {
|
|
84
|
+
return {
|
|
85
|
+
riskLevel: 'critical',
|
|
86
|
+
reasons: [`Action blocked by deny pattern: ${denyMatch.pattern}`],
|
|
87
|
+
requiresConfirmation: false,
|
|
88
|
+
allowed: false,
|
|
89
|
+
blockReason: denyMatch.description ?? `Matches deny pattern: ${denyMatch.pattern}`,
|
|
90
|
+
matchedRules: ['deny_pattern'],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 2. リスクレベル評価
|
|
95
|
+
let riskLevel = this.evaluateRiskLevel(action, matchedRules, reasons);
|
|
96
|
+
|
|
97
|
+
// 3. 許可パターンチェック(REQ-012-04)
|
|
98
|
+
// 注意: 高リスクアクション(削除など)は許可パターンでリスクを下げない
|
|
99
|
+
const allowMatch = this.checkAllowPatterns(action);
|
|
100
|
+
const highRiskActionTypes = ['file_delete', 'directory_delete', 'command_execute'];
|
|
101
|
+
if (allowMatch && !highRiskActionTypes.includes(action.type)) {
|
|
102
|
+
// 許可パターンにマッチした場合、リスクレベルを下げる
|
|
103
|
+
if (riskLevel !== 'critical') {
|
|
104
|
+
riskLevel = 'low';
|
|
105
|
+
reasons.push(`Matched allow pattern: ${allowMatch.pattern}`);
|
|
106
|
+
matchedRules.push('allow_pattern');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 4. 最大リスクレベルチェック
|
|
111
|
+
if (isRiskLevelAtLeast(riskLevel, this.policy.maxRiskLevel)) {
|
|
112
|
+
if (RISK_LEVEL_ORDER[riskLevel] > RISK_LEVEL_ORDER[this.policy.maxRiskLevel]) {
|
|
113
|
+
return {
|
|
114
|
+
riskLevel,
|
|
115
|
+
reasons,
|
|
116
|
+
requiresConfirmation: false,
|
|
117
|
+
allowed: false,
|
|
118
|
+
blockReason: `Risk level ${riskLevel} exceeds maximum allowed ${this.policy.maxRiskLevel}`,
|
|
119
|
+
matchedRules,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 5. 確認が必要か判定(REQ-012-02)
|
|
125
|
+
const requiresConfirmation = this.policy.requireConfirmation.includes(riskLevel);
|
|
126
|
+
if (requiresConfirmation) {
|
|
127
|
+
reasons.push(`Risk level ${riskLevel} requires confirmation`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
riskLevel,
|
|
132
|
+
reasons,
|
|
133
|
+
requiresConfirmation,
|
|
134
|
+
allowed: true,
|
|
135
|
+
matchedRules,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* アクションの実行を検証(確認が不要な場合のみ許可)
|
|
141
|
+
*/
|
|
142
|
+
validateAction(action: Action): void {
|
|
143
|
+
const analysis = this.analyze(action);
|
|
144
|
+
|
|
145
|
+
if (!analysis.allowed) {
|
|
146
|
+
throw new SecurityError('ACTION_BLOCKED', analysis.blockReason ?? 'Action blocked', analysis);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (analysis.requiresConfirmation) {
|
|
150
|
+
throw new SecurityError(
|
|
151
|
+
'CONFIRMATION_REQUIRED',
|
|
152
|
+
`Action requires confirmation: ${analysis.reasons.join(', ')}`,
|
|
153
|
+
analysis
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 確認付きでアクションを検証
|
|
160
|
+
*/
|
|
161
|
+
validateActionWithConfirmation(action: Action, confirmed: boolean): void {
|
|
162
|
+
const analysis = this.analyze(action);
|
|
163
|
+
|
|
164
|
+
if (!analysis.allowed) {
|
|
165
|
+
throw new SecurityError('ACTION_BLOCKED', analysis.blockReason ?? 'Action blocked', analysis);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (analysis.requiresConfirmation && !confirmed) {
|
|
169
|
+
throw new SecurityError(
|
|
170
|
+
'CONFIRMATION_DENIED',
|
|
171
|
+
'User did not confirm the action',
|
|
172
|
+
analysis
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 拒否パターンをチェック(REQ-012-03)
|
|
179
|
+
*/
|
|
180
|
+
private checkDenyPatterns(action: Action): PatternRule | null {
|
|
181
|
+
if (!action.target) return null;
|
|
182
|
+
|
|
183
|
+
for (const rule of this.policy.denyPatterns) {
|
|
184
|
+
// アクションタイプフィルター
|
|
185
|
+
if (rule.actionTypes && !rule.actionTypes.includes(action.type)) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// パターンマッチ
|
|
190
|
+
if (micromatch.isMatch(action.target, rule.pattern)) {
|
|
191
|
+
return rule;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 許可パターンをチェック(REQ-012-04)
|
|
200
|
+
*/
|
|
201
|
+
private checkAllowPatterns(action: Action): PatternRule | null {
|
|
202
|
+
if (!action.target) return null;
|
|
203
|
+
|
|
204
|
+
for (const rule of this.policy.allowPatterns) {
|
|
205
|
+
// アクションタイプフィルター
|
|
206
|
+
if (rule.actionTypes && !rule.actionTypes.includes(action.type)) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// パターンマッチ
|
|
211
|
+
if (micromatch.isMatch(action.target, rule.pattern)) {
|
|
212
|
+
return rule;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* リスクレベルを評価
|
|
221
|
+
*/
|
|
222
|
+
private evaluateRiskLevel(
|
|
223
|
+
action: Action,
|
|
224
|
+
matchedRules: string[],
|
|
225
|
+
reasons: string[]
|
|
226
|
+
): RiskLevel {
|
|
227
|
+
let maxRiskLevel: RiskLevel = 'low';
|
|
228
|
+
|
|
229
|
+
for (const rule of this.riskRules) {
|
|
230
|
+
if (this.matchesRule(action, rule)) {
|
|
231
|
+
matchedRules.push(rule.name);
|
|
232
|
+
reasons.push(rule.description);
|
|
233
|
+
|
|
234
|
+
// より高いリスクレベルを採用
|
|
235
|
+
if (RISK_LEVEL_ORDER[rule.riskLevel] > RISK_LEVEL_ORDER[maxRiskLevel]) {
|
|
236
|
+
maxRiskLevel = rule.riskLevel;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return maxRiskLevel;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* ルールがアクションにマッチするかチェック
|
|
246
|
+
*/
|
|
247
|
+
private matchesRule(action: Action, rule: RiskRule): boolean {
|
|
248
|
+
const { match } = rule;
|
|
249
|
+
|
|
250
|
+
// アクションタイプチェック
|
|
251
|
+
if (match.actionTypes && !match.actionTypes.includes(action.type)) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ターゲットパターンチェック
|
|
256
|
+
if (match.targetPatterns && action.target) {
|
|
257
|
+
const matchesTarget = match.targetPatterns.some((pattern) =>
|
|
258
|
+
micromatch.isMatch(action.target!, pattern)
|
|
259
|
+
);
|
|
260
|
+
if (!matchesTarget) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// パラメータ条件チェック
|
|
266
|
+
if (match.paramConditions && action.params) {
|
|
267
|
+
for (const [key, value] of Object.entries(match.paramConditions)) {
|
|
268
|
+
if (action.params[key] !== value) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 現在のポリシーを取得
|
|
279
|
+
*/
|
|
280
|
+
getPolicy(): SecurityPolicy {
|
|
281
|
+
return { ...this.policy };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* リスクルールを取得
|
|
286
|
+
*/
|
|
287
|
+
getRiskRules(): RiskRule[] {
|
|
288
|
+
return [...this.riskRules];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* ポリシーを更新
|
|
293
|
+
*/
|
|
294
|
+
updatePolicy(update: Partial<SecurityPolicy>): void {
|
|
295
|
+
if (update.allowPatterns) {
|
|
296
|
+
this.policy.allowPatterns = update.allowPatterns;
|
|
297
|
+
}
|
|
298
|
+
if (update.denyPatterns) {
|
|
299
|
+
this.policy.denyPatterns = update.denyPatterns;
|
|
300
|
+
}
|
|
301
|
+
if (update.requireConfirmation) {
|
|
302
|
+
this.policy.requireConfirmation = update.requireConfirmation;
|
|
303
|
+
}
|
|
304
|
+
if (update.maxRiskLevel) {
|
|
305
|
+
this.policy.maxRiskLevel = update.maxRiskLevel;
|
|
306
|
+
}
|
|
307
|
+
if (update.customRiskRules) {
|
|
308
|
+
this.policy.customRiskRules = update.customRiskRules;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security 型定義
|
|
3
|
+
*
|
|
4
|
+
* @requirement REQ-012
|
|
5
|
+
* @design REQ-012-01〜REQ-012-06
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// リスクレベル
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* リスクレベル
|
|
14
|
+
*/
|
|
15
|
+
export type RiskLevel = 'low' | 'medium' | 'high' | 'critical';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* リスクレベルの数値マッピング
|
|
19
|
+
*/
|
|
20
|
+
export const RISK_LEVEL_ORDER: Record<RiskLevel, number> = {
|
|
21
|
+
low: 1,
|
|
22
|
+
medium: 2,
|
|
23
|
+
high: 3,
|
|
24
|
+
critical: 4,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* リスクレベル比較
|
|
29
|
+
*/
|
|
30
|
+
export function compareRiskLevels(a: RiskLevel, b: RiskLevel): number {
|
|
31
|
+
return RISK_LEVEL_ORDER[a] - RISK_LEVEL_ORDER[b];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* リスクレベルが閾値以上かチェック
|
|
36
|
+
*/
|
|
37
|
+
export function isRiskLevelAtLeast(level: RiskLevel, threshold: RiskLevel): boolean {
|
|
38
|
+
return RISK_LEVEL_ORDER[level] >= RISK_LEVEL_ORDER[threshold];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// アクション定義
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* アクションタイプ
|
|
47
|
+
*/
|
|
48
|
+
export type ActionType =
|
|
49
|
+
| 'file_read'
|
|
50
|
+
| 'file_write'
|
|
51
|
+
| 'file_delete'
|
|
52
|
+
| 'file_move'
|
|
53
|
+
| 'file_copy'
|
|
54
|
+
| 'directory_create'
|
|
55
|
+
| 'directory_delete'
|
|
56
|
+
| 'command_execute'
|
|
57
|
+
| 'network_request'
|
|
58
|
+
| 'browser_navigate'
|
|
59
|
+
| 'browser_click'
|
|
60
|
+
| 'browser_type'
|
|
61
|
+
| 'code_execute'
|
|
62
|
+
| 'search'
|
|
63
|
+
| 'analyze'
|
|
64
|
+
| 'custom';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* アクション
|
|
68
|
+
*/
|
|
69
|
+
export interface Action {
|
|
70
|
+
/** アクションタイプ */
|
|
71
|
+
type: ActionType;
|
|
72
|
+
/** アクション名 */
|
|
73
|
+
name: string;
|
|
74
|
+
/** ターゲット(ファイルパス、URL等) */
|
|
75
|
+
target?: string;
|
|
76
|
+
/** パラメータ */
|
|
77
|
+
params?: Record<string, unknown>;
|
|
78
|
+
/** コンテキスト情報 */
|
|
79
|
+
context?: ActionContext;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* アクションコンテキスト
|
|
84
|
+
*/
|
|
85
|
+
export interface ActionContext {
|
|
86
|
+
/** ユーザーID */
|
|
87
|
+
userId?: string;
|
|
88
|
+
/** セッションID */
|
|
89
|
+
sessionId?: string;
|
|
90
|
+
/** ソースツール名 */
|
|
91
|
+
sourceTool?: string;
|
|
92
|
+
/** 親タスクID */
|
|
93
|
+
parentTaskId?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// オブザベーション(実行結果)
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* オブザベーション(アクション実行結果)
|
|
102
|
+
*/
|
|
103
|
+
export interface Observation {
|
|
104
|
+
/** 成功フラグ */
|
|
105
|
+
success: boolean;
|
|
106
|
+
/** 結果データ */
|
|
107
|
+
data?: unknown;
|
|
108
|
+
/** エラーメッセージ */
|
|
109
|
+
error?: string;
|
|
110
|
+
/** 実行時間(ミリ秒) */
|
|
111
|
+
duration: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// セキュリティポリシー
|
|
116
|
+
// ============================================================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* パターンルール
|
|
120
|
+
*/
|
|
121
|
+
export interface PatternRule {
|
|
122
|
+
/** パターン(glob形式) */
|
|
123
|
+
pattern: string;
|
|
124
|
+
/** 説明 */
|
|
125
|
+
description?: string;
|
|
126
|
+
/** 適用するアクションタイプ */
|
|
127
|
+
actionTypes?: ActionType[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* セキュリティポリシー(REQ-012)
|
|
132
|
+
*/
|
|
133
|
+
export interface SecurityPolicy {
|
|
134
|
+
/** 許可パターン(REQ-012-04) */
|
|
135
|
+
allowPatterns: PatternRule[];
|
|
136
|
+
/** 拒否パターン(REQ-012-03) */
|
|
137
|
+
denyPatterns: PatternRule[];
|
|
138
|
+
/** 確認が必要なリスクレベル(REQ-012-02) */
|
|
139
|
+
requireConfirmation: RiskLevel[];
|
|
140
|
+
/** 最大許容リスクレベル */
|
|
141
|
+
maxRiskLevel: RiskLevel;
|
|
142
|
+
/** カスタムリスクルール */
|
|
143
|
+
customRiskRules?: RiskRule[];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* リスクルール
|
|
148
|
+
*/
|
|
149
|
+
export interface RiskRule {
|
|
150
|
+
/** ルール名 */
|
|
151
|
+
name: string;
|
|
152
|
+
/** 説明 */
|
|
153
|
+
description: string;
|
|
154
|
+
/** マッチ条件 */
|
|
155
|
+
match: RiskRuleMatch;
|
|
156
|
+
/** 適用するリスクレベル */
|
|
157
|
+
riskLevel: RiskLevel;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* リスクルールのマッチ条件
|
|
162
|
+
*/
|
|
163
|
+
export interface RiskRuleMatch {
|
|
164
|
+
/** アクションタイプ */
|
|
165
|
+
actionTypes?: ActionType[];
|
|
166
|
+
/** ターゲットパターン(glob) */
|
|
167
|
+
targetPatterns?: string[];
|
|
168
|
+
/** パラメータ条件 */
|
|
169
|
+
paramConditions?: Record<string, unknown>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// セキュリティ分析結果
|
|
174
|
+
// ============================================================================
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* セキュリティ分析結果(REQ-012-01)
|
|
178
|
+
*/
|
|
179
|
+
export interface SecurityAnalysis {
|
|
180
|
+
/** リスクレベル */
|
|
181
|
+
riskLevel: RiskLevel;
|
|
182
|
+
/** リスク理由 */
|
|
183
|
+
reasons: string[];
|
|
184
|
+
/** 確認が必要か(REQ-012-02) */
|
|
185
|
+
requiresConfirmation: boolean;
|
|
186
|
+
/** 許可されるか */
|
|
187
|
+
allowed: boolean;
|
|
188
|
+
/** ブロック理由(許可されない場合) */
|
|
189
|
+
blockReason?: string;
|
|
190
|
+
/** マッチしたルール */
|
|
191
|
+
matchedRules: string[];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// 監査ログ
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 監査ログエントリ(REQ-012-05)
|
|
200
|
+
*/
|
|
201
|
+
export interface AuditLogEntry {
|
|
202
|
+
/** エントリID */
|
|
203
|
+
id: string;
|
|
204
|
+
/** タイムスタンプ */
|
|
205
|
+
timestamp: string;
|
|
206
|
+
/** アクション */
|
|
207
|
+
action: Action;
|
|
208
|
+
/** セキュリティ分析結果 */
|
|
209
|
+
analysis: SecurityAnalysis;
|
|
210
|
+
/** 実行結果(実行された場合) */
|
|
211
|
+
observation?: Observation;
|
|
212
|
+
/** ユーザー確認の結果(確認が必要だった場合) */
|
|
213
|
+
userConfirmation?: UserConfirmation;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* ユーザー確認
|
|
218
|
+
*/
|
|
219
|
+
export interface UserConfirmation {
|
|
220
|
+
/** 確認されたか */
|
|
221
|
+
confirmed: boolean;
|
|
222
|
+
/** 確認時刻 */
|
|
223
|
+
confirmedAt?: string;
|
|
224
|
+
/** 確認者 */
|
|
225
|
+
confirmedBy?: string;
|
|
226
|
+
/** コメント */
|
|
227
|
+
comment?: string;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 監査ログフィルター
|
|
232
|
+
*/
|
|
233
|
+
export interface AuditLogFilter {
|
|
234
|
+
/** 開始日時 */
|
|
235
|
+
startTime?: string;
|
|
236
|
+
/** 終了日時 */
|
|
237
|
+
endTime?: string;
|
|
238
|
+
/** アクションタイプ */
|
|
239
|
+
actionTypes?: ActionType[];
|
|
240
|
+
/** リスクレベル(以上) */
|
|
241
|
+
minRiskLevel?: RiskLevel;
|
|
242
|
+
/** 成功/失敗 */
|
|
243
|
+
success?: boolean;
|
|
244
|
+
/** ユーザーID */
|
|
245
|
+
userId?: string;
|
|
246
|
+
/** 検索キーワード */
|
|
247
|
+
keyword?: string;
|
|
248
|
+
/** 最大件数 */
|
|
249
|
+
limit?: number;
|
|
250
|
+
/** オフセット */
|
|
251
|
+
offset?: number;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ============================================================================
|
|
255
|
+
// デフォルト設定
|
|
256
|
+
// ============================================================================
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* デフォルトセキュリティポリシー
|
|
260
|
+
*/
|
|
261
|
+
export const DEFAULT_SECURITY_POLICY: SecurityPolicy = {
|
|
262
|
+
allowPatterns: [
|
|
263
|
+
{ pattern: '**/*.md', description: 'Markdown files are safe' },
|
|
264
|
+
{ pattern: '**/*.txt', description: 'Text files are safe' },
|
|
265
|
+
{ pattern: '**/*.json', description: 'JSON files are generally safe' },
|
|
266
|
+
],
|
|
267
|
+
denyPatterns: [
|
|
268
|
+
{ pattern: '/etc/**', description: 'System configuration files' },
|
|
269
|
+
{ pattern: '/usr/**', description: 'System binaries' },
|
|
270
|
+
{ pattern: '**/node_modules/**', description: 'Dependencies should not be modified' },
|
|
271
|
+
{ pattern: '**/.git/**', description: 'Git internal files' },
|
|
272
|
+
{ pattern: '**/.env*', description: 'Environment files with secrets' },
|
|
273
|
+
{ pattern: '**/*password*', description: 'Files with password in name' },
|
|
274
|
+
{ pattern: '**/*secret*', description: 'Files with secret in name' },
|
|
275
|
+
{ pattern: '**/*.key', description: 'Key files' },
|
|
276
|
+
{ pattern: '**/*.pem', description: 'Certificate files' },
|
|
277
|
+
],
|
|
278
|
+
requireConfirmation: ['high', 'critical'],
|
|
279
|
+
maxRiskLevel: 'critical',
|
|
280
|
+
customRiskRules: [],
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* ビルトインリスクルール
|
|
285
|
+
*/
|
|
286
|
+
export const BUILTIN_RISK_RULES: RiskRule[] = [
|
|
287
|
+
{
|
|
288
|
+
name: 'file_delete_high_risk',
|
|
289
|
+
description: 'File deletion is high risk (REQ-012-06)',
|
|
290
|
+
match: { actionTypes: ['file_delete', 'directory_delete'] },
|
|
291
|
+
riskLevel: 'high',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: 'command_execute_high_risk',
|
|
295
|
+
description: 'Command execution is high risk',
|
|
296
|
+
match: { actionTypes: ['command_execute'] },
|
|
297
|
+
riskLevel: 'high',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: 'code_execute_medium_risk',
|
|
301
|
+
description: 'Code execution is medium risk',
|
|
302
|
+
match: { actionTypes: ['code_execute'] },
|
|
303
|
+
riskLevel: 'medium',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: 'network_request_medium_risk',
|
|
307
|
+
description: 'Network requests are medium risk',
|
|
308
|
+
match: { actionTypes: ['network_request'] },
|
|
309
|
+
riskLevel: 'medium',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: 'file_write_low_risk',
|
|
313
|
+
description: 'File write is low risk by default',
|
|
314
|
+
match: { actionTypes: ['file_write'] },
|
|
315
|
+
riskLevel: 'low',
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: 'file_read_low_risk',
|
|
319
|
+
description: 'File read is low risk',
|
|
320
|
+
match: { actionTypes: ['file_read', 'search', 'analyze'] },
|
|
321
|
+
riskLevel: 'low',
|
|
322
|
+
},
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
// ============================================================================
|
|
326
|
+
// エラー
|
|
327
|
+
// ============================================================================
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* セキュリティエラーコード
|
|
331
|
+
*/
|
|
332
|
+
export type SecurityErrorCode =
|
|
333
|
+
| 'ACTION_BLOCKED'
|
|
334
|
+
| 'RISK_TOO_HIGH'
|
|
335
|
+
| 'CONFIRMATION_REQUIRED'
|
|
336
|
+
| 'CONFIRMATION_DENIED'
|
|
337
|
+
| 'POLICY_VIOLATION';
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* セキュリティエラー
|
|
341
|
+
*/
|
|
342
|
+
export class SecurityError extends Error {
|
|
343
|
+
constructor(
|
|
344
|
+
public readonly code: SecurityErrorCode,
|
|
345
|
+
message: string,
|
|
346
|
+
public readonly analysis?: SecurityAnalysis
|
|
347
|
+
) {
|
|
348
|
+
super(message);
|
|
349
|
+
this.name = 'SecurityError';
|
|
350
|
+
}
|
|
351
|
+
}
|