{"record":{"id":"20e06b897a1a069e","repo":"halo-dev/halo","slug":"esm-ui-provider-output-must-contain-at-most-one-en","errorCode":null,"errorMessage":"ESM UI provider output must contain at most one entry stylesheet.","messagePattern":"ESM UI provider output must contain at most one entry stylesheet\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"ui/packages/ui-plugin-bundler-kit/src/rsbuild-esm.ts","lineNumber":79,"sourceCode":"            );\n          }\n          const entryCode = entry.source().toString();\n          await validator.validateSource(entryCode, entryFile);\n          if (\n            !/\\bexport\\s+default\\b/.test(entryCode) &&\n            !/\\bexport\\s*\\{[^}]*\\bdefault\\b[^}]*\\}/s.test(entryCode)\n          ) {\n            throw new Error(\n              \"ESM UI provider output must expose a default PluginModule export.\"\n            );\n          }\n          const entryStyles =\n            compilation.entrypoints\n              .get(\"main\")\n              ?.getFiles()\n              .filter((fileName) => fileName.endsWith(\".css\")) || [];\n          if (entryStyles.length > 1) {\n            throw new Error(\n              \"ESM UI provider output must contain at most one entry stylesheet.\"\n            );\n          }\n          const manifest = validateEsmProviderManifest({\n            format: \"esm\",\n            entry: `./${entryFile}`,\n            ...(entryStyles[0] ? { style: `./${entryStyles[0]}` } : {}),\n          });\n          compilation.emitAsset(\n            ESM_PROVIDER_MANIFEST,\n            new sources.RawSource(`${JSON.stringify(manifest, null, 2)}\\n`)\n          );\n          const report = validator.getBuildReport();\n          console.info(report.summary);\n          if (report.warning) {\n            console.warn(report.warning);\n          }\n        }","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/ui/packages/ui-plugin-bundler-kit/src/rsbuild-esm.ts#L61-L97","documentation":"normalizeResourcePath Path.normalize()s the relative path and rejects results that are absolute, start with \"..\", or equal \".\" — i.e. attempts to escape the provider root. Caught upstream and surfaced as ClassifiedProvider.invalid.","triggerScenarios":"ui-plugin.json's entry/style contains parent traversal (e.g. \"../../secret.js\") or normalizes to the current directory \".\".","commonSituations":"Author tries to reference a file outside console/; malicious/buggy manifest aiming at sibling plugin assets; path collapsing to '.' after normalization.","solutions":["Keep entry/style strictly inside the provider root (console/) — no '..' segments.","Reference only files actually shipped under the plugin's console assets.","Treat this as a security signal: never let user input flow into manifest paths unchecked."],"exampleFix":"// before — escapes the provider root\n{\n  \"format\": \"esm\",\n  \"entry\": \"../../other/index.js\"\n}\n// after — file inside console/\n{\n  \"format\": \"esm\",\n  \"entry\": \"index.js\"\n}","handlingStrategy":"validation","validationCode":"Path n = Path.of(entry.replace('\\\\', '/')).normalize();\nif (n.isAbsolute() || n.startsWith(\"..\") || n.toString().equals(\".\")) {\n    throw new IllegalStateException(\"entry escapes provider root: \" + entry);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep all referenced files inside console/.","Reject '..' in manifest paths during code review.","Treat path-escape attempts as a security issue, not just a build error."],"tags":["plugin","ui-bundle","esm","provider-manifest","path-traversal","security"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}