mirror of
https://github.com/github/codeql-action.git
synced 2025-12-19 13:59:30 +08:00
Compare commits
77 Commits
default-se
...
v3.31.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45c373516f | ||
|
|
311b632b9d | ||
|
|
d300581d5e | ||
|
|
7348876640 | ||
|
|
4f34645a82 | ||
|
|
e7c7a2d323 | ||
|
|
f47c8e6a9b | ||
|
|
74951318a2 | ||
|
|
5676d1f64a | ||
|
|
c1bea80e56 | ||
|
|
2d9c0b97af | ||
|
|
827017f97b | ||
|
|
bffd034ab1 | ||
|
|
817dbfb39b | ||
|
|
793f7006bb | ||
|
|
d2e9832330 | ||
|
|
c2e4b7785f | ||
|
|
66d7f51a10 | ||
|
|
497990dfed | ||
|
|
89cb79a131 | ||
|
|
dbf6819ebd | ||
|
|
5af51f4048 | ||
|
|
e439418aab | ||
|
|
249860e323 | ||
|
|
d3ced5c96c | ||
|
|
c12d7c1f2d | ||
|
|
2e2a1cf1ef | ||
|
|
e2cca77d06 | ||
|
|
801a18bea6 | ||
|
|
1c715a714c | ||
|
|
c3d42c5d08 | ||
|
|
9031cd9330 | ||
|
|
f58938aee2 | ||
|
|
1f1c162805 | ||
|
|
7ab96a0e6f | ||
|
|
e3cb86275a | ||
|
|
f94c9befff | ||
|
|
e5971bdba6 | ||
|
|
c5a9d29dc9 | ||
|
|
9f1109665d | ||
|
|
f8f60f3a2b | ||
|
|
f4d10b9ef7 | ||
|
|
5d5cd550d3 | ||
|
|
c6eb09db21 | ||
|
|
09db9044dc | ||
|
|
d3cd47d8d6 | ||
|
|
8e9caa5100 | ||
|
|
23a6333b88 | ||
|
|
c503cb4fbb | ||
|
|
c2805e0a04 | ||
|
|
c0d3370b54 | ||
|
|
ddd0dc746a | ||
|
|
2f607936ce | ||
|
|
37e7dfbaa0 | ||
|
|
d198d2fabf | ||
|
|
9e3918e481 | ||
|
|
7dd1575dac | ||
|
|
28fc48d83c | ||
|
|
12c6008004 | ||
|
|
d3019effb0 | ||
|
|
42213152a8 | ||
|
|
e677e67801 | ||
|
|
5f3f3164ad | ||
|
|
ba42101490 | ||
|
|
f11af5849b | ||
|
|
ba5430dc86 | ||
|
|
13e883e119 | ||
|
|
755f44910c | ||
|
|
948223fe01 | ||
|
|
a37add20d4 | ||
|
|
ab163cf08b | ||
|
|
319796f085 | ||
|
|
bd1ac56295 | ||
|
|
a8d1ac45b9 | ||
|
|
c551c50310 | ||
|
|
01f1a24033 | ||
|
|
b264e15259 |
2
.github/actions/check-sarif/action.yml
vendored
2
.github/actions/check-sarif/action.yml
vendored
@@ -16,5 +16,5 @@ inputs:
|
||||
Comma separated list of query ids that should NOT be included in this SARIF file.
|
||||
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: index.js
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
name: Verify that the best-effort debug artifact scan completed
|
||||
description: Verifies that the best-effort debug artifact scan completed successfully during tests
|
||||
runs:
|
||||
using: node24
|
||||
main: index.js
|
||||
post: post.js
|
||||
@@ -1,2 +0,0 @@
|
||||
// The main step is a no-op, since we can only verify artifact scan completion in the post step.
|
||||
console.log("Will verify artifact scan completion in the post step.");
|
||||
@@ -1,11 +0,0 @@
|
||||
// Post step - runs after the workflow completes, when artifact scan has finished
|
||||
const process = require("process");
|
||||
|
||||
const scanFinished = process.env.CODEQL_ACTION_ARTIFACT_SCAN_FINISHED;
|
||||
|
||||
if (scanFinished !== "true") {
|
||||
console.error("Error: Best-effort artifact scan did not complete. Expected CODEQL_ACTION_ARTIFACT_SCAN_FINISHED=true");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("✓ Best-effort artifact scan completed successfully");
|
||||
1
.github/workflows/__global-proxy.yml
generated
vendored
1
.github/workflows/__global-proxy.yml
generated
vendored
@@ -76,7 +76,6 @@ jobs:
|
||||
- uses: ./../action/analyze
|
||||
env:
|
||||
https_proxy: http://squid-proxy:3128
|
||||
CODEQL_ACTION_TOLERATE_MISSING_GIT_VERSION: true
|
||||
CODEQL_ACTION_TEST_MODE: true
|
||||
container:
|
||||
image: ubuntu:22.04
|
||||
|
||||
@@ -6,11 +6,6 @@ env:
|
||||
# Diff informed queries add an additional query filter which is not yet
|
||||
# taken into account by these tests.
|
||||
CODEQL_ACTION_DIFF_INFORMED_QUERIES: false
|
||||
# Specify overlay enablement manually to ensure stability around the exclude-from-incremental
|
||||
# query filter. Here we only enable for the default code scanning suite.
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS: true
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS_JAVASCRIPT: false
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_JAVASCRIPT: true
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -58,8 +58,6 @@ jobs:
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: '9.x'
|
||||
- name: Assert best-effort artifact scan completed
|
||||
uses: ./../action/.github/actions/verify-debug-artifact-scan-completed
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
|
||||
2
.github/workflows/debug-artifacts-safe.yml
vendored
2
.github/workflows/debug-artifacts-safe.yml
vendored
@@ -54,8 +54,6 @@ jobs:
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: '9.x'
|
||||
- name: Assert best-effort artifact scan completed
|
||||
uses: ./../action/.github/actions/verify-debug-artifact-scan-completed
|
||||
- uses: ./../action/init
|
||||
id: init
|
||||
with:
|
||||
|
||||
39
CHANGELOG.md
39
CHANGELOG.md
@@ -2,64 +2,60 @@
|
||||
|
||||
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
|
||||
|
||||
## [UNRELEASED]
|
||||
## 3.31.9 - 16 Dec 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.9 - 16 Dec 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.8 - 11 Dec 2025
|
||||
## 3.31.8 - 11 Dec 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.8. [#3354](https://github.com/github/codeql-action/pull/3354)
|
||||
|
||||
## 4.31.7 - 05 Dec 2025
|
||||
## 3.31.7 - 05 Dec 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.7. [#3343](https://github.com/github/codeql-action/pull/3343)
|
||||
|
||||
## 4.31.6 - 01 Dec 2025
|
||||
## 3.31.6 - 01 Dec 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.5 - 24 Nov 2025
|
||||
## 3.31.5 - 24 Nov 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.6. [#3321](https://github.com/github/codeql-action/pull/3321)
|
||||
|
||||
## 4.31.4 - 18 Nov 2025
|
||||
## 3.31.4 - 18 Nov 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.3 - 13 Nov 2025
|
||||
## 3.31.3 - 13 Nov 2025
|
||||
|
||||
- CodeQL Action v3 will be deprecated in December 2026. The Action now logs a warning for customers who are running v3 but could be running v4. For more information, see [Upcoming deprecation of CodeQL Action v3](https://github.blog/changelog/2025-10-28-upcoming-deprecation-of-codeql-action-v3/).
|
||||
- Update default CodeQL bundle version to 2.23.5. [#3288](https://github.com/github/codeql-action/pull/3288)
|
||||
|
||||
## 4.31.2 - 30 Oct 2025
|
||||
## 3.31.2 - 30 Oct 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.1 - 30 Oct 2025
|
||||
## 3.31.1 - 30 Oct 2025
|
||||
|
||||
- The `add-snippets` input has been removed from the `analyze` action. This input has been deprecated since CodeQL Action 3.26.4 in August 2024 when this removal was announced.
|
||||
|
||||
## 4.31.0 - 24 Oct 2025
|
||||
## 3.31.0 - 24 Oct 2025
|
||||
|
||||
- Bump minimum CodeQL bundle version to 2.17.6. [#3223](https://github.com/github/codeql-action/pull/3223)
|
||||
- When SARIF files are uploaded by the `analyze` or `upload-sarif` actions, the CodeQL Action automatically performs post-processing steps to prepare the data for the upload. Previously, these post-processing steps were only performed before an upload took place. We are now changing this so that the post-processing steps will always be performed, even when the SARIF files are not uploaded. This does not change anything for the `upload-sarif` action. For `analyze`, this may affect Advanced Setup for CodeQL users who specify a value other than `always` for the `upload` input. [#3222](https://github.com/github/codeql-action/pull/3222)
|
||||
|
||||
## 4.30.9 - 17 Oct 2025
|
||||
## 3.30.9 - 17 Oct 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.3. [#3205](https://github.com/github/codeql-action/pull/3205)
|
||||
- Experimental: A new `setup-codeql` action has been added which is similar to `init`, except it only installs the CodeQL CLI and does not initialize a database. Do not use this in production as it is part of an internal experiment and subject to change at any time. [#3204](https://github.com/github/codeql-action/pull/3204)
|
||||
|
||||
## 4.30.8 - 10 Oct 2025
|
||||
## 3.30.8 - 10 Oct 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.30.7 - 06 Oct 2025
|
||||
## 3.30.7 - 06 Oct 2025
|
||||
|
||||
- [v4+ only] The CodeQL Action now runs on Node.js v24. [#3169](https://github.com/github/codeql-action/pull/3169)
|
||||
No user facing changes.
|
||||
|
||||
## 3.30.6 - 02 Oct 2025
|
||||
|
||||
@@ -295,17 +291,13 @@ No user facing changes.
|
||||
## 3.26.12 - 07 Oct 2024
|
||||
|
||||
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.14.5 and earlier. These versions of CodeQL were discontinued on 24 September 2024 alongside GitHub Enterprise Server 3.10, and will be unsupported by CodeQL Action versions 3.27.0 and later and versions 2.27.0 and later. [#2520](https://github.com/github/codeql-action/pull/2520)
|
||||
|
||||
- If you are using one of these versions, please update to CodeQL CLI version 2.14.6 or later. For instance, if you have specified a custom version of the CLI using the 'tools' input to the 'init' Action, you can remove this input to use the default version.
|
||||
|
||||
- Alternatively, if you want to continue using a version of the CodeQL CLI between 2.13.5 and 2.14.5, you can replace `github/codeql-action/*@v3` by `github/codeql-action/*@v3.26.11` and `github/codeql-action/*@v2` by `github/codeql-action/*@v2.26.11` in your code scanning workflow to ensure you continue using this version of the CodeQL Action.
|
||||
|
||||
## 3.26.11 - 03 Oct 2024
|
||||
|
||||
- _Upcoming breaking change_: Add support for using `actions/download-artifact@v4` to programmatically consume CodeQL Action debug artifacts.
|
||||
|
||||
Starting November 30, 2024, GitHub.com customers will [no longer be able to use `actions/download-artifact@v3`](https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/). Therefore, to avoid breakage, customers who programmatically download the CodeQL Action debug artifacts should set the `CODEQL_ACTION_ARTIFACT_V4_UPGRADE` environment variable to `true` and bump `actions/download-artifact@v3` to `actions/download-artifact@v4` in their workflows. The CodeQL Action will enable this behavior by default in early November and workflows that have not yet bumped `actions/download-artifact@v3` to `actions/download-artifact@v4` will begin failing then.
|
||||
|
||||
This change is currently unavailable for GitHub Enterprise Server customers, as `actions/upload-artifact@v4` and `actions/download-artifact@v4` are not yet compatible with GHES.
|
||||
- Update default CodeQL bundle version to 2.19.1. [#2519](https://github.com/github/codeql-action/pull/2519)
|
||||
|
||||
@@ -428,12 +420,9 @@ No user facing changes.
|
||||
## 3.25.0 - 15 Apr 2024
|
||||
|
||||
- The deprecated feature for extracting dependencies for a Python analysis has been removed. [#2224](https://github.com/github/codeql-action/pull/2224)
|
||||
|
||||
As a result, the following inputs and environment variables are now ignored:
|
||||
|
||||
- The `setup-python-dependencies` input to the `init` Action
|
||||
- The `CODEQL_ACTION_DISABLE_PYTHON_DEPENDENCY_INSTALLATION` environment variable
|
||||
|
||||
We recommend removing any references to these from your workflows. For more information, see the release notes for CodeQL Action v3.23.0 and v2.23.0.
|
||||
- Automatically overwrite an existing database if found on the filesystem. [#2229](https://github.com/github/codeql-action/pull/2229)
|
||||
- Bump the minimum CodeQL bundle version to 2.12.6. [#2232](https://github.com/github/codeql-action/pull/2232)
|
||||
|
||||
@@ -94,6 +94,6 @@ outputs:
|
||||
sarif-id:
|
||||
description: The ID of the uploaded SARIF file.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: "../lib/analyze-action.js"
|
||||
post: "../lib/analyze-action-post.js"
|
||||
|
||||
@@ -15,5 +15,5 @@ inputs:
|
||||
$GITHUB_WORKSPACE as its working directory.
|
||||
required: false
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/autobuild-action.js'
|
||||
|
||||
@@ -165,6 +165,6 @@ outputs:
|
||||
codeql-version:
|
||||
description: The version of the CodeQL binary used for analysis
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/init-action.js'
|
||||
post: '../lib/init-action-post.js'
|
||||
|
||||
42755
lib/analyze-action-post.js
generated
42755
lib/analyze-action-post.js
generated
File diff suppressed because it is too large
Load Diff
36887
lib/analyze-action.js
generated
36887
lib/analyze-action.js
generated
File diff suppressed because it is too large
Load Diff
35546
lib/autobuild-action.js
generated
35546
lib/autobuild-action.js
generated
File diff suppressed because it is too large
Load Diff
42884
lib/init-action-post.js
generated
42884
lib/init-action-post.js
generated
File diff suppressed because it is too large
Load Diff
36987
lib/init-action.js
generated
36987
lib/init-action.js
generated
File diff suppressed because it is too large
Load Diff
35544
lib/resolve-environment-action.js
generated
35544
lib/resolve-environment-action.js
generated
File diff suppressed because it is too large
Load Diff
35538
lib/setup-codeql-action.js
generated
35538
lib/setup-codeql-action.js
generated
File diff suppressed because it is too large
Load Diff
42048
lib/start-proxy-action-post.js
generated
42048
lib/start-proxy-action-post.js
generated
File diff suppressed because it is too large
Load Diff
35907
lib/start-proxy-action.js
generated
35907
lib/start-proxy-action.js
generated
File diff suppressed because it is too large
Load Diff
35582
lib/upload-lib.js
generated
35582
lib/upload-lib.js
generated
File diff suppressed because it is too large
Load Diff
42643
lib/upload-sarif-action-post.js
generated
42643
lib/upload-sarif-action-post.js
generated
File diff suppressed because it is too large
Load Diff
35556
lib/upload-sarif-action.js
generated
35556
lib/upload-sarif-action.js
generated
File diff suppressed because it is too large
Load Diff
986
package-lock.json
generated
986
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.31.10",
|
||||
"version": "3.31.9",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
@@ -24,12 +24,12 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/artifact": "^5.0.1",
|
||||
"@actions/artifact": "^4.0.0",
|
||||
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
|
||||
"@actions/cache": "^5.0.1",
|
||||
"@actions/core": "^2.0.1",
|
||||
"@actions/exec": "^2.0.0",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@actions/cache": "^4.1.0",
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^6.0.0",
|
||||
"@actions/glob": "^0.5.0",
|
||||
"@actions/http-client": "^3.0.0",
|
||||
"@actions/io": "^2.0.0",
|
||||
@@ -51,7 +51,7 @@
|
||||
"@ava/typescript": "6.0.0",
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"@types/archiver": "^7.0.0",
|
||||
@@ -61,7 +61,7 @@
|
||||
"@types/node-forge": "^1.3.14",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/sinon": "^21.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||
"@typescript-eslint/parser": "^8.48.0",
|
||||
"ava": "^6.4.1",
|
||||
"esbuild": "^0.27.1",
|
||||
|
||||
@@ -23,7 +23,6 @@ services:
|
||||
- 3128:3128
|
||||
env:
|
||||
https_proxy: http://squid-proxy:3128
|
||||
CODEQL_ACTION_TOLERATE_MISSING_GIT_VERSION: true
|
||||
steps:
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
|
||||
@@ -21,5 +21,5 @@ outputs:
|
||||
environment:
|
||||
description: The inferred build environment configuration.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/resolve-environment-action.js'
|
||||
|
||||
@@ -35,5 +35,5 @@ outputs:
|
||||
codeql-version:
|
||||
description: The version of the CodeQL binary that was installed.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/setup-codeql-action.js'
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
import test from "ava";
|
||||
|
||||
import { scanArtifactsForTokens } from "./artifact-scanner";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { getRecordingLogger, LoggedMessage } from "./testing-utils";
|
||||
|
||||
test("scanArtifactsForTokens detects GitHub tokens in files", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-"));
|
||||
|
||||
try {
|
||||
// Create a test file with a fake GitHub token
|
||||
const testFile = path.join(tempDir, "test.txt");
|
||||
fs.writeFileSync(
|
||||
testFile,
|
||||
"This is a test file with token ghp_1234567890123456789012345678901234AB",
|
||||
);
|
||||
|
||||
const error = await t.throwsAsync(
|
||||
async () => await scanArtifactsForTokens([testFile], logger),
|
||||
);
|
||||
|
||||
t.regex(
|
||||
error?.message || "",
|
||||
/Found 1 potential GitHub token.*Personal Access Token/,
|
||||
);
|
||||
t.regex(error?.message || "", /test\.txt/);
|
||||
} finally {
|
||||
// Clean up
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("scanArtifactsForTokens handles files without tokens", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-"));
|
||||
|
||||
try {
|
||||
// Create a test file without tokens
|
||||
const testFile = path.join(tempDir, "test.txt");
|
||||
fs.writeFileSync(
|
||||
testFile,
|
||||
"This is a test file without any sensitive data",
|
||||
);
|
||||
|
||||
await t.notThrowsAsync(
|
||||
async () => await scanArtifactsForTokens([testFile], logger),
|
||||
);
|
||||
} finally {
|
||||
// Clean up
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
if (os.platform() !== "win32") {
|
||||
test("scanArtifactsForTokens finds token in debug artifacts", async (t) => {
|
||||
t.timeout(15000); // 15 seconds
|
||||
const messages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(messages, { logToConsole: false });
|
||||
// The zip here is a regression test based on
|
||||
// https://github.com/github/codeql-action/security/advisories/GHSA-vqf5-2xx6-9wfm
|
||||
const testZip = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"src",
|
||||
"testdata",
|
||||
"debug-artifacts-with-fake-token.zip",
|
||||
);
|
||||
|
||||
// This zip file contains a nested structure with a fake token in:
|
||||
// my-db-java-partial.zip/trap/java/invocations/kotlin.9017231652989744319.trap
|
||||
const error = await t.throwsAsync(
|
||||
async () => await scanArtifactsForTokens([testZip], logger),
|
||||
);
|
||||
|
||||
t.regex(
|
||||
error?.message || "",
|
||||
/Found.*potential GitHub token/,
|
||||
"Should detect token in nested zip",
|
||||
);
|
||||
t.regex(
|
||||
error?.message || "",
|
||||
/kotlin\.9017231652989744319\.trap/,
|
||||
"Should report the .trap file containing the token",
|
||||
);
|
||||
|
||||
const logOutput = messages.map((msg) => msg.message).join("\n");
|
||||
t.regex(
|
||||
logOutput,
|
||||
/^Extracting gz file: .*\.gz$/m,
|
||||
"Logs should show that .gz files were extracted",
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,379 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
import * as exec from "@actions/exec";
|
||||
|
||||
import { Logger } from "./logging";
|
||||
import { getErrorMessage } from "./util";
|
||||
|
||||
/**
|
||||
* GitHub token patterns to scan for.
|
||||
* These patterns match various GitHub token formats.
|
||||
*/
|
||||
const GITHUB_TOKEN_PATTERNS = [
|
||||
{
|
||||
name: "Personal Access Token",
|
||||
pattern: /\bghp_[a-zA-Z0-9]{36}\b/g,
|
||||
},
|
||||
{
|
||||
name: "OAuth Access Token",
|
||||
pattern: /\bgho_[a-zA-Z0-9]{36}\b/g,
|
||||
},
|
||||
{
|
||||
name: "User-to-Server Token",
|
||||
pattern: /\bghu_[a-zA-Z0-9]{36}\b/g,
|
||||
},
|
||||
{
|
||||
name: "Server-to-Server Token",
|
||||
pattern: /\bghs_[a-zA-Z0-9]{36}\b/g,
|
||||
},
|
||||
{
|
||||
name: "Refresh Token",
|
||||
pattern: /\bghr_[a-zA-Z0-9]{36}\b/g,
|
||||
},
|
||||
{
|
||||
name: "App Installation Access Token",
|
||||
pattern: /\bghs_[a-zA-Z0-9]{255}\b/g,
|
||||
},
|
||||
];
|
||||
|
||||
interface TokenFinding {
|
||||
tokenType: string;
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
interface ScanResult {
|
||||
scannedFiles: number;
|
||||
findings: TokenFinding[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a file for GitHub tokens.
|
||||
*
|
||||
* @param filePath Path to the file to scan
|
||||
* @param relativePath Relative path for display purposes
|
||||
* @param logger Logger instance
|
||||
* @returns Array of token findings in the file
|
||||
*/
|
||||
function scanFileForTokens(
|
||||
filePath: string,
|
||||
relativePath: string,
|
||||
logger: Logger,
|
||||
): TokenFinding[] {
|
||||
const findings: TokenFinding[] = [];
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, "utf8");
|
||||
|
||||
for (const { name, pattern } of GITHUB_TOKEN_PATTERNS) {
|
||||
const matches = content.match(pattern);
|
||||
if (matches) {
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
findings.push({ tokenType: name, filePath: relativePath });
|
||||
}
|
||||
logger.debug(`Found ${matches.length} ${name}(s) in ${relativePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
return findings;
|
||||
} catch (e) {
|
||||
// If we can't read the file as text, it's likely binary or inaccessible
|
||||
logger.debug(
|
||||
`Could not scan file ${filePath} for tokens: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively extracts and scans archive files (.zip, .gz, .tar.gz).
|
||||
*
|
||||
* @param archivePath Path to the archive file
|
||||
* @param relativeArchivePath Relative path of the archive for display
|
||||
* @param extractDir Directory to extract to
|
||||
* @param logger Logger instance
|
||||
* @param depth Current recursion depth (to prevent infinite loops)
|
||||
* @returns Scan results
|
||||
*/
|
||||
async function scanArchiveFile(
|
||||
archivePath: string,
|
||||
relativeArchivePath: string,
|
||||
extractDir: string,
|
||||
logger: Logger,
|
||||
depth: number = 0,
|
||||
): Promise<ScanResult> {
|
||||
const MAX_DEPTH = 10; // Prevent infinite recursion
|
||||
if (depth > MAX_DEPTH) {
|
||||
throw new Error(
|
||||
`Maximum archive extraction depth (${MAX_DEPTH}) reached for ${archivePath}`,
|
||||
);
|
||||
}
|
||||
|
||||
const result: ScanResult = {
|
||||
scannedFiles: 0,
|
||||
findings: [],
|
||||
};
|
||||
|
||||
try {
|
||||
const tempExtractDir = fs.mkdtempSync(
|
||||
path.join(extractDir, `extract-${depth}-`),
|
||||
);
|
||||
|
||||
// Determine archive type and extract accordingly
|
||||
const fileName = path.basename(archivePath).toLowerCase();
|
||||
if (fileName.endsWith(".tar.gz") || fileName.endsWith(".tgz")) {
|
||||
// Extract tar.gz files
|
||||
logger.debug(`Extracting tar.gz file: ${archivePath}`);
|
||||
await exec.exec("tar", ["-xzf", archivePath, "-C", tempExtractDir], {
|
||||
silent: true,
|
||||
});
|
||||
} else if (fileName.endsWith(".tar.zst")) {
|
||||
// Extract tar.zst files
|
||||
logger.debug(`Extracting tar.zst file: ${archivePath}`);
|
||||
await exec.exec(
|
||||
"tar",
|
||||
["--zstd", "-xf", archivePath, "-C", tempExtractDir],
|
||||
{
|
||||
silent: true,
|
||||
},
|
||||
);
|
||||
} else if (fileName.endsWith(".zst")) {
|
||||
// Extract .zst files (single file compression)
|
||||
logger.debug(`Extracting zst file: ${archivePath}`);
|
||||
const outputFile = path.join(
|
||||
tempExtractDir,
|
||||
path.basename(archivePath, ".zst"),
|
||||
);
|
||||
await exec.exec("zstd", ["-d", archivePath, "-o", outputFile], {
|
||||
silent: true,
|
||||
});
|
||||
} else if (fileName.endsWith(".gz")) {
|
||||
// Extract .gz files (single file compression)
|
||||
logger.debug(`Extracting gz file: ${archivePath}`);
|
||||
const outputFile = path.join(
|
||||
tempExtractDir,
|
||||
path.basename(archivePath, ".gz"),
|
||||
);
|
||||
await exec.exec("gunzip", ["-c", archivePath], {
|
||||
outStream: fs.createWriteStream(outputFile),
|
||||
silent: true,
|
||||
});
|
||||
} else if (fileName.endsWith(".zip")) {
|
||||
// Extract zip files
|
||||
logger.debug(`Extracting zip file: ${archivePath}`);
|
||||
await exec.exec(
|
||||
"unzip",
|
||||
["-q", "-o", archivePath, "-d", tempExtractDir],
|
||||
{
|
||||
silent: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Scan the extracted contents
|
||||
const scanResult = await scanDirectory(
|
||||
tempExtractDir,
|
||||
relativeArchivePath,
|
||||
logger,
|
||||
depth + 1,
|
||||
);
|
||||
result.scannedFiles += scanResult.scannedFiles;
|
||||
result.findings.push(...scanResult.findings);
|
||||
|
||||
// Clean up extracted files
|
||||
fs.rmSync(tempExtractDir, { recursive: true, force: true });
|
||||
} catch (e) {
|
||||
logger.debug(
|
||||
`Could not extract or scan archive file ${archivePath}: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a single file, including recursive archive extraction if applicable.
|
||||
*
|
||||
* @param fullPath Full path to the file
|
||||
* @param relativePath Relative path for display
|
||||
* @param extractDir Directory to use for extraction (for archive files)
|
||||
* @param logger Logger instance
|
||||
* @param depth Current recursion depth
|
||||
* @returns Scan results
|
||||
*/
|
||||
async function scanFile(
|
||||
fullPath: string,
|
||||
relativePath: string,
|
||||
extractDir: string,
|
||||
logger: Logger,
|
||||
depth: number = 0,
|
||||
): Promise<ScanResult> {
|
||||
const result: ScanResult = {
|
||||
scannedFiles: 1,
|
||||
findings: [],
|
||||
};
|
||||
|
||||
// Check if it's an archive file and recursively scan it
|
||||
const fileName = path.basename(fullPath).toLowerCase();
|
||||
const isArchive =
|
||||
fileName.endsWith(".zip") ||
|
||||
fileName.endsWith(".tar.gz") ||
|
||||
fileName.endsWith(".tgz") ||
|
||||
fileName.endsWith(".tar.zst") ||
|
||||
fileName.endsWith(".zst") ||
|
||||
fileName.endsWith(".gz");
|
||||
|
||||
if (isArchive) {
|
||||
const archiveResult = await scanArchiveFile(
|
||||
fullPath,
|
||||
relativePath,
|
||||
extractDir,
|
||||
logger,
|
||||
depth,
|
||||
);
|
||||
result.scannedFiles += archiveResult.scannedFiles;
|
||||
result.findings.push(...archiveResult.findings);
|
||||
}
|
||||
|
||||
// Scan the file itself for tokens (unless it's a pure binary archive format)
|
||||
const fileFindings = scanFileForTokens(fullPath, relativePath, logger);
|
||||
result.findings.push(...fileFindings);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scans a directory for GitHub tokens.
|
||||
*
|
||||
* @param dirPath Directory path to scan
|
||||
* @param baseRelativePath Base relative path for computing display paths
|
||||
* @param logger Logger instance
|
||||
* @param depth Current recursion depth
|
||||
* @returns Scan results
|
||||
*/
|
||||
async function scanDirectory(
|
||||
dirPath: string,
|
||||
baseRelativePath: string,
|
||||
logger: Logger,
|
||||
depth: number = 0,
|
||||
): Promise<ScanResult> {
|
||||
const result: ScanResult = {
|
||||
scannedFiles: 0,
|
||||
findings: [],
|
||||
};
|
||||
|
||||
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dirPath, entry.name);
|
||||
const relativePath = path.join(baseRelativePath, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
const subResult = await scanDirectory(
|
||||
fullPath,
|
||||
relativePath,
|
||||
logger,
|
||||
depth,
|
||||
);
|
||||
result.scannedFiles += subResult.scannedFiles;
|
||||
result.findings.push(...subResult.findings);
|
||||
} else if (entry.isFile()) {
|
||||
const fileResult = await scanFile(
|
||||
fullPath,
|
||||
relativePath,
|
||||
path.dirname(fullPath),
|
||||
logger,
|
||||
depth,
|
||||
);
|
||||
result.scannedFiles += fileResult.scannedFiles;
|
||||
result.findings.push(...fileResult.findings);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a list of files and directories for GitHub tokens.
|
||||
* Recursively extracts and scans archive files (.zip, .gz, .tar.gz).
|
||||
*
|
||||
* @param filesToScan List of file paths to scan
|
||||
* @param logger Logger instance
|
||||
* @returns Scan results
|
||||
*/
|
||||
export async function scanArtifactsForTokens(
|
||||
filesToScan: string[],
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
logger.info(
|
||||
"Starting best-effort check for potential GitHub tokens in debug artifacts (for testing purposes only)...",
|
||||
);
|
||||
|
||||
const result: ScanResult = {
|
||||
scannedFiles: 0,
|
||||
findings: [],
|
||||
};
|
||||
|
||||
// Create a temporary directory for extraction
|
||||
const tempScanDir = fs.mkdtempSync(path.join(os.tmpdir(), "artifact-scan-"));
|
||||
|
||||
try {
|
||||
for (const filePath of filesToScan) {
|
||||
const stats = fs.statSync(filePath);
|
||||
const fileName = path.basename(filePath);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
const dirResult = await scanDirectory(filePath, fileName, logger);
|
||||
result.scannedFiles += dirResult.scannedFiles;
|
||||
result.findings.push(...dirResult.findings);
|
||||
} else if (stats.isFile()) {
|
||||
const fileResult = await scanFile(
|
||||
filePath,
|
||||
fileName,
|
||||
tempScanDir,
|
||||
logger,
|
||||
);
|
||||
result.scannedFiles += fileResult.scannedFiles;
|
||||
result.findings.push(...fileResult.findings);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute statistics from findings
|
||||
const tokenTypesCounts = new Map<string, number>();
|
||||
const filesWithTokens = new Set<string>();
|
||||
for (const finding of result.findings) {
|
||||
tokenTypesCounts.set(
|
||||
finding.tokenType,
|
||||
(tokenTypesCounts.get(finding.tokenType) || 0) + 1,
|
||||
);
|
||||
filesWithTokens.add(finding.filePath);
|
||||
}
|
||||
|
||||
const tokenTypesSummary = Array.from(tokenTypesCounts.entries())
|
||||
.map(([type, count]) => `${count} ${type}${count > 1 ? "s" : ""}`)
|
||||
.join(", ");
|
||||
|
||||
const baseSummary = `scanned ${result.scannedFiles} files, found ${result.findings.length} potential token(s) in ${filesWithTokens.size} file(s)`;
|
||||
const summaryWithTypes = tokenTypesSummary
|
||||
? `${baseSummary} (${tokenTypesSummary})`
|
||||
: baseSummary;
|
||||
|
||||
logger.info(`Artifact check complete: ${summaryWithTypes}`);
|
||||
|
||||
if (result.findings.length > 0) {
|
||||
const fileList = Array.from(filesWithTokens).join(", ");
|
||||
throw new Error(
|
||||
`Found ${result.findings.length} potential GitHub token(s) (${tokenTypesSummary}) in debug artifacts at: ${fileList}. This is a best-effort check for testing purposes only.`,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
// Clean up temporary directory
|
||||
try {
|
||||
fs.rmSync(tempScanDir, { recursive: true, force: true });
|
||||
} catch (e) {
|
||||
logger.debug(
|
||||
`Could not clean up temporary scan directory: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import * as configUtils from "./config-utils";
|
||||
import * as errorMessages from "./error-messages";
|
||||
import { Feature } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { GitVersionInfo } from "./git-utils";
|
||||
import { KnownLanguage, Language } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import {
|
||||
@@ -979,7 +978,6 @@ interface OverlayDatabaseModeTestSetup {
|
||||
languages: Language[];
|
||||
codeqlVersion: string;
|
||||
gitRoot: string | undefined;
|
||||
gitVersion: GitVersionInfo | undefined;
|
||||
codeScanningConfig: configUtils.UserConfig;
|
||||
diskUsage: DiskUsage | undefined;
|
||||
memoryFlagValue: number;
|
||||
@@ -994,10 +992,6 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
|
||||
languages: [KnownLanguage.javascript],
|
||||
codeqlVersion: CODEQL_OVERLAY_MINIMUM_VERSION,
|
||||
gitRoot: "/some/git/root",
|
||||
gitVersion: new GitVersionInfo(
|
||||
gitUtils.GIT_MINIMUM_VERSION_FOR_OVERLAY,
|
||||
gitUtils.GIT_MINIMUM_VERSION_FOR_OVERLAY,
|
||||
),
|
||||
codeScanningConfig: {},
|
||||
diskUsage: {
|
||||
numAvailableBytes: 50_000_000_000,
|
||||
@@ -1076,7 +1070,6 @@ const getOverlayDatabaseModeMacro = test.macro({
|
||||
setup.buildMode,
|
||||
undefined,
|
||||
setup.codeScanningConfig,
|
||||
setup.gitVersion,
|
||||
logger,
|
||||
);
|
||||
|
||||
@@ -1780,32 +1773,6 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Fallback due to old git version",
|
||||
{
|
||||
overlayDatabaseEnvVar: "overlay",
|
||||
gitVersion: new GitVersionInfo("2.30.0", "2.30.0"), // Version below required 2.38.0
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Fallback when git version cannot be determined",
|
||||
{
|
||||
overlayDatabaseEnvVar: "overlay",
|
||||
gitVersion: undefined,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
// Exercise language-specific overlay analysis features code paths
|
||||
for (const language in KnownLanguage) {
|
||||
test(
|
||||
|
||||
@@ -22,19 +22,11 @@ import {
|
||||
parseUserConfig,
|
||||
UserConfig,
|
||||
} from "./config/db-config";
|
||||
import { addDiagnostic, makeTelemetryDiagnostic } from "./diagnostics";
|
||||
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import * as errorMessages from "./error-messages";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { RepositoryProperties } from "./feature-flags/properties";
|
||||
import {
|
||||
getGitRoot,
|
||||
getGitVersionOrThrow,
|
||||
GIT_MINIMUM_VERSION_FOR_OVERLAY,
|
||||
GitVersionInfo,
|
||||
isAnalyzingDefaultBranch,
|
||||
} from "./git-utils";
|
||||
import { getGitRoot, isAnalyzingDefaultBranch } from "./git-utils";
|
||||
import { KnownLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
@@ -53,8 +45,6 @@ import {
|
||||
isDefined,
|
||||
checkDiskUsage,
|
||||
getCodeQLMemoryLimit,
|
||||
getErrorMessage,
|
||||
isInTestMode,
|
||||
} from "./util";
|
||||
|
||||
export * from "./config/db-config";
|
||||
@@ -719,7 +709,6 @@ export async function getOverlayDatabaseMode(
|
||||
buildMode: BuildMode | undefined,
|
||||
ramInput: string | undefined,
|
||||
codeScanningConfig: UserConfig,
|
||||
gitVersion: GitVersionInfo | undefined,
|
||||
logger: Logger,
|
||||
): Promise<{
|
||||
overlayDatabaseMode: OverlayDatabaseMode;
|
||||
@@ -822,22 +811,6 @@ export async function getOverlayDatabaseMode(
|
||||
);
|
||||
return nonOverlayAnalysis;
|
||||
}
|
||||
if (gitVersion === undefined) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
"the Git version could not be determined. " +
|
||||
"Falling back to creating a normal full database instead.",
|
||||
);
|
||||
return nonOverlayAnalysis;
|
||||
}
|
||||
if (!gitVersion.isAtLeast(GIT_MINIMUM_VERSION_FOR_OVERLAY)) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
`the installed Git version is older than ${GIT_MINIMUM_VERSION_FOR_OVERLAY}. ` +
|
||||
"Falling back to creating a normal full database instead.",
|
||||
);
|
||||
return nonOverlayAnalysis;
|
||||
}
|
||||
|
||||
return {
|
||||
overlayDatabaseMode,
|
||||
@@ -930,24 +903,6 @@ export async function initConfig(
|
||||
config.computedConfig["query-filters"] = [];
|
||||
}
|
||||
|
||||
let gitVersion: GitVersionInfo | undefined = undefined;
|
||||
try {
|
||||
gitVersion = await getGitVersionOrThrow();
|
||||
logger.info(`Using Git version ${gitVersion.fullVersion}`);
|
||||
await logGitVersionTelemetry(config, gitVersion);
|
||||
} catch (e) {
|
||||
logger.warning(`Could not determine Git version: ${getErrorMessage(e)}`);
|
||||
// Throw the error in test mode so it's more visible, unless the environment
|
||||
// variable is set to tolerate this, for example because we're running in a
|
||||
// Docker container where git may not be available.
|
||||
if (
|
||||
isInTestMode() &&
|
||||
process.env[EnvVar.TOLERATE_MISSING_GIT_VERSION] !== "true"
|
||||
) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// The choice of overlay database mode depends on the selection of languages
|
||||
// and queries, which in turn depends on the user config and the augmentation
|
||||
// properties. So we need to calculate the overlay database mode after the
|
||||
@@ -961,7 +916,6 @@ export async function initConfig(
|
||||
config.buildMode,
|
||||
inputs.ramInput,
|
||||
config.computedConfig,
|
||||
gitVersion,
|
||||
logger,
|
||||
);
|
||||
logger.info(
|
||||
@@ -1362,26 +1316,3 @@ export function getPrimaryAnalysisConfig(config: Config): AnalysisConfig {
|
||||
? CodeScanning
|
||||
: CodeQuality;
|
||||
}
|
||||
|
||||
/** Logs the Git version as a telemetry diagnostic. */
|
||||
async function logGitVersionTelemetry(
|
||||
config: Config,
|
||||
gitVersion: GitVersionInfo,
|
||||
): Promise<void> {
|
||||
if (config.languages.length > 0) {
|
||||
addDiagnostic(
|
||||
config,
|
||||
// Arbitrarily choose the first language. We could also choose all languages, but that
|
||||
// increases the risk of misinterpreting the data.
|
||||
config.languages[0],
|
||||
makeTelemetryDiagnostic(
|
||||
"codeql-action/git-version-telemetry",
|
||||
"Git version telemetry",
|
||||
{
|
||||
fullVersion: gitVersion.fullVersion,
|
||||
truncatedVersion: gitVersion.truncatedVersion,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,14 +95,13 @@ export async function cleanupAndUploadDatabases(
|
||||
|
||||
const reports: DatabaseUploadResult[] = [];
|
||||
for (const language of config.languages) {
|
||||
let bundledDbSize: number | undefined = undefined;
|
||||
try {
|
||||
// Upload the database bundle.
|
||||
// Although we are uploading arbitrary file contents to the API, it's worth
|
||||
// noting that it's the API's job to validate that the contents is acceptable.
|
||||
// This API method is available to anyone with write access to the repo.
|
||||
const bundledDb = await bundleDb(config, language, codeql, language);
|
||||
bundledDbSize = fs.statSync(bundledDb).size;
|
||||
const bundledDbSize = fs.statSync(bundledDb).size;
|
||||
const bundledDbReadStream = fs.createReadStream(bundledDb);
|
||||
const commitOid = await gitUtils.getCommitOid(
|
||||
actionsUtil.getRequiredInput("checkout_path"),
|
||||
@@ -145,9 +144,6 @@ export async function cleanupAndUploadDatabases(
|
||||
reports.push({
|
||||
language,
|
||||
error: util.getErrorMessage(e),
|
||||
...(bundledDbSize !== undefined
|
||||
? { zipped_upload_size_bytes: bundledDbSize }
|
||||
: {}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import archiver from "archiver";
|
||||
|
||||
import { getOptionalInput, getTemporaryDirectory } from "./actions-util";
|
||||
import { dbIsFinalized } from "./analyze";
|
||||
import { scanArtifactsForTokens } from "./artifact-scanner";
|
||||
import { type CodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
@@ -24,7 +23,6 @@ import {
|
||||
getCodeQLDatabasePath,
|
||||
getErrorMessage,
|
||||
GitHubVariant,
|
||||
isInTestMode,
|
||||
listFolder,
|
||||
} from "./util";
|
||||
|
||||
@@ -271,14 +269,6 @@ export async function uploadDebugArtifacts(
|
||||
return "upload-not-supported";
|
||||
}
|
||||
|
||||
// When running in test mode, perform a best effort scan of the debug artifacts. The artifact
|
||||
// scanner is basic and not reliable or fast enough for production use, but it can help catch
|
||||
// some issues early.
|
||||
if (isInTestMode()) {
|
||||
await scanArtifactsForTokens(toUpload, logger);
|
||||
core.exportVariable("CODEQL_ACTION_ARTIFACT_SCAN_FINISHED", "true");
|
||||
}
|
||||
|
||||
let suffix = "";
|
||||
const matrix = getOptionalInput("matrix");
|
||||
if (matrix) {
|
||||
|
||||
@@ -185,27 +185,3 @@ export function flushDiagnostics(config: Config) {
|
||||
// Reset the unwritten diagnostics array.
|
||||
unwrittenDiagnostics = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a telemetry-only diagnostic message. This is a convenience function
|
||||
* for creating diagnostics that should only be sent to telemetry and not
|
||||
* displayed on the status page or CLI summary table.
|
||||
*
|
||||
* @param id An identifier under which it makes sense to group this diagnostic message
|
||||
* @param name Display name
|
||||
* @param attributes Structured metadata
|
||||
*/
|
||||
export function makeTelemetryDiagnostic(
|
||||
id: string,
|
||||
name: string,
|
||||
attributes: { [key: string]: any },
|
||||
): DiagnosticMessage {
|
||||
return makeDiagnostic(id, name, {
|
||||
attributes,
|
||||
visibility: {
|
||||
cliSummaryTable: false,
|
||||
statusPage: false,
|
||||
telemetry: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -129,10 +129,4 @@ export enum EnvVar {
|
||||
* the workflow is valid and validation is not necessary.
|
||||
*/
|
||||
SKIP_WORKFLOW_VALIDATION = "CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION",
|
||||
|
||||
/**
|
||||
* Whether to tolerate failure to determine the git version (only applicable in test mode).
|
||||
* Intended for use in environments where git may not be installed, such as Docker containers.
|
||||
*/
|
||||
TOLERATE_MISSING_GIT_VERSION = "CODEQL_ACTION_TOLERATE_MISSING_GIT_VERSION",
|
||||
}
|
||||
|
||||
@@ -293,7 +293,6 @@ export const featureConfig: Record<
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_UPLOAD_OVERLAY_DB_TO_API",
|
||||
minimumVersion: undefined,
|
||||
toolsFeature: ToolsFeature.BundleSupportsOverlay,
|
||||
},
|
||||
[Feature.UseRepositoryProperties]: {
|
||||
defaultValue: false,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
@@ -316,6 +315,7 @@ test("getFileOidsUnderPath returns correct file mapping", async (t) => {
|
||||
"a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts",
|
||||
);
|
||||
|
||||
try {
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
|
||||
t.deepEqual(result, {
|
||||
@@ -329,10 +329,13 @@ test("getFileOidsUnderPath returns correct file mapping", async (t) => {
|
||||
["ls-files", "--recurse-submodules", "--format=%(objectname)_%(path)"],
|
||||
"Cannot list Git OIDs of tracked files.",
|
||||
]);
|
||||
} finally {
|
||||
runGitCommandStub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("getFileOidsUnderPath handles quoted paths", async (t) => {
|
||||
sinon
|
||||
const runGitCommandStub = sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves(
|
||||
"30d998ded095371488be3a729eb61d86ed721a18_lib/normal-file.js\n" +
|
||||
@@ -340,6 +343,7 @@ test("getFileOidsUnderPath handles quoted paths", async (t) => {
|
||||
'a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_"lib/file\\twith\\ttabs.js"',
|
||||
);
|
||||
|
||||
try {
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
|
||||
t.deepEqual(result, {
|
||||
@@ -347,17 +351,26 @@ test("getFileOidsUnderPath handles quoted paths", async (t) => {
|
||||
"lib/file with spaces.js": "d89514599a9a99f22b4085766d40af7b99974827",
|
||||
"lib/file\twith\ttabs.js": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
|
||||
});
|
||||
} finally {
|
||||
runGitCommandStub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("getFileOidsUnderPath handles empty output", async (t) => {
|
||||
sinon.stub(gitUtils as any, "runGitCommand").resolves("");
|
||||
const runGitCommandStub = sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves("");
|
||||
|
||||
try {
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
t.deepEqual(result, {});
|
||||
} finally {
|
||||
runGitCommandStub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("getFileOidsUnderPath throws on unexpected output format", async (t) => {
|
||||
sinon
|
||||
const runGitCommandStub = sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves(
|
||||
"30d998ded095371488be3a729eb61d86ed721a18_lib/git-utils.js\n" +
|
||||
@@ -365,6 +378,7 @@ test("getFileOidsUnderPath throws on unexpected output format", async (t) => {
|
||||
"a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts",
|
||||
);
|
||||
|
||||
try {
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
@@ -374,64 +388,7 @@ test("getFileOidsUnderPath throws on unexpected output format", async (t) => {
|
||||
message: 'Unexpected "git ls-files" output: invalid-line-format',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("getGitVersionOrThrow returns version for valid git output", async (t) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves(`git version 2.40.0${os.EOL}`);
|
||||
|
||||
const version = await gitUtils.getGitVersionOrThrow();
|
||||
t.is(version.truncatedVersion, "2.40.0");
|
||||
t.is(version.fullVersion, "2.40.0");
|
||||
});
|
||||
|
||||
test("getGitVersionOrThrow throws for invalid git output", async (t) => {
|
||||
sinon.stub(gitUtils as any, "runGitCommand").resolves("invalid output");
|
||||
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
await gitUtils.getGitVersionOrThrow();
|
||||
},
|
||||
{
|
||||
instanceOf: Error,
|
||||
message: "Could not parse Git version from output: invalid output",
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("getGitVersionOrThrow handles Windows-style git output", async (t) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves("git version 2.40.0.windows.1");
|
||||
|
||||
const version = await gitUtils.getGitVersionOrThrow();
|
||||
// The truncated version should contain just the major.minor.patch portion
|
||||
t.is(version.truncatedVersion, "2.40.0");
|
||||
t.is(version.fullVersion, "2.40.0.windows.1");
|
||||
});
|
||||
|
||||
test("getGitVersionOrThrow throws when git command fails", async (t) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.rejects(new Error("git not found"));
|
||||
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
await gitUtils.getGitVersionOrThrow();
|
||||
},
|
||||
{
|
||||
instanceOf: Error,
|
||||
message: "git not found",
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("GitVersionInfo.isAtLeast correctly compares versions", async (t) => {
|
||||
const version = new gitUtils.GitVersionInfo("2.40.0", "2.40.0");
|
||||
|
||||
t.true(version.isAtLeast("2.38.0"));
|
||||
t.true(version.isAtLeast("2.40.0"));
|
||||
t.false(version.isAtLeast("2.41.0"));
|
||||
t.false(version.isAtLeast("3.0.0"));
|
||||
} finally {
|
||||
runGitCommandStub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as core from "@actions/core";
|
||||
import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
import * as io from "@actions/io";
|
||||
import * as semver from "semver";
|
||||
|
||||
import {
|
||||
getOptionalInput,
|
||||
@@ -10,52 +9,6 @@ import {
|
||||
} from "./actions-util";
|
||||
import { ConfigurationError, getRequiredEnvParam } from "./util";
|
||||
|
||||
/**
|
||||
* Minimum Git version required for overlay analysis. The `git ls-files --format`
|
||||
* option, which is used by `getFileOidsUnderPath`, was introduced in Git 2.38.0.
|
||||
*/
|
||||
export const GIT_MINIMUM_VERSION_FOR_OVERLAY = "2.38.0";
|
||||
|
||||
/**
|
||||
* Git version information
|
||||
*
|
||||
* The full version string as reported by `git --version` may not be
|
||||
* semver-compatible (e.g., "2.40.0.windows.1"). This class captures both
|
||||
* the full version string and a truncated semver-compatible version string
|
||||
* (e.g., "2.40.0").
|
||||
*/
|
||||
export class GitVersionInfo {
|
||||
constructor(
|
||||
/** Truncated semver-compatible version */
|
||||
public truncatedVersion: string,
|
||||
/** Full version string as reported by `git --version` */
|
||||
public fullVersion: string,
|
||||
) {}
|
||||
|
||||
isAtLeast(minVersion: string): boolean {
|
||||
return semver.gte(this.truncatedVersion, minVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version of Git installed on the system and throws an error if
|
||||
* the version cannot be determined.
|
||||
*/
|
||||
export async function getGitVersionOrThrow(): Promise<GitVersionInfo> {
|
||||
const stdout = await runGitCommand(
|
||||
undefined,
|
||||
["--version"],
|
||||
"Failed to get git version.",
|
||||
);
|
||||
// Git version output can vary: "git version 2.40.0" or "git version 2.40.0.windows.1"
|
||||
// We capture just the major.minor.patch portion to ensure semver compatibility.
|
||||
const match = stdout.trim().match(/^git version ((\d+\.\d+\.\d+).*)$/);
|
||||
if (match?.[1] && match?.[2]) {
|
||||
return new GitVersionInfo(match[2], match[1]);
|
||||
}
|
||||
throw new Error(`Could not parse Git version from output: ${stdout.trim()}`);
|
||||
}
|
||||
|
||||
export const runGitCommand = async function (
|
||||
workingDirectory: string | undefined,
|
||||
args: string[],
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
flushDiagnostics,
|
||||
logUnwrittenDiagnostics,
|
||||
makeDiagnostic,
|
||||
makeTelemetryDiagnostic,
|
||||
} from "./diagnostics";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, Features } from "./feature-flags";
|
||||
@@ -426,10 +425,17 @@ async function run() {
|
||||
// Arbitrarily choose the first language. We could also choose all languages, but that
|
||||
// increases the risk of misinterpreting the data.
|
||||
config.languages[0],
|
||||
makeTelemetryDiagnostic(
|
||||
makeDiagnostic(
|
||||
"codeql-action/bundle-download-telemetry",
|
||||
"CodeQL bundle download telemetry",
|
||||
toolsDownloadStatusReport,
|
||||
{
|
||||
attributes: toolsDownloadStatusReport,
|
||||
visibility: {
|
||||
cliSummaryTable: false,
|
||||
statusPage: false,
|
||||
telemetry: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -788,10 +794,17 @@ async function recordZstdAvailability(
|
||||
// Arbitrarily choose the first language. We could also choose all languages, but that
|
||||
// increases the risk of misinterpreting the data.
|
||||
config.languages[0],
|
||||
makeTelemetryDiagnostic(
|
||||
makeDiagnostic(
|
||||
"codeql-action/zstd-availability",
|
||||
"Zstandard availability",
|
||||
zstdAvailability,
|
||||
{
|
||||
attributes: zstdAvailability,
|
||||
visibility: {
|
||||
cliSummaryTable: false,
|
||||
statusPage: false,
|
||||
telemetry: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
BIN
src/testdata/debug-artifacts-with-fake-token.zip
vendored
BIN
src/testdata/debug-artifacts-with-fake-token.zip
vendored
Binary file not shown.
@@ -152,38 +152,27 @@ export interface LoggedMessage {
|
||||
message: string | Error;
|
||||
}
|
||||
|
||||
export function getRecordingLogger(
|
||||
messages: LoggedMessage[],
|
||||
{ logToConsole }: { logToConsole?: boolean } = { logToConsole: true },
|
||||
): Logger {
|
||||
export function getRecordingLogger(messages: LoggedMessage[]): Logger {
|
||||
return {
|
||||
debug: (message: string) => {
|
||||
messages.push({ type: "debug", message });
|
||||
if (logToConsole) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(message);
|
||||
}
|
||||
},
|
||||
info: (message: string) => {
|
||||
messages.push({ type: "info", message });
|
||||
if (logToConsole) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(message);
|
||||
}
|
||||
},
|
||||
warning: (message: string | Error) => {
|
||||
messages.push({ type: "warning", message });
|
||||
if (logToConsole) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(message);
|
||||
}
|
||||
},
|
||||
error: (message: string | Error) => {
|
||||
messages.push({ type: "error", message });
|
||||
if (logToConsole) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(message);
|
||||
}
|
||||
},
|
||||
isDebug: () => true,
|
||||
startGroup: () => undefined,
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { VersionInfo } from "./codeql";
|
||||
|
||||
export enum ToolsFeature {
|
||||
BuiltinExtractorsSpecifyDefaultQueries = "builtinExtractorsSpecifyDefaultQueries",
|
||||
BundleSupportsOverlay = "bundleSupportsOverlay",
|
||||
DatabaseInterpretResultsSupportsSarifRunProperty = "databaseInterpretResultsSupportsSarifRunProperty",
|
||||
ForceOverwrite = "forceOverwrite",
|
||||
IndirectTracingSupportsStaticBinaries = "indirectTracingSupportsStaticBinaries",
|
||||
|
||||
@@ -29,6 +29,6 @@ outputs:
|
||||
proxy_urls:
|
||||
description: A stringified JSON array of objects containing the types and URLs of the configured registries.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: "../lib/start-proxy-action.js"
|
||||
post: "../lib/start-proxy-action-post.js"
|
||||
|
||||
@@ -41,6 +41,6 @@ outputs:
|
||||
|
||||
{ "code-scanning": "some-id", "code-quality": "some-other-id" }
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/upload-sarif-action.js'
|
||||
post: '../lib/upload-sarif-action-post.js'
|
||||
|
||||
Reference in New Issue
Block a user