{"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/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/runtime/server/astro-island.ts#L142-L178","documentation":"Client-side hydration guard inside the <astro-island> custom element. After the component module loads, the element resolves the component-export attribute (default 'default'). A plain export name that is exactly one of the forbidden keys ('__proto__', 'constructor', 'prototype') is rejected before lookup, because resolving it would touch Object prototype members. This is a prototype-pollution protection for the browser hydration runtime.","triggerScenarios":"The component-export attribute of an astro-island element equals '__proto__', 'constructor', or 'prototype'. Reachable via hand-edited or tampered built HTML, a plugin/proxy rewriting island attributes, or a component module that literally exports a member named 'constructor' or 'prototype' and is referenced by that name.","commonSituations":"Almost never occurs in a normal build; seen when output HTML is mutated by optimizers/minifiers/security appliances, when islands are hand-authored, or when pen-testing tooling injects crafted attribute values.","solutions":["Rename the export in the component module and rebuild so the attribute references a valid export name","Stop hand-writing or mutating <astro-island> markup; let Astro generate it","Audit any HTML post-processing plugins (minifiers, CDN rewriters) that touch component-export attributes"],"exampleFix":"// before (hand-authored island markup)\n<astro-island component-export=\"constructor\" ...></astro-island>\n\n// after\n<astro-island component-export=\"MyWidget\" ...></astro-island>","handlingStrategy":"validation","validationCode":"const FORBIDDEN = new Set(['__proto__', 'constructor', 'prototype']);\nconst exportName = islandEl.getAttribute('component-export') ?? 'default';\nif (FORBIDDEN.has(exportName)) throw new Error('Refusing forbidden export name: ' + exportName);","typeGuard":"function isSafeExportName(name: string): boolean {\n  return !['__proto__', 'constructor', 'prototype'].includes(name);\n}","tryCatchPattern":"If generating astro-island markup programmatically, wrap attribute assignment in try/catch and log the offending component-export value; hydration errors are already caught internally by handleHydrationError, so surface them via an error boundary or console handler instead of crashing.","preventionTips":["Let Astro generate astro-island markup; never hand-author component-export attributes","Avoid exports literally named constructor or prototype","Keep HTML minifiers/CDN rewriters away from astro-island attributes"],"tags":["astro-island","hydration","security","prototype-pollution","browser"],"backgroundTag":"prototype-pollution","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}