1
1
import { Container } from '@n8n/di' ;
2
2
import type { INode } from 'n8n-workflow' ;
3
3
import { 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' ;
5
5
import { join } from 'node:path' ;
6
6
7
7
import {
@@ -30,6 +30,7 @@ beforeEach(() => {
30
30
// @ts -expect-error undefined property
31
31
error . code = 'ENOENT' ;
32
32
( fsAccess as jest . Mock ) . mockRejectedValue ( error ) ;
33
+ ( fsRealpath as jest . Mock ) . mockImplementation ( ( path : string ) => path ) ;
33
34
34
35
instanceSettings = Container . get ( InstanceSettings ) ;
35
36
} ) ;
@@ -39,115 +40,125 @@ describe('isFilePathBlocked', () => {
39
40
process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
40
41
} ) ;
41
42
42
- it ( 'should return true for static cache dir' , ( ) => {
43
+ it ( 'should return true for static cache dir' , async ( ) => {
43
44
const filePath = instanceSettings . staticCacheDir ;
44
- expect ( isFilePathBlocked ( filePath ) ) . toBe ( true ) ;
45
+ expect ( await isFilePathBlocked ( filePath ) ) . toBe ( true ) ;
45
46
} ) ;
46
47
47
- it ( 'should return true for restricted paths' , ( ) => {
48
+ it ( 'should return true for restricted paths' , async ( ) => {
48
49
const restrictedPath = instanceSettings . n8nFolder ;
49
- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
50
+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
50
51
} ) ;
51
52
52
- it ( 'should handle empty allowed paths' , ( ) => {
53
+ it ( 'should handle empty allowed paths' , async ( ) => {
53
54
delete process . env [ RESTRICT_FILE_ACCESS_TO ] ;
54
- const result = isFilePathBlocked ( '/some/random/path' ) ;
55
+ const result = await isFilePathBlocked ( '/some/random/path' ) ;
55
56
expect ( result ) . toBe ( false ) ;
56
57
} ) ;
57
58
58
- it ( 'should handle multiple allowed paths' , ( ) => {
59
+ it ( 'should handle multiple allowed paths' , async ( ) => {
59
60
process . env [ RESTRICT_FILE_ACCESS_TO ] = '/path1;/path2;/path3' ;
60
61
const allowedPath = '/path2/somefile' ;
61
- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
62
+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
62
63
} ) ;
63
64
64
- it ( 'should handle empty strings in allowed paths' , ( ) => {
65
+ it ( 'should handle empty strings in allowed paths' , async ( ) => {
65
66
process . env [ RESTRICT_FILE_ACCESS_TO ] = '/path1;;/path2' ;
66
67
const allowedPath = '/path2/somefile' ;
67
- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
68
+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
68
69
} ) ;
69
70
70
- it ( 'should trim whitespace in allowed paths' , ( ) => {
71
+ it ( 'should trim whitespace in allowed paths' , async ( ) => {
71
72
process . env [ RESTRICT_FILE_ACCESS_TO ] = ' /path1 ; /path2 ; /path3 ' ;
72
73
const allowedPath = '/path2/somefile' ;
73
- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
74
+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
74
75
} ) ;
75
76
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 ( ) => {
77
78
process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'false' ;
78
79
const restrictedPath = instanceSettings . n8nFolder ;
79
- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
80
+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
80
81
} ) ;
81
82
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 ( ) => {
83
84
process . env [ RESTRICT_FILE_ACCESS_TO ] = '/some/allowed/path' ;
84
85
const restrictedPath = instanceSettings . n8nFolder ;
85
- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
86
+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
86
87
} ) ;
87
88
88
- it ( 'should return false when path is in allowed paths' , ( ) => {
89
+ it ( 'should return false when path is in allowed paths' , async ( ) => {
89
90
const allowedPath = '/some/allowed/path' ;
90
91
process . env [ RESTRICT_FILE_ACCESS_TO ] = allowedPath ;
91
- expect ( isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
92
+ expect ( await isFilePathBlocked ( allowedPath ) ) . toBe ( false ) ;
92
93
} ) ;
93
94
94
- it ( 'should return true when file paths in CONFIG_FILES' , ( ) => {
95
+ it ( 'should return true when file paths in CONFIG_FILES' , async ( ) => {
95
96
process . env [ CONFIG_FILES ] = '/path/to/config1,/path/to/config2' ;
96
97
const configPath = '/path/to/config1/somefile' ;
97
- expect ( isFilePathBlocked ( configPath ) ) . toBe ( true ) ;
98
+ expect ( await isFilePathBlocked ( configPath ) ) . toBe ( true ) ;
98
99
} ) ;
99
100
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 ( ) => {
101
102
process . env [ CUSTOM_EXTENSION_ENV ] = '/path/to/extensions1;/path/to/extensions2' ;
102
103
const extensionPath = '/path/to/extensions1/somefile' ;
103
- expect ( isFilePathBlocked ( extensionPath ) ) . toBe ( true ) ;
104
+ expect ( await isFilePathBlocked ( extensionPath ) ) . toBe ( true ) ;
104
105
} ) ;
105
106
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 ( ) => {
107
108
process . env [ BINARY_DATA_STORAGE_PATH ] = '/path/to/binary/storage' ;
108
109
const binaryPath = '/path/to/binary/storage/somefile' ;
109
- expect ( isFilePathBlocked ( binaryPath ) ) . toBe ( true ) ;
110
+ expect ( await isFilePathBlocked ( binaryPath ) ) . toBe ( true ) ;
110
111
} ) ;
111
112
112
- it ( 'should block file paths in email template paths' , ( ) => {
113
+ it ( 'should block file paths in email template paths' , async ( ) => {
113
114
process . env [ UM_EMAIL_TEMPLATES_INVITE ] = '/path/to/invite/templates' ;
114
115
process . env [ UM_EMAIL_TEMPLATES_PWRESET ] = '/path/to/pwreset/templates' ;
115
116
116
117
const invitePath = '/path/to/invite/templates/invite.html' ;
117
118
const pwResetPath = '/path/to/pwreset/templates/reset.html' ;
118
119
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 ) ;
121
122
} ) ;
122
123
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 ( ) => {
124
125
const homeVarName = process . platform === 'win32' ? 'USERPROFILE' : 'HOME' ;
125
126
const userHome = process . env . N8N_USER_FOLDER ?? process . env [ homeVarName ] ?? process . cwd ( ) ;
126
127
127
128
process . env [ RESTRICT_FILE_ACCESS_TO ] = userHome ;
128
129
process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
129
130
const restrictedPath = instanceSettings . n8nFolder ;
130
- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
131
+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( true ) ;
131
132
} ) ;
132
133
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 ( ) => {
134
135
const homeVarName = process . platform === 'win32' ? 'USERPROFILE' : 'HOME' ;
135
136
const userHome = process . env . N8N_USER_FOLDER ?? process . env [ homeVarName ] ?? process . cwd ( ) ;
136
137
137
138
process . env [ RESTRICT_FILE_ACCESS_TO ] = userHome ;
138
139
process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
139
140
const restrictedPath = join ( userHome , 'somefile.txt' ) ;
140
- expect ( isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
141
+ expect ( await isFilePathBlocked ( restrictedPath ) ) . toBe ( false ) ;
141
142
} ) ;
142
143
143
- it ( 'should not block similar paths' , ( ) => {
144
+ it ( 'should not block similar paths' , async ( ) => {
144
145
const homeVarName = process . platform === 'win32' ? 'USERPROFILE' : 'HOME' ;
145
146
const userHome = process . env . N8N_USER_FOLDER ?? process . env [ homeVarName ] ?? process . cwd ( ) ;
146
147
147
148
process . env [ RESTRICT_FILE_ACCESS_TO ] = userHome ;
148
149
process . env [ BLOCK_FILE_ACCESS_TO_N8N_FILES ] = 'true' ;
149
150
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 ) ;
151
162
} ) ;
152
163
} ) ;
153
164
0 commit comments