이경수

final commit

......@@ -32,9 +32,10 @@ class HomeView(View):
# context['form'] = ServerList_form
# context['object_list'] = object_list
context = {}
context['user_name'] = str(request.user)
return render(self.request, self.template_name)
return render(self.request, self.template_name, context)
def post(self, request, *args, **kwargs):
......
......@@ -97,7 +97,9 @@
<td>{{ object.update_date }}</td>
<td>{{ object.fileName }}</td>
<td>{{ object.funcName }}</td>
<td>{{ object.url }}</td>
<td>
<a href="{{ object.url }}">{{ object.url }}</a>
</td>
<td>{{ object.cveDetail }}</td>
</tr>
{% endfor %}
......@@ -107,7 +109,7 @@
<button id="prev" class="btn btn-primary" style="background-color: #FF0066; border: #FF0066;"><< Prev</button>
<button id="next" class="btn btn-primary" style="margin-left: 200px; background-color: #FF0066; border: #FF0066;">Next >></button>
</div> -->
<!-- {% for object in object_list %}
<div class="portfolio-modal mfp-hide" id="portfolio-modal-{{ forloop.counter }}">
......
......@@ -534,5 +534,7 @@ class StaticView(TemplateView):
context['level8'] = level8
context['level9'] = level9
context['logined_user'] = str(request.user)
return render(self.request, self.template_name, context)
......
......@@ -47,6 +47,10 @@
<li class="nav-item mx-0 mx-lg-1">
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="{% url 'edit' %}">Mypage</a>
</li>
<li class="nav-item mx-0 mx-lg-1">
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" style="color: yellow;" href="#">Welcome! {{ user_name }}</a>
</li>
{% else %}
<li class="nav-item mx-0 mx-lg-1">
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="/accounts/login">Login</a>
......
......@@ -35,7 +35,7 @@ namespace VulnCrawler
AWS.Account account = AWS.account;
/* AWS 정보 출력 */
Console.WriteLine($"Endpoint: {account.Endpoint}, ID: {account.Id}, PW: {account.Pw}");
Console.WriteLine($"Endpoint: {account.Endpoint}, ID: {account.Id}");
try
{
/* DB 접속 시도 */
......
......@@ -41,14 +41,11 @@ namespace VulnCrawler
if (string.IsNullOrEmpty(cve)) {
continue;
}
string commitUrl = $"{crawler.PushUrl}/commit/{commit.Sha}";
foreach (var parent in commit.Parents) {
try
{
// 부모 커밋과 현재 커밋을 Compare 하여 패치 내역을 가져옴
var patch = crawler.Repository.Diff.Compare<Patch>(parent.Tree, commit.Tree);
......@@ -132,7 +129,7 @@ namespace VulnCrawler
};
/* VulnDB에 추가 */
VulnRDS._InsertVulnData(vuln);
//VulnRDS._InsertVulnData(vuln);
}
}
......
......@@ -11,7 +11,7 @@ using System.Threading.Tasks;
using VulnCrawler;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using LibGit2Sharp;
namespace VulnUserCodeAnalyzer
{
......@@ -42,7 +42,12 @@ namespace VulnUserCodeAnalyzer
public static void AutoLoad()
{
var dir = new DirectoryInfo(@"c:\CVE");
if (!dir.Exists)
{
Console.WriteLine("found not CVE Directory");
return;
}
Console.WriteLine("Loading CVE List...");
foreach (var json in dir.EnumerateFiles("*.json"))
{
var match = Regex.Match(json.Name, @"(20\d\d)");
......@@ -59,7 +64,7 @@ namespace VulnUserCodeAnalyzer
var dict = LoadCveJson(int.Parse(match.Value));
CveDict.Add(year, dict);
Console.WriteLine($"cve 로드 완료 {year}, 개수 : {CveDict[year].Count}");
Console.WriteLine($"Finished loading CVE List Year: {year}, Count : {CveDict[year].Count}");
}
}
......@@ -109,12 +114,38 @@ namespace VulnUserCodeAnalyzer
}
class Program
{
static void Main(string[] args)
/// <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);
Console.WriteLine($"Progress: {percent.ToString("P2")}, Remain: {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}");
}
public static void Clone(string path, string url)
{
Console.WriteLine($"Start Cloning Path : {path}");
string clone = Repository.Clone(url, $@"{path}", new CloneOptions { OnTransferProgress = TransferProgress, OnCheckoutProgress = CheckoutProcess });
Console.ResetColor();
Console.WriteLine($"Finished Clone Repository: {clone}");
}
static void Main(string[] args)
{
/* 연도별 CVE JSON 파일 로드 */
//CVE_JSON.AutoLoad();
CVE_JSON.AutoLoad();
/* 크롤러 타입 */
var crawler = new VulnC();
......@@ -129,7 +160,7 @@ namespace VulnUserCodeAnalyzer
AWS.LoadAccount(xml);
AWS.Account account = AWS.account;
/* AWS 정보 출력 */
Console.WriteLine($"Endpoint: {account.Endpoint}, ID: {account.Id}, PW: {account.Pw}");
Console.WriteLine($"Endpoint: {account.Endpoint}, ID: {account.Id}");
try
{
/* DB 접속 시도 */
......@@ -137,227 +168,267 @@ namespace VulnUserCodeAnalyzer
}
catch (Exception e)
{
Console.WriteLine($"접속 에러 :: {e.ToString()}");
Console.WriteLine($"Connection Error :: {e.ToString()}");
return;
}
/* AWS 연결 여부 확인 */
if (VulnRDS.Conn.State == System.Data.ConnectionState.Open)
{
Console.WriteLine("접속 성공");
Console.WriteLine("Connection Success");
}
else
{
Console.WriteLine("연결 실패");
Console.WriteLine("Fail Connection");
return;
}
var reposits = VulnRDS.SelectAllReposit();
foreach (var (userName, repository) in reposits)
{
Console.WriteLine($"{userName}, {repository}");
}
Console.ReadLine();
/* hashDict = 사용된 사용자 함수 정보 */
var hashDict = new Dictionary<int, HashSet<VulnAbstractCrawler.UserBlock>>();
/* 경과 시간 체크 */
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
DirectoryInfo dirInfo = new DirectoryInfo(@"C:\code");
/* 모든 .c 파일 탐색 */
var codeFiles = dirInfo.EnumerateFiles("*.c", SearchOption.AllDirectories);
int totalFileCount = codeFiles.Count();
int count = 0;
foreach (var codeFile in codeFiles)
while (true)
{
Console.WriteLine(codeFile.FullName);
using (var reader = codeFile.OpenText())
string userId = string.Empty;
string repoPath = string.Empty;
Stopwatch repoWatch = new Stopwatch();
repoWatch.Start();
while (true)
{
/* 사용자 코드를 함수별로 나눔 */
var dict = crawler.CrawlUserCode(reader);
foreach (var item in dict)
var elapsedSeconds = repoWatch.Elapsed.TotalSeconds;
if (elapsedSeconds < 10)
{
/* hashDict의 키와 item.key는 함수 블록의 코드 길이 */
if (!hashDict.ContainsKey(item.Key))
continue;
}
Console.WriteLine("Checking User DB...");
var reposits = VulnRDS.SelectAllReposit();
foreach (var (userName, repository) in reposits)
{
if (string.IsNullOrWhiteSpace(repository))
{
hashDict[item.Key] = new HashSet<VulnAbstractCrawler.UserBlock>();
continue;
}
/* item.Value는 각 코드 길이 마다의 블록 정보
* Bloom Filter에 코드 블록 해쉬값 기록
*/
foreach (var hash in item.Value)
var repoBytes = Encoding.Unicode.GetBytes(repository);
var repoBase64 = Convert.ToBase64String(repoBytes);
var repoDir = new DirectoryInfo($@"C:\Repo\{repoBase64}");
if (repoDir.Exists)
{
hash.Path = codeFile.FullName;
hashDict[item.Key].Add(hash);
filter.Add(hash.Hash);
continue;
}
repoDir.Create();
Console.WriteLine($"Clone... Path : {repoDir.FullName}, Url : {repository}");
Clone(repoDir.FullName, repository);
repoPath = repoDir.FullName;
userId = userName;
}
if (!string.IsNullOrWhiteSpace(repoPath) && !string.IsNullOrWhiteSpace(userId))
{
break;
}
count++;
double per = ((double)count / (double)totalFileCount) * 100;
Console.WriteLine($"{count} / {totalFileCount} :: {per.ToString("#0.0")}%, 개체 수 : {hashDict.Count}");
repoWatch.Restart();
}
}
var findBlocks = new Queue<VulnAbstractCrawler.UserBlock>();
var vulnDict = new Dictionary<string, IEnumerable<VulnRDS._Vuln>>();
foreach (var set in hashDict)
{
/* 사용자 코드의 길이 마다 DB로 부터 같은 길이의 CVE 레코드 목록 가져옴 */
var cveList = VulnRDS.SelectVulnbyLen(set.Key).Select(v => v.Cve).Distinct();
foreach (var cve in cveList)
//Console.WriteLine("엔터를 누르세요");
//Console.ReadLine();
/* hashDict = 사용된 사용자 함수 정보 */
var hashDict = new Dictionary<int, HashSet<VulnAbstractCrawler.UserBlock>>();
/* 경과 시간 체크 */
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
DirectoryInfo dirInfo = new DirectoryInfo(repoPath);
/* 모든 .c 파일 탐색 */
var codeFiles = dirInfo.EnumerateFiles("*.c", SearchOption.AllDirectories);
int totalFileCount = codeFiles.Count();
int count = 0;
foreach (var codeFile in codeFiles)
{
if (!vulnDict.ContainsKey(cve))
Console.WriteLine(codeFile.FullName);
using (var reader = codeFile.OpenText())
{
vulnDict[cve] = new HashSet<VulnRDS._Vuln>();
var vulnHashSet = vulnDict[cve] as HashSet<VulnRDS._Vuln>;
/* 같은 길이의 CVE에서 또 같은 종류의 CVE 레코드 목록 가져옴
* 같은 종류의 CVE 레코드들이 사용자 코드에서 모두 포함되어야
* CVE를 가지고 있다고 인정하는 프로그램 정책 때문
*/
var searchedCveHashList = VulnRDS.SelectVulnbyCve(cve);
Console.WriteLine($"cve:{cve}, {searchedCveHashList.Count()}개 가져옴");
foreach (var s in searchedCveHashList)
/* 사용자 코드를 함수별로 나눔 */
var dict = crawler.CrawlUserCode(reader);
foreach (var item in dict)
{
vulnHashSet.Add(s);
/* hashDict의 키와 item.key는 함수 블록의 코드 길이 */
if (!hashDict.ContainsKey(item.Key))
{
hashDict[item.Key] = new HashSet<VulnAbstractCrawler.UserBlock>();
}
/* item.Value는 각 코드 길이 마다의 블록 정보
* Bloom Filter에 코드 블록 해쉬값 기록
*/
foreach (var hash in item.Value)
{
hash.Path = codeFile.FullName;
hashDict[item.Key].Add(hash);
filter.Add(hash.Hash);
}
}
count++;
double per = ((double)count / (double)totalFileCount) * 100;
Console.WriteLine($"{count} / {totalFileCount} :: {per.ToString("#0.0")}%, 개체 수 : {hashDict.Count}");
}
}
}
var findCveDict = new Dictionary<string, List<VulnAbstractCrawler.UserBlock>>();
var findCveList = new HashSet<string>();
/* 본격적인 취약점 매칭 부분 */
foreach (var vulnSet in vulnDict)
{
//Console.WriteLine($"-----cve:{vulnSet.Key}");
bool match = false;
foreach (var vuln in vulnSet.Value)
var findBlocks = new Queue<VulnAbstractCrawler.UserBlock>();
var vulnDict = new Dictionary<string, IEnumerable<VulnRDS._Vuln>>();
foreach (var set in hashDict)
{
/* 사용자 코드 해쉬 저장해논 bloom filter에 취약점 레코드 해쉬값들이 포함되는지 확인
* 포함이 된다는 건 해당 취약점 레코드가 사용자 코드에도 있다는 뜻(취약점)
* 같은 종류의 CVE 레코드가 전부 필터에 포함된다면 취약점으로 판단한다.
*/
if (filter.Contains(vuln.BlockHash))
/* 사용자 코드의 길이 마다 DB로 부터 같은 길이의 CVE 레코드 목록 가져옴 */
var cveList = VulnRDS.SelectVulnbyLen(set.Key).Select(v => v.Cve).Distinct();
foreach (var cve in cveList)
{
if (hashDict.ContainsKey(vuln.LenFunc))
if (!vulnDict.ContainsKey(cve))
{
/* Bloom Filter는 아쉽게도 포함 여부만 알 수 있기에
* 포함되었음을 알았다면 검색해서 정보를 구한다. */
var userBlock = hashDict[vuln.LenFunc].FirstOrDefault(b => b.Hash == vuln.BlockHash);
if (userBlock == null)
vulnDict[cve] = new HashSet<VulnRDS._Vuln>();
var vulnHashSet = vulnDict[cve] as HashSet<VulnRDS._Vuln>;
/* 같은 길이의 CVE에서 또 같은 종류의 CVE 레코드 목록 가져옴
* 같은 종류의 CVE 레코드들이 사용자 코드에서 모두 포함되어야
* CVE를 가지고 있다고 인정하는 프로그램 정책 때문
*/
var searchedCveHashList = VulnRDS.SelectVulnbyCve(cve);
Console.WriteLine($"CVE:{cve}, Received Count : {searchedCveHashList.Count()}");
foreach (var s in searchedCveHashList)
{
continue;
vulnHashSet.Add(s);
}
/* 해당 유저 블록을 임시 저장한다.
* 밑에서 블록 정보를 DB로 전송하기 위해서다.
*/
if (!findCveDict.ContainsKey(vuln.Cve))
}
}
}
var findCveDict = new Dictionary<string, List<VulnAbstractCrawler.UserBlock>>();
var findCveList = new HashSet<string>();
/* 본격적인 취약점 매칭 부분 */
foreach (var vulnSet in vulnDict)
{
Console.WriteLine($"-----cve:{vulnSet.Key}");
bool match = false;
foreach (var vuln in vulnSet.Value)
{
/* 사용자 코드 해쉬 저장해논 bloom filter에 취약점 레코드 해쉬값들이 포함되는지 확인
* 포함이 된다는 건 해당 취약점 레코드가 사용자 코드에도 있다는 뜻(취약점)
* 같은 종류의 CVE 레코드가 전부 필터에 포함된다면 취약점으로 판단한다.
*/
if (filter.Contains(vuln.BlockHash))
{
if (hashDict.ContainsKey(vuln.LenFunc))
{
findCveDict[vuln.Cve] = new List<VulnAbstractCrawler.UserBlock>();
/* Bloom Filter는 아쉽게도 포함 여부만 알 수 있기에
* 포함되었음을 알았다면 검색해서 정보를 구한다. */
var userBlock = hashDict[vuln.LenFunc].FirstOrDefault(b => b.Hash == vuln.BlockHash);
if (userBlock == null)
{
continue;
}
/* 해당 유저 블록을 임시 저장한다.
* 밑에서 블록 정보를 DB로 전송하기 위해서다.
*/
if (!findCveDict.ContainsKey(vuln.Cve))
{
findCveDict[vuln.Cve] = new List<VulnAbstractCrawler.UserBlock>();
}
userBlock.Url = vuln.Url;
findCveDict[vuln.Cve].Add(userBlock);
match = true;
}
userBlock.Url = vuln.Url;
findCveDict[vuln.Cve].Add(userBlock);
match = true;
}
else
{
match = false;
break;
}
}
/* 취약점 레코드가 전부 있어야 CVE 찾음 인정 */
if (match)
{
Console.WriteLine($"Matched CVE : {vulnSet.Key}");
/* 찾았으면 cve값을 기록함 밑에서 찾은 cve 정보 전송하기 위해 */
findCveList.Add(vulnSet.Key);
}
else
{
match = false;
break;
Console.WriteLine("Not");
}
}
/* 취약점 레코드가 전부 있어야 CVE 찾음 인정 */
if (match)
{
Console.WriteLine($"CVE 찾음 {vulnSet.Key}");
/* 찾았으면 cve값을 기록함 밑에서 찾은 cve 정보 전송하기 위해 */
findCveList.Add(vulnSet.Key);
}
else
{
Console.WriteLine("없음");
}
}
stopwatch.Stop();
/* 매칭 끝 후처리 (출력, DB 전송 등) */
var hours = stopwatch.Elapsed.Hours;
var minutes = stopwatch.Elapsed.Minutes;
var seconds = stopwatch.Elapsed.Seconds;
Console.WriteLine($"경과 시간 {hours.ToString("00")}:{minutes.ToString("00")}:{seconds.ToString("00")}");
Console.WriteLine($"찾은 CVE 개수 : {findCveList.Count}");
var yearMatch = new Regex(@"CVE-(\d{4})-(\d+)");
foreach (var cve in findCveList)
{
Console.WriteLine(cve);
var c = yearMatch.Match(cve);
int year = int.Parse(c.Groups[1].Value);
if (!CVE_JSON.CveDict.ContainsKey(year))
{
continue;
}
if (!CVE_JSON.CveDict[year].ContainsKey(cve))
{
continue;
}
var data = CVE_JSON.CveDict[year][cve];
stopwatch.Stop();
/* 매칭 끝 후처리 (출력, DB 전송 등) */
var hours = stopwatch.Elapsed.Hours;
var minutes = stopwatch.Elapsed.Minutes;
var seconds = stopwatch.Elapsed.Seconds;
Console.WriteLine($"Elapsed Time : {hours.ToString("00")}:{minutes.ToString("00")}:{seconds.ToString("00")}");
Console.WriteLine($"Matched CVE Count : {findCveList.Count}");
/* 취약점 타입 분류 */
string type = "NORMAL";
if (data.Detail.IndexOf("overflow", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "OVERFLOW";
}
else if (data.Detail.IndexOf("xss", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "XSS";
}
else if (data.Detail.IndexOf("injection", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "SQLINJECTION";
}
else if (data.Detail.IndexOf("dos", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "DOS";
}
else if (data.Detail.IndexOf("Memory", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "MEMORY";
}
else if (data.Detail.IndexOf("CSRF", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "CSRF";
}
else if (data.Detail.IndexOf("inclusion", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "FILEINCLUSION";
}
else if (data.Detail.IndexOf("EXCUTE", StringComparison.CurrentCultureIgnoreCase) > 0)
var yearMatch = new Regex(@"CVE-(\d{4})-(\d+)");
foreach (var cve in findCveList)
{
type = "EXCUTE";
}
var urlBytes = Convert.FromBase64String(findCveDict[cve].FirstOrDefault().Url);
string url = Encoding.Unicode.GetString(urlBytes);
Console.WriteLine(cve);
var c = yearMatch.Match(cve);
int year = int.Parse(c.Groups[1].Value);
if (!CVE_JSON.CveDict.ContainsKey(year))
{
continue;
}
if (!CVE_JSON.CveDict[year].ContainsKey(cve))
{
continue;
}
var data = CVE_JSON.CveDict[year][cve];
/* DB 전송 */
VulnRDS.InsertVulnDetail(new VulnRDS.Vuln_detail
{
CveName = data.Code,
Type = type,
Level = data.Level.ToString(),
Year = data.Year.ToString(),
CveDetail = data.Detail,
Publish_date = data.Publish_Date.ToString("yyyy-MM-dd"),
Update_date = data.Update_Date.ToString("yyyy-MM-dd"),
UserName = "samsung",
Url = url,
FileName = findCveDict[cve].FirstOrDefault().Path.Replace(@"C:\code", ""),
FuncName = findCveDict[cve].FirstOrDefault().FuncName,
Product = data.Type,
});
Console.WriteLine("추가 완료");
/* 취약점 타입 분류 */
string type = "NORMAL";
if (data.Detail.IndexOf("overflow", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "OVERFLOW";
}
else if (data.Detail.IndexOf("xss", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "XSS";
}
else if (data.Detail.IndexOf("injection", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "SQLINJECTION";
}
else if (data.Detail.IndexOf("dos", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "DOS";
}
else if (data.Detail.IndexOf("Memory", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "MEMORY";
}
else if (data.Detail.IndexOf("CSRF", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "CSRF";
}
else if (data.Detail.IndexOf("inclusion", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "FILEINCLUSION";
}
else if (data.Detail.IndexOf("EXCUTE", StringComparison.CurrentCultureIgnoreCase) > 0)
{
type = "EXCUTE";
}
var urlBytes = Convert.FromBase64String(findCveDict[cve].FirstOrDefault().Url);
string url = Encoding.Unicode.GetString(urlBytes);
var vulnDetail = new VulnRDS.Vuln_detail
{
CveName = data.Code,
Type = type,
Level = data.Level.ToString(),
Year = data.Year.ToString(),
CveDetail = data.Detail,
Publish_date = data.Publish_Date.ToString("yyyy-MM-dd"),
Update_date = data.Update_Date.ToString("yyyy-MM-dd"),
UserName = userId,
Url = url,
FileName = findCveDict[cve].FirstOrDefault().Path.Replace(repoPath, ""),
FuncName = findCveDict[cve].FirstOrDefault().FuncName,
Product = data.Type,
};
Console.WriteLine("추가 완료");
/* DB 전송 */
VulnRDS.InsertVulnDetail(vulnDetail);
Console.WriteLine($"Added CVE: {vulnDetail.CveName}, Type: {vulnDetail.Type}, CVSS: {vulnDetail.Level}");
}
}
}
}
......
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\LibGit2Sharp.NativeBinaries.1.0.217\build\net461\LibGit2Sharp.NativeBinaries.props" Condition="Exists('..\packages\LibGit2Sharp.NativeBinaries.1.0.217\build\net461\LibGit2Sharp.NativeBinaries.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
......@@ -11,6 +12,8 @@
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
......@@ -32,6 +35,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="LibGit2Sharp, Version=0.25.0.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
<HintPath>..\packages\LibGit2Sharp.0.25.2\lib\netstandard2.0\LibGit2Sharp.dll</HintPath>
</Reference>
<Reference Include="MySql.Data, Version=8.0.10.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL" />
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
......@@ -64,4 +70,10 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\LibGit2Sharp.NativeBinaries.1.0.217\build\net461\LibGit2Sharp.NativeBinaries.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\LibGit2Sharp.NativeBinaries.1.0.217\build\net461\LibGit2Sharp.NativeBinaries.props'))" />
</Target>
</Project>
\ No newline at end of file
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="LibGit2Sharp" version="0.25.2" targetFramework="net461" />
<package id="LibGit2Sharp.NativeBinaries" version="1.0.217" targetFramework="net461" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
</packages>
\ No newline at end of file
......