From 31c15c887036f34d3a50937e49861a2493d57ec5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Oct 2025 01:44:58 +0000 Subject: [PATCH 1/5] Initial plan From 8dbcd0c228d1db612193ac27728fcdc6cf66758f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Oct 2025 01:51:24 +0000 Subject: [PATCH 2/5] Implement secure file uploads using system temp directory and add store() method Co-authored-by: grantcopley <1197835+grantcopley@users.noreply.github.com> --- ModuleConfig.cfc | 8 ++- interceptors/CBWIRE.cfc | 12 +++- models/CBWIREController.cfc | 6 +- models/FileUpload.cfc | 38 ++++++++++- test-harness/tests/specs/CBWIRESpec.cfc | 9 ++- test-harness/tests/specs/FileUploadSpec.cfc | 72 ++++++++++++++++++--- 6 files changed, 129 insertions(+), 16 deletions(-) diff --git a/ModuleConfig.cfc b/ModuleConfig.cfc index 6407f67b..88fcbca7 100644 --- a/ModuleConfig.cfc +++ b/ModuleConfig.cfc @@ -25,7 +25,13 @@ component { */ "moduleRootPath": getCanonicalPath( getCurrentTemplatePath().replaceNoCase( "/ModuleConfig.cfc", "", "one" ) ), /** - * The default storage path for all cbwire components. + * The default storage path for file uploads. + * Uses the system temporary directory for security. + */ + "uploadsStoragePath": getCanonicalPath( getTempDirectory() & "/cbwire" ), + /** + * The default storage path for single-file component compilation. + * This must be in the module directory for WireBox to instantiate components. */ "storagePath": getCanonicalPath( getCurrentTemplatePath().replaceNoCase( "/ModuleConfig.cfc", "", "one" ) & "/models/tmp" ), /** diff --git a/interceptors/CBWIRE.cfc b/interceptors/CBWIRE.cfc index f3448341..6463489e 100644 --- a/interceptors/CBWIRE.cfc +++ b/interceptors/CBWIRE.cfc @@ -25,12 +25,20 @@ component { */ function preReinit() { local.settings = getSettings(); - local.tmpDirectory = local.settings.moduleRootPath & "/models/tmp"; - + + // Clean up single-file component temp directory + local.tmpDirectory = local.settings.storagePath; if ( directoryExists( local.tmpDirectory ) ) { directoryDelete( local.tmpDirectory, true ); directoryCreate( local.tmpDirectory ); } + + // Clean up file uploads temp directory + local.uploadsTmpDirectory = local.settings.uploadsStoragePath; + if ( directoryExists( local.uploadsTmpDirectory ) ) { + directoryDelete( local.uploadsTmpDirectory, true ); + directoryCreate( local.uploadsTmpDirectory ); + } } /** diff --git a/models/CBWIREController.cfc b/models/CBWIREController.cfc index 28e792c8..34c947e9 100644 --- a/models/CBWIREController.cfc +++ b/models/CBWIREController.cfc @@ -133,7 +133,7 @@ component accessors="true" singleton { */ function handleFileUpload( incomingRequest, event ) { // Determine our storage path for temporary files - local.storagePath = getCanonicalPath( variables.moduleSettings.storagePath ); + local.storagePath = getCanonicalPath( variables.moduleSettings.uploadsStoragePath ); // Ensure the storage path exists if( !directoryExists( local.storagePath ) ){ @@ -174,10 +174,10 @@ component accessors="true" singleton { return event.noRender(); } - local.metaPath = getCanonicalPath( variables.moduleSettings.storagePath & "/#local.uuid#.json" ); + local.metaPath = getCanonicalPath( variables.moduleSettings.uploadsStoragePath & "/#local.uuid#.json" ); local.metaJSON = deserializeJSON( fileRead( local.metaPath ) ); - local.contents = fileReadBinary( getCanonicalPath( variables.moduleSettings.storagePath & "/#local.metaJSON.serverFile#" ) ); + local.contents = fileReadBinary( getCanonicalPath( variables.moduleSettings.uploadsStoragePath & "/#local.metaJSON.serverFile#" ) ); event .sendFile( file = local.contents, diff --git a/models/FileUpload.cfc b/models/FileUpload.cfc index 822590a8..937bdfcc 100644 --- a/models/FileUpload.cfc +++ b/models/FileUpload.cfc @@ -122,11 +122,47 @@ component { return variables.meta; } + /** + * Moves the file from temporary storage to a permanent location. + * + * @path The destination path where the file should be stored + * @return The absolute path to the stored file + */ + function store( required string path ){ + var destinationPath = getCanonicalPath( arguments.path ); + + // Check if destination is a directory + if ( directoryExists( destinationPath ) ) { + destinationPath = getCanonicalPath( destinationPath & "/" & variables.meta.serverFile ); + } + + // Ensure the destination directory exists + var destinationDir = getDirectoryFromPath( destinationPath ); + if ( !directoryExists( destinationDir ) ) { + directoryCreate( destinationDir, true ); + } + + // Move the file from temporary to permanent location + fileMove( variables.temporaryStoragePath, destinationPath ); + + // Update the temporary storage path to the new location + variables.temporaryStoragePath = destinationPath; + + // Update metadata + variables.meta.serverDirectory = destinationDir; + variables.meta.serverFile = getFileFromPath( destinationPath ); + + // Update the metadata file + fileWrite( getMetaPath(), serializeJSON( variables.meta ) ); + + return destinationPath; + } + /** * Returns the path to the temp directory (mockable in tests) */ function getUploadTempDirectory(){ - return getCanonicalPath( variables.moduleSettings.moduleRootPath & "/models/tmp" ); + return getCanonicalPath( variables.moduleSettings.uploadsStoragePath ); } /** diff --git a/test-harness/tests/specs/CBWIRESpec.cfc b/test-harness/tests/specs/CBWIRESpec.cfc index 158e5fb2..a212fb89 100644 --- a/test-harness/tests/specs/CBWIRESpec.cfc +++ b/test-harness/tests/specs/CBWIRESpec.cfc @@ -2,12 +2,19 @@ component extends="coldbox.system.testing.BaseTestCase" { function beforeAll() { super.beforeAll(); - // delete any files in models/tmp folder + // delete any files in models/tmp folder (for single-file components) local.tempFolder = expandPath( "../../../models/tmp" ); if ( directoryExists( local.tempFolder ) ) { directoryDelete( local.tempFolder, true ); } directoryCreate( local.tempFolder ); + + // Clean out uploads temp directory + local.uploadsTempFolder = getTempDirectory() & "/cbwire"; + if ( directoryExists( local.uploadsTempFolder ) ) { + directoryDelete( local.uploadsTempFolder, true ); + } + directoryCreate( local.uploadsTempFolder ); } // Lifecycle methods and BDD suites as before... diff --git a/test-harness/tests/specs/FileUploadSpec.cfc b/test-harness/tests/specs/FileUploadSpec.cfc index 3c571244..2e9f1a26 100644 --- a/test-harness/tests/specs/FileUploadSpec.cfc +++ b/test-harness/tests/specs/FileUploadSpec.cfc @@ -4,7 +4,8 @@ component extends="coldbox.system.testing.BaseTestCase" { super.beforeAll(); // Clean out tmp directory before all tests - local.tempFolder = expandPath( "../../../models/tmp" ); + // Use system temp directory + cbwire subdirectory + local.tempFolder = getTempDirectory() & "/cbwire"; if ( directoryExists( local.tempFolder ) ) { directoryDelete( local.tempFolder, true ); } @@ -99,17 +100,72 @@ component extends="coldbox.system.testing.BaseTestCase" { expect( result.getBase64Src() ).toInclude( "data:" ); }); - it( "should return correct upload temp directory path with proper slash separation", function() { + it( "should return correct upload temp directory path using system temp directory", function() { // Create a fresh FileUpload instance (not mocked) to test getUploadTempDirectory var fileUploadInstance = getInstance( "FileUpload@cbwire" ); var tempDir = fileUploadInstance.getUploadTempDirectory(); - // The path should contain "/models/tmp" with proper slash separation - expect( tempDir ).toInclude( "/models/tmp" ); - // Should not have malformed concatenation like "wiremodels" - expect( tempDir ).notToInclude( "wiremodels" ); - // Should end with models/tmp - expect( right( tempDir, 10 ) ).toBe( "models/tmp" ); + // The path should end with /cbwire + expect( tempDir ).toInclude( "/cbwire" ); + // Should use the system temp directory + expect( tempDir ).toInclude( getTempDirectory() ); + }); + + it( "should store file to a specified directory path", function() { + var result = loadMockedFileUpload( "test-store", "text", "plain" ); + var destinationDir = getTempDirectory() & "/cbwire-test-store"; + + // Ensure the test directory exists + if ( !directoryExists( destinationDir ) ) { + directoryCreate( destinationDir ); + } + + // Store the file + var storedPath = result.store( destinationDir ); + + // Verify the file was moved to the destination + expect( fileExists( storedPath ) ).toBeTrue(); + expect( storedPath ).toInclude( destinationDir ); + expect( storedPath ).toInclude( "logo.png" ); + + // Clean up + if ( directoryExists( destinationDir ) ) { + directoryDelete( destinationDir, true ); + } + }); + + it( "should store file to a specified file path", function() { + var result = loadMockedFileUpload( "test-store-file", "text", "plain" ); + var destinationDir = getTempDirectory() & "/cbwire-test-store-file"; + var destinationPath = destinationDir & "/myfile.png"; + + // Store the file with specific filename + var storedPath = result.store( destinationPath ); + + // Verify the file was moved to the destination with the new name + expect( fileExists( storedPath ) ).toBeTrue(); + expect( storedPath ).toBe( getCanonicalPath( destinationPath ) ); + + // Clean up + if ( directoryExists( destinationDir ) ) { + directoryDelete( destinationDir, true ); + } + }); + + it( "should update temporary storage path after store", function() { + var result = loadMockedFileUpload( "test-store-update", "text", "plain" ); + var destinationDir = getTempDirectory() & "/cbwire-test-store-update"; + + // Store the file + var storedPath = result.store( destinationDir ); + + // Verify the temporary storage path was updated + expect( result.getTemporaryStoragePath() ).toBe( storedPath ); + + // Clean up + if ( directoryExists( destinationDir ) ) { + directoryDelete( destinationDir, true ); + } }); }); From 90010dad8b64ef95989a1d8dda2bb258d227ba4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Oct 2025 01:54:09 +0000 Subject: [PATCH 3/5] Add clarifying comments to store() method Co-authored-by: grantcopley <1197835+grantcopley@users.noreply.github.com> --- models/FileUpload.cfc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/models/FileUpload.cfc b/models/FileUpload.cfc index 937bdfcc..f7298902 100644 --- a/models/FileUpload.cfc +++ b/models/FileUpload.cfc @@ -125,7 +125,11 @@ component { /** * Moves the file from temporary storage to a permanent location. * - * @path The destination path where the file should be stored + * The metadata file remains in the uploads temp directory to track the upload state, + * while the actual file is moved to the specified permanent location. This allows + * the FileUpload object to continue tracking the file even after it's been stored. + * + * @path The destination path where the file should be stored (can be a directory or full file path) * @return The absolute path to the stored file */ function store( required string path ){ @@ -148,11 +152,11 @@ component { // Update the temporary storage path to the new location variables.temporaryStoragePath = destinationPath; - // Update metadata + // Update metadata to reflect new file location variables.meta.serverDirectory = destinationDir; variables.meta.serverFile = getFileFromPath( destinationPath ); - // Update the metadata file + // Update the metadata file (stays in temp directory for upload tracking) fileWrite( getMetaPath(), serializeJSON( variables.meta ) ); return destinationPath; From bf6573791eafdf3a5b3df6edd61e4b7ab9522c4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Oct 2025 01:55:29 +0000 Subject: [PATCH 4/5] Add test for destroy() after store() to ensure cleanup works correctly Co-authored-by: grantcopley <1197835+grantcopley@users.noreply.github.com> --- test-harness/tests/specs/FileUploadSpec.cfc | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test-harness/tests/specs/FileUploadSpec.cfc b/test-harness/tests/specs/FileUploadSpec.cfc index 2e9f1a26..aac746ca 100644 --- a/test-harness/tests/specs/FileUploadSpec.cfc +++ b/test-harness/tests/specs/FileUploadSpec.cfc @@ -168,6 +168,28 @@ component extends="coldbox.system.testing.BaseTestCase" { } }); + it( "should be able to destroy file after store", function() { + var result = loadMockedFileUpload( "test-store-destroy", "text", "plain" ); + var destinationDir = getTempDirectory() & "/cbwire-test-store-destroy"; + + // Store the file + var storedPath = result.store( destinationDir ); + + // Verify file exists at new location + expect( fileExists( storedPath ) ).toBeTrue(); + + // Now destroy should delete from the new location + result.destroy(); + + // Verify file was deleted + expect( fileExists( storedPath ) ).toBeFalse(); + + // Clean up directory + if ( directoryExists( destinationDir ) ) { + directoryDelete( destinationDir, true ); + } + }); + }); } From 170b3480fc9bf1ac8c4faf9c99988d69bac35719 Mon Sep 17 00:00:00 2001 From: Grant Copley Date: Sat, 11 Oct 2025 21:47:45 -0500 Subject: [PATCH 5/5] Fixes for file uploads --- models/Component.cfc | 9 +++-- .../resources/fileupload_metadata.json | 1 - test-harness/tests/resources/logo_test.png | Bin 0 -> 21625 bytes test-harness/tests/specs/CBWIRESpec.cfc | 2 +- test-harness/tests/specs/FileUploadSpec.cfc | 32 +++++++++++++----- 5 files changed, 32 insertions(+), 12 deletions(-) delete mode 100644 test-harness/resources/fileupload_metadata.json create mode 100644 test-harness/tests/resources/logo_test.png diff --git a/models/Component.cfc b/models/Component.cfc index 84e02673..0c5132f1 100644 --- a/models/Component.cfc +++ b/models/Component.cfc @@ -466,8 +466,13 @@ component output="true" accessors="true" { } ); } else { var initialState = variables._initialDataProperties; - // Reset individual property - variables.data[ arguments.property ] = initialState[ arguments.property ]; + // Reset individual property (only if it exists in initial state) + if ( initialState.keyExists( arguments.property ) ) { + variables.data[ arguments.property ] = initialState[ arguments.property ]; + } else { + // Property doesn't exist in initial state, set to empty string + variables.data[ arguments.property ] = ""; + } } } diff --git a/test-harness/resources/fileupload_metadata.json b/test-harness/resources/fileupload_metadata.json deleted file mode 100644 index 8a712e02..00000000 --- a/test-harness/resources/fileupload_metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"uuid":"test","fileSize":1234,"contentType":"text","contentSubType":"plain","serverDirectory":"/Users/grantcopley/Code/ortus/cbwire/cbwire/test-harness/tests/resources","serverFile":"logo.png"} \ No newline at end of file diff --git a/test-harness/tests/resources/logo_test.png b/test-harness/tests/resources/logo_test.png new file mode 100644 index 0000000000000000000000000000000000000000..1febd07e53f3f62ae4b83d71b6b20522fe7b2897 GIT binary patch literal 21625 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 Srcg})0000