@@ -39,7 +39,7 @@ function saveConfig(config) {
3939
4040// Commands
4141async function install ( ) {
42- console . log ( "Installing file opener protocol..." )
42+ logInfo ( "Installing file opener protocol..." )
4343 ensureConfigDir ( )
4444
4545 // Register protocol using protocol-registry
@@ -48,17 +48,24 @@ async function install() {
4848 const handlerPath = path . join ( __dirname , "bin" , "fopen-handler-simple.cjs" )
4949 const command = `node "${ handlerPath } " "$_URL_"`
5050
51- console . log ( `Registering protocol: "fileopener"` )
52- console . log ( `With command: "${ command } "` )
51+ logInfo ( "Registering protocol" , {
52+ "Protocol" : "fileopener" ,
53+ "Command" : command ,
54+ "Handler path" : handlerPath
55+ } )
5356
5457 await protocolRegistry . register ( "fileopener" , command , {
5558 override : true ,
5659 terminal : false
5760 } )
58- console . log ( '\nProtocol registration successful!' )
59- console . log ( "Configuration directory: " + CONFIG_DIR )
61+ logInfo ( "Protocol registration successful" , {
62+ "Configuration directory" : CONFIG_DIR
63+ } )
6064 } catch ( error ) {
61- console . error ( '\nError during protocol registration:' , error )
65+ logError ( "Error during protocol registration" , {
66+ "Error" : error . message ,
67+ "Stack" : error . stack
68+ } )
6269 }
6370}
6471
@@ -129,6 +136,29 @@ function uninstall() {
129136 }
130137}
131138
139+ function cleanLogs ( ) {
140+ const LOG_FILE = path . join ( CONFIG_DIR , "handler.log" )
141+ const BACKUP_LOG_FILE = LOG_FILE + ".old"
142+
143+ let cleaned = false
144+
145+ if ( fs . existsSync ( LOG_FILE ) ) {
146+ fs . unlinkSync ( LOG_FILE )
147+ console . log ( "Current log file removed" )
148+ cleaned = true
149+ }
150+
151+ if ( fs . existsSync ( BACKUP_LOG_FILE ) ) {
152+ fs . unlinkSync ( BACKUP_LOG_FILE )
153+ console . log ( "Backup log file removed" )
154+ cleaned = true
155+ }
156+
157+ if ( ! cleaned ) {
158+ console . log ( "No log files found to clean" )
159+ }
160+ }
161+
132162// File opening functionality
133163function openFile ( filePath ) {
134164 const platform = process . platform
@@ -180,146 +210,55 @@ function openFile(filePath) {
180210 } , 5000 ) // 5 second timeout
181211}
182212
183- // Security validation functions
184- function validateFilePath ( filePath , projectPath ) {
185- // Check for path traversal attempts
186- if ( filePath . includes ( '..' ) || filePath . includes ( '~' ) ) {
187- console . log ( "Security violation: Path traversal attempt detected" )
188- return false
213+ // Logging functions
214+ function logInfo ( message , details = { } ) {
215+ const timestamp = new Date ( ) . toISOString ( )
216+ console . log ( `[${ timestamp } ] INFO: ${ message } ` )
217+
218+ for ( const [ key , value ] of Object . entries ( details ) ) {
219+ console . log ( `[${ timestamp } ] ${ key } : "${ value } "` )
189220 }
221+ }
190222
191- // Check for absolute paths (should be relative to project)
192- if ( path . isAbsolute ( filePath ) ) {
193- console . log ( "Security violation: Absolute path not allowed" )
194- return false
223+ function logWarning ( message , details = { } ) {
224+ const timestamp = new Date ( ) . toISOString ( )
225+ console . log ( `[${ timestamp } ] WARNING: ${ message } ` )
226+
227+ for ( const [ key , value ] of Object . entries ( details ) ) {
228+ console . log ( `[${ timestamp } ] ${ key } : "${ value } "` )
195229 }
230+ }
196231
197- // Normalize the path to prevent various bypass attempts
198- const normalizedPath = path . normalize ( filePath )
232+ function logError ( message , details = { } ) {
233+ const timestamp = new Date ( ) . toISOString ( )
234+ console . log ( `[${ timestamp } ] ERROR: ${ message } ` )
199235
200- // Check for any remaining traversal attempts after normalization
201- if ( normalizedPath . includes ( '..' ) ) {
202- console . log ( "Security violation: Path traversal detected after normalization" )
203- return false
236+ for ( const [ key , value ] of Object . entries ( details ) ) {
237+ console . log ( `[${ timestamp } ] ${ key } : "${ value } "` )
204238 }
239+ }
205240
206- // Resolve full file path
207- const fullPath = path . resolve ( path . join ( projectPath , normalizedPath ) )
208- const normalizedProjectPath = path . resolve ( projectPath )
209-
210- // Ensure the resolved path is within the project directory
211- if ( ! fullPath . startsWith ( normalizedProjectPath ) ) {
212- console . log ( "Security violation: Path outside project directory" )
213- return false
241+ function logSecurityViolation ( violationType , details ) {
242+ const timestamp = new Date ( ) . toISOString ( )
243+ console . log ( `[${ timestamp } ] SECURITY VIOLATION: ${ violationType } ` )
244+
245+ for ( const [ key , value ] of Object . entries ( details ) ) {
246+ console . log ( `[${ timestamp } ] ${ key } : "${ value } "` )
214247 }
248+ }
215249
216- // Check for symbolic links that might escape the project directory
217- try {
218- const realPath = fs . realpathSync ( fullPath )
219- if ( ! realPath . startsWith ( normalizedProjectPath ) ) {
220- console . log ( "Security violation: Symbolic link escapes project directory" )
221- return false
222- }
223- } catch ( error ) {
224- // If realpathSync fails, the file might not exist yet, but we'll check later
225- // This is not a security violation by itself
250+ function logSecurityAttempt ( attemptType , filePath , projectPath , additionalInfo = { } ) {
251+ const timestamp = new Date ( ) . toISOString ( )
252+ console . log ( `[${ timestamp } ] SECURITY VIOLATION: ${ attemptType } ` )
253+ console . log ( `[${ timestamp } ] Attempted path: "${ filePath } "` )
254+ console . log ( `[${ timestamp } ] Project path: "${ projectPath } "` )
255+
256+ for ( const [ key , value ] of Object . entries ( additionalInfo ) ) {
257+ console . log ( `[${ timestamp } ] ${ key } : "${ value } "` )
226258 }
227-
228- return true
229259}
230260
231- // Parse URL and open file
232- function handleUrl ( url ) {
233- console . log ( `Processing URL: ${ url } ` )
234261
235- try {
236- const parsedUrl = new URL ( url )
237-
238- if ( parsedUrl . protocol !== "fileopener:" ) {
239- console . log ( `Invalid protocol: ${ parsedUrl . protocol } . Expected 'fileopener:'` )
240- return
241- }
242-
243- const project = parsedUrl . hostname
244- if ( ! project ) {
245- console . log ( "Project name is required in URL" )
246- return
247- }
248-
249- // Handle special case for config
250- if ( project === 'config' && ! parsedUrl . pathname . replace ( / ^ \/ + / , '' ) ) {
251- console . log ( `Opening config file: ${ CONFIG_FILE } ` )
252- openFile ( CONFIG_FILE )
253- return
254- }
255-
256- let filePath
257- // Check for legacy query parameter format
258- const queryPath = parsedUrl . searchParams . get ( "path" )
259- if ( queryPath ) {
260- // Legacy format: fileopener://project?path=file/path
261- filePath = decodeURIComponent ( queryPath )
262- } else {
263- // Modern format: fileopener://project/file/path
264- filePath = parsedUrl . pathname . slice ( 1 ) // Remove leading slash
265- if ( ! filePath ) {
266- console . log ( "File path is required in URL" )
267- return
268- }
269- filePath = decodeURIComponent ( filePath )
270- }
271-
272- console . log ( `Project: ${ project } , File: ${ filePath } ` )
273-
274- // Get project path from config (whitelist check)
275- const config = getConfig ( )
276- const projectPath = config . projects [ project ]
277-
278- if ( ! projectPath ) {
279- console . log ( `Project '${ project } ' not found in configuration` )
280- console . log ( "Available projects:" )
281- for ( const [ name , path ] of Object . entries ( config . projects ) ) {
282- console . log ( ` ${ name } -> ${ path } ` )
283- }
284- return
285- }
286-
287- // Security validation: whitelist-based path checking
288- if ( ! validateFilePath ( filePath , projectPath ) ) {
289- console . log ( "Access denied: Security policy violation" )
290- console . log ( `Attempted access to: ${ filePath } ` )
291- console . log ( `Allowed project path: ${ projectPath } ` )
292- return
293- }
294-
295- // Resolve full file path (after validation)
296- const fullPath = path . resolve ( path . join ( projectPath , path . normalize ( filePath ) ) )
297-
298- // Final security check: ensure the resolved path is within the project directory
299- const normalizedProjectPath = path . resolve ( projectPath )
300- if ( ! fullPath . startsWith ( normalizedProjectPath ) ) {
301- console . log ( "Security violation: Final path validation failed" )
302- return
303- }
304-
305- // Check if file exists
306- if ( ! fs . existsSync ( fullPath ) ) {
307- console . log ( `File not found: ${ fullPath } ` )
308- return
309- }
310-
311- // Open the file
312- openFile ( fullPath )
313-
314- // Exit the process after opening the file
315- setTimeout ( ( ) => {
316- process . exit ( 0 )
317- } , 100 ) // Small delay to ensure file opening message is displayed
318- } catch ( error ) {
319- console . log ( `Failed to parse URL: ${ error . message } ` )
320- process . exit ( 1 )
321- }
322- }
323262
324263// Open config file
325264function openConfig ( ) {
@@ -357,24 +296,19 @@ switch (command) {
357296 case "uninstall" :
358297 uninstall ( )
359298 break
360- case "open" :
361- if ( ! args [ 1 ] ) {
362- console . log ( "Missing required URL" )
363- console . log ( "Usage: fopen open <fileopener://url>" )
364- } else {
365- handleUrl ( args [ 1 ] )
366- }
367- break
368299 case "config" :
369300 openConfig ( )
370301 break
302+ case "clean-logs" :
303+ cleanLogs ( )
304+ break
371305 default :
372306 console . log ( "File opener CLI - Use one of the subcommands:" )
373- console . log ( " install - Register the fileopener:// protocol" )
374- console . log ( " add - Add a project alias" )
375- console . log ( " list - List all configured projects" )
376- console . log ( " remove - Remove a project alias" )
377- console . log ( " uninstall - Unregister the protocol" )
378- console . log ( " open - Open a file using fileopener:// URL " )
379- console . log ( " config - Open the configuration file " )
307+ console . log ( " install - Register the fileopener:// protocol" )
308+ console . log ( " add - Add a project alias" )
309+ console . log ( " list - List all configured projects" )
310+ console . log ( " remove - Remove a project alias" )
311+ console . log ( " uninstall - Unregister the protocol" )
312+ console . log ( " config - Open the configuration file " )
313+ console . log ( " clean-logs - Clean log files " )
380314}
0 commit comments