Tidy up System Manifest (#190)

Co-authored-by: Chris Rizzitello <crizzitello@ics.com>
main
crizzitello 2022-07-12 15:09:42 -04:00 committed by GitHub
parent 4727200bc9
commit 1f2e1f32de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 188 additions and 201 deletions

View File

@ -4,19 +4,20 @@
using namespace porting;
void SystemManifest::applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb) {
bool shouldMigrateConfig = options.importConfig && !configPath.isEmpty();
bool shouldMigrateDb = options.importDb == SystemManifestImportOptions::Merge && !dbPath.isEmpty();
void SystemManifest::applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb)
{
bool shouldMigrateConfig = options.importConfig && !configPath.isEmpty();
bool shouldMigrateDb = options.importDb == SystemManifestImportOptions::Merge && !dbPath.isEmpty();
if (shouldMigrateConfig) {
Q_EMIT onStatusUpdate(tr("Importing Settings"));
migrateConfig();
}
if (shouldMigrateConfig) {
Q_EMIT onStatusUpdate(tr("Importing Settings"));
migrateConfig();
}
if (shouldMigrateDb) {
migrateDb(systemDb);
}
Q_EMIT onComplete();
if (shouldMigrateDb) {
migrateDb(systemDb);
}
Q_EMIT onComplete();
}
void SystemManifest::migrateConfig()
@ -53,7 +54,7 @@ void SystemManifest::migrateDb(DatabaseConnection* systemDb)
, importRecord.operationSlug
, contentSensitiveFilename(importRecord.contentType));
auto fullFileExportPath = m_fileTemplate.arg(pathToManifest, item.exportPath);
auto fullFileExportPath = m_fileTemplate.arg(m_pathToManifest, item.exportPath);
auto parentDir = FileHelpers::getDirname(newEvidencePath);
if (!QDir().exists(parentDir))
QDir().mkpath(parentDir);
@ -76,17 +77,16 @@ void SystemManifest::migrateDb(DatabaseConnection* systemDb)
QString SystemManifest::pathToFile(const QString& filename)
{
return m_fileTemplate.arg(pathToManifest, filename);
return m_fileTemplate.arg(m_pathToManifest, filename);
}
QString SystemManifest::contentSensitiveExtension(const QString& contentType) {
if (contentType == Codeblock::contentType()) {
return Codeblock::extension();
}
else if(contentType == Screenshot::contentType()) {
return Screenshot::extension();
}
return QStringLiteral(".bin");
QString SystemManifest::contentSensitiveExtension(const QString& contentType)
{
if (contentType == Codeblock::contentType())
return Codeblock::extension();
if(contentType == Screenshot::contentType())
return Screenshot::extension();
return QStringLiteral(".bin");
}
QString SystemManifest::contentSensitiveFilename(const QString& contentType)
@ -98,94 +98,91 @@ QString SystemManifest::contentSensitiveFilename(const QString& contentType)
return QStringLiteral("ashirt_unknown_type_%1.bin").arg(StringHelpers::randomString());
}
SystemManifest* SystemManifest::readManifest(const QString& pathToExportFile) {
auto content = FileHelpers::readFile(pathToExportFile);
auto manifest = parseJSONItem<SystemManifest*>(content, &SystemManifest::deserialize);
manifest->pathToManifest = FileHelpers::getDirname(pathToExportFile);
return manifest;
SystemManifest* SystemManifest::readManifest(const QString& pathToExportFile)
{
auto content = FileHelpers::readFile(pathToExportFile);
auto manifest = parseJSONItem<SystemManifest*>(content, &SystemManifest::deserialize);
manifest->m_pathToManifest = FileHelpers::getDirname(pathToExportFile);
return manifest;
}
void SystemManifest::exportManifest(DatabaseConnection* db, const QString& outputDirPath,
const SystemManifestExportOptions& options) {
if (!options.includesAnything()) {
return;
}
const SystemManifestExportOptions& options)
{
if (!options.includesAnything() || outputDirPath.isEmpty())
return;
bool success = QDir().mkpath(outputDirPath);
if (!success) {
return;
}
if (!QDir().mkpath(outputDirPath))
return;
os = QSysInfo::kernelType(); // may need to check possible answers, or maybe just compare to new system value?
os = QSysInfo::kernelType(); // may need to check possible answers, or maybe just compare to new system value?
QString basePath = QDir(outputDirPath).path();
QString basePath = QDir(outputDirPath).path();
if (options.exportConfig) {
Q_EMIT onStatusUpdate(tr("Exporting settings"));
configPath = QStringLiteral("config.json");
AppConfig::getInstance().writeConfig(m_fileTemplate.arg(basePath, configPath));
}
if (options.exportConfig) {
Q_EMIT onStatusUpdate(tr("Exporting settings"));
configPath = QStringLiteral("config.json");
AppConfig::getInstance().writeConfig(m_fileTemplate.arg(basePath, configPath));
}
if (options.exportDb) {
Q_EMIT onStatusUpdate(tr("Exporting Evidence"));
dbPath = QStringLiteral("db.sqlite");
evidenceManifestPath = QStringLiteral("evidence.json");
auto allEvidence = DatabaseConnection::createEvidenceExportView(m_fileTemplate.arg(basePath, dbPath), EvidenceFilters(), db);
Q_EMIT onReady(allEvidence.size());
porting::EvidenceManifest evidenceManifest = copyEvidence(basePath, allEvidence);
// write evidence manifest
FileHelpers::writeFile(m_fileTemplate.arg(basePath, evidenceManifestPath),
QJsonDocument(EvidenceManifest::serialize(evidenceManifest)).toJson());
}
if (options.exportDb) {
Q_EMIT onStatusUpdate(tr("Exporting Evidence"));
dbPath = QStringLiteral("db.sqlite");
evidenceManifestPath = QStringLiteral("evidence.json");
auto allEvidence = DatabaseConnection::createEvidenceExportView(m_fileTemplate.arg(basePath, dbPath), EvidenceFilters(), db);
Q_EMIT onReady(allEvidence.size());
porting::EvidenceManifest evidenceManifest = copyEvidence(basePath, allEvidence);
// write evidence manifest
FileHelpers::writeFile(m_fileTemplate.arg(basePath, evidenceManifestPath),
QJsonDocument(EvidenceManifest::serialize(evidenceManifest)).toJson());
}
QString exportPath = QStringLiteral("%1/system.json").arg(basePath);
this->pathToManifest = exportPath;
FileHelpers::writeFile(exportPath, QJsonDocument(serialize(*this)).toJson());
Q_EMIT onComplete();
m_pathToManifest = QStringLiteral("%1/system.json").arg(basePath);
FileHelpers::writeFile(m_pathToManifest, QJsonDocument(serialize(*this)).toJson());
Q_EMIT onComplete();
}
porting::EvidenceManifest SystemManifest::copyEvidence(const QString& baseExportPath,
QList<model::Evidence> allEvidence) {
QString relativeEvidenceDir = QStringLiteral("evidence");
QDir().mkpath(m_fileTemplate.arg(baseExportPath, relativeEvidenceDir));
QList<model::Evidence> allEvidence)
{
QString relativeEvidenceDir = QStringLiteral("evidence");
QDir().mkpath(m_fileTemplate.arg(baseExportPath, relativeEvidenceDir));
porting::EvidenceManifest evidenceManifest;
for (size_t evidenceIndex = 0; evidenceIndex < allEvidence.size(); evidenceIndex++) {
auto evi = allEvidence.at(evidenceIndex);
auto newName = QStringLiteral("ashirt_evidence_%1.%2")
.arg(StringHelpers::randomString(10), contentSensitiveExtension(evi.contentType));
auto item = porting::EvidenceItem(evi.id, relativeEvidenceDir + "/" + newName);
auto dstPath = m_fileTemplate.arg(baseExportPath, item.exportPath);
QFile srcFile(evi.path);
srcFile.copy(dstPath);
if (!srcFile.error() != QFileDevice::NoError)
Q_EMIT onCopyFileError(evi.path, dstPath, srcFile.errorString());
else
evidenceManifest.entries.append(item);
Q_EMIT onFileProcessed(evidenceIndex + 1);
}
return evidenceManifest;
porting::EvidenceManifest evidenceManifest;
for (size_t evidenceIndex = 0; evidenceIndex < allEvidence.size(); evidenceIndex++) {
auto evi = allEvidence.at(evidenceIndex);
auto newName = QStringLiteral("ashirt_evidence_%1.%2")
.arg(StringHelpers::randomString(10), contentSensitiveExtension(evi.contentType));
auto item = porting::EvidenceItem(evi.id, m_fileTemplate.arg(relativeEvidenceDir, newName));
auto dstPath = m_fileTemplate.arg(baseExportPath, item.exportPath);
QFile srcFile(evi.path);
srcFile.copy(dstPath);
if (!srcFile.error() != QFileDevice::NoError)
Q_EMIT onCopyFileError(evi.path, dstPath, srcFile.errorString());
else
evidenceManifest.entries.append(item);
Q_EMIT onFileProcessed(evidenceIndex + 1);
}
return evidenceManifest;
}
QJsonObject SystemManifest::serialize(const SystemManifest& src) {
QJsonObject o;
o.insert(QStringLiteral("operatingSystem"), src.os);
o.insert(QStringLiteral("databasePath"), src.dbPath);
o.insert(QStringLiteral("configPath"), src.configPath);
o.insert(QStringLiteral("serversPath"), src.serversPath);
o.insert(QStringLiteral("evidenceManifestPath"), src.evidenceManifestPath);
return o;
QJsonObject SystemManifest::serialize(const SystemManifest& src)
{
QJsonObject o;
o.insert(QStringLiteral("operatingSystem"), src.os);
o.insert(QStringLiteral("databasePath"), src.dbPath);
o.insert(QStringLiteral("configPath"), src.configPath);
o.insert(QStringLiteral("serversPath"), src.serversPath);
o.insert(QStringLiteral("evidenceManifestPath"), src.evidenceManifestPath);
return o;
}
SystemManifest* SystemManifest::deserialize(const QJsonObject& o) {
auto manifest = new SystemManifest;
manifest->os = o.value(QStringLiteral("operatingSystem")).toString();
manifest->dbPath = o.value(QStringLiteral("databasePath")).toString();
manifest->configPath = o.value(QStringLiteral("configPath")).toString();
manifest->serversPath = o.value(QStringLiteral("serversPath")).toString();
manifest->evidenceManifestPath = o.value(QStringLiteral("evidenceManifestPath")).toString();
return manifest;
SystemManifest* SystemManifest::deserialize(const QJsonObject& o)
{
auto manifest = new SystemManifest;
manifest->os = o.value(QStringLiteral("operatingSystem")).toString();
manifest->dbPath = o.value(QStringLiteral("databasePath")).toString();
manifest->configPath = o.value(QStringLiteral("configPath")).toString();
manifest->serversPath = o.value(QStringLiteral("serversPath")).toString();
manifest->evidenceManifestPath = o.value(QStringLiteral("evidenceManifestPath")).toString();
return manifest;
}

View File

@ -14,124 +14,114 @@
#include "system_porting_options.h"
namespace porting {
class SystemManifest : public QObject {
Q_OBJECT
class SystemManifest : public QObject {
Q_OBJECT
public:
SystemManifest() {}
~SystemManifest(){}
public:
SystemManifest() {}
~SystemManifest(){}
/**
* @brief readManifest parses the the system.json (as provided by the caller) into a complete SystemManifest
* @param pathToExportFile the location of the system.json file
* @return the completed SystemManifest
*/
static SystemManifest* readManifest(const QString& pathToExportFile);
/**
* @brief readManifest parses the the system.json (as provided by the caller) into a complete SystemManifest
* @param pathToExportFile the location of the system.json file
* @return the completed SystemManifest
* @throws FileError if there is an issue reading the file at the indicated path
*/
static SystemManifest* readManifest(const QString& pathToExportFile);
/// serialize converts the internal state of the system manifest into a json object
static QJsonObject serialize(const SystemManifest& src);
/// deserialize converts a json object into a system manifest instance
static SystemManifest* deserialize(const QJsonObject& o);
public:
signals:
/// onReady fires when the breadth of the import/export is known to let the caller know that real work is starting
void onReady(quint64 numFilesToProcess);
/// onFileProcessed fires when an evidence file is copied during import or export
void onFileProcessed(quint64 runningCount);
/// onComplete fires when the entire import/export is finished
void onComplete();
/// onCopyFileError fires when a file cannot be copied during import or export
void onCopyFileError(QString srcPath, QString dstPath, const QString& errStr);
/// onStatusUpdate fires when the system moves between import/export phases
void onStatusUpdate(QString text);
/**
* @brief applyManifest takes the given manifest object (and options), and begins merging that data with the running system
* @param options switches to control what gets imported
* @param systemDb The currently running/system database
* @throws QSqlError If there is an issue ingesting database records from the exported database
*/
void applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb);
public:
/// serialize converts the internal state of the system manifest into a json object
static QJsonObject serialize(const SystemManifest& src);
/**
* @brief exportManifest starts the long process of copying config and evidence into the specified directory.
* @param db the connection to the primary database
* @param outputDirPath the path to the expected export directory. Files will be placed under this directory
* (not wrapped in another directory)
* @param options exporting options (e.g. do you want to copy both evidence *and* config
* @throws QSqlError if there is an issue accessing the system database, or the copied database
*/
void exportManifest(DatabaseConnection* db, const QString& outputDirPath,
const SystemManifestExportOptions& options);
/// deserialize converts a json object into a system manifest instance
static SystemManifest* deserialize(const QJsonObject& o);
/// os is the operating system associated with the originating export
QString os;
/// dbPath is the (relative) path to the database file from the originating export
QString dbPath;
/// configPath is the (relative) path to the config file from the originating export
QString configPath;
/// serversPath is currently unused
QString serversPath;
/// evidenceManifestPath is the (relative) path to the evidence manifest file from the originating export
QString evidenceManifestPath;
private:
/// contentSensitiveFilename returns a (random) filename for the given content type. This, in
/// turn, relies on the underlying type to provide a sensible value. If no match is found, then
/// "ashirt_unknown_type_XXXXXX.bin" (X's will be replaced with random characters) is returned
/// instead.
/// @see FileHelpers::randomFilename
static QString contentSensitiveFilename(const QString& contentType);
signals:
/// onReady fires when the breadth of the import/export is known to let the caller know that real work is starting
void onReady(quint64 numFilesToProcess);
/// onFileProcessed fires when an evidence file is copied during import or export
void onFileProcessed(quint64 runningCount);
/// onComplete fires when the entire import/export is finished
void onComplete();
/// onCopyFileError fires when a file cannot be copied during import or export
void onCopyFileError(QString srcPath, QString dstPath, const QString& errStr);
/// onStatusUpdate fires when the system moves between import/export phases
void onStatusUpdate(QString text);
/// contentSensitiveExtension returns a file extension for the given content type. This, in turn,
/// relies on the underlying type to provide a sensible value. If no match is found, then ".bin"
/// is returned instead
static QString contentSensitiveExtension(const QString& contentType);
private:
/// contentSensitiveFilename returns a (random) filename for the given content type. This, in
/// turn, relies on the underlying type to provide a sensible value. If no match is found, then
/// "ashirt_unknown_type_XXXXXX.bin" (X's will be replaced with random characters) is returned
/// instead.
/// @see FileHelpers::randomFilename
static QString contentSensitiveFilename(const QString& contentType);
public:
/**
* @brief applyManifest takes the given manifest object (and options), and begins merging that data with the running system
* @param options switches to control what gets imported
* @param systemDb The currently running/system database
* @throws FileError is there is an issue reading the incoming files
* @throws QSqlError If there is an issue ingesting database records from the exported database
*/
void applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb);
/// contentSensitiveExtension returns a file extension for the given content type. This, in turn,
/// relies on the underlying type to provide a sensible value. If no match is found, then ".bin"
/// is returned instead
static QString contentSensitiveExtension(const QString& contentType);
/**
* @brief exportManifest starts the long process of copying config and evidence into the specified directory.
* @param db the connection to the primary database
* @param outputDirPath the path to the expected export directory. Files will be placed under this directory
* (not wrapped in another directory)
* @param options exporting options (e.g. do you want to copy both evidence *and* config
* @throws FileError if there is a problem with the given directory, or with a generated filename
* @throws QSqlError if there is an issue accessing the system database, or the copied database
*/
void exportManifest(DatabaseConnection* db, const QString& outputDirPath,
const SystemManifestExportOptions& options);
/**
* @brief migrateConfig imports the config file associated with the started import
* @throws FileError if the config file cannot be copied
*/
void migrateConfig();
private:
/**
* @brief migrateConfig imports the config file associated with the started import
* @throws FileError if the config file cannot be copied
*/
void migrateConfig();
/**
* @brief migrateDb imports all of the database and evidence files associated with the started import
* emits onStatusUpdate signal for periodic progress updates
* emits onCopyFileError signal if there is an issue copying evidence files
* emits onFileProcessed for each file processed
* @param systemDb a pointer to the "standard" system database/running database
* @throws QSqlError
*/
void migrateDb(DatabaseConnection* systemDb);
/**
* @brief migrateDb imports all of the database and evidence files associated with the started import
* emits onStatusUpdate signal for periodic progress updates
* emits onCopyFileError signal if there is an issue copying evidence files
* emits onFileProcessed for each file processed
* @param systemDb a pointer to the "standard" system database/running database
* @throws QSqlError
*/
void migrateDb(DatabaseConnection* systemDb);
/// pathToFile is a small helper method to combine the absolute path to the manifest with the relative
/// path to the given filename. The result is an absolute path to the given file
QString pathToFile(const QString& filename);
/// pathToFile is a small helper method to combine the absolute path to the manifest with the relative
/// path to the given filename. The result is an absolute path to the given file
QString pathToFile(const QString& filename);
/**
* @brief copyEvidence will iteratively copy all evidence files provided to the indicated path. Files are renamed
* to avoid any name collisions. Files are namespaced into givenPath/evidence
* This emits a onCopyFileError signal if there is an issue copying files
* This emits a onFileProcessed signal when the attempted copy completes (so you may get an error _and_ processed signal on the same file. Error will be first)
* @param baseExportPath The path to the desired export directory
* @param allEvidence a vector of evidence _data_ to export (files will be found and read from within this function)
* @return an EvidenceManifest listing all of the files copied, and their new names.
*/
porting::EvidenceManifest copyEvidence(const QString& baseExportPath,
QList<model::Evidence> allEvidence);
/**
* @brief copyEvidence will iteratively copy all evidence files provided to the indicated path. Files are renamed
* to avoid any name collisions. Files are namespaced into givenPath/evidence
* This emits a onCopyFileError signal if there is an issue copying files
* This emits a onFileProcessed signal when the attempted copy completes (so you may get an error _and_ processed signal on the same file. Error will be first)
* @param baseExportPath The path to the desired export directory
* @param allEvidence a vector of evidence _data_ to export (files will be found and read from within this function)
* @return an EvidenceManifest listing all of the files copied, and their new names.
*/
porting::EvidenceManifest copyEvidence(const QString& baseExportPath,
QList<model::Evidence> allEvidence);
public:
/// os is the operating system associated with the originating export
QString os;
/// dbPath is the (relative) path to the database file from the originating export
QString dbPath;
/// configPath is the (relative) path to the config file from the originating export
QString configPath;
/// serversPath is currently unused
QString serversPath;
/// evidenceManifestPath is the (relative) path to the evidence manifest file from the originating export
QString evidenceManifestPath;
private:
/// pathToManifest is the (absolute) path to the system manifest file from the originating export
QString pathToManifest;
inline static const QString m_fileTemplate = QStringLiteral("%1/%2");
};
/// pathToManifest is the (absolute) path to the system manifest file from the originating export
QString m_pathToManifest;
inline static const QString m_fileTemplate = QStringLiteral("%1/%2");
};
}