{"record":{"id":"543146647a68ee00","repo":"withastro/astro","slug":"invalid-component-export-path-componentexport","errorCode":null,"errorMessage":"Invalid component export path: ${componentExport}","messagePattern":"Invalid component export path: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/runtime/server/astro-island.ts","lineNumber":160,"sourceCode":"\t\t\tif (Astro[directive] === undefined) {\n\t\t\t\twindow.addEventListener(`astro:${directive}`, () => this.start(), { once: true });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait Astro[directive]!(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tconst rendererUrl = this.getAttribute('renderer-url');\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [componentModule, { default: hydrator }] = await Promise.all([\n\t\t\t\t\t\t\t\tthis.importWithRetry(this.getAttribute('component-url')!),\n\t\t\t\t\t\t\t\trendererUrl\n\t\t\t\t\t\t\t\t\t? this.importWithRetry(rendererUrl)\n\t\t\t\t\t\t\t\t\t: Promise.resolve({ default: () => () => {} }),\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\tconst componentExport = this.getAttribute('component-export') || 'default';\n\t\t\t\t\t\t\tif (!componentExport.includes('.')) {\n\t\t\t\t\t\t\t\tif (FORBIDDEN_COMPONENT_EXPORT_KEYS.has(componentExport)) {\n\t\t\t\t\t\t\t\t\tthrow new Error(`Invalid component export path: ${componentExport}`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tthis.Component = componentModule[componentExport];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.Component = componentModule;\n\t\t\t\t\t\t\t\tfor (const part of componentExport.split('.')) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tFORBIDDEN_COMPONENT_EXPORT_KEYS.has(part) ||\n\t\t\t\t\t\t\t\t\t\t!this.Component ||\n\t\t\t\t\t\t\t\t\t\t(typeof this.Component !== 'object' && typeof this.Component !== 'function') ||\n\t\t\t\t\t\t\t\t\t\t!Object.hasOwn(this.Component, part)\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(`Invalid component export path: ${componentExport}`);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tthis.Component = this.Component[part];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthis.hydrator = hydrator;\n\t\t\t\t\t\t\treturn this.hydrate;","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/runtime/server/astro-island.ts#L142-L178","documentation":"When hydrating an island, Astro resolves the component export path from the `component-export` attribute. It forbids the prototype-pollution keys `__proto__`, `constructor`, and `prototype` (the `FORBIDDEN_COMPONENT_EXPORT_KEYS` set) anywhere in the export path, and also requires each dotted segment to be an own property of a real object/function. Any violation throws a plain `Invalid component export path` Error.","triggerScenarios":"A hydrated island's serialized HTML carries a `component-export` attribute equal to (or containing a dotted segment of) `__proto__`, `constructor`, or `prototype`; or a dotted path segment that isn't an own enumerable property of the resolved module/object.","commonSituations":"Tampered or hand-edited island HTML; a build artifact where the export name resolved to a forbidden key; an attempt to traverse the module prototype chain via the export path; desync between the module's exports and the recorded `component-export` after a refactor.","solutions":["Regenerate the page so the island's `component-export` attribute reflects a real, allowed named export.","Ensure the referenced component is exported under a plain named (or default) export that isn't a prototype key.","If serving cached/CDN HTML, purge the cache so stale or tampered island attributes are replaced.","Treat unexpected occurrences as a possible XSS/prototype-pollution probe and audit the page source."],"exampleFix":"// before — island HTML references a forbidden export\n<astro-island component-export=\"__proto__\" ...></astro-island>\n\n// after — reference a real named export\n<astro-island component-export=\"default\" ...></astro-island>","handlingStrategy":"validation","validationCode":"const FORBIDDEN = new Set(['__proto__','constructor','prototype']);\nfunction isSafeComponentExport(exportPath) {\n  return exportPath.split('.').every(seg =>\n    seg.length > 0 && !FORBIDDEN.has(seg) && /^[A-Za-z_$][\\w$]*$/.test(seg)\n  );\n}","typeGuard":"function isSafeExportPath(exportPath) {\n  const FORBIDDEN = new Set(['__proto__','constructor','prototype']);\n  return typeof exportPath === 'string' &&\n    exportPath.length > 0 &&\n    exportPath.split('.').every(seg => seg && !FORBIDDEN.has(seg));\n}","tryCatchPattern":"null","preventionTips":["Regenerate pages so island component-export attributes match real exports.","Never ship hand-edited island HTML with prototype-pollution keys.","Purge cached/CDN HTML when refactoring component exports."],"tags":["security","runtime","components","hydration","prototype-pollution"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}