mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 06:33:55 +01:00
Merge pull request #17596 from overleaf/rh-permissions-policy
[web] Add Permissions-Policy header GitOrigin-RevId: 8934bbbda411102580d9ef8af135dcdc147627f9
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// @ts-check
|
||||
|
||||
const Settings = require('@overleaf/settings')
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').HttpPermissionsPolicy} HttpPermissionsPolicy
|
||||
* */
|
||||
|
||||
class HttpPermissionsPolicyMiddleware {
|
||||
/**
|
||||
* Initialise the middleware with a Permissions Policy config
|
||||
* @param {HttpPermissionsPolicy} policy
|
||||
*/
|
||||
constructor(policy) {
|
||||
this.middleware = this.middleware.bind(this)
|
||||
if (policy) {
|
||||
this.policy = this.buildPermissionsPolicy(policy)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the provided policy is valid
|
||||
* @param {HttpPermissionsPolicy} policy
|
||||
* @returns {boolean}
|
||||
*/
|
||||
validatePermissionsPolicy(policy) {
|
||||
let policyIsValid = true
|
||||
|
||||
if (!policy.allowed) {
|
||||
return true
|
||||
}
|
||||
|
||||
for (const [directive, origins] of Object.entries(policy.allowed)) {
|
||||
// Do any directives in the allowlist clash with the denylist?
|
||||
if (policy.blocked && policy.blocked.includes(directive)) {
|
||||
policyIsValid = false
|
||||
}
|
||||
if (!origins) {
|
||||
policyIsValid = false
|
||||
}
|
||||
}
|
||||
|
||||
return policyIsValid
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Permissions-Policy header string from the given policy configuration
|
||||
* @param {HttpPermissionsPolicy} policy
|
||||
* @returns {string}
|
||||
*/
|
||||
buildPermissionsPolicy(policy) {
|
||||
if (!this.validatePermissionsPolicy(policy)) {
|
||||
throw new Error('Invalid Permissions-Policy header configuration')
|
||||
}
|
||||
|
||||
const policyElements = []
|
||||
|
||||
if (policy.blocked && policy.blocked.length > 0) {
|
||||
policyElements.push(
|
||||
policy.blocked.map(policyElement => `${policyElement}=()`).join(', ')
|
||||
)
|
||||
}
|
||||
|
||||
if (policy.allowed && Object.entries(policy.allowed).length > 0) {
|
||||
policyElements.push(
|
||||
Object.keys(policy.allowed)
|
||||
.map(allowKey => `${allowKey}=(${policy.allowed[allowKey]})`)
|
||||
.join(', ')
|
||||
)
|
||||
}
|
||||
|
||||
return policyElements.join(', ')
|
||||
}
|
||||
|
||||
middleware(req, res, next) {
|
||||
if (this.policy && Settings.useHttpPermissionsPolicy) {
|
||||
const originalRender = res.render
|
||||
|
||||
res.render = (...args) => {
|
||||
res.setHeader('Permissions-Policy', this.policy)
|
||||
originalRender.apply(res, args)
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HttpPermissionsPolicyMiddleware
|
||||
@@ -9,6 +9,7 @@ const Router = require('../router')
|
||||
const helmet = require('helmet')
|
||||
const UserSessionsRedis = require('../Features/User/UserSessionsRedis')
|
||||
const Csrf = require('./Csrf')
|
||||
const HttpPermissionsPolicyMiddleware = require('./HttpPermissionsPolicy')
|
||||
|
||||
const sessionsRedisClient = UserSessionsRedis.client()
|
||||
|
||||
@@ -143,6 +144,15 @@ if (Settings.blockCrossOriginRequests) {
|
||||
app.use(Csrf.blockCrossOriginRequests())
|
||||
}
|
||||
|
||||
if (Settings.useHttpPermissionsPolicy) {
|
||||
const httpPermissionsPolicy = new HttpPermissionsPolicyMiddleware(
|
||||
Settings.httpPermissions
|
||||
)
|
||||
logger.debug('adding permissions policy config', Settings.httpPermissions)
|
||||
|
||||
webRouter.use(httpPermissionsPolicy.middleware)
|
||||
}
|
||||
|
||||
RedirectManager.apply(webRouter)
|
||||
|
||||
webRouter.use(cookieParser(Settings.security.sessionSecret))
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface HttpPermissionsPolicyRule {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
export interface HttpPermissionsPolicy {
|
||||
blocked: [string]
|
||||
allowed: HttpPermissionsPolicyRule
|
||||
}
|
||||
Reference in New Issue
Block a user