Compare commits

..

1 Commits

Author SHA1 Message Date
Julien HENRY c9d327c024 SQSCANGHA-84 Remove outdated wget/curl references
The action was refactored to use Node.js (@actions/tool-cache) for
downloads, which doesn't rely on wget or curl. Update the README and
QA workflow to reflect this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 17:25:27 +02:00
7 changed files with 14 additions and 183 deletions
+9 -25
View File
@@ -245,9 +245,9 @@ jobs:
- name: Assert Sonar Scanner CLI was not executed
run: |
./test/assertFileDoesntExist ./output.properties
scannerBinariesUrlIsEscapedWithWget:
scannerBinariesUrlCommandInjectionTest:
name: >
'scannerBinariesUrl' is escaped with wget so special chars are not injected in the download command
'scannerBinariesUrl' does not allow command injection via semicolons
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -266,22 +266,14 @@ jobs:
- name: Assert file.txt does not exist
run: |
./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/file.txt"
scannerBinariesUrlIsEscapedWithCurl:
scannerBinariesUrlCommandInjectionWithSpacesTest:
name: >
'scannerBinariesUrl' is escaped with curl so special chars are not injected in the download command
'scannerBinariesUrl' does not allow command injection via spaces and quotes
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Remove wget
run: sudo apt-get remove -y wget
- name: Assert wget is not available
run: |
if command -v wget 2>&1 >/dev/null
then
exit 1
fi
- name: Run action with scannerBinariesUrl
id: runTest
uses: ./
@@ -472,22 +464,14 @@ jobs:
run: |
./test/assertFileContains ./output.properties "sonar.host.url=mirror.sonarcloud.io"
./test/assertFileContains ./output.properties "sonar.scanner.sonarcloudUrl=mirror.sonarcloud.io"
curlPerformsRedirect:
scannerBinariesUrlRedirectFollowed:
name: >
curl performs redirect when scannerBinariesUrl returns 3xx
scannerBinariesUrl redirect (3xx) is followed
runs-on: github-ubuntu-latest-s
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Remove wget
run: sudo apt-get remove -y wget
- name: Assert wget is not available
run: |
if command -v wget 2>&1 >/dev/null
then
exit 1
fi
- name: Generate SSL certificates for nginx
run: ./generate-ssl.sh
working-directory: .github/qa-nginx-redirecting
@@ -841,8 +825,8 @@ jobs:
- projectBaseDirInputTest
- scannerVersionTest
- scannerBinariesUrlTest
- scannerBinariesUrlIsEscapedWithWget
- scannerBinariesUrlIsEscapedWithCurl
- scannerBinariesUrlCommandInjectionTest
- scannerBinariesUrlCommandInjectionWithSpacesTest
- dontFailGradleTest
- dontFailGradleKotlinTest
- dontFailMavenTest
@@ -850,7 +834,7 @@ jobs:
- runnerDebugUsedTest
- runAnalysisWithCacheTest
- overrideSonarcloudUrlTest
- curlPerformsRedirect
- scannerBinariesUrlRedirectFollowed
- useSslCertificate
- analysisWithSslCertificate
- updateTruststoreWhenPresent
+2 -2
View File
@@ -483,11 +483,11 @@ See also [example configurations of C++ projects for SonarQube Server](https://g
When running the action in a self-hosted runner or container, please ensure that the following programs are installed:
* **curl** or **wget**
* **unzip**
* **gpg**
* **dirmngr**
Note: `gpg` and `dirmngr` are only required for GPG signature verification (enabled by default). They can be omitted when setting `skipSignatureVerification: true`.
### Additional information
The `sonarqube-scan-action/install-build-wrapper` action installs `coreutils` if run on macOS.
+1 -13
View File
@@ -10,7 +10,6 @@ import { ok } from 'assert';
import 'string_decoder';
import * as events from 'events';
import { setTimeout as setTimeout$1 } from 'timers';
import * as fs$2 from 'node:fs/promises';
import * as os$1 from 'node:os';
import * as path$1 from 'node:path';
import { join } from 'node:path';
@@ -4142,15 +4141,6 @@ function cleanupGpgHome(gpgHome) {
const TOOLNAME = "sonar-scanner-cli";
async function ensureZipExtension(filePath) {
if (filePath.endsWith(".zip")) {
return filePath;
}
const zipPath = `${filePath}.zip`;
await fs$2.rename(filePath, zipPath);
return zipPath;
}
/**
* Download the Sonar Scanner CLI for the current environment and cache it.
*/
@@ -4198,9 +4188,7 @@ async function installSonarScanner({
await verifySignature(downloadPath, signaturePath);
}
// PowerShell 5.1 (used on some Windows agents) requires the .zip extension for Expand-Archive
const extractInput = await ensureZipExtension(downloadPath);
const extractedPath = await extractZip(extractInput);
const extractedPath = await extractZip(downloadPath);
// Find the actual scanner directory inside the extracted folder
const scannerPath = path$1.join(
+1 -1
View File
File diff suppressed because one or more lines are too long
-2
View File
@@ -1,2 +0,0 @@
[tools]
node = "24"
@@ -20,7 +20,6 @@
import assert from "node:assert/strict";
import { describe, it, mock } from "node:test";
import nodeFsPromises from "node:fs/promises";
const SCANNER_VERSION = "6.2.0.4584";
const BINARIES_URL = "https://my.artifactory.example.com/sonar-scanner-cli";
@@ -36,15 +35,6 @@ function mockUtils(t) {
});
}
function mockFsPromises(t) {
t.mock.module("node:fs/promises", {
namedExports: {
...nodeFsPromises,
rename: mock.fn(async () => {}),
},
});
}
describe("installSonarScanner", () => {
it("should forward scannerBinariesAuthHeader to both binary and signature downloads", async (t) => {
const downloadCalls = [];
@@ -54,7 +44,6 @@ describe("installSonarScanner", () => {
});
mockUtils(t);
mockFsPromises(t);
t.mock.module("@actions/tool-cache", {
namedExports: {
@@ -103,7 +92,6 @@ describe("installSonarScanner", () => {
});
mockUtils(t);
mockFsPromises(t);
t.mock.module("@actions/tool-cache", {
namedExports: {
@@ -150,7 +138,6 @@ describe("installSonarScanner", () => {
});
mockUtils(t);
mockFsPromises(t);
t.mock.module("@actions/tool-cache", {
namedExports: {
@@ -184,120 +171,6 @@ describe("installSonarScanner", () => {
assert.equal(downloadCalls[0].auth, "Bearer mytoken");
});
it("should rename downloaded file to add .zip extension before extraction", async (t) => {
const renameCalls = [];
const extractZipCalls = [];
mockUtils(t);
t.mock.module("node:fs/promises", {
namedExports: {
...nodeFsPromises,
rename: mock.fn(async (src, dest) => {
renameCalls.push({ src, dest });
}),
},
});
t.mock.module("@actions/tool-cache", {
namedExports: {
find: mock.fn(() => null),
downloadTool: mock.fn(async () => "/tmp/downloaded-file"),
extractZip: mock.fn(async (p) => {
extractZipCalls.push(p);
return "/tmp/extracted";
}),
cacheDir: mock.fn(async () => "/tmp/cached"),
},
});
t.mock.module("@actions/core", {
namedExports: {
info: mock.fn(),
warning: mock.fn(),
addPath: mock.fn(),
},
});
t.mock.module("../gpg-verification.js", {
namedExports: {
verifySignature: mock.fn(async () => {}),
},
});
const { installSonarScanner } = await import(
`../install-sonar-scanner.js?test=rename-zip`
);
await installSonarScanner({
scannerVersion: SCANNER_VERSION,
scannerBinariesUrl: BINARIES_URL,
skipSignatureVerification: true,
});
assert.equal(renameCalls.length, 1, "Should rename downloaded file");
assert.equal(renameCalls[0].src, "/tmp/downloaded-file");
assert.equal(renameCalls[0].dest, "/tmp/downloaded-file.zip");
assert.equal(extractZipCalls.length, 1, "Should call extractZip once");
assert.equal(extractZipCalls[0], "/tmp/downloaded-file.zip", "Should extract the renamed file");
});
it("should not rename downloaded file when it already has .zip extension", async (t) => {
const renameCalls = [];
const extractZipCalls = [];
mockUtils(t);
t.mock.module("node:fs/promises", {
namedExports: {
...nodeFsPromises,
rename: mock.fn(async (src, dest) => {
renameCalls.push({ src, dest });
}),
},
});
t.mock.module("@actions/tool-cache", {
namedExports: {
find: mock.fn(() => null),
downloadTool: mock.fn(async () => "/tmp/downloaded-file.zip"),
extractZip: mock.fn(async (p) => {
extractZipCalls.push(p);
return "/tmp/extracted";
}),
cacheDir: mock.fn(async () => "/tmp/cached"),
},
});
t.mock.module("@actions/core", {
namedExports: {
info: mock.fn(),
warning: mock.fn(),
addPath: mock.fn(),
},
});
t.mock.module("../gpg-verification.js", {
namedExports: {
verifySignature: mock.fn(async () => {}),
},
});
const { installSonarScanner } = await import(
`../install-sonar-scanner.js?test=no-rename-zip`
);
await installSonarScanner({
scannerVersion: SCANNER_VERSION,
scannerBinariesUrl: BINARIES_URL,
skipSignatureVerification: true,
});
assert.equal(renameCalls.length, 0, "Should not rename when already .zip");
assert.equal(extractZipCalls.length, 1, "Should call extractZip once");
assert.equal(extractZipCalls[0], "/tmp/downloaded-file.zip", "Should extract original file");
});
it("should use cached tool when available and skip download", async (t) => {
const downloadToolFn = mock.fn();
+1 -13
View File
@@ -18,7 +18,6 @@
import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import {
@@ -30,15 +29,6 @@ import { verifySignature } from "./gpg-verification.js";
const TOOLNAME = "sonar-scanner-cli";
async function ensureZipExtension(filePath) {
if (filePath.endsWith(".zip")) {
return filePath;
}
const zipPath = `${filePath}.zip`;
await fs.rename(filePath, zipPath);
return zipPath;
}
/**
* Download the Sonar Scanner CLI for the current environment and cache it.
*/
@@ -86,9 +76,7 @@ export async function installSonarScanner({
await verifySignature(downloadPath, signaturePath);
}
// PowerShell 5.1 (used on some Windows agents) requires the .zip extension for Expand-Archive
const extractInput = await ensureZipExtension(downloadPath);
const extractedPath = await tc.extractZip(extractInput);
const extractedPath = await tc.extractZip(downloadPath);
// Find the actual scanner directory inside the extracted folder
const scannerPath = path.join(