diff --git a/README.md b/README.md index c68d6cb..6abe930 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ There are some config settings you need to change in the files below. | `CMD_LDAP_USERNAMEFIELD` | Fallback to userid | The LDAP field which is used as the username on CodiMD | | `CMD_LDAP_TLS_CA` | `server-cert.pem, root.pem` | Root CA for LDAP TLS in PEM format (use comma to separate) | | `CMD_LDAP_PROVIDERNAME` | `My institution` | Optional name to be displayed at login form indicating the LDAP provider | -| `CMD_SAML_IDPSSOURL` | `https://idp.example.com/sso` | authentication endpoint of IdP. for details, see [guide](docs/guides/auth.md#saml-onelogin). | +| `CMD_SAML_IDPSSOURL` | `https://idp.example.com/sso` | authentication endpoint of IdP. for details, see [guide](docs/guides/auth/saml-onelogin.md). | | `CMD_SAML_IDPCERT` | `/path/to/cert.pem` | certificate file path of IdP in PEM format | | `CMD_SAML_ISSUER` | no example | identity of the service provider (optional, default: serverurl)" | | `CMD_SAML_IDENTIFIERFORMAT` | no example | name identifier format (optional, default: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`) | diff --git a/app.json b/app.json index 6a6142e..816540d 100644 --- a/app.json +++ b/app.json @@ -6,7 +6,7 @@ "Markdown", "Notes" ], - "website": "https://hackmd.io", + "website": "https://codimd.org", "repository": "https://github.com/hackmdio/codimd", "logo": "https://github.com/hackmdio/codimd/raw/master/public/codimd-icon-1024.png", "success_url": "/", diff --git a/bin/manage_users b/bin/manage_users index 8c35620..f744150 100755 --- a/bin/manage_users +++ b/bin/manage_users @@ -2,7 +2,7 @@ // First configure the logger so it does not spam the console const logger = require("../lib/logger"); -logger.transports.console.level = "warning"; +logger.transports.forEach((transport) => transport.level = "warning") const models = require("../lib/models/"); const readline = require("readline-sync"); diff --git a/docs/guides/auth/saml.md b/docs/guides/auth/saml.md index 97ac9d3..7f63b74 100644 --- a/docs/guides/auth/saml.md +++ b/docs/guides/auth/saml.md @@ -5,9 +5,9 @@ Authentication guide - SAML The basic procedure is the same as the case of OneLogin which is mentioned in [OneLogin-Guide](./saml-onelogin.md). If you want to match your IdP, you can use more configurations as below. -* If your IdP accepts metadata XML of the service provider to ease configuraion, use this url to download metadata XML. +* If your IdP accepts metadata XML of the service provider to ease configuration, use this url to download metadata XML. * {{your-serverurl}}/auth/saml/metadata - * _Note: If not accessable from IdP, download to local once and upload to IdP._ + * _Note: If not accessible from IdP, download to local once and upload to IdP._ * Change the value of `issuer`, `identifierFormat` to match your IdP. * `issuer`: A unique id to identify the application to the IdP, which is the base URL of your HackMD as default * `identifierFormat`: A format of unique id to identify the user of IdP, which is the format based on email address as default. It is recommend that you use as below. @@ -59,7 +59,7 @@ The basic procedure is the same as the case of OneLogin which is mentioned in [O HMD_SAML_ATTRIBUTE_EMAIL=mail ```` -* If you want to controll permission by group membership, add group attribute name and required group (allowed) or external group (not allowed). +* If you want to control permission by group membership, add group attribute name and required group (allowed) or external group (not allowed). * `groupAttribute`: An attribute name of group membership * `requiredGroups`: Group names array for allowed access to HackMD. Use vertical bar to separate for environment variables. * `externalGroups`: Group names array for not allowed access to HackMD. Use vertical bar to separate for environment variables. diff --git a/lib/config/default.js b/lib/config/default.js index 5a7ae0a..9e401f3 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -151,5 +151,5 @@ module.exports = { allowEmailRegister: true, allowGravatar: true, allowPDFExport: true, - openID: true + openID: false } diff --git a/lib/letter-avatars.js b/lib/letter-avatars.js index 55cf9c3..a5dd820 100644 --- a/lib/letter-avatars.js +++ b/lib/letter-avatars.js @@ -1,6 +1,6 @@ 'use strict' // external modules -const md5 = require('blueimp-md5') +const crypto = require('crypto') const randomcolor = require('randomcolor') const config = require('./config') @@ -30,16 +30,21 @@ exports.generateAvatarURL = function (name, email = '', big = true) { if (typeof email !== 'string') { email = '' + name + '@example.com' } + name=encodeURIComponent(name) + + let hash = crypto.createHash('md5') + hash.update(email.toLowerCase()) + let hexDigest = hash.digest('hex') if (email !== '' && config.allowGravatar) { - photo = 'https://www.gravatar.com/avatar/' + md5(email.toLowerCase()) + photo = 'https://www.gravatar.com/avatar/' + hexDigest; if (big) { photo += '?s=400' } else { photo += '?s=96' } } else { - photo = config.serverURL + '/user/' + (name || email.substring(0, email.lastIndexOf('@')) || md5(email.toLowerCase())) + '/avatar.svg' + photo = config.serverURL + '/user/' + (name || email.substring(0, email.lastIndexOf('@')) || hexDigest) + '/avatar.svg' } return photo } diff --git a/lib/response.js b/lib/response.js index b94f473..8191e74 100644 --- a/lib/response.js +++ b/lib/response.js @@ -320,7 +320,7 @@ function actionPDF (req, res, note) { res.setHeader('Content-Type', 'application/pdf; charset=UTF-8') res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling stream.pipe(res) - fs.unlink(path) + fs.unlinkSync(path) }) } diff --git a/locales/ja.json b/locales/ja.json index 80623c8..7358af4 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -111,5 +111,7 @@ "Do you really want to delete your user account?": "本当にアカウントを削除しますか?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "この操作はあなたのアカウントとあなたの所有するすべてのノートを削除し、さらに他の人のノートからあなたのアカウントへの参照を除去します。", "Delete user": "ユーザーの削除", - "Export user data": "ユーザーデータをエクスポート" + "Export user data": "ユーザーデータをエクスポート", + "Help us translating on %s": "%s の翻訳にご協力ください", + "Source Code": "ソースコード" } \ No newline at end of file diff --git a/package.json b/package.json index cf1e706..f9f3f0e 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "CodiMD", - "version": "1.2.1", + "version": "1.3.0", "description": "Realtime collaborative markdown notes on all platforms.", "main": "app.js", "license": "AGPL-3.0", "scripts": { - "test": "npm run-script eslint && npm run-script jsonlint", - "eslint": "node_modules/.bin/eslint lib public app.js", + "test": "npm run-script eslint && npm run-script jsonlint && mocha", + "eslint": "node_modules/.bin/eslint lib public test app.js", "jsonlint": "find . -not -path './node_modules/*' -type f -name '*.json' -o -type f -name '*.json.example' | while read json; do echo $json ; jq . $json; done", "standard": "echo 'standard is no longer being used, use `npm run eslint` instead!' && exit 1", "dev": "webpack --config webpack.dev.js --progress --colors --watch", @@ -23,9 +23,8 @@ "aws-sdk": "^2.345.0", "azure-storage": "^2.7.0", "base64url": "^3.0.0", - "blueimp-md5": "^2.6.0", "body-parser": "^1.15.2", - "bootstrap": "^3.3.7", + "bootstrap": "^3.4.0", "bootstrap-validator": "^0.11.8", "chance": "^1.0.4", "cheerio": "^0.22.0", @@ -47,7 +46,7 @@ "formidable": "^1.0.17", "gist-embed": "~2.6.0", "graceful-fs": "^4.1.11", - "handlebars": "^4.0.6", + "handlebars": "^4.0.13", "helmet": "^3.13.0", "highlight.js": "~9.12.0", "i18n": "^0.8.3", @@ -98,7 +97,7 @@ "passport-ldapauth": "^2.0.0", "passport-local": "^1.0.0", "passport-oauth2": "^1.4.0", - "passport-saml": "^0.35.0", + "passport-saml": "^1.0.0", "passport-twitter": "^1.0.4", "passport.socketio": "^3.7.0", "pdfobject": "^2.0.201604172", @@ -133,6 +132,11 @@ "ws": "^6.0.0", "xss": "^1.0.3" }, + "resolutions": { + "**/tough-cookie": "~2.4.0", + "**/minimatch": "^3.0.2", + "**/request": "^2.88.0" + }, "engines": { "node": ">=6.x" }, @@ -184,6 +188,8 @@ "less": "^2.7.1", "less-loader": "^4.1.0", "mini-css-extract-plugin": "^0.4.1", + "mocha": "^5.2.0", + "mock-require": "^3.0.3", "optimize-css-assets-webpack-plugin": "^5.0.0", "script-loader": "^0.7.2", "string-loader": "^0.0.1", diff --git a/public/docs/features.md b/public/docs/features.md index a4ffb63..e7ce89c 100644 --- a/public/docs/features.md +++ b/public/docs/features.md @@ -222,9 +222,6 @@ When you’re a carpenter making a beautiful chest of drawers, you’re not goin ### SlideShare {%slideshare briansolis/26-disruptive-technology-trends-2016-2018-56796196 %} -### Speakerdeck -{%speakerdeck sugarenia/xxlcss-how-to-scale-css-and-keep-your-sanity %} - ### PDF **Caution: this might be blocked by your browser if not using an `https` URL.** {%pdf https://papers.nips.cc/paper/5346-sequence-to-sequence-learning-with-neural-networks.pdf %} diff --git a/public/docs/release-notes.md b/public/docs/release-notes.md index c775b70..5c47e54 100644 --- a/public/docs/release-notes.md +++ b/public/docs/release-notes.md @@ -1,6 +1,101 @@ Release Notes === + 1.3.0 2019-03-03 00:00 +--- + +### Enhancements +* Run db migrations on `npm start` +* Add documentation about integration with AD LDAP +* Add `rel="noopener"` to all links +* Add documentation about integration with Nextcloud for authentication +* Update URL on frontpage to point to codimd.org +* Replace Fontawesome with Forkawesome +* Add OpenID support +* Add print icon to slide view +* Add auto-complete for language names that are highlighted in codeblocks +* Improve translations for Chinese, Dutch, French, German, Italien, Korean, Polish, and Russian language +* Add Download action to published document API +* Add reset password feature to `manage_users` script +* Move from own `./tmp` directory to system temp directory +* Add Etherpad migration guide +* Move XSS library to a more native position +* Use full version string to determine changes from the backend +* Update winston (logging library) +* Use slide preview in slide example +* Improve migration handling +* Update reveal.js to version 3.7.0 +* Replace scrypt library with its successor +* Replace `to-markdown` with `turndown` (successor library) +* Update socket.io +* Add warning on missing base URL +* Update bootstrap to version 3.4.0 +* Update handlebar + +### Fixes +* Fix paths in GitLab documentation +* Fix missing `data:` URL in CSP +* Fix oAuth2 name/label field +* Fix GitLab API integration +* Fix auto-completed but not rendered emojis +* Fix menu organization depending on enabled services +* Fix some logging in the OT module +* Fix some unhandled internalOAuthError exception +* Fix unwanted creation of robots.txt document in "freeurl-mode" +* Fix some links on index page to lead to the right sections on feature page +* Fix document breaking, empty headlines +* Fix wrong multiplication for HSTS header seconds +* Fix wrong subdirectories in exported user data +* Fix CSP for speaker notes +* Fix CSP for disqus +* Fix URL API usage +* Fix Gist embedding +* Fix upload provider error message +* Fix unescaped disqus user names +* Fix SAML vulnerability +* Fix link to SAML guide +* Fix deep dependency problem with node 6.x +* Fix broken PDF export by wrong unlink call +* Fix possible XSS attack in MathJax + +### Refactors +* Refactor to use `ws` instead of the the no longer supported `uws` +* Refactor frontend build system to use webpack version 4 +* Refactor file path configuration (views, uploads, …) +* Refactor `manage_users` script +* Refactor handling of template variables +* Refactor linting to use eslint + +### Removes +* Remove no longer working Octicons +* Remove links to our old Gitter channel +* Remove unused library node-uuid +* Remove unneeded blueimp-md5 dependency +* Remove speakerdeck due to broken implementation + +### Contributors +* Adam.emts (translator) +* [Alex Garcia](https://github.com/asg017) +* [Cédric Couralet (micedre)](https://github.com/micedre) +* [Claudius Coenen](https://github.com/ccoenen) +* [Daan Sprenkels](https://github.com/dsprenkels) +* [David Mehren](https://github.com/davidmehren) +* [Erona](https://github.com/Eronana) +* [Felix Yan](https://github.com/felixonmars) +* [Jonathan](https://github.com/phrix32) +* Jong-kai Yang (translator) +* [MartB](https://github.com/MartB) +* [Max Wu (jackycute)](https://github.com/jackycute) +* [mcnesium](https://github.com/mcnesium) +* Nullnine (translator) +* RanoIP (translator) +* [SuNbiT](https://github.com/sunbit) +* Sylke Vicious (translator) +* Timothee (translator) +* [WilliButz](https://github.com/WilliButz) +* [Xaver Maierhofer](https://github.com/xf-) +* [云屿](https://github.com/cloudyu) + 1.2.1 2018-09-26 00:00 --- diff --git a/public/js/extra.js b/public/js/extra.js index 76e9563..f8e0eb2 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -459,34 +459,13 @@ export function finishView (view) { // speakerdeck view.find('div.speakerdeck.raw').removeClass('raw') .each((key, value) => { - const url = `https://speakerdeck.com/oembed.json?url=https%3A%2F%2Fspeakerdeck.com%2F${encodeURIComponent($(value).attr('data-speakerdeckid'))}` - // use yql because speakerdeck not support jsonp - $.ajax({ - url: 'https://query.yahooapis.com/v1/public/yql', - data: { - q: `select * from json where url ='${url}'`, - format: 'json' - }, - dataType: 'jsonp', - success (data) { - if (!data.query || !data.query.results) return - const json = data.query.results.json - const html = json.html - var ratio = json.height / json.width - $(value).html(html) - const iframe = $(value).children('iframe') - const src = iframe.attr('src') - if (src.indexOf('//') === 0) { iframe.attr('src', `https:${src}`) } - const inner = $('
').append(iframe) - const height = iframe.attr('height') - const width = iframe.attr('width') - ratio = (height / width) * 100 - inner.css('padding-bottom', `${ratio}%`) - $(value).html(inner) - if (window.viewAjaxCallback) window.viewAjaxCallback() - } - }) - }) + const url = `https://speakerdeck.com/${$(value).attr('data-speakerdeckid')}` + const inner = $('Speakerdeck') + inner.attr('href', url) + inner.attr('rel', 'noopener noreferrer') + inner.attr('target', '_blank') + $(value).append(inner) + }) // pdf view.find('div.pdf.raw').removeClass('raw') .each(function (key, value) { diff --git a/public/js/index.js b/public/js/index.js index a845b5d..76f952c 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1,7 +1,6 @@ /* eslint-env browser, jquery */ -/* global CodeMirror, Cookies, moment, editor, ui, Spinner, - modeType, Idle, serverurl, key, gapi, Dropbox, FilePicker - ot, MediaUploader, hex2rgb, num_loaded, Visibility */ +/* global CodeMirror, Cookies, moment, Spinner, Idle, serverurl, + key, Dropbox, ot, hex2rgb, Visibility */ require('../vendor/showup/showup') diff --git a/public/js/render.js b/public/js/render.js index ff5e2bf..87e5cfd 100644 --- a/public/js/render.js +++ b/public/js/render.js @@ -45,7 +45,7 @@ var filterXSSOptions = { // allow comment tag if (tag === '!--') { // do not filter its attributes - return html + return html.replace(/<(?!!--)/g, '<').replace(/-->/g, '__HTML_COMMENT_END__').replace(/>/g, '>').replace(/__HTML_COMMENT_END__/g, '-->') } }, onTagAttr: function (tag, name, value, isWhiteAttr) { diff --git a/public/views/codimd/foot.ejs b/public/views/codimd/foot.ejs index 3a7fefd..d054279 100644 --- a/public/views/codimd/foot.ejs +++ b/public/views/codimd/foot.ejs @@ -3,11 +3,12 @@ - + + @@ -23,5 +24,6 @@ <% } else { %> + <%- include ../build/index-pack-scripts %> <% } %> diff --git a/public/views/codimd/head.ejs b/public/views/codimd/head.ejs index afa89d1..e3edde6 100644 --- a/public/views/codimd/head.ejs +++ b/public/views/codimd/head.ejs @@ -8,7 +8,7 @@ <% if(useCDN) { %> - + diff --git a/public/views/html.hbs b/public/views/html.hbs index 490d31a..42710d6 100644 --- a/public/views/html.hbs +++ b/public/views/html.hbs @@ -15,7 +15,7 @@ - + @@ -49,7 +49,7 @@ {{{ui-toc-affix}}} - + - + diff --git a/public/views/index/head.ejs b/public/views/index/head.ejs index 6c89885..ce1eb07 100644 --- a/public/views/index/head.ejs +++ b/public/views/index/head.ejs @@ -10,7 +10,7 @@ <% if(useCDN) { %> - + diff --git a/public/views/pretty.ejs b/public/views/pretty.ejs index ff4dcc2..12561a3 100644 --- a/public/views/pretty.ejs +++ b/public/views/pretty.ejs @@ -18,7 +18,7 @@ <% if(useCDN) { %> - + @@ -75,11 +75,12 @@ <% if(useCDN) { %> - + + @@ -92,6 +93,7 @@ <% } else { %> + <%- include build/pretty-pack-scripts %> <% } %> <%- include shared/ga %> diff --git a/public/views/shared/disqus.ejs b/public/views/shared/disqus.ejs index 840d1e3..2311d3f 100644 --- a/public/views/shared/disqus.ejs +++ b/public/views/shared/disqus.ejs @@ -5,7 +5,7 @@ var disqus_config = function () { }; (function() { var d = document, s = d.createElement('script'); - s.src = 'https://<%= disqus %>.disqus.com/embed.js'; + s.src = 'https://<%= disqus.replace(/[^A-Za-z0-9]+/g, '') %>.disqus.com/embed.js'; s.setAttribute('data-timestamp', +new Date()); (d.head || d.body).appendChild(s); })(); diff --git a/public/views/slide.ejs b/public/views/slide.ejs index bbdf589..a92e1da 100644 --- a/public/views/slide.ejs +++ b/public/views/slide.ejs @@ -96,6 +96,7 @@ + @@ -108,6 +109,7 @@ <% } else { %> + <%- include build/slide-pack-scripts %> <% } %>