UtilTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace Test;
  8. use OC_Util;
  9. /**
  10. * Class UtilTest
  11. *
  12. * @package Test
  13. * @group DB
  14. */
  15. class UtilTest extends \Test\TestCase {
  16. public function testGetVersion(): void {
  17. $version = \OCP\Util::getVersion();
  18. $this->assertTrue(is_array($version));
  19. foreach ($version as $num) {
  20. $this->assertTrue(is_int($num));
  21. }
  22. }
  23. public function testSanitizeHTML(): void {
  24. $badArray = [
  25. 'While it is unusual to pass an array',
  26. 'this function actually <blink>supports</blink> it.',
  27. 'And therefore there needs to be a <script>alert("Unit"+\'test\')</script> for it!',
  28. [
  29. 'And It Even May <strong>Nest</strong>',
  30. ],
  31. ];
  32. $goodArray = [
  33. 'While it is unusual to pass an array',
  34. 'this function actually &lt;blink&gt;supports&lt;/blink&gt; it.',
  35. 'And therefore there needs to be a &lt;script&gt;alert(&quot;Unit&quot;+&#039;test&#039;)&lt;/script&gt; for it!',
  36. [
  37. 'And It Even May &lt;strong&gt;Nest&lt;/strong&gt;'
  38. ],
  39. ];
  40. $result = OC_Util::sanitizeHTML($badArray);
  41. $this->assertEquals($goodArray, $result);
  42. $badString = '<img onload="alert(1)" />';
  43. $result = OC_Util::sanitizeHTML($badString);
  44. $this->assertEquals('&lt;img onload=&quot;alert(1)&quot; /&gt;', $result);
  45. $badString = "<script>alert('Hacked!');</script>";
  46. $result = OC_Util::sanitizeHTML($badString);
  47. $this->assertEquals('&lt;script&gt;alert(&#039;Hacked!&#039;);&lt;/script&gt;', $result);
  48. $goodString = 'This is a good string without HTML.';
  49. $result = OC_Util::sanitizeHTML($goodString);
  50. $this->assertEquals('This is a good string without HTML.', $result);
  51. }
  52. public function testEncodePath(): void {
  53. $component = '/§#@test%&^ä/-child';
  54. $result = OC_Util::encodePath($component);
  55. $this->assertEquals('/%C2%A7%23%40test%25%26%5E%C3%A4/-child', $result);
  56. }
  57. public function testIsNonUTF8Locale(): void {
  58. // OC_Util::isNonUTF8Locale() assumes escapeshellcmd('§') returns '' with non-UTF-8 locale.
  59. $locale = setlocale(LC_CTYPE, 0);
  60. setlocale(LC_CTYPE, 'C');
  61. $this->assertEquals('', escapeshellcmd('§'));
  62. $this->assertEquals('\'\'', escapeshellarg('§'));
  63. setlocale(LC_CTYPE, 'C.UTF-8');
  64. $this->assertEquals('§', escapeshellcmd('§'));
  65. $this->assertEquals('\'§\'', escapeshellarg('§'));
  66. setlocale(LC_CTYPE, $locale);
  67. }
  68. public function testFileInfoLoaded(): void {
  69. $expected = function_exists('finfo_open');
  70. $this->assertEquals($expected, \OC_Util::fileInfoLoaded());
  71. }
  72. /**
  73. * Host is "localhost" this is a valid for emails,
  74. * but not for default strict email verification that requires a top level domain.
  75. * So we check that with strict email verification we fallback to the default
  76. */
  77. public function testGetDefaultEmailAddressStrict(): void {
  78. $email = \OCP\Util::getDefaultEmailAddress('no-reply');
  79. $this->assertEquals('no-reply@localhost.localdomain', $email);
  80. }
  81. /**
  82. * If no strict email check is enabled "localhost" should validate as a valid email domain
  83. */
  84. public function testGetDefaultEmailAddress(): void {
  85. $config = \OC::$server->getConfig();
  86. $config->setAppValue('core', 'enforce_strict_email_check', 'no');
  87. $email = \OCP\Util::getDefaultEmailAddress('no-reply');
  88. $this->assertEquals('no-reply@localhost', $email);
  89. $config->deleteAppValue('core', 'enforce_strict_email_check');
  90. }
  91. public function testGetDefaultEmailAddressFromConfig(): void {
  92. $config = \OC::$server->getConfig();
  93. $config->setSystemValue('mail_domain', 'example.com');
  94. $email = \OCP\Util::getDefaultEmailAddress('no-reply');
  95. $this->assertEquals('no-reply@example.com', $email);
  96. $config->deleteSystemValue('mail_domain');
  97. }
  98. public function testGetConfiguredEmailAddressFromConfig(): void {
  99. $config = \OC::$server->getConfig();
  100. $config->setSystemValue('mail_domain', 'example.com');
  101. $config->setSystemValue('mail_from_address', 'owncloud');
  102. $email = \OCP\Util::getDefaultEmailAddress('no-reply');
  103. $this->assertEquals('owncloud@example.com', $email);
  104. $config->deleteSystemValue('mail_domain');
  105. $config->deleteSystemValue('mail_from_address');
  106. }
  107. public function testGetInstanceIdGeneratesValidId(): void {
  108. \OC::$server->getConfig()->deleteSystemValue('instanceid');
  109. $instanceId = OC_Util::getInstanceId();
  110. $this->assertStringStartsWith('oc', $instanceId);
  111. $matchesRegex = preg_match('/^[a-z0-9]+$/', $instanceId);
  112. $this->assertSame(1, $matchesRegex);
  113. }
  114. /**
  115. * Test needUpgrade() when the core version is increased
  116. */
  117. public function testNeedUpgradeCore(): void {
  118. $config = \OC::$server->getConfig();
  119. $oldConfigVersion = $config->getSystemValue('version', '0.0.0');
  120. $oldSessionVersion = \OC::$server->getSession()->get('OC_Version');
  121. $this->assertFalse(\OCP\Util::needUpgrade());
  122. $config->setSystemValue('version', '7.0.0.0');
  123. \OC::$server->getSession()->set('OC_Version', [7, 0, 0, 1]);
  124. self::invokePrivate(new \OCP\Util, 'needUpgradeCache', [null]);
  125. $this->assertTrue(\OCP\Util::needUpgrade());
  126. $config->setSystemValue('version', $oldConfigVersion);
  127. \OC::$server->getSession()->set('OC_Version', $oldSessionVersion);
  128. self::invokePrivate(new \OCP\Util, 'needUpgradeCache', [null]);
  129. $this->assertFalse(\OCP\Util::needUpgrade());
  130. }
  131. public function testCheckDataDirectoryValidity(): void {
  132. $dataDir = \OC::$server->getTempManager()->getTemporaryFolder();
  133. touch($dataDir . '/.ncdata');
  134. $errors = \OC_Util::checkDataDirectoryValidity($dataDir);
  135. $this->assertEmpty($errors);
  136. \OCP\Files::rmdirr($dataDir);
  137. $dataDir = \OC::$server->getTempManager()->getTemporaryFolder();
  138. // no touch
  139. $errors = \OC_Util::checkDataDirectoryValidity($dataDir);
  140. $this->assertNotEmpty($errors);
  141. \OCP\Files::rmdirr($dataDir);
  142. $errors = \OC_Util::checkDataDirectoryValidity('relative/path');
  143. $this->assertNotEmpty($errors);
  144. }
  145. protected function setUp(): void {
  146. parent::setUp();
  147. \OC_Util::$scripts = [];
  148. \OC_Util::$styles = [];
  149. self::invokePrivate(\OCP\Util::class, 'scripts', [[]]);
  150. self::invokePrivate(\OCP\Util::class, 'scriptDeps', [[]]);
  151. }
  152. protected function tearDown(): void {
  153. parent::tearDown();
  154. \OC_Util::$scripts = [];
  155. \OC_Util::$styles = [];
  156. self::invokePrivate(\OCP\Util::class, 'scripts', [[]]);
  157. self::invokePrivate(\OCP\Util::class, 'scriptDeps', [[]]);
  158. }
  159. public function testAddScript(): void {
  160. \OCP\Util::addScript('first', 'myFirstJSFile');
  161. \OCP\Util::addScript('core', 'myFancyJSFile1');
  162. \OCP\Util::addScript('files', 'myFancyJSFile2', 'core');
  163. \OCP\Util::addScript('myApp5', 'myApp5JSFile', 'myApp2');
  164. \OCP\Util::addScript('myApp', 'myFancyJSFile3');
  165. \OCP\Util::addScript('core', 'myFancyJSFile4');
  166. // after itself
  167. \OCP\Util::addScript('core', 'myFancyJSFile5', 'core');
  168. // add duplicate
  169. \OCP\Util::addScript('core', 'myFancyJSFile1');
  170. // dependency chain
  171. \OCP\Util::addScript('myApp4', 'myApp4JSFile', 'myApp3');
  172. \OCP\Util::addScript('myApp3', 'myApp3JSFile', 'myApp2');
  173. \OCP\Util::addScript('myApp2', 'myApp2JSFile', 'myApp');
  174. \OCP\Util::addScript('core', 'common');
  175. \OCP\Util::addScript('core', 'main');
  176. $scripts = \OCP\Util::getScripts();
  177. // Core should appear first
  178. $this->assertEquals(
  179. 0,
  180. array_search('core/js/common', $scripts, true)
  181. );
  182. $this->assertEquals(
  183. 1,
  184. array_search('core/js/main', $scripts, true)
  185. );
  186. $this->assertEquals(
  187. 2,
  188. array_search('core/js/myFancyJSFile1', $scripts, true)
  189. );
  190. $this->assertEquals(
  191. 3,
  192. array_search('core/js/myFancyJSFile4', $scripts, true)
  193. );
  194. // Dependencies should appear before their children
  195. $this->assertLessThan(
  196. array_search('files/js/myFancyJSFile2', $scripts, true),
  197. array_search('core/js/myFancyJSFile3', $scripts, true)
  198. );
  199. $this->assertLessThan(
  200. array_search('myApp2/js/myApp2JSFile', $scripts, true),
  201. array_search('myApp/js/myFancyJSFile3', $scripts, true)
  202. );
  203. $this->assertLessThan(
  204. array_search('myApp3/js/myApp3JSFile', $scripts, true),
  205. array_search('myApp2/js/myApp2JSFile', $scripts, true)
  206. );
  207. $this->assertLessThan(
  208. array_search('myApp4/js/myApp4JSFile', $scripts, true),
  209. array_search('myApp3/js/myApp3JSFile', $scripts, true)
  210. );
  211. $this->assertLessThan(
  212. array_search('myApp5/js/myApp5JSFile', $scripts, true),
  213. array_search('myApp2/js/myApp2JSFile', $scripts, true)
  214. );
  215. // No duplicates
  216. $this->assertEquals(
  217. $scripts,
  218. array_unique($scripts)
  219. );
  220. // All scripts still there
  221. $scripts = [
  222. 'core/js/common',
  223. 'core/js/main',
  224. 'core/js/myFancyJSFile1',
  225. 'core/js/myFancyJSFile4',
  226. 'core/js/myFancyJSFile5',
  227. 'first/l10n/en',
  228. 'first/js/myFirstJSFile',
  229. 'files/l10n/en',
  230. 'files/js/myFancyJSFile2',
  231. 'myApp/l10n/en',
  232. 'myApp/js/myFancyJSFile3',
  233. 'myApp2/l10n/en',
  234. 'myApp2/js/myApp2JSFile',
  235. 'myApp5/l10n/en',
  236. 'myApp5/js/myApp5JSFile',
  237. 'myApp3/l10n/en',
  238. 'myApp3/js/myApp3JSFile',
  239. 'myApp4/l10n/en',
  240. 'myApp4/js/myApp4JSFile',
  241. ];
  242. foreach ($scripts as $script) {
  243. $this->assertContains($script, $scripts);
  244. }
  245. }
  246. public function testAddScriptCircularDependency(): void {
  247. \OCP\Util::addScript('circular', 'file1', 'dependency');
  248. \OCP\Util::addScript('dependency', 'file2', 'circular');
  249. $scripts = \OCP\Util::getScripts();
  250. $this->assertContains('circular/js/file1', $scripts);
  251. $this->assertContains('dependency/js/file2', $scripts);
  252. }
  253. public function testAddVendorScript(): void {
  254. \OC_Util::addVendorScript('core', 'myFancyJSFile1');
  255. \OC_Util::addVendorScript('myApp', 'myFancyJSFile2');
  256. \OC_Util::addVendorScript('core', 'myFancyJSFile0', true);
  257. \OC_Util::addVendorScript('core', 'myFancyJSFile10', true);
  258. // add duplicate
  259. \OC_Util::addVendorScript('core', 'myFancyJSFile1');
  260. $this->assertEquals([
  261. 'core/vendor/myFancyJSFile10',
  262. 'core/vendor/myFancyJSFile0',
  263. 'core/vendor/myFancyJSFile1',
  264. 'myApp/vendor/myFancyJSFile2',
  265. ], \OC_Util::$scripts);
  266. $this->assertEquals([], \OC_Util::$styles);
  267. }
  268. public function testAddTranslations(): void {
  269. \OC_Util::addTranslations('appId', 'de');
  270. $this->assertEquals([
  271. 'appId/l10n/de'
  272. ], \OC_Util::$scripts);
  273. $this->assertEquals([], \OC_Util::$styles);
  274. }
  275. public function testAddStyle(): void {
  276. \OC_Util::addStyle('core', 'myFancyCSSFile1');
  277. \OC_Util::addStyle('myApp', 'myFancyCSSFile2');
  278. \OC_Util::addStyle('core', 'myFancyCSSFile0', true);
  279. \OC_Util::addStyle('core', 'myFancyCSSFile10', true);
  280. // add duplicate
  281. \OC_Util::addStyle('core', 'myFancyCSSFile1');
  282. $this->assertEquals([], \OC_Util::$scripts);
  283. $this->assertEquals([
  284. 'core/css/myFancyCSSFile10',
  285. 'core/css/myFancyCSSFile0',
  286. 'core/css/myFancyCSSFile1',
  287. 'myApp/css/myFancyCSSFile2',
  288. ], \OC_Util::$styles);
  289. }
  290. public function testAddVendorStyle(): void {
  291. \OC_Util::addVendorStyle('core', 'myFancyCSSFile1');
  292. \OC_Util::addVendorStyle('myApp', 'myFancyCSSFile2');
  293. \OC_Util::addVendorStyle('core', 'myFancyCSSFile0', true);
  294. \OC_Util::addVendorStyle('core', 'myFancyCSSFile10', true);
  295. // add duplicate
  296. \OC_Util::addVendorStyle('core', 'myFancyCSSFile1');
  297. $this->assertEquals([], \OC_Util::$scripts);
  298. $this->assertEquals([
  299. 'core/vendor/myFancyCSSFile10',
  300. 'core/vendor/myFancyCSSFile0',
  301. 'core/vendor/myFancyCSSFile1',
  302. 'myApp/vendor/myFancyCSSFile2',
  303. ], \OC_Util::$styles);
  304. }
  305. public function testShortenMultibyteString(): void {
  306. $this->assertEquals('Short nuff', \OCP\Util::shortenMultibyteString('Short nuff', 255));
  307. $this->assertEquals('ABC', \OCP\Util::shortenMultibyteString('ABCDEF', 3));
  308. // each of the characters is 12 bytes
  309. $this->assertEquals('🙈', \OCP\Util::shortenMultibyteString('🙈🙊🙉', 16, 2));
  310. }
  311. }