From 6ce84626abeee15fd27246e40e49e6d17990953b Mon Sep 17 00:00:00 2001 From: Ruslans Tarasovs Date: Mon, 16 Mar 2026 12:38:46 +0200 Subject: [PATCH] Move docs directory validation to after copying README.md Signed-off-by: Ruslans Tarasovs --- .changeset/tough-pots-dream.md | 5 +++++ .../cli-e2e-tests/techdocs-cli.test.ts | 14 ++++++++++++++ .../techdocs-node/src/stages/generate/techdocs.ts | 11 ++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 .changeset/tough-pots-dream.md diff --git a/.changeset/tough-pots-dream.md b/.changeset/tough-pots-dream.md new file mode 100644 index 0000000000..ba652224ab --- /dev/null +++ b/.changeset/tough-pots-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +Fixed bug causing --legacyCopyReadmeMdToIndexMd option to fail if docs directory is not present diff --git a/packages/techdocs-cli/cli-e2e-tests/techdocs-cli.test.ts b/packages/techdocs-cli/cli-e2e-tests/techdocs-cli.test.ts index a2720c934e..d9bba51a74 100644 --- a/packages/techdocs-cli/cli-e2e-tests/techdocs-cli.test.ts +++ b/packages/techdocs-cli/cli-e2e-tests/techdocs-cli.test.ts @@ -60,6 +60,7 @@ jest.setTimeout(timeout * 2); describe('end-to-end', () => { const cwd = path.resolve(__dirname, '../src/example-docs'); + const legacyCwd = path.resolve(__dirname, '../src/legacy-docs'); const entryPoint = path.resolve(__dirname, '../bin/techdocs-cli'); afterEach(async () => { @@ -106,6 +107,19 @@ describe('end-to-end', () => { expect(proc.exit).toEqual(0); }); + it('can generate with --legacyCopyReadmeMdToIndexMd option', async () => { + const proc = await executeCommand( + entryPoint, + ['generate', '--no-docker', '--legacyCopyReadmeMdToIndexMd'], + { + legacyCwd, + timeout, + }, + ); + expect(proc.stdout).toContain('Successfully generated docs'); + expect(proc.exit).toEqual(0); + }); + it('can serve in mkdocs', async () => { const proc = await executeCommand( entryPoint, diff --git a/plugins/techdocs-node/src/stages/generate/techdocs.ts b/plugins/techdocs-node/src/stages/generate/techdocs.ts index 8a764eb391..cc4fb816d1 100644 --- a/plugins/techdocs-node/src/stages/generate/techdocs.ts +++ b/plugins/techdocs-node/src/stages/generate/techdocs.ts @@ -121,11 +121,6 @@ export class TechdocsGenerator implements GeneratorBase { this.options.dangerouslyAllowAdditionalKeys, ); - // Validate that no symlinks in the docs directory point outside the input directory - // This prevents path traversal attacks where malicious symlinks could leak host files - const resolvedDocsDir = path.join(inputDir, docsDir ?? 'docs'); - await validateDocsDirectory(resolvedDocsDir, inputDir); - if (parsedLocationAnnotation) { await patchMkdocsYmlPreBuild( mkdocsYmlPath, @@ -139,6 +134,12 @@ export class TechdocsGenerator implements GeneratorBase { await patchIndexPreBuild({ inputDir, logger: childLogger, docsDir }); } + // Validate that no symlinks in the docs directory point outside the input directory + // This prevents path traversal attacks where malicious symlinks could leak host files + const resolvedDocsDir = path.join(inputDir, docsDir ?? 'docs'); + + await validateDocsDirectory(resolvedDocsDir, inputDir); + // patch the list of mkdocs plugins const defaultPlugins = this.options.defaultPlugins ?? [];