mirror of https://github.com/infosecn1nja/C3.git
Validate channel creation arguments against the form definiton
parent
456f84338f
commit
d95a7dd653
|
@ -69,7 +69,7 @@ try
|
|||
auto channel = C3::Linter::MakeDevice(createParams, chInfo);
|
||||
|
||||
std::cout << "Create channel 2" << std::endl;
|
||||
auto const& ch2Args = config.m_ComplementaryChannelArguments ? *config.m_ComplementaryChannelArguments : form.GetComplementaryArgs(createParams);
|
||||
auto const& ch2Args = config.m_ComplementaryChannelArguments ? *config.m_ComplementaryChannelArguments : form.GetComplementaryArgs(config.m_ChannelArguments);
|
||||
json createParams2 = form.FillForm(ch2Args);
|
||||
auto ch2 = C3::Linter::MakeDevice(createParams2, chInfo);
|
||||
|
||||
|
|
|
@ -297,6 +297,7 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Form.h" />
|
||||
<ClInclude Include="FormElement.hpp" />
|
||||
<ClInclude Include="InputContext.h" />
|
||||
<ClInclude Include="InputVector.h" />
|
||||
<ClInclude Include="StdAfx.h" />
|
||||
|
|
|
@ -15,10 +15,7 @@ namespace MWR::C3::Linter
|
|||
auto createParams = m_ArgumentsForm;
|
||||
auto fillArg = [&input](json& arg)
|
||||
{
|
||||
auto& name = arg.at("name").get_ref<std::string const&>();
|
||||
auto& type = arg.at("type").get_ref<std::string const&>();
|
||||
arg["value"] = input.GetNext();
|
||||
// TODO input validation against m_ArgumentsForm min, max, type, etc.
|
||||
ValidateAndSet(arg, input.GetNext());
|
||||
};
|
||||
for (size_t i = 0; i < createParams.size(); ++i)
|
||||
{
|
||||
|
@ -33,26 +30,46 @@ namespace MWR::C3::Linter
|
|||
}
|
||||
catch (std::out_of_range&)
|
||||
{
|
||||
throw std::runtime_error("Failed to create channel: not enough arguments given");
|
||||
input.Reset();
|
||||
std::string message;
|
||||
auto AddParameterMessage = [&message, &input](json& arg)
|
||||
{
|
||||
auto& name = arg.at("name").get_ref<std::string const&>();
|
||||
auto& type = arg.at("type").get_ref<std::string const&>();
|
||||
message += name + " (" + type + ") > " + input.GetOptionalNext().value_or("") + '\n';
|
||||
};
|
||||
for (size_t i = 0; i < m_ArgumentsForm.size(); ++i)
|
||||
{
|
||||
auto& arg = m_ArgumentsForm[i];
|
||||
if (arg.is_array())
|
||||
for (size_t j = 0; j < arg.size(); ++j)
|
||||
AddParameterMessage(arg[j]);
|
||||
else
|
||||
AddParameterMessage(arg);
|
||||
}
|
||||
throw std::runtime_error("Failed to create channel: not enough arguments given.\nRequired parameter > received argument\n" + message);
|
||||
}
|
||||
}
|
||||
|
||||
StringVector Form::GetComplementaryArgs(json const& form)
|
||||
StringVector Form::GetComplementaryArgs(StringVector const& input)
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
for (auto const& arg : form)
|
||||
StringVector args;
|
||||
size_t k = 0;
|
||||
for (size_t i = 0; i < m_ArgumentsForm.size(); ++i)
|
||||
{
|
||||
auto& arg = m_ArgumentsForm[i];
|
||||
if (arg.is_array())
|
||||
{
|
||||
for (size_t j = 0; j < arg.size(); ++j)
|
||||
{
|
||||
auto rotateRight = [s = arg.size()](size_t index) { return (index + 1) % s; };
|
||||
args.emplace_back(arg[rotateRight(j)].at("value").get<std::string>());
|
||||
auto rotateRight = [s = arg.size()](size_t index) { return (index + s - 1) % s; };
|
||||
args.emplace_back(input.at(k + rotateRight(j)));
|
||||
}
|
||||
k += arg.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
args.emplace_back(arg["value"].get<std::string>());
|
||||
args.emplace_back(input.at(k++));
|
||||
}
|
||||
}
|
||||
return args;
|
||||
|
|
|
@ -9,9 +9,10 @@ namespace MWR::C3::Linter
|
|||
|
||||
json FillForm(InputVector input);
|
||||
|
||||
StringVector GetComplementaryArgs(json const& form);
|
||||
StringVector GetComplementaryArgs(StringVector const& input);
|
||||
|
||||
private:
|
||||
// TODO Form should contain a list of form elements (validated), not just a json
|
||||
json m_ArgumentsForm;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
#pragma once
|
||||
|
||||
namespace MWR::C3::Linter
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class FormElement
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
Unknown = 0,
|
||||
Uint8,
|
||||
Uint16,
|
||||
Uint32,
|
||||
Uint64,
|
||||
Int8,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
Float,
|
||||
Boolean,
|
||||
String,
|
||||
Ip,
|
||||
Binary,
|
||||
};
|
||||
|
||||
virtual void ValidateAndSet(std::string_view input) = 0;
|
||||
|
||||
protected:
|
||||
FormElement(json& definition) : m_Definition(definition)
|
||||
{
|
||||
}
|
||||
|
||||
json& m_Definition;
|
||||
};
|
||||
|
||||
inline void CheckLengthConstraint(json const& definition, std::string_view input)
|
||||
{
|
||||
if (definition.contains("min") && input.length() < definition["min"].get<size_t>())
|
||||
throw std::invalid_argument("Input \""s + std::string(input) + "\" too short for argument " + definition.at("name").get<std::string>() + ". (min = " + std::to_string(definition["min"].get<size_t>()) + ", actual = " + std::to_string(input.length()) + ')');
|
||||
|
||||
if (definition.contains("max") && input.length() > definition["max"].get<size_t>())
|
||||
throw std::invalid_argument("Input \""s + std::string(input) + "\" too long for argument " + definition.at("name").get<std::string>() + ". (max = " + std::to_string(definition["max"].get<size_t>()) + ", actual = " + std::to_string(input.length()) + ')');
|
||||
}
|
||||
|
||||
template<typename Numeric>
|
||||
void CheckValueConstraint(json const& definition, Numeric value)
|
||||
{
|
||||
if (definition.contains("min") && value < definition["min"])
|
||||
throw std::invalid_argument("Value too low for argument " + definition.at("name").get<std::string>() + ". (min = " + std::to_string(definition["min"].get<Numeric>()) + ", actual = " + std::to_string(value) + ')');
|
||||
|
||||
if (definition.contains("max") && value > definition["max"])
|
||||
throw std::invalid_argument("Value too high for argument " + definition.at("name").get<std::string>() + ". (min = " + std::to_string(definition["min"].get<Numeric>()) + ", actual = " + std::to_string(value) + ')');
|
||||
};
|
||||
|
||||
class BooleanFormElement : public FormElement
|
||||
{
|
||||
public:
|
||||
BooleanFormElement(json& element) : FormElement(element)
|
||||
{
|
||||
}
|
||||
|
||||
void ValidateAndSet(std::string_view input) override
|
||||
{
|
||||
std::string lowercase(input);
|
||||
std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), ::tolower);
|
||||
auto trueOptions = { "true", "yes", "y", "1" };
|
||||
m_Definition["value"] = std::any_of(begin(trueOptions), end(trueOptions), [&lowercase](auto&& trueOption) {return lowercase == trueOption; });
|
||||
}
|
||||
};
|
||||
|
||||
class StringFormElement : public FormElement
|
||||
{
|
||||
public:
|
||||
StringFormElement(json& element) : FormElement(element)
|
||||
{
|
||||
}
|
||||
|
||||
void ValidateAndSet(std::string_view input) override
|
||||
{
|
||||
CheckLengthConstraint(m_Definition, input);
|
||||
m_Definition["value"] = input;
|
||||
}
|
||||
};
|
||||
|
||||
class IpFormElement : public FormElement
|
||||
{
|
||||
public:
|
||||
IpFormElement(json& element) : FormElement(element)
|
||||
{
|
||||
}
|
||||
|
||||
void ValidateAndSet(std::string_view input) override
|
||||
{
|
||||
if (!IsIpV4(std::string(input)))
|
||||
throw std::invalid_argument("Failed to IPv4 value from input ("s + std::string(input) + ')');
|
||||
m_Definition["value"] = input;
|
||||
}
|
||||
|
||||
static bool IsIpV4(std::string const& input)
|
||||
{
|
||||
sockaddr_in client;
|
||||
client.sin_family = AF_INET;
|
||||
switch (InetPtonA(AF_INET, input.c_str(), &client.sin_addr.s_addr))
|
||||
{
|
||||
case 1:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class BinaryFormElement : public FormElement
|
||||
{
|
||||
public:
|
||||
BinaryFormElement(json& element) : FormElement(element)
|
||||
{
|
||||
}
|
||||
|
||||
void ValidateAndSet(std::string_view input) override
|
||||
{
|
||||
auto decoded = base64::decode<std::string>(input);
|
||||
CheckLengthConstraint(m_Definition, decoded);
|
||||
m_Definition["value"] = input;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Numeric>
|
||||
class NumericFormElement : public FormElement
|
||||
{
|
||||
public:
|
||||
NumericFormElement(json& element) : FormElement(element)
|
||||
{
|
||||
}
|
||||
|
||||
using NumericType = Numeric;
|
||||
void ValidateAndSet(std::string_view input) override
|
||||
{
|
||||
NumericType value;
|
||||
auto x = std::from_chars(input.data(), input.data() + input.size(), value);
|
||||
if (x.ptr != input.data() + input.size())
|
||||
throw std::invalid_argument("Failed to read numeric value from input \""s + std::string(input) + '"');
|
||||
CheckValueConstraint(m_Definition, value);
|
||||
m_Definition["value"] = value;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::unique_ptr<FormElement> MakeFormElement(json& element)
|
||||
{
|
||||
switch (element.at("type").get<FormElement::Type>())
|
||||
{
|
||||
case FormElement::Type::Uint8:
|
||||
return std::make_unique<NumericFormElement<uint8_t>>(element);
|
||||
case FormElement::Type::Uint16:
|
||||
return std::make_unique<NumericFormElement<uint16_t>>(element);
|
||||
case FormElement::Type::Uint32:
|
||||
return std::make_unique<NumericFormElement<uint32_t>>(element);
|
||||
case FormElement::Type::Uint64:
|
||||
return std::make_unique<NumericFormElement<uint64_t>>(element);
|
||||
case FormElement::Type::Int8:
|
||||
return std::make_unique<NumericFormElement<int8_t>>(element);
|
||||
case FormElement::Type::Int16:
|
||||
return std::make_unique<NumericFormElement<int16_t>>(element);
|
||||
case FormElement::Type::Int32:
|
||||
return std::make_unique<NumericFormElement<int32_t>>(element);
|
||||
case FormElement::Type::Int64:
|
||||
return std::make_unique<NumericFormElement<int64_t>>(element);
|
||||
case FormElement::Type::Float:
|
||||
return std::make_unique<NumericFormElement<float>>(element);
|
||||
case FormElement::Type::Boolean:
|
||||
return std::make_unique<BooleanFormElement>(element);
|
||||
case FormElement::Type::String:
|
||||
return std::make_unique<StringFormElement>(element);
|
||||
case FormElement::Type::Ip:
|
||||
return std::make_unique<IpFormElement>(element);
|
||||
case FormElement::Type::Binary:
|
||||
return std::make_unique<BinaryFormElement>(element);
|
||||
default:
|
||||
throw std::runtime_error("Unknown form argument type: \"" + element["type"].get<std::string>() + '"');
|
||||
}
|
||||
}
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM
|
||||
(
|
||||
FormElement::Type,
|
||||
{
|
||||
{FormElement::Type::Unknown, nullptr},
|
||||
{FormElement::Type::Uint8, "uint8"},
|
||||
{FormElement::Type::Uint16, "uint16"},
|
||||
{FormElement::Type::Uint32, "uint32"},
|
||||
{FormElement::Type::Uint64, "uint64"},
|
||||
{FormElement::Type::Int8, "int8"},
|
||||
{FormElement::Type::Int16, "int16"},
|
||||
{FormElement::Type::Int32, "int32"},
|
||||
{FormElement::Type::Int64, "int64"},
|
||||
{FormElement::Type::Float, "float"},
|
||||
{FormElement::Type::Boolean, "boolean"},
|
||||
{FormElement::Type::String, "string"},
|
||||
{FormElement::Type::Ip, "ip"},
|
||||
{FormElement::Type::Binary, "binary"},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
inline void ValidateAndSet(json& element, std::string_view input)
|
||||
{
|
||||
auto formElement = MakeFormElement(element);
|
||||
formElement->ValidateAndSet(input);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,4 +26,16 @@ namespace MWR::C3::Linter
|
|||
throw std::out_of_range("Input vector has no more elements");
|
||||
return *m_Current++;
|
||||
}
|
||||
|
||||
void InputVector::Reset()
|
||||
{
|
||||
m_Current = m_Input.cbegin();
|
||||
}
|
||||
|
||||
std::optional<std::string> InputVector::GetOptionalNext()
|
||||
{
|
||||
if (m_Current == m_Input.cend())
|
||||
return {};
|
||||
return *m_Current++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,13 @@ namespace MWR::C3::Linter
|
|||
InputVector& operator = (InputVector const& other);
|
||||
|
||||
std::string GetNext();
|
||||
void Reset();
|
||||
size_t Size() { return m_Input.size(); }
|
||||
std::string& operator[](size_t index) { return m_Input.operator [](index); }
|
||||
std::string const& operator[](size_t index) const { return m_Input.operator [](index); }
|
||||
std::string at(size_t index) { return m_Input.at(index); }
|
||||
std::string const& at(size_t index) const { return m_Input.at(index); }
|
||||
std::optional<std::string> GetOptionalNext();
|
||||
private:
|
||||
StringVector m_Input;
|
||||
StringVector::const_iterator m_Current;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
// Standard library includes.
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <charconv>
|
||||
|
||||
// C3 inclusion.
|
||||
#include "Common/MWR/C3/Sdk.hpp"
|
||||
|
@ -13,6 +15,9 @@
|
|||
|
||||
using json = nlohmann::json;
|
||||
|
||||
#include "Common/CppCodec/base64_default_rfc4648.hpp"
|
||||
|
||||
#include "InputContext.h"
|
||||
#include "InputVector.h"
|
||||
#include "FormElement.hpp"
|
||||
#include "Form.h"
|
||||
|
|
Loading…
Reference in New Issue