노현종

Vuln 템플릿 메서드 패턴화, 클래스 정리

VulnC, VulnPython 분리
VulnAbstractCrawler 클래스에서 VulnWorker 클래스로 템플릿 메서드 패턴화를 위해 분리하였음.
작업 진행은 이제 VulnWorker의 Run을 실행하면 됨.
......@@ -8,7 +8,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
//using MySql.Data.MySqlClient;
using AESENC;
using System.Security;
using System.Runtime.InteropServices;
......@@ -32,7 +32,7 @@ namespace VulnCrawler
//AWS.Account account = AWS.account;
//Console.WriteLine($"Endpoint: {account.Endpoint}, ID: {account.Id}, PW: {account.Pw}");
//MySqlConnectionStringBuilder builder = new MySqlConnectionStringBuilder {
// Server = "",
// UserID = id,
......@@ -44,9 +44,9 @@ namespace VulnCrawler
//string strConn = builder.ToString();
//builder = null;
//MySqlConnection conn = new MySqlConnection(strConn);
//try {
// String sql = "INSERT INTO members (id, pwd, name) " +
// "VALUES ('gon', '111', '김삿갓')";
......@@ -59,10 +59,24 @@ namespace VulnCrawler
//} catch (Exception e) {
// Console.WriteLine(e.ToString());
//}
Run();
}
public static void Run() {
// Repository 폴더들이 있는 주소를 지정하면 하위 폴더 목록을 가져옴(Repository 목록)
var directorys = Directory.GetDirectories(@"c:\VulnPy");
if (directorys.Length == 0) {
Console.WriteLine("Repository 목록 찾기 실패");
return;
}
// Repository 목록 만큼 반복함.
foreach (var directory in directorys) {
VulnWorker.Run<VulnPython>(directory);
}
}
static String SecureStringToString(SecureString value) {
IntPtr valuePtr = IntPtr.Zero;
try {
......@@ -90,106 +104,7 @@ namespace VulnCrawler
}
return pwd;
}
public static void Run() {
// Repository 폴더들이 있는 주소를 지정하면 하위 폴더 목록을 가져옴(Repository 목록)
var directorys = Directory.GetDirectories(@"c:\VulnPy");
if (directorys.Length == 0) {
Console.WriteLine("Repository 목록 찾기 실패");
return;
}
// Repository 목록 만큼 반복함.
foreach (var directory in directorys) {
var pyCrawl = new VulnPython(directory);
var commits = pyCrawl.Commits;
foreach (var commit in commits) {
// 커밋 메시지
string message = commit.Message;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Commit Message: {message}");
Console.ResetColor();
foreach (var parent in commit.Parents) {
// 부모 커밋과 현재 커밋을 Compare 하여 패치 내역을 가져옴
var patch = pyCrawl.Repository.Diff.Compare<Patch>(parent.Tree, commit.Tree);
// 패치 엔트리 파일 배열 중에 파일 확장자가 .py인 것만 가져옴
// (실질적인 코드 변경 커밋만 보기 위해서)
var entrys = pyCrawl.GetPatchEntryChanges(patch);
// 현재 커밋에 대한 패치 엔트리 배열을 출력함
PrintPatchEntrys(entrys, pyCrawl);
}
}
}
}
public static void PrintPatchEntrys(IEnumerable<PatchEntryChanges> entrys, VulnAbstractCrawler pyCrawl) {
foreach (var entry in entrys) {
// 현재 패치 엔트리 정보 출력(추가된 줄 수, 삭제된 줄 수, 패치 이전 경로, 패치 후 경로)
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"status: {entry.Status.ToString()}");
Console.WriteLine($"added: {entry.LinesAdded.ToString()}, deleted: {entry.LinesDeleted.ToString()}");
Console.WriteLine($"old path: {entry.OldPath.ToString()}, new path: {entry.Path.ToString()}");
Console.ResetColor();
// 기존 소스코드
var oldOid = entry.OldOid;
Blob oldBlob = pyCrawl.Repository.Lookup<Blob>(oldOid);
string oldContent = oldBlob.GetContentText();
// 변경된 소스코드
var newOid = entry.Oid;
Blob newBlob = pyCrawl.Repository.Lookup<Blob>(newOid);
string newContent = newBlob.GetContentText();
var regs = pyCrawl.GetMatches(entry.Patch);
// 패치 전 코드 (oldContent)
// 패치 후 코드 (newContent)
// 패치 코드 (entry.Patch)
// 출력
//if (regs.Count > 0) {
// Console.BackgroundColor = ConsoleColor.DarkBlue;
// Console.WriteLine($"Old Content: \n{oldContent}");
// Console.ResetColor();
// Console.BackgroundColor = ConsoleColor.DarkMagenta;
// Console.WriteLine($"New Content: \n{newContent}");
// Console.ResetColor();
// Console.BackgroundColor = ConsoleColor.DarkRed;
// Console.WriteLine($"Patched: \n{entry.Patch}");
// Console.ResetColor();
// Console.WriteLine("-----------");
// Console.WriteLine(regs.Count);
//}
// 패치 코드에서 매칭된 파이썬 함수들로부터
// 패치 전 코드 파일(oldBlob)을 탐색하여 원본 파이썬 함수 가져오고(originalFunc)
//
foreach (var reg in regs) {
var match = reg as Match;
string methodName = match.Groups[VulnAbstractCrawler.MethodName].Value;
string originalFunc, md5;
(originalFunc, md5) = pyCrawl.GetPatchResult(oldBlob.GetContentStream(),
match.Groups[VulnAbstractCrawler.MethodName].Value);
// 패치 전 원본 함수
Console.WriteLine($"Original Func: {originalFunc}");
// 해쉬 후
Console.WriteLine($"Original Func MD5: {md5}");
}
}
}
/// <summary>
/// 디렉토리 삭제 함수
......@@ -213,26 +128,7 @@ namespace VulnCrawler
Directory.Delete(targetDir, false);
}
/// <summary>
/// Clone 콜백 함수
/// </summary>
/// <param name="progress"></param>
/// <returns></returns>
public static bool TransferProgress(TransferProgress progress) {
int totalBytes = progress.TotalObjects;
int receivedBytes = progress.ReceivedObjects;
long receivedTotal = progress.ReceivedBytes;
double received = progress.ReceivedBytes / 1000000;
double percent = ((double)receivedBytes / (double)totalBytes) * 10;
Console.WriteLine($"진행률: {percent.ToString("P2")}, 남은 파일: {receivedBytes} of {totalBytes}"); //, 받은 용량: {received.ToString()}MB");
Console.ForegroundColor = ConsoleColor.DarkGreen;
return true;
}
public static void CheckoutProcess(string path, int completedSteps, int totalSteps) {
Console.WriteLine($"{completedSteps}, {totalSteps}, {path}");
}
}
......
using LibGit2Sharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace VulnCrawler
{
// 추상 클래스
public abstract class VulnAbstractCrawler
{
/// <summary>
/// 생성자
/// 경로를 입력받아서(path)
/// 레파지토리를 초기화하고
/// 커밋 목록을 검색함
/// </summary>
/// <param name="path"></param>
public VulnAbstractCrawler() {
}
// 소멸자
~VulnAbstractCrawler() {
Repository.Dispose();
}
#region 메서드 패턴 정규식 그룹
// 정규식 그룹화
// @@ -oldStart,oldLines +newStart,newLines @@ MethodName():
public static string OldStart => "oldStart";
public static string OldLines => "oldLines";
public static string NewStart => "newStart";
public static string NewLines => "newLines";
public static string MethodName => "methodName";
#endregion
public void Init(string path) {
Repository = new Repository(path);
Commits = SearchCommits();
}
/// <summary>
/// 레파지토리
/// </summary>
public Repository Repository { get; private set; }
/// <summary>
/// 커밋 목록
/// </summary>
public IEnumerable<Commit> Commits { get; private set; }
/// <summary>
/// 커밋에서 검색할 정규식 문자열
/// </summary>
public string SearchCommitPattern => @"CVE-20\d\d-\d{4}";
/// <summary>
/// 패치 코드에서 함수 찾을 정규식 패턴 문자열
/// </summary>
protected abstract string RegexFuncPattern { get; }
protected abstract string Extension { get; }
public virtual IEnumerable<PatchEntryChanges> GetPatchEntryChanges(Patch patch) {
return patch.Where(e => e.Path.EndsWith(Extension)).ToList();
}
/// <summary>
/// 정규식을 이용하여 @@ -\d,\d +\d,\d @@ MethodName(): 이런 패턴을 찾고
/// 그룹화 하여 반환함 (OldStart, OldLines, NewStart, NewLines, MethodName
/// </summary>
/// <param name="patchCode">찾을 코드</param>
/// <returns>정규식 그룹 컬렉션</returns>
public abstract MatchCollection GetMatches(string patchCode);
/// <summary>
/// 파일스트림으로 부터 원본 함수 구하는 함수
/// </summary>
/// <param name="oldStream">파일 스트림</param>
/// <param name="methodName">찾을 메서드 이름</param>
/// <returns>함수 문자열</returns>
protected abstract string GetOriginalFunc(Stream oldStream, string methodName);
/// <summary>
/// 실제 프로세스
/// </summary>
/// <param name="oldStream"></param>
/// <param name="methodName"></param>
/// <returns></returns>
public virtual (string originalFunc, string hash) Process(Stream oldStream, string methodName) {
// 패치 전 원본 함수 구하고
string func = GetOriginalFunc(oldStream, methodName);
// 주석 제거하고
func = RemoveComment(func);
// 해쉬하고
string md5 = MD5HashFunc(func);
return (func, md5);
}
/// <summary>
/// 주석 제거 함수
/// </summary>
/// <param name="original">제거할 문자열</param>
/// <returns>결과 문자열</returns>
public abstract string RemoveComment(string original);
/// <summary>
/// 커밋 검색 함수(정규식 사용)
/// 정규식은 SearchKeyword 사용함
/// </summary>
/// <returns>커밋 목록</returns>
public virtual IEnumerable<Commit> SearchCommits() {
// where => 조건에 맞는 것을 찾음(CVE-20\d\d-\d{4}로 시작하는 커밋만 골라냄)
var commits = Repository.Commits
.Where(c => Regex.Match(c.Message, SearchCommitPattern, RegexOptions.IgnoreCase).Success)
.ToList();
return commits;
}
/// <summary>
/// 커밋 메시지로부터 CVE 코드 추출
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public string GetCVE(string msg) {
var match = Regex.Match(msg, SearchCommitPattern, RegexOptions.IgnoreCase);
if (match.Success) {
return match.Value;
}
return string.Empty;
}
/// <summary>
/// MD5 함수
/// </summary>
/// <param name="str">INPUT 문자열</param>
/// <returns>결과 문자열</returns>
protected static string MD5HashFunc(string str) {
StringBuilder MD5Str = new StringBuilder();
byte[] byteArr = Encoding.ASCII.GetBytes(str);
byte[] resultArr = (new MD5CryptoServiceProvider()).ComputeHash(byteArr);
for (int cnti = 0; cnti < resultArr.Length; cnti++) {
MD5Str.Append(resultArr[cnti].ToString("X2"));
}
return MD5Str.ToString();
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace VulnCrawler
{
public class VulnC : VulnAbstractCrawler
{
protected override string RegexFuncPattern => throw new NotImplementedException();
protected override string Extension => throw new NotImplementedException();
public override MatchCollection GetMatches(string patchCode) {
throw new NotImplementedException();
}
public override string RemoveComment(string original) {
throw new NotImplementedException();
}
protected override string GetOriginalFunc(Stream oldStream, string methodName) {
throw new NotImplementedException();
}
}
}
......@@ -57,7 +57,10 @@
<Compile Include="AWS.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VulnAbstractCrawler.cs" />
<Compile Include="VulnC.cs" />
<Compile Include="VulnPython.cs" />
<Compile Include="VulnWorker.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
......
using LibGit2Sharp;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace VulnCrawler
{
// 추상 클래스
public abstract class VulnAbstractCrawler {
/// <summary>
/// 생성자
/// 경로를 입력받아서(path)
/// 레파지토리를 초기화하고
/// 커밋 목록을 검색함
/// </summary>
/// <param name="path"></param>
public VulnAbstractCrawler(string path) {
Repository = new Repository(path);
Commits = SearchCommits();
}
// 소멸자
~VulnAbstractCrawler() {
Repository.Dispose();
}
// 정규식 그룹화
// @@ -oldStart,oldLines +newStart,newLines @@ MethodName():
public static string OldStart => "oldStart";
public static string OldLines => "oldLines";
public static string NewStart => "newStart";
public static string NewLines => "newLines";
public static string MethodName => "methodName";
/// <summary>
/// 레파지토리
/// </summary>
public Repository Repository { get; private set; }
/// <summary>
/// 커밋 목록
/// </summary>
public IEnumerable<Commit> Commits { get; private set; }
/// <summary>
/// 커밋에서 검색할 정규식 문자열
/// </summary>
protected string SearchKeyword => @"CVE-20\d\d-\d{4}";
/// <summary>
/// 패치 코드에서 함수 찾을 정규식 패턴 문자열
/// </summary>
protected abstract string RegexFuncPattern { get; }
protected abstract string Extension { get; }
public abstract IEnumerable<PatchEntryChanges> GetPatchEntryChanges(Patch patch);
/// <summary>
/// 정규식을 이용하여 @@ -\d,\d +\d,\d @@ MethodName(): 이런 패턴을 찾고
/// 그룹화 하여 반환함 (OldStart, OldLines, NewStart, NewLines, MethodName
/// </summary>
/// <param name="patchCode">찾을 코드</param>
/// <returns>정규식 그룹 컬렉션</returns>
public abstract MatchCollection GetMatches(string patchCode);
/// <summary>
/// 파일스트림으로 부터 원본 함수 구하는 함수
/// </summary>
/// <param name="oldStream">파일 스트림</param>
/// <param name="methodName">찾을 메서드 이름</param>
/// <returns>함수 문자열</returns>
protected abstract string GetOriginalFunc(Stream oldStream, string methodName);
public virtual (string originalFunc, string hash) GetPatchResult(Stream oldStream, string methodName) {
// 패치 전 원본 함수 구하고
string func = GetOriginalFunc(oldStream, methodName);
// 주석 제거하고
func = RemoveComment(func);
Console.WriteLine(func);
// 해쉬하고
string md5 = MD5HashFunc(func);
return (func, md5);
}
/// <summary>
/// 주석 제거 함수
/// </summary>
/// <param name="original">제거할 문자열</param>
/// <returns>결과 문자열</returns>
public abstract string RemoveComment(string original);
/// <summary>
/// 커밋 검색 함수(정규식 사용)
/// 정규식은 SearchKeyword 사용함
/// </summary>
/// <returns>커밋 목록</returns>
public virtual IEnumerable<Commit> SearchCommits() {
// where => 조건에 맞는 것을 찾음(CVE-20\d\d-\d{4}로 시작하는 커밋만 골라냄)
var commits = Repository.Commits
.Where(c => Regex.Match(c.Message, SearchKeyword, RegexOptions.IgnoreCase).Success)
.ToList();
return commits;
}
/// <summary>
/// MD5 함수
/// </summary>
/// <param name="str">INPUT 문자열</param>
/// <returns>결과 문자열</returns>
protected static string MD5HashFunc(string str) {
StringBuilder MD5Str = new StringBuilder();
byte[] byteArr = Encoding.ASCII.GetBytes(str);
byte[] resultArr = (new MD5CryptoServiceProvider()).ComputeHash(byteArr);
for (int cnti = 0; cnti < resultArr.Length; cnti++) {
MD5Str.Append(resultArr[cnti].ToString("X2"));
}
return MD5Str.ToString();
}
}
public class VulnC : VulnAbstractCrawler
{
public VulnC(string path) : base(path) {
}
protected override string RegexFuncPattern => throw new NotImplementedException();
protected override string Extension => ".c";
public override MatchCollection GetMatches(string patchCode) {
throw new NotImplementedException();
}
public override IEnumerable<PatchEntryChanges> GetPatchEntryChanges(Patch patch) {
throw new NotImplementedException();
}
public override string RemoveComment(string original) {
throw new NotImplementedException();
}
protected override string GetOriginalFunc(Stream oldStream, string methodName) {
throw new NotImplementedException();
}
}
/// <summary>
/// 파이썬 크롤러
/// </summary>
public class VulnPython : VulnAbstractCrawler
{
public VulnPython(string path) : base(path) {
}
protected override string Extension => ".py";
protected override string RegexFuncPattern => $@"@@ \-(?<{OldStart}>\d+),(?<{OldLines}>\d+) \+(?<{NewStart}>\d+),(?<{NewLines}>\d+) @@ def (?<{MethodName}>\w+)";
......@@ -195,12 +58,7 @@ namespace VulnCrawler
}
return oldBuilder.ToString();
}
public override IEnumerable<PatchEntryChanges> GetPatchEntryChanges(Patch patch) {
return patch.Where(e => e.Path.EndsWith(Extension)).ToList();
}
public override string RemoveComment(string original) {
......

using LibGit2Sharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace VulnCrawler
{
public static class VulnWorker
{
public static void Run<T>(string dirPath) where T : VulnAbstractCrawler, new() {
var self = new T();
self.Init(dirPath);
var commits = self.Commits;
foreach (var commit in commits) {
// 커밋 메시지
string message = commit.Message;
string cve = self.GetCVE(message);
if (string.IsNullOrEmpty(cve)) {
continue;
}
foreach (var parent in commit.Parents) {
// 부모 커밋과 현재 커밋을 Compare 하여 패치 내역을 가져옴
var patch = self.Repository.Diff.Compare<Patch>(parent.Tree, commit.Tree);
// 패치 엔트리 파일 배열 중에 파일 확장자가 .py인 것만 가져옴
// (실질적인 코드 변경 커밋만 보기 위해서)
var entrys = self.GetPatchEntryChanges(patch);
// 현재 커밋에 대한 패치 엔트리 배열을 출력함
PrintPatchEntrys(entrys, self, message, cve);
}
}
}
private static void PrintPatchEntrys(IEnumerable<PatchEntryChanges> entrys, VulnAbstractCrawler self, string commitMsg, string cve) {
foreach (var entry in entrys) {
// 기존 소스코드
var oldOid = entry.OldOid;
Blob oldBlob = self.Repository.Lookup<Blob>(oldOid);
string oldContent = oldBlob.GetContentText();
// 변경된 소스코드
var newOid = entry.Oid;
Blob newBlob = self.Repository.Lookup<Blob>(newOid);
string newContent = newBlob.GetContentText();
var regs = self.GetMatches(entry.Patch);
// 패치 전 코드 (oldContent)
// 패치 후 코드 (newContent)
// 패치 코드 (entry.Patch)
// 출력
//if (regs.Count > 0) {
// Console.BackgroundColor = ConsoleColor.DarkBlue;
// Console.WriteLine($"Old Content: \n{oldContent}");
// Console.ResetColor();
// Console.BackgroundColor = ConsoleColor.DarkMagenta;
// Console.WriteLine($"New Content: \n{newContent}");
// Console.ResetColor();
// Console.BackgroundColor = ConsoleColor.DarkRed;
// Console.WriteLine($"Patched: \n{entry.Patch}");
// Console.ResetColor();
// Console.WriteLine("-----------");
// Console.WriteLine(regs.Count);
//}
// 패치 코드에서 매칭된 파이썬 함수들로부터
// 패치 전 코드 파일(oldBlob)을 탐색하여 원본 파이썬 함수 가져오고(originalFunc)
//
foreach (var reg in regs) {
var match = reg as Match;
string methodName = match.Groups[VulnAbstractCrawler.MethodName].Value;
string originalFunc, md5;
(originalFunc, md5) = self.Process(oldBlob.GetContentStream(),
match.Groups[VulnAbstractCrawler.MethodName].Value);
// 현재 패치 엔트리 정보 출력(추가된 줄 수, 삭제된 줄 수, 패치 이전 경로, 패치 후 경로)
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"status: {entry.Status.ToString()}");
Console.WriteLine($"added: {entry.LinesAdded.ToString()}, deleted: {entry.LinesDeleted.ToString()}");
Console.WriteLine($"old path: {entry.OldPath.ToString()}, new path: {entry.Path.ToString()}");
Console.ResetColor();
Console.Write($"CVE: ");
Console.ForegroundColor = ConsoleColor.Red;
Console.Write($"{cve}");
Console.WriteLine("");
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Commit Message: {commitMsg}");
Console.ResetColor();
// 패치 전 원본 함수
Console.WriteLine($"Original Func: {originalFunc}");
// 해쉬 후
Console.WriteLine($"Original Func MD5: {md5}");
Console.WriteLine("==============================");
}
}
}
}
}