Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/actions/version-bump-summary/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Version Bump Summary
description: Generates a JSON summary of all packages bumped in the current commit, with their previous and current versions.

inputs:
ref:
description: Git commit SHA or ref to read tags from. Defaults to HEAD.
required: false
default: HEAD

runs:
using: composite
steps:
- name: Generate version bump summary
shell: bash
run: node ${{ github.action_path }}/index.js ${{ inputs.ref }}
44 changes: 44 additions & 0 deletions .github/actions/version-bump-summary/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const { execSync } = require("child_process");
const fs = require("fs");

const ref = process.argv[2] || "HEAD";

const newTags = execSync(`git tag --points-at ${ref}`)

Check warning

Code scanning / CodeQL

Indirect uncontrolled command line Medium

This command depends on an unsanitized
command-line argument
.

Copilot Autofix

AI 1 day ago

In general, the safest fix is to avoid passing concatenated strings to child_process.exec/execSync and instead use spawn/spawnSync/execFile/execFileSync with an argument array, which bypasses the shell and prevents shell metacharacters from being interpreted. Where input is already an array of arguments, pass it directly; where you receive a single string of arguments, parse it safely rather than simple string concatenation.

For this specific script, the best fix without changing functionality is:

  • Replace the execSync call on line 6 that builds a single string `git tag --points-at ${ref}` with an execFileSync (or equivalent) call that invokes git directly and passes ["tag", "--points-at", ref] as an argument array. This preserves the intended git tag behavior while preventing shell interpretation of ref.
  • Since there is already a require("child_process") destructuring import on line 1, either:
    • extend it to also pull in execFileSync, or
    • refactor slightly to import the module and destructure both functions.
      To minimize change, just add execFileSync to the existing destructuring.
  • The other execSync on line 17 already quotes packageName inside the command and is not using tainted input from process.argv; CodeQL did not flag it, so we leave it unchanged per the instructions to avoid altering behavior unnecessarily.

Concretely, in .github/actions/version-bump-summary/index.js:

  • Update line 1 to const { execSync, execFileSync } = require("child_process");.
  • Replace the execSync call on line 6 with execFileSync("git", ["tag", "--points-at", ref]).

No additional helper methods are needed beyond importing execFileSync.

Suggested changeset 1
.github/actions/version-bump-summary/index.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/actions/version-bump-summary/index.js b/.github/actions/version-bump-summary/index.js
--- a/.github/actions/version-bump-summary/index.js
+++ b/.github/actions/version-bump-summary/index.js
@@ -1,9 +1,9 @@
-const { execSync } = require("child_process");
+const { execSync, execFileSync } = require("child_process");
 const fs = require("fs");
 
 const ref = process.argv[2] || "HEAD";
 
-const newTags = execSync(`git tag --points-at ${ref}`)
+const newTags = execFileSync("git", ["tag", "--points-at", ref])
   .toString()
   .trim()
   .split("\n")
EOF
@@ -1,9 +1,9 @@
const { execSync } = require("child_process");
const { execSync, execFileSync } = require("child_process");
const fs = require("fs");

const ref = process.argv[2] || "HEAD";

const newTags = execSync(`git tag --points-at ${ref}`)
const newTags = execFileSync("git", ["tag", "--points-at", ref])
.toString()
.trim()
.split("\n")
Copilot is powered by AI and may make mistakes. Always verify output.
.toString()
.trim()
.split("\n")
.filter(Boolean);

const summary = newTags.map((tag) => {
const atIndex = tag.lastIndexOf("@");
const packageName = tag.slice(0, atIndex);
const currentVersion = tag.slice(atIndex + 1);

const previousTags = execSync(
`git tag --sort=-version:refname -l "${packageName}@*"`
)
.toString()
.trim()
.split("\n")
.filter(Boolean);

const previousTag = previousTags.find((t) => t !== tag);
const previousVersion = previousTag
? previousTag.slice(previousTag.lastIndexOf("@") + 1)
: null;

return { package: packageName, previousVersion, currentVersion };
});

const jsonContent = JSON.stringify(summary, null, 2);
const textContent = summary
.map((entry) => {
const prev = entry.previousVersion || "new";
return `${entry.package}: ${prev} -> ${entry.currentVersion}`;
})
.join("\n");

console.log(textContent);

fs.writeFileSync("/tmp/version-bump-summary.json", jsonContent + "\n");

Check failure

Code scanning / CodeQL

Insecure temporary file High

Insecure creation of file in
the os temp dir
.

Copilot Autofix

AI 1 day ago

In general, to fix insecure temporary file usage, avoid hard-coded filenames in shared temp directories and instead use a library or API that (a) creates the file atomically, (b) ensures it does not already exist, and (c) sets secure permissions. For Node.js, the recommended approach is to use a library like tmp that handles these details safely.

For this specific file, we should replace the direct writes to /tmp/version-bump-summary.json and /tmp/version-bump-summary.txt with secure temporary files created using tmp. Since the rest of the script appears to assume only that the files exist and contain the expected content (and does not depend on specific paths), we can (1) create two secure temporary files with stable prefixes (for debugging clarity) using tmp.fileSync, (2) write the JSON and text content into those file descriptors or paths, and (3) optionally print the generated file paths to stdout so any downstream step can discover them if needed. This preserves functionality (files are written to a temp location) while making the file creation safe.

Concretely:

  • Add const tmp = require("tmp"); near the top alongside other require calls.
  • Replace fs.writeFileSync("/tmp/version-bump-summary.json", ...) with code that calls tmp.fileSync({ prefix: "version-bump-summary-", postfix: ".json" }), then writes to the resulting .name path.
  • Similarly, create a secure temp file for the text output with a .txt postfix.
  • Optionally, log the temp file paths so other tooling can read them, without changing core behavior.

All changes must be confined to .github/actions/version-bump-summary/index.js within the provided snippet.

Suggested changeset 1
.github/actions/version-bump-summary/index.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/actions/version-bump-summary/index.js b/.github/actions/version-bump-summary/index.js
--- a/.github/actions/version-bump-summary/index.js
+++ b/.github/actions/version-bump-summary/index.js
@@ -1,5 +1,6 @@
 const { execSync } = require("child_process");
 const fs = require("fs");
+const tmp = require("tmp");
 
 const ref = process.argv[2] || "HEAD";
 
@@ -40,5 +41,17 @@
 
 console.log(textContent);
 
-fs.writeFileSync("/tmp/version-bump-summary.json", jsonContent + "\n");
-fs.writeFileSync("/tmp/version-bump-summary.txt", textContent + "\n");
+const jsonTempFile = tmp.fileSync({
+  prefix: "version-bump-summary-",
+  postfix: ".json",
+});
+const textTempFile = tmp.fileSync({
+  prefix: "version-bump-summary-",
+  postfix: ".txt",
+});
+
+fs.writeFileSync(jsonTempFile.name, jsonContent + "\n");
+fs.writeFileSync(textTempFile.name, textContent + "\n");
+
+console.log(`JSON summary written to: ${jsonTempFile.name}`);
+console.log(`Text summary written to: ${textTempFile.name}`);
EOF
@@ -1,5 +1,6 @@
const { execSync } = require("child_process");
const fs = require("fs");
const tmp = require("tmp");

const ref = process.argv[2] || "HEAD";

@@ -40,5 +41,17 @@

console.log(textContent);

fs.writeFileSync("/tmp/version-bump-summary.json", jsonContent + "\n");
fs.writeFileSync("/tmp/version-bump-summary.txt", textContent + "\n");
const jsonTempFile = tmp.fileSync({
prefix: "version-bump-summary-",
postfix: ".json",
});
const textTempFile = tmp.fileSync({
prefix: "version-bump-summary-",
postfix: ".txt",
});

fs.writeFileSync(jsonTempFile.name, jsonContent + "\n");
fs.writeFileSync(textTempFile.name, textContent + "\n");

console.log(`JSON summary written to: ${jsonTempFile.name}`);
console.log(`Text summary written to: ${textTempFile.name}`);
Copilot is powered by AI and may make mistakes. Always verify output.
fs.writeFileSync("/tmp/version-bump-summary.txt", textContent + "\n");

Check failure

Code scanning / CodeQL

Insecure temporary file High

Insecure creation of file in
the os temp dir
.

Copilot Autofix

AI 1 day ago

In general, to fix insecure temp file issues, avoid writing to fixed filenames in shared temp directories. Instead, use a library such as tmp to create securely randomized temporary files/directories with appropriate permissions, or derive your own filenames inside a securely created temporary directory.

For this script, the best minimal-impact fix is to create a secure temporary directory using the tmp library and write these two output files into that directory, keeping the filenames (version-bump-summary.json / .txt) so any existing consumers that just care about file names within a known directory can still work, but now under a unique, private temp directory. Concretely:

  • Add an import for tmp.
  • Call tmp.dirSync() once, capturing the generated directory path.
  • Use path.join(tempDir, "version-bump-summary.json") and path.join(tempDir, "version-bump-summary.txt") instead of the hard-coded /tmp/... paths.
    We should also import path since it is not currently imported in this file and is a standard Node module.

All required changes are within .github/actions/version-bump-summary/index.js:

  • Add const tmp = require("tmp"); and const path = require("path"); at the top.
  • After computing jsonContent and textContent, create a secure temp directory: const tempDir = tmp.dirSync({ unsafeCleanup: true }).name;.
  • Replace the two fs.writeFileSync("/tmp/...") calls with calls that write into tempDir using path.join.
Suggested changeset 1
.github/actions/version-bump-summary/index.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/actions/version-bump-summary/index.js b/.github/actions/version-bump-summary/index.js
--- a/.github/actions/version-bump-summary/index.js
+++ b/.github/actions/version-bump-summary/index.js
@@ -1,5 +1,7 @@
 const { execSync } = require("child_process");
 const fs = require("fs");
+const path = require("path");
+const tmp = require("tmp");
 
 const ref = process.argv[2] || "HEAD";
 
@@ -38,7 +40,9 @@
   })
   .join("\n");
 
+const tempDir = tmp.dirSync({ unsafeCleanup: true }).name;
+
 console.log(textContent);
 
-fs.writeFileSync("/tmp/version-bump-summary.json", jsonContent + "\n");
-fs.writeFileSync("/tmp/version-bump-summary.txt", textContent + "\n");
+fs.writeFileSync(path.join(tempDir, "version-bump-summary.json"), jsonContent + "\n");
+fs.writeFileSync(path.join(tempDir, "version-bump-summary.txt"), textContent + "\n");
EOF
@@ -1,5 +1,7 @@
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const tmp = require("tmp");

const ref = process.argv[2] || "HEAD";

@@ -38,7 +40,9 @@
})
.join("\n");

const tempDir = tmp.dirSync({ unsafeCleanup: true }).name;

console.log(textContent);

fs.writeFileSync("/tmp/version-bump-summary.json", jsonContent + "\n");
fs.writeFileSync("/tmp/version-bump-summary.txt", textContent + "\n");
fs.writeFileSync(path.join(tempDir, "version-bump-summary.json"), jsonContent + "\n");
fs.writeFileSync(path.join(tempDir, "version-bump-summary.txt"), textContent + "\n");
Copilot is powered by AI and may make mistakes. Always verify output.
4 changes: 4 additions & 0 deletions .github/workflows/npmjs-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ jobs:
run: |
yarn lerna publish --sign-git-tag --sign-git-commit --include-merged-tags --conventional-commits --conventional-graduate --verify-access --yes

- name: Generate version bump summary
if: inputs.dry-run == false
uses: ./.github/actions/version-bump-summary

- name: Extract published version
if: inputs.dry-run == false
id: extract-version
Expand Down