diff --git a/packages/rollup-plugin-webbundle/test/test.js b/packages/rollup-plugin-webbundle/test/test.js index 28e7537..00d0228 100644 --- a/packages/rollup-plugin-webbundle/test/test.js +++ b/packages/rollup-plugin-webbundle/test/test.js @@ -182,6 +182,37 @@ test('static with symbolic link throws error', async (t) => { } }); +test('static with directory-symlink loop throws refusal error without ELOOP', async (t) => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wbn-rollup-loop-')); + try { + const loopLink = path.join(tmpDir, 'loop'); + fs.symlinkSync('.', loopLink); + + const bundle = await rollup.rollup({ + input: 'fixtures/index.js', + plugins: [ + webbundle({ + output: 'out.wbn', + static: { dir: tmpDir }, + }), + ], + }); + + const error = await t.throwsAsync( + async () => { + await bundle.generate({ format: 'esm' }); + }, + { instanceOf: Error } + ); + t.is( + error.message, + `Refusing to bundle symbolic link at ${loopLink}. Replace it with a regular file or directory.` + ); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } +}); + test('static with scheme-like directory refuses foreign origin', async (t) => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wbn-rollup-foreign-')); try { diff --git a/packages/shared/test/test.js b/packages/shared/test/test.js index 3aba932..c9cc8ce 100644 --- a/packages/shared/test/test.js +++ b/packages/shared/test/test.js @@ -149,6 +149,33 @@ test('addFilesRecursively - refuses root directory being a symbolic link', (t) = } }); +test('addFilesRecursively - refuses directory-symlink loop (prevents ELOOP)', (t) => { + const tmpDir = fs.mkdtempSync( + path.join(os.tmpdir(), 'wbn-test-symlink-loop-') + ); + try { + const loopLink = path.join(tmpDir, 'loop'); + fs.symlinkSync('.', loopLink); + + const builder = new BundleBuilder(); + const error = t.throws( + () => { + addFilesRecursively(builder, 'https://example.com/', tmpDir, { + baseURL: 'https://example.com/', + output: 'out.wbn', + }); + }, + { instanceOf: Error } + ); + t.is( + error.message, + `Refusing to bundle symbolic link at ${loopLink}. Replace it with a regular file or directory.` + ); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } +}); + test('addAsset - refuses absolute exchange URL when baseURL is empty', (t) => { const builder = new BundleBuilder(); const error = t.throws( diff --git a/packages/webbundle-webpack-plugin/test/test.js b/packages/webbundle-webpack-plugin/test/test.js index 35b1255..8fb856a 100644 --- a/packages/webbundle-webpack-plugin/test/test.js +++ b/packages/webbundle-webpack-plugin/test/test.js @@ -130,6 +130,31 @@ test('static with symbolic link throws error', async (t) => { } }); +test('static with directory-symlink loop throws refusal error without ELOOP', async (t) => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wbn-webpack-loop-')); + try { + const loopLink = path.join(tmpDir, 'loop'); + fs.symlinkSync('.', loopLink); + + const error = await t.throwsAsync( + async () => { + await run({ + output: 'example.wbn', + static: { dir: tmpDir }, + }); + }, + { instanceOf: Error } + ); + t.true( + error.message.includes( + `Refusing to bundle symbolic link at ${loopLink}. Replace it with a regular file or directory.` + ) + ); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } +}); + test('static with scheme-like directory refuses foreign origin', async (t) => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wbn-webpack-foreign-')); try {