[Server] Apply card art rules to playmats (#7532)

* [Server] Apply card art rules to playmats

Card art DENY rules were only enforced for user card art
(SetCardArtParams) while playmats using the same art bypassed them.

Move the rule lookup to Servatrice_DatabaseInterface::isCardArtAllowed
with a default-allow base on Server_DatabaseInterface, delegate the
session check to it, reject DENY-listed Command_SetPlaymat with
RespFunctionNotAllowed, and strip DENY-listed deck playmats on
DeckSelect so banned art is never broadcast.

* [Server] Address review nits on playmat card art rules

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-10-16 19:50:43 +02:00 • committed by GitHub
parent bbe438b09a
commit b11b096828
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 55 additions and 22 deletions

View file

@ -2068,3 +2068,25 @@ void Servatrice_DatabaseInterface::addAuditRecord(const QString &user,
query->bindValue(":details", details);
execSqlQuery(query);
}
bool Servatrice_DatabaseInterface::isCardArtAllowed(const QString &cardName, const QString &cardProviderId)
{
if (cardName.isEmpty() || !checkSql()) {
return true;
}
QSqlQuery *q = prepareQuery(
"SELECT mode FROM {prefix}_card_art_name_rules WHERE card_name = :name AND card_provider_id = :provider");
q->bindValue(":name", cardName);
q->bindValue(":provider", cardProviderId);
if (!execSqlQuery(q)) {
qWarning() << "Card art rule lookup failed; failing open for" << cardName;
return true;
}
if (!q->next()) {
return true; // default allow
}
return q->value(0).toString() != "DENY";
}

View file

@ -218,6 +218,7 @@ public:
};
UptimeSnapshot getLatestUptimeSnapshot(int serverId);
bool removeUserAvatar(const QString &userName);
bool isCardArtAllowed(const QString &cardName, const QString &cardProviderId) override;
bool addForgotPassword(const QString &user);
bool removeForgotPassword(const QString &user) override;
bool doesForgotPasswordExist(const QString &user);

View file

@ -3481,26 +3481,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountImage(const Comm
return Response::RespOk;
}
bool AbstractServerSocketInterface::isCardNameAllowed(const QString &cardName, const QString &cardProviderId)
{
QSqlQuery *q = sqlInterface->prepareQuery(
"SELECT mode FROM {prefix}_card_art_name_rules WHERE card_name = :name AND card_provider_id = :provider");
q->bindValue(":name", cardName);
q->bindValue(":provider", cardProviderId);
if (!sqlInterface->execSqlQuery(q)) {
qWarning() << "Card art rule lookup failed; failing open for" << cardName;
return true;
}
if (!q->next()) {
return true; // default allow
}
return q->value(0).toString() != "DENY";
}
Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const Command_SetCardArtParams &cmd,
ResponseContainer & /* rc */)
{
@ -3527,7 +3507,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const
return Response::RespOk;
}
if (!isCardNameAllowed(cardName, cardProviderId)) {
if (!sqlInterface->isCardArtAllowed(cardName, cardProviderId)) {
return Response::RespFunctionNotAllowed;
}

View file

@ -216,7 +216,6 @@ private:
Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc);
Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc);
bool isCardNameAllowed(const QString &cardName, const QString &cardProviderId);
Response::ResponseCode cmdSetCardArtParams(const Command_SetCardArtParams &cmd, ResponseContainer &);
Response::ResponseCode cmdAddCardArtRule(const Command_AddCardArtRule &cmd, ResponseContainer &);
Response::ResponseCode cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd, ResponseContainer &);