1+ #!/usr/bin/env node
2+
3+ // Suppress AWS SDK v2 maintenance mode warnings
4+ process . env . AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE = '1' ;
5+
6+ const AWS = require ( 'aws-sdk' ) ;
7+ const crypto = require ( 'crypto' ) ;
8+
9+ const CONFIG = {
10+ endpoint : 'http://localhost:8000' ,
11+ accessKeyId : 'accessKey1' ,
12+ secretAccessKey : 'verySecretKey1' ,
13+ region : 'us-east-1' ,
14+ bucketName : 'mpu-bug' ,
15+ partSize : 5 * 1024 * 1024 ,
16+ HTTP : 'http' ,
17+ } ;
18+
19+ AWS . config . update ( {
20+ accessKeyId : CONFIG . accessKeyId ,
21+ secretAccessKey : CONFIG . secretAccessKey ,
22+ region : CONFIG . region ,
23+ sslEnabled : true ,
24+ s3ForcePathStyle : true ,
25+ httpOptions : {
26+ timeout : 30000 ,
27+ agent : require ( 'http' ) . Agent ( {
28+ rejectUnauthorized : false ,
29+ keepAlive : true ,
30+ maxSockets : 50
31+ } )
32+ }
33+ } ) ;
34+ const s3 = new AWS . S3 ( { endpoint : CONFIG . endpoint } ) ;
35+
36+ class SimpleMPURaceTester {
37+ constructor ( ) {
38+ this . templateObjectKey = `template-object-${ Date . now ( ) } ` ;
39+ this . templateObjectSize = 15 * 1024 * 1024 ; // 15MB for 3 parts of 5MB each
40+ }
41+
42+ generateTestData ( size = CONFIG . partSize ) {
43+ return crypto . randomBytes ( size ) ;
44+ }
45+
46+ async checkBucket ( ) {
47+ try {
48+ await s3 . headBucket ( { Bucket : CONFIG . bucketName } ) . promise ( ) ;
49+ console . log ( `✅ Bucket accessible: ${ CONFIG . bucketName } ` ) ;
50+ return true ;
51+ } catch ( error ) {
52+ console . error ( `❌ Cannot access bucket: ${ error . message } ` ) ;
53+ return false ;
54+ }
55+ }
56+
57+ async createTemplateObject ( ) {
58+ console . log ( `📄 Creating template object (${ Math . round ( this . templateObjectSize / 1024 / 1024 ) } MB)...` ) ;
59+ try {
60+ const templateData = this . generateTestData ( this . templateObjectSize ) ;
61+ await s3 . putObject ( {
62+ Bucket : CONFIG . bucketName ,
63+ Key : this . templateObjectKey ,
64+ Body : templateData ,
65+ ContentType : 'application/octet-stream'
66+ } ) . promise ( ) ;
67+
68+ await s3 . headObject ( {
69+ Bucket : CONFIG . bucketName ,
70+ Key : this . templateObjectKey
71+ } ) . promise ( ) ;
72+
73+ console . log ( `✅ Template object ready: ${ this . templateObjectKey } ` ) ;
74+ return true ;
75+ } catch ( error ) {
76+ console . error ( `❌ Template creation failed: ${ error . message } ` ) ;
77+ return false ;
78+ }
79+ }
80+
81+ async uploadPartCopy ( key , uploadId , partNumber , startByte , endByte ) {
82+ const params = {
83+ Bucket : CONFIG . bucketName ,
84+ Key : key ,
85+ UploadId : uploadId ,
86+ PartNumber : partNumber ,
87+ CopySource : `${ CONFIG . bucketName } /${ this . templateObjectKey } ` ,
88+ CopySourceRange : `bytes=${ startByte } -${ endByte } `
89+ } ;
90+
91+ try {
92+ const result = await s3 . uploadPartCopy ( params ) . promise ( ) ;
93+ return { ETag : result . ETag , PartNumber : partNumber } ;
94+ } catch ( error ) {
95+ console . error ( `❌ uploadPartCopy failed for part ${ partNumber } : ${ error . message } ` ) ;
96+ console . error ( ` Copy source: ${ CONFIG . bucketName } /${ this . templateObjectKey } ` ) ;
97+ console . error ( ` Range: bytes=${ startByte } -${ endByte } ` ) ;
98+ throw error ;
99+ }
100+ }
101+
102+ // Enhanced vulnerability detection
103+ async detectVulnerability ( objectKey ) {
104+ const versions = await s3 . listObjectVersions ( {
105+ Bucket : CONFIG . bucketName ,
106+ Prefix : objectKey
107+ } ) . promise ( ) ;
108+
109+
110+ const objectVersions = versions . Versions ?. filter ( v => v . Key === objectKey ) || [ ] ;
111+ console . log ( "VERSIONS " , objectVersions )
112+ console . log ( `📊 Check: ${ objectVersions . length } versions` ) ;
113+
114+ if ( objectVersions . length >= 2 ) {
115+ console . log ( `🎯 MULTIPLE VERSIONS DETECTED! Preserving immediately...` ) ;
116+ objectVersions . forEach ( ( v , i ) => {
117+ console . log ( ` Version ${ i + 1 } : ${ v . VersionId } - ETag: ${ v . ETag } - Modified: ${ v . LastModified } ` ) ;
118+ } ) ;
119+ }
120+
121+ return objectVersions
122+ }
123+
124+ async createMPU ( testId ) {
125+ const objectKey = `race-test-${ testId } ` ;
126+
127+ const createResult = await s3 . createMultipartUpload ( {
128+ Bucket : CONFIG . bucketName ,
129+ Key : objectKey ,
130+ ContentType : 'application/octet-stream'
131+ } ) . promise ( ) ;
132+
133+ const uploadId = createResult . UploadId ;
134+
135+ const numParts = 3 ;
136+ const partPromises = [ ] ;
137+
138+ for ( let partNum = 1 ; partNum <= numParts ; partNum ++ ) {
139+ const startByte = ( partNum - 1 ) * CONFIG . partSize ;
140+ const endByte = startByte + CONFIG . partSize - 1 ;
141+ const copyPromise = this . uploadPartCopy ( objectKey , uploadId , partNum , startByte , endByte ) ;
142+ partPromises . push ( copyPromise ) ;
143+ }
144+
145+ const parts = await Promise . all ( partPromises ) ;
146+ const partsList = parts . map ( part => ( {
147+ ETag : part . ETag ,
148+ PartNumber : part . PartNumber
149+ } ) ) ;
150+
151+ return { objectKey, uploadId, partsList } ;
152+ }
153+
154+ async completeMultipartUpload ( objectKey , uploadId , partsList ) {
155+ try {
156+ console . log ( `📦 Completing multipart upload for ${ objectKey } ...` ) ;
157+ return await s3 . completeMultipartUpload ( {
158+ Bucket : CONFIG . bucketName ,
159+ Key : objectKey ,
160+ UploadId : uploadId ,
161+ MultipartUpload : { Parts : partsList }
162+ } ) . promise ( ) ;
163+ } catch ( error ) {
164+ console . error ( `❌ Error during completion: ${ error . message } ` ) ;
165+ }
166+ }
167+
168+ async createVersionedBucket ( bucketName ) {
169+ try {
170+ console . log ( `📦 Creating versioned bucket: ${ bucketName } ...` ) ;
171+ await s3 . createBucket ( { Bucket : bucketName } ) . promise ( ) ;
172+ await s3 . putBucketVersioning ( {
173+ Bucket : bucketName ,
174+ VersioningConfiguration : {
175+ Status : 'Enabled'
176+ }
177+ } ) . promise ( ) ;
178+ console . log ( `✅ Versioned bucket created: ${ bucketName } ` ) ;
179+ } catch ( error ) {
180+ console . error ( `❌ Error creating versioned bucket: ${ error } ` ) ;
181+ }
182+ }
183+
184+ async deleteObjectByVersionID ( objectKey , versionId ) {
185+ await s3 . deleteObject ( {
186+ Bucket : CONFIG . bucketName ,
187+ Key : objectKey ,
188+ VersionId : versionId
189+ } ) . promise ( ) ;
190+ }
191+
192+ async getObjectByVersionID ( objectKey , versionId ) {
193+ try {
194+ const result = await s3 . getObject ( {
195+ Bucket : CONFIG . bucketName ,
196+ Key : objectKey ,
197+ VersionId : versionId
198+ } ) . promise ( ) ;
199+ return result . Body ;
200+ } catch ( error ) {
201+ console . error ( `❌ Error getting object by version ID: ${ error . message } ` ) ;
202+ throw error ;
203+ }
204+ }
205+ }
206+
207+ async function main ( ) {
208+ const tester = new SimpleMPURaceTester ( ) ;
209+ console . log ( '🔄 Starting testing...' ) ;
210+
211+ await tester . createVersionedBucket ( CONFIG . bucketName ) ;
212+
213+ if ( ! ( await tester . checkBucket ( ) ) ) {
214+ throw new Error ( 'Cannot access bucket' ) ;
215+ }
216+
217+ await tester . createTemplateObject ( ) ;
218+ const testId = `test-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) } ` ;
219+
220+ const { objectKey, uploadId, partsList } = await tester . createMPU ( testId ) ;
221+ console . log ( 'objectkey:' , objectKey ) ;
222+ console . log ( 'uploadid:' , uploadId ) ;
223+ console . log ( 'partlist:' , partsList ) ;
224+ const [ result1 , result2 ] = await Promise . all ( [
225+ tester . completeMultipartUpload ( objectKey , uploadId , partsList ) ,
226+ tester . completeMultipartUpload ( objectKey , uploadId , partsList ) ,
227+ ] ) ;
228+
229+ // await tester.completeMultipartUpload(objectKey, uploadId, partsList)
230+
231+ console . log ( `✅ Multipart upload completed: ${ result1 } and ${ result2 } ` ) ;
232+ console . log ( "✅ Multipart upload completed:" , result1 ) ;
233+ console . log ( "✅ Multipart upload completed:" , result2 ) ;
234+
235+ // const objectVersions = await tester.detectVulnerability(objectKey);
236+ // await tester.deleteObjectByVersionID(objectKey, objectVersions[0].VersionId)
237+
238+ // // console.log("2ND DETECTION")
239+ // await tester.detectVulnerability(objectKey);
240+
241+ // await tester.getObjectByVersionID(objectKey, objectVersions[1].VersionId)
242+ }
243+
244+ main ( ) ;
0 commit comments