docmirror/dev-sidecar · warning

getNonce: CSP 存在但未匹配到 nonce, CSP:

Error message

getNonce: CSP 存在但未匹配到 nonce, CSP:

What it means

When injecting scripts into an HTML response, the library extracts the CSP nonce from the response's `content-security-policy` (or report-only) header so injected <script> tags are allowed. If a CSP header exists but its text contains no `nonce-xxx` token matching the regex, it logs this warning (truncated to 500 chars) and returns an empty string — the injected script then has no nonce attribute.

Solutions

  1. Disable script injection for that domain in the interceptor config if nonce injection cannot succeed.
  2. Verify the CSP actually contains a script-src nonce (check response headers in DevTools); if only hashes are used, nonces cannot be extracted by design.
  3. Configure the site/server to include 'nonce-<value>' in its script-src so the regex matches.
  4. Ignore the warning for report-only CSPs — they enforce nothing, but injection may still be blocked elsewhere; check console for CSP violations.

Example fix

// server before
Content-Security-Policy: script-src 'self' 'unsafe-inline'
// after
Content-Security-Policy: script-src 'self' 'nonce-r4nd0mValue'
Defensive patterns

Strategy: fallback

Validate before calling

const csp = res.headers['content-security-policy'] || ''
const hasNonce = /['"]nonce-[^'"]+['"]/.test(csp)
if (csp && !hasNonce) disableScriptInjectionForThisDomain()

Type guard

const cspHasNonce = (csp) => typeof csp === 'string' && /['"]nonce-([^'"]+)['"]/.test(csp)

Prevention

When it happens

Trigger: A proxied site sends a CSP header without any script-src nonce directive (e.g. only 'unsafe-inline', hashes, or strict-dynamic without nonce), while script injection is enabled for that domain.

Common situations: Sites using CSP hash-based policies; report-only CSPs without nonces; CDNs or servers stripping nonces; dynamically generated CSPs with unusual quoting.


AI-assisted analysis of docmirror/dev-sidecar@7710cd56cc (2026-08-31). Data as JSON: /api/errors/e31752ff9cc1aad1. Report an issue: GitHub.

Appendix: source

Thrown at packages/mitmproxy/src/lib/interceptor/impl/res/script.js:27

function getScript (key, script, nonce) {
  const scriptUrl = SCRIPT_URL_PRE + key
  return `<script crossorigin="anonymous" defer="defer" type="application/javascript" src="${scriptUrl}"${nonce}></script>`
}
function getScriptByUrlOrPath (scriptUrlOrPath, nonce) {
  return `<script crossorigin="anonymous" defer="defer" type="application/javascript" src="${scriptUrlOrPath}"${nonce}></script>`
}

// 从 CSP 头中提取 nonce 值,用于注入脚本以通过 'strict-dynamic' 检查
function getNonceAttr (proxyRes) {
  // CSP 可能在 content-security-policy 或 content-security-policy-report-only 中
  const csp = proxyRes.headers['content-security-policy']
    || proxyRes.headers['content-security-policy-report-only']
  if (!csp) return ''
  // 支持单引号和双引号包裹的 nonce 值
  const match = csp.match(/['"]nonce-([^'"]+)['"]/)
  if (!match) {
    log.warn('getNonce: CSP 存在但未匹配到 nonce, CSP:', csp.substring(0, 500))
    return ''
  }
  return ` nonce="${match[1]}"`
}

module.exports = {
  name: 'script',
  priority: 211,
  responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
    const { rOptions, log, setting } = context

    // github特殊处理
    if (rOptions.hostname === 'github.com' && rOptions.headers['turbo-frame'] === 'repo-content-turbo-frame') {
      return
    }

    // 如果没有响应头 'content-type',或其值不是 'text/html',则不处理
    if (!proxyRes.headers['content-type'] || !proxyRes.headers['content-type'].includes('text/html')) {

View on GitHub (pinned to 7710cd56cc)