diff --git a/src/utils/__fixtures__/test.zip b/src/utils/__fixtures__/test.zip new file mode 100644 index 00000000..e1a361e2 Binary files /dev/null and b/src/utils/__fixtures__/test.zip differ diff --git a/src/utils/__tests__/system.test.ts b/src/utils/__tests__/system.test.ts index b9993172..0194aa0f 100644 --- a/src/utils/__tests__/system.test.ts +++ b/src/utils/__tests__/system.test.ts @@ -1,16 +1,18 @@ import * as fs from 'fs'; import { logger } from '../../logger'; -import { withTempFile } from '../files'; +import { scan, withTempDir, withTempFile } from '../files'; import { calculateChecksum, + extractZipArchive, hasExecutable, HashAlgorithm, HashOutputFormat, replaceEnvVariable, spawnProcess, } from '../system'; +import { relative, resolve } from 'path'; jest.mock('../../logger'); @@ -174,3 +176,101 @@ describe('isExecutableInPath', () => { expect(hasExecutable('./bin/non-existing-binary')).toBe(false); }); }); + +describe('extractZipArchive', () => { + const testZipPath = resolve(__dirname, '../__fixtures__/test.zip'); + + test('extracts a zip archive', async () => { + await withTempDir(async tmpDir => { + await extractZipArchive(testZipPath, tmpDir); + const files = (await scan(tmpDir)).map(file => relative(tmpDir, file)); + expect(files).toStrictEqual(['Sentry.zip']); + + const innerFilePath = resolve(tmpDir, files[0]) + expect(fs.statSync(innerFilePath).size).toBe(1409323); + }); + }); + + // This is a regression test for a bug in the original implementation based on + // unzipper when running on a new NodeJS v20. + // Extraction of the root archive would succeed but actually the contents + // of the nested archive would be compromised. Following attempt to + // extract the inner archive would yield: "ERROR invalid distance code" + test('extracts archive contained in another achive', async () => { + await withTempDir(async tmpDir => { + await extractZipArchive(testZipPath, tmpDir); + + const innerZipPath = resolve(tmpDir, 'Sentry.zip') + const innerDir = resolve(tmpDir, 'Sentry') + fs.mkdirSync(innerDir) + + await extractZipArchive(innerZipPath, innerDir); + const files = (await scan(innerDir)) + .map(file => relative(innerDir, file).replace(/\\/g, '/')) + .sort(); + expect(files).toStrictEqual([ + "Sentry.psd1", + "Sentry.psm1", + "assemblies-loader.ps1", + "lib/.gitignore", + "lib/net462/Microsoft.Bcl.AsyncInterfaces.dll", + "lib/net462/Microsoft.Bcl.AsyncInterfaces.license", + "lib/net462/Microsoft.Bcl.AsyncInterfaces.version", + "lib/net462/Sentry.dll", + "lib/net462/Sentry.license", + "lib/net462/Sentry.version", + "lib/net462/System.Buffers.dll", + "lib/net462/System.Buffers.license", + "lib/net462/System.Buffers.version", + "lib/net462/System.Collections.Immutable.dll", + "lib/net462/System.Collections.Immutable.license", + "lib/net462/System.Collections.Immutable.version", + "lib/net462/System.Memory.dll", + "lib/net462/System.Memory.license", + "lib/net462/System.Memory.version", + "lib/net462/System.Numerics.Vectors.dll", + "lib/net462/System.Numerics.Vectors.license", + "lib/net462/System.Numerics.Vectors.version", + "lib/net462/System.Reflection.Metadata.dll", + "lib/net462/System.Reflection.Metadata.license", + "lib/net462/System.Reflection.Metadata.version", + "lib/net462/System.Runtime.CompilerServices.Unsafe.dll", + "lib/net462/System.Runtime.CompilerServices.Unsafe.license", + "lib/net462/System.Runtime.CompilerServices.Unsafe.version", + "lib/net462/System.Text.Encodings.Web.dll", + "lib/net462/System.Text.Encodings.Web.license", + "lib/net462/System.Text.Encodings.Web.version", + "lib/net462/System.Text.Json.dll", + "lib/net462/System.Text.Json.license", + "lib/net462/System.Text.Json.version", + "lib/net462/System.Threading.Tasks.Extensions.dll", + "lib/net462/System.Threading.Tasks.Extensions.license", + "lib/net462/System.Threading.Tasks.Extensions.version", + "lib/net462/System.ValueTuple.dll", + "lib/net462/System.ValueTuple.license", + "lib/net462/System.ValueTuple.version", + "lib/net6.0/Sentry.dll", + "lib/net6.0/Sentry.license", + "lib/net6.0/Sentry.version", + "lib/net8.0/Sentry.dll", + "lib/net8.0/Sentry.license", + "lib/net8.0/Sentry.version", + "private/DiagnosticLogger.ps1", + "private/EventUpdater.ps1", + "private/Get-CurrentOptions.ps1", + "private/Get-SentryAssembliesDirectory.ps1", + "private/ScopeIntegration.ps1", + "private/SentryEventProcessor.cs", + "private/SentryEventProcessor.ps1", + "private/StackTraceProcessor.ps1", + "public/Add-SentryBreadcrumb.ps1", + "public/Edit-SentryScope.ps1", + "public/Invoke-WithSentry.ps1", + "public/Out-Sentry.ps1", + "public/Start-Sentry.ps1", + "public/Start-SentryTransaction.ps1", + "public/Stop-Sentry.ps1", + ]); + }); + }); +});