diff --git a/plugins/techdocs-node/src/stages/generate/helpers.test.ts b/plugins/techdocs-node/src/stages/generate/helpers.test.ts index 1f198b43e9..511ba970d1 100644 --- a/plugins/techdocs-node/src/stages/generate/helpers.test.ts +++ b/plugins/techdocs-node/src/stages/generate/helpers.test.ts @@ -543,6 +543,48 @@ describe('helpers', () => { ], ]); }); + + it('should use a symlink to README.md if docs/index.md does not exist', async () => { + mockDir.setContent({ + 'information.md': 'information.md content', + 'README.md': ctx => ctx.symlink('./information.md'), + }); + + await patchIndexPreBuild({ inputDir: mockDir.path, logger: mockLogger }); + + await expect( + fs.readFile(mockDir.resolve('docs/index.md'), 'utf-8'), + ).resolves.toEqual('information.md content'); + expect(warn.mock.calls).toEqual([ + [`${path.normalize('docs/index.md')} not found.`], + [`${path.normalize('docs/README.md')} not found.`], + [`${path.normalize('docs/readme.md')} not found.`], + ]); + }); + + it('should reject a symlink pointing outside of the current directory', async () => { + const anotherMockDir = createMockDirectory(); + + mockDir.setContent({ + 'information.md': 'information.md content', + 'README.md': ctx => ctx.symlink(anotherMockDir.resolve('tmp/secret')), + }); + + anotherMockDir.setContent({ + tmp: { + secret: 'password', + }, + }); + + await expect( + patchIndexPreBuild({ inputDir: mockDir.path, logger: mockLogger }), + ).rejects.toThrow(/not allowed to refer to a location outside/i); + expect(warn.mock.calls).toEqual([ + [`${path.normalize('docs/index.md')} not found.`], + [`${path.normalize('docs/README.md')} not found.`], + [`${path.normalize('docs/readme.md')} not found.`], + ]); + }); }); describe('addBuildTimestampMetadata', () => { diff --git a/plugins/techdocs-node/src/stages/generate/helpers.ts b/plugins/techdocs-node/src/stages/generate/helpers.ts index caecb47524..351fff2f93 100644 --- a/plugins/techdocs-node/src/stages/generate/helpers.ts +++ b/plugins/techdocs-node/src/stages/generate/helpers.ts @@ -399,6 +399,11 @@ export const patchIndexPreBuild = async ({ await fs.ensureDir(docsPath); for (const filePath of fallbacks) { + if (!isChildPath(inputDir, filePath)) { + throw new NotAllowedError( + `Path ${filePath} is not allowed to refer to a location outside ${inputDir}`, + ); + } try { await fs.copyFile(filePath, indexMdPath); return;