archrisk-engine 1.0.2 → 1.0.3
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/i18n/locales.d.ts +160 -0
- package/dist/i18n/locales.js +227 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/repoAnalyzer.d.ts +5 -11
- package/dist/repoAnalyzer.js +33 -139
- package/package.json +1 -1
- package/src/i18n/locales.ts +225 -0
- package/src/index.ts +1 -0
- package/src/repoAnalyzer.ts +35 -149
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
export declare const LOCALES: {
|
|
2
|
+
en: {
|
|
3
|
+
'RR-SEC-001': {
|
|
4
|
+
title: string;
|
|
5
|
+
category: string;
|
|
6
|
+
standard: string;
|
|
7
|
+
impact: string;
|
|
8
|
+
action: string;
|
|
9
|
+
reference: string;
|
|
10
|
+
whenItMatters: string;
|
|
11
|
+
};
|
|
12
|
+
'RR-TEST-001': {
|
|
13
|
+
title: string;
|
|
14
|
+
category: string;
|
|
15
|
+
evidence: string;
|
|
16
|
+
standard: string;
|
|
17
|
+
impact: string;
|
|
18
|
+
action: string;
|
|
19
|
+
reference: string;
|
|
20
|
+
whenItMatters: string;
|
|
21
|
+
};
|
|
22
|
+
'RR-CI-001': {
|
|
23
|
+
title: string;
|
|
24
|
+
category: string;
|
|
25
|
+
evidence: string;
|
|
26
|
+
standard: string;
|
|
27
|
+
impact: string;
|
|
28
|
+
action: string;
|
|
29
|
+
reference: string;
|
|
30
|
+
whenItMatters: string;
|
|
31
|
+
};
|
|
32
|
+
'RR-OPS-001': {
|
|
33
|
+
title: string;
|
|
34
|
+
category: string;
|
|
35
|
+
standard: string;
|
|
36
|
+
impact: string;
|
|
37
|
+
action: string;
|
|
38
|
+
reference: string;
|
|
39
|
+
whenItMatters: string;
|
|
40
|
+
};
|
|
41
|
+
'RR-LOG-001': {
|
|
42
|
+
title: string;
|
|
43
|
+
category: string;
|
|
44
|
+
evidence: string;
|
|
45
|
+
standard: string;
|
|
46
|
+
impact: string;
|
|
47
|
+
action: string;
|
|
48
|
+
reference: string;
|
|
49
|
+
whenItMatters: string;
|
|
50
|
+
};
|
|
51
|
+
'RR-DEP-001': {
|
|
52
|
+
title: string;
|
|
53
|
+
category: string;
|
|
54
|
+
standard: string;
|
|
55
|
+
impact: string;
|
|
56
|
+
action: string;
|
|
57
|
+
reference: string;
|
|
58
|
+
whenItMatters: string;
|
|
59
|
+
};
|
|
60
|
+
'RR-LINT-001': {
|
|
61
|
+
title: string;
|
|
62
|
+
category: string;
|
|
63
|
+
standard: string;
|
|
64
|
+
impact: string;
|
|
65
|
+
action: string;
|
|
66
|
+
reference: string;
|
|
67
|
+
whenItMatters: string;
|
|
68
|
+
};
|
|
69
|
+
DEFAULT: {
|
|
70
|
+
title: string;
|
|
71
|
+
category: string;
|
|
72
|
+
standard: string;
|
|
73
|
+
impact: string;
|
|
74
|
+
action: string;
|
|
75
|
+
reference: string;
|
|
76
|
+
whenItMatters: string;
|
|
77
|
+
};
|
|
78
|
+
DISCLOSURE: string;
|
|
79
|
+
CTA: string;
|
|
80
|
+
};
|
|
81
|
+
ko: {
|
|
82
|
+
'RR-SEC-001': {
|
|
83
|
+
title: string;
|
|
84
|
+
category: string;
|
|
85
|
+
standard: string;
|
|
86
|
+
impact: string;
|
|
87
|
+
action: string;
|
|
88
|
+
reference: string;
|
|
89
|
+
whenItMatters: string;
|
|
90
|
+
};
|
|
91
|
+
'RR-TEST-001': {
|
|
92
|
+
title: string;
|
|
93
|
+
category: string;
|
|
94
|
+
evidence: string;
|
|
95
|
+
standard: string;
|
|
96
|
+
impact: string;
|
|
97
|
+
action: string;
|
|
98
|
+
reference: string;
|
|
99
|
+
whenItMatters: string;
|
|
100
|
+
};
|
|
101
|
+
'RR-CI-001': {
|
|
102
|
+
title: string;
|
|
103
|
+
category: string;
|
|
104
|
+
evidence: string;
|
|
105
|
+
standard: string;
|
|
106
|
+
impact: string;
|
|
107
|
+
action: string;
|
|
108
|
+
reference: string;
|
|
109
|
+
whenItMatters: string;
|
|
110
|
+
};
|
|
111
|
+
'RR-OPS-001': {
|
|
112
|
+
title: string;
|
|
113
|
+
category: string;
|
|
114
|
+
standard: string;
|
|
115
|
+
impact: string;
|
|
116
|
+
action: string;
|
|
117
|
+
reference: string;
|
|
118
|
+
whenItMatters: string;
|
|
119
|
+
};
|
|
120
|
+
'RR-LOG-001': {
|
|
121
|
+
title: string;
|
|
122
|
+
category: string;
|
|
123
|
+
evidence: string;
|
|
124
|
+
standard: string;
|
|
125
|
+
impact: string;
|
|
126
|
+
action: string;
|
|
127
|
+
reference: string;
|
|
128
|
+
whenItMatters: string;
|
|
129
|
+
};
|
|
130
|
+
'RR-DEP-001': {
|
|
131
|
+
title: string;
|
|
132
|
+
category: string;
|
|
133
|
+
standard: string;
|
|
134
|
+
impact: string;
|
|
135
|
+
action: string;
|
|
136
|
+
reference: string;
|
|
137
|
+
whenItMatters: string;
|
|
138
|
+
};
|
|
139
|
+
'RR-LINT-001': {
|
|
140
|
+
title: string;
|
|
141
|
+
category: string;
|
|
142
|
+
standard: string;
|
|
143
|
+
impact: string;
|
|
144
|
+
action: string;
|
|
145
|
+
reference: string;
|
|
146
|
+
whenItMatters: string;
|
|
147
|
+
};
|
|
148
|
+
DEFAULT: {
|
|
149
|
+
title: string;
|
|
150
|
+
category: string;
|
|
151
|
+
standard: string;
|
|
152
|
+
impact: string;
|
|
153
|
+
action: string;
|
|
154
|
+
reference: string;
|
|
155
|
+
whenItMatters: string;
|
|
156
|
+
};
|
|
157
|
+
DISCLOSURE: string;
|
|
158
|
+
CTA: string;
|
|
159
|
+
};
|
|
160
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOCALES = void 0;
|
|
4
|
+
exports.LOCALES = {
|
|
5
|
+
en: {
|
|
6
|
+
'RR-SEC-001': {
|
|
7
|
+
title: "Security Vulnerability Detected",
|
|
8
|
+
category: 'Security',
|
|
9
|
+
standard: "OWASP Top 10 A03:2021 – Injection",
|
|
10
|
+
impact: "Potential for unauthorized access or data leakage through injection attacks.",
|
|
11
|
+
action: `
|
|
12
|
+
# Action: Isolate and use Environment Variables
|
|
13
|
+
subprocess.run(..., shell=False) # Recommended
|
|
14
|
+
# Or use .env file
|
|
15
|
+
import os
|
|
16
|
+
SECRET = os.getenv('MY_SECRET')
|
|
17
|
+
`,
|
|
18
|
+
reference: "https://docs.python.org/3/library/subprocess.html#security-considerations",
|
|
19
|
+
whenItMatters: "Immediately upon deployment, as scanners can detect this."
|
|
20
|
+
},
|
|
21
|
+
'RR-TEST-001': {
|
|
22
|
+
title: "Missing Automated Tests",
|
|
23
|
+
category: 'Service Interruption',
|
|
24
|
+
evidence: "No tests/ directory or pytest/unittest configuration found.",
|
|
25
|
+
standard: "pytest Framework Documentation",
|
|
26
|
+
impact: "Unable to verify if validation logic breaks existing features. High risk of regression.",
|
|
27
|
+
action: `
|
|
28
|
+
# Action: Create tests/test_smoke.py
|
|
29
|
+
def test_health_check():
|
|
30
|
+
assert True # Basic sanity check
|
|
31
|
+
`,
|
|
32
|
+
reference: "https://docs.pytest.org/",
|
|
33
|
+
whenItMatters: "When team size > 2 or deployment frequency increases."
|
|
34
|
+
},
|
|
35
|
+
'RR-CI-001': {
|
|
36
|
+
title: "Missing CI Pipeline",
|
|
37
|
+
category: 'Service Interruption',
|
|
38
|
+
evidence: "No GitHub Actions (.github/workflows/*.yml) or CI configuration found.",
|
|
39
|
+
standard: "GitHub Actions Documentation",
|
|
40
|
+
impact: "Manual deployments are prone to human error and lack consistency.",
|
|
41
|
+
action: `
|
|
42
|
+
# Action: Create .github/workflows/ci.yml
|
|
43
|
+
name: CI
|
|
44
|
+
on: [push]
|
|
45
|
+
jobs:
|
|
46
|
+
test:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v3
|
|
50
|
+
- run: npm test
|
|
51
|
+
`,
|
|
52
|
+
reference: "https://docs.github.com/en/actions",
|
|
53
|
+
whenItMatters: "When deploying more than twice a week."
|
|
54
|
+
},
|
|
55
|
+
'RR-OPS-001': {
|
|
56
|
+
title: "Project Hygiene Check Failed",
|
|
57
|
+
category: 'Service Interruption',
|
|
58
|
+
standard: "12-Factor App / Docker Documentation",
|
|
59
|
+
impact: "Inconsistency between dev and prod environments (\"It works on my machine\").",
|
|
60
|
+
action: `
|
|
61
|
+
# Checklist to Fix:
|
|
62
|
+
1. Create 'Dockerfile'
|
|
63
|
+
2. Create '.gitignore' (use gitignore.io)
|
|
64
|
+
3. Create 'requirements.txt' or 'package.json'
|
|
65
|
+
4. Create '.env.example'
|
|
66
|
+
`,
|
|
67
|
+
reference: "https://12factor.net/",
|
|
68
|
+
whenItMatters: "Onboarding new members or migrating servers."
|
|
69
|
+
},
|
|
70
|
+
'RR-LOG-001': {
|
|
71
|
+
title: "Insufficient Logging",
|
|
72
|
+
category: 'Maintenance',
|
|
73
|
+
evidence: "No logging configuration (logging, loguru) found in codebase.",
|
|
74
|
+
standard: "Python Logging Cookbook",
|
|
75
|
+
impact: "Zero visibility into runtime errors, making debugging impossible during outages.",
|
|
76
|
+
action: `
|
|
77
|
+
# Action: Python Logging Setup
|
|
78
|
+
import logging
|
|
79
|
+
logging.basicConfig(level=logging.INFO)
|
|
80
|
+
logger = logging.getLogger(__name__)
|
|
81
|
+
logger.info("Server started")
|
|
82
|
+
`,
|
|
83
|
+
reference: "https://docs.python.org/3/howto/logging-cookbook.html",
|
|
84
|
+
whenItMatters: "When a 500 error occurs in production."
|
|
85
|
+
},
|
|
86
|
+
'RR-DEP-001': {
|
|
87
|
+
title: "Structural Dependency Issue",
|
|
88
|
+
category: 'Scalability',
|
|
89
|
+
standard: "Clean Architecture: Dependency Rule",
|
|
90
|
+
impact: "High coupling between modules makes maintenance difficult and increases side effects.",
|
|
91
|
+
action: "Refactor to decouple modules or extract common logic.",
|
|
92
|
+
reference: "https://refactoring.guru/design-patterns",
|
|
93
|
+
whenItMatters: "As the codebase grows, refactoring costs explode."
|
|
94
|
+
},
|
|
95
|
+
'RR-LINT-001': {
|
|
96
|
+
title: "God Module Detected",
|
|
97
|
+
category: 'Maintenance',
|
|
98
|
+
standard: "Clean Code: Functions",
|
|
99
|
+
impact: "Single file has too many responsibilities, making changes risky.",
|
|
100
|
+
action: "Split file based on responsibilities (Separation of Concerns).",
|
|
101
|
+
reference: "https://pypi.org/project/flake8/",
|
|
102
|
+
whenItMatters: "When every feature addition causes a regression bug."
|
|
103
|
+
},
|
|
104
|
+
'DEFAULT': {
|
|
105
|
+
title: "Other Potential Risks",
|
|
106
|
+
category: 'Maintenance',
|
|
107
|
+
standard: "General Coding Best Practices",
|
|
108
|
+
impact: "Potential bugs or maintenance debt.",
|
|
109
|
+
action: "Review and consider refactoring.",
|
|
110
|
+
reference: "#",
|
|
111
|
+
whenItMatters: "When code quality starts to degrade."
|
|
112
|
+
},
|
|
113
|
+
'DISCLOSURE': "Pre-deploy audit complete. Detected risks may cause service interruptions or data loss in production.",
|
|
114
|
+
'CTA': "Automate this ritual. Install the GitHub App to manage Release Readiness continuously."
|
|
115
|
+
},
|
|
116
|
+
ko: {
|
|
117
|
+
'RR-SEC-001': {
|
|
118
|
+
title: "보안 취약점 위험 (Security Vulnerability)",
|
|
119
|
+
category: 'Security',
|
|
120
|
+
standard: "OWASP Top 10 A03:2021 – Injection",
|
|
121
|
+
impact: "외부 공격자가 시스템 권한을 탈취하거나 민감 정보를 유출할 수 있는 조건이 형성됩니다.",
|
|
122
|
+
action: `
|
|
123
|
+
# Action: 격리 및 환경변수 사용
|
|
124
|
+
subprocess.run(..., shell=False) # 권장
|
|
125
|
+
# 또는 .env 파일 사용
|
|
126
|
+
import os
|
|
127
|
+
SECRET = os.getenv('MY_SECRET')
|
|
128
|
+
`,
|
|
129
|
+
reference: "https://docs.python.org/3/library/subprocess.html#security-considerations",
|
|
130
|
+
whenItMatters: "배포 즉시 자동화된 스캐너나 공격자에 의해 탐지될 수 있습니다."
|
|
131
|
+
},
|
|
132
|
+
'RR-TEST-001': {
|
|
133
|
+
title: "자동화 테스트 부재 (Missing Automated Tests)",
|
|
134
|
+
category: 'Service Interruption',
|
|
135
|
+
evidence: "tests/ 디렉토리 또는 pytest/unittest 관련 설정을 찾을 수 없습니다.",
|
|
136
|
+
standard: "pytest Framework Documentation",
|
|
137
|
+
impact: "코드 변경 시 기존 기능이 파괴되었는지 확인할 방법이 없어, 배포 후 장애 발생 확률이 높아집니다.",
|
|
138
|
+
action: `
|
|
139
|
+
# Action: Create tests/test_smoke.py
|
|
140
|
+
def test_health_check():
|
|
141
|
+
assert True # Basic sanity check
|
|
142
|
+
`,
|
|
143
|
+
reference: "https://docs.pytest.org/",
|
|
144
|
+
whenItMatters: "팀원이 2명 이상으로 늘어나거나 배포 주기가 빨라질 때."
|
|
145
|
+
},
|
|
146
|
+
'RR-CI-001': {
|
|
147
|
+
title: "배포 자동화 파이프라인 부재 (Missing CI Pipeline)",
|
|
148
|
+
category: 'Service Interruption',
|
|
149
|
+
evidence: "GitHub Actions (.github/workflows/*.yml) 또는 CI 설정 파일이 없습니다.",
|
|
150
|
+
standard: "GitHub Actions Documentation",
|
|
151
|
+
impact: "사람의 수동 배포 과정에서 실수가 발생할 수 있으며, 일관된 배포 상태를 보장할 수 없습니다.",
|
|
152
|
+
action: `
|
|
153
|
+
# Action: Create .github/workflows/ci.yml
|
|
154
|
+
name: CI
|
|
155
|
+
on: [push]
|
|
156
|
+
jobs:
|
|
157
|
+
test:
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v3
|
|
161
|
+
- run: npm test
|
|
162
|
+
`,
|
|
163
|
+
reference: "https://docs.github.com/en/actions",
|
|
164
|
+
whenItMatters: "배포 빈도가 주 2회 이상으로 증가할 때."
|
|
165
|
+
},
|
|
166
|
+
'RR-OPS-001': {
|
|
167
|
+
title: "운영 기본 위생 체크 실패 (Project Hygiene)",
|
|
168
|
+
category: 'Service Interruption',
|
|
169
|
+
standard: "12-Factor App / Docker Documentation",
|
|
170
|
+
impact: "개발 환경과 운영 환경의 불일치로 인해 '내 컴퓨터에서는 되는데 서버에서는 안 되는' 문제가 발생합니다.",
|
|
171
|
+
action: `
|
|
172
|
+
# Checklist to Fix:
|
|
173
|
+
1. Create 'Dockerfile'
|
|
174
|
+
2. Create '.gitignore' (use gitignore.io)
|
|
175
|
+
3. Create 'requirements.txt' or 'package.json'
|
|
176
|
+
4. Create '.env.example'
|
|
177
|
+
`,
|
|
178
|
+
reference: "https://12factor.net/",
|
|
179
|
+
whenItMatters: "신규 입사자 온보딩 또는 서버 이관 시."
|
|
180
|
+
},
|
|
181
|
+
'RR-LOG-001': {
|
|
182
|
+
title: "로깅 설정 미흡 (Insufficient Logging)",
|
|
183
|
+
category: 'Maintenance',
|
|
184
|
+
evidence: "코드 내에서 로깅 설정(logging, loguru 등)이 발견되지 않았습니다.",
|
|
185
|
+
standard: "Python Logging Cookbook",
|
|
186
|
+
impact: "장애 발생 시 원인을 추적할 수 있는 데이터가 없어 해결 시간이 길어집니다.",
|
|
187
|
+
action: `
|
|
188
|
+
# Action: Python Logging Setup
|
|
189
|
+
import logging
|
|
190
|
+
logging.basicConfig(level=logging.INFO)
|
|
191
|
+
logger = logging.getLogger(__name__)
|
|
192
|
+
logger.info("Server started")
|
|
193
|
+
`,
|
|
194
|
+
reference: "https://docs.python.org/3/howto/logging-cookbook.html",
|
|
195
|
+
whenItMatters: "운영 중 알 수 없는 500 에러가 발생했을 때."
|
|
196
|
+
},
|
|
197
|
+
'RR-DEP-001': {
|
|
198
|
+
title: "구조적 의존성 결함 (Structural Dependency Issue)",
|
|
199
|
+
category: 'Scalability',
|
|
200
|
+
standard: "Clean Architecture: Dependency Rule",
|
|
201
|
+
impact: "모듈 간 결합도가 높아져 유지보수가 어려워지고, 사이드 이펙트가 발생하기 쉽습니다.",
|
|
202
|
+
action: "상호 참조하는 모듈을 분리하거나 공통 모듈로 추출하세요.",
|
|
203
|
+
reference: "https://refactoring.guru/design-patterns",
|
|
204
|
+
whenItMatters: "프로젝트 규모가 커질수록 리팩토링 비용이 기하급수적으로 증가합니다."
|
|
205
|
+
},
|
|
206
|
+
'RR-LINT-001': {
|
|
207
|
+
title: "거대 모듈 감지 (God Module)",
|
|
208
|
+
category: 'Maintenance',
|
|
209
|
+
standard: "Clean Code: Functions",
|
|
210
|
+
impact: "단일 파일의 책임이 과도하여 변경 시 영향 범위를 예측하기 어렵습니다.",
|
|
211
|
+
action: "책임에 따라 파일을 분리하세요 (Separation of Concerns).",
|
|
212
|
+
reference: "https://pypi.org/project/flake8/",
|
|
213
|
+
whenItMatters: "기능 추가 시마다 버그가 발생할 때."
|
|
214
|
+
},
|
|
215
|
+
'DEFAULT': {
|
|
216
|
+
title: "기타 잠재적 리스크 (Other Potential Risks)",
|
|
217
|
+
category: 'Maintenance',
|
|
218
|
+
standard: "General Coding Best Practices",
|
|
219
|
+
impact: "잠재적인 버그나 유지보수 어려움이 있을 수 있습니다.",
|
|
220
|
+
action: "해당 코드를 리뷰하고 리팩토링을 고려하세요.",
|
|
221
|
+
reference: "#",
|
|
222
|
+
whenItMatters: "지속적인 코드 품질 저하가 우려될 때."
|
|
223
|
+
},
|
|
224
|
+
'DISCLOSURE': "배포 전 감사가 완료되었습니다. 발견된 리스크들은 실제 운영 환경에서 예기치 못한 서비스 중단이나 데이터 손실을 야기할 수 있는 항목들입니다.",
|
|
225
|
+
'CTA': "배포 루틴 자동화를 위해 GitHub App을 설치하고 지속적인 배포 준비도(Release Readiness)를 관리하세요。"
|
|
226
|
+
}
|
|
227
|
+
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -14,9 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.LOCALES = void 0;
|
|
17
18
|
__exportStar(require("./analyzer.js"), exports);
|
|
18
19
|
__exportStar(require("./aiDiagnosis.js"), exports);
|
|
19
20
|
__exportStar(require("./archScanner.js"), exports);
|
|
20
21
|
__exportStar(require("./repoAnalyzer.js"), exports);
|
|
21
22
|
__exportStar(require("./deepAnalysis.js"), exports);
|
|
22
23
|
__exportStar(require("./jsAnalyzer.js"), exports);
|
|
24
|
+
var locales_js_1 = require("./i18n/locales.js");
|
|
25
|
+
Object.defineProperty(exports, "LOCALES", { enumerable: true, get: function () { return locales_js_1.LOCALES; } });
|
package/dist/repoAnalyzer.d.ts
CHANGED
|
@@ -25,16 +25,7 @@ export interface RepoAnalysisResult {
|
|
|
25
25
|
disclosure?: string;
|
|
26
26
|
cta?: string;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
* CEO-ready "Business Translation" for technical risks
|
|
30
|
-
*/
|
|
31
|
-
/**
|
|
32
|
-
* 3-Tier Business Audit Translation
|
|
33
|
-
*/
|
|
34
|
-
/**
|
|
35
|
-
* 3-Tier Business Audit Translation with Standard IDs
|
|
36
|
-
*/
|
|
37
|
-
export declare function getAuditDetails(id: string, type: string, issue: string): {
|
|
28
|
+
export declare function getAuditDetails(id: string, type: string, issue: string, lang?: 'en' | 'ko'): {
|
|
38
29
|
id: string;
|
|
39
30
|
title: string;
|
|
40
31
|
category: RepoAnalysisResult['findings'][0]['category'];
|
|
@@ -45,4 +36,7 @@ export declare function getAuditDetails(id: string, type: string, issue: string)
|
|
|
45
36
|
reference: string;
|
|
46
37
|
whenItMatters: string;
|
|
47
38
|
};
|
|
48
|
-
export declare function analyzeRepository(repoPath: string,
|
|
39
|
+
export declare function analyzeRepository(repoPath: string, options?: {
|
|
40
|
+
lang?: 'en' | 'ko';
|
|
41
|
+
resultsDir?: string;
|
|
42
|
+
}): Promise<RepoAnalysisResult>;
|
package/dist/repoAnalyzer.js
CHANGED
|
@@ -50,144 +50,38 @@ const archScanner_js_1 = require("./archScanner.js");
|
|
|
50
50
|
/**
|
|
51
51
|
* 3-Tier Business Audit Translation with Standard IDs
|
|
52
52
|
*/
|
|
53
|
-
|
|
53
|
+
const locales_js_1 = require("./i18n/locales.js");
|
|
54
|
+
function getAuditDetails(id, type, issue, lang = 'en') {
|
|
54
55
|
const commonFields = { id };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
category: 'Security',
|
|
60
|
-
evidence: issue,
|
|
61
|
-
standard: "OWASP Top 10 A03:2021 – Injection",
|
|
62
|
-
impact: "외부 공격자가 시스템 권한을 탈취하거나 민감 정보를 유출할 수 있는 조건이 형성됩니다.",
|
|
63
|
-
action: `
|
|
64
|
-
# Action: 격리 및 환경변수 사용
|
|
65
|
-
subprocess.run(..., shell=False) # 권장
|
|
66
|
-
# 또는 .env 파일 사용
|
|
67
|
-
import os
|
|
68
|
-
SECRET = os.getenv('MY_SECRET')
|
|
69
|
-
`,
|
|
70
|
-
reference: "https://docs.python.org/3/library/subprocess.html#security-considerations",
|
|
71
|
-
whenItMatters: "배포 즉시 자동화된 스캐너나 공격자에 의해 탐지될 수 있습니다."
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
if (id === 'RR-TEST-001') {
|
|
75
|
-
return {
|
|
76
|
-
...commonFields,
|
|
77
|
-
title: "자동화 테스트 부재 (Missing Automated Tests)",
|
|
78
|
-
category: 'Service Interruption',
|
|
79
|
-
evidence: "tests/ 디렉토리 또는 pytest/unittest 관련 설정을 찾을 수 없습니다.",
|
|
80
|
-
standard: "pytest Framework Documentation",
|
|
81
|
-
impact: "코드 변경 시 기존 기능이 파괴되었는지 확인할 방법이 없어, 배포 후 장애 발생 확률이 높아집니다.",
|
|
82
|
-
action: `
|
|
83
|
-
# Action: Create tests/test_smoke.py
|
|
84
|
-
def test_health_check():
|
|
85
|
-
assert True # Basic sanity check
|
|
86
|
-
`,
|
|
87
|
-
reference: "https://docs.pytest.org/",
|
|
88
|
-
whenItMatters: "팀원이 2명 이상으로 늘어나거나 배포 주기가 빨라질 때."
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
if (id === 'RR-CI-001') {
|
|
92
|
-
return {
|
|
93
|
-
...commonFields,
|
|
94
|
-
title: "배포 자동화 파이프라인 부재 (Missing CI Pipeline)",
|
|
95
|
-
category: 'Service Interruption',
|
|
96
|
-
evidence: "GitHub Actions (.github/workflows/*.yml) 또는 CI 설정 파일이 없습니다.",
|
|
97
|
-
standard: "GitHub Actions Documentation",
|
|
98
|
-
impact: "사람의 수동 배포 과정에서 실수가 발생할 수 있으며, 일관된 배포 상태를 보장할 수 없습니다.",
|
|
99
|
-
action: `
|
|
100
|
-
# Action: Create .github/workflows/ci.yml
|
|
101
|
-
name: CI
|
|
102
|
-
on: [push]
|
|
103
|
-
jobs:
|
|
104
|
-
test:
|
|
105
|
-
runs-on: ubuntu-latest
|
|
106
|
-
steps:
|
|
107
|
-
- uses: actions/checkout@v3
|
|
108
|
-
- run: npm test
|
|
109
|
-
`,
|
|
110
|
-
reference: "https://docs.github.com/en/actions",
|
|
111
|
-
whenItMatters: "배포 빈도가 주 2회 이상으로 증가할 때."
|
|
112
|
-
};
|
|
56
|
+
const messages = locales_js_1.LOCALES[lang] || locales_js_1.LOCALES['en'];
|
|
57
|
+
let details = messages['DEFAULT'];
|
|
58
|
+
if (messages[id]) {
|
|
59
|
+
details = messages[id];
|
|
113
60
|
}
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
...commonFields,
|
|
117
|
-
title: "운영 기본 위생 체크 실패 (Project Hygiene)",
|
|
118
|
-
category: 'Service Interruption',
|
|
119
|
-
evidence: issue, // Consolidated list will be passed here
|
|
120
|
-
standard: "12-Factor App / Docker Documentation",
|
|
121
|
-
impact: "개발 환경과 운영 환경의 불일치로 인해 '내 컴퓨터에서는 되는데 서버에서는 안 되는' 문제가 발생합니다.",
|
|
122
|
-
action: `
|
|
123
|
-
# Checklist to Fix:
|
|
124
|
-
1. Create 'Dockerfile'
|
|
125
|
-
2. Create '.gitignore' (use gitignore.io)
|
|
126
|
-
3. Create 'requirements.txt' or 'package.json'
|
|
127
|
-
4. Create '.env.example'
|
|
128
|
-
`,
|
|
129
|
-
reference: "https://12factor.net/",
|
|
130
|
-
whenItMatters: "신규 입사자 온보딩 또는 서버 이관 시."
|
|
131
|
-
};
|
|
61
|
+
else if (type === 'SecurityRisk') { // Fallback for general security type if ID not matched
|
|
62
|
+
details = messages['RR-SEC-001'];
|
|
132
63
|
}
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
...commonFields,
|
|
136
|
-
title: "로깅 설정 미흡 (Insufficient Logging)",
|
|
137
|
-
category: 'Maintenance',
|
|
138
|
-
evidence: "코드 내에서 로깅 설정(logging, loguru 등)이 발견되지 않았습니다.",
|
|
139
|
-
standard: "Python Logging Cookbook",
|
|
140
|
-
impact: "장애 발생 시 원인을 추적할 수 있는 데이터가 없어 해결 시간이 길어집니다.",
|
|
141
|
-
action: `
|
|
142
|
-
# Action: Python Logging Setup
|
|
143
|
-
import logging
|
|
144
|
-
logging.basicConfig(level=logging.INFO)
|
|
145
|
-
logger = logging.getLogger(__name__)
|
|
146
|
-
logger.info("Server started")
|
|
147
|
-
`,
|
|
148
|
-
reference: "https://docs.python.org/3/howto/logging-cookbook.html",
|
|
149
|
-
whenItMatters: "운영 중 알 수 없는 500 에러가 발생했을 때."
|
|
150
|
-
};
|
|
64
|
+
else if (type === 'CircularDependency') {
|
|
65
|
+
details = messages['RR-DEP-001'];
|
|
151
66
|
}
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
...commonFields,
|
|
155
|
-
title: "구조적 의존성 결함 (Structural Dependency Issue)",
|
|
156
|
-
category: 'Scalability',
|
|
157
|
-
evidence: issue,
|
|
158
|
-
standard: "Clean Architecture: Dependency Rule",
|
|
159
|
-
impact: "모듈 간 결합도가 높아져 유지보수가 어려워지고, 사이드 이펙트가 발생하기 쉽습니다.",
|
|
160
|
-
action: "상호 참조하는 모듈을 분리하거나 공통 모듈로 추출하세요.",
|
|
161
|
-
reference: "https://refactoring.guru/design-patterns",
|
|
162
|
-
whenItMatters: "프로젝트 규모가 커질수록 리팩토링 비용이 기하급수적으로 증가합니다."
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
if (id === 'RR-LINT-001' || type === 'GodModule') {
|
|
166
|
-
return {
|
|
167
|
-
...commonFields,
|
|
168
|
-
title: "거대 모듈 감지 (God Module)",
|
|
169
|
-
category: 'Maintenance',
|
|
170
|
-
evidence: issue,
|
|
171
|
-
standard: "Clean Code: Functions",
|
|
172
|
-
impact: "단일 파일의 책임이 과도하여 변경 시 영향 범위를 예측하기 어렵습니다.",
|
|
173
|
-
action: "책임에 따라 파일을 분리하세요 (Separation of Concerns).",
|
|
174
|
-
reference: "https://pypi.org/project/flake8/",
|
|
175
|
-
whenItMatters: "기능 추가 시마다 버그가 발생할 때."
|
|
176
|
-
};
|
|
67
|
+
else if (type === 'GodModule') {
|
|
68
|
+
details = messages['RR-LINT-001'];
|
|
177
69
|
}
|
|
178
70
|
return {
|
|
179
71
|
...commonFields,
|
|
180
|
-
title:
|
|
181
|
-
category:
|
|
182
|
-
evidence: issue,
|
|
183
|
-
standard:
|
|
184
|
-
impact:
|
|
185
|
-
action:
|
|
186
|
-
reference:
|
|
187
|
-
whenItMatters:
|
|
72
|
+
title: details.title,
|
|
73
|
+
category: details.category,
|
|
74
|
+
evidence: details.evidence || issue, // Use predefined evidence if exists, else dynamic issue
|
|
75
|
+
standard: details.standard,
|
|
76
|
+
impact: details.impact,
|
|
77
|
+
action: details.action,
|
|
78
|
+
reference: details.reference,
|
|
79
|
+
whenItMatters: details.whenItMatters
|
|
188
80
|
};
|
|
189
81
|
}
|
|
190
|
-
async function analyzeRepository(repoPath,
|
|
82
|
+
async function analyzeRepository(repoPath, options) {
|
|
83
|
+
const lang = options?.lang || 'en';
|
|
84
|
+
const resultsDir = options?.resultsDir;
|
|
191
85
|
const files = [];
|
|
192
86
|
const IGNORE_DIRS = ["node_modules", ".git", "dist", "build", "venv", ".venv", "__pycache__"];
|
|
193
87
|
function walk(dir) {
|
|
@@ -221,7 +115,7 @@ async function analyzeRepository(repoPath, resultsDir) {
|
|
|
221
115
|
const hasTests = fs.readdirSync(repoPath, { withFileTypes: true })
|
|
222
116
|
.some(d => d.isDirectory() && /tests?|spec/i.test(d.name));
|
|
223
117
|
if (!hasTests) {
|
|
224
|
-
const details = getAuditDetails('RR-TEST-001', 'ProductionRisk', '');
|
|
118
|
+
const details = getAuditDetails('RR-TEST-001', 'ProductionRisk', '', lang);
|
|
225
119
|
findings.push({
|
|
226
120
|
file: 'Repository Root',
|
|
227
121
|
line: 0,
|
|
@@ -234,7 +128,7 @@ async function analyzeRepository(repoPath, resultsDir) {
|
|
|
234
128
|
const hasCI = fs.readdirSync(repoPath, { withFileTypes: true })
|
|
235
129
|
.some(d => d.isDirectory() && /(\.github|\.circleci|jenkins|gitlab)/i.test(d.name));
|
|
236
130
|
if (!hasCI) {
|
|
237
|
-
const details = getAuditDetails('RR-CI-001', 'ProductionRisk', '');
|
|
131
|
+
const details = getAuditDetails('RR-CI-001', 'ProductionRisk', '', lang);
|
|
238
132
|
findings.push({
|
|
239
133
|
file: 'Repository Root',
|
|
240
134
|
line: 0,
|
|
@@ -263,7 +157,7 @@ async function analyzeRepository(repoPath, resultsDir) {
|
|
|
263
157
|
}
|
|
264
158
|
if (hygieneMissing.length > 0) {
|
|
265
159
|
const evidenceBlock = "\n" + hygieneMissing.join("\n");
|
|
266
|
-
const details = getAuditDetails('RR-OPS-001', 'ProductionRisk', evidenceBlock);
|
|
160
|
+
const details = getAuditDetails('RR-OPS-001', 'ProductionRisk', evidenceBlock, lang);
|
|
267
161
|
findings.push({
|
|
268
162
|
file: 'Repository Root',
|
|
269
163
|
line: 0,
|
|
@@ -285,13 +179,13 @@ async function analyzeRepository(repoPath, resultsDir) {
|
|
|
285
179
|
}
|
|
286
180
|
// God Module Check (>500 lines)
|
|
287
181
|
if (lines.length > 500) {
|
|
288
|
-
const details = getAuditDetails('RR-LINT-001', 'GodModule', `File length: ${lines.length} lines
|
|
182
|
+
const details = getAuditDetails('RR-LINT-001', 'GodModule', `File length: ${lines.length} lines`, lang);
|
|
289
183
|
findings.push({ file: relativePath, line: 0, type: 'ProductionRisk', ...details });
|
|
290
184
|
operationalGapCount++;
|
|
291
185
|
}
|
|
292
186
|
const result = await (0, analyzer_js_1.analyzePythonCode)(code, relativePath);
|
|
293
187
|
if (result.hasError) {
|
|
294
|
-
const details = getAuditDetails('RR-SEC-001', result.type || 'Error', result.error || 'Unknown Issue');
|
|
188
|
+
const details = getAuditDetails('RR-SEC-001', result.type || 'Error', result.error || 'Unknown Issue', lang);
|
|
295
189
|
findings.push({
|
|
296
190
|
file: relativePath,
|
|
297
191
|
line: result.line || 0,
|
|
@@ -315,7 +209,7 @@ async function analyzeRepository(repoPath, resultsDir) {
|
|
|
315
209
|
// We rely on rules for now.
|
|
316
210
|
const result = await (0, jsAnalyzer_js_1.analyzeJsTsCode)(code, relativePath);
|
|
317
211
|
if (result.hasError) {
|
|
318
|
-
const details = getAuditDetails('RR-SEC-002', result.type || 'Error', result.error || 'Unknown Issue'); // Use SEC-002 for JS? Or reuse.
|
|
212
|
+
const details = getAuditDetails('RR-SEC-002', result.type || 'Error', result.error || 'Unknown Issue', lang); // Use SEC-002 for JS? Or reuse.
|
|
319
213
|
findings.push({
|
|
320
214
|
file: relativePath,
|
|
321
215
|
line: result.line || 0,
|
|
@@ -331,14 +225,14 @@ async function analyzeRepository(repoPath, resultsDir) {
|
|
|
331
225
|
}
|
|
332
226
|
}
|
|
333
227
|
if (!hasLogging && files.filter(f => f.endsWith('.py')).length > 0) {
|
|
334
|
-
const details = getAuditDetails('RR-LOG-001', 'ProductionRisk', '');
|
|
228
|
+
const details = getAuditDetails('RR-LOG-001', 'ProductionRisk', '', lang);
|
|
335
229
|
findings.push({ file: 'Repository Root', line: 0, type: 'ProductionRisk', ...details });
|
|
336
230
|
operationalGapCount++;
|
|
337
231
|
}
|
|
338
232
|
// 3. Dependency Scan (Scalability -15)
|
|
339
233
|
const { adj, cycles } = (0, archScanner_js_1.depsScan)(repoPath);
|
|
340
234
|
for (const cycle of cycles) {
|
|
341
|
-
const details = getAuditDetails('RR-DEP-001', 'CircularDependency', `Cycle: ${cycle.join(' -> ')}
|
|
235
|
+
const details = getAuditDetails('RR-DEP-001', 'CircularDependency', `Cycle: ${cycle.join(' -> ')}`, lang);
|
|
342
236
|
findings.push({
|
|
343
237
|
file: cycle[0],
|
|
344
238
|
line: 0,
|
|
@@ -391,7 +285,7 @@ async function analyzeRepository(repoPath, resultsDir) {
|
|
|
391
285
|
operationalGaps: operationalGapCount
|
|
392
286
|
},
|
|
393
287
|
graphUrl,
|
|
394
|
-
disclosure:
|
|
395
|
-
cta:
|
|
288
|
+
disclosure: locales_js_1.LOCALES[lang]?.DISCLOSURE || locales_js_1.LOCALES['en'].DISCLOSURE,
|
|
289
|
+
cta: locales_js_1.LOCALES[lang]?.CTA || locales_js_1.LOCALES['en'].CTA
|
|
396
290
|
};
|
|
397
291
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
|
|
2
|
+
export const LOCALES = {
|
|
3
|
+
en: {
|
|
4
|
+
'RR-SEC-001': {
|
|
5
|
+
title: "Security Vulnerability Detected",
|
|
6
|
+
category: 'Security',
|
|
7
|
+
standard: "OWASP Top 10 A03:2021 – Injection",
|
|
8
|
+
impact: "Potential for unauthorized access or data leakage through injection attacks.",
|
|
9
|
+
action: `
|
|
10
|
+
# Action: Isolate and use Environment Variables
|
|
11
|
+
subprocess.run(..., shell=False) # Recommended
|
|
12
|
+
# Or use .env file
|
|
13
|
+
import os
|
|
14
|
+
SECRET = os.getenv('MY_SECRET')
|
|
15
|
+
`,
|
|
16
|
+
reference: "https://docs.python.org/3/library/subprocess.html#security-considerations",
|
|
17
|
+
whenItMatters: "Immediately upon deployment, as scanners can detect this."
|
|
18
|
+
},
|
|
19
|
+
'RR-TEST-001': {
|
|
20
|
+
title: "Missing Automated Tests",
|
|
21
|
+
category: 'Service Interruption',
|
|
22
|
+
evidence: "No tests/ directory or pytest/unittest configuration found.",
|
|
23
|
+
standard: "pytest Framework Documentation",
|
|
24
|
+
impact: "Unable to verify if validation logic breaks existing features. High risk of regression.",
|
|
25
|
+
action: `
|
|
26
|
+
# Action: Create tests/test_smoke.py
|
|
27
|
+
def test_health_check():
|
|
28
|
+
assert True # Basic sanity check
|
|
29
|
+
`,
|
|
30
|
+
reference: "https://docs.pytest.org/",
|
|
31
|
+
whenItMatters: "When team size > 2 or deployment frequency increases."
|
|
32
|
+
},
|
|
33
|
+
'RR-CI-001': {
|
|
34
|
+
title: "Missing CI Pipeline",
|
|
35
|
+
category: 'Service Interruption',
|
|
36
|
+
evidence: "No GitHub Actions (.github/workflows/*.yml) or CI configuration found.",
|
|
37
|
+
standard: "GitHub Actions Documentation",
|
|
38
|
+
impact: "Manual deployments are prone to human error and lack consistency.",
|
|
39
|
+
action: `
|
|
40
|
+
# Action: Create .github/workflows/ci.yml
|
|
41
|
+
name: CI
|
|
42
|
+
on: [push]
|
|
43
|
+
jobs:
|
|
44
|
+
test:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v3
|
|
48
|
+
- run: npm test
|
|
49
|
+
`,
|
|
50
|
+
reference: "https://docs.github.com/en/actions",
|
|
51
|
+
whenItMatters: "When deploying more than twice a week."
|
|
52
|
+
},
|
|
53
|
+
'RR-OPS-001': {
|
|
54
|
+
title: "Project Hygiene Check Failed",
|
|
55
|
+
category: 'Service Interruption',
|
|
56
|
+
standard: "12-Factor App / Docker Documentation",
|
|
57
|
+
impact: "Inconsistency between dev and prod environments (\"It works on my machine\").",
|
|
58
|
+
action: `
|
|
59
|
+
# Checklist to Fix:
|
|
60
|
+
1. Create 'Dockerfile'
|
|
61
|
+
2. Create '.gitignore' (use gitignore.io)
|
|
62
|
+
3. Create 'requirements.txt' or 'package.json'
|
|
63
|
+
4. Create '.env.example'
|
|
64
|
+
`,
|
|
65
|
+
reference: "https://12factor.net/",
|
|
66
|
+
whenItMatters: "Onboarding new members or migrating servers."
|
|
67
|
+
},
|
|
68
|
+
'RR-LOG-001': {
|
|
69
|
+
title: "Insufficient Logging",
|
|
70
|
+
category: 'Maintenance',
|
|
71
|
+
evidence: "No logging configuration (logging, loguru) found in codebase.",
|
|
72
|
+
standard: "Python Logging Cookbook",
|
|
73
|
+
impact: "Zero visibility into runtime errors, making debugging impossible during outages.",
|
|
74
|
+
action: `
|
|
75
|
+
# Action: Python Logging Setup
|
|
76
|
+
import logging
|
|
77
|
+
logging.basicConfig(level=logging.INFO)
|
|
78
|
+
logger = logging.getLogger(__name__)
|
|
79
|
+
logger.info("Server started")
|
|
80
|
+
`,
|
|
81
|
+
reference: "https://docs.python.org/3/howto/logging-cookbook.html",
|
|
82
|
+
whenItMatters: "When a 500 error occurs in production."
|
|
83
|
+
},
|
|
84
|
+
'RR-DEP-001': {
|
|
85
|
+
title: "Structural Dependency Issue",
|
|
86
|
+
category: 'Scalability',
|
|
87
|
+
standard: "Clean Architecture: Dependency Rule",
|
|
88
|
+
impact: "High coupling between modules makes maintenance difficult and increases side effects.",
|
|
89
|
+
action: "Refactor to decouple modules or extract common logic.",
|
|
90
|
+
reference: "https://refactoring.guru/design-patterns",
|
|
91
|
+
whenItMatters: "As the codebase grows, refactoring costs explode."
|
|
92
|
+
},
|
|
93
|
+
'RR-LINT-001': {
|
|
94
|
+
title: "God Module Detected",
|
|
95
|
+
category: 'Maintenance',
|
|
96
|
+
standard: "Clean Code: Functions",
|
|
97
|
+
impact: "Single file has too many responsibilities, making changes risky.",
|
|
98
|
+
action: "Split file based on responsibilities (Separation of Concerns).",
|
|
99
|
+
reference: "https://pypi.org/project/flake8/",
|
|
100
|
+
whenItMatters: "When every feature addition causes a regression bug."
|
|
101
|
+
},
|
|
102
|
+
'DEFAULT': {
|
|
103
|
+
title: "Other Potential Risks",
|
|
104
|
+
category: 'Maintenance',
|
|
105
|
+
standard: "General Coding Best Practices",
|
|
106
|
+
impact: "Potential bugs or maintenance debt.",
|
|
107
|
+
action: "Review and consider refactoring.",
|
|
108
|
+
reference: "#",
|
|
109
|
+
whenItMatters: "When code quality starts to degrade."
|
|
110
|
+
},
|
|
111
|
+
'DISCLOSURE': "Pre-deploy audit complete. Detected risks may cause service interruptions or data loss in production.",
|
|
112
|
+
'CTA': "Automate this ritual. Install the GitHub App to manage Release Readiness continuously."
|
|
113
|
+
},
|
|
114
|
+
ko: {
|
|
115
|
+
'RR-SEC-001': {
|
|
116
|
+
title: "보안 취약점 위험 (Security Vulnerability)",
|
|
117
|
+
category: 'Security',
|
|
118
|
+
standard: "OWASP Top 10 A03:2021 – Injection",
|
|
119
|
+
impact: "외부 공격자가 시스템 권한을 탈취하거나 민감 정보를 유출할 수 있는 조건이 형성됩니다.",
|
|
120
|
+
action: `
|
|
121
|
+
# Action: 격리 및 환경변수 사용
|
|
122
|
+
subprocess.run(..., shell=False) # 권장
|
|
123
|
+
# 또는 .env 파일 사용
|
|
124
|
+
import os
|
|
125
|
+
SECRET = os.getenv('MY_SECRET')
|
|
126
|
+
`,
|
|
127
|
+
reference: "https://docs.python.org/3/library/subprocess.html#security-considerations",
|
|
128
|
+
whenItMatters: "배포 즉시 자동화된 스캐너나 공격자에 의해 탐지될 수 있습니다."
|
|
129
|
+
},
|
|
130
|
+
'RR-TEST-001': {
|
|
131
|
+
title: "자동화 테스트 부재 (Missing Automated Tests)",
|
|
132
|
+
category: 'Service Interruption',
|
|
133
|
+
evidence: "tests/ 디렉토리 또는 pytest/unittest 관련 설정을 찾을 수 없습니다.",
|
|
134
|
+
standard: "pytest Framework Documentation",
|
|
135
|
+
impact: "코드 변경 시 기존 기능이 파괴되었는지 확인할 방법이 없어, 배포 후 장애 발생 확률이 높아집니다.",
|
|
136
|
+
action: `
|
|
137
|
+
# Action: Create tests/test_smoke.py
|
|
138
|
+
def test_health_check():
|
|
139
|
+
assert True # Basic sanity check
|
|
140
|
+
`,
|
|
141
|
+
reference: "https://docs.pytest.org/",
|
|
142
|
+
whenItMatters: "팀원이 2명 이상으로 늘어나거나 배포 주기가 빨라질 때."
|
|
143
|
+
},
|
|
144
|
+
'RR-CI-001': {
|
|
145
|
+
title: "배포 자동화 파이프라인 부재 (Missing CI Pipeline)",
|
|
146
|
+
category: 'Service Interruption',
|
|
147
|
+
evidence: "GitHub Actions (.github/workflows/*.yml) 또는 CI 설정 파일이 없습니다.",
|
|
148
|
+
standard: "GitHub Actions Documentation",
|
|
149
|
+
impact: "사람의 수동 배포 과정에서 실수가 발생할 수 있으며, 일관된 배포 상태를 보장할 수 없습니다.",
|
|
150
|
+
action: `
|
|
151
|
+
# Action: Create .github/workflows/ci.yml
|
|
152
|
+
name: CI
|
|
153
|
+
on: [push]
|
|
154
|
+
jobs:
|
|
155
|
+
test:
|
|
156
|
+
runs-on: ubuntu-latest
|
|
157
|
+
steps:
|
|
158
|
+
- uses: actions/checkout@v3
|
|
159
|
+
- run: npm test
|
|
160
|
+
`,
|
|
161
|
+
reference: "https://docs.github.com/en/actions",
|
|
162
|
+
whenItMatters: "배포 빈도가 주 2회 이상으로 증가할 때."
|
|
163
|
+
},
|
|
164
|
+
'RR-OPS-001': {
|
|
165
|
+
title: "운영 기본 위생 체크 실패 (Project Hygiene)",
|
|
166
|
+
category: 'Service Interruption',
|
|
167
|
+
standard: "12-Factor App / Docker Documentation",
|
|
168
|
+
impact: "개발 환경과 운영 환경의 불일치로 인해 '내 컴퓨터에서는 되는데 서버에서는 안 되는' 문제가 발생합니다.",
|
|
169
|
+
action: `
|
|
170
|
+
# Checklist to Fix:
|
|
171
|
+
1. Create 'Dockerfile'
|
|
172
|
+
2. Create '.gitignore' (use gitignore.io)
|
|
173
|
+
3. Create 'requirements.txt' or 'package.json'
|
|
174
|
+
4. Create '.env.example'
|
|
175
|
+
`,
|
|
176
|
+
reference: "https://12factor.net/",
|
|
177
|
+
whenItMatters: "신규 입사자 온보딩 또는 서버 이관 시."
|
|
178
|
+
},
|
|
179
|
+
'RR-LOG-001': {
|
|
180
|
+
title: "로깅 설정 미흡 (Insufficient Logging)",
|
|
181
|
+
category: 'Maintenance',
|
|
182
|
+
evidence: "코드 내에서 로깅 설정(logging, loguru 등)이 발견되지 않았습니다.",
|
|
183
|
+
standard: "Python Logging Cookbook",
|
|
184
|
+
impact: "장애 발생 시 원인을 추적할 수 있는 데이터가 없어 해결 시간이 길어집니다.",
|
|
185
|
+
action: `
|
|
186
|
+
# Action: Python Logging Setup
|
|
187
|
+
import logging
|
|
188
|
+
logging.basicConfig(level=logging.INFO)
|
|
189
|
+
logger = logging.getLogger(__name__)
|
|
190
|
+
logger.info("Server started")
|
|
191
|
+
`,
|
|
192
|
+
reference: "https://docs.python.org/3/howto/logging-cookbook.html",
|
|
193
|
+
whenItMatters: "운영 중 알 수 없는 500 에러가 발생했을 때."
|
|
194
|
+
},
|
|
195
|
+
'RR-DEP-001': {
|
|
196
|
+
title: "구조적 의존성 결함 (Structural Dependency Issue)",
|
|
197
|
+
category: 'Scalability',
|
|
198
|
+
standard: "Clean Architecture: Dependency Rule",
|
|
199
|
+
impact: "모듈 간 결합도가 높아져 유지보수가 어려워지고, 사이드 이펙트가 발생하기 쉽습니다.",
|
|
200
|
+
action: "상호 참조하는 모듈을 분리하거나 공통 모듈로 추출하세요.",
|
|
201
|
+
reference: "https://refactoring.guru/design-patterns",
|
|
202
|
+
whenItMatters: "프로젝트 규모가 커질수록 리팩토링 비용이 기하급수적으로 증가합니다."
|
|
203
|
+
},
|
|
204
|
+
'RR-LINT-001': {
|
|
205
|
+
title: "거대 모듈 감지 (God Module)",
|
|
206
|
+
category: 'Maintenance',
|
|
207
|
+
standard: "Clean Code: Functions",
|
|
208
|
+
impact: "단일 파일의 책임이 과도하여 변경 시 영향 범위를 예측하기 어렵습니다.",
|
|
209
|
+
action: "책임에 따라 파일을 분리하세요 (Separation of Concerns).",
|
|
210
|
+
reference: "https://pypi.org/project/flake8/",
|
|
211
|
+
whenItMatters: "기능 추가 시마다 버그가 발생할 때."
|
|
212
|
+
},
|
|
213
|
+
'DEFAULT': {
|
|
214
|
+
title: "기타 잠재적 리스크 (Other Potential Risks)",
|
|
215
|
+
category: 'Maintenance',
|
|
216
|
+
standard: "General Coding Best Practices",
|
|
217
|
+
impact: "잠재적인 버그나 유지보수 어려움이 있을 수 있습니다.",
|
|
218
|
+
action: "해당 코드를 리뷰하고 리팩토링을 고려하세요.",
|
|
219
|
+
reference: "#",
|
|
220
|
+
whenItMatters: "지속적인 코드 품질 저하가 우려될 때."
|
|
221
|
+
},
|
|
222
|
+
'DISCLOSURE': "배포 전 감사가 완료되었습니다. 발견된 리스크들은 실제 운영 환경에서 예기치 못한 서비스 중단이나 데이터 손실을 야기할 수 있는 항목들입니다.",
|
|
223
|
+
'CTA': "배포 루틴 자동화를 위해 GitHub App을 설치하고 지속적인 배포 준비도(Release Readiness)를 관리하세요。"
|
|
224
|
+
}
|
|
225
|
+
};
|
package/src/index.ts
CHANGED
package/src/repoAnalyzer.ts
CHANGED
|
@@ -42,153 +42,39 @@ export interface RepoAnalysisResult {
|
|
|
42
42
|
/**
|
|
43
43
|
* 3-Tier Business Audit Translation with Standard IDs
|
|
44
44
|
*/
|
|
45
|
-
|
|
46
|
-
const commonFields = { id };
|
|
47
|
-
|
|
48
|
-
if (id === 'RR-SEC-001' || type === 'SecurityRisk') {
|
|
49
|
-
return {
|
|
50
|
-
...commonFields,
|
|
51
|
-
title: "보안 취약점 위험 (Security Vulnerability)",
|
|
52
|
-
category: 'Security',
|
|
53
|
-
evidence: issue,
|
|
54
|
-
standard: "OWASP Top 10 A03:2021 – Injection",
|
|
55
|
-
impact: "외부 공격자가 시스템 권한을 탈취하거나 민감 정보를 유출할 수 있는 조건이 형성됩니다.",
|
|
56
|
-
action: `
|
|
57
|
-
# Action: 격리 및 환경변수 사용
|
|
58
|
-
subprocess.run(..., shell=False) # 권장
|
|
59
|
-
# 또는 .env 파일 사용
|
|
60
|
-
import os
|
|
61
|
-
SECRET = os.getenv('MY_SECRET')
|
|
62
|
-
`,
|
|
63
|
-
reference: "https://docs.python.org/3/library/subprocess.html#security-considerations",
|
|
64
|
-
whenItMatters: "배포 즉시 자동화된 스캐너나 공격자에 의해 탐지될 수 있습니다."
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (id === 'RR-TEST-001') {
|
|
69
|
-
return {
|
|
70
|
-
...commonFields,
|
|
71
|
-
title: "자동화 테스트 부재 (Missing Automated Tests)",
|
|
72
|
-
category: 'Service Interruption',
|
|
73
|
-
evidence: "tests/ 디렉토리 또는 pytest/unittest 관련 설정을 찾을 수 없습니다.",
|
|
74
|
-
standard: "pytest Framework Documentation",
|
|
75
|
-
impact: "코드 변경 시 기존 기능이 파괴되었는지 확인할 방법이 없어, 배포 후 장애 발생 확률이 높아집니다.",
|
|
76
|
-
action: `
|
|
77
|
-
# Action: Create tests/test_smoke.py
|
|
78
|
-
def test_health_check():
|
|
79
|
-
assert True # Basic sanity check
|
|
80
|
-
`,
|
|
81
|
-
reference: "https://docs.pytest.org/",
|
|
82
|
-
whenItMatters: "팀원이 2명 이상으로 늘어나거나 배포 주기가 빨라질 때."
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (id === 'RR-CI-001') {
|
|
87
|
-
return {
|
|
88
|
-
...commonFields,
|
|
89
|
-
title: "배포 자동화 파이프라인 부재 (Missing CI Pipeline)",
|
|
90
|
-
category: 'Service Interruption',
|
|
91
|
-
evidence: "GitHub Actions (.github/workflows/*.yml) 또는 CI 설정 파일이 없습니다.",
|
|
92
|
-
standard: "GitHub Actions Documentation",
|
|
93
|
-
impact: "사람의 수동 배포 과정에서 실수가 발생할 수 있으며, 일관된 배포 상태를 보장할 수 없습니다.",
|
|
94
|
-
action: `
|
|
95
|
-
# Action: Create .github/workflows/ci.yml
|
|
96
|
-
name: CI
|
|
97
|
-
on: [push]
|
|
98
|
-
jobs:
|
|
99
|
-
test:
|
|
100
|
-
runs-on: ubuntu-latest
|
|
101
|
-
steps:
|
|
102
|
-
- uses: actions/checkout@v3
|
|
103
|
-
- run: npm test
|
|
104
|
-
`,
|
|
105
|
-
reference: "https://docs.github.com/en/actions",
|
|
106
|
-
whenItMatters: "배포 빈도가 주 2회 이상으로 증가할 때."
|
|
107
|
-
};
|
|
108
|
-
}
|
|
45
|
+
import { LOCALES } from './i18n/locales.js';
|
|
109
46
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
4. Create '.env.example'
|
|
124
|
-
`,
|
|
125
|
-
reference: "https://12factor.net/",
|
|
126
|
-
whenItMatters: "신규 입사자 온보딩 또는 서버 이관 시."
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (id === 'RR-LOG-001') {
|
|
131
|
-
return {
|
|
132
|
-
...commonFields,
|
|
133
|
-
title: "로깅 설정 미흡 (Insufficient Logging)",
|
|
134
|
-
category: 'Maintenance',
|
|
135
|
-
evidence: "코드 내에서 로깅 설정(logging, loguru 등)이 발견되지 않았습니다.",
|
|
136
|
-
standard: "Python Logging Cookbook",
|
|
137
|
-
impact: "장애 발생 시 원인을 추적할 수 있는 데이터가 없어 해결 시간이 길어집니다.",
|
|
138
|
-
action: `
|
|
139
|
-
# Action: Python Logging Setup
|
|
140
|
-
import logging
|
|
141
|
-
logging.basicConfig(level=logging.INFO)
|
|
142
|
-
logger = logging.getLogger(__name__)
|
|
143
|
-
logger.info("Server started")
|
|
144
|
-
`,
|
|
145
|
-
reference: "https://docs.python.org/3/howto/logging-cookbook.html",
|
|
146
|
-
whenItMatters: "운영 중 알 수 없는 500 에러가 발생했을 때."
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (id === 'RR-DEP-001' || type === 'CircularDependency') {
|
|
151
|
-
return {
|
|
152
|
-
...commonFields,
|
|
153
|
-
title: "구조적 의존성 결함 (Structural Dependency Issue)",
|
|
154
|
-
category: 'Scalability',
|
|
155
|
-
evidence: issue,
|
|
156
|
-
standard: "Clean Architecture: Dependency Rule",
|
|
157
|
-
impact: "모듈 간 결합도가 높아져 유지보수가 어려워지고, 사이드 이펙트가 발생하기 쉽습니다.",
|
|
158
|
-
action: "상호 참조하는 모듈을 분리하거나 공통 모듈로 추출하세요.",
|
|
159
|
-
reference: "https://refactoring.guru/design-patterns",
|
|
160
|
-
whenItMatters: "프로젝트 규모가 커질수록 리팩토링 비용이 기하급수적으로 증가합니다."
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (id === 'RR-LINT-001' || type === 'GodModule') {
|
|
165
|
-
return {
|
|
166
|
-
...commonFields,
|
|
167
|
-
title: "거대 모듈 감지 (God Module)",
|
|
168
|
-
category: 'Maintenance',
|
|
169
|
-
evidence: issue,
|
|
170
|
-
standard: "Clean Code: Functions",
|
|
171
|
-
impact: "단일 파일의 책임이 과도하여 변경 시 영향 범위를 예측하기 어렵습니다.",
|
|
172
|
-
action: "책임에 따라 파일을 분리하세요 (Separation of Concerns).",
|
|
173
|
-
reference: "https://pypi.org/project/flake8/",
|
|
174
|
-
whenItMatters: "기능 추가 시마다 버그가 발생할 때."
|
|
175
|
-
};
|
|
47
|
+
export function getAuditDetails(id: string, type: string, issue: string, lang: 'en' | 'ko' = 'en'): { id: string; title: string; category: RepoAnalysisResult['findings'][0]['category']; evidence: string; standard: string; impact: string; action: string; reference: string; whenItMatters: string } {
|
|
48
|
+
const commonFields = { id };
|
|
49
|
+
const messages = LOCALES[lang] || LOCALES['en'];
|
|
50
|
+
let details: any = messages['DEFAULT'];
|
|
51
|
+
|
|
52
|
+
if (messages[id as keyof typeof messages]) {
|
|
53
|
+
details = messages[id as keyof typeof messages];
|
|
54
|
+
} else if (type === 'SecurityRisk') { // Fallback for general security type if ID not matched
|
|
55
|
+
details = messages['RR-SEC-001'];
|
|
56
|
+
} else if (type === 'CircularDependency') {
|
|
57
|
+
details = messages['RR-DEP-001'];
|
|
58
|
+
} else if (type === 'GodModule') {
|
|
59
|
+
details = messages['RR-LINT-001'];
|
|
176
60
|
}
|
|
177
61
|
|
|
178
62
|
return {
|
|
179
63
|
...commonFields,
|
|
180
|
-
title:
|
|
181
|
-
category:
|
|
182
|
-
evidence: issue,
|
|
183
|
-
standard:
|
|
184
|
-
impact:
|
|
185
|
-
action:
|
|
186
|
-
reference:
|
|
187
|
-
whenItMatters:
|
|
64
|
+
title: details.title,
|
|
65
|
+
category: details.category as any,
|
|
66
|
+
evidence: details.evidence || issue, // Use predefined evidence if exists, else dynamic issue
|
|
67
|
+
standard: details.standard,
|
|
68
|
+
impact: details.impact,
|
|
69
|
+
action: details.action,
|
|
70
|
+
reference: details.reference,
|
|
71
|
+
whenItMatters: details.whenItMatters
|
|
188
72
|
};
|
|
189
73
|
}
|
|
190
74
|
|
|
191
|
-
export async function analyzeRepository(repoPath: string, resultsDir?: string): Promise<RepoAnalysisResult> {
|
|
75
|
+
export async function analyzeRepository(repoPath: string, options?: { lang?: 'en' | 'ko', resultsDir?: string }): Promise<RepoAnalysisResult> {
|
|
76
|
+
const lang = options?.lang || 'en';
|
|
77
|
+
const resultsDir = options?.resultsDir;
|
|
192
78
|
const files: string[] = [];
|
|
193
79
|
const IGNORE_DIRS = ["node_modules", ".git", "dist", "build", "venv", ".venv", "__pycache__"];
|
|
194
80
|
|
|
@@ -220,7 +106,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
220
106
|
const hasTests = fs.readdirSync(repoPath, { withFileTypes: true })
|
|
221
107
|
.some(d => d.isDirectory() && /tests?|spec/i.test(d.name));
|
|
222
108
|
if (!hasTests) {
|
|
223
|
-
const details = getAuditDetails('RR-TEST-001', 'ProductionRisk', '');
|
|
109
|
+
const details = getAuditDetails('RR-TEST-001', 'ProductionRisk', '', lang);
|
|
224
110
|
findings.push({
|
|
225
111
|
file: 'Repository Root',
|
|
226
112
|
line: 0,
|
|
@@ -234,7 +120,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
234
120
|
const hasCI = fs.readdirSync(repoPath, { withFileTypes: true })
|
|
235
121
|
.some(d => d.isDirectory() && /(\.github|\.circleci|jenkins|gitlab)/i.test(d.name));
|
|
236
122
|
if (!hasCI) {
|
|
237
|
-
const details = getAuditDetails('RR-CI-001', 'ProductionRisk', '');
|
|
123
|
+
const details = getAuditDetails('RR-CI-001', 'ProductionRisk', '', lang);
|
|
238
124
|
findings.push({
|
|
239
125
|
file: 'Repository Root',
|
|
240
126
|
line: 0,
|
|
@@ -266,7 +152,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
266
152
|
|
|
267
153
|
if (hygieneMissing.length > 0) {
|
|
268
154
|
const evidenceBlock = "\n" + hygieneMissing.join("\n");
|
|
269
|
-
const details = getAuditDetails('RR-OPS-001', 'ProductionRisk', evidenceBlock);
|
|
155
|
+
const details = getAuditDetails('RR-OPS-001', 'ProductionRisk', evidenceBlock, lang);
|
|
270
156
|
findings.push({
|
|
271
157
|
file: 'Repository Root',
|
|
272
158
|
line: 0,
|
|
@@ -292,7 +178,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
292
178
|
|
|
293
179
|
// God Module Check (>500 lines)
|
|
294
180
|
if (lines.length > 500) {
|
|
295
|
-
const details = getAuditDetails('RR-LINT-001', 'GodModule', `File length: ${lines.length} lines
|
|
181
|
+
const details = getAuditDetails('RR-LINT-001', 'GodModule', `File length: ${lines.length} lines`, lang);
|
|
296
182
|
findings.push({ file: relativePath, line: 0, type: 'ProductionRisk', ...details });
|
|
297
183
|
operationalGapCount++;
|
|
298
184
|
}
|
|
@@ -300,7 +186,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
300
186
|
const result = await analyzePythonCode(code, relativePath);
|
|
301
187
|
|
|
302
188
|
if (result.hasError) {
|
|
303
|
-
const details = getAuditDetails('RR-SEC-001', result.type || 'Error', result.error || 'Unknown Issue');
|
|
189
|
+
const details = getAuditDetails('RR-SEC-001', result.type || 'Error', result.error || 'Unknown Issue', lang);
|
|
304
190
|
findings.push({
|
|
305
191
|
file: relativePath,
|
|
306
192
|
line: result.line || 0,
|
|
@@ -327,7 +213,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
327
213
|
const result = await analyzeJsTsCode(code, relativePath);
|
|
328
214
|
|
|
329
215
|
if (result.hasError) {
|
|
330
|
-
const details = getAuditDetails('RR-SEC-002', result.type || 'Error', result.error || 'Unknown Issue'); // Use SEC-002 for JS? Or reuse.
|
|
216
|
+
const details = getAuditDetails('RR-SEC-002', result.type || 'Error', result.error || 'Unknown Issue', lang); // Use SEC-002 for JS? Or reuse.
|
|
331
217
|
findings.push({
|
|
332
218
|
file: relativePath,
|
|
333
219
|
line: result.line || 0,
|
|
@@ -343,7 +229,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
343
229
|
}
|
|
344
230
|
|
|
345
231
|
if (!hasLogging && files.filter(f => f.endsWith('.py')).length > 0) {
|
|
346
|
-
const details = getAuditDetails('RR-LOG-001', 'ProductionRisk', '');
|
|
232
|
+
const details = getAuditDetails('RR-LOG-001', 'ProductionRisk', '', lang);
|
|
347
233
|
findings.push({ file: 'Repository Root', line: 0, type: 'ProductionRisk', ...details });
|
|
348
234
|
operationalGapCount++;
|
|
349
235
|
}
|
|
@@ -351,7 +237,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
351
237
|
// 3. Dependency Scan (Scalability -15)
|
|
352
238
|
const { adj, cycles } = depsScan(repoPath);
|
|
353
239
|
for (const cycle of cycles) {
|
|
354
|
-
const details = getAuditDetails('RR-DEP-001', 'CircularDependency', `Cycle: ${cycle.join(' -> ')}
|
|
240
|
+
const details = getAuditDetails('RR-DEP-001', 'CircularDependency', `Cycle: ${cycle.join(' -> ')}`, lang);
|
|
355
241
|
findings.push({
|
|
356
242
|
file: cycle[0],
|
|
357
243
|
line: 0,
|
|
@@ -408,7 +294,7 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
|
|
|
408
294
|
operationalGaps: operationalGapCount
|
|
409
295
|
},
|
|
410
296
|
graphUrl,
|
|
411
|
-
disclosure:
|
|
412
|
-
cta:
|
|
297
|
+
disclosure: LOCALES[lang]?.DISCLOSURE || LOCALES['en'].DISCLOSURE,
|
|
298
|
+
cta: LOCALES[lang]?.CTA || LOCALES['en'].CTA
|
|
413
299
|
};
|
|
414
300
|
}
|