schangxiang@126.com
2025-09-19 0821aa23eabe557c0d9ef5dbe6989c68be35d1fe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
'use strict';
 
const extend = require('extend');
const platform = require('platform');
const utils = require('../utils');
 
const HEADER = [
  'x-content-security-policy',
  'content-security-policy',
];
const REPORT_ONLY_HEADER = [
  'x-content-security-policy-report-only',
  'content-security-policy-report-only',
];
 
module.exports = options => {
  return async function csp(ctx, next) {
    await next();
 
    const opts = utils.merge(options, ctx.securityOptions.csp);
    if (utils.checkIfIgnore(opts, ctx)) return;
 
    let finalHeader;
    let value;
    const matchedOption = extend(true, {}, opts.policy);
    const isIE = platform.parse(ctx.header['user-agent']).name === 'IE';
    const bufArray = [];
 
    const headers = opts.reportOnly ? REPORT_ONLY_HEADER : HEADER;
    if (isIE && opts.supportIE) {
      finalHeader = headers[0];
    } else {
      finalHeader = headers[1];
    }
 
    for (const key in matchedOption) {
 
      value = matchedOption[key];
      value = Array.isArray(value) ? value : [ value ];
 
      // Other arrays are splitted into strings EXCEPT `sandbox`
      if (key === 'sandbox' && value[0] === true) {
        bufArray.push(key);
      } else {
        if (key === 'script-src') {
          const hasNonce = value.some(function(val) {
            return val.indexOf('nonce-') !== -1;
          });
 
          if (!hasNonce) {
            value.push('\'nonce-' + ctx.nonce + '\'');
          }
        }
 
        value = value.map(function(d) {
          if (d.startsWith('.')) {
            d = '*' + d;
          }
          return d;
        });
        bufArray.push(key + ' ' + value.join(' '));
      }
    }
    const headerString = bufArray.join(';');
    ctx.set(finalHeader, headerString);
    ctx.set('x-csp-nonce', ctx.nonce);
  };
};