diff --git a/handwritten/storage/package.json b/handwritten/storage/package.json index 7e68d03dc8ac..3ea3b0fb2644 100644 --- a/handwritten/storage/package.json +++ b/handwritten/storage/package.json @@ -71,7 +71,7 @@ "samples-test": "npm link && cd samples/ && npm link ../ && npm test && cd ../", "system-test:esm": "mocha build/esm/system-test --timeout 600000 --exit", "system-test": "mocha build/cjs/system-test --timeout 600000 --exit", - "test": "cross-env NODE_OPTIONS='--no-deprecation' c8 mocha build/cjs/test" + "test": "cross-env NODE_OPTIONS=\"--require ./scripts/preload-yargs.cjs --no-deprecation\" c8 mocha build/cjs/test" }, "dependencies": { "@google-cloud/paginator": "^5.0.0", @@ -106,7 +106,7 @@ "@types/request": "^2.48.4", "@types/sinon": "^17.0.0", "@types/tmp": "0.2.6", - "@types/yargs": "^17.0.10", + "@types/yargs": "^17.0.35", "c8": "^9.0.0", "form-data": "^4.0.4", "gapic-tools": "^0.4.0", @@ -125,7 +125,7 @@ "path-to-regexp": "6.3.0", "tmp": "^0.2.0", "typescript": "^5.1.6", - "yargs": "^17.3.1", + "yargs": "^17.7.2", "cross-env": "^7.0.3" }, "homepage": "https://github.com/googleapis/google-cloud-node/tree/main/handwritten/storage" diff --git a/handwritten/storage/scripts/preload-yargs.cjs b/handwritten/storage/scripts/preload-yargs.cjs new file mode 100644 index 000000000000..7810c92273bf --- /dev/null +++ b/handwritten/storage/scripts/preload-yargs.cjs @@ -0,0 +1,39 @@ +const Module = require('module'); +const fs = require('fs'); +const path = require('path'); + +const originalResolveFilename = Module._resolveFilename; + +Module._resolveFilename = function(request, parent, isMain, options) { + if (request === 'yargs/yargs') { + const resolved = originalResolveFilename.apply(this, arguments); + if (resolved.endsWith('.mjs')) { + return resolved; + } + // Create a unique shim file in the local scripts directory to avoid shared temp directory vulnerabilities + const safeHash = Buffer.from(resolved).toString('base64').replace(/[^a-zA-Z0-9]/g, ''); + const shimPath = path.join(__dirname, `yargs-shim-${safeHash}.cjs`); + + if (!fs.existsSync(shimPath)) { + const content = fs.readFileSync(resolved, 'utf8'); + // Replace `./build/index.cjs` with the absolute path + const buildIndexPath = path.join(path.dirname(resolved), 'build', 'index.cjs'); + // We must replace ALL relative requires. Luckily yargs/yargs only requires `./build/index.cjs` + const newContent = content.replace(/require\(['"]\.\/build\/index\.cjs['"]\)/g, `require(${JSON.stringify(buildIndexPath)})`); + fs.writeFileSync(shimPath, newContent); + } + + global.__yargsShimCleanups = global.__yargsShimCleanups || new Set(); + if (!global.__yargsShimCleanups.has(shimPath)) { + global.__yargsShimCleanups.add(shimPath); + process.on('exit', () => { + try { + fs.unlinkSync(shimPath); + } catch (e) {} + }); + } + + return shimPath; + } + return originalResolveFilename.apply(this, arguments); +}; \ No newline at end of file diff --git a/handwritten/storage/src/file.ts b/handwritten/storage/src/file.ts index 1e62634e4c64..27765d935a99 100644 --- a/handwritten/storage/src/file.ts +++ b/handwritten/storage/src/file.ts @@ -2187,6 +2187,29 @@ class File extends ServiceObject { // remove temporary noop listener as we now create a pipeline that handles the errors emitStream.removeListener('error', noop); + if (fileWriteStream.destroyed) { + let callbackCalled = false; + const onError = (err: Error) => { + if (!callbackCalled) { + callbackCalled = true; + pipelineCallback(err); + } + }; + fileWriteStream.once('error', onError); + emitStream.destroy(); + + process.nextTick(() => { + fileWriteStream.removeListener('error', onError); + if (!callbackCalled) { + callbackCalled = true; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const err = (fileWriteStream as any).errored || new Error('Write stream destroyed'); + pipelineCallback(err); + } + }); + return; + } + pipeline( emitStream, ...(transformStreams as [Transform]), diff --git a/handwritten/storage/test/file.ts b/handwritten/storage/test/file.ts index 311d5749582d..26823995b907 100644 --- a/handwritten/storage/test/file.ts +++ b/handwritten/storage/test/file.ts @@ -118,7 +118,15 @@ const fakePromisify = { }; const fsCached = fs; -const fakeFs = {...fsCached}; +const safeFs: any = {}; +const descriptors = Object.getOwnPropertyDescriptors(fsCached); +for (const key of Object.keys(descriptors)) { + const desc = descriptors[key]; + if (desc && !desc.get) { + Object.defineProperty(safeFs, key, desc); + } +} +const fakeFs = {...safeFs} as typeof fs; const zlibCached = zlib; let createGunzipOverride: Function | null; @@ -223,7 +231,7 @@ describe('File', () => { }); beforeEach(() => { - Object.assign(fakeFs, fsCached); + Object.assign(fakeFs, safeFs); Object.assign(fakeOs, osCached); // eslint-disable-next-line @typescript-eslint/no-explicit-any FakeServiceObject.prototype.request = util.noop as any;