diff --git a/.jshintignore b/.jshintignore index 1eb999871..ae60d4ba0 100644 --- a/.jshintignore +++ b/.jshintignore @@ -5,6 +5,7 @@ www/common/chainpad.js storage/kad.js www/common/otaml.js +server.js NetFluxWebsocketSrv.js NetFluxWebsocketServer.js WebRTCSrv.js diff --git a/bower.json b/bower.json index 5704b3aec..a6fc85b41 100644 --- a/bower.json +++ b/bower.json @@ -29,7 +29,7 @@ "rangy": "rangy-release#~1.3.0", "json.sortify": "~2.1.0", "fabric.js": "fabric#~1.6.0", - "hyperjson": "~1.3.1", + "hyperjson": "~1.4.0", "textpatcher": "^1.3.0", "proxy-polyfill": "^0.1.5", "chainpad": "^0.3.0", diff --git a/config.js.dist b/config.js.dist index 91bf38d95..da80bb374 100644 --- a/config.js.dist +++ b/config.js.dist @@ -14,24 +14,35 @@ module.exports = { * Examples are provided below */ -/* httpHeaders: { - "Content-Security-Policy": [ - "default-src 'none'", - "style-src 'unsafe-inline' 'self'", - "script-src 'self' 'unsafe-eval' 'unsafe-inline'", - "child-src 'self' cryptpad.fr *.cryptpad.fr", - "font-src 'self'", - "connect-src 'self' wss://cryptpad.fr", - // data: is used by codemirror, (insecure remote) images are included by - // users of the wysiwyg who embed photos in their pads - "img-src data: *", - ].join('; '), - "X-XSS-Protection": "1; mode=block", "X-Content-Type-Options": "nosniff", // 'X-Frame-Options': 'SAMEORIGIN', - },*/ + }, + + contentSecurity: [ + "default-src 'none'", + "style-src 'unsafe-inline' 'self'", + "script-src 'self'", + "child-src 'self' cryptpad.fr *.cryptpad.fr", + "font-src 'self'", + "connect-src 'self' wss://cryptpad.fr", + // data: is used by codemirror + "img-src 'self' data:", + ].join('; '), + + // CKEditor requires significantly more lax content security policy in order to function. + padContentSecurity: [ + "default-src 'none'", + "style-src 'unsafe-inline' 'self'", + // Unsafe inline, unsafe-eval are needed for ckeditor :( + "script-src 'self' 'unsafe-eval' 'unsafe-inline'", + "child-src 'self' cryptpad.fr *.cryptpad.fr", + "font-src 'self'", + "connect-src 'self' wss://cryptpad.fr", + // (insecure remote) images are included by users of the wysiwyg who embed photos in their pads + "img-src *", + ].join('; '), httpPort: 3000, @@ -51,7 +62,7 @@ module.exports = { //websocketPort: 3000, /* if you want to run a different version of cryptpad but using the same websocket - * server, you should use the other server port as websocketPort and disable + * server, you should use the other server port as websocketPort and disable * the websockets on that server */ //useExternalWebsocket: false, diff --git a/customize.dist/about.html b/customize.dist/about.html index c0f3a0b97..988efafc3 100644 --- a/customize.dist/about.html +++ b/customize.dist/about.html @@ -1,5 +1,6 @@ + Cryptpad: Zero Knowledge, Collaborative Real Time Editing @@ -9,16 +10,8 @@ - + - - -
@@ -126,4 +119,3 @@ - diff --git a/customize.dist/contact.html b/customize.dist/contact.html index 7b2cd03ae..1985d8347 100644 --- a/customize.dist/contact.html +++ b/customize.dist/contact.html @@ -1,5 +1,6 @@ + Cryptpad: Zero Knowledge, Collaborative Real Time Editing @@ -9,16 +10,8 @@ - + - - -
@@ -123,4 +116,3 @@ - diff --git a/customize.dist/index.html b/customize.dist/index.html index eb1815e34..12697787e 100644 --- a/customize.dist/index.html +++ b/customize.dist/index.html @@ -1,5 +1,6 @@ + Cryptpad: Zero Knowledge, Collaborative Real Time Editing @@ -9,16 +10,8 @@ - + - - -
@@ -245,4 +238,3 @@ - diff --git a/customize.dist/main.css b/customize.dist/main.css index fb3be659c..791563ed3 100644 --- a/customize.dist/main.css +++ b/customize.dist/main.css @@ -1306,7 +1306,6 @@ html.cp, max-width: 90%; max-height: 90%; margin: auto; - border: 5px solid red; } .cp div.modal, .cp div#modal { diff --git a/customize.dist/privacy.html b/customize.dist/privacy.html index 10910bfff..403a0f1c4 100644 --- a/customize.dist/privacy.html +++ b/customize.dist/privacy.html @@ -1,5 +1,6 @@ + Cryptpad: Zero Knowledge, Collaborative Real Time Editing @@ -9,16 +10,8 @@ - + - - -
@@ -144,4 +137,3 @@ - diff --git a/customize.dist/src/fragments/appscript.html b/customize.dist/src/fragments/appscript.html index 66cc0acea..3f1dd7b58 100644 --- a/customize.dist/src/fragments/appscript.html +++ b/customize.dist/src/fragments/appscript.html @@ -1,3 +1,2 @@ - - + diff --git a/customize.dist/src/fragments/empty.html b/customize.dist/src/fragments/empty.html index 05fd6a0cb..d42a450af 100644 --- a/customize.dist/src/fragments/empty.html +++ b/customize.dist/src/fragments/empty.html @@ -1,2 +1 @@ -
diff --git a/customize.dist/src/fragments/script.html b/customize.dist/src/fragments/script.html index 99ea023b5..35a65e8a1 100644 --- a/customize.dist/src/fragments/script.html +++ b/customize.dist/src/fragments/script.html @@ -1,2 +1 @@ - - + diff --git a/customize.dist/src/less/cryptpad.less b/customize.dist/src/less/cryptpad.less index a4b2f148e..6e0f951fb 100644 --- a/customize.dist/src/less/cryptpad.less +++ b/customize.dist/src/less/cryptpad.less @@ -861,8 +861,6 @@ form.realtime, div.realtime { max-width: 90%; max-height: 90%; margin: auto; - - border: 5px solid red; } } } diff --git a/customize.dist/src/less/toolbar.less b/customize.dist/src/less/toolbar.less index 3936b13f5..4330c98af 100644 --- a/customize.dist/src/less/toolbar.less +++ b/customize.dist/src/less/toolbar.less @@ -309,7 +309,10 @@ float: left; margin-bottom: -1px; .cryptpad-user-list { - float: right; + //float: right; + pre { + white-space: pre; + } } button { margin: 2px 4px 2px 0px; diff --git a/customize.dist/src/template.html b/customize.dist/src/template.html index 314fa29f9..069e23511 100644 --- a/customize.dist/src/template.html +++ b/customize.dist/src/template.html @@ -1,5 +1,6 @@ + Cryptpad: Zero Knowledge, Collaborative Real Time Editing @@ -10,13 +11,6 @@ {{script}} - - {{topbar}} @@ -30,4 +24,3 @@ {{footer}} - diff --git a/customize.dist/terms.html b/customize.dist/terms.html index 205f6c2a7..a92c99c93 100644 --- a/customize.dist/terms.html +++ b/customize.dist/terms.html @@ -1,5 +1,6 @@ + Cryptpad: Zero Knowledge, Collaborative Real Time Editing @@ -9,16 +10,8 @@ - + - - -
@@ -127,4 +120,3 @@ - diff --git a/customize.dist/toolbar.css b/customize.dist/toolbar.css index 5d81a1123..bf5458f84 100644 --- a/customize.dist/toolbar.css +++ b/customize.dist/toolbar.css @@ -372,8 +372,8 @@ float: left; margin-bottom: -1px; } -.cryptpad-toolbar-leftside .cryptpad-user-list { - float: right; +.cryptpad-toolbar-leftside .cryptpad-user-list pre { + white-space: pre; } .cryptpad-toolbar-leftside button { margin: 2px 4px 2px 0px; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index c32f346eb..d04b3d015 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -50,10 +50,8 @@ define(function () { out.orangeLight = "Your slow connection may impact your experience"; out.redLight = "You are disconnected from the session"; - out.importButton = 'IMPORT'; out.importButtonTitle = 'Import a pad from a local file'; - out.exportButton = 'EXPORT'; out.exportButtonTitle = 'Export this pad to a local file'; out.exportPrompt = 'What would you like to name your file?'; @@ -64,7 +62,6 @@ define(function () { out.clickToEdit = "Click to edit"; - out.forgetButton = 'FORGET'; out.forgetButtonTitle = 'Move this pad to the trash'; out.forgetPrompt = 'Clicking OK will move this pad to your trash. Are you sure?'; out.movedToTrash = 'That pad has been moved to the trash.
Access my Drive'; @@ -75,19 +72,14 @@ define(function () { out.newButton = 'New'; out.newButtonTitle = 'Create a new pad'; - out.presentButton = 'PRESENT'; out.presentButtonTitle = "Enter presentation mode"; out.presentSuccess = 'Hit ESC to exit presentation mode'; - out.sourceButton = 'VIEW SOURCE'; //TODO remove? hidden behind the present mode - out.sourceButtonTitle = "Leave presentation mode"; - out.backgroundButton = 'BACKGROUND COLOR'; out.backgroundButtonTitle = 'Change the background color in the presentation'; - out.colorButton = 'TEXT COLOR'; out.colorButtonTitle = 'Change the text color in presentation mode'; out.editShare = "Editing link"; - out.editShareTitle = "Copy the edit link to clipboard"; + out.editShareTitle = "Copy the editing link to clipboard"; out.viewShare = "Read-only link"; out.viewShareTitle = "Copy the read-only link to clipboard"; out.viewOpen = "Open read-only link in new tab"; @@ -110,7 +102,6 @@ define(function () { out.poll_p_save = "Your settings are updated instantly, so you never need to save."; out.poll_p_encryption = "All your input is encrypted so only people who have the link can access it. Even the server cannot see what you change."; - out.wizardButton = 'WIZARD'; out.wizardLog = "Click the button in the top left to return to your poll"; out.wizardTitle = "Use the wizard to create your poll"; out.wizardConfirm = "Are you really ready to add these options to your poll?"; @@ -221,7 +212,7 @@ define(function () { out.login_noSuchUser = 'Invalid username or password. Try again, or sign up'; out.login_invalUser = 'Username required'; out.login_invalPass = 'Password required'; - out.login_unhandledError = 'An unexpected error occured :('; + out.login_unhandledError = 'An unexpected error occurred :('; out.register_importRecent = "Import pad history (Recommended)"; out.register_acceptTerms = "I accept the terms of service"; diff --git a/package.json b/package.json index 05b1862fe..ba331a899 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "1.1.0", + "version": "1.1.1", "dependencies": { "express": "~4.10.1", "ws": "^1.0.1", diff --git a/server.js b/server.js index 548a78949..503558c1f 100644 --- a/server.js +++ b/server.js @@ -7,6 +7,7 @@ var Https = require('https'); var Fs = require('fs'); var WebSocketServer = require('ws').Server; var NetfluxSrv = require('./NetfluxWebsocketSrv'); +var Package = require('./package.json'); var config = require('./config'); var websocketPort = config.websocketPort || config.httpPort; @@ -19,20 +20,31 @@ var app = Express(); var httpsOpts; +const clone = (x) => (JSON.parse(JSON.stringify(x))); + var setHeaders = (function () { if (typeof(config.httpHeaders) !== 'object') { return function () {}; } - var headers = JSON.parse(JSON.stringify(config.httpHeaders)); + const headers = clone(config.httpHeaders); + if (config.contentSecurity) { + headers['Content-Security-Policy'] = clone(config.contentSecurity); + } + const padHeaders = clone(headers); + if (config.padContentSecurity) { + padHeaders['Content-Security-Policy'] = clone(config.padContentSecurity); + } if (Object.keys(headers).length) { - return function (res) { - for (var header in headers) { res.setHeader(header, headers[header]); } + return function (req, res) { + const h = /^\/pad\/inner\.html.*/.test(req.url) ? padHeaders : headers; + for (let header in h) { res.setHeader(header, h[header]); } }; } return function () {}; }()); app.use(function (req, res, next) { - setHeaders(res); + setHeaders(req, res); + if (/[\?\&]ver=[^\/]+$/.test(req.url)) { res.setHeader("Cache-Control", "max-age=31536000"); } next(); }); @@ -82,6 +94,10 @@ app.get('/api/config', function(req, res){ var host = req.headers.host.replace(/\:[0-9]+/, ''); res.setHeader('Content-Type', 'text/javascript'); res.send('define(' + JSON.stringify({ + requireConf: { + waitSeconds: 60, + urlArgs: 'ver=' + Package.version + }, websocketPath: config.useExternalWebsocket ? undefined : config.websocketPath, websocketURL:'ws' + ((useSecureWebsockets) ? 's' : '') + '://' + host + ':' + websocketPort + '/cryptpad_websocket', diff --git a/www/code/index.html b/www/code/index.html index df3fe1142..af6247a40 100644 --- a/www/code/index.html +++ b/www/code/index.html @@ -4,13 +4,7 @@ CryptPad - - +
- - +
- diff --git a/www/code/main.js b/www/code/main.js index 5ece7a208..e8999ffee 100644 --- a/www/code/main.js +++ b/www/code/main.js @@ -689,7 +689,7 @@ define([ // inform of network disconnect setEditable(false); toolbar.failed(); - Cryptpad.alert(Messages.common_connectionLost); + Cryptpad.alert(Messages.common_connectionLost, undefined, force); }; var onConnectionChange = config.onConnectionChange = function (info) { @@ -700,7 +700,7 @@ define([ toolbar.reconnecting(info.myId); Cryptpad.findOKButton().click(); } else { - Cryptpad.alert(Messages.common_connectionLost); + Cryptpad.alert(Messages.common_connectionLost, undefined, force); } }; diff --git a/www/common/boot.js b/www/common/boot.js new file mode 100644 index 000000000..406521778 --- /dev/null +++ b/www/common/boot.js @@ -0,0 +1,5 @@ +// Stage 0, this gets cached which means we can't change it. boot2.js is changable. +define(['/api/config?cb=' + (+new Date()).toString(16)], function (Config) { + if (Config.requireConf) { require.config(Config.requireConf); } + require(['/common/boot2.js']); +}); diff --git a/www/common/boot2.js b/www/common/boot2.js new file mode 100644 index 000000000..72519b73e --- /dev/null +++ b/www/common/boot2.js @@ -0,0 +1,6 @@ +// This is stage 1, it can be changed but you must bump the version of the project. +define([], function () { + // fix up locations so that relative urls work. + require.config({ baseUrl: window.location.pathname }); + require([document.querySelector('script[data-bootload]').getAttribute('data-bootload')]); +}); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 6660cac5b..99beca31b 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1,5 +1,5 @@ define([ - '/api/config?cb=' + Math.random().toString(16).slice(2), + '/api/config', '/customize/messages.js?app=' + window.location.pathname.split('/').filter(function (x) { return x; }).join('.'), '/customize/fsStore.js', '/bower_components/chainpad-crypto/crypto.js?v=0.1.5', @@ -163,10 +163,14 @@ define([ // var isArray = function (o) { return Object.prototype.toString.call(o) === '[object Array]'; }; var isArray = common.isArray = $.isArray; - var fixHTML = common.fixHTML = function (html) { - return html.replace(/&"']/g, function (x) { + return ({ "<": "<", ">": ">", "&": "&", '"': """, "'": "'" })[x]; + }); }; + var truncate = common.truncate = function (text, len) { if (typeof(text) === 'string' && text.length > len) { return text.slice(0, len) + '…'; @@ -851,7 +855,7 @@ define([ if (!$('#' + LOADING).is(':visible')) { common.addLoadingScreen(); } $('.spinnerContainer').hide(); if (transparent) { $('#' + LOADING).css('opacity', 0.8); } - $('#' + LOADING).find('p').html(error || Messages.error); + $('#' + LOADING).find('p').text(error || Messages.error); }; /* @@ -924,7 +928,7 @@ define([ switch (type) { case 'export': button = $('