codinsight 0.2.0__tar.gz → 0.4.0__tar.gz
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.
- {codinsight-0.2.0 → codinsight-0.4.0}/PKG-INFO +3 -2
- {codinsight-0.2.0 → codinsight-0.4.0}/pyproject.toml +19 -2
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/redundancy.py +1 -2
- codinsight-0.4.0/src/code_insight/code_analysis/security.py +327 -0
- codinsight-0.4.0/src/code_insight/code_analysis/vector.py +80 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/core.py +23 -5
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/multi_analysis.py +9 -10
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/trend_analysis/trend_analysis.py +1 -1
- {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/PKG-INFO +3 -2
- {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/SOURCES.txt +2 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/requires.txt +1 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/README.md +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/setup.cfg +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/__init__.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/__init__.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/abstract.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/algorithm.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/complexity.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/quality.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/readability.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/struct.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/style.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/py.typed +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/trend_analysis/__init__.py +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/dependency_links.txt +0 -0
- {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/top_level.txt +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codinsight
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Add your description here
|
|
5
|
-
Requires-Python: >=3.
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: codetovec
|
|
7
8
|
Requires-Dist: matplotlib>=3.10.5
|
|
8
9
|
Requires-Dist: pandas>=2.3.1
|
|
9
10
|
Requires-Dist: pycodestyle>=2.14.0
|
|
@@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codinsight"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Add your description here"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
11
|
dependencies = [
|
|
12
|
+
"codetovec",
|
|
12
13
|
"matplotlib>=3.10.5",
|
|
13
14
|
"pandas>=2.3.1",
|
|
14
15
|
"pycodestyle>=2.14.0",
|
|
@@ -35,6 +36,22 @@ addopts = "--exclude=tests"
|
|
|
35
36
|
packages = ["src/code_insight"]
|
|
36
37
|
include = ["src/code_insight/py.typed"]
|
|
37
38
|
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
line-length = 88
|
|
41
|
+
include = ["codinsight/**/*.py"]
|
|
42
|
+
|
|
43
|
+
[tool.ruff.lint]
|
|
44
|
+
select = ["F", "E", "I", "B"]
|
|
45
|
+
|
|
46
|
+
[tool.ruff.lint.isort]
|
|
47
|
+
# black profileと同等の設定
|
|
48
|
+
split-on-trailing-comma = true
|
|
49
|
+
combine-as-imports = true
|
|
50
|
+
|
|
51
|
+
[tool.ruff.format]
|
|
52
|
+
quote-style = "double"
|
|
53
|
+
indent-style = "space"
|
|
54
|
+
|
|
38
55
|
[tool.pytest.ini_options]
|
|
39
56
|
testpaths = ["tests"]
|
|
40
57
|
pythonpath = ["src"]
|
|
@@ -286,8 +286,7 @@ class Redundancy(AbstractAnalysis[RedundancyAnalysisResult, RedundancyAnalysisCo
|
|
|
286
286
|
if isinstance(node_value, bytes):
|
|
287
287
|
node_value = node_value.decode()
|
|
288
288
|
structure_elements.append(
|
|
289
|
-
f"return_const_{type(node.value.value).__name__}_"
|
|
290
|
-
f"{node_value}"
|
|
289
|
+
f"return_const_{type(node.value.value).__name__}_{node_value}"
|
|
291
290
|
)
|
|
292
291
|
elif isinstance(node.value, ast.BinOp):
|
|
293
292
|
structure_elements.append(
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import re
|
|
3
|
+
from typing import List, Set
|
|
4
|
+
|
|
5
|
+
from code_insight.code_analysis.abstract import (
|
|
6
|
+
AbstractAnalysis,
|
|
7
|
+
BaseAnalysisConfig,
|
|
8
|
+
BaseAnalysisResult,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SecurityAnalysisConfig(BaseAnalysisConfig):
|
|
13
|
+
"""
|
|
14
|
+
セキュリティ解析設定
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
check_hardcoded_secrets : bool
|
|
19
|
+
ハードコードされた秘密情報をチェックするか, by default True
|
|
20
|
+
check_dangerous_functions : bool
|
|
21
|
+
危険な関数の使用をチェックするか, by default True
|
|
22
|
+
check_sql_injection : bool
|
|
23
|
+
SQLインジェクション脆弱性をチェックするか, by default True
|
|
24
|
+
secret_patterns : List[str]
|
|
25
|
+
秘密情報検出パターン, by default 標準パターン
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
check_hardcoded_secrets: bool = True
|
|
29
|
+
check_dangerous_functions: bool = True
|
|
30
|
+
check_sql_injection: bool = True
|
|
31
|
+
secret_patterns: List[str] = [
|
|
32
|
+
r"password\s*=\s*['\"][^'\"]{3,}['\"]",
|
|
33
|
+
r"api_key\s*=\s*['\"][^'\"]{10,}['\"]",
|
|
34
|
+
r"secret\s*=\s*['\"][^'\"]{8,}['\"]",
|
|
35
|
+
r"token\s*=\s*['\"][^'\"]{10,}['\"]",
|
|
36
|
+
r"key\s*=\s*['\"][^'\"]{8,}['\"]",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SecurityAnalysisResult(BaseAnalysisResult):
|
|
41
|
+
"""
|
|
42
|
+
解析結果(セキュリティ)
|
|
43
|
+
|
|
44
|
+
Attributes
|
|
45
|
+
----------
|
|
46
|
+
hardcoded_secrets_count : int
|
|
47
|
+
ハードコードされた秘密情報の数
|
|
48
|
+
dangerous_function_count : int
|
|
49
|
+
危険な関数の使用数
|
|
50
|
+
sql_injection_risk_count : int
|
|
51
|
+
SQLインジェクション脆弱性の可能性がある箇所の数
|
|
52
|
+
input_validation_missing_count : int
|
|
53
|
+
入力検証不備の数
|
|
54
|
+
security_score : float
|
|
55
|
+
セキュリティスコア(0.0-1.0、高いほど安全)
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
hardcoded_secrets_count: int
|
|
59
|
+
dangerous_function_count: int
|
|
60
|
+
sql_injection_risk_count: int
|
|
61
|
+
input_validation_missing_count: int
|
|
62
|
+
security_score: float
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Security(AbstractAnalysis[SecurityAnalysisResult, SecurityAnalysisConfig]):
|
|
66
|
+
"""
|
|
67
|
+
解析クラス(セキュリティ)
|
|
68
|
+
|
|
69
|
+
Notes
|
|
70
|
+
-----
|
|
71
|
+
コードのセキュリティ脆弱性を多角的に解析するクラス
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, config: SecurityAnalysisConfig | None = None) -> None:
|
|
75
|
+
"""
|
|
76
|
+
コンストラクタ
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
config : SecurityAnalysisConfig | None, optional
|
|
81
|
+
セキュリティ解析設定, by default None
|
|
82
|
+
"""
|
|
83
|
+
super().__init__(config)
|
|
84
|
+
|
|
85
|
+
def get_default_config(self) -> SecurityAnalysisConfig:
|
|
86
|
+
"""
|
|
87
|
+
デフォルト設定を取得
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
SecurityAnalysisConfig
|
|
92
|
+
デフォルトのセキュリティ解析設定
|
|
93
|
+
"""
|
|
94
|
+
return SecurityAnalysisConfig()
|
|
95
|
+
|
|
96
|
+
def analyze(self, source_code: str) -> SecurityAnalysisResult:
|
|
97
|
+
"""
|
|
98
|
+
コード解析
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
source_code : str
|
|
103
|
+
解析対象のソースコード
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
SecurityAnalysisResult
|
|
108
|
+
セキュリティ解析結果
|
|
109
|
+
"""
|
|
110
|
+
if not self.config.enabled:
|
|
111
|
+
return SecurityAnalysisResult(
|
|
112
|
+
hardcoded_secrets_count=0,
|
|
113
|
+
dangerous_function_count=0,
|
|
114
|
+
sql_injection_risk_count=0,
|
|
115
|
+
input_validation_missing_count=0,
|
|
116
|
+
security_score=1.0,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
tree = self.parse_source_code(source_code)
|
|
120
|
+
|
|
121
|
+
hardcoded_secrets = self.get_hardcoded_secrets_count(source_code, tree)
|
|
122
|
+
dangerous_functions = self.get_dangerous_function_count(source_code, tree)
|
|
123
|
+
sql_injection_risks = self.get_sql_injection_risk_count(source_code, tree)
|
|
124
|
+
input_validation_missing = self.get_input_validation_missing_count(source_code, tree)
|
|
125
|
+
|
|
126
|
+
security_score = self.calculate_security_score(
|
|
127
|
+
hardcoded_secrets, dangerous_functions, sql_injection_risks, input_validation_missing
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return SecurityAnalysisResult(
|
|
131
|
+
hardcoded_secrets_count=hardcoded_secrets,
|
|
132
|
+
dangerous_function_count=dangerous_functions,
|
|
133
|
+
sql_injection_risk_count=sql_injection_risks,
|
|
134
|
+
input_validation_missing_count=input_validation_missing,
|
|
135
|
+
security_score=security_score,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def parse_source_code(self, source_code: str) -> ast.AST:
|
|
139
|
+
"""
|
|
140
|
+
ソースコードを解析
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
source_code : str
|
|
145
|
+
解析対象のソースコード
|
|
146
|
+
|
|
147
|
+
Returns
|
|
148
|
+
-------
|
|
149
|
+
ast.AST
|
|
150
|
+
解析済みのAST
|
|
151
|
+
"""
|
|
152
|
+
return ast.parse(source_code)
|
|
153
|
+
|
|
154
|
+
def get_hardcoded_secrets_count(
|
|
155
|
+
self, source_code: str, tree: ast.AST | None = None
|
|
156
|
+
) -> int:
|
|
157
|
+
"""
|
|
158
|
+
ハードコードされた秘密情報の数を取得
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
source_code : str
|
|
163
|
+
解析対象のソースコード
|
|
164
|
+
tree : ast.AST | None, optional
|
|
165
|
+
解析済みのAST, by default None
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
int
|
|
170
|
+
ハードコードされた秘密情報の数
|
|
171
|
+
"""
|
|
172
|
+
if not self.config.check_hardcoded_secrets:
|
|
173
|
+
return 0
|
|
174
|
+
|
|
175
|
+
count = 0
|
|
176
|
+
source_lower = source_code.lower()
|
|
177
|
+
|
|
178
|
+
for pattern in self.config.secret_patterns:
|
|
179
|
+
matches = re.findall(pattern, source_lower, re.IGNORECASE)
|
|
180
|
+
count += len(matches)
|
|
181
|
+
|
|
182
|
+
return count
|
|
183
|
+
|
|
184
|
+
def get_dangerous_function_count(
|
|
185
|
+
self, source_code: str, tree: ast.AST | None = None
|
|
186
|
+
) -> int:
|
|
187
|
+
"""
|
|
188
|
+
危険な関数の使用数を取得
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
source_code : str
|
|
193
|
+
解析対象のソースコード
|
|
194
|
+
tree : ast.AST | None, optional
|
|
195
|
+
解析済みのAST, by default None
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
int
|
|
200
|
+
危険な関数の使用数
|
|
201
|
+
"""
|
|
202
|
+
if not self.config.check_dangerous_functions:
|
|
203
|
+
return 0
|
|
204
|
+
|
|
205
|
+
tree = tree or self.parse_source_code(source_code)
|
|
206
|
+
dangerous_functions = {"eval", "exec", "compile", "__import__"}
|
|
207
|
+
count = 0
|
|
208
|
+
|
|
209
|
+
for node in ast.walk(tree):
|
|
210
|
+
if isinstance(node, ast.Call):
|
|
211
|
+
if isinstance(node.func, ast.Name) and node.func.id in dangerous_functions:
|
|
212
|
+
count += 1
|
|
213
|
+
|
|
214
|
+
return count
|
|
215
|
+
|
|
216
|
+
def get_sql_injection_risk_count(
|
|
217
|
+
self, source_code: str, tree: ast.AST | None = None
|
|
218
|
+
) -> int:
|
|
219
|
+
"""
|
|
220
|
+
SQLインジェクション脆弱性の可能性がある箇所の数を取得
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
source_code : str
|
|
225
|
+
解析対象のソースコード
|
|
226
|
+
tree : ast.AST | None, optional
|
|
227
|
+
解析済みのAST, by default None
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
int
|
|
232
|
+
SQLインジェクション脆弱性の可能性がある箇所の数
|
|
233
|
+
"""
|
|
234
|
+
if not self.config.check_sql_injection:
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
tree = tree or self.parse_source_code(source_code)
|
|
238
|
+
count = 0
|
|
239
|
+
|
|
240
|
+
sql_patterns = [
|
|
241
|
+
r"select\s+.*\s+from\s+.*\+",
|
|
242
|
+
r"insert\s+into\s+.*\+",
|
|
243
|
+
r"update\s+.*\s+set\s+.*\+",
|
|
244
|
+
r"delete\s+from\s+.*\+",
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
for node in ast.walk(tree):
|
|
248
|
+
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
|
|
249
|
+
if isinstance(node.left, ast.Constant) and isinstance(node.left.value, str):
|
|
250
|
+
sql_text = node.left.value.lower()
|
|
251
|
+
for pattern in sql_patterns:
|
|
252
|
+
if re.search(pattern, sql_text):
|
|
253
|
+
count += 1
|
|
254
|
+
break
|
|
255
|
+
|
|
256
|
+
return count
|
|
257
|
+
|
|
258
|
+
def get_input_validation_missing_count(
|
|
259
|
+
self, source_code: str, tree: ast.AST | None = None
|
|
260
|
+
) -> int:
|
|
261
|
+
"""
|
|
262
|
+
入力検証不備の数を取得
|
|
263
|
+
|
|
264
|
+
Parameters
|
|
265
|
+
----------
|
|
266
|
+
source_code : str
|
|
267
|
+
解析対象のソースコード
|
|
268
|
+
tree : ast.AST | None, optional
|
|
269
|
+
解析済みのAST, by default None
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
int
|
|
274
|
+
入力検証不備の数
|
|
275
|
+
"""
|
|
276
|
+
tree = tree or self.parse_source_code(source_code)
|
|
277
|
+
count = 0
|
|
278
|
+
input_functions = {"input", "raw_input"}
|
|
279
|
+
|
|
280
|
+
for node in ast.walk(tree):
|
|
281
|
+
if isinstance(node, ast.Call):
|
|
282
|
+
if isinstance(node.func, ast.Name) and node.func.id in input_functions:
|
|
283
|
+
parent_found = False
|
|
284
|
+
for parent in ast.walk(tree):
|
|
285
|
+
if isinstance(parent, ast.If):
|
|
286
|
+
for child in ast.walk(parent):
|
|
287
|
+
if child is node:
|
|
288
|
+
parent_found = True
|
|
289
|
+
break
|
|
290
|
+
if not parent_found:
|
|
291
|
+
count += 1
|
|
292
|
+
|
|
293
|
+
return count
|
|
294
|
+
|
|
295
|
+
def calculate_security_score(
|
|
296
|
+
self,
|
|
297
|
+
hardcoded_secrets: int,
|
|
298
|
+
dangerous_functions: int,
|
|
299
|
+
sql_injection_risks: int,
|
|
300
|
+
input_validation_missing: int,
|
|
301
|
+
) -> float:
|
|
302
|
+
"""
|
|
303
|
+
セキュリティスコアを計算
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
hardcoded_secrets : int
|
|
308
|
+
ハードコードされた秘密情報の数
|
|
309
|
+
dangerous_functions : int
|
|
310
|
+
危険な関数の使用数
|
|
311
|
+
sql_injection_risks : int
|
|
312
|
+
SQLインジェクション脆弱性の可能性がある箇所の数
|
|
313
|
+
input_validation_missing : int
|
|
314
|
+
入力検証不備の数
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
float
|
|
319
|
+
セキュリティスコア(0.0-1.0、高いほど安全)
|
|
320
|
+
"""
|
|
321
|
+
total_issues = hardcoded_secrets + dangerous_functions + sql_injection_risks + input_validation_missing
|
|
322
|
+
|
|
323
|
+
if total_issues == 0:
|
|
324
|
+
return 1.0
|
|
325
|
+
|
|
326
|
+
penalty = min(total_issues * 0.1, 1.0)
|
|
327
|
+
return max(1.0 - penalty, 0.0)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
from code_insight.code_analysis.abstract import (
|
|
3
|
+
AbstractAnalysis,
|
|
4
|
+
BaseAnalysisConfig,
|
|
5
|
+
BaseAnalysisResult,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from codetovec import CodeToVec
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class VectorAnalysisConfig(BaseAnalysisConfig):
|
|
12
|
+
"""
|
|
13
|
+
ベクトル解析の設定
|
|
14
|
+
|
|
15
|
+
Attributes
|
|
16
|
+
----------
|
|
17
|
+
remove_comments : bool
|
|
18
|
+
コメントを削除するかどうか, by default False
|
|
19
|
+
remove_docstrings : bool
|
|
20
|
+
Docstringを削除するかどうか, by default False
|
|
21
|
+
remove_blank_lines : bool
|
|
22
|
+
空行を削除するかどうか, by default False
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
remove_comments: bool = False
|
|
26
|
+
remove_docstrings: bool = False
|
|
27
|
+
remove_blank_lines: bool = False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class VectorAnalysisResult(BaseAnalysisResult):
|
|
31
|
+
"""
|
|
32
|
+
ベクトル解析結果
|
|
33
|
+
|
|
34
|
+
Attributes
|
|
35
|
+
----------
|
|
36
|
+
vector : list[float]
|
|
37
|
+
抽出されたベクトル表現
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
vector: list[float]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Vector(AbstractAnalysis[VectorAnalysisResult, VectorAnalysisConfig]):
|
|
44
|
+
"""
|
|
45
|
+
ベクトル解析クラス
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def get_default_config(self) -> VectorAnalysisConfig:
|
|
49
|
+
"""
|
|
50
|
+
デフォルト設定を取得
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
VectorAnalysisConfig
|
|
55
|
+
デフォルトのベクトル解析設定
|
|
56
|
+
"""
|
|
57
|
+
return VectorAnalysisConfig()
|
|
58
|
+
|
|
59
|
+
def analyze(self, source_code: str) -> VectorAnalysisResult:
|
|
60
|
+
"""
|
|
61
|
+
ソースコードからベクトルを抽出する
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
source_code : str
|
|
66
|
+
解析対象のソースコード
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
VectorAnalysisResult
|
|
71
|
+
抽出されたベクトル表現
|
|
72
|
+
"""
|
|
73
|
+
codetovec = CodeToVec()
|
|
74
|
+
vector = codetovec.execute(
|
|
75
|
+
text=source_code,
|
|
76
|
+
remove_comments=self.config.remove_comments,
|
|
77
|
+
remove_docstrings=self.config.remove_docstrings,
|
|
78
|
+
remove_blank_lines=self.config.remove_blank_lines,
|
|
79
|
+
)
|
|
80
|
+
return VectorAnalysisResult(vector=vector)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from enum import StrEnum, auto
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
5
|
|
|
@@ -16,8 +16,10 @@ from code_insight.code_analysis.readability import (
|
|
|
16
16
|
ReadabilityAnalysisConfig,
|
|
17
17
|
)
|
|
18
18
|
from code_insight.code_analysis.redundancy import Redundancy, RedundancyAnalysisConfig
|
|
19
|
+
from code_insight.code_analysis.security import Security, SecurityAnalysisConfig
|
|
19
20
|
from code_insight.code_analysis.struct import Struct, StructAnalysisConfig
|
|
20
|
-
from code_insight.code_analysis.style import Style, StyleAnalysisConfig
|
|
21
|
+
from code_insight.code_analysis.style import Style, StyleAnalysisConfig
|
|
22
|
+
from code_insight.code_analysis.vector import Vector, VectorAnalysisConfig
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
class AnalysisConfigs(BaseModel):
|
|
@@ -40,6 +42,8 @@ class AnalysisConfigs(BaseModel):
|
|
|
40
42
|
複雑度解析設定, by default None
|
|
41
43
|
quality : QualityAnalysisConfig | None
|
|
42
44
|
品質解析設定, by default None
|
|
45
|
+
security : SecurityAnalysisConfig | None
|
|
46
|
+
セキュリティ解析設定, by default None
|
|
43
47
|
"""
|
|
44
48
|
|
|
45
49
|
style: StyleAnalysisConfig | None = None
|
|
@@ -49,6 +53,8 @@ class AnalysisConfigs(BaseModel):
|
|
|
49
53
|
algorithm: AlgorithmAnalysisConfig | None = None
|
|
50
54
|
complexity: ComplexityAnalysisConfig | None = None
|
|
51
55
|
quality: QualityAnalysisConfig | None = None
|
|
56
|
+
security: SecurityAnalysisConfig | None = None
|
|
57
|
+
vector: VectorAnalysisConfig | None = None
|
|
52
58
|
|
|
53
59
|
|
|
54
60
|
class CodeAnalysisType(StrEnum):
|
|
@@ -71,6 +77,10 @@ class CodeAnalysisType(StrEnum):
|
|
|
71
77
|
複雑度解析
|
|
72
78
|
QUALITY : str
|
|
73
79
|
品質解析
|
|
80
|
+
SECURITY : str
|
|
81
|
+
セキュリティ解析
|
|
82
|
+
VECTOR : str
|
|
83
|
+
ベクトル解析
|
|
74
84
|
"""
|
|
75
85
|
|
|
76
86
|
STYLE = auto()
|
|
@@ -80,6 +90,8 @@ class CodeAnalysisType(StrEnum):
|
|
|
80
90
|
ALGORITHM = auto()
|
|
81
91
|
COMPLEXITY = auto()
|
|
82
92
|
QUALITY = auto()
|
|
93
|
+
SECURITY = auto()
|
|
94
|
+
VECTOR = auto()
|
|
83
95
|
|
|
84
96
|
@staticmethod
|
|
85
97
|
def get_code_analysis_class(
|
|
@@ -119,6 +131,10 @@ class CodeAnalysisType(StrEnum):
|
|
|
119
131
|
return Complexity(config) # type: ignore
|
|
120
132
|
elif type == CodeAnalysisType.QUALITY:
|
|
121
133
|
return Quality(config) # type: ignore
|
|
134
|
+
elif type == CodeAnalysisType.SECURITY:
|
|
135
|
+
return Security(config) # type: ignore
|
|
136
|
+
elif type == CodeAnalysisType.VECTOR:
|
|
137
|
+
return Vector(config) # type: ignore
|
|
122
138
|
else:
|
|
123
139
|
raise ValueError(f"Invalid code analysis type: {type}")
|
|
124
140
|
|
|
@@ -156,7 +172,7 @@ class CodeAnalysis:
|
|
|
156
172
|
|
|
157
173
|
def analyze(
|
|
158
174
|
self, types: list[CodeAnalysisType]
|
|
159
|
-
) -> dict[CodeAnalysisType,
|
|
175
|
+
) -> dict[CodeAnalysisType, BaseAnalysisResult]:
|
|
160
176
|
"""
|
|
161
177
|
コード解析
|
|
162
178
|
|
|
@@ -167,10 +183,10 @@ class CodeAnalysis:
|
|
|
167
183
|
|
|
168
184
|
Returns
|
|
169
185
|
-------
|
|
170
|
-
dict[CodeAnalysisType,
|
|
186
|
+
dict[CodeAnalysisType, BaseAnalysisResult]
|
|
171
187
|
解析結果の辞書
|
|
172
188
|
"""
|
|
173
|
-
result: dict[CodeAnalysisType,
|
|
189
|
+
result: dict[CodeAnalysisType, BaseAnalysisResult] = {}
|
|
174
190
|
for type in types:
|
|
175
191
|
config = self._get_config_for_type(type)
|
|
176
192
|
result[type] = CodeAnalysisType.get_code_analysis_class(
|
|
@@ -205,5 +221,7 @@ class CodeAnalysis:
|
|
|
205
221
|
CodeAnalysisType.ALGORITHM: self.configs.algorithm,
|
|
206
222
|
CodeAnalysisType.COMPLEXITY: self.configs.complexity,
|
|
207
223
|
CodeAnalysisType.QUALITY: self.configs.quality,
|
|
224
|
+
CodeAnalysisType.SECURITY: self.configs.security,
|
|
225
|
+
CodeAnalysisType.VECTOR: self.configs.vector,
|
|
208
226
|
}
|
|
209
227
|
return config_map.get(analysis_type)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Iterable
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
@@ -24,7 +24,7 @@ class FileAnalysisResult(BaseModel):
|
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
26
|
path: str
|
|
27
|
-
results: dict[
|
|
27
|
+
results: dict[CodeAnalysisType, BaseAnalysisResult]
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class AggregateStats(BaseModel):
|
|
@@ -46,7 +46,7 @@ class AggregateStats(BaseModel):
|
|
|
46
46
|
total_files: int
|
|
47
47
|
analyzed_files: int
|
|
48
48
|
errors: list[str]
|
|
49
|
-
by_type_avg: dict[
|
|
49
|
+
by_type_avg: dict[CodeAnalysisType, dict[str, float]]
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class MultiAnalysisResult(BaseModel):
|
|
@@ -169,16 +169,15 @@ def analyze_file(
|
|
|
169
169
|
source_code = path.read_text(encoding="utf-8", errors="ignore")
|
|
170
170
|
analysis = CodeAnalysis(source_code=source_code, configs=configs)
|
|
171
171
|
result_map = analysis.analyze(types)
|
|
172
|
-
as_dict: dict[
|
|
172
|
+
as_dict: dict[CodeAnalysisType, BaseAnalysisResult] = {}
|
|
173
173
|
for t, model in result_map.items():
|
|
174
|
-
|
|
175
|
-
as_dict[t.name] = m.model_dump()
|
|
174
|
+
as_dict[t] = model
|
|
176
175
|
return FileAnalysisResult(path=str(path), results=as_dict)
|
|
177
176
|
|
|
178
177
|
|
|
179
178
|
def _aggregate_numeric_means(
|
|
180
179
|
files: list[FileAnalysisResult],
|
|
181
|
-
) -> dict[
|
|
180
|
+
) -> dict[CodeAnalysisType, dict[str, float]]:
|
|
182
181
|
"""
|
|
183
182
|
数値メトリクスの平均値を集約
|
|
184
183
|
|
|
@@ -192,17 +191,17 @@ def _aggregate_numeric_means(
|
|
|
192
191
|
dict[str, dict[str, float]]
|
|
193
192
|
解析タイプ別の平均値辞書
|
|
194
193
|
"""
|
|
195
|
-
by_type: dict[
|
|
194
|
+
by_type: dict[CodeAnalysisType, dict[str, list[float]]] = {}
|
|
196
195
|
|
|
197
196
|
for fa in files:
|
|
198
197
|
for tname, metrics in fa.results.items():
|
|
199
198
|
if tname not in by_type:
|
|
200
199
|
by_type[tname] = {}
|
|
201
|
-
for key, val in metrics.items():
|
|
200
|
+
for key, val in metrics.model_dump().items():
|
|
202
201
|
if isinstance(val, (int, float)):
|
|
203
202
|
by_type[tname].setdefault(key, []).append(float(val))
|
|
204
203
|
|
|
205
|
-
avg: dict[
|
|
204
|
+
avg: dict[CodeAnalysisType, dict[str, float]] = {}
|
|
206
205
|
for tname, metrics_map in by_type.items():
|
|
207
206
|
avg[tname] = {}
|
|
208
207
|
for key, values in metrics_map.items():
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codinsight
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Add your description here
|
|
5
|
-
Requires-Python: >=3.
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: codetovec
|
|
7
8
|
Requires-Dist: matplotlib>=3.10.5
|
|
8
9
|
Requires-Dist: pandas>=2.3.1
|
|
9
10
|
Requires-Dist: pycodestyle>=2.14.0
|
|
@@ -11,8 +11,10 @@ src/code_insight/code_analysis/complexity.py
|
|
|
11
11
|
src/code_insight/code_analysis/quality.py
|
|
12
12
|
src/code_insight/code_analysis/readability.py
|
|
13
13
|
src/code_insight/code_analysis/redundancy.py
|
|
14
|
+
src/code_insight/code_analysis/security.py
|
|
14
15
|
src/code_insight/code_analysis/struct.py
|
|
15
16
|
src/code_insight/code_analysis/style.py
|
|
17
|
+
src/code_insight/code_analysis/vector.py
|
|
16
18
|
src/code_insight/trend_analysis/__init__.py
|
|
17
19
|
src/code_insight/trend_analysis/trend_analysis.py
|
|
18
20
|
src/codinsight.egg-info/PKG-INFO
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|