From e5f6bafcc9b9e179eacda6fa8394888f6629b497 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Wed, 20 Nov 2024 18:06:34 +0000 Subject: [PATCH] fix(github-actions): ensure that the artifacts provided for preview service are not symlinks Detect situations in which provided metadata is a symlink instead of a direct file which could be attempting to exfiltrate secrets. --- .../extract-artifact-metadata.js | 7 ++++++- .../lib/extract-artifact-metadata.ts | 14 +++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/github-actions/previews/upload-artifacts-to-firebase/extract-artifact-metadata.js b/github-actions/previews/upload-artifacts-to-firebase/extract-artifact-metadata.js index a593c16d5..489151c20 100644 --- a/github-actions/previews/upload-artifacts-to-firebase/extract-artifact-metadata.js +++ b/github-actions/previews/upload-artifacts-to-firebase/extract-artifact-metadata.js @@ -19399,7 +19399,12 @@ var artifactMetadata = { async function main() { const [artifactDirPath] = process.argv.slice(2); for (const [key, name] of Object.entries(artifactMetadata)) { - const content = await fs.promises.readFile(path.join(artifactDirPath, name), "utf8"); + const expectedPath = path.join(artifactDirPath, name); + const realPath = await fs.promises.realpath(expectedPath); + if (expectedPath !== realPath) { + throw Error(`Value for unsafe-${key} not stored directly in file as expected, instead stored in ${realPath}`); + } + const content = await fs.promises.readFile(expectedPath, "utf8"); const outputName = `unsafe-${key}`; console.info(`Setting output: ${outputName} = ${content}`); (0, import_core.setOutput)(outputName, content.trim()); diff --git a/github-actions/previews/upload-artifacts-to-firebase/lib/extract-artifact-metadata.ts b/github-actions/previews/upload-artifacts-to-firebase/lib/extract-artifact-metadata.ts index 66550d353..9266d7935 100644 --- a/github-actions/previews/upload-artifacts-to-firebase/lib/extract-artifact-metadata.ts +++ b/github-actions/previews/upload-artifacts-to-firebase/lib/extract-artifact-metadata.ts @@ -23,7 +23,19 @@ async function main() { const [artifactDirPath] = process.argv.slice(2); for (const [key, name] of Object.entries(artifactMetadata)) { - const content = await fs.promises.readFile(path.join(artifactDirPath, name), 'utf8'); + /** The expected path of the artifact */ + const expectedPath = path.join(artifactDirPath, name); + + // We confirm that the provided artifact path is actually in the expected location instead of pointing somewhere + // else to exfiltrate information. + const realPath = await fs.promises.realpath(expectedPath); + if (expectedPath !== realPath) { + throw Error( + `Value for unsafe-${key} not stored directly in file as expected, instead stored in ${realPath}`, + ); + } + + const content = await fs.promises.readFile(expectedPath, 'utf8'); const outputName = `unsafe-${key}`; console.info(`Setting output: ${outputName} = ${content}`);