Merge pull request #8834 from KebsCS/pr-command-argument-validation

Add validation for chat and console command arguments
This commit is contained in:
Dennis Felsing 2024-09-01 07:35:36 +00:00 committed by GitHub
commit bb3c76a290
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 81 additions and 33 deletions

View file

@ -53,7 +53,7 @@ public:
virtual int GetInteger(unsigned Index) const = 0; virtual int GetInteger(unsigned Index) const = 0;
virtual float GetFloat(unsigned Index) const = 0; virtual float GetFloat(unsigned Index) const = 0;
virtual const char *GetString(unsigned Index) const = 0; virtual const char *GetString(unsigned Index) const = 0;
virtual ColorHSLA GetColor(unsigned Index, bool Light) const = 0; virtual std::optional<ColorHSLA> GetColor(unsigned Index, bool Light) const = 0;
virtual void RemoveArgument(unsigned Index) = 0; virtual void RemoveArgument(unsigned Index) = 0;

View file

@ -111,14 +111,16 @@ void SIntConfigVariable::ResetToOld()
void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData) void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData)
{ {
SColorConfigVariable *pData = static_cast<SColorConfigVariable *>(pUserData); SColorConfigVariable *pData = static_cast<SColorConfigVariable *>(pUserData);
char aBuf[IConsole::CMDLINE_LENGTH + 64];
if(pResult->NumArguments()) if(pResult->NumArguments())
{ {
if(pData->CheckReadOnly()) if(pData->CheckReadOnly())
return; return;
const ColorHSLA Color = pResult->GetColor(0, pData->m_Light); const auto Color = pResult->GetColor(0, pData->m_Light);
const unsigned Value = Color.Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha); if(Color)
{
const unsigned Value = Color->Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha);
*pData->m_pVariable = Value; *pData->m_pVariable = Value;
if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME) if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
@ -126,7 +128,12 @@ void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUs
} }
else else
{ {
char aBuf[256]; str_format(aBuf, sizeof(aBuf), "%s is not a valid color.", pResult->GetString(0));
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf);
}
}
else
{
str_format(aBuf, sizeof(aBuf), "Value: %u", *pData->m_pVariable); str_format(aBuf, sizeof(aBuf), "Value: %u", *pData->m_pVariable);
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf); pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf);
@ -493,8 +500,8 @@ void CConfigManager::Con_Toggle(IConsole::IResult *pResult, void *pUserData)
{ {
SColorConfigVariable *pColorVariable = static_cast<SColorConfigVariable *>(pVariable); SColorConfigVariable *pColorVariable = static_cast<SColorConfigVariable *>(pVariable);
const float Darkest = pColorVariable->m_Light ? 0.5f : 0.0f; const float Darkest = pColorVariable->m_Light ? 0.5f : 0.0f;
const ColorHSLA Value = *pColorVariable->m_pVariable == pResult->GetColor(1, pColorVariable->m_Light).Pack(Darkest, pColorVariable->m_Alpha) ? pResult->GetColor(2, pColorVariable->m_Light) : pResult->GetColor(1, pColorVariable->m_Light); const std::optional<ColorHSLA> Value = *pColorVariable->m_pVariable == pResult->GetColor(1, pColorVariable->m_Light).value_or(ColorHSLA(0, 0, 0)).Pack(Darkest, pColorVariable->m_Alpha) ? pResult->GetColor(2, pColorVariable->m_Light) : pResult->GetColor(1, pColorVariable->m_Light);
pColorVariable->SetValue(Value.Pack(Darkest, pColorVariable->m_Alpha)); pColorVariable->SetValue(Value.value_or(ColorHSLA(0, 0, 0)).Pack(Darkest, pColorVariable->m_Alpha));
} }
else if(pVariable->m_Type == SConfigVariable::VAR_STRING) else if(pVariable->m_Type == SConfigVariable::VAR_STRING)
{ {

View file

@ -40,22 +40,29 @@ float CConsole::CResult::GetFloat(unsigned Index) const
return str_tofloat(m_apArgs[Index]); return str_tofloat(m_apArgs[Index]);
} }
ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light) const std::optional<ColorHSLA> CConsole::CResult::GetColor(unsigned Index, bool Light) const
{ {
if(Index >= m_NumArgs) if(Index >= m_NumArgs)
return ColorHSLA(0, 0, 0); return std::nullopt;
const char *pStr = m_apArgs[Index]; const char *pStr = m_apArgs[Index];
if(str_isallnum(pStr) || ((pStr[0] == '-' || pStr[0] == '+') && str_isallnum(pStr + 1))) // Teeworlds Color (Packed HSL) if(str_isallnum(pStr) || ((pStr[0] == '-' || pStr[0] == '+') && str_isallnum(pStr + 1))) // Teeworlds Color (Packed HSL)
{ {
const ColorHSLA Hsla = ColorHSLA(str_toulong_base(pStr, 10), true); unsigned long Value = str_toulong_base(pStr, 10);
if(Value == std::numeric_limits<unsigned long>::max())
return std::nullopt;
const ColorHSLA Hsla = ColorHSLA(Value, true);
if(Light) if(Light)
return Hsla.UnclampLighting(); return Hsla.UnclampLighting();
return Hsla; return Hsla;
} }
else if(*pStr == '$') // Hex RGB/RGBA else if(*pStr == '$') // Hex RGB/RGBA
{ {
return color_cast<ColorHSLA>(color_parse<ColorRGBA>(pStr + 1).value_or(ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f))); auto ParsedColor = color_parse<ColorRGBA>(pStr + 1);
if(ParsedColor)
return color_cast<ColorHSLA>(ParsedColor.value());
else
return std::nullopt;
} }
else if(!str_comp_nocase(pStr, "red")) else if(!str_comp_nocase(pStr, "red"))
return ColorHSLA(0.0f / 6.0f, 1, .5f); return ColorHSLA(0.0f / 6.0f, 1, .5f);
@ -76,7 +83,7 @@ ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light) const
else if(!str_comp_nocase(pStr, "black")) else if(!str_comp_nocase(pStr, "black"))
return ColorHSLA(0, 0, 0); return ColorHSLA(0, 0, 0);
return ColorHSLA(0, 0, 0); return std::nullopt;
} }
const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const
@ -129,12 +136,12 @@ int CConsole::ParseStart(CResult *pResult, const char *pString, int Length)
return 0; return 0;
} }
int CConsole::ParseArgs(CResult *pResult, const char *pFormat) int CConsole::ParseArgs(CResult *pResult, const char *pFormat, FCommandCallback pfnCallback)
{ {
char Command = *pFormat; char Command = *pFormat;
char *pStr; char *pStr;
int Optional = 0; int Optional = 0;
int Error = 0; int Error = PARSEARGS_OK;
pResult->ResetVictim(); pResult->ResetVictim();
@ -155,7 +162,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
{ {
if(!Optional) if(!Optional)
{ {
Error = 1; Error = PARSEARGS_MISSING_VALUE;
break; break;
} }
@ -191,7 +198,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
pStr++; // skip due to escape pStr++; // skip due to escape
} }
else if(pStr[0] == 0) else if(pStr[0] == 0)
return 1; // return error return PARSEARGS_MISSING_VALUE; // return error
*pDst = *pStr; *pDst = *pStr;
pDst++; pDst++;
@ -215,13 +222,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
if(Command == 'r') // rest of the string if(Command == 'r') // rest of the string
break; break;
else if(Command == 'v') // validate victim else if(Command == 'v' || Command == 'i' || Command == 'f' || Command == 's')
pStr = str_skip_to_whitespace(pStr);
else if(Command == 'i') // validate int
pStr = str_skip_to_whitespace(pStr);
else if(Command == 'f') // validate float
pStr = str_skip_to_whitespace(pStr);
else if(Command == 's') // validate string
pStr = str_skip_to_whitespace(pStr); pStr = str_skip_to_whitespace(pStr);
if(pStr[0] != 0) // check for end of string if(pStr[0] != 0) // check for end of string
@ -230,6 +231,32 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
pStr++; pStr++;
} }
// validate args
if(Command == 'i')
{
// don't validate colors here
if(pfnCallback != &SColorConfigVariable::CommandCallback)
{
int Value;
if(!str_toint(pResult->GetString(pResult->NumArguments() - 1), &Value) ||
Value == std::numeric_limits<int>::max() || Value == std::numeric_limits<int>::min())
{
Error = PARSEARGS_INVALID_INTEGER;
break;
}
}
}
else if(Command == 'f')
{
float Value;
if(!str_tofloat(pResult->GetString(pResult->NumArguments() - 1), &Value) ||
Value == std::numeric_limits<float>::max() || Value == std::numeric_limits<float>::min())
{
Error = PARSEARGS_INVALID_FLOAT;
break;
}
}
if(pVictim) if(pVictim)
{ {
pResult->SetVictim(pVictim); pResult->SetVictim(pVictim);
@ -487,9 +514,14 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientId, bo
if(Stroke || IsStrokeCommand) if(Stroke || IsStrokeCommand)
{ {
if(ParseArgs(&Result, pCommand->m_pParams)) if(int Error = ParseArgs(&Result, pCommand->m_pParams, pCommand->m_pfnCallback))
{ {
char aBuf[TEMPCMD_NAME_LENGTH + TEMPCMD_PARAMS_LENGTH + 32]; char aBuf[CMDLINE_LENGTH + 64];
if(Error == PARSEARGS_INVALID_INTEGER)
str_format(aBuf, sizeof(aBuf), "%s is not a valid integer.", Result.GetString(Result.NumArguments() - 1));
else if(Error == PARSEARGS_INVALID_FLOAT)
str_format(aBuf, sizeof(aBuf), "%s is not a valid decimal number.", Result.GetString(Result.NumArguments() - 1));
else
str_format(aBuf, sizeof(aBuf), "Invalid arguments. Usage: %s %s", pCommand->m_pName, pCommand->m_pParams); str_format(aBuf, sizeof(aBuf), "Invalid arguments. Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
Print(OUTPUT_LEVEL_STANDARD, "chatresp", aBuf); Print(OUTPUT_LEVEL_STANDARD, "chatresp", aBuf);
} }

View file

@ -115,7 +115,7 @@ class CConsole : public IConsole
const char *GetString(unsigned Index) const override; const char *GetString(unsigned Index) const override;
int GetInteger(unsigned Index) const override; int GetInteger(unsigned Index) const override;
float GetFloat(unsigned Index) const override; float GetFloat(unsigned Index) const override;
ColorHSLA GetColor(unsigned Index, bool Light) const override; std::optional<ColorHSLA> GetColor(unsigned Index, bool Light) const override;
void RemoveArgument(unsigned Index) override void RemoveArgument(unsigned Index) override
{ {
@ -144,7 +144,16 @@ class CConsole : public IConsole
}; };
int ParseStart(CResult *pResult, const char *pString, int Length); int ParseStart(CResult *pResult, const char *pString, int Length);
int ParseArgs(CResult *pResult, const char *pFormat);
enum
{
PARSEARGS_OK = 0,
PARSEARGS_MISSING_VALUE,
PARSEARGS_INVALID_INTEGER,
PARSEARGS_INVALID_FLOAT,
};
int ParseArgs(CResult *pResult, const char *pFormat, FCommandCallback pfnCallback = 0);
/* /*
this function will set pFormat to the next parameter (i,s,r,v,?) it contains and this function will set pFormat to the next parameter (i,s,r,v,?) it contains and

View file

@ -109,8 +109,8 @@ void cxxbridge1$IConsole_IResult$GetString(const ::IConsole_IResult &self, ::std
} }
void cxxbridge1$IConsole_IResult$GetColor(const ::IConsole_IResult &self, ::std::uint32_t Index, bool Light, ::ColorHSLA *return$) noexcept { void cxxbridge1$IConsole_IResult$GetColor(const ::IConsole_IResult &self, ::std::uint32_t Index, bool Light, ::ColorHSLA *return$) noexcept {
::ColorHSLA (::IConsole_IResult::*GetColor$)(::std::uint32_t, bool) const = &::IConsole_IResult::GetColor; std::optional<::ColorHSLA> (::IConsole_IResult::*GetColor$)(::std::uint32_t, bool) const = &::IConsole_IResult::GetColor;
new (return$) ::ColorHSLA((self.*GetColor$)(Index, Light)); new(return$)::ColorHSLA((self.*GetColor$)(Index, Light).value_or(::ColorHSLA(0, 0, 0)));
} }
::std::int32_t cxxbridge1$IConsole_IResult$NumArguments(const ::IConsole_IResult &self) noexcept { ::std::int32_t cxxbridge1$IConsole_IResult$NumArguments(const ::IConsole_IResult &self) noexcept {