11import { Container } from '@n8n/di' ;
22import type { INode } from 'n8n-workflow' ;
33import { createReadStream } from 'node:fs' ;
4- import { access as fsAccess } from 'node:fs/promises' ;
4+ import { access as fsAccess , realpath as fsRealpath } from 'node:fs/promises' ;
55import { join } from 'node:path' ;
66
77import {
@@ -30,6 +30,7 @@ beforeEach(() => {
3030 // @ts -expect-error undefined property
3131 error . code = 'ENOENT' ;
3232 ( fsAccess as jest . Mock ) . mockRejectedValue ( error ) ;
33+ ( fsRealpath as jest . Mock ) . mockImplementation ( ( path : string ) => path ) ;
3334
3435 instanceSettings = Container . get ( InstanceSettings ) ;
3536} ) ;
@@ -39,115 +40,125 @@ describe('isFilePathBlocked', () => {
3940 process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
4041 } ) ;
4142
42- it ( 'should return true for static cache dir' , ( ) => {
43+ it ( 'should return true for static cache dir' , async ( ) => {
4344 const filePath = instanceSettings . staticCacheDir ;
44- expect ( isFilePathBlocked ( filePath ) ) . toBe ( true ) ;
45+ expect ( await isFilePathBlocked ( filePath ) ) . toBe ( true ) ;
4546 } ) ;
4647
47- it ( 'should return true for restricted paths' , ( ) => {
48+ it ( 'should return true for restricted paths' , async ( ) => {
4849 const restrictedPath = instanceSettings . n8nFolder ;
49- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
50+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
5051 } ) ;
5152
52- it ( 'should handle empty allowed paths' , ( ) => {
53+ it ( 'should handle empty allowed paths' , async ( ) => {
5354 delete process . env [ RESTRICT_FILE_ACCESS_TO ] ;
54- const result = isFilePathBlocked ( '/some/random/path' ) ;
55+ const result = await isFilePathBlocked ( '/some/random/path' ) ;
5556 expect ( result ) . toBe ( false ) ;
5657 } ) ;
5758
58- it ( 'should handle multiple allowed paths' , ( ) => {
59+ it ( 'should handle multiple allowed paths' , async ( ) => {
5960 process . env [ RESTRICT_FILE_ACCESS_TO ] = '/path1;/path2;/path3' ;
6061 const allowedPath = '/path2/somefile' ;
61- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
62+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
6263 } ) ;
6364
64- it ( 'should handle empty strings in allowed paths' , ( ) => {
65+ it ( 'should handle empty strings in allowed paths' , async ( ) => {
6566 process . env [ RESTRICT_FILE_ACCESS_TO ] = '/path1;;/path2' ;
6667 const allowedPath = '/path2/somefile' ;
67- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
68+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
6869 } ) ;
6970
70- it ( 'should trim whitespace in allowed paths' , ( ) => {
71+ it ( 'should trim whitespace in allowed paths' , async ( ) => {
7172 process . env [ RESTRICT_FILE_ACCESS_TO ] = ' /path1 ; /path2 ; /path3 ' ;
7273 const allowedPath = '/path2/somefile' ;
73- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
74+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
7475 } ) ;
7576
76- it ( 'should return false when BLOCK_FILE_ACCESS_TO_N8N_FILES is false' , ( ) => {
77+ it ( 'should return false when BLOCK_FILE_ACCESS_TO_N8N_FILES is false' , async ( ) => {
7778 process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'false' ;
7879 const restrictedPath = instanceSettings . n8nFolder ;
79- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
80+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
8081 } ) ;
8182
82- it ( 'should return true when path is in allowed paths but still restricted' , ( ) => {
83+ it ( 'should return true when path is in allowed paths but still restricted' , async ( ) => {
8384 process . env [ RESTRICT_FILE_ACCESS_TO ] = '/some/allowed/path' ;
8485 const restrictedPath = instanceSettings . n8nFolder ;
85- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
86+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
8687 } ) ;
8788
88- it ( 'should return false when path is in allowed paths' , ( ) => {
89+ it ( 'should return false when path is in allowed paths' , async ( ) => {
8990 const allowedPath = '/some/allowed/path' ;
9091 process . env [ RESTRICT_FILE_ACCESS_TO ] = allowedPath ;
91- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
92+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
9293 } ) ;
9394
94- it ( 'should return true when file paths in CONFIG_FILES' , ( ) => {
95+ it ( 'should return true when file paths in CONFIG_FILES' , async ( ) => {
9596 process . env [ CONFIG_FILES ] = '/path/to/config1,/path/to/config2' ;
9697 const configPath = '/path/to/config1/somefile' ;
97- expect ( isFilePathBlocked ( configPath ) ) . toBe ( true ) ;
98+ expect ( await isFilePathBlocked ( configPath ) ) . toBe ( true ) ;
9899 } ) ;
99100
100- it ( 'should return true when file paths in CUSTOM_EXTENSION_ENV' , ( ) => {
101+ it ( 'should return true when file paths in CUSTOM_EXTENSION_ENV' , async ( ) => {
101102 process . env [ CUSTOM_EXTENSION_ENV ] = '/path/to/extensions1;/path/to/extensions2' ;
102103 const extensionPath = '/path/to/extensions1/somefile' ;
103- expect ( isFilePathBlocked ( extensionPath ) ) . toBe ( true ) ;
104+ expect ( await isFilePathBlocked ( extensionPath ) ) . toBe ( true ) ;
104105 } ) ;
105106
106- it ( 'should return true when file paths in BINARY_DATA_STORAGE_PATH' , ( ) => {
107+ it ( 'should return true when file paths in BINARY_DATA_STORAGE_PATH' , async ( ) => {
107108 process . env [ BINARY_DATA_STORAGE_PATH ] = '/path/to/binary/storage' ;
108109 const binaryPath = '/path/to/binary/storage/somefile' ;
109- expect ( isFilePathBlocked ( binaryPath ) ) . toBe ( true ) ;
110+ expect ( await isFilePathBlocked ( binaryPath ) ) . toBe ( true ) ;
110111 } ) ;
111112
112- it ( 'should block file paths in email template paths' , ( ) => {
113+ it ( 'should block file paths in email template paths' , async ( ) => {
113114 process . env [ UM_EMAIL_TEMPLATES_INVITE ] = '/path/to/invite/templates' ;
114115 process . env [ UM_EMAIL_TEMPLATES_PWRESET ] = '/path/to/pwreset/templates' ;
115116
116117 const invitePath = '/path/to/invite/templates/invite.html' ;
117118 const pwResetPath = '/path/to/pwreset/templates/reset.html' ;
118119
119- expect ( isFilePathBlocked ( invitePath ) ) . toBe ( true ) ;
120- expect ( isFilePathBlocked ( pwResetPath ) ) . toBe ( true ) ;
120+ expect ( await isFilePathBlocked ( invitePath ) ) . toBe ( true ) ;
121+ expect ( await isFilePathBlocked ( pwResetPath ) ) . toBe ( true ) ;
121122 } ) ;
122123
123- it ( 'should block access to n8n files if restrict and block are set' , ( ) => {
124+ it ( 'should block access to n8n files if restrict and block are set' , async ( ) => {
124125 const homeVarName = process . platform === 'win32' ? 'USERPROFILE' : 'HOME' ;
125126 const userHome = process . env . N8N_USER_FOLDER ?? process . env [ homeVarName ] ?? process . cwd ( ) ;
126127
127128 process . env [ RESTRICT_FILE_ACCESS_TO ] = userHome ;
128129 process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
129130 const restrictedPath = instanceSettings . n8nFolder ;
130- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
131+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
131132 } ) ;
132133
133- it ( 'should allow access to parent folder if restrict and block are set' , ( ) => {
134+ it ( 'should allow access to parent folder if restrict and block are set' , async ( ) => {
134135 const homeVarName = process . platform === 'win32' ? 'USERPROFILE' : 'HOME' ;
135136 const userHome = process . env . N8N_USER_FOLDER ?? process . env [ homeVarName ] ?? process . cwd ( ) ;
136137
137138 process . env [ RESTRICT_FILE_ACCESS_TO ] = userHome ;
138139 process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
139140 const restrictedPath = join ( userHome , 'somefile.txt' ) ;
140- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
141+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
141142 } ) ;
142143
143- it ( 'should not block similar paths' , ( ) => {
144+ it ( 'should not block similar paths' , async ( ) => {
144145 const homeVarName = process . platform === 'win32' ? 'USERPROFILE' : 'HOME' ;
145146 const userHome = process . env . N8N_USER_FOLDER ?? process . env [ homeVarName ] ?? process . cwd ( ) ;
146147
147148 process . env [ RESTRICT_FILE_ACCESS_TO ] = userHome ;
148149 process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
149150 const restrictedPath = join ( userHome , '.n8n_x' ) ;
150- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
151+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
152+ } ) ;
153+
154+ it ( 'should return true for a symlink in a allowed path to a restricted path' , async ( ) => {
155+ process . env [ RESTRICT_FILE_ACCESS_TO ] = '/path1' ;
156+ const allowedPath = '/path1/symlink' ;
157+ const actualPath = '/path2/realfile' ;
158+ ( fsRealpath as jest . Mock ) . mockImplementation ( ( path : string ) =>
159+ path === allowedPath ? actualPath : path ,
160+ ) ;
161+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( true ) ;
151162 } ) ;
152163} ) ;
153164
0 commit comments