From 7a682397e2491071489ad892cad36cd57e767e62 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 7 May 2021 14:23:15 +0200 Subject: [PATCH 01/29] Add checkup test about Google's Floc --- lib/defaults.js | 1 + www/checkup/main.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/lib/defaults.js b/lib/defaults.js index 3d5e74576..635e155be 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -48,6 +48,7 @@ Default.httpHeaders = function () { "X-XSS-Protection": "1; mode=block", "X-Content-Type-Options": "nosniff", "Access-Control-Allow-Origin": "*", + "Permissions-policy":"interest-cohort=()" }; }; Default.mainPages = function () { diff --git a/www/checkup/main.js b/www/checkup/main.js index aeb8a4239..87e310e32 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -365,6 +365,15 @@ define([ }); }); + assert(function (cb, msg) { + msg.innerText = "Missing HTTP header required to disable Google's Floc."; + $.ajax('/?'+ (+new Date()), { + complete: function (xhr) { + cb(xhr.getResponseHeader('permissions-policy') === 'interest-cohort=()'); + }, + }); + }); + assert(function (cb, msg) { msg = msg; return void cb(true); From 004e0ec010208e7f4f97ce8e557ff9efdfdadfdc Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 7 May 2021 18:04:49 +0530 Subject: [PATCH 02/29] update lodash dependency --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b1c24cbc..75b03cfe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1706,9 +1706,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.merge": { From 62f8fb93b5c56b66b1ca4c37da69d90eab5caba9 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 7 May 2021 18:07:13 +0530 Subject: [PATCH 03/29] take notes of desirable admin RPC commands --- lib/commands/admin-rpc.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/commands/admin-rpc.js b/lib/commands/admin-rpc.js index 32338b772..9ab86bdf2 100644 --- a/lib/commands/admin-rpc.js +++ b/lib/commands/admin-rpc.js @@ -348,6 +348,9 @@ var commands = { CLEAR_CACHED_CHANNEL_INDEX: clearChannelIndex, GET_CACHED_CHANNEL_INDEX: getChannelIndex, + // TODO implement admin historyTrim + // TODO implement kick from channel + // TODO implement force-disconnect user(s)? CLEAR_CACHED_CHANNEL_METADATA: clearChannelMetadata, GET_CACHED_CHANNEL_METADATA: getChannelMetadata, From 3f498af12b5ab90f566834c2e48cc58560d0f2cb Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 7 May 2021 18:07:26 +0530 Subject: [PATCH 04/29] update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d333700b..bcb7e9230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# WIP + +* fix opening links from temporary shared folders on iphone or other contexts that do not support shared workers +* add checkup test for disabling google FLoC +* update lodash devDependency + # 4.5.0 ## Goals From e5ee3ee22adb60e8b3748d452d2a55ec7996d20e Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 7 May 2021 18:03:39 +0200 Subject: [PATCH 05/29] Fix type error in sheets --- www/common/onlyoffice/v4/sdkjs/cell/sdk-all.js | 14 +++++++------- www/common/onlyoffice/v4/sdkjs/slide/sdk-all.js | 14 +++++++------- www/common/onlyoffice/v4/sdkjs/word/sdk-all.js | 14 +++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/www/common/onlyoffice/v4/sdkjs/cell/sdk-all.js b/www/common/onlyoffice/v4/sdkjs/cell/sdk-all.js index b8d012784..cdefc6082 100644 --- a/www/common/onlyoffice/v4/sdkjs/cell/sdk-all.js +++ b/www/common/onlyoffice/v4/sdkjs/cell/sdk-all.js @@ -12867,13 +12867,13 @@ deleted){self.dependencyFormulas.delColumnTable(tableName,deleted);var wsActive= wsActive.getHidden())wsActive.setHidden(false);if(!bNoBuildDep)this.dependencyFormulas.initOpen();if(bSnapshot)this.snapshot=this._getSnapshot()};Workbook.prototype.initPostOpenZip=function(pivotCaches){var t=this;this.forEach(function(ws){ws.initPostOpenZip(pivotCaches,t.oNumFmtsOpen)})};Workbook.prototype.setCommonIndexObjectsFrom=function(wb){this.oStyleManager=wb.oStyleManager;this.sharedStrings=wb.sharedStrings;this.workbookFormulas=wb.workbookFormulas};Workbook.prototype.forEach=function(callback, isCopyPaste){if(isCopyPaste||isCopyPaste===false)callback(this.getActiveWs(),this.getActive());else for(var i=0,l=this.aWorksheets.length;i=0&&index=0&&index=0&&indexBefore=0&&index=0&&insertBefore=0&&index=0&&indexBefore=0&&index=0&&insertBefore=0&&index0&&index=0&&indexFrom=0&&indexTo=0&&index=0&&index=0&&indexBefore=0&&index=0&&insertBefore=0&&index=0&&indexBefore=0&&index=0&&insertBefore=0&&index0&&index=0&&indexFrom=0&&indexTo=0&&index=0&&index=0&&indexBefore=0&&index=0&&insertBefore=0&&index=0&&indexBefore=0&&index=0&&insertBefore=0&&index0&&index=0&&indexFrom=0&&indexTo Date: Tue, 11 May 2021 16:17:00 +0530 Subject: [PATCH 06/29] handle admin decree errors on the admin panel addresses #642 --- www/admin/inner.js | 58 ++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/www/admin/inner.js b/www/admin/inner.js index ffc9a561c..8e3ed16d2 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -267,8 +267,11 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'ADMIN_DECREE', data: ['RESTRICT_REGISTRATION', [val]] - }, function (e) { - if (e) { UI.warn(Messages.error); console.error(e); } + }, function (e, response) { + if (e || response.error) { + UI.warn(Messages.error); + console.error(e, response); + } APP.updateStatus(function () { spinner.done(); state = APP.instanceStatus.restrictRegistration; @@ -316,8 +319,11 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'ADMIN_DECREE', data: ['UPDATE_DEFAULT_STORAGE', data] - }, function (e) { - if (e) { UI.warn(Messages.error); return void console.error(e); } + }, function (e, response) { + if (e || response.error) { + UI.warn(Messages.error); + return void console.error(e, response); + } var limit = getPrettySize(l); $div.find('.cp-admin-defaultlimit-value').text(Messages._getKey('admin_limit', [limit])); }); @@ -448,8 +454,12 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'ADMIN_DECREE', data: ['RM_QUOTA', data] - }, function (e) { - if (e) { UI.warn(Messages.error); console.error(e); } + }, function (e, response) { + if (e || response.error) { + UI.warn(Messages.error); + console.error(e, response); + return; + } APP.refreshLimits(); $key.val(''); }); @@ -462,8 +472,12 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'ADMIN_DECREE', data: ['SET_QUOTA', data] - }, function (e) { - if (e) { UI.warn(Messages.error); console.error(e); } + }, function (e, response) { + if (e || response.error) { + UI.warn(Messages.error); + console.error(e, response); + return; + } APP.refreshLimits(); $key.val(''); }); @@ -1030,9 +1044,10 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'ADMIN_DECREE', data: ['SET_LAST_BROADCAST_HASH', [lastHash]] - }, function (e) { - if (e) { - console.error(e); + }, function (e, response) { + if (e || response.error) { + UI.warn(Messages.error); + console.error(e, response); return; } console.log('lastBroadcastHash updated'); @@ -1336,9 +1351,10 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'ADMIN_DECREE', data: ['SET_MAINTENANCE', [data]] - }, function (e) { - if (e) { - UI.warn(Messages.error); console.error(e); + }, function (e, response) { + if (e || response.error) { + UI.warn(Messages.error); + console.error(e, response); $button.prop('disabled', ''); return; } @@ -1430,10 +1446,11 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'ADMIN_DECREE', data: ['SET_SURVEY_URL', [data]] - }, function (e) { - if (e) { + }, function (e, response) { + if (e || response.error) { $button.prop('disabled', ''); - UI.warn(Messages.error); console.error(e); + UI.warn(Messages.error); + console.error(e, response); return; } // Maintenance applied, send notification @@ -1529,11 +1546,12 @@ define([ sFrameChan.query('Q_ADMIN_RPC', { cmd: 'GET_WORKER_PROFILES', }, function (e, data) { - if (e) { return void console.error(e); } + if (e || data.error) { + UI.warn(Messages.error); + return void console.error(e, data); + } //console.info(data); $div.find("table").remove(); - - process(data); $div.append(table); }); From 6eaee92ac3cd159b36f2c4a42ae3afb5773ce1ef Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 12 May 2021 10:12:00 +0530 Subject: [PATCH 07/29] rename variables in AppConfig to minimize copy-paste problems --- www/common/application_config_internal.js | 70 +++++++++++------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js index c7b66c1e5..791ecf367 100644 --- a/www/common/application_config_internal.js +++ b/www/common/application_config_internal.js @@ -4,14 +4,14 @@ * file (make a copy from /customize.dist/application_config.js) */ define(function() { - var config = {}; + var AppConfig = {}; /* Select the buttons displayed on the main page to create new collaborative sessions. * Removing apps from the list will prevent users from accessing them. They will instead be * redirected to the drive. * You should never remove the drive from this list. */ - config.availablePadTypes = ['drive', 'teams', 'pad', 'sheet', 'code', 'slide', 'poll', 'kanban', 'whiteboard', + AppConfig.availablePadTypes = ['drive', 'teams', 'pad', 'sheet', 'code', 'slide', 'poll', 'kanban', 'whiteboard', /*'doc', 'presentation',*/ 'file', /*'todo',*/ 'contacts' /*, 'calendar' */]; /* The registered only types are apps restricted to registered users. * You should never remove apps from this list unless you know what you're doing. The apps @@ -20,7 +20,7 @@ define(function() { * users and these users will be redirected to the login page if they still try to access * the app */ - config.registeredOnlyTypes = ['file', 'contacts', 'notifications', 'support']; + AppConfig.registeredOnlyTypes = ['file', 'contacts', 'notifications', 'support']; /* CryptPad is available is multiple languages, but only English and French are maintained * by the developers. The other languages may be outdated, and any missing string for a langauge @@ -30,37 +30,37 @@ define(function() { * can be found at the top of the file `/customize.dist/messages.js`. The list should only * contain languages code ('en', 'fr', 'de', 'pt-br', etc.), not their full name. */ - //config.availableLanguages = ['en', 'fr', 'de']; + //AppConfig.availableLanguages = ['en', 'fr', 'de']; /* You can display a link to the imprint (legal notice) of your website in the static pages * footer. To do so, you can either set the following value to `true` and create an imprint.html page * in the `customize` directory. You can also set it to an absolute URL if your imprint page already exists. */ - config.imprint = false; - // config.imprint = true; - // config.imprint = 'https://xwiki.com/en/company/legal-notice'; + AppConfig.imprint = false; + // AppConfig.imprint = true; + // AppConfig.imprint = 'https://xwiki.com/en/company/legal-notice'; /* You can display a link to your own privacy policy in the static pages footer. * To do so, set the following value to the absolute URL of your privacy policy. */ - // config.privacy = 'https://xwiki.com/en/company/PrivacyPolicy'; + // AppConfig.privacy = 'https://xwiki.com/en/company/PrivacyPolicy'; /* We (the project's developers) include the ability to display a 'Roadmap' in static pages footer. * This is disabled by default. * We use this to publish the project's development roadmap, but you can use it however you like. * To do so, set the following value to an absolute URL. */ - //config.roadmap = 'https://cryptpad.fr/kanban/#/2/kanban/view/PLM0C3tFWvYhd+EPzXrbT+NxB76Z5DtZhAA5W5hG9wo/'; + //AppConfig.roadmap = 'https://cryptpad.fr/kanban/#/2/kanban/view/PLM0C3tFWvYhd+EPzXrbT+NxB76Z5DtZhAA5W5hG9wo/'; /* Cryptpad apps use a common API to display notifications to users * by default, notifications are hidden after 5 seconds * You can change their duration here (measured in milliseconds) */ - config.notificationTimeout = 5000; - config.disableUserlistNotifications = false; + AppConfig.notificationTimeout = 5000; + AppConfig.disableUserlistNotifications = false; // Update the default colors available in the whiteboard application - config.whiteboardPalette = [ + AppConfig.whiteboardPalette = [ '#000000', // black '#FFFFFF', // white '#848484', // grey @@ -82,14 +82,14 @@ define(function() { // Background color in the apps with centered content: // - file app in view mode // - rich text app when editor's width reduced in settings - config.appBackgroundColor = '#666'; + AppConfig.appBackgroundColor = '#666'; // Set enableTemplates to false to remove the button allowing users to save a pad as a template // and remove the template category in CryptDrive - config.enableTemplates = true; + AppConfig.enableTemplates = true; // Set enableHistory to false to remove the "History" button in all the apps. - config.enableHistory = true; + AppConfig.enableHistory = true; /* user passwords are hashed with scrypt, and salted with their username. this value will be appended to the username, causing the resulting hash @@ -101,15 +101,15 @@ define(function() { created. Changing it at a later time will break logins for all existing users. */ - config.loginSalt = ''; - config.minimumPasswordLength = 8; + AppConfig.loginSalt = ''; + AppConfig.minimumPasswordLength = 8; // Amount of time (ms) before aborting the session when the algorithm cannot synchronize the pad - config.badStateTimeout = 30000; + AppConfig.badStateTimeout = 30000; // Customize the icon used for each application. // You can update the colors by making a copy of /customize.dist/src/less2/include/colortheme.less - config.applicationsIcon = { + AppConfig.applicationsIcon = { file: 'cptools-file', fileupload: 'cptools-file-upload', folderupload: 'cptools-folder-upload', @@ -130,50 +130,50 @@ define(function() { // Ability to create owned pads and expiring pads through a new pad creation screen. // The new screen can be disabled by the users in their settings page - config.displayCreationScreen = true; + AppConfig.displayCreationScreen = true; // Prevent anonymous users from storing pads in their drive - config.disableAnonymousStore = false; + AppConfig.disableAnonymousStore = false; // Hide the usage bar in settings and drive - //config.hideUsageBar = true; + //AppConfig.hideUsageBar = true; // Disable feedback for all the users and hide the settings part about feedback - //config.disableFeedback = true; + //AppConfig.disableFeedback = true; // Add new options in the share modal (extend an existing tab or add a new tab). // More info about how to use it on the wiki: - // https://github.com/xwiki-labs/cryptpad/wiki/Application-config#configcustomizeshareoptions - //config.customizeShareOptions = function (hashes, tabs, config) {}; + // https://github.com/xwiki-labs/cryptpad/wiki/Application-config#configcustomizeshareoptions XXX page is a 404 + //AppConfig.customizeShareOptions = function (hashes, tabs, config) {}; // Add code to be executed on every page before loading the user object. `isLoggedIn` (bool) is // indicating if the user is registered or anonymous. Here you can change the way anonymous users // work in CryptPad, use an external SSO or even force registration // *NOTE*: You have to call the `callback` function to continue the loading process - //config.beforeLogin = function(isLoggedIn, callback) {}; + //AppConfig.beforeLogin = function(isLoggedIn, callback) {}; // Add code to be executed on every page after the user object is loaded (also work for // unregistered users). This allows you to interact with your users' drive // *NOTE*: You have to call the `callback` function to continue the loading process - //config.afterLogin = function(api, callback) {}; + //AppConfig.afterLogin = function(api, callback) {}; // Disabling the profile app allows you to import the profile informations (display name, avatar) // from an external source and make sure the users can't change them from CryptPad. - // You can use config.afterLogin to import these values in the users' drive. - //config.disableProfile = true; + // You can use AppConfig.afterLogin to import these values in the users' drive. + //AppConfig.disableProfile = true; // Disable the use of webworkers and sharedworkers in CryptPad. // Workers allow us to run the websockets connection and open the user drive in a separate thread. // SharedWorkers allow us to load only one websocket and one user drive for all the browser tabs, // making it much faster to open new tabs. - config.disableWorkers = false; + AppConfig.disableWorkers = false; // Teams are always loaded during the initial loading screen (for the first tab only if // SharedWorkers are available). Allowing users to be members of multiple teams can // make them have a very slow loading time. To avoid impacting the user experience // significantly, we're limiting the number of teams per user to 3 by default. // You can change this value here. - //config.maxTeamsSlots = 5; + //AppConfig.maxTeamsSlots = 5; // Each team is considered as a registered user by the server. Users and teams are indistinguishable // in the database so teams will offer the same storage limits as users by default. @@ -181,7 +181,7 @@ define(function() { // We're limiting the number of teams each user is able to own to 1 in order to make sure // users don't use "fake" teams (1 member) just to increase their storage limit. // You can change the value here. - // config.maxOwnedTeams = 5; + // AppConfig.maxOwnedTeams = 5; // The userlist displayed in collaborative documents is stored alongside the document data. // Everytime someone with edit rights joins a document or modify their user data (display @@ -192,14 +192,14 @@ define(function() { // position of other users' cursor. You can configure the number of user from which the session // will enter into degraded mode. A big number may result in collaborative edition being broken, // but this number depends on the network and CPU performances of each user's device. - config.degradedLimit = 8; + AppConfig.degradedLimit = 8; // In "legacy" mode, one-time users were always creating an "anonymous" drive when visiting CryptPad // in which they could store their pads. The new "driveless" mode allow users to open an existing // pad without creating a drive in the background. The drive will only be created if they visit // a different page (Drive, Settings, etc.) or try to create a new pad themselves. You can disable // the driveless mode by changing the following value to "false" - config.allowDrivelessMode = true; + AppConfig.allowDrivelessMode = true; - return config; + return AppConfig; }); From 32494fca0c154dfd44d48bc5ecadc60c9bd101ea Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 12 May 2021 14:18:26 +0530 Subject: [PATCH 08/29] let NGINX handle its own headers --- docs/example.nginx.conf | 7 +++++++ server.js | 17 +++++------------ www/checkup/app-checkup.less | 4 ++-- www/checkup/main.js | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/example.nginx.conf b/docs/example.nginx.conf index 78e4f30ce..a51a1ecaa 100644 --- a/docs/example.nginx.conf +++ b/docs/example.nginx.conf @@ -167,6 +167,13 @@ server { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # These settings prevent both NGINX and the API server + # from setting the same headers and creating duplicates + proxy_hide_header Cross-Origin-Resource-Policy; + add_header Cross-Origin-Resource-Policy cross-origin; + proxy_hide_header Cross-Origin-Embedder-Policy; + add_header Cross-Origin-Embedder-Policy require-corp; } # encrypted blobs are immutable and are thus cached for a year diff --git a/server.js b/server.js index 3a71f83b8..1824cf59c 100644 --- a/server.js +++ b/server.js @@ -108,28 +108,21 @@ var setHeaders = (function () { // apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere applyHeaderMap(res, { "Cross-Origin-Opener-Policy": /^\/sheet\//.test(req.url)? 'same-origin': '', - "Cross-Origin-Embedder-Policy": 'require-corp', }); if (Env.NO_SANDBOX) { // handles correct configuration for local development // https://stackoverflow.com/questions/11531121/add-duplicate-http-response-headers-in-nodejs applyHeaderMap(res, { "Cross-Origin-Resource-Policy": 'cross-origin', + "Cross-Origin-Embedder-Policy": 'require-corp', }); } - // Don't set CSP headers on /api/config because they aren't necessary and they cause problems + // Don't set CSP headers on /api/ endpoints + // because they aren't necessary and they cause problems // when duplicated by NGINX in production environments - if (/^\/api\/(broadcast|config)/.test(req.url)) { - /* - if (Env.NO_SANDBOX) { - applyHeaderMap(res, { - "Cross-Origin-Resource-Policy": 'cross-origin', - }); - } - */ - return; - } + if (/^\/api\/(broadcast|config)/.test(req.url)) { return; } + applyHeaderMap(res, { "Cross-Origin-Resource-Policy": 'cross-origin', }); diff --git a/www/checkup/app-checkup.less b/www/checkup/app-checkup.less index 40e6a1add..a475d5983 100644 --- a/www/checkup/app-checkup.less +++ b/www/checkup/app-checkup.less @@ -20,7 +20,7 @@ html, body { } .pending { - border: 1px solid white; + border: 1px solid @cryptpad_text_col; .fa { margin-right: 20px; } @@ -45,7 +45,7 @@ html, body { table { td { padding: 5px; - border: 1px solid white; + border: 1px solid @cryptpad_text_col; } } diff --git a/www/checkup/main.js b/www/checkup/main.js index 87e310e32..6983e461c 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -237,7 +237,7 @@ define([ var blockUrl = Login.Block.getBlockUrl(opt.blockKeys); var blockRequest = Login.Block.serialize("{}", opt.blockKeys); var removeRequest = Login.Block.remove(opt.blockKeys); - console.log('Test block URL:', blockUrl); + console.warn('Testing block URL (%s). One 404 is normal.', blockUrl); var userHash = '/2/drive/edit/000000000000000000000000'; var secret = Hash.getSecrets('drive', userHash); @@ -375,7 +375,7 @@ define([ }); assert(function (cb, msg) { - msg = msg; + msg.innerText = "This test is incorrect."; return void cb(true); /* msg.appendChild(h('span', [ @@ -419,7 +419,6 @@ define([ $.ajax('/api/broadcast', { dataType: 'text', complete: function (xhr) { - console.log(xhr); cb(xhr.status === 200); }, }); @@ -445,6 +444,7 @@ define([ var expect = { 'cross-origin-resource-policy': 'cross-origin', + 'cross-origin-embedder-policy': 'require-corp', }; var incorrect = Object.keys(expect).some(function (k) { var response = xhr.getResponseHeader(k); From d9b9ca680fee7739256317c39c309d3d2f91cf9a Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 17 May 2021 11:00:25 +0200 Subject: [PATCH 09/29] Fix calendar day offset --- www/calendar/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 58262f956..58ad22ac1 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -119,7 +119,7 @@ define([ }; var getWeekDays = function (large) { - var baseDate = new Date(Date.UTC(2017, 0, 1)); // just a Sunday + var baseDate = new Date(2017, 0, 1); // just a Sunday var weekDays = []; for(var i = 0; i < 7; i++) { weekDays.push(baseDate.toLocaleDateString(undefined, { weekday: 'long' })); From 2ed25c38fb6aed00397e645fb9dd8d17aa32eec4 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 18 May 2021 12:25:53 +0530 Subject: [PATCH 10/29] display more information about incorrect headers on checkup page --- www/checkup/main.js | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/www/checkup/main.js b/www/checkup/main.js index 6983e461c..b17709a26 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -424,7 +424,11 @@ define([ }); }); - var checkAPIHeaders = function (url, cb) { + var code = function (content) { + return h('code', content); + }; + + var checkAPIHeaders = function (url, msg, cb) { $.ajax(url, { dataType: 'text', complete: function (xhr) { @@ -446,14 +450,29 @@ define([ 'cross-origin-resource-policy': 'cross-origin', 'cross-origin-embedder-policy': 'require-corp', }; - var incorrect = Object.keys(expect).some(function (k) { + var incorrect = false; + + Object.keys(expect).forEach(function (k) { var response = xhr.getResponseHeader(k); - if (response !== expect[k]) { - return true; + var expected = expect[k]; + if (response !== expected) { + incorrect = true; + msg.appendChild(h('p', [ + 'The ', + code(k), + ' header for ', + code(url), + " is '", + code(response), + "' instead of '", + code(expected), + "' as expected.", + ])); + } }); - if (duplicated || incorrect) { console.error(allHeaders); } + if (duplicated || incorrect) { console.debug(allHeaders); } cb(!duplicated && !incorrect); }, }); @@ -464,13 +483,13 @@ define([ assert(function (cb, msg) { var url = '/api/config'; msg.innerText = url + INCORRECT_HEADER_TEXT; - checkAPIHeaders(url, cb); + checkAPIHeaders(url, msg, cb); }); assert(function (cb, msg) { var url = '/api/broadcast'; msg.innerText = url + INCORRECT_HEADER_TEXT; - checkAPIHeaders(url, cb); + checkAPIHeaders(url, msg, cb); }); var setWarningClass = function (msg) { From f5c029e937de0bf02079e2f1e7ebca2bb09f7558 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 18 May 2021 10:47:11 +0200 Subject: [PATCH 11/29] Add config options to prevent anonymous users from creating pads #704 --- www/common/application_config_internal.js | 2 ++ www/common/sframe-common.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js index 791ecf367..904fe7e45 100644 --- a/www/common/application_config_internal.js +++ b/www/common/application_config_internal.js @@ -134,6 +134,8 @@ define(function() { // Prevent anonymous users from storing pads in their drive AppConfig.disableAnonymousStore = false; + // Prevent anonymous users from creating new pads (they can still access and edit existing ones) + AppConfig.disableAnonymousPadCreation = false; // Hide the usage bar in settings and drive //AppConfig.hideUsageBar = true; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 70c239d67..227656040 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -448,6 +448,9 @@ define([ } }; funcs.createPad = function (cfg, cb) { + if (AppConfig.disableAnonymousPadCreation && !funcs.isLoggedIn()) { + return void UI.errorLoadingScreen(Messages.mustLogin); + } ctx.sframeChan.query("Q_CREATE_PAD", { owned: cfg.owned, expire: cfg.expire, From d819ff093d8386f39cc9e3c85ff4a2bcfa9af331 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 18 May 2021 10:59:48 +0200 Subject: [PATCH 12/29] Fix date rendering with 12h format in flatpickr inputs --- www/admin/inner.js | 4 ++++ www/lib/calendar/date-picker.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/www/admin/inner.js b/www/admin/inner.js index 8e3ed16d2..527b081c5 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -1314,19 +1314,23 @@ define([ var $start = $(start); var $end = $(end); var is24h = false; + var dateFormat = "Y-m-d H:i"; try { is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/); } catch (e) {} + if (!is24h) { dateFormat = "Y-m-d h:i K"; } var endPickr = Flatpickr(end, { enableTime: true, time_24hr: is24h, + dateFormat: dateFormat, minDate: new Date() }); Flatpickr(start, { enableTime: true, time_24hr: is24h, minDate: new Date(), + dateFormat: dateFormat, onChange: function () { endPickr.set('minDate', new Date($start.val())); } diff --git a/www/lib/calendar/date-picker.js b/www/lib/calendar/date-picker.js index bc42dbbeb..d66e98c3d 100644 --- a/www/lib/calendar/date-picker.js +++ b/www/lib/calendar/date-picker.js @@ -9,14 +9,17 @@ define([ var end = cfg.endpicker; var is24h = false + var dateFormat = "Y-m-d H:i"; try { is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/); } catch (e) {} + if (!is24h) { dateFormat = "Y-m-d h:i K"; } var e = $(end.input)[0]; var endPickr = Flatpickr(e, { enableTime: true, time_24hr: is24h, + dateFormat: dateFormat, minDate: start.date }); endPickr.setDate(end.date); @@ -25,6 +28,7 @@ define([ var startPickr = Flatpickr(s, { enableTime: true, time_24hr: is24h, + dateFormat: dateFormat, onChange: function () { endPickr.set('minDate', startPickr.parseDate(s.value)); } From 755300d742ea4d27da4d449b02b8c701d27fc78d Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 18 May 2021 12:31:31 +0200 Subject: [PATCH 13/29] Add email and support config in the admin UI --- www/admin/inner.js | 117 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/www/admin/inner.js b/www/admin/inner.js index 527b081c5..5bc76d011 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -16,6 +16,7 @@ define([ '/support/ui.js', '/lib/datepicker/flatpickr.js', + '/bower_components/tweetnacl/nacl-fast.min.js', 'css!/lib/datepicker/flatpickr.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', @@ -44,6 +45,7 @@ define([ 'instanceStatus': {} }; + var Nacl = window.nacl; var common; var sFrameChan; @@ -54,6 +56,7 @@ define([ 'cp-admin-archive', 'cp-admin-unarchive', 'cp-admin-registration', + 'cp-admin-email' ], 'quota': [ // Msg.admin_cat_quota 'cp-admin-defaultlimit', @@ -71,7 +74,8 @@ define([ ], 'support': [ // Msg.admin_cat_support 'cp-admin-support-list', - 'cp-admin-support-init' + 'cp-admin-support-init', + 'cp-admin-support-priv', ], 'broadcast': [ // Msg.admin_cat_broadcast 'cp-admin-maintenance', @@ -285,6 +289,48 @@ define([ return $div; }; + Messages.admin_emailTitle = "Admin contact email"; // XXX + Messages.admin_emailHint = "Set the contact email for your instance here"; // XXX + Messages.admin_emailButton = "Update"; + create['email'] = function () { + var key = 'email'; + var $div = makeBlock(key, true); // Msg.admin_emailHint, Msg.admin_emailTitle, Msg.admin_emailButton + var $button = $div.find('button'); + + var input = h('input', { + type: 'email', + value: ApiConfig.adminEmail || '' + }); + var $input = $(input); + var innerDiv = h('div.cp-admin-setlimit-form', input); + var spinner = UI.makeSpinner($(innerDiv)); + + $button.click(function () { + if (!$input.val()) { return; } + spinner.spin(); + $button.attr('disabled', 'disabled'); + sFrameChan.query('Q_ADMIN_RPC', { + cmd: 'ADMIN_DECREE', + data: ['SET_ADMIN_EMAIL', [$input.val()]] + }, function (e, response) { + $button.removeAttr('disabled'); + if (e || response.error) { + UI.warn(Messages.error); + $input.val(''); + console.error(e, response); + spinner.hide(); + return; + } + spinner.done(); + UI.log(Messages.saved); + }); + }); + + $button.before(innerDiv); + + return $div; + }; + var getPrettySize = UIElements.prettySize; create['defaultlimit'] = function () { @@ -651,8 +697,13 @@ define([ }; var supportKey = ApiConfig.supportMailbox; + var checkAdminKey = function (priv) { + if (!supportKey) { return; } + return Hash.checkBoxKeyPair(priv, supportKey); + }; + create['support-list'] = function () { - if (!supportKey || !APP.privateKey) { return; } + if (!supportKey || !APP.privateKey || !checkAdminKey(APP.privateKey)) { return; } var $container = makeBlock('support-list'); // Msg.admin_supportListHint, .admin_supportListTitle var $div = $(h('div.cp-support-container')).appendTo($container); @@ -913,15 +964,66 @@ define([ }; - var checkAdminKey = function (priv) { - if (!supportKey) { return; } - return Hash.checkBoxKeyPair(priv, supportKey); - }; + Messages.admin_supportPrivTitle = "Support admin key"; // XXX + Messages.admin_supportPrivHint = "Display the private key allowing other admins to access the support. A form to enter this key will be displayed in their admin panel."; + Messages.admin_supportPrivButton = "Show key"; + create['support-priv'] = function () { + if (!supportKey || !APP.privateKey || !checkAdminKey(APP.privateKey)) { return; } + var $div = makeBlock('support-priv', true); // Msg.admin_supportPrivHint, .admin_supportPrivTitle, .admin_supportPrivButton + var $button = $div.find('button').click(function () { + $button.remove(); + $div.append(h('pre', APP.privateKey)); + }); + return $div; + }; + Messages.admin_supportInitGenerate = "Generate support keys"; // XXX create['support-init'] = function () { var $div = makeBlock('support-init'); // Msg.admin_supportInitHint, .admin_supportInitTitle if (!supportKey) { - $div.append(h('p', Messages.admin_supportInitHelp)); + (function () { + $div.append(h('p', Messages.admin_supportInitHelp)); // XXX Update text for this key + var button = h('button.btn.btn-primary', Messages.admin_supportInitGenerate); + var $button = $(button).appendTo($div); + $div.append($button); + var spinner = UI.makeSpinner($div); + $button.click(function () { + spinner.spin(); + $button.attr('disabled', 'disabled'); + var keyPair = Nacl.box.keyPair(); + var pub = Nacl.util.encodeBase64(keyPair.publicKey); + var priv = Nacl.util.encodeBase64(keyPair.secretKey); + // Store the private key first. It won't be used until the decree is accepted. + sFrameChan.query("Q_ADMIN_MAILBOX", priv, function (err, obj) { + if (err || (obj && obj.error)) { + console.error(err || obj.error); + UI.warn(Messages.error); + spinner.hide(); + return; + } + // Then send the decree + sFrameChan.query('Q_ADMIN_RPC', { + cmd: 'ADMIN_DECREE', + data: ['SET_SUPPORT_MAILBOX', [pub]] + }, function (e, response) { + $button.removeAttr('disabled'); + if (e || response.error) { + UI.warn(Messages.error); + console.error(e, response); + spinner.hide(); + return; + } + spinner.done(); + UI.log(Messages.saved); + supportKey = pub; + APP.privateKey = priv; + $('.cp-admin-support-init').hide(); + APP.$rightside.append(create['support-list']()); + APP.$rightside.append(create['support-priv']()); + }); + }); + }); + })(); return $div; } if (!APP.privateKey || !checkAdminKey(APP.privateKey)) { @@ -951,6 +1053,7 @@ define([ APP.privateKey = key; $('.cp-admin-support-init').hide(); APP.$rightside.append(create['support-list']()); + APP.$rightside.append(create['support-priv']()); }); }); return $div; From 7ce2b67fd62e1d486768adb240ed906f187a9822 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 19 May 2021 13:46:52 +0530 Subject: [PATCH 14/29] replace 'pricing' link text with 'features' when subscriptions are disabled addresses #683 --- customize.dist/pages.js | 13 ++++++++++++- customize.dist/pages/features.js | 14 ++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/customize.dist/pages.js b/customize.dist/pages.js index e3ae2b83e..26b0e7a4e 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -43,6 +43,17 @@ define([ return Pages.externalLink(el, Pages.localizeDocsLink(href)); }; + var accounts = Pages.accounts = { + donateURL: AppConfig.donateURL || "https://opencollective.com/cryptpad/", + upgradeURL: AppConfig.upgradeURL + }; + + Pages.areSubscriptionsAllowed = function () { + try { + return ApiConfig.allowSubscriptions && accounts.upgradeURL && !ApiConfig.restrictRegistration; + } catch (err) { return void console.error(err); } + }; + var languageSelector = function () { var options = []; var languages = Msg._languages; @@ -133,7 +144,7 @@ define([ footerCol('footer_product', [ footLink('/what-is-cryptpad.html', 'topbar_whatIsCryptpad'), Pages.docsLink, - footLink('/features.html', 'pricing'), + footLink('/features.html', Pages.areSubscriptionsAllowed()? 'pricing': 'features'), // Messages.pricing, Messages.features Pages.githubLink, footLink('https://opencollective.com/cryptpad/contribute/', 'footer_donate'), ]), diff --git a/customize.dist/pages/features.js b/customize.dist/pages/features.js index ee82d5d67..1dfd2417f 100644 --- a/customize.dist/pages/features.js +++ b/customize.dist/pages/features.js @@ -8,10 +8,7 @@ define([ '/api/config', '/common/common-ui-elements.js', ], function ($, h, Msg, AppConfig, LocalStore, Pages, Config, UIElements) { - var accounts = { - donateURL: AppConfig.donateURL || "https://opencollective.com/cryptpad/", - upgradeURL: AppConfig.upgradeURL - }; + var accounts = Pages.accounts; return function () { Msg.features_f_apps_note = AppConfig.availablePadTypes.map(function (app) { @@ -145,10 +142,11 @@ define([ ]), ]), ]); - var availableFeatures = - (Config.allowSubscriptions && accounts.upgradeURL && !Config.restrictRegistration) ? - [anonymousFeatures, registeredFeatures, premiumFeatures] : - [anonymousFeatures, registeredFeatures]; + var availableFeatures = [ + anonymousFeatures, + registeredFeatures, + Pages.areSubscriptionsAllowed() ? premiumFeatures: undefined, + ]; return h('div#cp-main', [ Pages.infopageTopbar(), From f2422483a5bd2bbed9ac20cf407e3f1df9e777b7 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 May 2021 14:37:04 +0530 Subject: [PATCH 15/29] note that some configurable restrictions are only enforced clientside and remove comments about an unsupported API addresses #704 --- www/common/application_config_internal.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js index 904fe7e45..dcfb3872b 100644 --- a/www/common/application_config_internal.js +++ b/www/common/application_config_internal.js @@ -133,8 +133,10 @@ define(function() { AppConfig.displayCreationScreen = true; // Prevent anonymous users from storing pads in their drive + // NOTE: this is only enforced client-side as the server does not distinguish between users drives and pads AppConfig.disableAnonymousStore = false; // Prevent anonymous users from creating new pads (they can still access and edit existing ones) + // NOTE: this is only enforced client-side and will not prevent malicious clients from storing data AppConfig.disableAnonymousPadCreation = false; // Hide the usage bar in settings and drive @@ -143,11 +145,6 @@ define(function() { // Disable feedback for all the users and hide the settings part about feedback //AppConfig.disableFeedback = true; - // Add new options in the share modal (extend an existing tab or add a new tab). - // More info about how to use it on the wiki: - // https://github.com/xwiki-labs/cryptpad/wiki/Application-config#configcustomizeshareoptions XXX page is a 404 - //AppConfig.customizeShareOptions = function (hashes, tabs, config) {}; - // Add code to be executed on every page before loading the user object. `isLoggedIn` (bool) is // indicating if the user is registered or anonymous. Here you can change the way anonymous users // work in CryptPad, use an external SSO or even force registration From 87c6e3270dc2dd1fa887cf95f81365e9d3bb05df Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 May 2021 16:13:28 +0530 Subject: [PATCH 16/29] make a note about restricting channel creation to registered users --- lib/hk-util.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/hk-util.js b/lib/hk-util.js index 7c244c174..5a96970d0 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -701,6 +701,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) { } if (msgCount === 0 && !metadata_cache[channelName] && Server.channelContainsUser(channelName, userId)) { + // TODO this might be a good place to reject channel creation by anonymous users handleFirstMessage(Env, channelName, metadata); Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(metadata)]); } From 0c7f77f5ed7d8bd18fbb8daa7a17d46d3e5be6b5 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 May 2021 16:16:07 +0530 Subject: [PATCH 17/29] sketch out some more sandbox tests and note down some improvements --- www/checkup/app-checkup.less | 6 ++ www/checkup/main.js | 30 ++++-- www/checkup/sandbox/index.html | 11 +++ www/checkup/sandbox/main.js | 175 +++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 8 deletions(-) create mode 100644 www/checkup/sandbox/index.html create mode 100644 www/checkup/sandbox/main.js diff --git a/www/checkup/app-checkup.less b/www/checkup/app-checkup.less index a475d5983..99999e2bb 100644 --- a/www/checkup/app-checkup.less +++ b/www/checkup/app-checkup.less @@ -12,6 +12,12 @@ html, body { color: @cryptpad_text_col; font-family: "IBM Plex Mono"; + iframe.sandbox-test { + display: block; + width: 100%; + height: 100%; + } + .report { font-size: 30px; max-width: 50%; diff --git a/www/checkup/main.js b/www/checkup/main.js index b17709a26..896156fc6 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -20,6 +20,7 @@ define([ ], function ($, ApiConfig, Assertions, h, Messages, DomReady, nThen, SFCommonO, Login, Hash, Util, Pinpad, NetConfig, Pages) { + var Assert = Assertions(); var trimSlashes = function (s) { if (typeof(s) !== 'string') { return s; } @@ -52,6 +53,13 @@ define([ var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin); var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin); +/* +// XXX display results from this iframe on this page + document.body.appendChild(h('iframe', { + class: 'sandbox-test', + src: trimmedSafe + '/checkup/sandbox/index.html', + })); +*/ assert(function (cb, msg) { msg.appendChild(h('span', [ @@ -117,7 +125,7 @@ define([ var checkAvailability = function (url, cb) { $.ajax({ - url: url, + url: url, // XXX bust cache data: {}, complete: function (xhr) { cb(xhr.status === 200); @@ -158,6 +166,7 @@ define([ ])); var to; + var obj; nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { @@ -165,8 +174,13 @@ define([ console.error('TIMEOUT loading iframe on the safe domain'); cb(false); }, 5000); - SFCommonO.initIframe(waitFor); + obj = SFCommonO.initIframe(waitFor); + }).nThen(function () { + SFCommonO.start({ + href: obj.href, + }); }).nThen(function () { + console.error("DONE?"); // Iframe is loaded clearTimeout(to); cb(true); @@ -344,7 +358,7 @@ define([ //'cross-origin-opener-policy': 'same-origin', // FIXME this is in our nginx config but not server.js }; - $.ajax(url, { + $.ajax(url, { // XXX bust cache complete: function (xhr) { cb(!Object.keys(expect).some(function (k) { var response = xhr.getResponseHeader(k); @@ -390,7 +404,7 @@ define([ RESTART_WARNING(), ])); - $.ajax(sheetURL, { + $.ajax(sheetURL, { // FIXME bust cache complete: function (xhr) { var csp = xhr.getResponseHeader('Content-Security-Policy'); if (!/unsafe\-eval/.test(csp)) { @@ -416,7 +430,7 @@ define([ "Your browser console may provide more details as to why this resource could not be loaded. ", ])); - $.ajax('/api/broadcast', { + $.ajax('/api/broadcast', { // XXX bust cache dataType: 'text', complete: function (xhr) { cb(xhr.status === 200); @@ -429,7 +443,7 @@ define([ }; var checkAPIHeaders = function (url, msg, cb) { - $.ajax(url, { + $.ajax(url, { // XXX bust cache dataType: 'text', complete: function (xhr) { var allHeaders = xhr.getAllResponseHeaders(); @@ -481,13 +495,13 @@ define([ var INCORRECT_HEADER_TEXT = ' was served with duplicated or incorrect headers. Compare your reverse-proxy configuration against the provided example.'; assert(function (cb, msg) { - var url = '/api/config'; + var url = '/api/config'; // XXX bust cache msg.innerText = url + INCORRECT_HEADER_TEXT; checkAPIHeaders(url, msg, cb); }); assert(function (cb, msg) { - var url = '/api/broadcast'; + var url = '/api/broadcast'; // XXX bust cache msg.innerText = url + INCORRECT_HEADER_TEXT; checkAPIHeaders(url, msg, cb); }); diff --git a/www/checkup/sandbox/index.html b/www/checkup/sandbox/index.html new file mode 100644 index 000000000..04a9502d3 --- /dev/null +++ b/www/checkup/sandbox/index.html @@ -0,0 +1,11 @@ + + + + + + + + +
+ + diff --git a/www/checkup/sandbox/main.js b/www/checkup/sandbox/main.js new file mode 100644 index 000000000..8066dc1b3 --- /dev/null +++ b/www/checkup/sandbox/main.js @@ -0,0 +1,175 @@ +define([ + 'jquery', + '/api/config', + '/assert/assertions.js', + '/common/hyperscript.js', + '/customize/messages.js', + '/common/dom-ready.js', + '/bower_components/nthen/index.js', + '/common/sframe-common-outer.js', + '/customize/login.js', + '/common/common-hash.js', + '/common/common-util.js', + '/common/pinpad.js', + '/common/outer/network-config.js', + '/customize/pages.js', + + '/bower_components/tweetnacl/nacl-fast.min.js', + 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', + 'less!/checkup/app-checkup.less', +], function ($, ApiConfig, Assertions, h, Messages, DomReady, + nThen, SFCommonO, Login, Hash, Util, Pinpad, + NetConfig, Pages) { + var Assert = Assertions(); + var assert = function (f, msg) { + Assert(f, msg || h('span.advisory-text.cp-danger')); + }; + + var code = function (content) { + return h('code', content); + }; + + var getHeaders = function (url, cb) { + $.ajax(url + "?test=" + (+new Date()), { + dataType: 'text', + complete: function (xhr) { + var allHeaders = xhr.getAllResponseHeaders(); + return void cb(void 0, allHeaders, xhr); + }, + }); + }; + var parseCSP = function (CSP) { + //console.error(CSP); + var CSP_headers = {}; + CSP.split(";") + .forEach(function (rule) { + rule = (rule || "").trim(); + if (!rule) { return; } + var parts = rule.split(/\s/); + var first = parts[0]; + var rest = rule.slice(first.length + 1); + CSP_headers[first] = rest; + //console.error(rule.trim()); + console.info("[%s] '%s'", first, rest); + }); + return CSP_headers; + }; + + var hasUnsafeEval = function (CSP_headers) { + return /unsafe\-eval/.test(CSP_headers['script-src']); + }; + + var hasUnsafeInline = function (CSP_headers) { + return /unsafe\-inline/.test(CSP_headers['script-src']); + }; + + var hasOnlyOfficeHeaders = function (CSP_headers) { + if (!hasUnsafeEval(CSP_headers)) { + console.error("NO_UNSAFE_EVAL"); + console.log(CSP_headers); + return false; + } + if (!hasUnsafeInline(CSP_headers)) { + console.error("NO_UNSAFE_INLINE"); + return void false; + } + return true; + }; + + // XXX run these from /checkup/inner.js and report to /checkup/main.js + assert(function (cb, msg) { + var url = '/sheet/inner.html'; + msg.appendChild(h('span', [ + code(url), + ' has the wrong headers.', + ])); + getHeaders(url, function (err, headers, xhr) { + var CSP_headers = parseCSP(xhr.getResponseHeader('content-security-policy')); + cb(hasOnlyOfficeHeaders(CSP_headers)); + }); + }); + + assert(function (cb, msg) { + var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; + msg.appendChild(h('span', [ + code(url), + ' has the wrong headers.', + ])); + getHeaders(url, function (err, headers, xhr) { + var CSP_headers = parseCSP(xhr.getResponseHeader('content-security-policy')); + cb(hasOnlyOfficeHeaders(CSP_headers)); + }); + }); + + var row = function (cells) { + return h('tr', cells.map(function (cell) { + return h('td', cell); + })); + }; + + var failureReport = function (obj) { + return h('div.error', [ + h('h5', obj.message), + h('table', [ + row(["Failed test number", obj.test + 1]), + row(["Returned value", obj.output]), + ]), + ]); + }; + + var completed = 0; + var $progress = $('#cp-progress'); + + var versionStatement = function () { + return h('p', [ + "This instance is running ", + h('span.cp-app-checkup-version',[ + "CryptPad", + ' ', + Pages.versionString, + ]), + '.', + ]); + }; + + Assert.run(function (state) { + var errors = state.errors; + var failed = errors.length; + + Messages.assert_numberOfTestsPassed = "{0} / {1} tests passed."; + + var statusClass = failed? 'failure': 'success'; + + var failedDetails = "Details found below"; + var successDetails = "This checkup only tests the most common configuration issues. You may still experience errors or incorrect behaviour."; + var details = h('p', failed? failedDetails: successDetails); + + var summary = h('div.summary.' + statusClass, [ + versionStatement(), + h('p', Messages._getKey('assert_numberOfTestsPassed', [ + state.passed, + state.total + ])), + details, + ]); + + var report = h('div.report', [ + summary, + h('div.failures', errors.map(failureReport)), + ]); + + $progress.remove(); + $('body').prepend(report); + }, function (i, total) { + console.log('test '+ i +' completed'); + completed++; + Messages.assert_numberOfTestsCompleted = "{0} / {1} tests completed."; + $progress.html('').append(h('div.report.pending.summary', [ + versionStatement(), + h('p', [ + h('i.fa.fa-spinner.fa-pulse'), + h('span', Messages._getKey('assert_numberOfTestsCompleted', [completed, total])) + ]) + ])); + }); +}); From 2262929c090695646368c9b77627ca650afee6d8 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 21 May 2021 18:38:33 +0530 Subject: [PATCH 18/29] remove hardcoded translations for new admin components --- www/admin/inner.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/www/admin/inner.js b/www/admin/inner.js index 5bc76d011..03e2270a9 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -289,9 +289,6 @@ define([ return $div; }; - Messages.admin_emailTitle = "Admin contact email"; // XXX - Messages.admin_emailHint = "Set the contact email for your instance here"; // XXX - Messages.admin_emailButton = "Update"; create['email'] = function () { var key = 'email'; var $div = makeBlock(key, true); // Msg.admin_emailHint, Msg.admin_emailTitle, Msg.admin_emailButton @@ -963,26 +960,22 @@ define([ return $container; }; - - Messages.admin_supportPrivTitle = "Support admin key"; // XXX - Messages.admin_supportPrivHint = "Display the private key allowing other admins to access the support. A form to enter this key will be displayed in their admin panel."; - Messages.admin_supportPrivButton = "Show key"; create['support-priv'] = function () { if (!supportKey || !APP.privateKey || !checkAdminKey(APP.privateKey)) { return; } var $div = makeBlock('support-priv', true); // Msg.admin_supportPrivHint, .admin_supportPrivTitle, .admin_supportPrivButton var $button = $div.find('button').click(function () { $button.remove(); - $div.append(h('pre', APP.privateKey)); + var $selectable = $(UI.dialog.selectable(APP.privateKey)).css({ 'max-width': '28em' }); + $div.append($selectable); }); return $div; }; - Messages.admin_supportInitGenerate = "Generate support keys"; // XXX create['support-init'] = function () { var $div = makeBlock('support-init'); // Msg.admin_supportInitHint, .admin_supportInitTitle if (!supportKey) { (function () { - $div.append(h('p', Messages.admin_supportInitHelp)); // XXX Update text for this key + $div.append(h('p', Messages.admin_supportInitHelp)); var button = h('button.btn.btn-primary', Messages.admin_supportInitGenerate); var $button = $(button).appendTo($div); $div.append($button); @@ -1419,7 +1412,7 @@ define([ var is24h = false; var dateFormat = "Y-m-d H:i"; try { - is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/); + is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/); // XXX } catch (e) {} if (!is24h) { dateFormat = "Y-m-d h:i K"; } From 7000abf3c017ec3b367250fb5f4602c69bcef6ed Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 21 May 2021 18:47:53 +0530 Subject: [PATCH 19/29] widen markdown code blocks --- customize.dist/src/less2/include/markdown.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customize.dist/src/less2/include/markdown.less b/customize.dist/src/less2/include/markdown.less index 717b49990..3943f128d 100644 --- a/customize.dist/src/less2/include/markdown.less +++ b/customize.dist/src/less2/include/markdown.less @@ -194,7 +194,7 @@ pre > code { display: block; position: relative; - width: 90%; + width: 100%; margin: auto; padding: 5px; overflow-x: auto; From 506c78f121cd88b372c5de01f15018aead65e02e Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 21 May 2021 18:48:26 +0530 Subject: [PATCH 20/29] increment version string to 4.6.0 --- customize.dist/pages.js | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 26b0e7a4e..aaf7c60b3 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -105,7 +105,7 @@ define([ var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ? '/imprint.html' : AppConfig.imprint); - Pages.versionString = "v4.5.0"; + Pages.versionString = "v4.6.0"; // used for the about menu diff --git a/package-lock.json b/package-lock.json index 75b03cfe1..b1d794abe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cryptpad", - "version": "4.5.0", + "version": "4.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3314ffd17..8526afc8a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "4.5.0", + "version": "4.6.0", "license": "AGPL-3.0+", "repository": { "type": "git", From cc5674585899e24339aa7b4d7899269d483cbe7c Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 21 May 2021 20:35:48 +0530 Subject: [PATCH 21/29] add more thorough tests for sandbox configuration on the checkup page --- www/checkup/app-checkup.less | 6 -- www/checkup/main.js | 194 +++++++++++++++++++++++++++++++---- www/checkup/sandbox/main.js | 183 ++++++--------------------------- 3 files changed, 202 insertions(+), 181 deletions(-) diff --git a/www/checkup/app-checkup.less b/www/checkup/app-checkup.less index 99999e2bb..a475d5983 100644 --- a/www/checkup/app-checkup.less +++ b/www/checkup/app-checkup.less @@ -12,12 +12,6 @@ html, body { color: @cryptpad_text_col; font-family: "IBM Plex Mono"; - iframe.sandbox-test { - display: block; - width: 100%; - height: 100%; - } - .report { font-size: 30px; max-width: 50%; diff --git a/www/checkup/main.js b/www/checkup/main.js index 896156fc6..3ac3b190a 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -20,7 +20,6 @@ define([ ], function ($, ApiConfig, Assertions, h, Messages, DomReady, nThen, SFCommonO, Login, Hash, Util, Pinpad, NetConfig, Pages) { - var Assert = Assertions(); var trimSlashes = function (s) { if (typeof(s) !== 'string') { return s; } @@ -51,15 +50,12 @@ define([ ]); }; + var cacheBuster = function (url) { + return url + '?test=' + (+new Date()); + }; + var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin); var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin); -/* -// XXX display results from this iframe on this page - document.body.appendChild(h('iframe', { - class: 'sandbox-test', - src: trimmedSafe + '/checkup/sandbox/index.html', - })); -*/ assert(function (cb, msg) { msg.appendChild(h('span', [ @@ -125,7 +121,7 @@ define([ var checkAvailability = function (url, cb) { $.ajax({ - url: url, // XXX bust cache + url: cacheBuster(url), data: {}, complete: function (xhr) { cb(xhr.status === 200); @@ -166,7 +162,6 @@ define([ ])); var to; - var obj; nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { @@ -174,13 +169,8 @@ define([ console.error('TIMEOUT loading iframe on the safe domain'); cb(false); }, 5000); - obj = SFCommonO.initIframe(waitFor); + SFCommonO.initIframe(waitFor); }).nThen(function () { - SFCommonO.start({ - href: obj.href, - }); - }).nThen(function () { - console.error("DONE?"); // Iframe is loaded clearTimeout(to); cb(true); @@ -351,14 +341,14 @@ define([ assert(function (cb, msg) { msg.innerText = "Missing HTTP headers required for .xlsx export from sheets. "; - var url = sheetURL; + var url = cacheBuster(sheetURL); var expect = { 'cross-origin-resource-policy': 'cross-origin', 'cross-origin-embedder-policy': 'require-corp', //'cross-origin-opener-policy': 'same-origin', // FIXME this is in our nginx config but not server.js }; - $.ajax(url, { // XXX bust cache + $.ajax(url, { complete: function (xhr) { cb(!Object.keys(expect).some(function (k) { var response = xhr.getResponseHeader(k); @@ -430,7 +420,7 @@ define([ "Your browser console may provide more details as to why this resource could not be loaded. ", ])); - $.ajax('/api/broadcast', { // XXX bust cache + $.ajax(cacheBuster('/api/broadcast'), { dataType: 'text', complete: function (xhr) { cb(xhr.status === 200); @@ -443,7 +433,7 @@ define([ }; var checkAPIHeaders = function (url, msg, cb) { - $.ajax(url, { // XXX bust cache + $.ajax(cacheBuster(url), { dataType: 'text', complete: function (xhr) { var allHeaders = xhr.getAllResponseHeaders(); @@ -495,13 +485,13 @@ define([ var INCORRECT_HEADER_TEXT = ' was served with duplicated or incorrect headers. Compare your reverse-proxy configuration against the provided example.'; assert(function (cb, msg) { - var url = '/api/config'; // XXX bust cache + var url = '/api/config'; msg.innerText = url + INCORRECT_HEADER_TEXT; checkAPIHeaders(url, msg, cb); }); assert(function (cb, msg) { - var url = '/api/broadcast'; // XXX bust cache + var url = '/api/broadcast'; msg.innerText = url + INCORRECT_HEADER_TEXT; checkAPIHeaders(url, msg, cb); }); @@ -559,6 +549,166 @@ define([ cb(false); }); + var response = Util.response(function (err) { + console.error('SANDBOX_ERROR', err); + }); + + var sandboxIframe = h('iframe', { + class: 'sandbox-test', + src: cacheBuster(trimmedSafe + '/checkup/sandbox/index.html'), + }); + document.body.appendChild(sandboxIframe); + + var sandboxIframeReady = Util.mkEvent(true); + setTimeout(function () { + sandboxIframeReady.fire("TIMEOUT"); + }, 10 * 1000); + + var postMessage = function (content, cb) { + try { + var txid = Util.uid(); + content.txid = txid; + response.expect(txid, cb, 15000); + sandboxIframe.contentWindow.postMessage(JSON.stringify(content), '*'); + } catch (err) { + console.error(err); + } + }; + + window.addEventListener('message', function (event) { + try { + var msg = JSON.parse(event.data); + if (msg.command === 'READY') { return void sandboxIframeReady.fire(); } + var txid = msg.txid; + if (!txid) { return console.log("no handler for ", txid); } + response.handle(txid, msg.content); + } catch (err) { + console.error(event); + console.error(err); + } + }); + + var parseCSP = function (CSP) { + //console.error(CSP); + var CSP_headers = {}; + CSP.split(";") + .forEach(function (rule) { + rule = (rule || "").trim(); + if (!rule) { return; } + var parts = rule.split(/\s/); + var first = parts[0]; + var rest = rule.slice(first.length + 1); + CSP_headers[first] = rest; + //console.error(rule.trim()); + //console.info("[%s] '%s'", first, rest); + }); + return CSP_headers; + }; + + var hasUnsafeEval = function (CSP_headers) { + return /unsafe\-eval/.test(CSP_headers['script-src']); + }; + + var hasUnsafeInline = function (CSP_headers) { + return /unsafe\-inline/.test(CSP_headers['script-src']); + }; + + var hasOnlyOfficeHeaders = function (CSP_headers) { + if (!hasUnsafeEval(CSP_headers)) { + console.error("NO_UNSAFE_EVAL"); + console.log(CSP_headers); + return false; + } + if (!hasUnsafeInline(CSP_headers)) { + console.error("NO_UNSAFE_INLINE"); + return void false; + } + return true; + }; + + var CSP_WARNING = function (url) { + return h('span', [ + code(url), + ' does not have the required ', + code("'content-security-policy'"), + ' headers set. This is most often related to incorrectly configured sandbox domains or reverse proxies.', + ]); + }; + + assert(function (_cb, msg) { + var url = '/sheet/inner.html'; + var cb = Util.once(Util.mkAsync(_cb)); + msg.appendChild(CSP_WARNING(url)); + nThen(function (w) { + sandboxIframeReady.reg(w(function (err) { + if (!err) { return; } + w.abort(); + cb(err); + })); + }).nThen(function () { + postMessage({ + command: 'GET_HEADER', + content: { + url: url, + header: 'content-security-policy', + }, + }, function (content) { + var CSP_headers = parseCSP(content); + cb(hasOnlyOfficeHeaders(CSP_headers)); + }); + }); + }); + + assert(function (cb, msg) { + var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; + msg.appendChild(CSP_WARNING(url)); + nThen(function (w) { + sandboxIframeReady.reg(w(function (err) { + if (!err) { return; } + w.abort(); + cb(err); + })); + }).nThen(function () { + postMessage({ + command: 'GET_HEADER', + content: { + url: url, + header: 'content-security-policy', + }, + }, function (content) { + var CSP_headers = parseCSP(content); + cb(hasOnlyOfficeHeaders(CSP_headers)); + }); + }); + }); + + assert(function (cb, msg) { + var url = '/sheet/inner.html'; + msg.appendChild(h('span', [ + code(url), + ' does not have the required ', + code("'cross-origin-opener-policy'"), + ' headers set.', + ])); + nThen(function (w) { + sandboxIframeReady.reg(w(function (err) { + if (!err) { return; } + w.abort(); + cb(err); + })); + }).nThen(function () { + postMessage({ + command: 'GET_HEADER', + content: { + url: url, + header: 'cross-origin-opener-policy', + }, + }, function (content) { + cb(content === 'same-origin'); + }); + }); + }); + if (false) { assert(function (cb, msg) { msg.innerText = 'fake test to simulate failure'; diff --git a/www/checkup/sandbox/main.js b/www/checkup/sandbox/main.js index 8066dc1b3..7450ed0e3 100644 --- a/www/checkup/sandbox/main.js +++ b/www/checkup/sandbox/main.js @@ -1,34 +1,16 @@ define([ 'jquery', - '/api/config', - '/assert/assertions.js', - '/common/hyperscript.js', - '/customize/messages.js', - '/common/dom-ready.js', - '/bower_components/nthen/index.js', - '/common/sframe-common-outer.js', - '/customize/login.js', - '/common/common-hash.js', - '/common/common-util.js', - '/common/pinpad.js', - '/common/outer/network-config.js', - '/customize/pages.js', + //'/bower_components/nthen/index.js', + //'/common/common-util.js', '/bower_components/tweetnacl/nacl-fast.min.js', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/checkup/app-checkup.less', -], function ($, ApiConfig, Assertions, h, Messages, DomReady, - nThen, SFCommonO, Login, Hash, Util, Pinpad, - NetConfig, Pages) { - var Assert = Assertions(); - var assert = function (f, msg) { - Assert(f, msg || h('span.advisory-text.cp-danger')); +], function ($ /*, nThen, Util */) { + var postMessage = function (content) { + window.parent.postMessage(JSON.stringify(content), '*'); }; - - var code = function (content) { - return h('code', content); - }; - + postMessage({ command: "READY", }); var getHeaders = function (url, cb) { $.ajax(url + "?test=" + (+new Date()), { dataType: 'text', @@ -38,138 +20,33 @@ define([ }, }); }; - var parseCSP = function (CSP) { - //console.error(CSP); - var CSP_headers = {}; - CSP.split(";") - .forEach(function (rule) { - rule = (rule || "").trim(); - if (!rule) { return; } - var parts = rule.split(/\s/); - var first = parts[0]; - var rest = rule.slice(first.length + 1); - CSP_headers[first] = rest; - //console.error(rule.trim()); - console.info("[%s] '%s'", first, rest); - }); - return CSP_headers; - }; - - var hasUnsafeEval = function (CSP_headers) { - return /unsafe\-eval/.test(CSP_headers['script-src']); - }; - - var hasUnsafeInline = function (CSP_headers) { - return /unsafe\-inline/.test(CSP_headers['script-src']); - }; - - var hasOnlyOfficeHeaders = function (CSP_headers) { - if (!hasUnsafeEval(CSP_headers)) { - console.error("NO_UNSAFE_EVAL"); - console.log(CSP_headers); - return false; - } - if (!hasUnsafeInline(CSP_headers)) { - console.error("NO_UNSAFE_INLINE"); - return void false; - } - return true; - }; - - // XXX run these from /checkup/inner.js and report to /checkup/main.js - assert(function (cb, msg) { - var url = '/sheet/inner.html'; - msg.appendChild(h('span', [ - code(url), - ' has the wrong headers.', - ])); + var COMMANDS = {}; + COMMANDS.GET_HEADER = function (content, cb) { + var url = content.url; getHeaders(url, function (err, headers, xhr) { - var CSP_headers = parseCSP(xhr.getResponseHeader('content-security-policy')); - cb(hasOnlyOfficeHeaders(CSP_headers)); + cb(xhr.getResponseHeader(content.header)); }); - }); - - assert(function (cb, msg) { - var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; - msg.appendChild(h('span', [ - code(url), - ' has the wrong headers.', - ])); - getHeaders(url, function (err, headers, xhr) { - var CSP_headers = parseCSP(xhr.getResponseHeader('content-security-policy')); - cb(hasOnlyOfficeHeaders(CSP_headers)); - }); - }); - - var row = function (cells) { - return h('tr', cells.map(function (cell) { - return h('td', cell); - })); - }; - - var failureReport = function (obj) { - return h('div.error', [ - h('h5', obj.message), - h('table', [ - row(["Failed test number", obj.test + 1]), - row(["Returned value", obj.output]), - ]), - ]); - }; - - var completed = 0; - var $progress = $('#cp-progress'); - - var versionStatement = function () { - return h('p', [ - "This instance is running ", - h('span.cp-app-checkup-version',[ - "CryptPad", - ' ', - Pages.versionString, - ]), - '.', - ]); }; - Assert.run(function (state) { - var errors = state.errors; - var failed = errors.length; - - Messages.assert_numberOfTestsPassed = "{0} / {1} tests passed."; - - var statusClass = failed? 'failure': 'success'; - - var failedDetails = "Details found below"; - var successDetails = "This checkup only tests the most common configuration issues. You may still experience errors or incorrect behaviour."; - var details = h('p', failed? failedDetails: successDetails); - - var summary = h('div.summary.' + statusClass, [ - versionStatement(), - h('p', Messages._getKey('assert_numberOfTestsPassed', [ - state.passed, - state.total - ])), - details, - ]); - - var report = h('div.report', [ - summary, - h('div.failures', errors.map(failureReport)), - ]); - - $progress.remove(); - $('body').prepend(report); - }, function (i, total) { - console.log('test '+ i +' completed'); - completed++; - Messages.assert_numberOfTestsCompleted = "{0} / {1} tests completed."; - $progress.html('').append(h('div.report.pending.summary', [ - versionStatement(), - h('p', [ - h('i.fa.fa-spinner.fa-pulse'), - h('span', Messages._getKey('assert_numberOfTestsCompleted', [completed, total])) - ]) - ])); + window.addEventListener("message", function (event) { + if (event && event.data) { + try { + //console.log(JSON.parse(event.data)); + var msg = JSON.parse(event.data); + var command = msg.command; + var txid = msg.txid; + COMMANDS[command](msg.content, function (response) { + // postMessage with same txid + postMessage({ + txid: txid, + content: response, + }); + }); + } catch (err) { + console.error(err); + } + } else { + console.error(event); + } }); }); From 1f86578920d7f9ff849e473ec78e564d24e7f29a Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 21 May 2021 20:38:47 +0530 Subject: [PATCH 22/29] update instructions for adminEmail configuration --- config/config.example.js | 5 ----- www/checkup/main.js | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index f53f3bb5b..ef54f3413 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -147,11 +147,6 @@ module.exports = { */ //removeDonateButton: false, - /* CryptPad will display a point of contact for your instance on its contact page - * (/contact.html) if you provide it below. - */ - adminEmail: 'i.did.not.read.my.config@cryptpad.fr', - /* * By default, CryptPad contacts one of our servers once a day. * This check-in will also send some very basic information about your instance including its diff --git a/www/checkup/main.js b/www/checkup/main.js index 3ac3b190a..2b1c68ffa 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -511,8 +511,7 @@ define([ 'This instance does not provide a valid ', h('code', 'adminEmail'), ' which can make it difficult to contact its adminstrator to report vulnerabilities or abusive content.', - ' This can be configured in ', CONFIG_PATH(), '. ', - RESTART_WARNING(), + " This can be configured on your instance's admin panel. Use the provided 'Flush cache' button for this change to take effect for all users." ])); cb(email); }); From 9c3dc7aa9c46f943c83ea863cecde5252f018ba4 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 26 May 2021 19:05:19 +0530 Subject: [PATCH 23/29] simplify some tests on the checkup page --- www/checkup/main.js | 89 ++++++++++++++++--------------------- www/checkup/sandbox/main.js | 4 +- 2 files changed, 40 insertions(+), 53 deletions(-) diff --git a/www/checkup/main.js b/www/checkup/main.js index 2b1c68ffa..2ab2cbbb0 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -574,6 +574,19 @@ define([ } }; + var deferredPostMessage = function (content, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); + nThen(function (w) { + sandboxIframeReady.reg(w(function (err) { + if (!err) { return; } + w.abort(); + cb(err); + })); + }).nThen(function () { + postMessage(content, cb); + }); + }; + window.addEventListener('message', function (event) { try { var msg = JSON.parse(event.data); @@ -638,46 +651,30 @@ define([ var url = '/sheet/inner.html'; var cb = Util.once(Util.mkAsync(_cb)); msg.appendChild(CSP_WARNING(url)); - nThen(function (w) { - sandboxIframeReady.reg(w(function (err) { - if (!err) { return; } - w.abort(); - cb(err); - })); - }).nThen(function () { - postMessage({ - command: 'GET_HEADER', - content: { - url: url, - header: 'content-security-policy', - }, - }, function (content) { - var CSP_headers = parseCSP(content); - cb(hasOnlyOfficeHeaders(CSP_headers)); - }); + deferredPostMessage({ + command: 'GET_HEADER', + content: { + url: url, + header: 'content-security-policy', + }, + }, function (content) { + var CSP_headers = parseCSP(content); + cb(hasOnlyOfficeHeaders(CSP_headers)); }); }); assert(function (cb, msg) { var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; msg.appendChild(CSP_WARNING(url)); - nThen(function (w) { - sandboxIframeReady.reg(w(function (err) { - if (!err) { return; } - w.abort(); - cb(err); - })); - }).nThen(function () { - postMessage({ - command: 'GET_HEADER', - content: { - url: url, - header: 'content-security-policy', - }, - }, function (content) { - var CSP_headers = parseCSP(content); - cb(hasOnlyOfficeHeaders(CSP_headers)); - }); + deferredPostMessage({ + command: 'GET_HEADER', + content: { + url: url, + header: 'content-security-policy', + }, + }, function (content) { + var CSP_headers = parseCSP(content); + cb(hasOnlyOfficeHeaders(CSP_headers)); }); }); @@ -689,22 +686,14 @@ define([ code("'cross-origin-opener-policy'"), ' headers set.', ])); - nThen(function (w) { - sandboxIframeReady.reg(w(function (err) { - if (!err) { return; } - w.abort(); - cb(err); - })); - }).nThen(function () { - postMessage({ - command: 'GET_HEADER', - content: { - url: url, - header: 'cross-origin-opener-policy', - }, - }, function (content) { - cb(content === 'same-origin'); - }); + deferredPostMessage({ + command: 'GET_HEADER', + content: { + url: url, + header: 'cross-origin-opener-policy', + }, + }, function (content) { + cb(content === 'same-origin'); }); }); diff --git a/www/checkup/sandbox/main.js b/www/checkup/sandbox/main.js index 7450ed0e3..7ddfb07f8 100644 --- a/www/checkup/sandbox/main.js +++ b/www/checkup/sandbox/main.js @@ -1,12 +1,10 @@ define([ 'jquery', - //'/bower_components/nthen/index.js', - //'/common/common-util.js', '/bower_components/tweetnacl/nacl-fast.min.js', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/checkup/app-checkup.less', -], function ($ /*, nThen, Util */) { +], function ($) { var postMessage = function (content) { window.parent.postMessage(JSON.stringify(content), '*'); }; From a5245fba207eaa13aa21743f651163cf7cee3874 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 26 May 2021 19:07:21 +0530 Subject: [PATCH 24/29] disable attempted execution of scripts in PDFs it triggered a CSP error which prevented teh rest of the document from rendering --- www/common/pdfjs/build/pdf.worker.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/pdfjs/build/pdf.worker.js b/www/common/pdfjs/build/pdf.worker.js index 753d5c5d2..2e1bb365e 100644 --- a/www/common/pdfjs/build/pdf.worker.js +++ b/www/common/pdfjs/build/pdf.worker.js @@ -5355,10 +5355,12 @@ var PDFFunction = function PDFFunctionClosure() { var domain = IR[1]; var range = IR[2]; var code = IR[3]; +/* var compiled = new PostScriptCompiler().compile(code, domain, range); if (compiled) { return new Function('src', 'srcOffset', 'dest', 'destOffset', compiled); } +*/ (0, _util.info)('Unable to compile PS function'); var numOutputs = range.length >> 1; var numInputs = domain.length >> 1; @@ -38545,4 +38547,4 @@ if (typeof PDFJS === 'undefined' || !PDFJS.compatibilityChecked) { /***/ }) /******/ ]); }); -//# sourceMappingURL=pdf.worker.js.map \ No newline at end of file +//# sourceMappingURL=pdf.worker.js.map From af7027e5edb4cc2b00bc2bf034225cb97fef3d25 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 May 2021 12:37:14 +0530 Subject: [PATCH 25/29] remove XXX that is actually O.K. --- www/admin/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/admin/inner.js b/www/admin/inner.js index 03e2270a9..3956d7e50 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -1412,7 +1412,7 @@ define([ var is24h = false; var dateFormat = "Y-m-d H:i"; try { - is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/); // XXX + is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/); } catch (e) {} if (!is24h) { dateFormat = "Y-m-d h:i K"; } From b8796d47513dd49721214a381445ad33920c9279 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 May 2021 12:37:50 +0530 Subject: [PATCH 26/29] WIP changelog --- CHANGELOG.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcb7e9230..e667c55b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,38 @@ -# WIP +# 4.6.0 -* fix opening links from temporary shared folders on iphone or other contexts that do not support shared workers -* add checkup test for disabling google FLoC +## Goals + +* work on polls/surveys +* stabilize and implement tests + +## Update notes + +* checkup/server/config + * test for anti-FLoC header + * add anti-FloC header to server so the default dev server passes all tests + * update NGINX example to avoid duplicated headers + * simplify dev server headers + * adjust table borders for dark/light mode + * say what headers are wrong +* rename exported object in `application_config_internal.js` to avoid copypasta errors +* `AppConfig.disableAnonymousPadCreation = false;` + * note that it's only enforced client-side * update lodash devDependency +## Features + +* generate `supportMailbox` keys via the admin panel + * document this +* markdown preview + * code blocks are full width + +## Bug fixes + +* fix opening links from temporary shared folders on iphone or other contexts that do not support shared workers +* show "features" instead of "pricing" in static pages' footer when premium subscriptions are not available +* use preferred 12/24h time format in date picker +* add error handling to admin panel decree RPCs + # 4.5.0 ## Goals From cba66d5db39444448e2027d2dba32871115abbdd Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 May 2021 14:17:32 +0530 Subject: [PATCH 27/29] close websockets when the checkup is complete --- www/checkup/main.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/www/checkup/main.js b/www/checkup/main.js index 2ab2cbbb0..66d2ccaaa 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -173,10 +173,13 @@ define([ }).nThen(function () { // Iframe is loaded clearTimeout(to); + console.log("removing sandbox iframe"); + $('iframe#sbox-iframe').remove(); cb(true); }); }); + var shared_websocket; // Test Websocket var evWSError = Util.mkEvent(true); assert(function (_cb, msg) { @@ -188,7 +191,7 @@ define([ ])); })); - var ws = new WebSocket(NetConfig.getWebsocketURL()); + var ws = shared_websocket = new WebSocket(NetConfig.getWebsocketURL()); var to = setTimeout(function () { console.error('Websocket TIMEOUT'); evWSError.fire(); @@ -207,6 +210,7 @@ define([ }); // Test login block + var shared_realtime; assert(function (_cb, msg) { var websocketErr = "No WebSocket available"; var cb = Util.once(Util.both(_cb, function (status) { @@ -268,7 +272,7 @@ define([ console.error("Can't create new channel. This may also be a websocket issue."); return void cb(false); } - RT = rt; + shared_realtime = RT = rt; var proxy = rt.proxy; proxy.edPublic = opt.edPublic; proxy.edPrivate = opt.edPrivate; @@ -334,7 +338,6 @@ define([ }).nThen(function () { cb(true); }); - }); var sheetURL = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; @@ -511,7 +514,9 @@ define([ 'This instance does not provide a valid ', h('code', 'adminEmail'), ' which can make it difficult to contact its adminstrator to report vulnerabilities or abusive content.', - " This can be configured on your instance's admin panel. Use the provided 'Flush cache' button for this change to take effect for all users." + " This can be configured on your instance's admin panel. Use the provided ", + code("Flush cache'"), + " button for this change to take effect for all users.", ])); cb(email); }); @@ -591,6 +596,7 @@ define([ try { var msg = JSON.parse(event.data); if (msg.command === 'READY') { return void sandboxIframeReady.fire(); } + if (msg.q === "READY") { return; } // ignore messages from the usual sandboxed iframe var txid = msg.txid; if (!txid) { return console.log("no handler for ", txid); } response.handle(txid, msg.content); @@ -763,6 +769,14 @@ define([ $progress.remove(); $('body').prepend(report); + try { + console.log('closing shared websocket'); + shared_websocket.close(); + } catch (err) { console.error(err); } + try { + console.log('closing shared realtime'); + shared_realtime.network.disconnect(); + } catch (err) { console.error(err); } }, function (i, total) { console.log('test '+ i +' completed'); completed++; From ae2e7818035455994069dda41aaf4af6a2eecada Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 May 2021 14:18:06 +0530 Subject: [PATCH 28/29] remove mention of the supportMailboxPublicKey from the example config --- config/config.example.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index ef54f3413..d05b985af 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -122,19 +122,6 @@ module.exports = { ], */ - /* CryptPad's administration panel includes a "support" tab - * wherein administrators with a secret key can view messages - * sent from users via the encrypted forms on the /support/ page - * - * To enable this functionality: - * run `node ./scripts/generate-admin-keys.js` - * save the public key in your config in the value below - * add the private key via the admin panel - * and back it up in a secure manner - * - */ - // supportMailboxPublicKey: "", - /* We're very proud that CryptPad is available to the public as free software! * We do, however, still need to pay our bills as we develop the platform. * From 8ecf7a70c441b8b4bfd44a09945828d48b0ef29c Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 May 2021 14:33:03 +0530 Subject: [PATCH 29/29] lint compliance and dead code removal --- www/checkup/main.js | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/www/checkup/main.js b/www/checkup/main.js index 66d2ccaaa..2beee1229 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -191,7 +191,8 @@ define([ ])); })); - var ws = shared_websocket = new WebSocket(NetConfig.getWebsocketURL()); + var ws = new WebSocket(NetConfig.getWebsocketURL()); + shared_websocket = ws; var to = setTimeout(function () { console.error('Websocket TIMEOUT'); evWSError.fire(); @@ -381,40 +382,6 @@ define([ }); }); - assert(function (cb, msg) { - msg.innerText = "This test is incorrect."; - return void cb(true); - /* - msg.appendChild(h('span', [ - "The spreadsheet editor's code was not served with the required Content-Security Policy headers. ", - "This is most often caused by incorrectly configured sandbox parameters (", - h('code', 'httpUnsafeOrigin'), - ' and ', - h('code', 'httpSafeOrigin'), - ' in ', - CONFIG_PATH, - "), or settings in your reverse proxy's configuration which don't match your application server's config. ", - RESTART_WARNING(), - ])); - - $.ajax(sheetURL, { // FIXME bust cache - complete: function (xhr) { - var csp = xhr.getResponseHeader('Content-Security-Policy'); - if (!/unsafe\-eval/.test(csp)) { - // OnlyOffice requires unsafe-eval - console.error('CSP', csp); - return cb("expected 'unsafe-eval'"); - } - if (!/unsafe\-inline/.test(csp)) { - // OnlyOffice also requires unsafe-inline - console.error('CSP', csp); - return cb("expected 'unsafe-inline'"); - } - cb(true); - }, - }); */ - }); - assert(function (cb, msg) { msg.appendChild(h('span', [ h('code', '/api/broadcast'),