Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 9, 2025

This PR upgrades Biome from version 1.9.x to the latest 2.2.5 release, addressing several breaking changes in the Biome 2.x configuration schema and behavior.

Key Changes

1. Package Version Updates

  • Updated @biomejs/biome from ~1.9.3 to ~2.2.5 in both root and build-tools workspaces
  • Updated pnpm-lock.yaml with new dependency versions

2. Configuration Schema Updates

Biome 2.x introduced several breaking changes to the configuration schema:

  • Removed files.ignore and formatter.ignore: These properties are no longer supported. Biome 2.x now relies on VCS integration with .gitignore files (via vcs.useIgnoreFile: true)
  • Renamed include to includes: Property name was pluralized across all override configurations
  • Removed organizeImports top-level config: This functionality moved to the assist system in 2.x
  • Added root property: Root configuration now explicitly sets "root": true, and nested configs set "root": false"

3. Critical Configuration Inheritance Fix

The most significant change in Biome 2.x is how configuration inheritance works:

  • In Biome 1.x, nested configs used the extends directive to reference parent configurations
  • In Biome 2.x, configs with "root": false automatically inherit from parent configs discovered through the filesystem
  • Removed extends directives from 167 nested biome.jsonc files across the repository to fix resolution errors

This change was necessary because Biome 2.x changed how it resolves extends paths, causing "module not found" errors when scanning directories.

4. Auto-Migration and Formatting

  • Ran biome migrate --write to automatically update configuration files
  • Applied Biome 2.2.5 formatting to the entire codebase, formatting 7,153 files
  • Fixed 3,638 files that had formatting differences between versions

Testing

The upgrade maintains the existing code style and formatting rules. All formatting has been applied using Biome 2.2.5 to ensure consistency.

Related Changes

  • Updated package.json scripts to use explicit paths to biome binary (workaround for pnpm binary availability)
  • Updated test data configurations in build-tools to match new schema

Fixes #[issue number if applicable]

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • googlechromelabs.github.io
    • Triggering command: node install.mjs (dns block)
  • https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.87/linux64/chrome-headless-shell-linux64.zip
    • Triggering command: node install.mjs (http block)
    • Triggering command: REDACTED, pid is -1 (http block)
  • https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.87/linux64/chrome-linux64.zip
    • Triggering command: node install.mjs (http block)
    • Triggering command: REDACTED, pid is -1 (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Right now we use version 1.9 of biome to format the main part of our repo, and the build-tools release group. We want to upgrade to the latest biome 2.x release, which has breaking changes, so evaluate those changes in light of our config, then make the necessary changes to upgrade the version of biome in the main workspace (see the root pnpm-workspace.yaml). Run pnpm checks from the root to check formatting -- this should pass as you upgrade, so if formatting changes are made, they should be committed.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits October 9, 2025 22:56
Co-authored-by: tylerbutler <19589+tylerbutler@users.noreply.github.com>
Co-authored-by: tylerbutler <19589+tylerbutler@users.noreply.github.com>
Copilot AI changed the title [WIP] Upgrade biome from version 1.9 to latest 2.x release Upgrade Biome from 1.9.x to 2.2.5 Oct 9, 2025
Copilot AI requested a review from tylerbutler October 9, 2025 23:19
Copilot finished work on behalf of tylerbutler October 9, 2025 23:19
public async getShaForBranch(branch: string, remote?: string) {
const refspec = remote ? `refs/remotes/${remote}/${branch}` : `refs/heads/${branch}`;
const refspec = remote
? `refs/remotes/${remote}/${branch}`

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.

Copilot Autofix

AI 15 days ago

To fix the problem, arguments that may contain user/library-controlled data (the branch and remote values) must not be directly inserted into a shell command string. Instead, the command should be provided to the shell using a safe API, such as child_process.execFile (or an equivalent in the execNoError method implementation). This API accepts the command and arguments as an array, so that they are not processed or split by the shell, and any special characters in arguments will not result in shell injection.

  • Update the call to execNoError("show-ref " + refspec) to instead pass the command as an argument and the arguments as an array: execNoError("show-ref", [refspec]).
  • Ensure the implementation of execNoError actually makes use of a safe process-spawning method, such as child_process.execFile, where the command and argument array are used directly. (If its current implementation only accepts single-string commands, its signature and implementation should also be updated, but only if we have access to change it.)
  • The same logic applies to any similar usages elsewhere in the immediate code blocks.

This fix should be performed in build-tools/packages/build-tools/src/common/gitRepo.ts: update line 49 and any related code to ensure argument separation and eliminate unconstrained string command concatenation.


Suggested changeset 1
build-tools/packages/build-tools/src/common/gitRepo.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/build-tools/packages/build-tools/src/common/gitRepo.ts b/build-tools/packages/build-tools/src/common/gitRepo.ts
--- a/build-tools/packages/build-tools/src/common/gitRepo.ts
+++ b/build-tools/packages/build-tools/src/common/gitRepo.ts
@@ -46,7 +46,7 @@
 		const refspec = remote
 			? `refs/remotes/${remote}/${branch}`
 			: `refs/heads/${branch}`;
-		const result = await this.execNoError(`show-ref ${refspec}`);
+		const result = await this.execNoError("show-ref", [refspec]);
 		if (result) {
 			const line = result.split(/\r?\n/)[0];
 			if (line) {
EOF
@@ -46,7 +46,7 @@
const refspec = remote
? `refs/remotes/${remote}/${branch}`
: `refs/heads/${branch}`;
const result = await this.execNoError(`show-ref ${refspec}`);
const result = await this.execNoError("show-ref", [refspec]);
if (result) {
const line = result.split(/\r?\n/)[0];
if (line) {
Copilot is powered by AI and may make mistakes. Always verify output.
const refspec = remote ? `refs/remotes/${remote}/${branch}` : `refs/heads/${branch}`;
const refspec = remote
? `refs/remotes/${remote}/${branch}`
: `refs/heads/${branch}`;

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.

Copilot Autofix

AI 15 days ago

To fix this issue, we must prevent unsafe interpolation of untrusted arguments like branch and remote into shell commands, which in their current form could be interpreted in unexpected ways by the shell. We should avoid constructing command lines with string concatenation from tainted input. The preferred solution is to provide the arguments to the command as an array, avoiding the shell. In this scenario, we should revise the invocation to use the equivalent of child_process.execFile, passing command arguments (not a single shell command string).

Our code changes will be:

  • In getShaForBranch(branch: string, remote?: string), instead of constructing a single command string, split it into the git command and argument array, e.g. ["show-ref", refspec].
  • Fix the invocation to use the correct utility function (execNoError) that supports such argument passing (if not, refactor it).
  • To support this, the used execNoError implementation (in "./utils") must support argument array form and not always use the shell (execFile or equivalent).
  • We'll only revise code seen (as per the prompt) so changes are confined to the shown regions and method.

Suggested changeset 1
build-tools/packages/build-tools/src/common/gitRepo.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/build-tools/packages/build-tools/src/common/gitRepo.ts b/build-tools/packages/build-tools/src/common/gitRepo.ts
--- a/build-tools/packages/build-tools/src/common/gitRepo.ts
+++ b/build-tools/packages/build-tools/src/common/gitRepo.ts
@@ -46,7 +46,7 @@
 		const refspec = remote
 			? `refs/remotes/${remote}/${branch}`
 			: `refs/heads/${branch}`;
-		const result = await this.execNoError(`show-ref ${refspec}`);
+		const result = await this.execNoError(["show-ref", refspec]);
 		if (result) {
 			const line = result.split(/\r?\n/)[0];
 			if (line) {
EOF
@@ -46,7 +46,7 @@
const refspec = remote
? `refs/remotes/${remote}/${branch}`
: `refs/heads/${branch}`;
const result = await this.execNoError(`show-ref ${refspec}`);
const result = await this.execNoError(["show-ref", refspec]);
if (result) {
const line = result.split(/\r?\n/)[0];
if (line) {
Copilot is powered by AI and may make mistakes. Always verify output.
`tag -l "${pattern}" --sort=-committerdate`,
`get tags ${pattern}`,
);
const tags = results

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.

Copilot Autofix

AI 15 days ago

The single best way to fix this problem is to avoid interpolating untrusted input into the shell command string. For simple commands without pipes/redirects, it is best to pass arguments as an array to the child process, e.g. using execFile. However, since this.exec() is an internal wrapper and the code we can see constructs command strings, the safest fix is to properly shell-escape any input that comes from users or external callers. The shell-quote library is the industry standard for safely quoting such arguments.

Plan:

  • In every place where untrusted arguments (e.g., pattern) are interpolated into a shell command string, use shell-quote to escape the value.
  • Add a new import for shell-quote at the top of the file.
  • Wrap all interpolations of pattern (specifically in line 212 in getAllTags and line 194 in getTags) using shellQuote.quote([pattern]) so that it is safely quoted.

No other code or logic needs to change; the fix is localized to the shell command construction.


Suggested changeset 2
build-tools/packages/build-tools/src/common/gitRepo.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/build-tools/packages/build-tools/src/common/gitRepo.ts b/build-tools/packages/build-tools/src/common/gitRepo.ts
--- a/build-tools/packages/build-tools/src/common/gitRepo.ts
+++ b/build-tools/packages/build-tools/src/common/gitRepo.ts
@@ -6,6 +6,7 @@
 import { parseISO } from "date-fns";
 import registerDebug from "debug";
 import { exec, execNoError } from "./utils";
+import shellQuote from "shell-quote";
 
 const traceGitRepo = registerDebug("fluid-build:gitRepo");
 
@@ -191,7 +192,7 @@
 	 * @param pattern pattern of tags to get
 	 */
 	public async getTags(pattern: string) {
-		return await this.exec(`tag -l ${pattern}`, `get tags ${pattern}`);
+		return await this.exec(`tag -l ${shellQuote.quote([pattern])}`, `get tags ${pattern}`);
 	}
 
 	/**
@@ -209,7 +210,7 @@
 			pattern === undefined || pattern.length === 0
 				? await this.exec(`tag -l --sort=-committerdate`, `get all tags`)
 				: await this.exec(
-						`tag -l "${pattern}" --sort=-committerdate`,
+						`tag -l ${shellQuote.quote([pattern])} --sort=-committerdate`,
 						`get tags ${pattern}`,
 					);
 		const tags = results
EOF
@@ -6,6 +6,7 @@
import { parseISO } from "date-fns";
import registerDebug from "debug";
import { exec, execNoError } from "./utils";
import shellQuote from "shell-quote";

const traceGitRepo = registerDebug("fluid-build:gitRepo");

@@ -191,7 +192,7 @@
* @param pattern pattern of tags to get
*/
public async getTags(pattern: string) {
return await this.exec(`tag -l ${pattern}`, `get tags ${pattern}`);
return await this.exec(`tag -l ${shellQuote.quote([pattern])}`, `get tags ${pattern}`);
}

/**
@@ -209,7 +210,7 @@
pattern === undefined || pattern.length === 0
? await this.exec(`tag -l --sort=-committerdate`, `get all tags`)
: await this.exec(
`tag -l "${pattern}" --sort=-committerdate`,
`tag -l ${shellQuote.quote([pattern])} --sort=-committerdate`,
`get tags ${pattern}`,
);
const tags = results
build-tools/packages/build-tools/package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/build-tools/packages/build-tools/package.json b/build-tools/packages/build-tools/package.json
--- a/build-tools/packages/build-tools/package.json
+++ b/build-tools/packages/build-tools/package.json
@@ -63,7 +63,8 @@
 		"ts-deepmerge": "^7.0.1",
 		"type-fest": "^2.19.0",
 		"typescript": "~5.4.5",
-		"yaml": "^2.6.0"
+		"yaml": "^2.6.0",
+		"shell-quote": "^1.8.3"
 	},
 	"devDependencies": {
 		"@biomejs/biome": "~1.9.3",
EOF
@@ -63,7 +63,8 @@
"ts-deepmerge": "^7.0.1",
"type-fest": "^2.19.0",
"typescript": "~5.4.5",
"yaml": "^2.6.0"
"yaml": "^2.6.0",
"shell-quote": "^1.8.3"
},
"devDependencies": {
"@biomejs/biome": "~1.9.3",
This fix introduces these dependencies
Package Version Security advisories
shell-quote (npm) 1.8.3 None
Copilot is powered by AI and may make mistakes. Always verify output.
`show -s --format=%cI "${gitRef}"`,
`get commit date ${gitRef}`,
)
).trim();

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.

Copilot Autofix

AI 15 days ago

The best way to fix the vulnerability is to avoid constructing a shell command by interpolating untrusted input into the command string and then passing it to a shell for execution. Instead, use child_process.execFile (or similar) to invoke git with arguments supplied as an array, so that arguments are interpreted only as arguments (not as shell commands), preventing injection. In this codebase, the exec method (imported from ./utils) may be acting as a wrapper for running shell commands. To fix this, update the affected call in getCommitDate so that untrusted input is passed as a single argument element and not as part of the shell string. This means forming the command as "show -s --format=%cI", [gitRef].

Assuming the existing exec method can handle array-style arguments for safer execution, update the code to use a parameterized argument array instead of a string. If not, and the exec method cannot be changed here, escape the input using a library like shell-quote. However, the preferred and safest fix is to adapt the input as an array.

Edit lines 298 (getCommitDate method invocation) so that gitRef is passed as a single argument within an argument array, instead of interpolated directly into the command string.


Suggested changeset 1
build-tools/packages/build-tools/src/common/gitRepo.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/build-tools/packages/build-tools/src/common/gitRepo.ts b/build-tools/packages/build-tools/src/common/gitRepo.ts
--- a/build-tools/packages/build-tools/src/common/gitRepo.ts
+++ b/build-tools/packages/build-tools/src/common/gitRepo.ts
@@ -293,9 +293,10 @@
 	 * @returns The commit date of the ref.
 	 */
 	public async getCommitDate(gitRef: string) {
+		// Use argument array to avoid shell interpolation
 		const result = (
 			await this.exec(
-				`show -s --format=%cI "${gitRef}"`,
+				['show', '-s', '--format=%cI', gitRef].join(' '),
 				`get commit date ${gitRef}`,
 			)
 		).trim();
EOF
@@ -293,9 +293,10 @@
* @returns The commit date of the ref.
*/
public async getCommitDate(gitRef: string) {
// Use argument array to avoid shell interpolation
const result = (
await this.exec(
`show -s --format=%cI "${gitRef}"`,
['show', '-s', '--format=%cI', gitRef].join(' '),
`get commit date ${gitRef}`,
)
).trim();
Copilot is powered by AI and may make mistakes. Always verify output.
`push --set-upstream ${remote} ${branchName}`,
`publish branch`,
);
}

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.

Copilot Autofix

AI 15 days ago

To safely construct a shell command with possibly untrusted input, never embed user input directly into a shell command string. Instead, pass user input as separate arguments to the subprocess without letting the shell interpret it. In Node.js, child_process.execFile() or its promisified versions support this type of invocation.

In this code, the exec method should be updated (or a new code path used) to support passing an array of arguments instead of a command string (if it does not already). For the setUpstream function, instead of forming a string, we will construct an array:
['push', '--set-upstream', remote, branchName]
and pass it to a modified exec function or new execFile helper that takes arguments as an array, avoiding shell interpretation entirely.

If the exec wrapper only accepts a string, you'll need to enhance it (or add a new method) to support taking command and argument arrays, or call the safe underlying function directly for this case.

What to change in this file:

  • In setUpstream, construct an argument array instead of a shell command string.
  • Use execFile (or equivalent) instead of exec if possible and available, passing in command and arguments as an array.
  • If exec cannot be safely adapted, define a new wrapper using Node.js's child_process.execFile (or similar) to safely execute the Git command.

Required imports/definitions:

  • You may need to import execFile from Node's child_process (or from ./utils if re-exported there).
  • If using a promisified execFile, adapt accordingly.

Suggested changeset 1
build-tools/packages/build-tools/src/common/gitRepo.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/build-tools/packages/build-tools/src/common/gitRepo.ts b/build-tools/packages/build-tools/src/common/gitRepo.ts
--- a/build-tools/packages/build-tools/src/common/gitRepo.ts
+++ b/build-tools/packages/build-tools/src/common/gitRepo.ts
@@ -304,9 +304,11 @@
 	}
 
 	public async setUpstream(branchName: string, remote: string = "origin") {
-		return await this.exec(
-			`push --set-upstream ${remote} ${branchName}`,
-			`publish branch`,
+		// Use execFile for safe argument separation (no shell interpolation)
+		return await exec(
+			"git",
+			["push", "--set-upstream", remote, branchName],
+			this.resolvedRoot,
 		);
 	}
 
EOF
@@ -304,9 +304,11 @@
}

public async setUpstream(branchName: string, remote: string = "origin") {
return await this.exec(
`push --set-upstream ${remote} ${branchName}`,
`publish branch`,
// Use execFile for safe argument separation (no shell interpolation)
return await exec(
"git",
["push", "--set-upstream", remote, branchName],
this.resolvedRoot,
);
}

Copilot is powered by AI and may make mistakes. Always verify output.
}

if (in_quotedPathSegment.startsWith('"') && in_quotedPathSegment.endsWith('"')) {
if (
in_quotedPathSegment.startsWith('"') &&

Check failure

Code scanning / CodeQL

Double escaping or unescaping High

This replacement may produce '' characters that are double-unescaped
here
.

Copilot Autofix

AI 15 days ago

To fix the double unescaping problem, change the order in which escape sequences are replaced in the unquotePathSegment function. Specifically, unescape double quotes (\\"") before unescaping double backslashes (\\\\\). This ensures that any backslash that is part of an escape sequence for a quote is correctly interpreted. Only edit the relevant lines (380–383) in the function; no new imports, methods, or definitions are needed.

Suggested changeset 1
experimental/PropertyDDS/packages/property-changeset/src/pathHelper.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/experimental/PropertyDDS/packages/property-changeset/src/pathHelper.ts b/experimental/PropertyDDS/packages/property-changeset/src/pathHelper.ts
--- a/experimental/PropertyDDS/packages/property-changeset/src/pathHelper.ts
+++ b/experimental/PropertyDDS/packages/property-changeset/src/pathHelper.ts
@@ -377,10 +377,11 @@
 			);
 
 			// Then we unescape escape symbols
-			in_quotedPathSegment = in_quotedPathSegment.replace(/\\\\/g, "\\");
-
 			// Then we unescape quotes
 			in_quotedPathSegment = in_quotedPathSegment.replace(/\\"/g, '"');
+
+			// Then we unescape escape symbols
+			in_quotedPathSegment = in_quotedPathSegment.replace(/\\\\/g, "\\");
 		}
 
 		return in_quotedPathSegment;
EOF
@@ -377,10 +377,11 @@
);

// Then we unescape escape symbols
in_quotedPathSegment = in_quotedPathSegment.replace(/\\\\/g, "\\");

// Then we unescape quotes
in_quotedPathSegment = in_quotedPathSegment.replace(/\\"/g, '"');

// Then we unescape escape symbols
in_quotedPathSegment = in_quotedPathSegment.replace(/\\\\/g, "\\");
}

return in_quotedPathSegment;
Copilot is powered by AI and may make mistakes. Always verify output.
target = new RegExp(in_obj.source, in_obj.toString().replace(/.*\//, ""));
target = new RegExp(
in_obj.source,
in_obj.toString().replace(/.*\//, ""),

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
Comment on lines 104 to +106
joinSessionMatch =
/(.*)\/v2\.1\/drive(s\/([\dA-Za-z]+))?\/items\/(([\dA-Za-z]+)!\d+)/.exec(pathname);
/(.*)\/v2\.1\/drive(s\/([\dA-Za-z]+))?\/items\/(([\dA-Za-z]+)!\d+)/.exec(
pathname,

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
Comment on lines +105 to +107
/(.*)\/v2\.1\/drive(s\/([\dA-Za-z]+))?\/items\/(([\dA-Za-z]+)!\d+)/.exec(
pathname,
);

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of 'a'.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants