From f189d33f29bb0e196ecc6422905b208cee6e5e93 Mon Sep 17 00:00:00 2001 From: Richie Bendall Date: Tue, 14 Jan 2020 21:05:39 +1300 Subject: [PATCH] fix: Properly parse gravatar boolean and switch to standard linter. Signed-off-by: Richie Bendall --- middleware/cors.js | 8 ++-- middleware/load-options.js | 44 ++++++++++---------- middleware/load-user.js | 26 ++++++------ package.json | 14 +++---- routes/get.js | 70 +++++++++++++++---------------- routes/post.js | 83 ++++++++++++++++++++++--------------- routes/utils.js | 12 +++--- server.js | 62 +++++++++++++-------------- test.js | 66 ++++++++++++++--------------- users/volmering.json | 4 +- yarn.lock | Bin 188408 -> 175171 bytes 11 files changed, 201 insertions(+), 188 deletions(-) diff --git a/middleware/cors.js b/middleware/cors.js index 894bcf9f..58910309 100644 --- a/middleware/cors.js +++ b/middleware/cors.js @@ -1,8 +1,8 @@ module.exports = (_req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Origin', '*') res.header( 'Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept' - ); - next(); -}; + ) + next() +} diff --git a/middleware/load-options.js b/middleware/load-options.js index 5879c25e..a624b9f6 100644 --- a/middleware/load-options.js +++ b/middleware/load-options.js @@ -1,63 +1,63 @@ -const currentYear = new Date().getFullYear(); +const currentYear = new Date().getFullYear() module.exports = (req, res, next) => { - const parts = req.url.split('/'); + const parts = req.url.split('/') res.locals.options = parts.reduce( (acc, curr) => { - if (!curr) return acc; + if (!curr) return acc - let match = curr.match(/^@?(\d{4})$/) || []; + let match = curr.match(/^@?(\d{4})$/) || [] if (match.length) { // Pinned year if (curr.startsWith('@')) { - acc.pinnedYear = parseInt(curr.substr(1), 10); + acc.pinnedYear = parseInt(curr.substr(1), 10) } else { - acc.startYear = parseInt(curr, 10); + acc.startYear = parseInt(curr, 10) } - return acc; + return acc } - match = curr.match(/^(\d{4})-(\d{4})$/) || []; + match = curr.match(/^(\d{4})-(\d{4})$/) || [] if (match.length) { - acc.startYear = parseInt(match[1], 10); - acc.endYear = parseInt(match[2], 10); + acc.startYear = parseInt(match[1], 10) + acc.endYear = parseInt(match[2], 10) - return acc; + return acc } if (curr.startsWith('license')) { acc.format = curr .split('.') .pop() - .trim(); - return acc; + .trim() + return acc } if (curr.startsWith('+')) { - acc.license = curr.substr(1).toUpperCase(); - return acc; + acc.license = curr.substr(1).toUpperCase() + return acc } - acc.sha = curr; // not actually supported now - 2019-06-19 - return acc; + acc.sha = curr // not actually supported now - 2019-06-19 + return acc }, { format: 'html', startYear: null, endYear: currentYear, - sha: null, + sha: null } - ); + ) if (res.locals.options.sha) { res.setHeader( 'X-note', 'SHA and commit pinning is no longer supported, showing you latest release' - ); + ) } - next(); -}; + next() +} diff --git a/middleware/load-user.js b/middleware/load-user.js index eb4f9424..0f93d492 100644 --- a/middleware/load-user.js +++ b/middleware/load-user.js @@ -1,35 +1,35 @@ -const fs = require('fs-extra'); -const path = require('path'); +const fs = require('fs-extra') +const path = require('path') module.exports = async (req, res, next) => { - const id = req.hostname.split('.')[0]; - res.locals.id = id; + const id = req.hostname.split('.')[0] + res.locals.id = id if (req.method.toUpperCase() !== 'GET') { - return next(); + return next() } // Otherwise load up the user json file res.locals.user = { - copyright: '', - }; + copyright: '' + } try { const data = await fs.readFile( path.join(__dirname, '..', 'users', `${id}.json`), 'utf8' - ); - res.locals.user = { ...res.locals.user, ...JSON.parse(data) }; + ) + res.locals.user = { ...res.locals.user, ...JSON.parse(data) } } catch ({ code, message }) { if (code !== 'ENOENT') { res .code(500) .send( `An internal error occurred - open an issue on https://github.com/remy/mit-license with the following information: ${message}` - ); - return; + ) + return } } - next(); -}; + next() +} diff --git a/package.json b/package.json index c51cd738..101c86ca 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "dev": "nodemon .", "serve": "node server.js", "test": "node test.js", - "lint": "eslint server.js middleware/*.js routes/*.js --color" + "lint": "standard" }, "bugs": { "url": "https://github.com/remy/mit-license/issues" @@ -41,17 +41,15 @@ "postcss-middleware": "^1.1.4", "postcss-preset-env": "^6.7.0", "serve-favicon": "^2.5.0", - "temp-dir": "^2.0.0" + "temp-dir": "^2.0.0", + "yn": "^4.0.0" }, "devDependencies": { - "@remy/eslint": "^3.2.2", - "babel-eslint": "^10.0.3", "css": "^2.2.4", - "eslint": "^6.8.0", - "eslint-plugin-node": "^11.0.0", "has-flag": "^4.0.0", - "husky": "^3.1.0", - "nodemon": "^2.0.2" + "husky": "^4.0.7", + "nodemon": "^2.0.2", + "standard": "^14.3.1" }, "resolutions": { "postcss-middleware/vinyl-fs/glob-stream/micromatch/braces": "^3.0.2" diff --git a/routes/get.js b/routes/get.js index ffd2e3ff..15d0f937 100644 --- a/routes/get.js +++ b/routes/get.js @@ -1,48 +1,48 @@ -const md5 = require('md5'); -const path = require('path'); -const { stripTags, escapeTags, unescapeTags } = require('./utils'); -const _ = require('lodash'); +const md5 = require('md5') +const path = require('path') +const { stripTags, escapeTags, unescapeTags } = require('./utils') +const _ = require('lodash') -function getCopyrightHTML(user, plain) { - let html = ''; +function getCopyrightHTML (user, plain) { + let html = '' const name = _.isString(user) ? user : plain ? user.name || user.copyright - : escapeTags(user.name || user.copyright); + : escapeTags(user.name || user.copyright) if (user.url) { - html = `${name}`; + html = `${name}` } else { - html = name; + html = name } if (user.email) { html += ` <${ plain ? user.email : escapeTags(user.email) - }>`; + }>` } - return html; + return html } module.exports = (req, res) => { - const { user, options } = res.locals; - let name; - let gravatar; + const { user, options } = res.locals + let name + let gravatar // No error and valid if (user.copyright) { if (_.isString(user.copyright)) { - name = getCopyrightHTML(user, options.format !== 'html'); + name = getCopyrightHTML(user, options.format !== 'html') } else if (_.isArray(user.copyright) && user.copyright.every(val => _.isString(val))) { // Supports: ['Remy Sharp', 'Richie Bendall'] name = user.copyright .map(v => (options.format !== 'html' ? v : escapeTags(v))) - .join(', '); + .join(', ') } else { - name = user.copyright.map(getCopyrightHTML).join(', '); + name = user.copyright.map(getCopyrightHTML).join(', ') } } @@ -50,47 +50,47 @@ module.exports = (req, res) => { // Supports regular format gravatar = `Profile image`; + )}" />` } else if (_.isObject(user.copyright[0]) && user.gravatar) { // Supports multi-user format gravatar = `Profile image`; + )}" />` } const year = options.pinnedYear ? options.pinnedYear - : [options.startYear, options.endYear].filter(Boolean).join('-'); - const license = (options.license || user.license || 'MIT').toUpperCase(); - const format = options.format || user.format || 'html'; + : [options.startYear, options.endYear].filter(Boolean).join('-') + const license = (options.license || user.license || 'MIT').toUpperCase() + const format = options.format || user.format || 'html' const args = { info: `${year} ${name}`, theme: user.theme || 'default', - gravatar, - }; + gravatar + } - const filename = path.join(__dirname, '..', 'licenses', license); + const filename = path.join(__dirname, '..', 'licenses', license) req.app.render(filename, args, (error, content) => { if (error) { - res.status(500).send(error); - return; + res.status(500).send(error) + return } if (format === 'txt') { - const plain = content.match(/
(.*)<\/article>/ms)[1]; + const plain = content.match(/
(.*)<\/article>/ms)[1] res .set('Content-Type', 'text/plain; charset=UTF-8') - .send(unescapeTags(stripTags(plain)).trim()); - return; + .send(unescapeTags(stripTags(plain)).trim()) + return } if (format === 'html') { - res.send(content); - return; + res.send(content) + return } - res.json({ ...user, ...options }); - }); -}; + res.json({ ...user, ...options }) + }) +} diff --git a/routes/post.js b/routes/post.js index dae92717..a9dbf528 100644 --- a/routes/post.js +++ b/routes/post.js @@ -1,42 +1,44 @@ -const fs = require('fs-extra'); -const path = require('path'); -const btoa = require('btoa'); -const { version } = require(path.join(__dirname, '..', 'package.json')); -const _ = require('lodash'); +const fs = require('fs-extra') +const path = require('path') +const btoa = require('btoa') +const { version } = require(path.join(__dirname, '..', 'package.json')) +const _ = require('lodash') const github = require('@octokit/rest')({ // GitHub personal access token auth: process.env.github_token, // User agent with version from package.json - userAgent: `mit-license v${version}`, -}); -const { validDomainId } = require('./utils'); + userAgent: `mit-license v${version}` +}) +const yn = require('yn') -function getUserData({ query, body }) { +const { validDomainId } = require('./utils') + +function getUserData ({ query, body }) { // If query parameters provided - if (_.size(query) > 0) return query; + if (_.size(query) > 0) return query // If the data parsed as {'{data: "value"}': ''} - if (_.size(body) === 1 && !_.first(_.values(body))) return JSON.parse(_.first(_.keys(body))); + if (_.size(body) === 1 && !_.first(_.values(body))) return JSON.parse(_.first(_.keys(body))) // Fallback - return body; + return body } // HTTP POST API module.exports = async (req, res) => { - const { hostname } = req; + const { hostname } = req // Get different parts of hostname (example: remy.mit-license.org -> ['remy', 'mit-license', 'org']) - const params = hostname.split('.'); + const params = hostname.split('.') // This includes the copyright, year, etc. - const userData = getUserData(req); + const userData = getUserData(req) // If there isn't enough part of the hostname if (params.length < 2) { - res.status(400).send('Please specify a subdomain in the URL.'); - return; + res.status(400).send('Please specify a subdomain in the URL.') + return } // Extract the name from the URL - const id = _.first(params); + const id = _.first(params) if (!validDomainId(id)) { // Return a vague error intentionally @@ -44,31 +46,44 @@ module.exports = async (req, res) => { .status(400) .send( 'User already exists - to update values, please send a pull request on https://github.com/remy/mit-license' - ); + ) - return; + return } // Check if the user file exists in the users directory - const exists = await fs.pathExists(path.join(__dirname, '..', 'users', `${id}.json`)); + const exists = await fs.pathExists(path.join(__dirname, '..', 'users', `${id}.json`)) if (exists) { res .status(409) .send( 'User already exists - to update values, please send a pull request on https://github.com/remy/mit-license' - ); - return; + ) + return + } + + if (userData.gravatar) { + // Parse the string version of a boolean or similar + userData.gravatar = yn(userData.gravatar, { lenient: true }) + if (_.isUndefined(userData.gravatar)) { + res + .status(400) + .send( + 'The "gravatar" JSON property must be a boolean.' + ) + return + } } // File doesn't exist // If copyright property and key doesn't exist if (!userData.copyright) { - res.status(400).send('JSON requires "copyright" property and value'); - return; + res.status(400).send('JSON requires "copyright" property and value') + return } try { - const fileContent = JSON.stringify(userData, 0, 2); + const fileContent = JSON.stringify(userData, 0, 2) await github.repos.createOrUpdateFile({ owner: 'remy', @@ -78,18 +93,18 @@ module.exports = async (req, res) => { content: btoa(fileContent), committer: { name: 'MIT License Bot', - email: 'remy@leftlogic.com', - }, - }); + email: 'remy@leftlogic.com' + } + }) - await fs.writeFile(path.join(__dirname, '..', 'users', `${id}.json`), fileContent); + await fs.writeFile(path.join(__dirname, '..', 'users', `${id}.json`), fileContent) - res.status(201).send(`MIT license page created: https://${hostname}`); + res.status(201).send(`MIT license page created: https://${hostname}`) } catch (err) { res .status(500) .send( - `Unable to create new user - please send a pull request on https://github.com/remy/mit-license` - ); + 'Unable to create new user - please send a pull request on https://github.com/remy/mit-license' + ) } -}; +} diff --git a/routes/utils.js b/routes/utils.js index 72ae9d02..869c28af 100644 --- a/routes/utils.js +++ b/routes/utils.js @@ -1,16 +1,16 @@ -const _ = require('lodash'); +const _ = require('lodash') const tags = { '<': '<', '>': '>', - '&': '&', -}; -const untags = _.invert(tags); + '&': '&' +} +const untags = _.invert(tags) module.exports = { escapeTags: str => (str || '').replace(/[<>&]/g, m => tags[m]), unescapeTags: str => (str || '').replace(/(<|>|&)/g, m => untags[m]), stripTags: str => (str || '').replace(/<(?:.|\n)*?>/gm, ''), - validDomainId: str => /^[\w-_]+$/.test(str), -}; + validDomainId: str => /^[\w-_]+$/.test(str) +} diff --git a/server.js b/server.js index 8f4c5c1d..6df2a972 100644 --- a/server.js +++ b/server.js @@ -5,68 +5,68 @@ IMPORTANT: Set the `github_token` environment variable to a personal access to Server port: The `PORT` environment variable can also be set to control the port the server should be hosted on. */ -const express = require('express'); -const minify = require('express-minify'); -const favicon = require('serve-favicon'); -const postcssMiddleware = require('postcss-middleware'); -const tmpdir = require('temp-dir'); -const path = require('path'); +const express = require('express') +const minify = require('express-minify') +const favicon = require('serve-favicon') +const postcssMiddleware = require('postcss-middleware') +const tmpdir = require('temp-dir') +const path = require('path') // Server -var PORT = process.env.PORT || 8080; +var PORT = process.env.PORT || 8080 // Prepare application -const app = express(); +const app = express() app.use( minify({ - cache: tmpdir, + cache: tmpdir }) -); -app.use(favicon(path.join(__dirname, 'favicon.ico'))); -app.set('views', path.join(__dirname, '/licenses')); -app.set('view engine', 'ejs'); +) +app.use(favicon(path.join(__dirname, 'favicon.ico'))) +app.set('views', path.join(__dirname, '/licenses')) +app.set('view engine', 'ejs') // Setup static files -app.use('/robots.txt', express.static('robots.txt')); -app.use('/favicon.ico', express.static(`${__dirname}/favicon.ico`)); +app.use('/robots.txt', express.static('robots.txt')) +app.use('/favicon.ico', express.static(`${__dirname}/favicon.ico`)) app.use( '/themes', postcssMiddleware({ plugins: [ require('postcss-preset-env')({ overrideBrowserslist: '>= 0%', - stage: 0, - }), + stage: 0 + }) ], - src(req) { - return path.join(__dirname, 'themes', req.path); - }, + src (req) { + return path.join(__dirname, 'themes', req.path) + } }), express.static('themes') -); +) // Middleware // CORS -app.use(require('./middleware/cors')); +app.use(require('./middleware/cors')) // Parse URL-encoded bodies (as sent by HTML forms) app.use( express.urlencoded({ - extended: true, + extended: true }) -); +) // Parse JSON bodies (as sent by API clients) -app.use(express.json()); +app.use(express.json()) // Capture the id from the subdomain -app.use(require('./middleware/load-user')); -app.use(require('./middleware/load-options')); +app.use(require('./middleware/load-user')) +app.use(require('./middleware/load-options')) // HTTP POST API -app.post('/', require('./routes/post')); -app.get('/*', require('./routes/get')); +app.post('/', require('./routes/post')) +app.get('/*', require('./routes/get')) // Start listening for HTTP requests app.listen(PORT, () => { - console.log(`🚀 on http://localhost:${PORT}`); -}); + console.log(`🚀 on http://localhost:${PORT}`) +}) diff --git a/test.js b/test.js index a741e2ec..9593a3c2 100644 --- a/test.js +++ b/test.js @@ -1,68 +1,68 @@ -const path = require('path'); -const fs = require('fs-extra'); -const CSS = require('css'); -const { validDomainId } = require('./routes/utils'); -const hasFlag = require('has-flag'); +const path = require('path') +const fs = require('fs-extra') +const CSS = require('css') +const { validDomainId } = require('./routes/utils') +const hasFlag = require('has-flag') -function report(content, fix) { - console.error(content); - if (fix && hasFlag('--fix')) fix(); - process.exitCode = 1; +function report (content, fix) { + console.error(content) + if (fix && hasFlag('--fix')) fix() + process.exitCode = 1 } (async () => { - const users = await fs.readdir('users'); + const users = await fs.readdir('users') users.forEach(async user => { if (!user.endsWith('json')) { report(`${user} is not a json file`, () => fs.unlink(path.join('users', user), () => { }) - ); + ) } if (!validDomainId(user.replace('.json', ''))) { - report(`${user} is not a valid domain id.`); + report(`${user} is not a valid domain id.`) } try { - const data = await fs.readFile(path.join('users', user), 'utf8'); + const data = await fs.readFile(path.join('users', user), 'utf8') try { - const u = JSON.parse(data); + const u = JSON.parse(data) if (!u.locked && !u.copyright) { - report(`Copyright not specified in ${user}`); + report(`Copyright not specified in ${user}`) } if (u.version) { report(`Version tag found in ${user}`, () => { - delete u.version; - const stringified = `${JSON.stringify(u, 0, 2)}\n`; - fs.writeFile(path.join('users', user), stringified, () => { }); - }); + delete u.version + const stringified = `${JSON.stringify(u, 0, 2)}\n` + fs.writeFile(path.join('users', user), stringified, () => { }) + }) } if (typeof u.gravatar === 'string') { report(`Gravatar boolean encoded as string found in ${user}`, () => { - u.gravatar = u.gravatar === 'true'; - const stringified = `${JSON.stringify(u, 0, 2)}\n`; - fs.writeFile(path.join('users', user), stringified, () => { }); - }); + u.gravatar = u.gravatar === 'true' + const stringified = `${JSON.stringify(u, 0, 2)}\n` + fs.writeFile(path.join('users', user), stringified, () => { }) + }) } } catch ({ message }) { - report(`Invalid JSON in ${user} (${message})`); + report(`Invalid JSON in ${user} (${message})`) } } catch ({ message }) { - report(`Unable to read ${user} (${message})`); + report(`Unable to read ${user} (${message})`) } - }); + }) - const themes = await fs.readdir('themes'); + const themes = await fs.readdir('themes') await themes.forEach(async theme => { if (theme.endsWith('css')) { try { - const data = await fs.readFile(path.join('themes', theme), 'utf8'); + const data = await fs.readFile(path.join('themes', theme), 'utf8') try { - CSS.parse(data); + CSS.parse(data) } catch ({ message }) { - report(`Invalid CSS in ${theme} (${message})`); + report(`Invalid CSS in ${theme} (${message})`) } } catch ({ message }) { - report(`Unable to read ${theme} (${message})`); + report(`Unable to read ${theme} (${message})`) } } - }); -})(); + }) +})() diff --git a/users/volmering.json b/users/volmering.json index bca4a555..147fdc15 100644 --- a/users/volmering.json +++ b/users/volmering.json @@ -4,5 +4,5 @@ "format": "text", "license": "mit", "theme": "default", - "gravatar": "false" -} \ No newline at end of file + "gravatar": false +} diff --git a/yarn.lock b/yarn.lock index 43984aa33732e0947eea672c73996a739518c2c5..46bd5c3cdd7dc10707c6c13a5a9e40be0c9e5d6e 100644 GIT binary patch delta 10550 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 a?&!z9GtOQ)`kD8|KXdWO`at}%vHu68)YQoU