11import { z } from 'zod'
2+ import path from 'path'
23import { StructuredTool , ToolParams } from '@langchain/core/tools'
34import { Serializable } from '@langchain/core/load/serializable'
4- import { NodeFileStore } from 'langchain/stores/file/node'
55import { INode , INodeData , INodeParams } from '../../../src/Interface'
6- import { getBaseClasses } from '../../../src/utils'
6+ import { getBaseClasses , getUserHome } from '../../../src/utils'
7+ import { SecureFileStore , FileSecurityConfig } from '../../../src/SecureFileStore'
78
89abstract class BaseFileStore extends Serializable {
910 abstract readFile ( path : string ) : Promise < string >
@@ -20,30 +21,91 @@ class WriteFile_Tools implements INode {
2021 category : string
2122 baseClasses : string [ ]
2223 inputs : INodeParams [ ]
24+ warning : string
2325
2426 constructor ( ) {
2527 this . label = 'Write File'
2628 this . name = 'writeFile'
27- this . version = 1 .0
29+ this . version = 2 .0
2830 this . type = 'WriteFile'
2931 this . icon = 'writefile.svg'
3032 this . category = 'Tools'
33+ this . warning = 'This tool can be used to write files to the disk. It is recommended to use this tool with caution.'
3134 this . description = 'Write file to disk'
3235 this . baseClasses = [ this . type , 'Tool' , ...getBaseClasses ( WriteFileTool ) ]
3336 this . inputs = [
3437 {
35- label : 'Base Path' ,
36- name : 'basePath ' ,
37- placeholder : `C:\\Users\\User\\Desktop ` ,
38+ label : 'Workspace Path' ,
39+ name : 'workspacePath ' ,
40+ placeholder : `C:\\Users\\User\\MyProject ` ,
3841 type : 'string' ,
42+ description : 'Base workspace directory for file operations. All file paths will be relative to this directory.' ,
43+ optional : true
44+ } ,
45+ {
46+ label : 'Enforce Workspace Boundaries' ,
47+ name : 'enforceWorkspaceBoundaries' ,
48+ type : 'boolean' ,
49+ description : 'When enabled, restricts file access to the workspace directory for security. Recommended: true' ,
50+ default : true ,
51+ optional : true
52+ } ,
53+ {
54+ label : 'Max File Size (MB)' ,
55+ name : 'maxFileSize' ,
56+ type : 'number' ,
57+ description : 'Maximum file size in megabytes that can be written' ,
58+ default : 10 ,
59+ optional : true
60+ } ,
61+ {
62+ label : 'Allowed Extensions' ,
63+ name : 'allowedExtensions' ,
64+ type : 'string' ,
65+ description : 'Comma-separated list of allowed file extensions (e.g., .txt,.json,.md). Leave empty to allow all.' ,
66+ placeholder : '.txt,.json,.md,.py,.js' ,
3967 optional : true
4068 }
4169 ]
4270 }
4371
4472 async init ( nodeData : INodeData ) : Promise < any > {
45- const basePath = nodeData . inputs ?. basePath as string
46- const store = basePath ? new NodeFileStore ( basePath ) : new NodeFileStore ( )
73+ const workspacePath = nodeData . inputs ?. workspacePath as string
74+ const enforceWorkspaceBoundaries = nodeData . inputs ?. enforceWorkspaceBoundaries !== false // Default to true
75+ const maxFileSize = nodeData . inputs ?. maxFileSize as number
76+ const allowedExtensions = nodeData . inputs ?. allowedExtensions as string
77+
78+ // Parse allowed extensions
79+ const allowedExtensionsList = allowedExtensions ? allowedExtensions . split ( ',' ) . map ( ( ext ) => ext . trim ( ) . toLowerCase ( ) ) : [ ]
80+
81+ let store : BaseFileStore
82+
83+ if ( workspacePath ) {
84+ // Create secure file store with workspace boundaries
85+ const config : FileSecurityConfig = {
86+ workspacePath,
87+ enforceWorkspaceBoundaries,
88+ maxFileSize : maxFileSize ? maxFileSize * 1024 * 1024 : undefined , // Convert MB to bytes
89+ allowedExtensions : allowedExtensionsList . length > 0 ? allowedExtensionsList : undefined
90+ }
91+ store = new SecureFileStore ( config )
92+ } else {
93+ // Fallback to current working directory with security warnings
94+ if ( enforceWorkspaceBoundaries ) {
95+ const fallbackWorkspacePath = path . join ( getUserHome ( ) , '.flowise' )
96+ console . warn ( `[WriteFile] No workspace path specified, using ${ fallbackWorkspacePath } with security restrictions` )
97+ store = new SecureFileStore ( {
98+ workspacePath : fallbackWorkspacePath ,
99+ enforceWorkspaceBoundaries : true ,
100+ maxFileSize : maxFileSize ? maxFileSize * 1024 * 1024 : undefined ,
101+ allowedExtensions : allowedExtensionsList . length > 0 ? allowedExtensionsList : undefined
102+ } )
103+ } else {
104+ console . warn ( '[WriteFile] SECURITY WARNING: Workspace boundaries disabled - unrestricted file access enabled' )
105+ store = SecureFileStore . createUnsecure ( )
106+ }
107+ }
108+
47109 return new WriteFileTool ( { store } )
48110 }
49111}
@@ -68,7 +130,7 @@ export class WriteFileTool extends StructuredTool {
68130
69131 name = 'write_file'
70132
71- description = 'Write file from disk'
133+ description = 'Write file to disk'
72134
73135 store : BaseFileStore
74136
@@ -80,7 +142,7 @@ export class WriteFileTool extends StructuredTool {
80142
81143 async _call ( { file_path, text } : z . infer < typeof this . schema > ) {
82144 await this . store . writeFile ( file_path , text )
83- return ' File written to successfully.'
145+ return ` File written to ${ file_path } successfully.`
84146 }
85147}
86148
0 commit comments