|
|
"""
|
|
|
JavaScript代码分析器
|
|
|
"""
|
|
|
import re
|
|
|
import os
|
|
|
from typing import List, Dict, Any
|
|
|
from .base_analyzer import BaseAnalyzer
|
|
|
|
|
|
class JavaScriptAnalyzer(BaseAnalyzer):
|
|
|
"""JavaScript代码分析器"""
|
|
|
|
|
|
def __init__(self):
|
|
|
super().__init__()
|
|
|
self.name = "JavaScript Analyzer"
|
|
|
self.version = "1.0.0"
|
|
|
self.supported_extensions = ["js", "jsx", "ts", "tsx"]
|
|
|
self.description = "JavaScript/TypeScript代码静态分析器"
|
|
|
|
|
|
async def analyze(self, project_path: str, config: Dict[str, Any] = None) -> List[Dict[str, Any]]:
|
|
|
"""分析JavaScript代码"""
|
|
|
vulnerabilities = []
|
|
|
|
|
|
# 获取所有JavaScript文件
|
|
|
js_files = self.get_project_files(project_path)
|
|
|
|
|
|
for file_path in js_files:
|
|
|
try:
|
|
|
# 读取文件内容
|
|
|
content = self.read_file_content(file_path)
|
|
|
if not content:
|
|
|
continue
|
|
|
|
|
|
lines = content.split('\n')
|
|
|
|
|
|
# 执行各种检查
|
|
|
vulnerabilities.extend(self._check_security_issues(lines, file_path))
|
|
|
vulnerabilities.extend(self._check_performance_issues(lines, file_path))
|
|
|
vulnerabilities.extend(self._check_maintainability_issues(lines, file_path))
|
|
|
|
|
|
except Exception as e:
|
|
|
# 分析错误
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS000",
|
|
|
message=f"分析错误: {str(e)}",
|
|
|
file_path=file_path,
|
|
|
severity="high",
|
|
|
category="reliability"
|
|
|
))
|
|
|
|
|
|
return vulnerabilities
|
|
|
|
|
|
def _check_security_issues(self, lines: List[str], file_path: str) -> List[Dict[str, Any]]:
|
|
|
"""检查安全问题"""
|
|
|
vulnerabilities = []
|
|
|
|
|
|
for i, line in enumerate(lines):
|
|
|
line_num = i + 1
|
|
|
line_stripped = line.strip()
|
|
|
|
|
|
# 检查eval使用
|
|
|
if 'eval(' in line_stripped:
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS101",
|
|
|
message="使用eval(),存在代码注入风险",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="critical",
|
|
|
category="security",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
# 检查innerHTML使用
|
|
|
elif 'innerHTML' in line_stripped and '=' in line_stripped:
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS102",
|
|
|
message="使用innerHTML,存在XSS风险",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="high",
|
|
|
category="security",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
# 检查document.write使用
|
|
|
elif 'document.write(' in line_stripped:
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS103",
|
|
|
message="使用document.write(),存在XSS风险",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="high",
|
|
|
category="security",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
# 检查console.log在生产环境中的使用
|
|
|
elif 'console.log(' in line_stripped:
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS104",
|
|
|
message="生产环境中不应使用console.log",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="low",
|
|
|
category="security",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
# 检查硬编码的敏感信息
|
|
|
elif re.search(r'password\s*[:=]\s*["\'][^"\']+["\']', line_stripped, re.IGNORECASE):
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS105",
|
|
|
message="代码中包含硬编码的密码",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="high",
|
|
|
category="security",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
return vulnerabilities
|
|
|
|
|
|
def _check_performance_issues(self, lines: List[str], file_path: str) -> List[Dict[str, Any]]:
|
|
|
"""检查性能问题"""
|
|
|
vulnerabilities = []
|
|
|
|
|
|
for i, line in enumerate(lines):
|
|
|
line_num = i + 1
|
|
|
line_stripped = line.strip()
|
|
|
|
|
|
# 检查DOM操作在循环中
|
|
|
if re.search(r'for\s*\([^)]*\)\s*\{', line_stripped):
|
|
|
# 查找循环体中的DOM操作
|
|
|
for j in range(i + 1, min(i + 20, len(lines))):
|
|
|
loop_line = lines[j].strip()
|
|
|
if loop_line == '}':
|
|
|
break
|
|
|
if any(dom_op in loop_line for dom_op in ['getElementById', 'querySelector', 'appendChild']):
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS201",
|
|
|
message="循环中进行DOM操作,影响性能",
|
|
|
file_path=file_path,
|
|
|
line_number=j + 1,
|
|
|
severity="medium",
|
|
|
category="performance",
|
|
|
code_snippet=loop_line
|
|
|
))
|
|
|
|
|
|
# 检查未使用的变量声明
|
|
|
elif line_stripped.startswith('var ') or line_stripped.startswith('let ') or line_stripped.startswith('const '):
|
|
|
var_name = re.search(r'(var|let|const)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)', line_stripped)
|
|
|
if var_name:
|
|
|
var_name = var_name.group(2)
|
|
|
# 检查变量是否在后续代码中使用
|
|
|
is_used = False
|
|
|
for k in range(i + 1, len(lines)):
|
|
|
if var_name in lines[k]:
|
|
|
is_used = True
|
|
|
break
|
|
|
|
|
|
if not is_used:
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS202",
|
|
|
message=f"未使用的变量: {var_name}",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="low",
|
|
|
category="performance",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
return vulnerabilities
|
|
|
|
|
|
def _check_maintainability_issues(self, lines: List[str], file_path: str) -> List[Dict[str, Any]]:
|
|
|
"""检查可维护性问题"""
|
|
|
vulnerabilities = []
|
|
|
|
|
|
for i, line in enumerate(lines):
|
|
|
line_num = i + 1
|
|
|
line_stripped = line.strip()
|
|
|
|
|
|
# 检查函数长度(简单检查)
|
|
|
if line_stripped.startswith('function ') or re.match(r'const\s+\w+\s*=\s*\([^)]*\)\s*=>', line_stripped):
|
|
|
# 计算函数体长度
|
|
|
brace_count = 0
|
|
|
function_start = i
|
|
|
for j in range(i, len(lines)):
|
|
|
line_content = lines[j]
|
|
|
brace_count += line_content.count('{')
|
|
|
brace_count -= line_content.count('}')
|
|
|
|
|
|
if brace_count > 0 and j > i:
|
|
|
continue
|
|
|
elif brace_count == 0 and j > i:
|
|
|
function_length = j - function_start
|
|
|
if function_length > 30:
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS301",
|
|
|
message="函数过长,建议拆分",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="medium",
|
|
|
category="maintainability",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
break
|
|
|
|
|
|
# 检查深度嵌套
|
|
|
elif line_stripped.endswith('{'):
|
|
|
indent_level = len(line) - len(line.lstrip())
|
|
|
if indent_level > 40: # 假设每层缩进4个空格
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS302",
|
|
|
message="代码嵌套过深,影响可读性",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="low",
|
|
|
category="maintainability",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
# 检查魔法数字
|
|
|
elif re.search(r'\b\d{3,}\b', line_stripped):
|
|
|
vulnerabilities.append(self.create_vulnerability(
|
|
|
rule_id="JS303",
|
|
|
message="存在魔法数字,建议使用常量",
|
|
|
file_path=file_path,
|
|
|
line_number=line_num,
|
|
|
severity="low",
|
|
|
category="maintainability",
|
|
|
code_snippet=line_stripped
|
|
|
))
|
|
|
|
|
|
return vulnerabilities
|