Skip to content

Commit 2e752b4

Browse files
committed
prevent fs write helper from re-resolving
1 parent 5dc9144 commit 2e752b4

File tree

5 files changed

+22
-14
lines changed

5 files changed

+22
-14
lines changed

packages/core/src/execution-engine/node-execution-context/utils/__tests__/file-system-helper-functions.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SecurityConfig } from '@n8n/config';
22
import { Container } from '@n8n/di';
33
import type { INode } from 'n8n-workflow';
4-
import { createReadStream } from 'node:fs';
4+
import { constants, createReadStream } from 'node:fs';
55
import { access as fsAccess, realpath as fsRealpath } from 'node:fs/promises';
66
import { join } from 'node:path';
77

@@ -304,7 +304,7 @@ describe('getFileSystemHelperFunctions', () => {
304304
helperFunctions.writeContentToFile(
305305
await helperFunctions.resolvePath(instanceSettings.n8nFolder + '/test.txt'),
306306
'content',
307-
'w',
307+
constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC,
308308
),
309309
).rejects.toThrow('not writable');
310310
});

packages/core/src/execution-engine/node-execution-context/utils/file-system-helper-functions.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const getAllowedPaths = () => {
3636
return allowedPaths;
3737
};
3838

39-
const resolvePath = async (path: PathLike): Promise<ResolvedFilePath> => {
39+
async function resolvePath(path: PathLike): Promise<ResolvedFilePath> {
4040
try {
4141
return (await fsRealpath(path)) as ResolvedFilePath; // apply brand, since we know it's resolved now
4242
} catch (error: unknown) {
@@ -45,7 +45,7 @@ const resolvePath = async (path: PathLike): Promise<ResolvedFilePath> => {
4545
}
4646
throw error;
4747
}
48-
};
48+
}
4949

5050
function isFilePathBlocked(resolvedFilePath: ResolvedFilePath): boolean {
5151
const allowedPaths = getAllowedPaths();
@@ -98,11 +98,10 @@ export const getFileSystemHelperFunctions = (node: INode): FileSystemHelperFunct
9898
stream.once('error', (error) => {
9999
if ((error as NodeJS.ErrnoException).code === 'ELOOP') {
100100
reject(
101-
new NodeOperationError(
102-
node,
103-
`The file "${String(resolvedFilePath)}" could not be opened.`,
104-
{ level: 'warning' },
105-
),
101+
new NodeOperationError(node, error, {
102+
level: 'warning',
103+
description: 'Symlinks are not allowed.',
104+
}),
106105
);
107106
} else {
108107
reject(error);
@@ -126,7 +125,10 @@ export const getFileSystemHelperFunctions = (node: INode): FileSystemHelperFunct
126125
},
127126
);
128127
}
129-
return await fsWriteFile(resolvedFilePath, content, { encoding: 'binary', flag });
128+
return await fsWriteFile(resolvedFilePath, content, {
129+
encoding: 'binary',
130+
flag: (flag ?? 0) | constants.O_NOFOLLOW,
131+
});
130132
},
131133
resolvePath,
132134
isFilePathBlocked,

packages/nodes-base/nodes/Files/ReadWriteFile/actions/write.operation.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { Readable } from 'stream';
1010
import { updateDisplayOptions } from '@utils/utilities';
1111

1212
import { errorMapper } from '../helpers/utils';
13+
import { constants } from 'node:fs';
1314

1415
export const properties: INodeProperties[] = [
1516
{
@@ -68,7 +69,9 @@ export async function execute(this: IExecuteFunctions, items: INodeExecutionData
6869
const dataPropertyName = this.getNodeParameter('dataPropertyName', itemIndex);
6970
fileName = this.getNodeParameter('fileName', itemIndex) as string;
7071
const options = this.getNodeParameter('options', itemIndex, {});
71-
const flag: string = options.append ? 'a' : 'w';
72+
const flag: number = options.append
73+
? constants.O_APPEND
74+
: constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC;
7275

7376
item = items[itemIndex];
7477

packages/nodes-base/nodes/WriteBinaryFile/WriteBinaryFile.node.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
INodeType,
66
INodeTypeDescription,
77
} from 'n8n-workflow';
8+
import { constants } from 'node:fs';
89
import type { Readable } from 'stream';
910

1011
export class WriteBinaryFile implements INodeType {
@@ -75,7 +76,9 @@ export class WriteBinaryFile implements INodeType {
7576

7677
const options = this.getNodeParameter('options', 0, {});
7778

78-
const flag: string = options.append ? 'a' : 'w';
79+
const flag: number = options.append
80+
? constants.O_APPEND
81+
: constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC;
7982

8083
item = items[itemIndex];
8184

packages/workflow/src/interfaces.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ export interface BaseHelperFunctions {
694694
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
695695
}
696696

697-
const __brand = Symbol('brand');
697+
const __brand = Symbol('resolvedFilePath');
698698

699699
export type ResolvedFilePath = string & {
700700
[__brand]: 'ResolvedFilePath';
@@ -708,7 +708,7 @@ export interface FileSystemHelperFunctions {
708708
writeContentToFile(
709709
path: ResolvedFilePath,
710710
content: string | Buffer | Readable,
711-
flag?: string,
711+
flag?: number,
712712
): Promise<void>;
713713
}
714714

0 commit comments

Comments
 (0)