Harden VDF token parsing

Handle unterminated quoted strings as invalid tokens and stop returning raw pointers to entries in the game list.
This commit is contained in:
Marco Allegretti 2026-04-22 12:42:55 +02:00
parent 4dd0e17afb
commit eb177e0394
2 changed files with 27 additions and 11 deletions

View file

@ -75,7 +75,12 @@ public:
return {TokenType::CloseBrace, {}}; return {TokenType::CloseBrace, {}};
} }
if (current == QLatin1Char('"')) { if (current == QLatin1Char('"')) {
return {TokenType::String, readQuotedString()}; bool terminated = false;
const QString text = readQuotedString(&terminated);
if (!terminated) {
return {TokenType::Invalid, text};
}
return {TokenType::String, text};
} }
return {TokenType::String, readBareString()}; return {TokenType::String, readBareString()};
@ -106,14 +111,21 @@ private:
} }
} }
QString readQuotedString() QString readQuotedString(bool *terminated)
{ {
QString result; QString result;
++m_pos; ++m_pos;
if (terminated) {
*terminated = false;
}
while (m_pos < m_input.size()) { while (m_pos < m_input.size()) {
const QChar current = m_input.at(m_pos++); const QChar current = m_input.at(m_pos++);
if (current == QLatin1Char('"')) { if (current == QLatin1Char('"')) {
if (terminated) {
*terminated = true;
}
return result; return result;
} }
if (current == QLatin1Char('\\') && m_pos < m_input.size()) { if (current == QLatin1Char('\\') && m_pos < m_input.size()) {
@ -840,22 +852,24 @@ void GameLauncherProvider::clearLastLaunchError()
Q_EMIT lastLaunchErrorChanged(); Q_EMIT lastLaunchErrorChanged();
} }
GameLauncherProvider::GameEntry *GameLauncherProvider::findEntryByStorageId(const QString &storageId) int GameLauncherProvider::findEntryIndexByStorageId(const QString &storageId) const
{ {
for (auto &entry : m_allGames) { for (int index = 0; index < m_allGames.size(); ++index) {
if (entry.storageId == storageId) { if (m_allGames.at(index).storageId == storageId) {
return &entry; return index;
} }
} }
return nullptr; return -1;
} }
void GameLauncherProvider::markLaunchSucceeded(const QString &storageId, const QString &name) void GameLauncherProvider::markLaunchSucceeded(const QString &storageId, const QString &name)
{ {
if (auto *entry = findEntryByStorageId(storageId)) { const int entryIndex = findEntryIndexByStorageId(storageId);
if (entryIndex >= 0) {
auto &entry = m_allGames[entryIndex];
const auto now = QDateTime::currentDateTime(); const auto now = QDateTime::currentDateTime();
saveRecentTimestamp(entry->storageId, now); saveRecentTimestamp(entry.storageId, now);
entry->lastPlayed = now; entry.lastPlayed = now;
} }
setPendingLaunch(name); setPendingLaunch(name);

View file

@ -97,7 +97,9 @@ private:
void saveRecentTimestamp(const QString &storageId, const QDateTime &when); void saveRecentTimestamp(const QString &storageId, const QDateTime &when);
void applyFilter(); void applyFilter();
void launchEntry(GameEntry &entry); void launchEntry(GameEntry &entry);
GameEntry *findEntryByStorageId(const QString &storageId); // Returns the current m_allGames index for the storage id.
// Callers must re-lookup after any mutation that can rebuild or reorder the list.
int findEntryIndexByStorageId(const QString &storageId) const;
void markLaunchSucceeded(const QString &storageId, const QString &name); void markLaunchSucceeded(const QString &storageId, const QString &name);
void markLaunchFailed(const QString &name, const QString &error); void markLaunchFailed(const QString &name, const QString &error);
void setPendingLaunch(const QString &name); void setPendingLaunch(const QString &name);