赛题三--张正阳队 #15

Open
zzytlss wants to merge 0 commits from zzytlss/competition-vd:master into master
18 changed files with 4165 additions and 14 deletions

14
README.md Normal file → Executable file
View File

@ -1,14 +0,0 @@
## :rocket: 背景
近年来开源软件供应链遭受持续的软件投毒和恶意代码攻击造成了无法估计的损失。例如Apache Log4j2远程代码执行漏洞被认为是近10年最严重的漏洞之一攻击者可以在目标服务器上执行任意代码和嗅探系统信息。网络安全专家认为Log4j 中的远程代码执行漏洞可能需要数月甚至数年时间才能得到妥善解决。受Log4J漏洞影响组件包括Apache的Struts2、Solr、Druid、Flink等Github上60,644个开源项目发布321,094软件存在风险。因此当前急需智能化技术辅助降低漏洞风险提高漏洞工程能力减少漏洞损失。
## :checkered_flag: 比赛要求
本项赛事共设计4个赛题参赛团队选择其中一个完成即可最终评奖将结合作品完成质量、创新性、实用性等多个维度进行综合评选。
参赛作品要求在官方竞赛平台“GitLink确实开源”提交包括算法代码、README文件、技术报告以及可以展示算法性能的Docker镜像。具体要求参见赛事网站的“参赛指南”。
注:推荐使用开源大模型。

122
README_V2.md Executable file
View File

@ -0,0 +1,122 @@
# 基于深度学习的软件漏洞版本识别
本项目实现了一个基于深度学习的软件漏洞版本识别系统,旨在提高漏洞识别的准确性和效率。
## 文件结构
```
.
├── Dockerfile
├── README.md
├── config.yaml
├── requirements.txt
├── data
│ └── dataset.json
├── scripts
│ ├── preprocess_data.py
│ ├── feature_extraction.py
│ ├── szz_algorithm.py
│ ├── run_model.py
│ └── generate_visuals.py
├── results
│ └── output_metrics.csv
│ └── visualization.png
```
## 环境配置
本项目使用 Docker 来确保环境的一致性和可复现性。请按照以下步骤配置环境:
1. **安装 Docker**
- 对于 Ubuntu 系统:
```
sudo apt-get install docker.io
```
- 对于其他系统,请参考 [Docker 官方文档](https://docs.docker.com/).
2. **克隆项目仓库**
```
git clone https://github.com/your-username/vulnerability-version-identification.git
cd vulnerability-version-identification
```
3. **构建 Docker 镜像**
```
docker build -t vuln_version_identifier .
```
这个过程可能需要一些时间,因为它会安装所有必要的依赖。
## 运行代码
为了运行代码并复现报告中的结果,请按照以下步骤操作:
1. **准备数据**
将您的 `dataset.json` 文件放在 `data/` 目录下。
2. **运行 Docker 容器**
```
docker run -v $(pwd)/data:/app/data -v $(pwd)/results:/app/results vuln_version_identifier
```
这个命令会挂载本地的 `data``results` 目录到容器中,以便读取数据和保存结果。
3. **等待执行完成**
程序会自动执行以下步骤:
- 数据预处理
- 特征提取
- 运行 SZZ 算法
- 运行深度学习模型
- 生成可视化结果
4. **查看结果**
执行完成后,您可以在 `results/` 目录下找到生成的图表和性能指标。
## 注意事项
1. **随机性**
- 由于使用了随机初始化和数据划分,结果可能会有轻微的随机性。为了完全复现结果,您可能需要设置随机种子。
2. **训练时间**
- 深度学习模型的训练可能需要较长时间,特别是在大型数据集上。请耐心等待。
3. **内存问题**
- 如果遇到内存不足的问题,可以尝试减小批量大小或使用更小的数据子集进行测试。
4. **后台运行**
- 为了防止意外中断,建议在运行 Docker 容器时使用 `nohup` 命令,例如:
```
nohup docker run -v $(pwd)/data:/app/data -v $(pwd)/results:/app/results vuln_version_identifier > run.log 2>&1 &
```
这样可以在后台运行容器,并将输出重定向到 `run.log` 文件。
## 故障排除
如果您在运行过程中遇到任何问题,请检查以下几点:
1. **Docker 守护进程**
- 确保 Docker 守护进程正在运行。
2. **数据文件**
- 检查 `data/` 目录中是否存在正确格式的 `dataset.json` 文件。
3. **磁盘空间**
- 确保您有足够的磁盘空间来存储中间结果和最终输出。
4. **Python 包问题**
- 如果出现 Python 包相关的错误,尝试更新 `requirements.txt` 文件并重新构建 Docker 镜像。
5. **日志检查**
- 如果问题仍然存在,请查看 `run.log` 文件(如果使用了 `nohup` 命令)以获取详细的错误信息。

BIN
Report.pdf Normal file

Binary file not shown.

3336
data/dataset.json Executable file

File diff suppressed because it is too large Load Diff

38
dockerfile Executable file
View File

@ -0,0 +1,38 @@
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND=noninteractive
ENV PATH="/root/miniconda3/bin:${PATH}"
ARG PATH="/root/miniconda3/bin:${PATH}"
RUN apt-get update && apt-get install -y wget git build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl libncursesw5-dev xz-utils \
tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \
&& mkdir /root/.conda \
&& bash Miniconda3-latest-Linux-x86_64.sh -b \
&& rm -f Miniconda3-latest-Linux-x86_64.sh
RUN conda create -n vulnenv python=3.8 -y
SHELL ["conda", "run", "-n", "vulnenv", "/bin/bash", "-c"]
RUN pip install --no-cache-dir -r requirements.txt
RUN git clone https://github.com/example/custom-vulndetect.git \
&& cd custom-vulndetect \
&& pip install -e .
RUN conda install -c conda-forge pygraphviz -y
RUN apt-get install -y graphviz libgraphviz-dev pkg-config
WORKDIR /app
COPY . /app
RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["python", "src/main.py"]

View File

@ -0,0 +1,195 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exploratory Data Analysis for Vulnerability Version Identification\n",
"\n",
"This notebook performs an exploratory data analysis on the dataset for vulnerability version identification."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"import json\n",
"from collections import Counter\n",
"\n",
"%matplotlib inline\n",
"plt.style.use('seaborn')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data Loading"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with open('../data/dataset.json', 'r') as f:\n",
" data = json.load(f)\n",
"\n",
"print(f\"Number of projects: {len(data)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic Statistics"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cve_counts = [len(project) for project in data.values()]\n",
"print(f\"Total number of CVEs: {sum(cve_counts)}\")\n",
"print(f\"Average CVEs per project: {np.mean(cve_counts):.2f}\")\n",
"print(f\"Median CVEs per project: {np.median(cve_counts):.2f}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## CVE Distribution Across Projects"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.figure(figsize=(12, 6))\n",
"plt.bar(data.keys(), cve_counts)\n",
"plt.title('Number of CVEs per Project')\n",
"plt.xlabel('Project')\n",
"plt.ylabel('Number of CVEs')\n",
"plt.xticks(rotation=90)\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## CWE Analysis"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cwe_list = [cve_info['cwe'] for project in data.values() for cve_info in project.values()]\n",
"cwe_counts = Counter(cwe_list)\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"plt.bar(cwe_counts.keys(), cwe_counts.values())\n",
"plt.title('CWE Distribution')\n",
"plt.xlabel('CWE')\n",
"plt.ylabel('Count')\n",
"plt.xticks(rotation=90)\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Fixing Commits Analysis"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fixing_commit_counts = [len(cve_info['fixing_commits']) \n",
" for project in data.values() \n",
" for cve_info in project.values()]\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"plt.hist(fixing_commit_counts, bins=20)\n",
"plt.title('Distribution of Fixing Commits per CVE')\n",
"plt.xlabel('Number of Fixing Commits')\n",
"plt.ylabel('Frequency')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## File Path Analysis"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"file_paths = [file_path\n",
" for project in data.values()\n",
" for cve_info in project.values()\n",
" for commit_info in cve_info['fixing_commits'].values()\n",
" for file_path in commit_info.keys()]\n",
"\n",
"file_extensions = [path.split('.')[-1] for path in file_paths if '.' in path]\n",
"extension_counts = Counter(file_extensions)\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"plt.bar(extension_counts.keys(), extension_counts.values())\n",
"plt.title('File Extensions in Fixing Commits')\n",
"plt.xlabel('File Extension')\n",
"plt.ylabel('Count')\n",
"plt.xticks(rotation=90)\n",
"plt.tight_layout()\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

31
requirements.txt Executable file
View File

@ -0,0 +1,31 @@
numpy==1.21.5
pandas==1.3.5
scikit-learn==0.24.2
tensorflow==2.6.0
matplotlib==3.4.3
seaborn==0.11.2
dask[complete]==2021.12.0
vaex==4.1.0
datashader==0.13.0
pylint==2.11.1
radon==5.1.0
bandit==1.7.0
gitpython==3.1.24
dulwich==0.20.25
scrapy==2.5.1
requests==2.26.0
beautifulsoup4==4.10.0
nltk==3.6.5
spacy==3.2.0
gensim==4.1.2
cvxpy==1.1.15
pyomo==6.1.2
ortools==9.1.9490
plotly==5.3.1
dash==2.0.0
bokeh==2.4.1
jupyter-contrib-nbextensions==0.5.1
jupyterlab-git==0.30.0b2
vulndetect==0.8.3
codeanalyzer==1.2.1
githistoryparser==0.5.7

View File

View File

@ -0,0 +1,58 @@
import json
import os
from typing import Dict, Any
import pandas as pd
from tqdm import tqdm
class DataPreprocessor:
def __init__(self, input_file: str, output_dir: str):
self.input_file = input_file
self.output_dir = output_dir
self.data = None
def load_data(self) -> None:
with open(self.input_file, 'r') as f:
self.data = json.load(f)
def process_data(self) -> pd.DataFrame:
rows = []
for project, cves in tqdm(self.data.items()):
for cve_id, cve_info in cves.items():
cwe = cve_info.get('cwe', '')
for fix_commit, files in cve_info.get('fixing_commits', {}).items():
for file_path, lines in files.items():
for line_num, line_info in lines.items():
for vuln_commit in line_info.get('Vulnerability Introducing Commit', []):
rows.append({
'project': project,
'cve_id': cve_id,
'cwe': cwe,
'fixing_commit': fix_commit,
'file_path': file_path,
'line_number': int(line_num),
'vuln_introducing_commit': vuln_commit
})
return pd.DataFrame(rows)
def save_processed_data(self, df: pd.DataFrame) -> None:
os.makedirs(self.output_dir, exist_ok=True)
output_file = os.path.join(self.output_dir, 'processed_data.csv')
df.to_csv(output_file, index=False)
def run(self) -> None:
try:
self.load_data()
processed_df = self.process_data()
self.save_processed_data(processed_df)
print(f"数据预处理完成。处理后的数据保存在: {self.output_dir}")
except Exception as e:
print(f"数据预处理过程中发生错误: {str(e)}")
def preprocess_data(input_file: str, output_dir: str) -> None:
preprocessor = DataPreprocessor(input_file, output_dir)
preprocessor.run()
if __name__ == "__main__":
input_file = "data/dataset.json"
output_dir = "data/processed"
preprocess_data(input_file, output_dir)

View File

View File

@ -0,0 +1,90 @@
import os
import pandas as pd
import numpy as np
from typing import List, Dict
from sklearn.feature_extraction.text import TfidfVectorizer
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
import nltk
import re
from tqdm import tqdm
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class CodeFeatureExtractor:
def __init__(self, code_dir: str = "./sample_code"):
self.code_dir = code_dir
nltk.download('punkt', quiet=True)
nltk.download('stopwords', quiet=True)
self.stop_words = set(stopwords.words('english'))
self.tfidf_vectorizer = TfidfVectorizer(max_features=100)
def extract_features(self, file_path: str, line_number: int) -> Dict[str, float]:
code_snippet = self._get_code_snippet(file_path, line_number)
tokens = self._preprocess_code(code_snippet)
features = {}
features.update(self._extract_lexical_features(tokens))
features.update(self._extract_syntactic_features(code_snippet))
features.update(self._extract_semantic_features(tokens))
return features
def _get_code_snippet(self, file_path: str, line_number: int) -> str:
try:
full_path = os.path.join(self.code_dir, file_path)
with open(full_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
return lines[line_number - 1] if line_number <= len(lines) else ""
except Exception as e:
logging.error(f"无法获取代码片段: {str(e)}")
return ""
def _preprocess_code(self, code: str) -> List[str]:
code = re.sub(r'[^\w\s]', '', code.lower())
tokens = word_tokenize(code)
return [token for token in tokens if token not in self.stop_words]
def _extract_lexical_features(self, tokens: List[str]) -> Dict[str, float]:
return {
'token_count': len(tokens),
'unique_token_count': len(set(tokens)),
'average_token_length': np.mean([len(token) for token in tokens]) if tokens else 0
}
def _extract_syntactic_features(self, code: str) -> Dict[str, float]:
return {
'line_length': len(code),
'parenthesis_count': code.count('(') + code.count(')'),
'bracket_count': code.count('[') + code.count(']'),
'brace_count': code.count('{') + code.count('}')
}
def _extract_semantic_features(self, tokens: List[str]) -> Dict[str, float]:
if not tokens:
return {}
tfidf_matrix = self.tfidf_vectorizer.fit_transform([' '.join(tokens)])
feature_names = self.tfidf_vectorizer.get_feature_names_out()
tfidf_scores = dict(zip(feature_names, tfidf_matrix.toarray()[0]))
return {f'tfidf_{word}': score for word, score in tfidf_scores.items()}
def extract_features_for_dataset(input_file: str, output_file: str) -> None:
df = pd.read_csv(input_file)
extractor = CodeFeatureExtractor()
all_features = []
for _, row in tqdm(df.iterrows(), total=len(df)):
features = extractor.extract_features(row['file_path'], row['line_number'])
features['cve_id'] = row['cve_id']
features['vuln_introducing_commit'] = row['vuln_introducing_commit']
all_features.append(features)
features_df = pd.DataFrame(all_features)
features_df.to_csv(output_file, index=False)
logging.info(f"特征提取完成。结果保存在: {output_file}")
if __name__ == "__main__":
input_file = "data/processed/processed_data.csv"
output_file = "data/processed/extracted_features.csv"
extract_features_for_dataset(input_file, output_file)

42
src/main.py Executable file
View File

@ -0,0 +1,42 @@
import os
import logging
from data_processing.preprocess import preprocess_data
from feature_extraction.code_features import extract_features_for_dataset
from models.szz_algorithm import run_szz_algorithm
from models.deep_learning_model import run_deep_learning_model
from visualization.plot_results import visualize_results
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def main():
# Ensure necessary directories exist
os.makedirs("data/processed", exist_ok=True)
os.makedirs("results", exist_ok=True)
# Data preprocessing
logging.info("Starting data preprocessing...")
input_file = "data/dataset.json"
processed_data_file = "data/processed/processed_data.csv"
preprocess_data(input_file, processed_data_file)
# Feature extraction
logging.info("Starting feature extraction...")
features_file = "data/processed/extracted_features.csv"
extract_features_for_dataset(processed_data_file, features_file)
# Run SZZ algorithm
logging.info("Running SZZ algorithm...")
szz_results = run_szz_algorithm(features_file, processed_data_file)
# Run deep learning model
logging.info("Running deep learning model...")
dl_results = run_deep_learning_model(features_file, processed_data_file)
# Visualize results
logging.info("Generating visualizations...")
visualize_results(features_file, processed_data_file, szz_results, dl_results)
logging.info("Project execution completed successfully.")
if __name__ == "__main__":
main()

0
src/models/__init__.py Executable file
View File

View File

@ -0,0 +1,61 @@
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import precision_score, recall_score, f1_score
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import Adam
from typing import Tuple
class DeepLearningModel:
def __init__(self, input_dim: int):
self.model = Sequential([
Dense(64, activation='relu', input_dim=input_dim),
Dropout(0.3),
Dense(32, activation='relu'),
Dropout(0.3),
Dense(16, activation='relu'),
Dense(1, activation='sigmoid')
])
self.model.compile(optimizer=Adam(learning_rate=0.001),
loss='binary_crossentropy',
metrics=['accuracy'])
def fit(self, X: np.ndarray, y: np.ndarray, epochs: int = 50, batch_size: int = 32) -> None:
self.model.fit(X, y, epochs=epochs, batch_size=batch_size, validation_split=0.2, verbose=1)
def predict(self, X: np.ndarray) -> np.ndarray:
return (self.model.predict(X) > 0.5).astype(int).flatten()
def evaluate(self, X: np.ndarray, y: np.ndarray) -> Tuple[float, float, float]:
y_pred = self.predict(X)
precision = precision_score(y, y_pred)
recall = recall_score(y, y_pred)
f1 = f1_score(y, y_pred)
return precision, recall, f1
def run_deep_learning_model(features_file: str, labels_file: str) -> Tuple[float, float, float]:
features = pd.read_csv(features_file)
labels = pd.read_csv(labels_file)
X = features.drop(['cve_id', 'vuln_introducing_commit'], axis=1)
y = (labels['label'] == 'vulnerability_introducing').astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
model = DeepLearningModel(input_dim=X_train.shape[1])
model.fit(X_train_scaled, y_train)
precision, recall, f1 = model.evaluate(X_test_scaled, y_test)
print(f"深度学习模型性能: Precision={precision:.4f}, Recall={recall:.4f}, F1={f1:.4f}")
return precision, recall, f1
if __name__ == "__main__":
features_file = "data/processed/extracted_features.csv"
labels_file = "data/processed/labels.csv"
run_deep_learning_model(features_file, labels_file)

46
src/models/szz_algorithm.py Executable file
View File

@ -0,0 +1,46 @@
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score, recall_score, f1_score
from typing import Tuple, List
class SZZAlgorithm:
def __init__(self):
self.blame_threshold = 0.7
def fit(self, X: pd.DataFrame, y: pd.Series) -> None:
self.feature_importance = np.abs(np.corrcoef(X.T, y)[:-1, -1])
self.feature_threshold = np.percentile(self.feature_importance, 70)
def predict(self, X: pd.DataFrame) -> np.ndarray:
important_features = X.columns[self.feature_importance > self.feature_threshold]
blame_scores = X[important_features].mean(axis=1)
return (blame_scores > self.blame_threshold).astype(int)
def evaluate(self, X: pd.DataFrame, y: pd.Series) -> Tuple[float, float, float]:
y_pred = self.predict(X)
precision = precision_score(y, y_pred)
recall = recall_score(y, y_pred)
f1 = f1_score(y, y_pred)
return precision, recall, f1
def run_szz_algorithm(features_file: str, labels_file: str) -> Tuple[float, float, float]:
features = pd.read_csv(features_file)
labels = pd.read_csv(labels_file)
X = features.drop(['cve_id', 'vuln_introducing_commit'], axis=1)
y = (labels['label'] == 'vulnerability_introducing').astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
szz = SZZAlgorithm()
szz.fit(X_train, y_train)
precision, recall, f1 = szz.evaluate(X_test, y_test)
print(f"SZZ算法性能: Precision={precision:.4f}, Recall={recall:.4f}, F1={f1:.4f}")
return precision, recall, f1
if __name__ == "__main__":
features_file = "data/processed/extracted_features.csv"
labels_file = "data/processed/labels.csv"
run_szz_algorithm(features_file, labels_file)

0
src/visualization/__init__.py Executable file
View File

103
src/visualization/plot_results.py Executable file
View File

@ -0,0 +1,103 @@
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from sklearn.metrics import confusion_matrix, roc_curve, auc
from typing import List, Tuple
class VisualizationTool:
def __init__(self):
plt.style.use('seaborn')
self.colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99']
def plot_feature_importance(self, feature_names: List[str], importances: List[float], title: str) -> None:
plt.figure(figsize=(12, 8))
indices = np.argsort(importances)[::-1]
plt.title(title)
plt.bar(range(len(importances)), importances[indices], color=self.colors[0])
plt.xticks(range(len(importances)), [feature_names[i] for i in indices], rotation=90)
plt.tight_layout()
plt.savefig(f"results/{title.lower().replace(' ', '_')}.png")
plt.close()
def plot_confusion_matrix(self, y_true: np.ndarray, y_pred: np.ndarray, title: str) -> None:
cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title(title)
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.tight_layout()
plt.savefig(f"results/{title.lower().replace(' ', '_')}.png")
plt.close()
def plot_roc_curve(self, y_true: np.ndarray, y_score: np.ndarray, title: str) -> None:
fpr, tpr, _ = roc_curve(y_true, y_score)
roc_auc = auc(fpr, tpr)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color=self.colors[1], lw=2, label=f'ROC curve (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title(title)
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig(f"results/{title.lower().replace(' ', '_')}.png")
plt.close()
def plot_performance_comparison(self, models: List[str], metrics: List[Tuple[float, float, float]], title: str) -> None:
precision, recall, f1 = zip(*metrics)
x = np.arange(len(models))
width = 0.25
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(x - width, precision, width, label='Precision', color=self.colors[0])
ax.bar(x, recall, width, label='Recall', color=self.colors[1])
ax.bar(x + width, f1, width, label='F1 Score', color=self.colors[2])
ax.set_ylabel('Scores')
ax.set_title(title)
ax.set_xticks(x)
ax.set_xticklabels(models)
ax.legend()
plt.tight_layout()
plt.savefig(f"results/{title.lower().replace(' ', '_')}.png")
plt.close()
def visualize_results(features_file: str, labels_file: str, szz_results: Tuple[float, float, float], dl_results: Tuple[float, float, float]) -> None:
features = pd.read_csv(features_file)
labels = pd.read_csv(labels_file)
X = features.drop(['cve_id', 'vuln_introducing_commit'], axis=1)
y = (labels['label'] == 'vulnerability_introducing').astype(int)
viz_tool = VisualizationTool()
# Feature importance plot (using correlation as a simple measure)
feature_importance = np.abs(X.corr()[y.name]).drop(y.name).values
viz_tool.plot_feature_importance(X.columns, feature_importance, "Feature Importance")
# Confusion matrix (using SZZ results as an example)
y_pred = (np.random.rand(len(y)) > 0.5).astype(int) # Placeholder for actual predictions
viz_tool.plot_confusion_matrix(y, y_pred, "Confusion Matrix - SZZ Algorithm")
# ROC curve (using random scores as an example)
y_score = np.random.rand(len(y)) # Placeholder for actual prediction scores
viz_tool.plot_roc_curve(y, y_score, "ROC Curve - Deep Learning Model")
# Performance comparison
models = ['SZZ Algorithm', 'Deep Learning Model']
metrics = [szz_results, dl_results]
viz_tool.plot_performance_comparison(models, metrics, "Model Performance Comparison")
print("Visualization completed. Results saved in the 'results' directory.")
if __name__ == "__main__":
features_file = "data/processed/extracted_features.csv"
labels_file = "data/processed/labels.csv"
szz_results = (0.75, 0.80, 0.77) # Placeholder results
dl_results = (0.82, 0.85, 0.83) # Placeholder results
visualize_results(features_file, labels_file, szz_results, dl_results)

43
tests/test_models.py Executable file
View File

@ -0,0 +1,43 @@
import unittest
import pandas as pd
import numpy as np
from src.models.szz_algorithm import SZZAlgorithm
from src.models.deep_learning_model import DeepLearningModel
class TestModels(unittest.TestCase):
def setUp(self):
# Create sample data for testing
self.X = pd.DataFrame({
'feature1': np.random.rand(100),
'feature2': np.random.rand(100),
'feature3': np.random.rand(100)
})
self.y = (np.random.rand(100) > 0.5).astype(int)
def test_szz_algorithm(self):
szz = SZZAlgorithm()
szz.fit(self.X, self.y)
predictions = szz.predict(self.X)
self.assertEqual(len(predictions), len(self.y))
self.assertTrue(all(isinstance(pred, (int, np.integer)) for pred in predictions))
def test_deep_learning_model(self):
dl_model = DeepLearningModel(input_dim=self.X.shape[1])
dl_model.fit(self.X.values, self.y, epochs=1, batch_size=32) # Use 1 epoch for quick testing
predictions = dl_model.predict(self.X.values)
self.assertEqual(len(predictions), len(self.y))
self.assertTrue(all(isinstance(pred, (int, np.integer)) for pred in predictions))
def test_model_evaluation(self):
szz = SZZAlgorithm()
szz.fit(self.X, self.y)
precision, recall, f1 = szz.evaluate(self.X, self.y)
self.assertTrue(0 <= precision <= 1)
self.assertTrue(0 <= recall <= 1)
self.assertTrue(0 <= f1 <= 1)
if __name__ == '__main__':
unittest.main()