pidgin/nest

ade72caa087d
Add extra way to report security vulnerability

- Add an extra way of reporting a security vulnerability in the project. This
is done by creating a new issue in our issue tracker and ensuring that the
visibility of it is set so that only Pidgin Developers can view it.
- Fix a simple mistake in markdown link syntax in the contributing page which
links back to the Security page.
- Change hardcoded link to list of advisories to a Hugo ref link (if we ever
change the location of the advisories page this will make Hugo throw an error
since it won't be able to find the page, otherwise the link would just end up
being broken without us necessarily knowing about it.

Testing Done:
Ran `dev-server.sh` and verified content looks as intended.

Reviewed at https://reviews.imfreedom.org/r/806/
/**
* For each content page:
* * reads last change date form mercurial
* * writes last edited date to front matter
*/
/*****************************************************************************
* Imports
*****************************************************************************/
const fs = require('fs').promises
const path = require('path')
const { spawn } = require('child_process')
const front = require('front-matter')
const yaml = require('js-yaml')
/*****************************************************************************
* Set Up
*****************************************************************************/
const mdRegex = /\.md$/
const hgLogArgs = ['log', '--follow', '--pager', 'never', '--color', 'never']
const yamlDumpSettings = { indent: 2 }
/*****************************************************************************
* Execution
*****************************************************************************/
getMdPaths(path.join(__dirname, '../hugo/content/')).then(paths =>
paths.map(async function (file) {
const [commits, { attributes, body }] = await Promise.all([
getCommits(file),
getFrontMatter(file),
])
const lastmod = commits[0].date
// If the md file has a date, use that. If it does not grab the date from
// the first commit.
const date = attributes.date ?? commits[commits.length - 1].date
const newFrontString = yaml
.dump({ ...attributes, lastmod, date }, yamlDumpSettings)
.trim()
const output = `---\n${newFrontString}\n---\n\n${body}`
await fs.writeFile(file, output)
console.log(`Updated: ${file}`)
})
)
/*****************************************************************************
* Mercurial
*****************************************************************************/
/**
* Get an ordered array commits for a file
* @param {string} file the file path
*/
function getCommits(file) {
return new Promise((resolve, reject) => {
const log = spawn('hg', [...hgLogArgs, file])
const commits = []
log.stdout.on('data', data => {
commits.push(
...data
.toString()
.split('\n\n')
.filter(Boolean)
.map(parseCommit)
.filter(filterCommits)
)
})
log.on('close', code => {
if (code !== 0) {
return reject(
new Error(`Unexpected return code from hg log ${code}`)
)
}
resolve(commits.sort((a, b) => a.date - b.date).reverse())
})
})
}
/**
* Converts the raw commit data from log into a a commit object
* @param {string} commitString raw commit from hg output
*/
function parseCommit(commitString) {
const commit = {}
commitString
.split('\n')
.filter(Boolean)
.forEach(line => {
const [, key, value] = /(\w+):\s+(.+)/.exec(line)
switch (key) {
case 'parent':
case 'bookmark':
if (!commit[key]) {
commit[key] = []
}
commit[key].push(value)
break
case 'date':
commit[key] = new Date(value)
break
default:
commit[key] = value
}
})
return commit
}
/**
* Filters out commits that contain an automation flag
* @param {commit}
*/
const filterCommits = ({ summary }) =>
!/^\[(Automated|Auto|Minor)\]/i.test(summary)
/*****************************************************************************
* Helpers
*****************************************************************************/
/**
* reads and parses a markdown file for frontmatter
* @param {string} file path to markdown file
*/
async function getFrontMatter(file) {
return fs.readFile(file, 'utf8').then(contents => front(contents))
}
/**
* creates a list of all markdown files in a directory
* @param {string} directory directory to search for markdown files
*/
async function getMdPaths(directory) {
let output = []
let items = (await fs.readdir(directory)).map(i => path.join(directory, i))
while (items.length) {
const item = items.pop()
const stat = await fs.stat(item)
if (stat.isDirectory()) {
items.push(...(await fs.readdir(item)).map(i =>
path.join(item, i)
))
} else if (stat.isFile() && mdRegex.test(item)) {
output.push(item)
}
}
return output.sort()
}