memento-mcp-server 1.16.2 → 1.16.3-b
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/dist/domains/anchor/services/anchor/n-hop-search-service.d.ts.map +1 -1
- package/dist/domains/anchor/services/anchor/n-hop-search-service.js +4 -5
- package/dist/domains/anchor/services/anchor/n-hop-search-service.js.map +1 -1
- package/dist/domains/embedding/services/embedding-service.d.ts.map +1 -1
- package/dist/domains/embedding/services/embedding-service.js +5 -2
- package/dist/domains/embedding/services/embedding-service.js.map +1 -1
- package/dist/domains/embedding/services/gemini-embedding-service.d.ts.map +1 -1
- package/dist/domains/embedding/services/gemini-embedding-service.js +6 -3
- package/dist/domains/embedding/services/gemini-embedding-service.js.map +1 -1
- package/dist/domains/embedding/services/lightweight-embedding-service.d.ts.map +1 -1
- package/dist/domains/embedding/services/lightweight-embedding-service.js +4 -2
- package/dist/domains/embedding/services/lightweight-embedding-service.js.map +1 -1
- package/dist/domains/embedding/services/minilm-embedding-service.d.ts.map +1 -1
- package/dist/domains/embedding/services/minilm-embedding-service.js +6 -3
- package/dist/domains/embedding/services/minilm-embedding-service.js.map +1 -1
- package/dist/domains/embedding/services/openai-embedding-service.d.ts.map +1 -1
- package/dist/domains/embedding/services/openai-embedding-service.js +5 -2
- package/dist/domains/embedding/services/openai-embedding-service.js.map +1 -1
- package/dist/domains/embedding/services/unified-embedding-service.d.ts.map +1 -1
- package/dist/domains/embedding/services/unified-embedding-service.js +6 -3
- package/dist/domains/embedding/services/unified-embedding-service.js.map +1 -1
- package/dist/domains/forgetting/services/forgetting-policy-service.d.ts.map +1 -1
- package/dist/domains/forgetting/services/forgetting-policy-service.js +3 -1
- package/dist/domains/forgetting/services/forgetting-policy-service.js.map +1 -1
- package/dist/domains/memory/services/memory-embedding-service.d.ts +1 -0
- package/dist/domains/memory/services/memory-embedding-service.d.ts.map +1 -1
- package/dist/domains/memory/services/memory-embedding-service.js +43 -39
- package/dist/domains/memory/services/memory-embedding-service.js.map +1 -1
- package/dist/domains/memory/services/memory-neighbor-service.d.ts.map +1 -1
- package/dist/domains/memory/services/memory-neighbor-service.js +13 -4
- package/dist/domains/memory/services/memory-neighbor-service.js.map +1 -1
- package/dist/domains/memory/tools/convert-episodic-to-semantic-tool.js +5 -6
- package/dist/domains/memory/tools/convert-episodic-to-semantic-tool.js.map +1 -1
- package/dist/domains/memory/tools/forget-tool.d.ts.map +1 -1
- package/dist/domains/memory/tools/forget-tool.js +5 -2
- package/dist/domains/memory/tools/forget-tool.js.map +1 -1
- package/dist/domains/memory/tools/memory-injection-prompt.d.ts.map +1 -1
- package/dist/domains/memory/tools/memory-injection-prompt.js +3 -1
- package/dist/domains/memory/tools/memory-injection-prompt.js.map +1 -1
- package/dist/domains/memory/tools/pin-tool.d.ts.map +1 -1
- package/dist/domains/memory/tools/pin-tool.js +5 -2
- package/dist/domains/memory/tools/pin-tool.js.map +1 -1
- package/dist/domains/memory/tools/unpin-tool.d.ts.map +1 -1
- package/dist/domains/memory/tools/unpin-tool.js +5 -2
- package/dist/domains/memory/tools/unpin-tool.js.map +1 -1
- package/dist/domains/monitoring/services/error-logging-service.d.ts +5 -0
- package/dist/domains/monitoring/services/error-logging-service.d.ts.map +1 -1
- package/dist/domains/monitoring/services/error-logging-service.js +28 -10
- package/dist/domains/monitoring/services/error-logging-service.js.map +1 -1
- package/dist/domains/monitoring/services/performance-alert-service.d.ts.map +1 -1
- package/dist/domains/monitoring/services/performance-alert-service.js +5 -2
- package/dist/domains/monitoring/services/performance-alert-service.js.map +1 -1
- package/dist/domains/relation/services/relation-graph.d.ts.map +1 -1
- package/dist/domains/relation/services/relation-graph.js +3 -4
- package/dist/domains/relation/services/relation-graph.js.map +1 -1
- package/dist/domains/search/algorithms/hybrid-search-engine.d.ts.map +1 -1
- package/dist/domains/search/algorithms/hybrid-search-engine.js +15 -10
- package/dist/domains/search/algorithms/hybrid-search-engine.js.map +1 -1
- package/dist/domains/search/algorithms/search-engine.d.ts.map +1 -1
- package/dist/domains/search/algorithms/search-engine.js +8 -4
- package/dist/domains/search/algorithms/search-engine.js.map +1 -1
- package/dist/domains/search/algorithms/vector-search-engine-migration.d.ts +13 -8
- package/dist/domains/search/algorithms/vector-search-engine-migration.d.ts.map +1 -1
- package/dist/domains/search/algorithms/vector-search-engine-migration.js +28 -45
- package/dist/domains/search/algorithms/vector-search-engine-migration.js.map +1 -1
- package/dist/domains/search/algorithms/vector-search-engine.d.ts +17 -35
- package/dist/domains/search/algorithms/vector-search-engine.d.ts.map +1 -1
- package/dist/domains/search/algorithms/vector-search-engine.js +94 -478
- package/dist/domains/search/algorithms/vector-search-engine.js.map +1 -1
- package/dist/domains/search/repositories/vector-performance.repository.d.ts.map +1 -1
- package/dist/domains/search/repositories/vector-performance.repository.js +3 -1
- package/dist/domains/search/repositories/vector-performance.repository.js.map +1 -1
- package/dist/domains/search/repositories/vector-search.repository.d.ts +1 -0
- package/dist/domains/search/repositories/vector-search.repository.d.ts.map +1 -1
- package/dist/domains/search/repositories/vector-search.repository.js +178 -157
- package/dist/domains/search/repositories/vector-search.repository.js.map +1 -1
- package/dist/domains/search/services/vector-search/vector-index-manager.d.ts.map +1 -1
- package/dist/domains/search/services/vector-search/vector-index-manager.js +7 -3
- package/dist/domains/search/services/vector-search/vector-index-manager.js.map +1 -1
- package/dist/domains/search/services/vector-search/vector-performance-tester.d.ts.map +1 -1
- package/dist/domains/search/services/vector-search/vector-performance-tester.js +3 -1
- package/dist/domains/search/services/vector-search/vector-performance-tester.js.map +1 -1
- package/dist/infrastructure/database/database/init.d.ts.map +1 -1
- package/dist/infrastructure/database/database/init.js +29 -7
- package/dist/infrastructure/database/database/init.js.map +1 -1
- package/dist/infrastructure/database/database/migrate.d.ts.map +1 -1
- package/dist/infrastructure/database/database/migrate.js +5 -2
- package/dist/infrastructure/database/database/migrate.js.map +1 -1
- package/dist/infrastructure/database/database/migration/backup-manager.d.ts.map +1 -1
- package/dist/infrastructure/database/database/migration/backup-manager.js +11 -5
- package/dist/infrastructure/database/database/migration/backup-manager.js.map +1 -1
- package/dist/infrastructure/database/database/migration/migration-detector.d.ts.map +1 -1
- package/dist/infrastructure/database/database/migration/migration-detector.js +3 -1
- package/dist/infrastructure/database/database/migration/migration-detector.js.map +1 -1
- package/dist/infrastructure/database/database/migration/migration-logger.d.ts.map +1 -1
- package/dist/infrastructure/database/database/migration/migration-logger.js +5 -2
- package/dist/infrastructure/database/database/migration/migration-logger.js.map +1 -1
- package/dist/infrastructure/database/database/migration/migration-runner.d.ts.map +1 -1
- package/dist/infrastructure/database/database/migration/migration-runner.js +24 -13
- package/dist/infrastructure/database/database/migration/migration-runner.js.map +1 -1
- package/dist/infrastructure/database/database/migration/schema-version-manager.d.ts.map +1 -1
- package/dist/infrastructure/database/database/migration/schema-version-manager.js +7 -3
- package/dist/infrastructure/database/database/migration/schema-version-manager.js.map +1 -1
- package/dist/infrastructure/database/database-optimizer.d.ts.map +1 -1
- package/dist/infrastructure/database/database-optimizer.js +7 -6
- package/dist/infrastructure/database/database-optimizer.js.map +1 -1
- package/dist/infrastructure/database/migration-history-service.d.ts.map +1 -1
- package/dist/infrastructure/database/migration-history-service.js +3 -1
- package/dist/infrastructure/database/migration-history-service.js.map +1 -1
- package/dist/infrastructure/logging/triple-extraction-logger.d.ts +6 -0
- package/dist/infrastructure/logging/triple-extraction-logger.d.ts.map +1 -1
- package/dist/infrastructure/logging/triple-extraction-logger.js +50 -12
- package/dist/infrastructure/logging/triple-extraction-logger.js.map +1 -1
- package/dist/infrastructure/scheduler/batch-scheduler.d.ts.map +1 -1
- package/dist/infrastructure/scheduler/batch-scheduler.js +5 -2
- package/dist/infrastructure/scheduler/batch-scheduler.js.map +1 -1
- package/dist/infrastructure/scheduler/file-logger.d.ts +4 -1
- package/dist/infrastructure/scheduler/file-logger.d.ts.map +1 -1
- package/dist/infrastructure/scheduler/file-logger.js +46 -16
- package/dist/infrastructure/scheduler/file-logger.js.map +1 -1
- package/dist/scripts/check-migration-status.d.ts.map +1 -1
- package/dist/scripts/check-migration-status.js +19 -6
- package/dist/scripts/check-migration-status.js.map +1 -1
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/http-server.js +2 -7
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +33 -7
- package/dist/server/index.js.map +1 -1
- package/dist/server/server-factory.d.ts +65 -0
- package/dist/server/server-factory.d.ts.map +1 -0
- package/dist/server/server-factory.js +40 -0
- package/dist/server/server-factory.js.map +1 -0
- package/dist/server/servers/sse-server.d.ts +33 -0
- package/dist/server/servers/sse-server.d.ts.map +1 -0
- package/dist/server/servers/sse-server.js +48 -0
- package/dist/server/servers/sse-server.js.map +1 -0
- package/dist/server/servers/stdio-server.d.ts +34 -0
- package/dist/server/servers/stdio-server.d.ts.map +1 -0
- package/dist/server/servers/stdio-server.js +58 -0
- package/dist/server/servers/stdio-server.js.map +1 -0
- package/dist/server/simple-mcp-server.d.ts +5 -0
- package/dist/server/simple-mcp-server.d.ts.map +1 -1
- package/dist/server/simple-mcp-server.js +17 -7
- package/dist/server/simple-mcp-server.js.map +1 -1
- package/dist/server/sse-server-impl.d.ts +22 -0
- package/dist/server/sse-server-impl.d.ts.map +1 -0
- package/dist/server/sse-server-impl.js +39 -0
- package/dist/server/sse-server-impl.js.map +1 -0
- package/dist/server/stdio-server-impl.d.ts +12 -0
- package/dist/server/stdio-server-impl.d.ts.map +1 -0
- package/dist/server/stdio-server-impl.js +19 -0
- package/dist/server/stdio-server-impl.js.map +1 -0
- package/dist/services/quality-assurance/quality-recorder.js +2 -2
- package/dist/services/quality-assurance/quality-recorder.js.map +1 -1
- package/dist/services/quality-assurance/quality-threshold-manager.js +1 -1
- package/dist/services/quality-assurance/quality-threshold-manager.js.map +1 -1
- package/dist/shared/config/environment.d.ts.map +1 -1
- package/dist/shared/config/environment.js +3 -1
- package/dist/shared/config/environment.js.map +1 -1
- package/dist/shared/types/vector-search.types.d.ts +1 -0
- package/dist/shared/types/vector-search.types.d.ts.map +1 -1
- package/dist/shared/utils/fts5-migration-status.d.ts.map +1 -1
- package/dist/shared/utils/fts5-migration-status.js +6 -2
- package/dist/shared/utils/fts5-migration-status.js.map +1 -1
- package/dist/shared/utils/logger.d.ts.map +1 -1
- package/dist/shared/utils/logger.js +15 -2
- package/dist/shared/utils/logger.js.map +1 -1
- package/dist/shared/utils/path-validator.d.ts +27 -0
- package/dist/shared/utils/path-validator.d.ts.map +1 -0
- package/dist/shared/utils/path-validator.js +166 -0
- package/dist/shared/utils/path-validator.js.map +1 -0
- package/dist/shared/utils/pii-masker.d.ts +31 -0
- package/dist/shared/utils/pii-masker.d.ts.map +1 -1
- package/dist/shared/utils/pii-masker.js +99 -0
- package/dist/shared/utils/pii-masker.js.map +1 -1
- package/dist/shared/utils/procedural-memory-extractor.d.ts.map +1 -1
- package/dist/shared/utils/procedural-memory-extractor.js +39 -33
- package/dist/shared/utils/procedural-memory-extractor.js.map +1 -1
- package/dist/shared/utils/prompt-template-loader.d.ts +6 -0
- package/dist/shared/utils/prompt-template-loader.d.ts.map +1 -1
- package/dist/shared/utils/prompt-template-loader.js +20 -8
- package/dist/shared/utils/prompt-template-loader.js.map +1 -1
- package/dist/shared/utils/reflection-notes-merge.d.ts.map +1 -1
- package/dist/shared/utils/reflection-notes-merge.js +5 -4
- package/dist/shared/utils/reflection-notes-merge.js.map +1 -1
- package/dist/shared/utils/sql-security-validator.d.ts +25 -0
- package/dist/shared/utils/sql-security-validator.d.ts.map +1 -0
- package/dist/shared/utils/sql-security-validator.js +67 -0
- package/dist/shared/utils/sql-security-validator.js.map +1 -0
- package/dist/shared/utils/write-coalescing.d.ts.map +1 -1
- package/dist/shared/utils/write-coalescing.js +7 -3
- package/dist/shared/utils/write-coalescing.js.map +1 -1
- package/package.json +2 -1
- package/scripts/__tests__/check-db-integrity.integration.spec.ts +163 -0
- package/scripts/__tests__/fix-migration.integration.spec.ts +203 -0
- package/scripts/__tests__/migrate-embedding-data.integration.spec.ts +219 -0
- package/scripts/__tests__/regenerate-embeddings.integration.spec.ts +192 -0
- package/scripts/backup-daily.bat +10 -2
- package/scripts/backup-embeddings.js +67 -56
- package/scripts/check-db-integrity.js +49 -25
- package/scripts/check-file-sizes.ts +4 -4
- package/scripts/check-path-traversal.ts +370 -0
- package/scripts/check-pii-masking.ts +357 -0
- package/scripts/check-sql-injection.ts +598 -0
- package/scripts/debug-embeddings.js +74 -93
- package/scripts/fix-migration.js +115 -80
- package/scripts/fix-vector-dimensions.js +70 -89
- package/scripts/migrate-embedding-data.js +111 -25
- package/scripts/regenerate-embeddings.js +31 -15
- package/scripts/run-migration.js +144 -107
- package/scripts/safe-migration.js +192 -142
- package/scripts/save-work-memory.ts +6 -7
- package/scripts/simple-migrate.js +66 -34
- package/scripts/simple-update.js +147 -109
- package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts +0 -56
- package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts.map +0 -1
- package/dist/domains/search/algorithms/vector-search-engine-refactored.js +0 -101
- package/dist/domains/search/algorithms/vector-search-engine-refactored.js.map +0 -1
|
@@ -1,46 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Memento 데이터베이스 무결성 검사 스크립트
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
|
|
6
|
+
*
|
|
7
|
+
* 사용법:
|
|
8
|
+
* - 개발 환경: npx tsx scripts/check-db-integrity.js
|
|
9
|
+
* - 프로덕션: npm run build && node dist/scripts/check-db-integrity.js
|
|
5
10
|
*/
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
// TypeScript 소스를 직접 import (tsx로 실행 시)
|
|
13
|
+
// 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
|
|
14
|
+
import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
|
|
15
|
+
import { existsSync, mkdirSync, appendFileSync } from 'fs';
|
|
16
|
+
import { join } from 'path';
|
|
10
17
|
|
|
11
|
-
const DB_PATH = './data/memory.db';
|
|
12
18
|
const LOG_PATH = './logs/db-integrity.log';
|
|
13
19
|
|
|
14
20
|
// 로그 디렉토리 생성
|
|
15
|
-
if (!
|
|
16
|
-
|
|
21
|
+
if (!existsSync('./logs')) {
|
|
22
|
+
mkdirSync('./logs', { recursive: true });
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
/**
|
|
26
|
+
* 로그 메시지 출력 및 파일 기록
|
|
27
|
+
*/
|
|
19
28
|
function log(message) {
|
|
20
29
|
const timestamp = new Date().toISOString();
|
|
21
30
|
const logMessage = `[${timestamp}] ${message}\n`;
|
|
22
31
|
console.log(message);
|
|
23
|
-
|
|
32
|
+
appendFileSync(LOG_PATH, logMessage);
|
|
24
33
|
}
|
|
25
34
|
|
|
26
|
-
|
|
35
|
+
/**
|
|
36
|
+
* 데이터베이스 무결성 검사
|
|
37
|
+
*
|
|
38
|
+
* @returns {Promise<boolean>} 검사 통과 여부
|
|
39
|
+
*/
|
|
40
|
+
async function checkDatabaseIntegrity() {
|
|
27
41
|
log('데이터베이스 무결성 검사 시작...');
|
|
28
42
|
|
|
43
|
+
let db = null;
|
|
44
|
+
|
|
29
45
|
try {
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 데이터베이스 연결 테스트
|
|
37
|
-
const db = new Database(DB_PATH);
|
|
46
|
+
// 공통 모듈을 사용하여 데이터베이스 초기화
|
|
47
|
+
// initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
|
|
48
|
+
db = await initializeDatabase();
|
|
38
49
|
|
|
39
50
|
// PRAGMA integrity_check 실행
|
|
40
51
|
const integrityResult = db.prepare('PRAGMA integrity_check').get();
|
|
41
52
|
if (integrityResult.integrity_check !== 'ok') {
|
|
42
53
|
log(`❌ 데이터베이스 무결성 검사 실패: ${integrityResult.integrity_check}`);
|
|
43
|
-
db.close();
|
|
44
54
|
return false;
|
|
45
55
|
}
|
|
46
56
|
|
|
@@ -52,7 +62,7 @@ function checkDatabaseIntegrity() {
|
|
|
52
62
|
|
|
53
63
|
if (tables.length < 3) {
|
|
54
64
|
log('❌ 필수 테이블이 누락되었습니다.');
|
|
55
|
-
|
|
65
|
+
log(` 발견된 테이블: ${tables.map(t => t.name).join(', ')}`);
|
|
56
66
|
return false;
|
|
57
67
|
}
|
|
58
68
|
|
|
@@ -65,17 +75,27 @@ function checkDatabaseIntegrity() {
|
|
|
65
75
|
log(` - 임베딩: ${embeddingCount.count}개`);
|
|
66
76
|
log(` - 테이블: ${tables.length}개`);
|
|
67
77
|
|
|
68
|
-
db.close();
|
|
69
78
|
return true;
|
|
70
79
|
|
|
71
80
|
} catch (error) {
|
|
72
81
|
log(`❌ 데이터베이스 검사 중 오류 발생: ${error.message}`);
|
|
82
|
+
if (error.stack) {
|
|
83
|
+
log(` 스택 트레이스: ${error.stack}`);
|
|
84
|
+
}
|
|
73
85
|
return false;
|
|
86
|
+
} finally {
|
|
87
|
+
// 데이터베이스 연결 종료
|
|
88
|
+
if (db) {
|
|
89
|
+
closeDatabase(db);
|
|
90
|
+
}
|
|
74
91
|
}
|
|
75
92
|
}
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
/**
|
|
95
|
+
* 메인 함수
|
|
96
|
+
*/
|
|
97
|
+
async function main() {
|
|
98
|
+
const isHealthy = await checkDatabaseIntegrity();
|
|
79
99
|
|
|
80
100
|
if (!isHealthy) {
|
|
81
101
|
log('🚨 데이터베이스에 문제가 있습니다. 백업에서 복구를 고려하세요.');
|
|
@@ -86,8 +106,12 @@ function main() {
|
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
// 스크립트가 직접 실행될 때만 main 함수 호출
|
|
110
|
+
if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
|
|
111
|
+
main().catch((error) => {
|
|
112
|
+
console.error('❌ 스크립트 실행 중 오류 발생:', error);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
});
|
|
91
115
|
}
|
|
92
116
|
|
|
93
|
-
|
|
117
|
+
export { checkDatabaseIntegrity };
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* tsx scripts/check-file-sizes.ts --ci
|
|
10
10
|
* tsx scripts/check-file-sizes.ts --threshold 500
|
|
11
11
|
* tsx scripts/check-file-sizes.ts --directory src/
|
|
12
|
-
* tsx scripts/check-file-sizes.ts --exclude
|
|
12
|
+
* tsx scripts/check-file-sizes.ts --exclude '*.spec.ts'
|
|
13
13
|
*
|
|
14
14
|
* 목표:
|
|
15
15
|
* - 핵심 핸들러/서비스 파일이 500줄 이하
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - 경고/에러 출력
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { readFileSync
|
|
20
|
+
import { readFileSync } from 'fs';
|
|
21
21
|
import { readdir } from 'fs/promises';
|
|
22
22
|
import { join, relative } from 'path';
|
|
23
23
|
|
|
@@ -111,7 +111,7 @@ function printHelp(): void {
|
|
|
111
111
|
tsx scripts/check-file-sizes.ts
|
|
112
112
|
tsx scripts/check-file-sizes.ts --ci
|
|
113
113
|
tsx scripts/check-file-sizes.ts --threshold 500
|
|
114
|
-
tsx scripts/check-file-sizes.ts --directory src/ --exclude
|
|
114
|
+
tsx scripts/check-file-sizes.ts --directory src/ --exclude '*.spec.ts'
|
|
115
115
|
`);
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -164,7 +164,7 @@ function validateFileSize(filePath: string, threshold: number): FileSizeResult {
|
|
|
164
164
|
* 패턴 매칭 (간단한 glob 패턴 지원)
|
|
165
165
|
*
|
|
166
166
|
* @param path - 파일 경로
|
|
167
|
-
* @param pattern - 패턴 (예:
|
|
167
|
+
* @param pattern - 패턴 (예: node_modules, *.spec.ts)
|
|
168
168
|
* @returns 매칭 여부
|
|
169
169
|
*/
|
|
170
170
|
function matchesPattern(path: string, pattern: string): boolean {
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Path Traversal 취약점 검사 스크립트
|
|
4
|
+
*
|
|
5
|
+
* PRD 0019: 보안 강화 (Phase 1) - Path Traversal 방지
|
|
6
|
+
*
|
|
7
|
+
* 사용법:
|
|
8
|
+
* tsx scripts/check-path-traversal.ts
|
|
9
|
+
* tsx scripts/check-path-traversal.ts --ci
|
|
10
|
+
* tsx scripts/check-path-traversal.ts --directory src/
|
|
11
|
+
*
|
|
12
|
+
* 목표:
|
|
13
|
+
* - 모든 파일 경로 처리 코드에서 경로 검증 적용 확인
|
|
14
|
+
* - Path Traversal 취약점 0개
|
|
15
|
+
* - CI/CD 통합 가능
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/* eslint-disable security/detect-unsafe-regex */
|
|
19
|
+
// 정규식 패턴은 안전한 패턴임
|
|
20
|
+
|
|
21
|
+
import { readFileSync } from 'fs';
|
|
22
|
+
import { readdir } from 'fs/promises';
|
|
23
|
+
import { join, relative } from 'path';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* CLI 옵션
|
|
27
|
+
*/
|
|
28
|
+
interface CliOptions {
|
|
29
|
+
ci?: boolean;
|
|
30
|
+
directory?: string;
|
|
31
|
+
exclude?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Path Traversal 취약점 발견 위치
|
|
36
|
+
*/
|
|
37
|
+
interface PathTraversalLocation {
|
|
38
|
+
file: string;
|
|
39
|
+
line: number;
|
|
40
|
+
column: number;
|
|
41
|
+
pattern: string; // 발견된 패턴 종류
|
|
42
|
+
context: string; // 해당 라인 내용
|
|
43
|
+
severity: 'high' | 'medium' | 'low'; // 심각도
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 검사 결과
|
|
48
|
+
*/
|
|
49
|
+
interface CheckResult {
|
|
50
|
+
total: number;
|
|
51
|
+
locations: PathTraversalLocation[];
|
|
52
|
+
byFile: Map<string, PathTraversalLocation[]>;
|
|
53
|
+
byPattern: Map<string, number>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 명령줄 인자 파싱
|
|
58
|
+
*/
|
|
59
|
+
function parseArgs(): CliOptions {
|
|
60
|
+
const args = process.argv.slice(2);
|
|
61
|
+
const options: CliOptions = {
|
|
62
|
+
exclude: ['**/node_modules/**', '**/dist/**', '**/*.d.ts', '**/*.spec.ts', '**/__tests__/**']
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < args.length; i++) {
|
|
66
|
+
const arg = args[i];
|
|
67
|
+
if (arg === '--ci') {
|
|
68
|
+
options.ci = true;
|
|
69
|
+
} else if (arg === '--directory' && args[i + 1]) {
|
|
70
|
+
options.directory = args[i + 1];
|
|
71
|
+
i++;
|
|
72
|
+
} else if (arg === '--exclude' && args[i + 1]) {
|
|
73
|
+
if (!options.exclude) {
|
|
74
|
+
options.exclude = [];
|
|
75
|
+
}
|
|
76
|
+
options.exclude.push(args[i + 1]);
|
|
77
|
+
i++;
|
|
78
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
79
|
+
printHelp();
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return options;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 도움말 출력
|
|
89
|
+
*/
|
|
90
|
+
function printHelp(): void {
|
|
91
|
+
console.log(`
|
|
92
|
+
Path Traversal 취약점 검사 스크립트
|
|
93
|
+
|
|
94
|
+
사용법:
|
|
95
|
+
tsx scripts/check-path-traversal.ts [options]
|
|
96
|
+
|
|
97
|
+
옵션:
|
|
98
|
+
--ci CI 모드 (취약점 발견 시 exit code 1 반환)
|
|
99
|
+
--directory <path> 검사할 디렉토리 (기본값: src/)
|
|
100
|
+
--exclude <pattern> 제외할 파일 패턴 (여러 번 사용 가능)
|
|
101
|
+
--help, -h 도움말 출력
|
|
102
|
+
|
|
103
|
+
예제:
|
|
104
|
+
tsx scripts/check-path-traversal.ts
|
|
105
|
+
tsx scripts/check-path-traversal.ts --ci
|
|
106
|
+
tsx scripts/check-path-traversal.ts --directory src/infrastructure
|
|
107
|
+
`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 파일이 제외 패턴에 해당하는지 확인
|
|
112
|
+
*/
|
|
113
|
+
function shouldExclude(file: string, excludePatterns: string[]): boolean {
|
|
114
|
+
for (const pattern of excludePatterns) {
|
|
115
|
+
// 간단한 패턴 매칭 (glob 패턴은 복잡하므로 기본적인 것만 지원)
|
|
116
|
+
if (pattern.includes('**')) {
|
|
117
|
+
const regex = new RegExp(pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*'));
|
|
118
|
+
if (regex.test(file)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
} else if (file.includes(pattern)) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 재귀적으로 디렉토리 탐색
|
|
130
|
+
*/
|
|
131
|
+
async function findFiles(
|
|
132
|
+
dir: string,
|
|
133
|
+
excludePatterns: string[],
|
|
134
|
+
fileList: string[] = []
|
|
135
|
+
): Promise<string[]> {
|
|
136
|
+
try {
|
|
137
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
138
|
+
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const fullPath = join(dir, entry.name);
|
|
141
|
+
const relativePath = relative(process.cwd(), fullPath);
|
|
142
|
+
|
|
143
|
+
if (shouldExclude(relativePath, excludePatterns)) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
await findFiles(fullPath, excludePatterns, fileList);
|
|
149
|
+
} else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
150
|
+
fileList.push(fullPath);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
// 디렉토리 읽기 실패는 무시
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return fileList;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 파일 내용에서 Path Traversal 취약점 검색
|
|
162
|
+
*/
|
|
163
|
+
function checkFile(filePath: string): PathTraversalLocation[] {
|
|
164
|
+
const locations: PathTraversalLocation[] = [];
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
168
|
+
const lines = content.split('\n');
|
|
169
|
+
const relativePath = relative(process.cwd(), filePath);
|
|
170
|
+
|
|
171
|
+
// 파일 경로를 다루는 코드 패턴 검색
|
|
172
|
+
const pathPatterns = [
|
|
173
|
+
{
|
|
174
|
+
pattern: /(readFile|writeFile|appendFile|createReadStream|createWriteStream|unlink|rmdir|mkdir|access|stat|readdir)\s*\([^)]*['"`]([^'"`]*(?:\.\.\/|\.\.\\\\)[^'"`]*)['"`]/g,
|
|
175
|
+
type: 'file-operation-with-traversal',
|
|
176
|
+
severity: 'high' as const
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
pattern: /path\.(join|resolve)\s*\([^)]*['"`]([^'"`]*(?:\.\.\/|\.\.\\\\)[^'"`]*)['"`]/g,
|
|
180
|
+
type: 'path-join-with-traversal',
|
|
181
|
+
severity: 'high' as const
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
pattern: /fs\.(readFile|writeFile|appendFile|createReadStream|createWriteStream|unlink|rmdir|mkdir|access|stat|readdir)Sync\s*\([^)]*['"`]([^'"`]*(?:\.\.\/|\.\.\\\\)[^'"`]*)['"`]/g,
|
|
185
|
+
type: 'fs-sync-with-traversal',
|
|
186
|
+
severity: 'high' as const
|
|
187
|
+
}
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
// 각 패턴 검색
|
|
191
|
+
for (const pathPattern of pathPatterns) {
|
|
192
|
+
let match;
|
|
193
|
+
while ((match = pathPattern.pattern.exec(content)) !== null) {
|
|
194
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
195
|
+
const line = lines[lineNumber - 1];
|
|
196
|
+
|
|
197
|
+
// validateFilePath 또는 sanitizeFileName 사용 여부 확인
|
|
198
|
+
const beforeContext = content.substring(Math.max(0, match.index - 500), match.index);
|
|
199
|
+
const afterContext = content.substring(match.index, Math.min(content.length, match.index + 500));
|
|
200
|
+
|
|
201
|
+
// 경로 검증이 적용되지 않은 경우
|
|
202
|
+
if (!beforeContext.includes('validateFilePath') &&
|
|
203
|
+
!beforeContext.includes('sanitizeFileName') &&
|
|
204
|
+
!afterContext.includes('validateFilePath') &&
|
|
205
|
+
!afterContext.includes('sanitizeFileName')) {
|
|
206
|
+
// path-validator.ts 파일 자체는 제외
|
|
207
|
+
if (!relativePath.includes('path-validator.ts')) {
|
|
208
|
+
locations.push({
|
|
209
|
+
file: relativePath,
|
|
210
|
+
line: lineNumber,
|
|
211
|
+
column: match.index - content.substring(0, match.index).lastIndexOf('\n') - 1,
|
|
212
|
+
pattern: pathPattern.type,
|
|
213
|
+
context: line.trim(),
|
|
214
|
+
severity: pathPattern.severity
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 사용자 입력을 받는 파일 경로 처리 코드 검색
|
|
222
|
+
const userInputPatterns = [
|
|
223
|
+
{
|
|
224
|
+
pattern: /(readFile|writeFile|appendFile|createReadStream|createWriteStream|unlink|rmdir|mkdir|access|stat|readdir)\s*\([^)]*(\w+)\s*[,)]/g,
|
|
225
|
+
type: 'file-operation-with-user-input',
|
|
226
|
+
severity: 'medium' as const
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
pattern: /path\.(join|resolve)\s*\([^)]*(\w+)\s*[,)]/g,
|
|
230
|
+
type: 'path-join-with-user-input',
|
|
231
|
+
severity: 'medium' as const
|
|
232
|
+
}
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
// 사용자 입력 패턴 검색 (함수 파라미터로 받는 경우)
|
|
236
|
+
for (const userInputPattern of userInputPatterns) {
|
|
237
|
+
let match;
|
|
238
|
+
while ((match = userInputPattern.pattern.exec(content)) !== null) {
|
|
239
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
240
|
+
const line = lines[lineNumber - 1];
|
|
241
|
+
|
|
242
|
+
// 함수 파라미터인지 확인 (함수 시그니처에서 파라미터로 받는 경우)
|
|
243
|
+
const beforeContext = content.substring(Math.max(0, match.index - 1000), match.index);
|
|
244
|
+
const functionMatch = beforeContext.match(/(?:function|const|let|var)\s+\w+\s*\([^)]*(\w+)\s*[,)]/);
|
|
245
|
+
|
|
246
|
+
if (functionMatch) {
|
|
247
|
+
const paramName = functionMatch[1];
|
|
248
|
+
// 해당 파라미터가 경로 검증 없이 사용되는지 확인
|
|
249
|
+
const paramUsagePattern = new RegExp(`\\b${paramName}\\b[^,)]*[,)]`, 'g');
|
|
250
|
+
const paramUsageMatch = paramUsagePattern.exec(line);
|
|
251
|
+
|
|
252
|
+
if (paramUsageMatch) {
|
|
253
|
+
// validateFilePath 또는 sanitizeFileName 사용 여부 확인
|
|
254
|
+
if (!beforeContext.includes('validateFilePath') &&
|
|
255
|
+
!beforeContext.includes('sanitizeFileName') &&
|
|
256
|
+
!line.includes('validateFilePath') &&
|
|
257
|
+
!line.includes('sanitizeFileName')) {
|
|
258
|
+
// path-validator.ts 파일 자체는 제외
|
|
259
|
+
if (!relativePath.includes('path-validator.ts')) {
|
|
260
|
+
locations.push({
|
|
261
|
+
file: relativePath,
|
|
262
|
+
line: lineNumber,
|
|
263
|
+
column: match.index - content.substring(0, match.index).lastIndexOf('\n') - 1,
|
|
264
|
+
pattern: userInputPattern.type,
|
|
265
|
+
context: line.trim(),
|
|
266
|
+
severity: userInputPattern.severity
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
} catch (error) {
|
|
276
|
+
// 파일 읽기 실패는 무시
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return locations;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 모든 파일 검사
|
|
284
|
+
*/
|
|
285
|
+
async function checkAllFiles(options: CliOptions): Promise<CheckResult> {
|
|
286
|
+
const directory = options.directory || 'src';
|
|
287
|
+
const excludePatterns = options.exclude || [];
|
|
288
|
+
|
|
289
|
+
const files = await findFiles(directory, excludePatterns);
|
|
290
|
+
const locations: PathTraversalLocation[] = [];
|
|
291
|
+
const byFile = new Map<string, PathTraversalLocation[]>();
|
|
292
|
+
const byPattern = new Map<string, number>();
|
|
293
|
+
|
|
294
|
+
for (const file of files) {
|
|
295
|
+
const fileLocations = checkFile(file);
|
|
296
|
+
if (fileLocations.length > 0) {
|
|
297
|
+
const relativePath = relative(process.cwd(), file);
|
|
298
|
+
locations.push(...fileLocations);
|
|
299
|
+
byFile.set(relativePath, fileLocations);
|
|
300
|
+
|
|
301
|
+
for (const loc of fileLocations) {
|
|
302
|
+
byPattern.set(loc.pattern, (byPattern.get(loc.pattern) || 0) + 1);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
total: locations.length,
|
|
309
|
+
locations,
|
|
310
|
+
byFile,
|
|
311
|
+
byPattern
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 결과 출력
|
|
317
|
+
*/
|
|
318
|
+
function printResults(result: CheckResult): void {
|
|
319
|
+
console.log('\n⚠️ 발견된 Path Traversal 취약점:', result.total, '개');
|
|
320
|
+
|
|
321
|
+
if (result.total === 0) {
|
|
322
|
+
console.log('✅ 모든 파일 경로 처리 코드에서 경로 검증이 적용되어 있습니다.');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
console.log('📁 파일별 취약점 목록:');
|
|
327
|
+
|
|
328
|
+
for (const [file, locations] of result.byFile.entries()) {
|
|
329
|
+
console.log(`\n ${file} (${locations.length}개):`);
|
|
330
|
+
|
|
331
|
+
for (const loc of locations) {
|
|
332
|
+
const severityIcon = loc.severity === 'high' ? '🔴' : loc.severity === 'medium' ? '🟡' : '🟢';
|
|
333
|
+
console.log(` ${severityIcon} 라인 ${loc.line}:${loc.column} - ${loc.pattern}`);
|
|
334
|
+
console.log(` ${loc.context}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
console.log('\n📊 패턴별 통계:');
|
|
339
|
+
for (const [pattern, count] of result.byPattern.entries()) {
|
|
340
|
+
console.log(` ${pattern}: ${count}개`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* 메인 함수
|
|
346
|
+
*/
|
|
347
|
+
async function main(): Promise<void> {
|
|
348
|
+
const options = parseArgs();
|
|
349
|
+
|
|
350
|
+
console.log('🔍 Path Traversal 취약점 검사 시작...\n');
|
|
351
|
+
|
|
352
|
+
const result = await checkAllFiles(options);
|
|
353
|
+
printResults(result);
|
|
354
|
+
|
|
355
|
+
if (options.ci && result.total > 0) {
|
|
356
|
+
console.error('\n❌ CI 실패: Path Traversal 취약점이 발견되었습니다.');
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (result.total === 0) {
|
|
361
|
+
console.log('\n✅ 검사 완료: 모든 파일 경로 처리 코드에서 경로 검증이 적용되어 있습니다.');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 스크립트 실행
|
|
366
|
+
main().catch(error => {
|
|
367
|
+
console.error('❌ 스크립트 실행 실패:', error);
|
|
368
|
+
process.exit(1);
|
|
369
|
+
});
|
|
370
|
+
|