Skip to content
This repository was archived by the owner on Dec 13, 2024. It is now read-only.

Commit

Permalink
feat: add security.txt
Browse files Browse the repository at this point in the history
  • Loading branch information
gaelreyrol committed Jan 28, 2020
1 parent edfff08 commit ebb71ac
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 4 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ This module is considered experimental and a work-in-progress.
- [x] X-Content-Type-Options
- [x] Referrer-Policy
- [x] Feature-Policy
- [ ] .well-known/security.txt
- [x] security.txt
- [ ] Documentation

[📖 **Release Notes**](./CHANGELOG.md)
Expand Down Expand Up @@ -50,7 +50,10 @@ yarn add @dansmaculotte/nuxt-security # or npm install @dansmaculotte/nuxt-secur
/* module options */
}
]
]
],

// Top level options
security: {}
}
```

Expand Down
25 changes: 23 additions & 2 deletions lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ const csp = require('helmet-csp')
const refererPolicy = require('referrer-policy')
const featurePolicy = require('feature-policy')

const logger = consola.withScope('nuxt:csp')
const securityFileGenerator = require('./securityFile/generator')
const securityFileFactory = require('./securityFile/factory')
const securityFileMiddleware = require('./securityFile/middleware')

const logger = consola.withScope('nuxt:security')

module.exports = function(moduleOptions) {
const defaults = {
Expand All @@ -13,11 +17,13 @@ module.exports = function(moduleOptions) {
csp: null,
referer: null,
features: null,
securityFile: null,
additionalHeaders: false
}

const options = {
...defaults,
...this.options['nuxt-csp'],
...this.options.security,
...moduleOptions
}

Expand Down Expand Up @@ -74,6 +80,21 @@ module.exports = function(moduleOptions) {
if (options.additionalHeaders) {
this.addServerMiddleware(configureAddtionnalHeaders())
}

if (options.securityFile) {
options.securityFile.wellKnowDir = '.well-known'
options.securityFile.fileName = 'security.txt'

const securityFileContents = securityFileFactory(options.securityFile)

this.nuxt.hook('generate:done', () => {
securityFileGenerator(options.securityFile, securityFileContents, this)
})

this.nuxt.hook('render:setupMiddleware', () => {
securityFileMiddleware(options.securityFile, securityFileContents, this)
})
}
}

module.exports.meta = require('../package.json')
48 changes: 48 additions & 0 deletions lib/securityFile/factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const generatedBy =
'# Generated by https://github.com/dansmaculotte/nuxt-security'

const contactOutput = contact => `Contact: ${contact}`
const encryptionOuput = url => `Encryption: ${url}`
const acknowledgmentOuput = url => `Acknowledgments: ${url}`
const languagesOutput = languages => `Preferred-Languages: ${languages}`
const canonicalOutput = url => `Canonical: ${url}`
const policyOuput = url => `Policy: ${url}`
const hiringOuput = url => `Hiring: ${url}`

const makeArray = arr => (Array.isArray(arr) ? arr : [arr])

module.exports = ({
contacts,
encryptions,
acknowledgments,
preferredLanguages,
canonical,
policies,
hirings
}) => {
const contents = [generatedBy]

makeArray(contacts).map(contact => contents.push(contactOutput(contact)))

makeArray(encryptions).map(encryption =>
contents.push(encryptionOuput(encryption))
)

makeArray(acknowledgments).map(acknowledgment =>
contents.push(acknowledgmentOuput(acknowledgment))
)

if (preferredLanguages && preferredLanguages.length) {
contents.push(languagesOutput(makeArray(preferredLanguages).join(',')))
}

if (canonical) {
contents.push(canonicalOutput(canonical))
}

makeArray(policies).map(policy => contents.push(policyOuput(policy)))

makeArray(hirings).map(hiring => contents.push(hiringOuput(hiring)))

return contents.join('\n')
}
21 changes: 21 additions & 0 deletions lib/securityFile/generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { resolve } = require('path')
const { writeFileSync, existsSync, mkdirSync } = require('fs')

module.exports = function(options, fileContents, nuxtInstance) {
const {
rootDir,
generate: { dir: generateDir }
} = nuxtInstance.options

// generate .well-know/security.txt in dist
nuxtInstance.nuxt.hook('generate:done', () => {
const generateDirPath = resolve(rootDir, generateDir, options.wellKnowDir)
const generateFilePath = resolve(generateDirPath, options.fileName)

if (!existsSync(generateDirPath)) {
mkdirSync(generateDirPath)
}

writeFileSync(generateFilePath, fileContents)
})
}
10 changes: 10 additions & 0 deletions lib/securityFile/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = function(options, fileContents, nuxtInstance) {
// render .well-know/security.txt via SSR
nuxtInstance.addServerMiddleware({
path: options.wellKnowDir + '/' + options.fileName,
handler(req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end(fileContents)
}
})
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"main": "lib/module.js",
"scripts": {
"dev": "nuxt test/fixture",
"dev:generate": "nuxt generate test/fixture",
"dev:build": "nuxt build test/fixture",
"lint": "eslint --ext .js,.vue lib test",
"release": "yarn test && standard-version && git push --follow-tags && npm publish",
"test": "yarn lint && jest"
Expand Down
12 changes: 12 additions & 0 deletions test/fixture/nuxt.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ module.exports = {
features: {
notifications: ["'none'"]
},
securityFile: {
contacts: [
'mailto:security@example.com',
'https://example.com/security'
],
canonical: 'https://example.com/.well-know/security.txt',
preferredLanguages: ['fr', 'en'],
encryptions: ['https://example.com/pgp-key.txt'],
acknowledgments: ['https://example.com/hall-of-fame.html'],
policies: ['https://example.com/policy.html'],
hirings: ['https://example.com/jobs.html']
},
additionalHeaders: true
}
}
Expand Down

0 comments on commit ebb71ac

Please sign in to comment.