diff --git a/CHANGELOG.md b/CHANGELOG.md index 746386293..748add63b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th ## [UNRELEASED] -No user facing changes. +- Add support for SHA-256 Git object IDs. [#3893](https://github.com/github/codeql-action/pull/3893) ## 4.35.4 - 07 May 2026 diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index 0f1b66059..217066b95 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -127527,7 +127527,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 7b3ec243c..34d103b7e 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -89819,7 +89819,7 @@ var determineBaseBranchHeadCommitOid = async function(checkoutPathOverride) { } } } - if (commitOid === mergeSha && headOid.length === 40 && baseOid.length === 40) { + if (commitOid === mergeSha && (headOid.length === 40 || headOid.length === 64) && (baseOid.length === 40 || baseOid.length === 64)) { return baseOid; } return void 0; @@ -89885,7 +89885,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index be61bdeab..1a63724e0 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -86338,7 +86338,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/init-action-post.js b/lib/init-action-post.js index b972b1ece..c4c26ac80 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -130981,7 +130981,7 @@ var determineBaseBranchHeadCommitOid = async function(checkoutPathOverride) { } } } - if (commitOid === mergeSha && headOid.length === 40 && baseOid.length === 40) { + if (commitOid === mergeSha && (headOid.length === 40 || headOid.length === 64) && (baseOid.length === 40 || baseOid.length === 64)) { return baseOid; } return void 0; @@ -131047,7 +131047,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/init-action.js b/lib/init-action.js index b7cdc23b7..7a29bba7a 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -87416,7 +87416,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index e1fa46a53..ba08c8fc4 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -86331,7 +86331,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/setup-codeql-action.js b/lib/setup-codeql-action.js index e86bbb192..c5fcd3ae5 100644 --- a/lib/setup-codeql-action.js +++ b/lib/setup-codeql-action.js @@ -86179,7 +86179,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/upload-lib.js b/lib/upload-lib.js index f1f90b4c2..bdfff78ab 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -89427,7 +89427,7 @@ var determineBaseBranchHeadCommitOid = async function(checkoutPathOverride) { } } } - if (commitOid === mergeSha && headOid.length === 40 && baseOid.length === 40) { + if (commitOid === mergeSha && (headOid.length === 40 || headOid.length === 64) && (baseOid.length === 40 || baseOid.length === 64)) { return baseOid; } return void 0; @@ -89493,7 +89493,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 75e8744be..0c24f28ec 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -89098,7 +89098,7 @@ var determineBaseBranchHeadCommitOid = async function(checkoutPathOverride) { } } } - if (commitOid === mergeSha && headOid.length === 40 && baseOid.length === 40) { + if (commitOid === mergeSha && (headOid.length === 40 || headOid.length === 64) && (baseOid.length === 40 || baseOid.length === 64)) { return baseOid; } return void 0; @@ -89164,7 +89164,7 @@ var getFileOidsUnderPath = async function(basePath) { "Cannot list Git OIDs of tracked files." ); const fileOidMap = {}; - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex); diff --git a/src/git-utils.test.ts b/src/git-utils.test.ts index 8804e1f99..591058c0d 100644 --- a/src/git-utils.test.ts +++ b/src/git-utils.test.ts @@ -305,6 +305,29 @@ test.serial("determineBaseBranchHeadCommitOid other error", async (t) => { infoStub.restore(); }); +test.serial( + "determineBaseBranchHeadCommitOid accepts SHA-256 OIDs", + async (t) => { + const mergeSha = "a".repeat(64); + const baseOid = "b".repeat(64); + const headOid = "c".repeat(64); + + process.env["GITHUB_EVENT_NAME"] = "pull_request"; + process.env["GITHUB_SHA"] = mergeSha; + + const runGitCommandStub = sinon + .stub(gitUtils as any, "runGitCommand") + .resolves(`commit ${mergeSha}\nparent ${baseOid}\nparent ${headOid}\n`); + + try { + const result = await gitUtils.determineBaseBranchHeadCommitOid(__dirname); + t.deepEqual(result, baseOid); + } finally { + runGitCommandStub.restore(); + } + }, +); + test.serial("decodeGitFilePath unquoted strings", async (t) => { t.deepEqual(gitUtils.decodeGitFilePath("foo"), "foo"); t.deepEqual(gitUtils.decodeGitFilePath("foo bar"), "foo bar"); @@ -436,6 +459,64 @@ test.serial("getFileOidsUnderPath handles quoted paths", async (t) => { }); }); +test.serial("getFileOidsUnderPath handles SHA-256 OIDs", async (t) => { + await withTmpDir(async (tmpDir) => { + const sha256OidA = + "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2c0d4b7e8f9a1234567890ab"; + const sha256OidB = + "aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"; + + sinon + .stub(gitUtils as any, "runGitCommand") + .callsFake(async (_cwd: any, args: any) => { + if (args[0] === "rev-parse") { + return `${tmpDir}\n`; + } + return ( + `100644 ${sha256OidA} 0\tlib/sha256-file-a.js\n` + + `100644 ${sha256OidB} 0\tsrc/sha256-file-b.ts` + ); + }); + + const result = await gitUtils.getFileOidsUnderPath("/fake/path"); + + t.deepEqual(result, { + "lib/sha256-file-a.js": sha256OidA, + "src/sha256-file-b.ts": sha256OidB, + }); + }); +}); + +test.serial( + "getFileOidsUnderPath rejects OIDs of unsupported length", + async (t) => { + await withTmpDir(async (tmpDir) => { + // 50-char OID: not a valid SHA-1 (40) or SHA-256 (64) length. The regex + // must not accept this even though every character is a valid hex digit. + const invalidLine = + "100644 30d998ded095371488be3a729eb61d86ed721a1830d998ded0 0\tlib/bad.js"; + sinon + .stub(gitUtils as any, "runGitCommand") + .callsFake(async (_cwd: any, args: any) => { + if (args[0] === "rev-parse") { + return `${tmpDir}\n`; + } + return invalidLine; + }); + + await t.throwsAsync( + async () => { + await gitUtils.getFileOidsUnderPath("/fake/path"); + }, + { + instanceOf: Error, + message: `Unexpected "git ls-files" output: ${invalidLine}`, + }, + ); + }); + }, +); + test.serial("getFileOidsUnderPath handles empty output", async (t) => { await withTmpDir(async (tmpDir) => { sinon diff --git a/src/git-utils.ts b/src/git-utils.ts index ec0478fa8..68769699b 100644 --- a/src/git-utils.ts +++ b/src/git-utils.ts @@ -163,11 +163,12 @@ export const determineBaseBranchHeadCommitOid = async function ( } } - // Let's confirm our assumptions: We had a merge commit and the parsed parent data looks correct + // Let's confirm our assumptions: We had a merge commit and the parsed parent + // data looks correct. OIDs are either 40 (SHA-1) or 64 (SHA-256) hex characters. if ( commitOid === mergeSha && - headOid.length === 40 && - baseOid.length === 40 + (headOid.length === 40 || headOid.length === 64) && + (baseOid.length === 40 || baseOid.length === 64) ) { return baseOid; } @@ -296,7 +297,8 @@ export const getFileOidsUnderPath = async function ( // 100644 4c51bc1d9e86cd86e01b0f340cb8ce095c33b283 0\tsrc/git-utils.test.ts // 100644 6b792ea543ce75d7a8a03df591e3c85311ecb64f 0\tsrc/git-utils.ts // The fields are: \t - const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/; + // The OID is either 40 (SHA-1) or 64 (SHA-256) hex characters. + const regex = /^[0-9]+ ([0-9a-f]{40}(?:[a-f0-9]{24})?) [0-9]+\t(.+)$/; for (const line of stdout.split("\n")) { if (line) { const match = line.match(regex);