{"record":{"id":"d161670de047b872","repo":"halo-dev/halo","slug":"provider-resource-path-escapes-its-root-resource","errorCode":null,"errorMessage":"Provider resource path escapes its root: {resourcePath}","messagePattern":"Provider resource path escapes its root: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"application/src/main/java/run/halo/app/plugin/UiPluginBundleServiceImpl.java","lineNumber":263,"sourceCode":"\n    private static String normalizeResourcePath(String resourcePath) {\n        if (!StringUtils.hasText(resourcePath)) {\n            throw new IllegalArgumentException(\"Provider resource path must not be blank\");\n        }\n        var normalizedSlashes = resourcePath.replace('\\\\', '/');\n        if (normalizedSlashes.startsWith(\"/\")\n                || normalizedSlashes.startsWith(\"//\")\n                || normalizedSlashes.contains(\"?\")\n                || normalizedSlashes.contains(\"#\")\n                || normalizedSlashes.matches(\"^[a-zA-Z][a-zA-Z0-9+.-]*:.*\")) {\n            throw new IllegalArgumentException(\n                    \"Provider resource path must be provider-root-relative: \" + resourcePath);\n        }\n        var normalized = Path.of(normalizedSlashes).normalize();\n        if (normalized.isAbsolute()\n                || normalized.startsWith(\"..\")\n                || normalized.toString().equals(\".\")) {\n            throw new IllegalArgumentException(\"Provider resource path escapes its root: \" + resourcePath);\n        }\n        return normalized.toString().replace('\\\\', '/');\n    }\n\n    private UiPluginProviderDescriptor createDescriptor(String version, List<KeyedProvider> keyedProviders) {\n        var hasLegacyProvider = keyedProviders.stream()\n                .map(KeyedProvider::provider)\n                .anyMatch(provider -> provider.kind() == ProviderKind.LEGACY);\n        return new UiPluginProviderDescriptor(\n                keyedProviders.stream().map(this::describeProvider).toList(),\n                hasLegacyProvider ? versionedBundleUrl(\"bundle.js\", version) : null);\n    }\n\n    private UiPluginProviderDescriptor.Provider describeProvider(KeyedProvider keyedProvider) {\n        var provider = keyedProvider.provider();\n        var candidate = provider.candidate();\n        var cacheKey = keyedProvider.cacheKey();\n        return switch (provider.kind()) {","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/java/run/halo/app/plugin/UiPluginBundleServiceImpl.java#L245-L281","documentation":"Error \"Provider resource path escapes its root: {resourcePath}\" thrown in halo-dev/halo.","triggerScenarios":"Thrown at application/src/main/java/run/halo/app/plugin/UiPluginBundleServiceImpl.java:263 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Remove '..' segments from the manifest resource path so it stays inside the provider root.","Rebuild the plugin bundle; path traversal in the manifest usually comes from a misconfigured output path.","Confirm the entry file is packaged under the provider root directory, not referenced from outside it."],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}