FOSSology  4.7.1
Open Source License Compliance by Open Source Software
test_helpers.cc
Go to the documentation of this file.
1 /*
2  SPDX-License-Identifier: GPL-2.0-only
3  Author: Dietmar Helmut Leher <helmut.leher.ext@vaillant-group.com>
4  SPDX-FileCopyrightText: © 2026 Vaillant GmbH
5 */
15 #include <cppunit/TestFixture.h>
16 #include <cppunit/extensions/HelperMacros.h>
17 
18 #include "ReuserDatabaseHandler.hpp"
20 
26 {
27 public:
29 
30  bool callIsValidIdentifier(const std::string& s)
31  {
32  return isValidIdentifier(s);
33  }
34 
35  std::string callReplaceUnicodeControlChars(const std::string& s)
36  {
38  }
39 
40  int callGetDecisionTypePriority(int decisionType)
41  {
42  return getDecisionTypePriority(decisionType);
43  }
44 };
45 
46 // isValidIdentifier tests
47 
48 class IsValidIdentifierTest : public CPPUNIT_NS::TestFixture
49 {
50  CPPUNIT_TEST_SUITE(IsValidIdentifierTest);
51  CPPUNIT_TEST(testEmptyStringIsInvalid);
52  CPPUNIT_TEST(testLowercaseLettersAreValid);
53  CPPUNIT_TEST(testUppercaseLettersAreValid);
54  CPPUNIT_TEST(testDigitsAreValid);
55  CPPUNIT_TEST(testUnderscoreIsValid);
57  CPPUNIT_TEST(testSpaceIsInvalid);
58  CPPUNIT_TEST(testHyphenIsInvalid);
59  CPPUNIT_TEST(testDotIsInvalid);
60  CPPUNIT_TEST(testSemicolonIsInvalid);
61  CPPUNIT_TEST(testSingleQuoteIsInvalid);
62  CPPUNIT_TEST(testDoubleQuoteIsInvalid);
63  CPPUNIT_TEST(testDollarIsInvalid);
64  CPPUNIT_TEST(testNullByteIsInvalid);
66  CPPUNIT_TEST(testKnownTableNamesAreValid);
67  CPPUNIT_TEST_SUITE_END();
68 
70 
71 protected:
76  {
77  CPPUNIT_ASSERT(!acc.callIsValidIdentifier(""));
78  }
79 
84  {
85  CPPUNIT_ASSERT(acc.callIsValidIdentifier("abc"));
86  }
87 
92  {
93  CPPUNIT_ASSERT(acc.callIsValidIdentifier("ABC"));
94  }
95 
100  {
101  CPPUNIT_ASSERT(acc.callIsValidIdentifier("123"));
102  }
103 
108  {
109  CPPUNIT_ASSERT(acc.callIsValidIdentifier("_"));
110  }
111 
116  {
117  CPPUNIT_ASSERT(acc.callIsValidIdentifier("uploadtree_a"));
118  CPPUNIT_ASSERT(acc.callIsValidIdentifier("upload_fk_123"));
119  }
120 
125  {
126  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("upload tree"));
127  }
128 
133  {
134  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("upload-tree"));
135  }
136 
141  {
142  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("public.uploadtree"));
143  }
144 
149  {
150  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("foo;DROP TABLE uploadtree"));
151  }
152 
157  {
158  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("up'load"));
159  }
160 
165  {
166  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("up\"load"));
167  }
168 
173  {
174  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("$1"));
175  }
176 
181  {
182  CPPUNIT_ASSERT(!acc.callIsValidIdentifier(std::string("up\x00load", 8)));
183  }
184 
192  {
193  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("t WHERE 1=1--"));
194  CPPUNIT_ASSERT(!acc.callIsValidIdentifier("t UNION SELECT 1"));
195  }
196 
201  {
202  CPPUNIT_ASSERT(acc.callIsValidIdentifier("uploadtree"));
203  CPPUNIT_ASSERT(acc.callIsValidIdentifier("uploadtree_a"));
204  // upload-specific table names follow the pattern uploadtree_<pk>
205  CPPUNIT_ASSERT(acc.callIsValidIdentifier("uploadtree_42"));
206  }
207 };
208 
209 CPPUNIT_TEST_SUITE_REGISTRATION(IsValidIdentifierTest);
210 
211 // replaceUnicodeControlChars tests
212 
213 class ReplaceUnicodeControlCharsTest : public CPPUNIT_NS::TestFixture
214 {
215  CPPUNIT_TEST_SUITE(ReplaceUnicodeControlCharsTest);
216  CPPUNIT_TEST(testPlainAsciiIsUnchanged);
217  CPPUNIT_TEST(testTabAndNewlineAreKept);
218  CPPUNIT_TEST(testCarriageReturnIsKept);
219  CPPUNIT_TEST(testNullByteIsStripped);
220  CPPUNIT_TEST(testC0ControlCharsAreStripped);
221  CPPUNIT_TEST(testC1ControlCharsAreStripped);
222  CPPUNIT_TEST(testDeleteCharIsStripped);
223  CPPUNIT_TEST(testUtf8MultiBytePrintableIsKept);
224  CPPUNIT_TEST(testSurrogatePairCodepointIsKept);
226  CPPUNIT_TEST(testEmptyStringIsUnchanged);
227  CPPUNIT_TEST_SUITE_END();
228 
230 
231  std::string call(const std::string& s)
232  {
233  return acc.callReplaceUnicodeControlChars(s);
234  }
235 
236 protected:
241  {
242  CPPUNIT_ASSERT_EQUAL(std::string("hello world"), call("hello world"));
243  }
244 
252  {
253  CPPUNIT_ASSERT_EQUAL(std::string("\t\n"), call("\t\n"));
254  }
255 
260  {
261  CPPUNIT_ASSERT_EQUAL(std::string("\r"), call("\r"));
262  }
263 
268  {
269  std::string in(1, '\x00');
270  CPPUNIT_ASSERT_EQUAL(std::string(""), call(in));
271  }
272 
280  {
281  // U+0001 through U+0008
282  for (char c = '\x01'; c <= '\x08'; ++c)
283  {
284  std::string in(1, c);
285  CPPUNIT_ASSERT_EQUAL_MESSAGE(
286  "Expected control char 0x" + std::to_string((unsigned char)c) + " to be stripped",
287  std::string(""), call(in));
288  }
289  // U+000B (VT) and U+000C (FF) are also stripped
290  CPPUNIT_ASSERT_EQUAL(std::string(""), call("\x0B"));
291  CPPUNIT_ASSERT_EQUAL(std::string(""), call("\x0C"));
292  // U+000E through U+001F
293  for (char c = '\x0E'; c <= '\x1F'; ++c)
294  {
295  std::string in(1, c);
296  CPPUNIT_ASSERT_EQUAL_MESSAGE(
297  "Expected control char 0x" + std::to_string((unsigned char)c) + " to be stripped",
298  std::string(""), call(in));
299  }
300  }
301 
309  {
310  // U+0080 in UTF-8 is \xC2\x80
311  std::string c1_80 = "\xC2\x80";
312  CPPUNIT_ASSERT_EQUAL(std::string(""), call(c1_80));
313  // U+009F in UTF-8 is \xC2\x9F
314  std::string c1_9f = "\xC2\x9F";
315  CPPUNIT_ASSERT_EQUAL(std::string(""), call(c1_9f));
316  }
317 
322  {
323  CPPUNIT_ASSERT_EQUAL(std::string(""), call("\x7F"));
324  }
325 
330  {
331  // © = U+00A9 = \xC2\xA9 in UTF-8
332  std::string copyright_sign = "\xC2\xA9";
333  CPPUNIT_ASSERT_EQUAL(copyright_sign, call(copyright_sign));
334  }
335 
346  {
347  // U+1F600 GRINNING FACE in UTF-8: \xF0\x9F\x98\x80
348  std::string emoji = "\xF0\x9F\x98\x80";
349  CPPUNIT_ASSERT_EQUAL(emoji, call(emoji));
350  }
351 
359  {
360  // "hello\x01world" -> "helloworld"
361  std::string in = "hello\x01world";
362  CPPUNIT_ASSERT_EQUAL(std::string("helloworld"), call(in));
363 
364  // control sandwiched between emoji
365  // \xF0\x9F\x98\x80 \x01 \xF0\x9F\x98\x80 -> emoji + emoji
366  std::string emoji = "\xF0\x9F\x98\x80";
367  std::string mixed = emoji + "\x01" + emoji;
368  CPPUNIT_ASSERT_EQUAL(emoji + emoji, call(mixed));
369  }
370 
375  {
376  CPPUNIT_ASSERT_EQUAL(std::string(""), call(""));
377  }
378 };
379 
380 CPPUNIT_TEST_SUITE_REGISTRATION(ReplaceUnicodeControlCharsTest);
381 
382 class DecisionTypePriorityTest : public CPPUNIT_NS::TestFixture
383 {
384  CPPUNIT_TEST_SUITE(DecisionTypePriorityTest);
385  CPPUNIT_TEST(testPriorityOrdering);
387  CPPUNIT_TEST_SUITE_END();
388 
389 protected:
390  int prio(int decisionType)
391  {
392  ReuserHelpersAccessor accessor;
393  return accessor.callGetDecisionTypePriority(decisionType);
394  }
395 
398  {
399  CPPUNIT_ASSERT(prio(0) < prio(3));
400  CPPUNIT_ASSERT(prio(3) < prio(4));
401  CPPUNIT_ASSERT(prio(4) < prio(7));
402  CPPUNIT_ASSERT(prio(7) < prio(6));
403  CPPUNIT_ASSERT(prio(6) < prio(5));
404  CPPUNIT_ASSERT_EQUAL(0, prio(999));
405  }
406 
413  {
414  const int irrelevantPk = 50;
415  const int irrelevantType = 4;
416  const int identifiedPk = 40;
417  const int identifiedType = 5;
418 
419  int chosenPk = irrelevantPk;
420  int chosenType = irrelevantType;
421 
422  auto applyRow = [&](int pk, int type)
423  {
424  if (prio(type) > prio(chosenType))
425  {
426  chosenPk = pk;
427  chosenType = type;
428  }
429  };
430 
431  applyRow(irrelevantPk, irrelevantType);
432  applyRow(identifiedPk, identifiedType);
433 
434  CPPUNIT_ASSERT_EQUAL(identifiedPk, chosenPk);
435  CPPUNIT_ASSERT_EQUAL(identifiedType, chosenType);
436  }
437 };
438 
439 CPPUNIT_TEST_SUITE_REGISTRATION(DecisionTypePriorityTest);
Mock ReuserDatabaseHandler for unit tests.
void testPriorityOrdering()
Matches PHP ReuserAgent::getDecisionTypePriority.
void testStrongerTypeBeatsWeakerRegardlessOfPkOrder()
Same pfile: IDENTIFIED (pk 40) must win over IRRELEVANT (pk 50).
void testDigitsAreValid()
A string of ASCII digits is valid.
Definition: test_helpers.cc:99
void testDotIsInvalid()
A dot makes an identifier invalid (guards against schema.table injection).
void testLowercaseLettersAreValid()
A string of lowercase ASCII letters is valid.
Definition: test_helpers.cc:83
void testUppercaseLettersAreValid()
A string of uppercase ASCII letters is valid.
Definition: test_helpers.cc:91
void testHyphenIsInvalid()
A hyphen makes an identifier invalid.
void testDoubleQuoteIsInvalid()
A double quote makes an identifier invalid.
void testDollarIsInvalid()
A dollar sign makes an identifier invalid.
void testSemicolonIsInvalid()
A semicolon makes an identifier invalid (guards against statement injection).
void testSqlInjectionPatternIsInvalid()
A classic SQL injection pattern is rejected.
void testMixedAlphanumericUnderscoreIsValid()
A typical mixed alphanumeric/underscore identifier is valid.
void testSingleQuoteIsInvalid()
A single quote makes an identifier invalid.
void testSpaceIsInvalid()
A space character makes an identifier invalid.
void testUnderscoreIsValid()
An underscore character is valid.
void testNullByteIsInvalid()
A null byte makes an identifier invalid.
void testKnownTableNamesAreValid()
The three concrete uploadtree table names used in production are valid.
void testEmptyStringIsInvalid()
An empty string is not a valid SQL identifier.
Definition: test_helpers.cc:75
Test double for ReuserDatabaseHandler.
void testMixedControlAndPrintableFiltered()
A string mixing control characters and printable text is filtered correctly.
void testDeleteCharIsStripped()
DEL character (U+007F) is stripped.
void testEmptyStringIsUnchanged()
An empty input string returns an empty string.
void testC1ControlCharsAreStripped()
C1 control characters U+0080-U+009F are stripped.
void testPlainAsciiIsUnchanged()
Plain ASCII text passes through unchanged.
void testCarriageReturnIsKept()
Carriage return (U+000D) is kept.
void testSurrogatePairCodepointIsKept()
A codepoint above U+FFFF (surrogate-pair range in UTF-16) is kept.
void testC0ControlCharsAreStripped()
C0 control characters U+0001-U+0008 are stripped.
void testTabAndNewlineAreKept()
Horizontal tab (U+0009) and line feed (U+000A) are kept.
void testUtf8MultiBytePrintableIsKept()
A printable multi-byte UTF-8 character (e.g. U+00A9 ©) is kept.
void testNullByteIsStripped()
Null byte (U+0000) is stripped.
static int getDecisionTypePriority(int decisionType)
Priority for decision types during reuse conflict resolution.
static bool isValidIdentifier(const std::string &s)
Validate that s contains only characters safe for SQL identifiers.
static std::string replaceUnicodeControlChars(const std::string &input)
Strip Unicode control characters (C0, C1, DEL) from input.
Thin subclass that exposes private helper methods for testing.
Definition: test_helpers.cc:26
int s
The socket that the CLI will use to communicate.
Definition: fo_cli.c:37