fix(core): Resolve custom node icon paths (#30946)

This commit is contained in:
Michael Kret 2026-06-03 09:00:56 +03:00 committed by GitHub
parent 2824370072
commit 7ab8254329
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 32 additions and 3 deletions

View File

@ -130,6 +130,14 @@ describe('LoadNodesAndCredentials', () => {
});
describe('N8N_CUSTOM_EXTENSIONS', () => {
it('should return file path if url contains a relative custom file path', () => {
const result = instanceCustom.resolveIcon(
packageNameCustom,
`${pathPrefixCustom}/node_modules/custom-node/icon.png`,
);
expect(result).toBe(`${dirCustom}/node_modules/custom-node/icon.png`);
});
it('should return file path if url contains "//" with absolute custom file path', () => {
const result = instanceCustom.resolveIcon(
packageNameCustom,

View File

@ -245,8 +245,14 @@ export class LoadNodesAndCredentials {
const pathPrefix = `/icons/${packageName}/`;
const urlFilePath = url.substring(pathPrefix.length);
const filePath = isCustom ? resolvePathCustom(urlFilePath) : resolvePath(urlFilePath);
if (isCustom && !isWindowsFilePath(urlFilePath)) {
const relativeFilePath = resolvePath(urlFilePath);
if (isContainedWithin(loader.directory, relativeFilePath)) {
return relativeFilePath;
}
}
const filePath = isCustom ? resolvePathCustom(urlFilePath) : resolvePath(urlFilePath);
return isContainedWithin(loader.directory, filePath) ? filePath : undefined;
}

View File

@ -109,6 +109,14 @@ describe('DirectoryLoader', () => {
expect(mockFs.readFileSync).not.toHaveBeenCalled();
});
it('should build custom icon URLs relative to the custom directory for absolute source paths', () => {
const loader = new CustomDirectoryLoader(directory);
loader.loadNodeFromFile(`${directory}/dist/Node1/Node1.node.js`);
expect(mockNode1.description.iconUrl).toBe('icons/CUSTOM/dist/Node1/node1.svg');
});
it('should load custom nodes when specified with CUSTOM prefix in includeNodes', async () => {
const loader = new CustomDirectoryLoader(directory, [], ['CUSTOM.node1', 'CUSTOM.node2']);

View File

@ -424,14 +424,21 @@ export abstract class DirectoryLoader implements NodeLoader {
private getIconPath(icon: string, filePath: string) {
const iconPath = path.join(path.dirname(filePath), icon.replace('file:', ''));
const absoluteIconPath = path.isAbsolute(iconPath)
? iconPath
: path.join(this.directory, iconPath);
if (!isContainedWithin(this.directory, path.join(this.directory, iconPath))) {
if (!isContainedWithin(this.directory, absoluteIconPath)) {
throw new UnexpectedError(
`Icon path "${iconPath}" is not contained within the package directory "${this.directory}"`,
);
}
return `icons/${this.packageName}/${iconPath}`;
const relativeIconPath = path.isAbsolute(iconPath)
? path.relative(this.directory, absoluteIconPath).replaceAll(path.sep, '/')
: iconPath;
return `icons/${this.packageName}/${relativeIconPath}`;
}
private fixIconPaths(