{"record":{"id":"22d8be15a16d3d0d","repo":"withastro/astro","slug":"security-csp-name-directive-defines-name-s","errorCode":null,"errorMessage":"`security.csp.${name}Directive` defines `${name}-src` resources (${sources.default.resources.join(' ')}) as well as ${shadowed.join(' and ')} resources or hashes. Because ${shadowed.join(' and ')} override `${name}-src` for their scope (browsers do not fall back), those `${name}-src` resources will not apply there. Add them to the corresponding `kind` if needed.","messagePattern":"`security\\.csp\\.(.+?)Directive` defines `(.+?)-src` resources \\((.+?)\\) as well as (.+?) resources or hashes\\. Because (.+?) override `(.+?)-src` for their scope \\(browsers do not fall back\\), those `(.+?)-src` resources will not apply there\\. Add them to the corresponding `kind` if needed\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/astro/src/core/messages/runtime.ts","lineNumber":453,"sourceCode":"\t\t{ name: 'style', directive: csp.styleDirective },\n\t] as const;\n\n\tfor (const { name, directive } of families) {\n\t\tconst sources = partitionByKind({\n\t\t\tresources: directive?.resources ?? [],\n\t\t\thashes: directive?.hashes ?? [],\n\t\t});\n\t\tif (sources.default.resources.length === 0) continue;\n\t\tconst shadowed: string[] = [];\n\t\tif (sources.element.resources.length > 0 || sources.element.hashes.length > 0) {\n\t\t\tshadowed.push(`\\`${name}-src-elem\\``);\n\t\t}\n\t\tif (sources.attribute.resources.length > 0 || sources.attribute.hashes.length > 0) {\n\t\t\tshadowed.push(`\\`${name}-src-attr\\``);\n\t\t}\n\t\tif (shadowed.length === 0) continue;\n\n\t\tlogger.warn(\n\t\t\t'csp',\n\t\t\t`\\`security.csp.${name}Directive\\` defines \\`${name}-src\\` resources (${sources.default.resources.join(\n\t\t\t\t' ',\n\t\t\t)}) as well as ${shadowed.join(' and ')} resources or hashes. Because ${shadowed.join(\n\t\t\t\t' and ',\n\t\t\t)} override \\`${name}-src\\` for their scope (browsers do not fall back), those \\`${name}-src\\` resources will not apply there. Add them to the corresponding \\`kind\\` if needed.`,\n\t\t);\n\t}\n}\n","sourceCodeStart":435,"sourceCodeEnd":463,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/core/messages/runtime.ts#L435-L463","documentation":"A scriptDirective/styleDirective can declare sources at three scopes: default (`${name}-src`), element (`${name}-src-elem`) and attribute (`${name}-src-attr`). CSP semantics make the elem/attr directives override the generic one for their scope with no browser fallback, so resources listed only under `default` never apply where an elem/attr entry exists. Astro warns (domain 'csp') when a directive mixes default resources with element/attribute entries so you can copy anything needed into the specific kind.","triggerScenarios":"security.csp.scriptDirective or styleDirective configured with resources at the default level plus a non-empty element and/or attribute entry (resources or hashes), e.g. { resources: ['https://cdn.example.com'], element: { resources: ['https://a.test'] } }.","commonSituations":"Tightening CSP for script tags without realizing script-src-elem no longer inherits script-src; adding hashes for inline event handlers next to a default allowlist; translating a raw CSP header into Astro's structured directives.","solutions":["Add the default-scoped resources to the element/attribute kinds (or vice versa) so every overriding scope allows what it needs","Remove the redundant default resources if the specific kinds fully cover usage","Keep only the default entry when you do not need elem/attr overrides at all"],"exampleFix":"// astro.config.mjs — before\nsecurity: {\n  csp: {\n    scriptDirective: {\n      resources: ['https://cdn.example.com'],\n      element: { resources: ['https://analytics.test'] },\n    },\n  },\n},\n\n// after — cdn also allowed where element scope overrides\nscriptDirective: {\n  resources: ['https://cdn.example.com'],\n  element: { resources: ['https://analytics.test', 'https://cdn.example.com'] },\n},","handlingStrategy":"validation","validationCode":"function findShadowedDirectives(directive) {\n  const shadowed = [];\n  if ((directive.element?.resources?.length ?? directive.element?.hashes?.length ?? 0) > 0) shadowed.push('element');\n  if ((directive.attribute?.resources?.length ?? directive.attribute?.hashes?.length ?? 0) > 0) shadowed.push('attribute');\n  return directive.resources?.length ? shadowed : [];\n}\n// fail a config-lint step when findShadowedDirectives(...) is non-empty","typeGuard":"function hasShadowedDefaults(d): d is { resources: string[]; element?: object; attribute?: object } {\n  return Array.isArray(d.resources) && d.resources.length > 0 &&\n    (hasEntries(d.element) || hasEntries(d.attribute));\n}","tryCatchPattern":null,"preventionTips":["Declare each scope explicitly instead of relying on default inheritance","Mirror allowlist entries into elem/attr kinds whenever you add them","Test the final CSP header in devtools against the resources each scope actually loads"],"tags":["csp","directives","security","config"],"backgroundTag":"csp-directive-misconfiguration","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}