Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
validator.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:insignificant reason:build-tool
4
5#include "validator.h"
6#include "driver.h"
7#include "ui4.h"
8#include "uic.h"
9
10#include <qstringview.h>
11
13
14using namespace Qt::StringLiterals;
15
16// An approximation of "Unicode Standard Annex #31" for checking property
17// and enumeration identifiers to prevent code injection attacks.
18// FIXME 6.9: Simplify according to QTBUG-126860
19static bool isIdStart(QChar c)
20{
21 bool result = false;
22 switch (c.category()) {
23 case QChar::Letter_Uppercase:
24 case QChar::Letter_Lowercase:
25 case QChar::Letter_Titlecase:
26 case QChar::Letter_Modifier:
27 case QChar::Letter_Other:
28 case QChar::Number_Letter:
29 result = true;
30 break;
31 default:
32 result = c == u'_';
33 break;
34 }
35 return result;
36}
37
38static bool isIdContinuation(QChar c)
39{
40 bool result = false;
41 switch (c.category()) {
42 case QChar::Letter_Uppercase:
43 case QChar::Letter_Lowercase:
44 case QChar::Letter_Titlecase:
45 case QChar::Letter_Modifier:
46 case QChar::Letter_Other:
47 case QChar::Number_Letter:
48 case QChar::Mark_NonSpacing:
49 case QChar::Mark_SpacingCombining:
50 case QChar::Number_DecimalDigit:
51 case QChar::Punctuation_Connector: // '_'
52 result = true;
53 break;
54 default:
55 break;
56 }
57 return result;
58}
59
60static bool isEnumIdContinuation(QChar c)
61{
62 return c == u':' || c == u'|' || c == u' ' || isIdContinuation(c);
63}
64
66{
67 return !name.isEmpty() && isIdStart(name.at(0))
68 && std::all_of(name.cbegin() + 1, name.cend(), isIdContinuation);
69}
70
71static bool checkEnumValue(QStringView name)
72{
73 return !name.isEmpty() && isIdStart(name.at(0))
74 && std::all_of(name.cbegin() + 1, name.cend(), isEnumIdContinuation);
75}
76
77static bool isClassNameContinuation(QChar c)
78{
79 return c == u':' || c == u'.' || isIdContinuation(c);
80}
81
82// Template arguments of promoted widgets, e.g. "ItemViewSized<QListView>"
84{
85 return c == u'<' || c == u'>' || c == u',' || c == u' ' || isClassNameContinuation(c);
86}
87
88static bool checkClassName(QStringView name, bool allowTemplates = false)
89{
90 const auto isValid = allowTemplates ? isTemplateClassNameContinuation
91 : isClassNameContinuation;
92 return !name.isEmpty() && isIdStart(name.at(0))
93 && std::all_of(name.cbegin() + 1, name.cend(), isValid);
94}
95
96// Checks for characters that would allow a code injection (C++/Python):
97// Only explicitly listed characters are allowed.
98static bool isHeaderChar(QChar c)
99{
100 return c == u'/' || c == u'\\' || c == u'.' || c == u'-' || c == u'+' || c == u':'
101 || c == u',' || c == u' ' || c == u'~' || isIdContinuation(c);
102}
103
104static bool checkHeader(QStringView name)
105{
106 return !name.isEmpty() && std::all_of(name.cbegin(), name.cend(), isHeaderChar);
107}
108
109// Parameter list of a signal/slot signature, e.g. "int,QObject*" or "QMap<QString,int>"
110static bool isSignatureParameterChar(QChar c)
111{
112 return c == u',' || c == u' ' || c == u'*' || c == u'&' || c == u'<' || c == u'>'
113 || c == u':' || isIdContinuation(c);
114}
115
116// Signal/slot signature of a connection, e.g. "valueChanged(int)": An identifier
117// followed by a parameter list that must not contain parentheses
119{
120 const qsizetype paren = name.indexOf(u'(');
121 if (paren <= 0 || !name.endsWith(u')') || !checkPropertyName(name.first(paren)))
122 return false;
123 const QStringView params = name.sliced(paren + 1, name.size() - paren - 2);
124 return std::all_of(params.cbegin(), params.cend(), isSignatureParameterChar);
125}
126
127static QString msgInvalidValue(const QString &name, const QString &value)
128{
129 return "Invalid property value: \""_L1 + name + "\": \""_L1 + value + u'"';
130}
131
132static QString msgInvalidPropertyName(const QString &name)
133{
134 return "Invalid property name: \""_L1 + name + u'"';
135}
136
137static QString msgInvalidClassName(const QString &name)
138{
139 return "Invalid class name: \""_L1 + name + u'"';
140}
141
142static QString msgInvalidPixmapFunction(const QString &name)
143{
144 return "Invalid pixmap function name: \""_L1 + name + u'"';
145}
146
147static QString msgInvalidAddPageMethod(const QString &name)
148{
149 return "Invalid add page method name: \""_L1 + name + u'"';
150}
151
152static QString msgInvalidHeader(const QString &name)
153{
154 return "Invalid header: \""_L1 + name + u'"';
155}
156
157static QString msgInvalidConnection(const DomConnection *connection)
158{
159 return "Invalid connection: \""_L1 + connection->elementSender() + "::"_L1
160 + connection->elementSignal() + " -> "_L1 + connection->elementReceiver() + "::"_L1
161 + connection->elementSlot() + u'"';
162}
163
164static void checkProperties(const QList<DomProperty *> &properties, QStringList *errors)
165{
166 for (DomProperty *p : properties) {
167 const bool isDynamicProperty = p->hasAttributeStdset() && p->attributeStdset() == 0;
168 const QString &name = p->attributeName();
169 if (!isDynamicProperty && !checkPropertyName(name))
170 errors->append(msgInvalidPropertyName(name));
171 switch (p->kind()) {
172 case DomProperty::Set:
173 p->setElementSet(p->elementSet().trimmed());
174 if (!checkEnumValue(p->elementSet()))
175 errors->append(msgInvalidValue(name, p->elementSet()));
176 break;
177 case DomProperty::Enum:
178 p->setElementEnum(p->elementEnum().trimmed());
179 if (!checkEnumValue(p->elementEnum()))
180 errors->append(msgInvalidValue(name, p->elementEnum()));
181 default:
182 break;
183 }
184 }
185}
186
188 m_driver(uic->driver())
189{
190}
191
192void Validator::acceptUI(DomUI *node)
193{
195
196 if (!checkClassName(node->elementClass()))
197 m_errors.append(msgInvalidClassName(node->elementClass()));
198
199 if (node->hasElementPixmapFunction()) {
200 const QString &pixmapFunction = node->elementPixmapFunction();
201 // Accommodate for legacy forms with empty entries
202 if (!pixmapFunction.isEmpty() && !checkClassName(pixmapFunction))
203 m_errors.append(msgInvalidPixmapFunction(node->elementPixmapFunction()));
204 }
205
206 if (node->hasElementCustomWidgets())
207 acceptCustomWidgets(node->elementCustomWidgets());
208 if (node->hasElementIncludes())
209 acceptIncludes(node->elementIncludes());
210 if (node->hasElementConnections())
211 acceptConnections(node->elementConnections());
212}
213
214void Validator::acceptWidget(DomWidget *node)
215{
216 (void) m_driver->findOrInsertWidget(node);
217
218 checkProperties(node->elementProperty(), &m_errors);
219
220 if (!checkClassName(node->attributeClass(), true))
221 m_errors.append(msgInvalidClassName(node->attributeClass()));
222
224}
225
226void Validator::acceptLayoutItem(DomLayoutItem *node)
227{
228 (void) m_driver->findOrInsertLayoutItem(node);
229
231}
232
233void Validator::acceptLayout(DomLayout *node)
234{
235 (void) m_driver->findOrInsertLayout(node);
236
237 checkProperties(node->elementProperty(), &m_errors);
238
240}
241
242void Validator::acceptActionGroup(DomActionGroup *node)
243{
244 (void) m_driver->findOrInsertActionGroup(node);
245
246 checkProperties(node->elementProperty(), &m_errors);
247
249}
250
251void Validator::acceptAction(DomAction *node)
252{
253 (void) m_driver->findOrInsertAction(node);
254
255 checkProperties(node->elementProperty(), &m_errors);
256
258}
259
260void Validator::acceptCustomWidget(DomCustomWidget *customWidget)
261{
262 if (customWidget->hasElementAddPageMethod()
263 && !checkPropertyName(customWidget->elementAddPageMethod())) {
264 m_errors.append(msgInvalidAddPageMethod(customWidget->elementAddPageMethod()));
265 }
266 if (customWidget->hasElementHeader()) {
267 const QString &header = customWidget->elementHeader()->text();
268 if (!checkHeader(header))
269 m_errors.append(msgInvalidHeader(header));
270 }
271 TreeWalker::acceptCustomWidget(customWidget);
272}
273
274void Validator::acceptInclude(DomInclude *incl)
275{
276 if (!checkHeader(incl->text()))
277 m_errors.append(msgInvalidHeader(incl->text()));
279}
280
281void Validator::acceptConnection(DomConnection *connection)
282{
283 if (!checkConnectionSignature(connection->elementSignal())
284 || !checkConnectionSignature(connection->elementSlot())) {
285 m_errors.append(msgInvalidConnection(connection));
286 }
288}
289
290QT_END_NAMESPACE
QString findOrInsertWidget(const DomWidget *ui_widget)
Definition driver.cpp:66
QString findOrInsertActionGroup(const DomActionGroup *ui_group)
Definition driver.cpp:101
QString findOrInsertLayoutItem(const DomLayoutItem *ui_layoutItem)
Definition driver.cpp:83
QString findOrInsertAction(const DomAction *ui_action)
Definition driver.cpp:106
QString findOrInsertLayout(const DomLayout *ui_layout)
Definition driver.cpp:78
Definition qlist.h:82
\inmodule QtCore
Definition qstringview.h:77
Definition uic.h:31
Driver * driver() const
Definition uic.h:39
Combined button and popup list for selecting options.
static bool isEnumIdContinuation(QChar c)
Definition validator.cpp:60
static bool checkConnectionSignature(QStringView name)
static QString msgInvalidConnection(const DomConnection *connection)
static QString msgInvalidValue(const QString &name, const QString &value)
static bool isSignatureParameterChar(QChar c)
static bool isIdStart(QChar c)
Definition validator.cpp:19
static QString msgInvalidHeader(const QString &name)
static bool checkEnumValue(QStringView name)
Definition validator.cpp:71
static bool checkClassName(QStringView name, bool allowTemplates=false)
Definition validator.cpp:88
static void checkProperties(const QList< DomProperty * > &properties, QStringList *errors)
static bool isHeaderChar(QChar c)
Definition validator.cpp:98
static bool checkPropertyName(QStringView name)
Definition validator.cpp:65
static bool isIdContinuation(QChar c)
Definition validator.cpp:38
static QString msgInvalidClassName(const QString &name)
static QString msgInvalidAddPageMethod(const QString &name)
static bool isClassNameContinuation(QChar c)
Definition validator.cpp:77
static QString msgInvalidPropertyName(const QString &name)
static bool isTemplateClassNameContinuation(QChar c)
Definition validator.cpp:83
static QString msgInvalidPixmapFunction(const QString &name)
static bool checkHeader(QStringView name)
virtual void acceptAction(DomAction *action)
virtual void acceptActionGroup(DomActionGroup *actionGroup)
virtual void acceptConnection(DomConnection *connection)
virtual void acceptLayoutItem(DomLayoutItem *layoutItem)
virtual void acceptIncludes(DomIncludes *includes)
virtual void acceptInclude(DomInclude *incl)
virtual void acceptCustomWidgets(DomCustomWidgets *customWidgets)
virtual void acceptCustomWidget(DomCustomWidget *customWidget)
virtual void acceptWidget(DomWidget *widget)
virtual void acceptUI(DomUI *ui)
virtual void acceptConnections(DomConnections *connections)
virtual void acceptLayout(DomLayout *layout)
void acceptUI(DomUI *node) override
void acceptLayoutItem(DomLayoutItem *node) override
void acceptConnection(DomConnection *connection) override
Validator(Uic *uic)
void acceptCustomWidget(DomCustomWidget *customWidget) override
void acceptWidget(DomWidget *node) override
void acceptLayout(DomLayout *node) override
void acceptAction(DomAction *node) override
void acceptActionGroup(DomActionGroup *node) override
void acceptInclude(DomInclude *incl) override