express-csp/test/index.test.js

128 lines
4.0 KiB
JavaScript

const path = require('path')
const request = require('supertest')
const express = require('express')
const fs = require('fs')
// Import the middleware factory (don't name this `csp` to avoid shadowing!)
const createCspMiddleware = require('../index.cjs')
describe('Content Security Policy middleware', () => {
const validPolicyPath = path.join(__dirname, '../csp-policy.yml')
const malformedPolicyPath = path.join(__dirname, 'bad-policy.yml')
const missingPolicyPath = path.join(__dirname, 'xxxxxx.yml')
const minimalPolicyPath = path.join(__dirname, 'minimal-policy.yml')
const customPolicyPath = path.join(__dirname, 'custom-policy.yml')
beforeAll(() => {
// Write a malformed YAML file (missing colon, bad list syntax)
fs.writeFileSync(malformedPolicyPath, `default-src 'self'\nthis-is: [bad yaml]`)
// Write a minimal custom policy
fs.writeFileSync(
minimalPolicyPath,
`
contentSecurityPolicy: false
`,
)
// Write a simple custom policy
fs.writeFileSync(
customPolicyPath,
`
contentSecurityPolicy:
directives:
default-src: ["'self'"]
script-src: ["'self'", "https://cdn.example.com"]
`,
)
})
afterAll(() => {
//fs.unlinkSync(missingPolicyPath)
fs.unlinkSync(minimalPolicyPath)
fs.unlinkSync(malformedPolicyPath)
fs.unlinkSync(customPolicyPath)
})
it('should load and apply CSP directives from YAML', async () => {
const app = express()
const csp = createCspMiddleware(validPolicyPath)
app.use(csp)
app.get('/', (req, res) => res.send('Hello World'))
const res = await request(app).get('/')
console.log(res.headers)
expect(res.headers['content-security-policy']).toBeDefined()
expect(res.text).toBe('Hello World')
})
it('should include specific CSP directives in header', async () => {
const app = express()
const csp = createCspMiddleware(validPolicyPath)
app.use(csp)
app.get('/', (req, res) => res.send('Hello'))
const res = await request(app).get('/')
const cspHeader = res.headers['content-security-policy']
expect(cspHeader).toMatch(/default-src 'self'/)
expect(cspHeader).toMatch(/script-src 'self'[^;]*example\.com/)
expect(cspHeader).toMatch(/style-src 'self'[^;]*'unsafe-inline'/)
})
it('should throw an error for malformed YAML', () => {
expect(() => {
createCspMiddleware(malformedPolicyPath)
}).toThrow(/Implicit keys|bad indentation|unexpected token/i)
})
it('should show defaults for missing YAML', async () => {
const app = express()
const csp = createCspMiddleware(missingPolicyPath)
app.use(csp)
app.get('/', (req, res) => res.send('Test Custom'))
const res = await request(app).get('/')
res.headers.TEST = 'Missing'
console.log(res.headers)
const cspHeader = res.headers['content-security-policy']
expect(cspHeader).toMatch(/default-src 'self'/)
})
it('should show nothing for minimal YAML', async () => {
const app = express()
const csp = createCspMiddleware(minimalPolicyPath)
app.use(csp)
app.get('/', (req, res) => res.send('Test Custom'))
const res = await request(app).get('/')
res.headers.TEST = 'Minimal'
console.log(res.headers)
const cspHeader = res.headers['content-security-policy']
const allowed = ['x-powered-by', 'content-type', 'content-length']
Object.keys(res.headers).forEach((key) => {
if (!allowed.includes(key.toLowerCase())) {
expect(key).not.toMatch(/^(content|x)-/i)
}
})
})
it('should apply a custom policy file correctly', async () => {
const app = express()
const csp = createCspMiddleware(customPolicyPath)
app.use(csp)
app.get('/', (req, res) => res.send('Test Custom'))
const res = await request(app).get('/')
res.headers.TEST = 'Custom'
console.log(res.headers)
const cspHeader = res.headers['content-security-policy']
expect(cspHeader).toMatch(/default-src 'self'/)
expect(cspHeader).toMatch(/script-src 'self' https:\/\/cdn\.example\.com/)
})
})