@sk8metal/michi-cli 0.10.1 → 0.12.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.
Files changed (139) hide show
  1. package/README.md +71 -848
  2. package/dist/scripts/constants/environments.d.ts +1 -1
  3. package/dist/scripts/constants/environments.d.ts.map +1 -1
  4. package/dist/scripts/constants/environments.js +0 -20
  5. package/dist/scripts/constants/environments.js.map +1 -1
  6. package/dist/scripts/phase-runner.js +1 -1
  7. package/dist/scripts/phase-runner.js.map +1 -1
  8. package/dist/scripts/utils/multi-repo-validator.d.ts +18 -0
  9. package/dist/scripts/utils/multi-repo-validator.d.ts.map +1 -1
  10. package/dist/scripts/utils/multi-repo-validator.js +42 -0
  11. package/dist/scripts/utils/multi-repo-validator.js.map +1 -1
  12. package/dist/scripts/utils/tasks-format-validator.js +3 -3
  13. package/dist/scripts/utils/tasks-format-validator.js.map +1 -1
  14. package/dist/scripts/utils/template-finder.d.ts +2 -2
  15. package/dist/scripts/utils/template-finder.d.ts.map +1 -1
  16. package/dist/scripts/utils/template-finder.js +3 -8
  17. package/dist/scripts/utils/template-finder.js.map +1 -1
  18. package/dist/src/cli.d.ts.map +1 -1
  19. package/dist/src/cli.js +0 -8
  20. package/dist/src/cli.js.map +1 -1
  21. package/dist/src/commands/init.d.ts +0 -4
  22. package/dist/src/commands/init.d.ts.map +1 -1
  23. package/dist/src/commands/init.js +6 -30
  24. package/dist/src/commands/init.js.map +1 -1
  25. package/dist/src/commands/setup-existing.d.ts +2 -6
  26. package/dist/src/commands/setup-existing.d.ts.map +1 -1
  27. package/dist/src/commands/setup-existing.js +8 -142
  28. package/dist/src/commands/setup-existing.js.map +1 -1
  29. package/docs/README.md +20 -83
  30. package/docs/getting-started/configuration.md +350 -0
  31. package/docs/getting-started/installation.md +59 -0
  32. package/docs/getting-started/quick-start.md +76 -0
  33. package/docs/guides/atlassian-integration.md +116 -0
  34. package/docs/guides/claude-code.md +155 -0
  35. package/docs/guides/multi-repo.md +117 -0
  36. package/docs/guides/workflow.md +382 -0
  37. package/docs/reference/ai-commands.md +92 -0
  38. package/docs/reference/cli.md +752 -0
  39. package/docs/reference/environment-variables.md +192 -0
  40. package/docs/troubleshooting.md +498 -0
  41. package/package.json +1 -3
  42. package/scripts/__tests__/create-project.test.ts +12 -12
  43. package/scripts/__tests__/setup-existing-project.test.ts +22 -22
  44. package/scripts/constants/__tests__/environments.test.ts +7 -50
  45. package/scripts/constants/environments.ts +1 -27
  46. package/scripts/phase-runner.ts +1 -1
  47. package/scripts/template/__tests__/renderer.test.ts +21 -21
  48. package/scripts/utils/__tests__/multi-repo-validator.test.ts +159 -1
  49. package/scripts/utils/multi-repo-validator.ts +50 -0
  50. package/scripts/utils/tasks-format-validator.ts +3 -3
  51. package/scripts/utils/template-finder.ts +5 -11
  52. package/templates/claude/agents/e2e-first-planner/AGENT.md +1 -1
  53. package/templates/claude/agents/pr-resolver/AGENT.md +15 -3
  54. package/templates/claude/commands/michi/e2e-plan.md +1 -1
  55. package/templates/claude/commands/michi/spec-design.md +2 -2
  56. package/templates/claude/commands/michi/spec-tasks.md +156 -0
  57. package/templates/claude/commands/michi/test-planning.md +1 -1
  58. package/templates/claude/commands/michi/validate-design.md +3 -3
  59. package/templates/claude/commands/michi-multi-repo/impl-all.md +30 -1
  60. package/templates/claude/commands/michi-multi-repo/propagate-specs.md +14 -1
  61. package/templates/claude/commands/michi-multi-repo/spec-review.md +16 -2
  62. package/templates/claude-agent/agents/repo-spec-executor.md +1 -1
  63. package/templates/claude-agent/commands/michi/spec-tasks.md +117 -0
  64. package/templates/claude-agent/rules/code-size-monitor.md +26 -0
  65. package/templates/claude-agent/rules/code-size-rules.md +32 -0
  66. package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +1 -1
  67. package/docs/context.md +0 -59
  68. package/docs/michi-development/contributing/development.md +0 -341
  69. package/docs/michi-development/contributing/release.md +0 -365
  70. package/docs/michi-development/design/config-unification.md +0 -733
  71. package/docs/michi-development/design/design-config-current-state.md +0 -330
  72. package/docs/michi-development/design/design-config-implementation.md +0 -628
  73. package/docs/michi-development/design/design-config-migration.md +0 -952
  74. package/docs/michi-development/design/design-config-security.md +0 -771
  75. package/docs/michi-development/design/design-config-solution.md +0 -583
  76. package/docs/michi-development/design/design-config-testing.md +0 -892
  77. package/docs/michi-development/testing/manual-verification-flow.md +0 -871
  78. package/docs/michi-development/testing/manual-verification-other-tools.md +0 -1279
  79. package/docs/michi-development/testing/manual-verification-troubleshooting.md +0 -122
  80. package/docs/michi-development/testing/pre-publish-checklist.md +0 -560
  81. package/docs/michi-development/testing-strategy.md +0 -87
  82. package/docs/plan.md +0 -275
  83. package/docs/user-guide/getting-started/github-token-setup.md +0 -510
  84. package/docs/user-guide/getting-started/new-repository-setup.md +0 -704
  85. package/docs/user-guide/getting-started/quick-start.md +0 -212
  86. package/docs/user-guide/getting-started/setup.md +0 -819
  87. package/docs/user-guide/guides/agent-skills-integration.md +0 -222
  88. package/docs/user-guide/guides/customization.md +0 -537
  89. package/docs/user-guide/guides/internationalization.md +0 -540
  90. package/docs/user-guide/guides/migration-guide.md +0 -138
  91. package/docs/user-guide/guides/multi-project.md +0 -368
  92. package/docs/user-guide/guides/multi-repo-guide.md +0 -1590
  93. package/docs/user-guide/guides/phase-automation.md +0 -419
  94. package/docs/user-guide/guides/workflow.md +0 -574
  95. package/docs/user-guide/hands-on/README.md +0 -142
  96. package/docs/user-guide/hands-on/claude-agent-setup.md +0 -597
  97. package/docs/user-guide/hands-on/claude-setup.md +0 -452
  98. package/docs/user-guide/hands-on/cursor-setup.md +0 -353
  99. package/docs/user-guide/hands-on/troubleshooting.md +0 -964
  100. package/docs/user-guide/hands-on/verification-checklist.md +0 -439
  101. package/docs/user-guide/hands-on/workflow-walkthrough.md +0 -1078
  102. package/docs/user-guide/reference/config.md +0 -589
  103. package/docs/user-guide/reference/multi-repo-api.md +0 -771
  104. package/docs/user-guide/reference/quick-reference.md +0 -297
  105. package/docs/user-guide/reference/security-test-payloads.md +0 -50
  106. package/docs/user-guide/reference/tasks-template.md +0 -550
  107. package/docs/user-guide/release/ci-setup-java.md +0 -114
  108. package/docs/user-guide/release/ci-setup-nodejs.md +0 -94
  109. package/docs/user-guide/release/ci-setup-php.md +0 -102
  110. package/docs/user-guide/release/ci-setup-troubleshooting.md +0 -94
  111. package/docs/user-guide/release/ci-setup.md +0 -188
  112. package/docs/user-guide/release/release-flow.md +0 -476
  113. package/docs/user-guide/templates/test-specs/README.md +0 -173
  114. package/docs/user-guide/templates/test-specs/e2e-test-spec-template.md +0 -553
  115. package/docs/user-guide/templates/test-specs/integration-test-spec-template.md +0 -435
  116. package/docs/user-guide/templates/test-specs/performance-test-spec-template.md +0 -454
  117. package/docs/user-guide/templates/test-specs/security-test-spec-template.md +0 -625
  118. package/docs/user-guide/templates/test-specs/unit-test-spec-template.md +0 -328
  119. package/docs/user-guide/testing/integration-tests.md +0 -312
  120. package/docs/user-guide/testing/tdd-cycle.md +0 -349
  121. package/docs/user-guide/testing/test-execution-flow.md +0 -396
  122. package/docs/user-guide/testing/test-failure-handling.md +0 -521
  123. package/docs/user-guide/testing/test-planning-flow.md +0 -185
  124. package/docs/user-guide/testing-strategy.md +0 -185
  125. package/docs/verification-guide.md +0 -518
  126. package/templates/cline/rules/atlassian-integration.md +0 -36
  127. package/templates/cline/rules/michi-core.md +0 -56
  128. package/templates/codex/AGENTS.override.md +0 -277
  129. package/templates/codex/prompts/confluence-sync.md +0 -177
  130. package/templates/codex/rules/README.md +0 -210
  131. package/templates/cursor/commands/kiro/kiro-spec-impl.md +0 -244
  132. package/templates/cursor/commands/kiro/kiro-spec-tasks.md +0 -354
  133. package/templates/cursor/commands/michi/confluence-sync.md +0 -76
  134. package/templates/cursor/commands/michi/project-switch.md +0 -69
  135. package/templates/cursor/rules/atlassian-mcp.mdc +0 -188
  136. package/templates/cursor/rules/github-ssot.mdc +0 -151
  137. package/templates/cursor/rules/multi-project.mdc +0 -81
  138. package/templates/gemini/commands/README.md +0 -41
  139. package/templates/gemini/rules/GEMINI.md +0 -80
@@ -1,771 +0,0 @@
1
- # Michi 設定統合設計書 - セキュリティとパフォーマンス
2
-
3
- **バージョン**: 1.0
4
- **作成日**: 2025-01-11
5
- **ステータス**: Draft
6
- **親ドキュメント**: [config-unification.md](./config-unification.md)
7
-
8
- ---
9
-
10
- ## 9. セキュリティとパフォーマンス
11
-
12
- 設定統一に伴うセキュリティとパフォーマンスの考慮事項です。
13
-
14
- ### 9.1 セキュリティ対策
15
-
16
- #### 9.1.1 認証情報の保護
17
-
18
- **脅威モデル**
19
-
20
- | 脅威 | 影響 | 対策 |
21
- |------|------|------|
22
- | **認証情報の漏洩** | 高 | ファイルパーミッション、.gitignore |
23
- | **環境変数の漏洩** | 中 | .env.example の提供、警告メッセージ |
24
- | **不正アクセス** | 高 | 最小権限の原則、トークンの有効期限 |
25
- | **中間者攻撃** | 中 | HTTPS必須、証明書検証 |
26
-
27
- **実装されるセキュリティ対策**
28
-
29
- 1. **ファイルパーミッションの強制**
30
-
31
- ```typescript
32
- // scripts/utils/security.ts
33
- import { chmodSync, statSync } from 'fs';
34
-
35
- /**
36
- * 機密ファイルのパーミッションを強制する
37
- */
38
- export function enforceSecurePermissions(filePath: string): void {
39
- const stats = statSync(filePath);
40
- const mode = stats.mode & 0o777;
41
-
42
- // 600 (rw-------) 以外の場合は警告
43
- if (mode !== 0o600) {
44
- console.warn(`⚠️ 警告: ${filePath} のパーミッションが不適切です`);
45
- console.warn(` 現在: ${mode.toString(8)}, 推奨: 600`);
46
- console.warn(` 自動的に 600 に変更します...`);
47
-
48
- chmodSync(filePath, 0o600);
49
- console.log(`✅ パーミッションを 600 に変更しました`);
50
- }
51
- }
52
-
53
- /**
54
- * グローバル .env ファイルの作成時にパーミッションを設定
55
- */
56
- export function createSecureEnvFile(filePath: string, content: string): void {
57
- writeFileSync(filePath, content, { mode: 0o600 });
58
- console.log(`✅ セキュアなファイルを作成しました: ${filePath}`);
59
- }
60
- ```
61
-
62
- 2. **.gitignore の自動更新**
63
-
64
- ```typescript
65
- // scripts/utils/gitignore.ts
66
- import { readFileSync, writeFileSync, existsSync } from 'fs';
67
-
68
- /**
69
- * .gitignore に機密ファイルを追加する
70
- */
71
- export function ensureGitignore(projectDir: string): void {
72
- const gitignorePath = join(projectDir, '.gitignore');
73
- const requiredEntries = [
74
- '.env',
75
- '.env.local',
76
- '~/.michi/.env',
77
- '.michi-backup-*/',
78
- '.michi/migration.log'
79
- ];
80
-
81
- let content = existsSync(gitignorePath)
82
- ? readFileSync(gitignorePath, 'utf-8')
83
- : '';
84
-
85
- let added = false;
86
- for (const entry of requiredEntries) {
87
- if (!content.includes(entry)) {
88
- content += `\n${entry}`;
89
- added = true;
90
- }
91
- }
92
-
93
- if (added) {
94
- writeFileSync(gitignorePath, content.trim() + '\n');
95
- console.log('✅ .gitignore を更新しました');
96
- }
97
- }
98
- ```
99
-
100
- 3. **環境変数の検証**
101
-
102
- ```typescript
103
- // src/config/validation.ts
104
- import { z } from 'zod';
105
-
106
- /**
107
- * 認証情報の形式を検証する
108
- */
109
- export const CredentialsSchema = z.object({
110
- // Atlassian API Token (24文字の英数字)
111
- confluenceApiToken: z.string()
112
- .min(24, 'API token is too short')
113
- .regex(/^[A-Za-z0-9]+$/, 'Invalid API token format'),
114
-
115
- jiraApiToken: z.string()
116
- .min(24, 'API token is too short')
117
- .regex(/^[A-Za-z0-9]+$/, 'Invalid API token format'),
118
-
119
- // GitHub Personal Access Token (ghp_ または ghp_classic プレフィックス)
120
- githubToken: z.string()
121
- .regex(
122
- /^(ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{82})$/,
123
- 'Invalid GitHub token format'
124
- ),
125
- });
126
-
127
- /**
128
- * 機密情報が含まれていないかチェック
129
- */
130
- export function detectSecretsInLogs(message: string): string {
131
- // トークンのパターンにマッチする部分をマスクする
132
- return message
133
- .replace(/ghp_[A-Za-z0-9]{36}/g, 'ghp_****')
134
- .replace(/github_pat_[A-Za-z0-9_]{82}/g, 'github_pat_****')
135
- .replace(/[A-Za-z0-9]{24,}/g, (match) => {
136
- // 24文字以上の英数字はトークンの可能性があるのでマスク
137
- return match.substring(0, 4) + '****';
138
- });
139
- }
140
- ```
141
-
142
- #### 9.1.2 ファイルパーミッションの管理
143
-
144
- **パーミッション設定基準**
145
-
146
- | ファイル | パーミッション | 理由 |
147
- |---------|--------------|------|
148
- | `~/.michi/.env` | 600 (rw-------) | 組織の認証情報を含む |
149
- | `.env` | 600 (rw-------) | プロジェクトの認証情報を含む |
150
- | `.michi/config.json` | 644 (rw-r--r--) | 認証情報を含まない |
151
- | `.michi/project.json` | 644 (rw-r--r--) | 認証情報を含まない |
152
- | `.michi-backup-*/*` | 700 (rwx------) | バックアップディレクトリ |
153
-
154
- **自動チェック機構**
155
-
156
- ```typescript
157
- // src/config/config-loader.ts (updated)
158
- export class ConfigLoader {
159
- private validateFilePermissions(): void {
160
- const sensitiveFiles = [
161
- this.globalEnvPath,
162
- join(this.projectDir, '.env')
163
- ];
164
-
165
- for (const filePath of sensitiveFiles) {
166
- if (!existsSync(filePath)) continue;
167
-
168
- const stats = statSync(filePath);
169
- const mode = stats.mode & 0o777;
170
-
171
- if (mode > 0o600) {
172
- throw new SecurityError(
173
- 'INSECURE_PERMISSIONS',
174
- `File ${filePath} has insecure permissions: ${mode.toString(8)}`,
175
- { filePath, mode, expected: 0o600 }
176
- );
177
- }
178
- }
179
- }
180
-
181
- public load(): AppConfig {
182
- // パーミッションチェック
183
- this.validateFilePermissions();
184
-
185
- // 設定読み込み
186
- // ...
187
- }
188
- }
189
- ```
190
-
191
- #### 9.1.3 入力検証
192
-
193
- **URL検証**
194
-
195
- ```typescript
196
- // src/config/validators.ts
197
- import { z } from 'zod';
198
-
199
- /**
200
- * Atlassian URL(Confluence/JIRA)の検証
201
- */
202
- export const AtlassianUrlSchema = z.string()
203
- .url('Invalid URL format')
204
- .regex(
205
- /^https:\/\/[a-zA-Z0-9-]+\.atlassian\.net$/,
206
- 'Must be a valid Atlassian Cloud URL (https://xxx.atlassian.net)'
207
- );
208
-
209
- /**
210
- * GitHub リポジトリURLの検証
211
- */
212
- export const GitHubRepoUrlSchema = z.string()
213
- .refine(
214
- (url) => {
215
- const httpsPattern = /^https:\/\/github\.com\/[^/]+\/[^/]+(\.git)?$/;
216
- const sshPattern = /^git@github\.com:[^/]+\/[^/]+(\.git)?$/;
217
- return httpsPattern.test(url) || sshPattern.test(url);
218
- },
219
- 'Must be a valid GitHub repository URL'
220
- );
221
-
222
- /**
223
- * メールアドレスの検証
224
- */
225
- export const EmailSchema = z.string()
226
- .email('Invalid email format')
227
- .max(254, 'Email too long');
228
- ```
229
-
230
- **コマンドインジェクション対策**
231
-
232
- ```typescript
233
- // scripts/utils/exec-safe.ts
234
- import { execSync } from 'child_process';
235
-
236
- /**
237
- * 安全なコマンド実行(シェルインジェクション対策)
238
- */
239
- export function execSafe(
240
- command: string,
241
- args: string[],
242
- options?: any
243
- ): string {
244
- // コマンドと引数を分離して実行
245
- // execSync の shell: false オプションを使用
246
- const fullCommand = `${command} ${args.map(escapeArg).join(' ')}`;
247
-
248
- return execSync(fullCommand, {
249
- ...options,
250
- shell: false, // シェルを経由しない
251
- encoding: 'utf-8'
252
- });
253
- }
254
-
255
- /**
256
- * シェル引数のエスケープ
257
- */
258
- function escapeArg(arg: string): string {
259
- // シングルクォートで囲み、内部のシングルクォートをエスケープ
260
- return `'${arg.replace(/'/g, "'\\''")}'`;
261
- }
262
- ```
263
-
264
- #### 9.1.4 セキュアなデフォルト設定
265
-
266
- **デフォルト値の設計**
267
-
268
- ```typescript
269
- // src/config/defaults.ts
270
-
271
- export const SECURE_DEFAULTS = {
272
- // HTTPS 強制
273
- confluence: {
274
- useHttps: true,
275
- verifySsl: true,
276
- },
277
-
278
- jira: {
279
- useHttps: true,
280
- verifySsl: true,
281
- },
282
-
283
- github: {
284
- useHttps: true,
285
- },
286
-
287
- // API呼び出しのタイムアウト(DoS対策)
288
- api: {
289
- timeout: 30000, // 30秒
290
- maxRetries: 3,
291
- retryDelay: 1000, // 1秒
292
- },
293
-
294
- // ロギング
295
- logging: {
296
- maskSecrets: true, // 機密情報を自動マスク
297
- level: 'info',
298
- },
299
- } as const;
300
- ```
301
-
302
- **設定値のサニタイズ**
303
-
304
- ```typescript
305
- // src/config/sanitize.ts
306
-
307
- /**
308
- * ログ出力前に機密情報をマスクする
309
- */
310
- export function sanitizeForLog(config: AppConfig): AppConfig {
311
- return {
312
- ...config,
313
- confluence: config.confluence ? {
314
- ...config.confluence,
315
- apiToken: '****',
316
- password: '****',
317
- } : undefined,
318
- jira: config.jira ? {
319
- ...config.jira,
320
- apiToken: '****',
321
- password: '****',
322
- } : undefined,
323
- github: config.github ? {
324
- ...config.github,
325
- token: config.github.token?.substring(0, 7) + '****',
326
- } : undefined,
327
- };
328
- }
329
- ```
330
-
331
- ### 9.2 パフォーマンス最適化
332
-
333
- #### 9.2.1 設定読み込みの最適化
334
-
335
- **問題点**
336
-
337
- - 毎回ファイルを読み込むとI/Oコストが高い
338
- - 複数のコマンド実行時に同じファイルを何度も読む
339
- - Zodバリデーションが重い
340
-
341
- **解決策: キャッシュ機構**
342
-
343
- ```typescript
344
- // src/config/config-loader.ts (updated)
345
-
346
- export class ConfigLoader {
347
- private static cache: Map<string, {
348
- config: AppConfig;
349
- timestamp: number;
350
- hash: string;
351
- }> = new Map();
352
-
353
- private static CACHE_TTL = 60000; // 1分
354
-
355
- /**
356
- * ファイルのハッシュ値を計算
357
- */
358
- private getFileHash(filePath: string): string {
359
- if (!existsSync(filePath)) return '';
360
-
361
- const content = readFileSync(filePath, 'utf-8');
362
- return createHash('sha256').update(content).digest('hex');
363
- }
364
-
365
- /**
366
- * キャッシュキーを生成
367
- */
368
- private getCacheKey(): string {
369
- const globalHash = this.getFileHash(this.globalEnvPath);
370
- const projectConfigHash = this.getFileHash(this.projectConfigPath);
371
- const projectEnvHash = this.getFileHash(this.projectEnvPath);
372
-
373
- return `${globalHash}:${projectConfigHash}:${projectEnvHash}`;
374
- }
375
-
376
- /**
377
- * キャッシュから設定を取得
378
- */
379
- public load(options: { noCache?: boolean } = {}): AppConfig {
380
- const cacheKey = this.getCacheKey();
381
- const now = Date.now();
382
-
383
- // キャッシュチェック
384
- if (!options.noCache) {
385
- const cached = ConfigLoader.cache.get(cacheKey);
386
- if (cached && now - cached.timestamp < ConfigLoader.CACHE_TTL) {
387
- return cached.config;
388
- }
389
- }
390
-
391
- // 設定を読み込み
392
- const config = this.loadInternal();
393
-
394
- // キャッシュに保存
395
- ConfigLoader.cache.set(cacheKey, {
396
- config,
397
- timestamp: now,
398
- hash: cacheKey
399
- });
400
-
401
- return config;
402
- }
403
-
404
- /**
405
- * キャッシュをクリア
406
- */
407
- public static clearCache(): void {
408
- ConfigLoader.cache.clear();
409
- }
410
- }
411
- ```
412
-
413
- **ベンチマーク目標**
414
-
415
- | 操作 | 目標時間 | 現在値 |
416
- |------|---------|--------|
417
- | 初回読み込み | < 100ms | TBD |
418
- | キャッシュヒット | < 10ms | TBD |
419
- | バリデーション | < 50ms | TBD |
420
- | マージ処理 | < 20ms | TBD |
421
-
422
- #### 9.2.2 メモリ使用量
423
-
424
- **メモリプロファイリング**
425
-
426
- ```typescript
427
- // scripts/benchmark/memory-profile.ts
428
- import { ConfigLoader } from '../src/config/config-loader.js';
429
-
430
- function measureMemoryUsage() {
431
- const before = process.memoryUsage();
432
-
433
- // 100回設定を読み込む
434
- for (let i = 0; i < 100; i++) {
435
- const loader = new ConfigLoader(process.cwd());
436
- loader.load();
437
- }
438
-
439
- const after = process.memoryUsage();
440
-
441
- console.log('Memory Usage:');
442
- console.log(` Heap Used: ${(after.heapUsed - before.heapUsed) / 1024 / 1024}MB`);
443
- console.log(` External: ${(after.external - before.external) / 1024 / 1024}MB`);
444
- }
445
-
446
- measureMemoryUsage();
447
- ```
448
-
449
- **最適化目標**
450
-
451
- - 1回の設定読み込み: < 5MB
452
- - キャッシュ保持: < 10MB (100エントリ)
453
- - ピークメモリ: < 50MB
454
-
455
- #### 9.2.3 キャッシュ戦略
456
-
457
- **多層キャッシュ**
458
-
459
- ```typescript
460
- // src/config/cache-strategy.ts
461
-
462
- interface CacheStrategy {
463
- get(key: string): AppConfig | null;
464
- set(key: string, value: AppConfig): void;
465
- invalidate(key: string): void;
466
- clear(): void;
467
- }
468
-
469
- /**
470
- * LRU(Least Recently Used)キャッシュ
471
- */
472
- class LRUCache implements CacheStrategy {
473
- private cache: Map<string, { value: AppConfig; timestamp: number }>;
474
- private maxSize: number;
475
-
476
- constructor(maxSize: number = 100) {
477
- this.cache = new Map();
478
- this.maxSize = maxSize;
479
- }
480
-
481
- get(key: string): AppConfig | null {
482
- const entry = this.cache.get(key);
483
- if (!entry) return null;
484
-
485
- // アクセスされたエントリを最後に移動(LRU)
486
- this.cache.delete(key);
487
- this.cache.set(key, entry);
488
-
489
- return entry.value;
490
- }
491
-
492
- set(key: string, value: AppConfig): void {
493
- // サイズ制限チェック
494
- if (this.cache.size >= this.maxSize) {
495
- // 最も古いエントリを削除
496
- const oldestKey = this.cache.keys().next().value;
497
- this.cache.delete(oldestKey);
498
- }
499
-
500
- this.cache.set(key, {
501
- value,
502
- timestamp: Date.now()
503
- });
504
- }
505
-
506
- invalidate(key: string): void {
507
- this.cache.delete(key);
508
- }
509
-
510
- clear(): void {
511
- this.cache.clear();
512
- }
513
- }
514
-
515
- /**
516
- * TTL(Time To Live)付きキャッシュ
517
- */
518
- class TTLCache implements CacheStrategy {
519
- private cache: Map<string, { value: AppConfig; expiry: number }>;
520
- private ttl: number;
521
-
522
- constructor(ttl: number = 60000) {
523
- this.cache = new Map();
524
- this.ttl = ttl;
525
- }
526
-
527
- get(key: string): AppConfig | null {
528
- const entry = this.cache.get(key);
529
- if (!entry) return null;
530
-
531
- // 有効期限チェック
532
- if (Date.now() > entry.expiry) {
533
- this.cache.delete(key);
534
- return null;
535
- }
536
-
537
- return entry.value;
538
- }
539
-
540
- set(key: string, value: AppConfig): void {
541
- this.cache.set(key, {
542
- value,
543
- expiry: Date.now() + this.ttl
544
- });
545
- }
546
-
547
- invalidate(key: string): void {
548
- this.cache.delete(key);
549
- }
550
-
551
- clear(): void {
552
- this.cache.clear();
553
- }
554
- }
555
- ```
556
-
557
- ### 9.3 監査とロギング
558
-
559
- #### 9.3.1 設定変更の監査ログ
560
-
561
- ```typescript
562
- // src/config/audit-log.ts
563
- import { writeFileSync, appendFileSync, existsSync } from 'fs';
564
- import { join } from 'path';
565
-
566
- interface AuditLogEntry {
567
- timestamp: string;
568
- user: string;
569
- action: string;
570
- target: string;
571
- changes: Record<string, { old: any; new: any }>;
572
- success: boolean;
573
- error?: string;
574
- }
575
-
576
- export class AuditLogger {
577
- private logPath: string;
578
-
579
- constructor(projectDir: string) {
580
- this.logPath = join(projectDir, '.michi', 'audit.log');
581
- }
582
-
583
- /**
584
- * 設定変更を記録
585
- */
586
- public logConfigChange(
587
- action: string,
588
- target: string,
589
- changes: Record<string, { old: any; new: any }>,
590
- success: boolean,
591
- error?: string
592
- ): void {
593
- const entry: AuditLogEntry = {
594
- timestamp: new Date().toISOString(),
595
- user: process.env.USER || 'unknown',
596
- action,
597
- target,
598
- changes: this.sanitizeChanges(changes),
599
- success,
600
- error
601
- };
602
-
603
- const logLine = JSON.stringify(entry) + '\n';
604
-
605
- if (!existsSync(this.logPath)) {
606
- writeFileSync(this.logPath, logLine, { mode: 0o600 });
607
- } else {
608
- appendFileSync(this.logPath, logLine);
609
- }
610
- }
611
-
612
- /**
613
- * 機密情報をマスク
614
- */
615
- private sanitizeChanges(
616
- changes: Record<string, { old: any; new: any }>
617
- ): Record<string, { old: any; new: any }> {
618
- const sensitiveKeys = ['apiToken', 'token', 'password', 'secret'];
619
-
620
- const sanitized: Record<string, { old: any; new: any }> = {};
621
-
622
- for (const [key, value] of Object.entries(changes)) {
623
- if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) {
624
- sanitized[key] = {
625
- old: '****',
626
- new: '****'
627
- };
628
- } else {
629
- sanitized[key] = value;
630
- }
631
- }
632
-
633
- return sanitized;
634
- }
635
-
636
- /**
637
- * 監査ログを取得
638
- */
639
- public getAuditLog(limit?: number): AuditLogEntry[] {
640
- if (!existsSync(this.logPath)) return [];
641
-
642
- const content = readFileSync(this.logPath, 'utf-8');
643
- const lines = content.trim().split('\n');
644
-
645
- const entries = lines
646
- .map(line => {
647
- try {
648
- return JSON.parse(line) as AuditLogEntry;
649
- } catch {
650
- return null;
651
- }
652
- })
653
- .filter((entry): entry is AuditLogEntry => entry !== null);
654
-
655
- if (limit) {
656
- return entries.slice(-limit);
657
- }
658
-
659
- return entries;
660
- }
661
- }
662
- ```
663
-
664
- **使用例**
665
-
666
- ```typescript
667
- // michi migrate コマンド内
668
- const auditLogger = new AuditLogger(projectDir);
669
-
670
- try {
671
- // 移行前の設定を記録
672
- const oldConfig = readOldConfig();
673
- const newConfig = performMigration();
674
-
675
- // 変更内容を計算
676
- const changes = calculateChanges(oldConfig, newConfig);
677
-
678
- // 成功をログ
679
- auditLogger.logConfigChange(
680
- 'migrate',
681
- '~/.michi/.env',
682
- changes,
683
- true
684
- );
685
- } catch (error) {
686
- // 失敗をログ
687
- auditLogger.logConfigChange(
688
- 'migrate',
689
- '~/.michi/.env',
690
- {},
691
- false,
692
- error.message
693
- );
694
- throw error;
695
- }
696
- ```
697
-
698
- #### 9.3.2 セキュリティイベントの記録
699
-
700
- ```typescript
701
- // src/config/security-logger.ts
702
-
703
- export class SecurityLogger {
704
- /**
705
- * 不正なアクセス試行を記録
706
- */
707
- public logUnauthorizedAccess(
708
- resource: string,
709
- reason: string
710
- ): void {
711
- const event = {
712
- timestamp: new Date().toISOString(),
713
- type: 'UNAUTHORIZED_ACCESS',
714
- resource,
715
- reason,
716
- user: process.env.USER,
717
- pid: process.pid
718
- };
719
-
720
- console.error('🚨 セキュリティイベント:', JSON.stringify(event));
721
-
722
- // セキュリティログに記録
723
- this.appendToSecurityLog(event);
724
- }
725
-
726
- /**
727
- * 機密ファイルへのアクセスを記録
728
- */
729
- public logSensitiveFileAccess(
730
- filePath: string,
731
- operation: 'read' | 'write'
732
- ): void {
733
- const event = {
734
- timestamp: new Date().toISOString(),
735
- type: 'SENSITIVE_FILE_ACCESS',
736
- filePath,
737
- operation,
738
- user: process.env.USER,
739
- pid: process.pid
740
- };
741
-
742
- this.appendToSecurityLog(event);
743
- }
744
-
745
- private appendToSecurityLog(event: any): void {
746
- const logPath = join(os.homedir(), '.michi', 'security.log');
747
- const logLine = JSON.stringify(event) + '\n';
748
-
749
- mkdirSync(dirname(logPath), { recursive: true });
750
- appendFileSync(logPath, logLine, { mode: 0o600 });
751
- }
752
- }
753
- ```
754
-
755
- ### 9.4 セキュリティチェックリスト
756
-
757
- リリース前に確認すべき項目:
758
-
759
- - [ ] すべての機密ファイルが .gitignore に追加されている
760
- - [ ] ファイルパーミッションが適切(600 for .env files)
761
- - [ ] Zodバリデーションがすべての入力に適用されている
762
- - [ ] ログに機密情報が含まれていない
763
- - [ ] HTTPS が強制されている
764
- - [ ] エラーメッセージに内部情報が含まれていない
765
- - [ ] セキュリティテストが全てパスしている
766
- - [ ] 監査ログが正しく記録されている
767
- - [ ] セキュリティドキュメントが最新である
768
- - [ ] 脆弱性スキャンを実行している(npm audit)
769
-
770
- ---
771
-