competition-vd/code/feature.py

385 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import datetime
import glob
import json
import os
import gc
import git
import logging
from tqdm import tqdm
import shutil
from collections import Counter
import math
from util import *
with open('../data/token_IDF.txt', 'r') as fp:
token_IDF = eval(fp.read())
def counter_to_dict(vunl_df_desc_token_counter):
i = 0
for item in vunl_df_desc_token_counter:
item = item.replace('Counter', '')
item = item.replace('(', '')
item = item.replace(')', '')
item = item.replace('{', '')
item = item.replace('}', '')
item = item.replace("'", '')
item = item.replace('"', '')
item = item.replace(' ', '')
# print(item)
# print(mess_df['mess_token_counter'][i])
# vuln_df['desc_token_counter'][i] = dict(item)
dic = {}
if item == '':
dic = {}
elif item.find(',') < 0:
key, value = item.split(':')
dic[key] = int(value)
else:
for pair in item.split(','):
key, value = pair.split(':')
dic[key] = int(value)
vunl_df_desc_token_counter[i] = dic
i += 1
return vunl_df_desc_token_counter
def weblinks_bug_issue_cve(weblinks, bug, issue, cve, row):
issue_cnt = len(issue)
web_cnt = len(weblinks)
bug_cnt = len(bug)
cve_cnt = len(cve)
return issue_cnt, web_cnt, bug_cnt, cve_cnt
def feature_time(committime, cvetime):
committime = datetime.datetime.strptime(committime, '%Y%m%d')
cvetime = datetime.datetime.strptime(cvetime, '%Y%m%d')
time_dis = abs((cvetime - committime).days)
return time_dis
def file_match_func(filepaths, funcs, desc):
files = [path.split('/')[-1] for path in filepaths]
file_match = As_in_B(files, desc)
filepath_match = As_in_B(filepaths, desc)
func_match = As_in_B(funcs, desc)
return file_match, filepath_match, func_match
def vuln_commit_token(reponame, commit, cwedesc_tokens, desc_tokens):
vuln_tokens = union_list(desc_tokens, cwedesc_tokens) # 合并列表
with open('../data/gitcommit/{}/{}'.format(reponame, commit), 'r') as fp:
commit_tokens = eval(fp.read())
commit_tokens_set = set(commit_tokens)
inter_token_total = inter_token(set(vuln_tokens), commit_tokens_set)
inter_token_total_cnt = len(inter_token_total)
inter_token_total_ratio = inter_token_total_cnt / len(vuln_tokens)
inter_token_cwe = inter_token(set(cwedesc_tokens), commit_tokens_set)
inter_token_cwe_cnt = len(inter_token_cwe)
inter_token_cwe_ratio = inter_token_cwe_cnt / (1 + len(cwedesc_tokens))
return vuln_tokens, commit_tokens, inter_token_total_cnt, inter_token_total_ratio, inter_token_cwe_cnt, inter_token_cwe_ratio
def get_vuln_idf(bug, links, cve, cves):
cve_match = 0
for item in cves:
if item.lower() in cve.lower():
cve_match = 1
break
bug_match = 0
for link in links:
if 'bug' in link or 'Bug' in link or 'bid' in link:
for item in bug:
if item in link:
bug_match = 1
break
return cve_match, bug_match
def get_vuln_loc(nvd_items, commit_items):
same_cnt = 0
commit_items = set(commit_items)
commit_items = list(commit_items)
for commit_item in commit_items:
for nvd_item in nvd_items:
if nvd_item == commit_item:
same_cnt += 1
break
same_ratio = same_cnt / (len(commit_items)+1)
unrelated_cnt = len(commit_items) - same_cnt
return same_cnt, same_ratio, unrelated_cnt
def get_vuln_type_relete(nvd_type, nvd_impact, commit_type, commit_impact, vuln_type_impact):# v_typ v_imp mes_typ mes_imp
l1, l2, l3 = 0, 0, 0
for nvd_item in nvd_type:
for commit_item in commit_type:
if nvd_item == commit_item:
l1 += 1
else:
l3 += 1
for nvd_item in nvd_type:
for commit_item in commit_impact:
if commit_item in vuln_type_impact.get(nvd_item):
l2 += 1
else:
l3 += 1
for commit_item in commit_type:
for nvd_item in nvd_impact:
if nvd_item in vuln_type_impact.get(commit_item):
l2 += 1
else:
l3 += 1
cnt = l1 + l2 + l3+1
return l1/cnt, l2/cnt, (l3+1)/cnt
# c1 # nvd
# c2 # code
def get_vuln_desc_text(c1, c2):
c3 = c1 and c2
same_token = c3.keys()
shared_num = len(same_token)
shared_ratio = shared_num / (len(c1.keys())+1)
c3_value = list(c3.values())
if len(c3_value) == 0:
c3_value = [0]
return shared_num, shared_ratio, max(c3_value), sum(c3_value), np.mean(c3_value), np.var(c3_value)
def cosine_similarity(x, y, norm=False):
assert len(x) == len(y), "len(x) != len(y)"
zero_list = [0] * len(x)
if x == zero_list or y == zero_list:
return float(1) if x == y else float(0)
res = np.array([[x[i] * y[i], x[i] * x[i], y[i] * y[i]]
for i in range(len(x))])
cos = sum(res[:, 0]) / (np.sqrt(sum(res[:, 1])) * np.sqrt(sum(res[:, 2])))
return 0.5 * cos + 0.5 if norm else cos # 归一化到[0, 1]区间内
def get_feature(row, commit_info): # row是Dataset_5000中1000行连续且repo相同的数据中的其中一行
commit = row['commit']
reponame = row['repo']
weblinks, bug, issue, cve, datetime, filepaths, funcs, addcnt, delcnt = commit_info[commit]
# [特征] CVE & Bug & Issue & Links 匹配
# issue_cnt, web_cnt, bug_cnt, cve_cnt, \
# web_match_nvd_links, issue_match_nvd_links, bug_match_nvd_links, cve_match \
# = weblinks_bug_issue_cve(weblinks, bug, issue, cve, row)
issue_cnt, web_cnt, bug_cnt, cve_cnt, \
= weblinks_bug_issue_cve(weblinks, bug, issue, cve, row)
# [特征] 时间差值
time_dis = feature_time(datetime, row['cvetime'])
# [特征] vuln 与 Commit tokens 重叠特征
vuln_tokens, commit_tokens, inter_token_total_cnt, inter_token_total_ratio, inter_token_cwe_cnt, inter_token_cwe_ratio \
= vuln_commit_token(reponame, commit, row['cwedesc'], row['desc_token'])
# [特征] vuln 与 commit 的TFIDF向量的余弦相似度值
c_vuln = Counter(vuln_tokens)
c_commit = Counter(commit_tokens)
len_vuln_tokens = len(vuln_tokens) + 1
len_commit_tokens = len(commit_tokens) + 1
vuln_tfidf = []
commit_tfidf = []
for token in token_IDF:
vuln_tfidf.append(c_vuln[token]/len_vuln_tokens * token_IDF[token])
commit_tfidf.append(
c_commit[token]/len_commit_tokens * token_IDF[token])
tfidf = cosine_similarity(vuln_tfidf, commit_tfidf)
# return addcnt, delcnt, issue_cnt, web_cnt, bug_cnt, cve_cnt, \
# web_match_nvd_links, issue_match_nvd_links, bug_match_nvd_links, cve_match, \
# time_dis, inter_token_cwe_cnt, inter_token_cwe_ratio, tfidf
return addcnt, delcnt, issue_cnt, web_cnt, bug_cnt, cve_cnt, time_dis, inter_token_cwe_cnt, inter_token_cwe_ratio, tfidf
dataset_df = pd.read_csv("../data/Dataset_150.csv")
dataset_df = reduce_mem_usage(dataset_df) # 内存优化
vuln_df = pd.read_csv('../data/vuln_data.csv')
vuln_df['desc_token'] = vuln_df['desc_token'].apply(eval)
counter_to_dict(vuln_df['desc_token_counter'])
vuln_df['links'] = vuln_df['links'].apply(eval)
vuln_df['cwedesc'] = vuln_df['cwedesc'].apply(eval)
vuln_df['cvetime'] = vuln_df['cvetime'].astype(str)
vuln_df['functions'] = vuln_df['functions'].apply(eval)
vuln_df['files'] = vuln_df['files'].apply(eval)
vuln_df['filepaths'] = vuln_df['filepaths'].apply(eval)
vuln_df['vuln_type'] = vuln_df['vuln_type'].apply(eval)
vuln_df['vuln_impact'] = vuln_df['vuln_impact'].apply(eval)
vuln_df = reduce_mem_usage(vuln_df)
mess_df = pd.read_csv("../data/mess_data.csv")
mess_df['mess_bugs'] = mess_df['mess_bugs'].apply(eval)
mess_df['mess_cves'] = mess_df['mess_cves'].apply(eval)
mess_df['mess_type'] = mess_df['mess_type'].apply(eval)
mess_df['mess_impact'] = mess_df['mess_impact'].apply(eval)
counter_to_dict(mess_df['mess_token_counter'])
mess_df = reduce_mem_usage(mess_df)
dataset_df = dataset_df.merge(vuln_df, how='left', on='cve')
dataset_df = dataset_df.merge(mess_df, how='left', on='commit')
del vuln_df # 删除
gc.collect() # 内存清理
# ----------------------vuln_type_impact-------------------
with open("../data/vuln_type_impact.json", 'r') as f:
vuln_type_impact = json.load(f)
repos = dataset_df.repo.unique() # 去除重复元素即得到repo的值域
for reponame in repos:
dirpath = 'tmp/' + reponame
# 生成tmp/reponame文件夹
if not os.path.exists(dirpath):
os.makedirs(dirpath)
tmp_df = dataset_df[dataset_df.repo == reponame] # reponame对应的所有行
gitpath = "../gitrepo/"
repo = git.Repo(gitpath+reponame)
commits = tmp_df.commit.unique() # 去除重复元素即得到commit的值域
commit_info = readfile('../data/commit_info/{}_commit_info'.format(reponame)) # 读取reponame_commit_info文件
total_cnt = tmp_df.shape[0] # 读取行数
each_cnt = 1000
epoch = int((total_cnt+each_cnt-1)/each_cnt)
logging.info('共有{}个epoch'.format(epoch))
t1 = time.time()
# batch process
# 生成tmp/reponame/xxxx.csv文件
for i in tqdm(range(epoch)):
if os.path.exists(dirpath+'/{:04}.csv'.format(i)):
continue
df = tmp_df.iloc[i * each_cnt: min((i + 1) * each_cnt, total_cnt)] # 从1000*i+1行开始的最多1000行数据
df["addcnt"], df["delcnt"], df["issue_cnt"], df["web_cnt"], df["bug_cnt"], df["cve_cnt"],\
df["time_dis"], df["inter_token_cwe_cnt"], df["inter_token_cwe_ratio"], df["vuln_commit_tfidf"] \
= zip(*df.apply(lambda row: get_feature(row, commit_info), axis=1)) # 对每一行使用get_feature
df['totalcnt'] = df["addcnt"] + df["delcnt"]
df.drop(['desc', 'links', 'cwedesc', 'cvetime'], axis=1, inplace=True)
df.drop(['mess_bugs', 'mess_cves', 'functions', 'filepaths',
'files', 'vuln_type', 'vuln_impact', 'mess_type', 'mess_impact',
'desc_token_counter', 'mess_token_counter'],
axis=1, inplace=True)
# 删除后特征 cve repo true_commit commit label desc_token desc_token_counter functions files filepaths vuln_type
# vuln_impact mess_bugs mess_cves mess_type mess_impact mess_token_counter
df.to_csv(dirpath+'/{:04}.csv'.format(i), index=False)
t2 = time.time()
logging.info('{}共耗时:{} min'.format(reponame, (t2 - t1) / 60))
gc.collect()
files = glob.glob(dirpath + '/*.csv') # 匹配tmp/reponame下的所有csv文件的路径
m = {}
for file in files:
idx = int(re.search('([0-9]+).csv', file).group(1)) # 将xxxx.csv文件的路径存入m[xxxx]
m[idx] = file
l = []
for i in range(epoch):
tmp = pd.read_csv(m[i])
l.append(tmp)
data_df = pd.concat(l) # 将上述处理好的csv文件合并
data_df = data_df.reset_index(drop=True)
shutil.rmtree(dirpath) # 删除reponame文件夹
logging.info("")
# print('当前时间为', time.strftime("%H:%M:%S"))
dirpath = 'tmp/' + reponame
if not os.path.exists(dirpath):
os.makedirs(dirpath)
# print(reponame+"正在处理")
code_df = pd.read_csv("../data/code_data/code_data_{}.csv".format(reponame))
# 字典化
code_df['code_files'] = code_df['code_files'] .apply(eval)
code_df['code_funcs'] = code_df['code_funcs'].apply(eval)
code_df['code_filepaths'] = code_df['code_filepaths'] .apply(eval)
# code_df['code_token_counter'] = code_df['code_token_counter'].apply(eval)
counter_to_dict(code_df['code_token_counter'])
tmp_df = dataset_df[dataset_df.repo == reponame]
tmp_df = tmp_df.merge(code_df, how='left', on='commit')
commits = tmp_df.commit.unique() # commit的值域
total_cnt = tmp_df.shape[0]
each_cnt = 2000
epoch = int((total_cnt+each_cnt-1)/each_cnt)
logging.info('共有{}个epoch'.format(epoch))
t1 = time.time()
for i in tqdm(range(epoch)):
if os.path.exists(dirpath+'/{:04}.csv'.format(i)):
continue
df = tmp_df.iloc[i * each_cnt: min((i + 1) * each_cnt, total_cnt)]
df['cve_match'], df['bug_match'] = zip(*df.apply(
lambda row: get_vuln_idf(row['mess_bugs'], row['links'], row['cve'], row['mess_cves']), axis=1))
df['filepath_same_cnt'], df['filepath_same_ratio'], df['filepath_unrelated_cnt'] = zip(*df.apply(
lambda row: get_vuln_loc(row['filepaths'], row['code_filepaths']), axis=1))
df['func_same_cnt'], df['func_same_ratio'], df['func_unrelated_cnt'] = zip(*df.apply(
lambda row: get_vuln_loc(row['functions'], row['code_funcs']), axis=1))
df['file_same_cnt'], df['file_same_ratio'], df['file_unrelated_cnt'] = zip(*df.apply(
lambda row: get_vuln_loc(row['files'], row['code_files']), axis=1))
df['vuln_type_1'], df['vuln_type_2'], df['vuln_type_3'] = zip(*df.apply(
lambda row: get_vuln_type_relete(row['vuln_type'], row['vuln_impact'], row['mess_type'], row['mess_impact'], vuln_type_impact), axis=1))
df['mess_shared_num'], df['mess_shared_ratio'], df['mess_max'], df['mess_sum'], df['mess_mean'], df['mess_var'] = zip(*df.apply(
lambda row: get_vuln_desc_text(row['desc_token_counter'], row['mess_token_counter']), axis=1))
df['code_shared_num'], df['code_shared_ratio'], df['code_max'], df['code_sum'], df['code_mean'], df['code_var'] = zip(*df.apply(
lambda row: get_vuln_desc_text(row['desc_token_counter'], row['code_token_counter']), axis=1))
df.drop(['desc', 'cwedesc', 'cvetime'], axis=1, inplace=True)
df.drop(['mess_bugs', 'links', 'mess_cves', 'functions', 'code_funcs', 'filepaths', 'code_filepaths',
'files', 'code_files', 'vuln_type', 'vuln_impact', 'mess_type', 'mess_impact',
'desc_token_counter', 'mess_token_counter', 'code_token_counter'],
axis=1, inplace=True)
df.to_csv(dirpath+'/{:04}.csv'.format(i), index=False)
t2 = time.time()
logging.info('{}共耗时:{} min'.format(reponame, (t2 - t1) / 60))
gc.collect()
files = glob.glob(dirpath+'/*.csv')
m = {}
l = []
for file in files:
idx = int(re.search('([0-9]+).csv', file).group(1))
m[idx] = file
l = [pd.read_csv(m[i]) for i in range(epoch)]
data_df2 = pd.concat(l)
data_df2 = data_df2.reset_index(drop=True)
shutil.rmtree(dirpath)
tmp_columns = ['cve_match', 'bug_match', 'func_same_cnt', 'func_same_ratio', 'func_unrelated_cnt',
'file_same_cnt', 'file_same_ratio', 'file_unrelated_cnt',
'filepath_same_cnt', 'filepath_same_ratio', 'filepath_unrelated_cnt',
'vuln_type_1', 'vuln_type_2', 'vuln_type_3',
'mess_shared_num', 'mess_shared_ratio', 'mess_max', 'mess_sum', 'mess_mean', 'mess_var',
'code_shared_num', 'code_shared_ratio', 'code_max', 'code_sum', 'code_mean', 'code_var']
data_df[tmp_columns] = data_df2[tmp_columns]
data_df.to_csv('../data/dataset/feature_{}.csv'.format(reponame), index=False)