claude-mpm 4.4.3__py3-none-any.whl → 4.4.5__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/agent_loader.py +3 -2
- claude_mpm/agents/agent_loader_integration.py +2 -1
- claude_mpm/agents/async_agent_loader.py +2 -2
- claude_mpm/agents/base_agent_loader.py +2 -2
- claude_mpm/agents/frontmatter_validator.py +1 -0
- claude_mpm/agents/system_agent_config.py +2 -1
- claude_mpm/cli/commands/doctor.py +44 -5
- claude_mpm/cli/commands/mpm_init.py +116 -62
- claude_mpm/cli/parsers/configure_parser.py +3 -1
- claude_mpm/cli/startup_logging.py +1 -3
- claude_mpm/config/agent_config.py +1 -1
- claude_mpm/config/paths.py +2 -1
- claude_mpm/core/agent_name_normalizer.py +1 -0
- claude_mpm/core/config.py +2 -1
- claude_mpm/core/config_aliases.py +2 -1
- claude_mpm/core/file_utils.py +0 -1
- claude_mpm/core/framework/__init__.py +6 -6
- claude_mpm/core/framework/formatters/__init__.py +2 -2
- claude_mpm/core/framework/formatters/capability_generator.py +19 -8
- claude_mpm/core/framework/formatters/content_formatter.py +8 -3
- claude_mpm/core/framework/formatters/context_generator.py +7 -3
- claude_mpm/core/framework/loaders/__init__.py +3 -3
- claude_mpm/core/framework/loaders/agent_loader.py +7 -3
- claude_mpm/core/framework/loaders/file_loader.py +16 -6
- claude_mpm/core/framework/loaders/instruction_loader.py +16 -6
- claude_mpm/core/framework/loaders/packaged_loader.py +36 -12
- claude_mpm/core/framework/processors/__init__.py +2 -2
- claude_mpm/core/framework/processors/memory_processor.py +14 -6
- claude_mpm/core/framework/processors/metadata_processor.py +5 -5
- claude_mpm/core/framework/processors/template_processor.py +12 -6
- claude_mpm/core/framework_loader.py +44 -20
- claude_mpm/core/log_manager.py +2 -1
- claude_mpm/core/tool_access_control.py +1 -0
- claude_mpm/core/unified_agent_registry.py +2 -1
- claude_mpm/core/unified_paths.py +1 -0
- claude_mpm/experimental/cli_enhancements.py +1 -0
- claude_mpm/hooks/base_hook.py +1 -0
- claude_mpm/hooks/instruction_reinforcement.py +1 -0
- claude_mpm/hooks/kuzu_memory_hook.py +20 -13
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/scripts/mpm_doctor.py +1 -0
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -1
- claude_mpm/services/agents/loading/framework_agent_loader.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -0
- claude_mpm/services/agents/management/agent_management_service.py +1 -1
- claude_mpm/services/agents/memory/memory_categorization_service.py +0 -1
- claude_mpm/services/agents/memory/memory_file_service.py +6 -2
- claude_mpm/services/agents/memory/memory_format_service.py +0 -1
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/async_session_logger.py +1 -1
- claude_mpm/services/claude_session_logger.py +1 -0
- claude_mpm/services/core/path_resolver.py +1 -0
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +126 -25
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +451 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +3 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +259 -32
- claude_mpm/services/event_bus/direct_relay.py +2 -1
- claude_mpm/services/event_bus/event_bus.py +1 -0
- claude_mpm/services/event_bus/relay.py +3 -2
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +1 -1
- claude_mpm/services/infrastructure/daemon_manager.py +1 -1
- claude_mpm/services/mcp_config_manager.py +301 -54
- claude_mpm/services/mcp_gateway/core/process_pool.py +62 -23
- claude_mpm/services/mcp_gateway/tools/__init__.py +6 -5
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +3 -1
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +16 -31
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/project/archive_manager.py +159 -96
- claude_mpm/services/project/documentation_manager.py +64 -45
- claude_mpm/services/project/enhanced_analyzer.py +132 -89
- claude_mpm/services/project/project_organizer.py +225 -131
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/socketio/server/eventbus_integration.py +1 -1
- claude_mpm/services/unified/__init__.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/__init__.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +97 -53
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +81 -40
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +277 -178
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +196 -112
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +83 -49
- claude_mpm/services/unified/config_strategies/__init__.py +111 -126
- claude_mpm/services/unified/config_strategies/config_schema.py +157 -111
- claude_mpm/services/unified/config_strategies/context_strategy.py +91 -89
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +183 -173
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +160 -152
- claude_mpm/services/unified/config_strategies/unified_config_service.py +124 -112
- claude_mpm/services/unified/config_strategies/validation_strategy.py +298 -259
- claude_mpm/services/unified/deployment_strategies/__init__.py +7 -7
- claude_mpm/services/unified/deployment_strategies/base.py +24 -28
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +168 -88
- claude_mpm/services/unified/deployment_strategies/local.py +49 -34
- claude_mpm/services/unified/deployment_strategies/utils.py +39 -43
- claude_mpm/services/unified/deployment_strategies/vercel.py +30 -24
- claude_mpm/services/unified/interfaces.py +0 -26
- claude_mpm/services/unified/migration.py +17 -40
- claude_mpm/services/unified/strategies.py +9 -26
- claude_mpm/services/unified/unified_analyzer.py +48 -44
- claude_mpm/services/unified/unified_config.py +21 -19
- claude_mpm/services/unified/unified_deployment.py +21 -26
- claude_mpm/storage/state_storage.py +1 -0
- claude_mpm/utils/agent_dependency_loader.py +18 -6
- claude_mpm/utils/common.py +14 -12
- claude_mpm/utils/database_connector.py +15 -12
- claude_mpm/utils/error_handler.py +1 -0
- claude_mpm/utils/log_cleanup.py +1 -0
- claude_mpm/utils/path_operations.py +1 -0
- claude_mpm/utils/session_logging.py +1 -1
- claude_mpm/utils/subprocess_utils.py +1 -0
- claude_mpm/validation/agent_validator.py +1 -1
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/METADATA +35 -15
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/RECORD +118 -117
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/top_level.txt +0 -0
@@ -12,11 +12,16 @@ Created: 2025-01-26
|
|
12
12
|
import ast
|
13
13
|
import re
|
14
14
|
from pathlib import Path
|
15
|
-
from typing import Any, Dict, List, Optional
|
15
|
+
from typing import Any, Dict, List, Optional
|
16
16
|
|
17
17
|
from claude_mpm.core.logging_utils import get_logger
|
18
18
|
|
19
|
-
from ..strategies import
|
19
|
+
from ..strategies import (
|
20
|
+
AnalyzerStrategy,
|
21
|
+
StrategyContext,
|
22
|
+
StrategyMetadata,
|
23
|
+
StrategyPriority,
|
24
|
+
)
|
20
25
|
|
21
26
|
logger = get_logger(__name__)
|
22
27
|
|
@@ -38,7 +43,7 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
38
43
|
"sql_injection": {
|
39
44
|
"patterns": [
|
40
45
|
r'(execute|query)\s*\(\s*["\'].*%[s|d].*["\'].*%',
|
41
|
-
r
|
46
|
+
r"(execute|query)\s*\(\s*.*\+.*\)",
|
42
47
|
r'f["\'].*SELECT.*{.*}.*FROM',
|
43
48
|
],
|
44
49
|
"severity": "critical",
|
@@ -55,35 +60,35 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
55
60
|
},
|
56
61
|
"weak_crypto": {
|
57
62
|
"patterns": [
|
58
|
-
r
|
59
|
-
r
|
60
|
-
r
|
63
|
+
r"(MD5|SHA1)\s*\(",
|
64
|
+
r"DES\s*\(",
|
65
|
+
r"Random\(\)(?!\.SystemRandom)",
|
61
66
|
],
|
62
67
|
"severity": "medium",
|
63
68
|
"description": "Weak cryptographic algorithm usage",
|
64
69
|
},
|
65
70
|
"command_injection": {
|
66
71
|
"patterns": [
|
67
|
-
r
|
68
|
-
r
|
69
|
-
r
|
72
|
+
r"os\.(system|popen|spawn.*)\s*\([^)]*\+[^)]*\)",
|
73
|
+
r"subprocess\.(run|call|Popen)\s*\([^)]*shell\s*=\s*True",
|
74
|
+
r"eval\s*\([^)]*input\s*\(",
|
70
75
|
],
|
71
76
|
"severity": "critical",
|
72
77
|
"description": "Potential command injection vulnerability",
|
73
78
|
},
|
74
79
|
"path_traversal": {
|
75
80
|
"patterns": [
|
76
|
-
r
|
77
|
-
r
|
78
|
-
r
|
81
|
+
r"open\s*\([^)]*\.\.[/\\]",
|
82
|
+
r"(read_file|write_file)\s*\([^)]*user_input",
|
83
|
+
r"Path\s*\([^)]*\+[^)]*\)",
|
79
84
|
],
|
80
85
|
"severity": "high",
|
81
86
|
"description": "Potential path traversal vulnerability",
|
82
87
|
},
|
83
88
|
"xss": {
|
84
89
|
"patterns": [
|
85
|
-
r
|
86
|
-
r
|
90
|
+
r"innerHTML\s*=\s*[^;]*user",
|
91
|
+
r"document\.write\s*\([^)]*user",
|
87
92
|
r'v-html\s*=\s*["\'][^"\']*user',
|
88
93
|
],
|
89
94
|
"severity": "high",
|
@@ -95,27 +100,27 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
95
100
|
CONFIG_ISSUES = {
|
96
101
|
"debug_enabled": {
|
97
102
|
"patterns": [
|
98
|
-
r
|
99
|
-
r
|
100
|
-
r
|
103
|
+
r"DEBUG\s*=\s*True",
|
104
|
+
r"debug\s*:\s*true",
|
105
|
+
r"app\.debug\s*=\s*True",
|
101
106
|
],
|
102
107
|
"severity": "medium",
|
103
108
|
"description": "Debug mode enabled in production configuration",
|
104
109
|
},
|
105
110
|
"insecure_cors": {
|
106
111
|
"patterns": [
|
107
|
-
r
|
112
|
+
r"Access-Control-Allow-Origin.*\*",
|
108
113
|
r'cors\s*\(.*origin\s*:\s*["\'].*\*',
|
109
|
-
r
|
114
|
+
r"CORS_ORIGIN_ALLOW_ALL\s*=\s*True",
|
110
115
|
],
|
111
116
|
"severity": "medium",
|
112
117
|
"description": "Insecure CORS configuration allowing all origins",
|
113
118
|
},
|
114
119
|
"missing_csrf": {
|
115
120
|
"patterns": [
|
116
|
-
r
|
117
|
-
r
|
118
|
-
r
|
121
|
+
r"csrf_enabled\s*=\s*False",
|
122
|
+
r"CSRF_ENABLED\s*=\s*False",
|
123
|
+
r"@csrf_exempt",
|
119
124
|
],
|
120
125
|
"severity": "high",
|
121
126
|
"description": "CSRF protection disabled",
|
@@ -189,7 +194,7 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
189
194
|
|
190
195
|
if target_path.is_file():
|
191
196
|
return self._analyze_file(target_path, options)
|
192
|
-
|
197
|
+
if target_path.is_dir():
|
193
198
|
return self._analyze_directory(target_path, options)
|
194
199
|
|
195
200
|
return {
|
@@ -227,7 +232,9 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
227
232
|
results["vulnerabilities"].extend(js_issues)
|
228
233
|
|
229
234
|
# Calculate risk score
|
230
|
-
results["risk_score"] = self._calculate_risk_score(
|
235
|
+
results["risk_score"] = self._calculate_risk_score(
|
236
|
+
results["vulnerabilities"]
|
237
|
+
)
|
231
238
|
|
232
239
|
# Add summary
|
233
240
|
results["summary"] = self._generate_summary(results["vulnerabilities"])
|
@@ -239,7 +246,9 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
239
246
|
|
240
247
|
return results
|
241
248
|
|
242
|
-
def _analyze_directory(
|
249
|
+
def _analyze_directory(
|
250
|
+
self, dir_path: Path, options: Dict[str, Any]
|
251
|
+
) -> Dict[str, Any]:
|
243
252
|
"""Analyze all files in a directory for security issues."""
|
244
253
|
results = {
|
245
254
|
"status": "success",
|
@@ -254,10 +263,29 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
254
263
|
|
255
264
|
# Define file extensions to analyze
|
256
265
|
analyzable_extensions = {
|
257
|
-
".py",
|
258
|
-
".
|
259
|
-
".
|
260
|
-
".
|
266
|
+
".py",
|
267
|
+
".js",
|
268
|
+
".jsx",
|
269
|
+
".ts",
|
270
|
+
".tsx",
|
271
|
+
".java",
|
272
|
+
".cs",
|
273
|
+
".php",
|
274
|
+
".rb",
|
275
|
+
".go",
|
276
|
+
".rs",
|
277
|
+
".cpp",
|
278
|
+
".c",
|
279
|
+
".h",
|
280
|
+
".yml",
|
281
|
+
".yaml",
|
282
|
+
".json",
|
283
|
+
".xml",
|
284
|
+
".conf",
|
285
|
+
".config",
|
286
|
+
".env",
|
287
|
+
".ini",
|
288
|
+
".properties",
|
261
289
|
}
|
262
290
|
|
263
291
|
# Analyze each file
|
@@ -272,16 +300,22 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
272
300
|
continue
|
273
301
|
|
274
302
|
file_result = self._analyze_file(file_path, options)
|
275
|
-
if
|
303
|
+
if (
|
304
|
+
file_result["status"] == "success"
|
305
|
+
and file_result["vulnerabilities"]
|
306
|
+
):
|
276
307
|
results["files"].append(file_result)
|
277
308
|
results["files_analyzed"] += 1
|
278
|
-
results["total_vulnerabilities"] += len(
|
309
|
+
results["total_vulnerabilities"] += len(
|
310
|
+
file_result["vulnerabilities"]
|
311
|
+
)
|
279
312
|
|
280
313
|
# Count by severity
|
281
314
|
for vuln in file_result["vulnerabilities"]:
|
282
315
|
severity = vuln.get("severity", "unknown")
|
283
|
-
results["vulnerabilities_by_severity"][severity] =
|
316
|
+
results["vulnerabilities_by_severity"][severity] = (
|
284
317
|
results["vulnerabilities_by_severity"].get(severity, 0) + 1
|
318
|
+
)
|
285
319
|
|
286
320
|
# Calculate overall risk score
|
287
321
|
results["risk_score"] = self._calculate_overall_risk(results)
|
@@ -291,7 +325,9 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
291
325
|
|
292
326
|
return results
|
293
327
|
|
294
|
-
def _scan_for_vulnerabilities(
|
328
|
+
def _scan_for_vulnerabilities(
|
329
|
+
self, content: str, file_path: Path
|
330
|
+
) -> List[Dict[str, Any]]:
|
295
331
|
"""Scan content for known vulnerability patterns."""
|
296
332
|
vulnerabilities = []
|
297
333
|
|
@@ -299,48 +335,67 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
299
335
|
for pattern in vuln_info["patterns"]:
|
300
336
|
matches = re.finditer(pattern, content, re.IGNORECASE | re.MULTILINE)
|
301
337
|
for match in matches:
|
302
|
-
line_num = content[:match.start()].count("\n") + 1
|
303
|
-
|
304
|
-
vulnerabilities.append(
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
338
|
+
line_num = content[: match.start()].count("\n") + 1
|
339
|
+
|
340
|
+
vulnerabilities.append(
|
341
|
+
{
|
342
|
+
"type": vuln_type,
|
343
|
+
"severity": vuln_info["severity"],
|
344
|
+
"description": vuln_info["description"],
|
345
|
+
"file": str(file_path),
|
346
|
+
"line": line_num,
|
347
|
+
"code": match.group(0)[:100], # Truncate long matches
|
348
|
+
"pattern": pattern,
|
349
|
+
}
|
350
|
+
)
|
313
351
|
|
314
352
|
return vulnerabilities
|
315
353
|
|
316
|
-
def _scan_for_config_issues(
|
354
|
+
def _scan_for_config_issues(
|
355
|
+
self, content: str, file_path: Path
|
356
|
+
) -> List[Dict[str, Any]]:
|
317
357
|
"""Scan for insecure configuration patterns."""
|
318
358
|
issues = []
|
319
359
|
|
320
360
|
# Only check configuration files
|
321
|
-
config_extensions = {
|
322
|
-
|
323
|
-
|
361
|
+
config_extensions = {
|
362
|
+
".yml",
|
363
|
+
".yaml",
|
364
|
+
".json",
|
365
|
+
".conf",
|
366
|
+
".config",
|
367
|
+
".ini",
|
368
|
+
".env",
|
369
|
+
}
|
370
|
+
if file_path.suffix not in config_extensions and file_path.name not in [
|
371
|
+
"settings.py",
|
372
|
+
"config.py",
|
373
|
+
"configuration.py",
|
374
|
+
]:
|
324
375
|
return issues
|
325
376
|
|
326
377
|
for issue_type, issue_info in self.CONFIG_ISSUES.items():
|
327
378
|
for pattern in issue_info["patterns"]:
|
328
379
|
matches = re.finditer(pattern, content, re.IGNORECASE | re.MULTILINE)
|
329
380
|
for match in matches:
|
330
|
-
line_num = content[:match.start()].count("\n") + 1
|
331
|
-
|
332
|
-
issues.append(
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
381
|
+
line_num = content[: match.start()].count("\n") + 1
|
382
|
+
|
383
|
+
issues.append(
|
384
|
+
{
|
385
|
+
"type": f"config_{issue_type}",
|
386
|
+
"severity": issue_info["severity"],
|
387
|
+
"description": issue_info["description"],
|
388
|
+
"file": str(file_path),
|
389
|
+
"line": line_num,
|
390
|
+
"code": match.group(0),
|
391
|
+
}
|
392
|
+
)
|
340
393
|
|
341
394
|
return issues
|
342
395
|
|
343
|
-
def _analyze_python_security(
|
396
|
+
def _analyze_python_security(
|
397
|
+
self, content: str, file_path: Path
|
398
|
+
) -> List[Dict[str, Any]]:
|
344
399
|
"""Perform Python-specific security analysis."""
|
345
400
|
issues = []
|
346
401
|
|
@@ -362,33 +417,41 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
362
417
|
if isinstance(node.func, ast.Name):
|
363
418
|
func_name = node.func.id
|
364
419
|
if func_name in dangerous_functions:
|
365
|
-
issues.append(
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
420
|
+
issues.append(
|
421
|
+
{
|
422
|
+
"type": "dangerous_function",
|
423
|
+
"severity": dangerous_functions[func_name],
|
424
|
+
"description": f"Use of dangerous function: {func_name}",
|
425
|
+
"file": str(file_path),
|
426
|
+
"line": node.lineno,
|
427
|
+
"code": func_name,
|
428
|
+
}
|
429
|
+
)
|
373
430
|
|
374
431
|
# Check for subprocess with shell=True
|
375
432
|
elif isinstance(node.func, ast.Attribute):
|
376
|
-
if (
|
377
|
-
node.func.value
|
378
|
-
node.func.
|
433
|
+
if (
|
434
|
+
hasattr(node.func.value, "id")
|
435
|
+
and node.func.value.id == "subprocess"
|
436
|
+
and node.func.attr in ["run", "call", "Popen"]
|
437
|
+
):
|
379
438
|
|
380
439
|
for keyword in node.keywords:
|
381
|
-
if
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
440
|
+
if (
|
441
|
+
keyword.arg == "shell"
|
442
|
+
and isinstance(keyword.value, ast.Constant)
|
443
|
+
and keyword.value.value is True
|
444
|
+
):
|
445
|
+
issues.append(
|
446
|
+
{
|
447
|
+
"type": "shell_injection",
|
448
|
+
"severity": "critical",
|
449
|
+
"description": "subprocess with shell=True is vulnerable to injection",
|
450
|
+
"file": str(file_path),
|
451
|
+
"line": node.lineno,
|
452
|
+
"code": "subprocess with shell=True",
|
453
|
+
}
|
454
|
+
)
|
392
455
|
|
393
456
|
self.generic_visit(node)
|
394
457
|
|
@@ -401,24 +464,26 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
401
464
|
|
402
465
|
return issues
|
403
466
|
|
404
|
-
def _analyze_javascript_security(
|
467
|
+
def _analyze_javascript_security(
|
468
|
+
self, content: str, file_path: Path
|
469
|
+
) -> List[Dict[str, Any]]:
|
405
470
|
"""Perform JavaScript-specific security analysis."""
|
406
471
|
issues = []
|
407
472
|
|
408
473
|
# Check for dangerous JavaScript patterns
|
409
474
|
js_patterns = {
|
410
475
|
"eval_usage": {
|
411
|
-
"pattern": r
|
476
|
+
"pattern": r"\beval\s*\(",
|
412
477
|
"severity": "critical",
|
413
478
|
"description": "Use of eval() is dangerous and should be avoided",
|
414
479
|
},
|
415
480
|
"innerhtml": {
|
416
|
-
"pattern": r
|
481
|
+
"pattern": r"\.innerHTML\s*=",
|
417
482
|
"severity": "high",
|
418
483
|
"description": "Direct innerHTML assignment can lead to XSS",
|
419
484
|
},
|
420
485
|
"document_write": {
|
421
|
-
"pattern": r
|
486
|
+
"pattern": r"document\.write\s*\(",
|
422
487
|
"severity": "medium",
|
423
488
|
"description": "document.write() can be dangerous with user input",
|
424
489
|
},
|
@@ -432,16 +497,18 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
432
497
|
for issue_type, issue_info in js_patterns.items():
|
433
498
|
matches = re.finditer(issue_info["pattern"], content, re.IGNORECASE)
|
434
499
|
for match in matches:
|
435
|
-
line_num = content[:match.start()].count("\n") + 1
|
500
|
+
line_num = content[: match.start()].count("\n") + 1
|
436
501
|
|
437
|
-
issues.append(
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
502
|
+
issues.append(
|
503
|
+
{
|
504
|
+
"type": f"js_{issue_type}",
|
505
|
+
"severity": issue_info["severity"],
|
506
|
+
"description": issue_info["description"],
|
507
|
+
"file": str(file_path),
|
508
|
+
"line": line_num,
|
509
|
+
"code": match.group(0),
|
510
|
+
}
|
511
|
+
)
|
445
512
|
|
446
513
|
return issues
|
447
514
|
|
@@ -492,7 +559,9 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
492
559
|
# Normalize and cap at 100
|
493
560
|
return min(100.0, round(weighted_score / max(results["files_analyzed"], 1), 2))
|
494
561
|
|
495
|
-
def _generate_summary(
|
562
|
+
def _generate_summary(
|
563
|
+
self, vulnerabilities: List[Dict[str, Any]]
|
564
|
+
) -> Dict[str, Any]:
|
496
565
|
"""Generate summary of security findings."""
|
497
566
|
summary = {
|
498
567
|
"total": len(vulnerabilities),
|
@@ -504,7 +573,9 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
504
573
|
for vuln in vulnerabilities:
|
505
574
|
# Count by severity
|
506
575
|
severity = vuln.get("severity", "unknown")
|
507
|
-
summary["by_severity"][severity] =
|
576
|
+
summary["by_severity"][severity] = (
|
577
|
+
summary["by_severity"].get(severity, 0) + 1
|
578
|
+
)
|
508
579
|
|
509
580
|
# Count by type
|
510
581
|
vuln_type = vuln.get("type", "unknown")
|
@@ -529,9 +600,7 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
529
600
|
)
|
530
601
|
|
531
602
|
if vuln_by_severity.get("high", 0) > 0:
|
532
|
-
recommendations.append(
|
533
|
-
"Prioritize fixing high-severity vulnerabilities"
|
534
|
-
)
|
603
|
+
recommendations.append("Prioritize fixing high-severity vulnerabilities")
|
535
604
|
|
536
605
|
# Type-specific recommendations
|
537
606
|
if results["files"]:
|
@@ -566,7 +635,9 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
566
635
|
)
|
567
636
|
|
568
637
|
if not recommendations:
|
569
|
-
recommendations.append(
|
638
|
+
recommendations.append(
|
639
|
+
"No critical security issues found. Continue with regular security audits."
|
640
|
+
)
|
570
641
|
|
571
642
|
return recommendations
|
572
643
|
|
@@ -578,10 +649,12 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
578
649
|
return metrics
|
579
650
|
|
580
651
|
if analysis_result.get("type") == "file":
|
581
|
-
metrics.update(
|
582
|
-
|
583
|
-
|
584
|
-
|
652
|
+
metrics.update(
|
653
|
+
{
|
654
|
+
"vulnerabilities": len(analysis_result.get("vulnerabilities", [])),
|
655
|
+
"risk_score": analysis_result.get("risk_score", 0),
|
656
|
+
}
|
657
|
+
)
|
585
658
|
|
586
659
|
# Count by severity
|
587
660
|
for vuln in analysis_result.get("vulnerabilities", []):
|
@@ -590,14 +663,20 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
590
663
|
metrics[key] = metrics.get(key, 0) + 1
|
591
664
|
|
592
665
|
elif analysis_result.get("type") == "directory":
|
593
|
-
metrics.update(
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
666
|
+
metrics.update(
|
667
|
+
{
|
668
|
+
"files_analyzed": analysis_result.get("files_analyzed", 0),
|
669
|
+
"total_vulnerabilities": analysis_result.get(
|
670
|
+
"total_vulnerabilities", 0
|
671
|
+
),
|
672
|
+
"risk_score": analysis_result.get("risk_score", 0),
|
673
|
+
}
|
674
|
+
)
|
598
675
|
|
599
676
|
# Add severity breakdown
|
600
|
-
for severity, count in analysis_result.get(
|
677
|
+
for severity, count in analysis_result.get(
|
678
|
+
"vulnerabilities_by_severity", {}
|
679
|
+
).items():
|
601
680
|
metrics[f"severity_{severity}"] = count
|
602
681
|
|
603
682
|
return metrics
|
@@ -624,7 +703,12 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
624
703
|
baseline_metrics = self.extract_metrics(baseline)
|
625
704
|
current_metrics = self.extract_metrics(current)
|
626
705
|
|
627
|
-
for key in [
|
706
|
+
for key in [
|
707
|
+
"severity_critical",
|
708
|
+
"severity_high",
|
709
|
+
"severity_medium",
|
710
|
+
"severity_low",
|
711
|
+
]:
|
628
712
|
baseline_count = baseline_metrics.get(key, 0)
|
629
713
|
current_count = current_metrics.get(key, 0)
|
630
714
|
|
@@ -658,4 +742,4 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
658
742
|
f"Vulnerabilities increased from {total_baseline} to {total_current}"
|
659
743
|
)
|
660
744
|
|
661
|
-
return comparison
|
745
|
+
return comparison
|