Repository navigation
Conversation
990a119 to
90592ea
Compare
dmerand
left a comment
There was a problem hiding this comment.
LGTM with the exception of this issue, which we discussed async.
| await inTemporaryDirectory(async (directory) => { | ||
| writeFileSync(joinPath(directory, 'git.cmd'), '@echo git version 2.55.0\r\n') | ||
|
|
||
| await expect(git.ensureGitVersionIsAtLeast('2.38.0', {cwd: directory})).rejects.toThrow( |
There was a problem hiding this comment.
This new Windows-only regression fails in Windows Node 26 CI: it expects /Skipped run of unsecure binary/, but receives Couldn't determine the installed Git version. The macOS smoke run skips this test.
Could we fix the Windows refusal/test behavior and get the Windows job plus required Unit tests green before landing? Please keep the planted-script regression. Since execa is mocked here, this failure alone does not prove that git.cmd ran.
There was a problem hiding this comment.
Pushed as an additional commit
lopez-mar
left a comment
There was a problem hiding this comment.
approved pending what we discussed async
Add a reusable CLI Kit Git version guard. App security checks the Git selected in a directory before every Git command it runs there, because a version manager can select Git by working directory. Git 2.38 is required for the protections used when reading untrusted repositories.
The check that refuses a command found in the working directory joined that directory to PATH with pathe's `:` delimiter, but `which` splits on `;` on Windows, so the directory was never searched. Look a bare command name up as a file in the working directory instead, which also handles either path separator and a directory whose name contains the delimiter. A command given as a path runs the file the caller named, so it isn't checked.
Git's empty transport allow-list still allows a helper with an empty name, which a remote gets from an empty `vcs` or a URL that starts with `::`. Git runs it as `git remote-`, which falls back to the repository's `remote-` alias. Replace that alias on every Git command, for Git versions that can't turn off lazy fetching.
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/public/node/git.d.ts@@ -102,6 +102,17 @@ export declare function getHeadSymbolicRef(directory?: string): Promise<string>;
* an abort error.
*/
export declare function ensureGitIsPresentOrAbort(): Promise<void>;
+export interface GitVersionCheckOptions {
+ /** The directory to run Git in. A version manager can select a different Git for each working directory. */
+ cwd?: string;
+}
+/**
+ * Aborts when Git isn't installed, can't run, reports an unrecognized version, or is older than the required minimum.
+ *
+ * @param minimumVersion - The oldest supported Git version, in semantic version format.
+ * @param options - Where to run Git.
+ */
+export declare function ensureGitVersionIsAtLeast(minimumVersion: string, options?: GitVersionCheckOptions): Promise<void>;
export declare class OutsideGitDirectoryError extends AbortError {
}
/**
|
WHY are these changes introduced?
shopify app security checkruns Git while inspecting untrusted repositories. Its protections rely onsafe.bareRepository, which was introduced in Git 2.38. Older versions can't enforce the complete protection set.WHAT is this pull request doing?
Require Git 2.38 or later before every Git command app security runs. A version manager can select a different Git for each working directory, so the version is checked in the directory where each command runs. The check runs Git the same way the scan does, so it also refuses a
gitexecutable in the scanned directory.A scan that doesn't use Git, such as
--no-git-ignorewith no Git-dependent secret candidate, still runs without it.When Git is missing, can't run, reports an unrecognized version, or is too old, the command stops with an actionable error. The version guard lives in CLI Kit for reuse, but only app security enforces it today. Other Git-using commands are unchanged.
Git versions before 2.45 can't turn off lazy fetching, and their empty transport allow-list still allows a helper with an empty name, which a remote gets from an empty
vcsor a URL that starts with::. Git runs that helper through the repository'sremote-alias, so every Git command now also replaces that alias.This also fixes CLI Kit's check that refuses a program found in the working directory, which never worked on Windows: it joined the directory to
PATHwith:, and Windows splitsPATHon;. It now looks up a bare command name as a file in the working directory, so either path separator works. A command given as a path, such as./build.sh, runs the file the caller named and isn't checked. This applies to every command the CLI runs.How to manually test your changes?
Create a temporary app and a Git executable that reports an unsupported version:
Run file listing with default Git filtering:
Confirm that it exits before gathering and reports that Git 2.38 or later is required.
Turn off Git filtering:
Confirm that it lists the app files and exits successfully because this scan doesn't use Git.
Replace the fake Git with one that, like a version manager, selects an old Git only in a nested repository:
Confirm that it reports Git 2.37.6 is installed, even though the app directory selects Git 2.55.0.
Remove the temporary directories:
Checklist
patchfor bug fixes ·minorfor new features ·majorfor breaking changes) and added a changeset withpnpm changeset add