From 1ed523628ec2373d94895fd91ff3093d4e2d0349 Mon Sep 17 00:00:00 2001 From: Benjamin Raymond <31401273+7PH@users.noreply.github.com> Date: Wed, 8 May 2024 11:31:47 +0200 Subject: [PATCH] SCANNPM-2 Validation fixes (#140) Co-authored-by: Lucas Paulger --- .npmignore | 142 --------------------------- ca.pem | 0 package.json | 3 +- src/constants.ts | 9 +- src/java.ts | 14 ++- src/properties.ts | 27 ++++- src/request.ts | 3 + src/scanner-engine.ts | 36 ++++--- src/types.ts | 10 +- test/unit/java.test.ts | 18 ++-- test/unit/properties.test.ts | 34 +++++++ test/unit/scanner-engine.test.ts | 40 +++++--- tools/orchestrator/package-lock.json | 42 +++++--- 13 files changed, 174 insertions(+), 204 deletions(-) delete mode 100644 .npmignore delete mode 100644 ca.pem diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 4133b4fe..00000000 --- a/.npmignore +++ /dev/null @@ -1,142 +0,0 @@ -# Created by https://www.gitignore.io/api/node,SonarQube,intellij+all - -### Intellij+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/dictionaries - -# Sensitive or high-churn files: -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.xml -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml - -# Gradle: -.idea/**/gradle.xml -.idea/**/libraries - -# CMake -cmake-build-debug/ - -# Mongo Explorer plugin: -.idea/**/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### Intellij+all Patch ### -# Ignores the whole idea folder -# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 - -.idea/ - - -### VS Code ### -.vscode/ - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -test-report.xml - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Typescript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# test report file -xunit.xml - - -### SonarQube ### -# SonarQube ignore files. -# -# https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner -# Sonar Scanner working directories -.sonar/ -.scannerwork/ - -# SonarLint working directories, configuration files (including credentials) -.sonarlint/ - -# End of https://www.gitignore.io/api/node,SonarQube,intellij+all - -!test/**/fixtures/**/* - -# MacOS -.DS_Store diff --git a/ca.pem b/ca.pem deleted file mode 100644 index e69de29b..00000000 diff --git a/package.json b/package.json index e3290777..1023a004 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "arrowParens": "avoid" }, "files": [ - "build/**" + "build/**", + "bin/**" ] } diff --git a/src/constants.ts b/src/constants.ts index dce67a06..1e64f903 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -17,7 +17,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import path from 'path'; import { ScannerProperty } from './types'; export const SCANNER_BOOTSTRAPPER_NAME = 'ScannerNpm'; @@ -65,3 +64,11 @@ export const SCANNER_CLI_MIRROR = export const SCANNER_CLI_INSTALL_PATH = 'native-sonar-scanner'; export const WINDOWS_WHERE_EXE_PATH = 'C:\\Windows\\System32\\where.exe'; + +export const SCANNER_DEPRECATED_PROPERTIES: ScannerProperty[][] = [ + [ScannerProperty.SonarWsTimeout, ScannerProperty.SonarScannerResponseTimeout], + [ScannerProperty.HttpProxyHost, ScannerProperty.SonarScannerProxyHost], + [ScannerProperty.HttpProxyPort, ScannerProperty.SonarScannerProxyPort], + [ScannerProperty.HttpProxyUser, ScannerProperty.SonarScannerProxyUser], + [ScannerProperty.HttpProxyPassword, ScannerProperty.SonarScannerProxyPassword], +]; diff --git a/src/java.ts b/src/java.ts index 7db691c9..4b32f080 100644 --- a/src/java.ts +++ b/src/java.ts @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - +import fsExtra from 'fs-extra'; import path from 'path'; import semver, { SemVer } from 'semver'; import { @@ -88,8 +88,7 @@ export async function serverSupportsJREProvisioning( properties: ScannerProperties, ): Promise { if (properties[ScannerProperty.SonarScannerInternalIsSonarCloud] === 'true') { - //TODO: return to true once SC has the new provisioning mechanism in place - return false; + return true; } // SonarQube @@ -107,7 +106,7 @@ export async function serverSupportsJREProvisioning( export async function fetchJRE(properties: ScannerProperties): Promise { log(LogLevel.DEBUG, 'Detecting latest version of JRE'); const jreMetaData = await fetchLatestSupportedJRE(properties); - log(LogLevel.INFO, 'Latest Supported JRE: ', jreMetaData); + log(LogLevel.DEBUG, 'Latest Supported JRE: ', jreMetaData); log(LogLevel.DEBUG, 'Looking for Cached JRE'); const cachedJrePath = await getCacheFileLocation(properties, { @@ -132,7 +131,12 @@ export async function fetchJRE(properties: ScannerProperties): Promise { const url = jreMetaData.downloadUrl ?? `${API_V2_JRE_ENDPOINT}/${jreMetaData.id}`; await download(url, archivePath); - await validateChecksum(archivePath, jreMetaData.sha256); + try { + await validateChecksum(archivePath, jreMetaData.sha256); + } catch (error) { + await fsExtra.remove(archivePath); + throw error; + } await extractArchive(archivePath, jreDirPath); return path.join(jreDirPath, jreMetaData.javaPath); } diff --git a/src/properties.ts b/src/properties.ts index bbfe6970..49693dc1 100644 --- a/src/properties.ts +++ b/src/properties.ts @@ -28,6 +28,7 @@ import { ENV_VAR_PREFIX, NPM_CONFIG_ENV_VAR_PREFIX, SCANNER_BOOTSTRAPPER_NAME, + SCANNER_DEPRECATED_PROPERTIES, SONARCLOUD_API_BASE_URL, SONARCLOUD_URL, SONARCLOUD_URL_REGEX, @@ -363,6 +364,28 @@ function getHttpProxyEnvProperties(serverUrl: string): ScannerProperties { return properties; } +function hotfixDeprecatedProperties(properties: ScannerProperties): ScannerProperties { + for (const [oldProp, newProp] of SCANNER_DEPRECATED_PROPERTIES) { + if (typeof properties[oldProp] !== 'undefined') { + if (typeof properties[newProp] === 'undefined') { + log( + LogLevel.WARN, + `Property "${oldProp}" is deprecated and will be removed in a future version. Please use "${newProp}" instead.`, + ); + properties[newProp] = properties[oldProp]; + } else { + log( + LogLevel.WARN, + `Both properties "${oldProp}" and "${newProp}" are set. "${oldProp}" is deprecated and will be removed in a future version. Value of deprecated property "${oldProp}" will be ignored.`, + ); + properties[oldProp] = properties[newProp]; + } + } + } + + return properties; +} + export function getProperties( scanOptions: ScanOptions, startTimestampMs: number, @@ -416,11 +439,11 @@ export function getProperties( // Hotfix host properties with custom SonarCloud URL const hostProperties = getHostProperties(properties); - return { + return hotfixDeprecatedProperties({ ...properties, // Can't be overridden: ...hostProperties, ...getBootstrapperProperties(startTimestampMs), 'sonar.projectBaseDir': projectBaseDir, - }; + }); } diff --git a/src/request.ts b/src/request.ts index 53bf1ece..7dbbcab3 100644 --- a/src/request.ts +++ b/src/request.ts @@ -149,6 +149,9 @@ export async function download(url: string, destPath: string, overrides?: AxiosR url, method: 'GET', responseType: 'stream', + headers: { + Accept: 'application/octet-stream', + }, ...overrides, }); diff --git a/src/scanner-engine.ts b/src/scanner-engine.ts index ef9b2ea6..ae38a79d 100644 --- a/src/scanner-engine.ts +++ b/src/scanner-engine.ts @@ -17,15 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import fsExtra from 'fs-extra'; import { spawn } from 'child_process'; import fs from 'fs'; import { API_V2_SCANNER_ENGINE_ENDPOINT } from './constants'; -import { - extractArchive, - getCacheDirectories, - getCacheFileLocation, - validateChecksum, -} from './file'; +import { getCacheDirectories, getCacheFileLocation, validateChecksum } from './file'; import { LogLevel, log, logWithPrefix } from './logging'; import { proxyUrlToJavaOptions } from './proxy'; import { download, fetch } from './request'; @@ -54,30 +50,33 @@ export async function fetchScannerEngine(properties: ScannerProperties) { properties[ScannerProperty.SonarScannerWasEngineCacheHit] = 'false'; - const { archivePath, unarchivePath: scannerEnginePath } = await getCacheDirectories(properties, { + const { archivePath } = await getCacheDirectories(properties, { checksum, filename, }); const url = downloadUrl ?? API_V2_SCANNER_ENGINE_ENDPOINT; log(LogLevel.DEBUG, `Starting download of Scanner Engine`); await download(url, archivePath); - log(LogLevel.INFO, `Downloaded Scanner Engine to ${scannerEnginePath}`); + log(LogLevel.INFO, `Downloaded Scanner Engine to ${archivePath}`); - await validateChecksum(archivePath, checksum); + try { + await validateChecksum(archivePath, checksum); + } catch (error) { + await fsExtra.remove(archivePath); + throw error; + } - log(LogLevel.INFO, `Extracting Scanner Engine to ${scannerEnginePath}`); - await extractArchive(archivePath, scannerEnginePath); - return scannerEnginePath; + return archivePath; } async function logOutput(message: string) { try { // Try and assume the log comes from the scanner engine const parsed = JSON.parse(message) as ScannerLogEntry; - logWithPrefix(parsed.level, 'ScannerEngine', parsed.formattedMessage); - if (parsed.throwable) { + logWithPrefix(parsed.level, 'ScannerEngine', parsed.message); + if (parsed.stacktrace) { // Console.log without newline - process.stdout.write(parsed.throwable); + process.stdout.write(parsed.stacktrace); } } catch (e) { process.stdout.write(message); @@ -93,7 +92,12 @@ export function runScannerEngine( log(LogLevel.INFO, 'Running the Scanner Engine'); // The scanner engine expects a JSON object of properties attached to a key name "scannerProperties" - const propertiesJSON = JSON.stringify({ scannerProperties: properties }); + const propertiesJSON = JSON.stringify({ + scannerProperties: Object.entries(properties).map(([key, value]) => ({ + key, + value, + })), + }); // Run the scanner-engine const args = [ diff --git a/src/types.ts b/src/types.ts index df86e93c..4912dccd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,8 +23,8 @@ export type CacheFileData = { checksum: string; filename: string }; export type ScannerLogEntry = { level: LogLevel; - formattedMessage: string; - throwable?: string; + message: string; + stacktrace?: string; }; export enum ScannerProperty { @@ -58,6 +58,12 @@ export enum ScannerProperty { SonarScannerInternalSqVersion = 'sonar.scanner.internal.sqVersion', SonarScannerCliVersion = 'sonar.scanner.version', SonarScannerCliMirror = 'sonar.scanner.mirror', + // Deprecated properties: + SonarWsTimeout = 'sonar.ws.timeout', + HttpProxyHost = 'http.proxyHost', + HttpProxyPort = 'http.proxyPort', + HttpProxyUser = 'http.proxyUser', + HttpProxyPassword = 'http.proxyPassword', } export type ScannerProperties = { diff --git a/test/unit/java.test.ts b/test/unit/java.test.ts index 72a1f5ca..3ba4fcef 100644 --- a/test/unit/java.test.ts +++ b/test/unit/java.test.ts @@ -19,8 +19,7 @@ */ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; -import fs from 'fs'; -import path from 'path'; +import fsExtra from 'fs-extra'; import { LogLevel, log } from '../../src/logging'; import { API_V2_JRE_ENDPOINT, SONARQUBE_JRE_PROVISIONING_MIN_VERSION } from '../../src/constants'; import * as file from '../../src/file'; @@ -101,7 +100,7 @@ describe('java', () => { ...MOCKED_PROPERTIES, [ScannerProperty.SonarScannerInternalIsSonarCloud]: 'true', }), - ).toBe(false); // TODO: return to true once SC has the new provisioning mechanism in place + ).toBe(true); }); it(`should return true for SQ version >= ${SONARQUBE_JRE_PROVISIONING_MIN_VERSION}`, async () => { @@ -139,10 +138,6 @@ describe('java', () => { }, }) .reply(200, serverResponse); - - mock - .onGet(`${API_V2_JRE_ENDPOINT}/${serverResponse[0].id}`) - .reply(200, fs.createReadStream(path.resolve(__dirname, '../unit/mocks/mock-jre.tar.gz'))); }); describe('when the JRE is cached', () => { @@ -191,6 +186,15 @@ describe('java', () => { expect(properties[ScannerProperty.SonarScannerWasJreCacheHit]).toBe(CacheStatus.Miss); }); + it('should remove file when checksum does not match', async () => { + jest.spyOn(file, 'validateChecksum').mockRejectedValue(new Error()); + jest.spyOn(fsExtra, 'remove'); + + await expect(fetchJRE(MOCKED_PROPERTIES)).rejects.toBeDefined(); + + expect(fsExtra.remove).toHaveBeenCalledWith('/mocked-archive-path'); + }); + it('should fail if no JRE matches', async () => { mock .onGet(API_V2_JRE_ENDPOINT, { diff --git a/test/unit/properties.test.ts b/test/unit/properties.test.ts index 898e2368..6123f6c7 100644 --- a/test/unit/properties.test.ts +++ b/test/unit/properties.test.ts @@ -481,6 +481,40 @@ describe('getProperties', () => { ); }); + it('should warn and replace deprecated properties', () => { + projectHandler.reset('fake_project_with_sonar_properties_file'); + projectHandler.setEnvironmentVariables({ + SONAR_SCANNER_JSON_PARAMS: JSON.stringify({ + 'sonar.ws.timeout': '000', + }), + }); + + const properties = getProperties( + { + options: { + 'sonar.scanner.responseTimeout': '111', + 'http.proxyHost': 'my-proxy.io', + }, + }, + projectHandler.getStartTime(), + ); + + expect(properties).toMatchObject({ + 'sonar.scanner.responseTimeout': '111', // Should not replace the deprecated property because its new version is also present + 'sonar.ws.timeout': '111', + 'sonar.scanner.proxyHost': 'my-proxy.io', // Should replace the deprecated property with the new one + 'http.proxyHost': 'my-proxy.io', + }); + expect(log).toHaveBeenCalledWith( + LogLevel.WARN, + 'Both properties "sonar.ws.timeout" and "sonar.scanner.responseTimeout" are set. "sonar.ws.timeout" is deprecated and will be removed in a future version. Value of deprecated property "sonar.ws.timeout" will be ignored.', + ); + expect(log).toHaveBeenCalledWith( + LogLevel.WARN, + 'Property "http.proxyHost" is deprecated and will be removed in a future version. Please use "sonar.scanner.proxyHost" instead.', + ); + }); + it('should set the [ScannerProperty.SonarScannerCliVersion] for all existing formats', () => { projectHandler.reset('fake_project_with_sonar_properties_file'); projectHandler.setEnvironmentVariables({ diff --git a/test/unit/scanner-engine.test.ts b/test/unit/scanner-engine.test.ts index b1983d6a..2419d9dd 100644 --- a/test/unit/scanner-engine.test.ts +++ b/test/unit/scanner-engine.test.ts @@ -22,6 +22,7 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { ChildProcess, spawn } from 'child_process'; import fs from 'fs'; +import fsExtra, { copySync } from 'fs-extra'; import sinon from 'sinon'; import { Readable } from 'stream'; import { API_V2_SCANNER_ENGINE_ENDPOINT } from '../../src/constants'; @@ -40,8 +41,8 @@ const MOCKED_PROPERTIES: ScannerProperties = { }; const MOCK_CACHE_DIRECTORIES = { - archivePath: 'mocked/path/to/sonar/cache/sha_test/scanner-engine-1.2.3.zip', - unarchivePath: 'mocked/path/to/sonar/cache/sha_test/scanner-engine-1.2.3.zip_extracted', + archivePath: 'mocked/path/to/sonar/cache/sha_test/scanner-engine-1.2.3.jar', + unarchivePath: 'mocked/path/to/sonar/cache/sha_test/scanner-engine-1.2.3.jar_extracted', }; jest.mock('../../src/constants', () => ({ ...jest.requireActual('../../src/constants'), @@ -62,7 +63,7 @@ describe('scanner-engine', () => { beforeEach(async () => { await request.initializeAxios(MOCKED_PROPERTIES); mock.onGet(API_V2_SCANNER_ENGINE_ENDPOINT).reply(200, { - filename: 'scanner-engine-1.2.3.zip', + filename: 'scanner-engine-1.2.3.jar', sha256: 'sha_test', } as AnalysisEngineResponseType); mock @@ -96,10 +97,21 @@ describe('scanner-engine', () => { expect(file.getCacheFileLocation).toHaveBeenCalledWith(MOCKED_PROPERTIES, { checksum: 'sha_test', - filename: 'scanner-engine-1.2.3.zip', + filename: 'scanner-engine-1.2.3.jar', }); }); + it('should remove file when checksum does not match', async () => { + jest.spyOn(file, 'validateChecksum').mockRejectedValue(new Error()); + jest.spyOn(fsExtra, 'remove'); + + await expect(fetchScannerEngine(MOCKED_PROPERTIES)).rejects.toBeDefined(); + + expect(fsExtra.remove).toHaveBeenCalledWith( + 'mocked/path/to/sonar/cache/sha_test/scanner-engine-1.2.3.jar', + ); + }); + describe('when the scanner engine is cached', () => { beforeEach(() => { jest.spyOn(file, 'getCacheFileLocation').mockResolvedValue('mocked/path/to/scanner-engine'); @@ -110,7 +122,7 @@ describe('scanner-engine', () => { expect(file.getCacheFileLocation).toHaveBeenCalledWith(MOCKED_PROPERTIES, { checksum: 'sha_test', - filename: 'scanner-engine-1.2.3.zip', + filename: 'scanner-engine-1.2.3.jar', }); expect(request.download).not.toHaveBeenCalled(); expect(file.extractArchive).not.toHaveBeenCalled(); @@ -125,13 +137,12 @@ describe('scanner-engine', () => { expect(file.getCacheFileLocation).toHaveBeenCalledWith(MOCKED_PROPERTIES, { checksum: 'sha_test', - filename: 'scanner-engine-1.2.3.zip', + filename: 'scanner-engine-1.2.3.jar', }); expect(request.download).toHaveBeenCalledTimes(1); - expect(file.extractArchive).toHaveBeenCalledTimes(1); expect(scannerEngine).toEqual( - 'mocked/path/to/sonar/cache/sha_test/scanner-engine-1.2.3.zip_extracted', + 'mocked/path/to/sonar/cache/sha_test/scanner-engine-1.2.3.jar', ); }); }); @@ -159,7 +170,10 @@ describe('scanner-engine', () => { expect(write).toHaveBeenCalledTimes(1); expect(write).toHaveBeenCalledWith( JSON.stringify({ - scannerProperties: MOCKED_PROPERTIES, + scannerProperties: Object.entries(MOCKED_PROPERTIES).map(([key, value]) => ({ + key, + value, + })), }), ); expect(spawn).toHaveBeenCalledWith('java', [ @@ -186,13 +200,13 @@ describe('scanner-engine', () => { const stdoutStub = sinon.stub(process.stdout, 'write').value(jest.fn()); const output = [ - JSON.stringify({ level: 'DEBUG', formattedMessage: 'the message' }), - JSON.stringify({ level: 'INFO', formattedMessage: 'another message' }), + JSON.stringify({ level: 'DEBUG', message: 'the message' }), + JSON.stringify({ level: 'INFO', message: 'another message' }), "some non-JSON message which shouldn't crash the bootstrapper", JSON.stringify({ level: 'ERROR', - formattedMessage: 'final message', - throwable: 'this is a throwable', + message: 'final message', + stacktrace: 'this is a stacktrace', }), ]; childProcessHandler.setOutput(output.join('\n')); diff --git a/tools/orchestrator/package-lock.json b/tools/orchestrator/package-lock.json index 66ecfa87..79284900 100644 --- a/tools/orchestrator/package-lock.json +++ b/tools/orchestrator/package-lock.json @@ -21,7 +21,7 @@ }, "node_modules/@types/mkdirp": { "version": "2.0.0", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/@types/mkdirp/-/mkdirp-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-2.0.0.tgz", "integrity": "sha512-c/iUqMymAlxLAyIK3u5SzrwkrkyOdv1XDc91T+b5FsY7Jr6ERhUD19jJHOhPW4GD6tmN6mFEorfSdks525pwdQ==", "deprecated": "This is a stub types definition. mkdirp provides its own type definitions, so you do not need this installed.", "dev": true, @@ -31,7 +31,7 @@ }, "node_modules/@types/node": { "version": "20.11.30", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/@types/node/-/node-20.11.30.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", "dev": true, "dependencies": { @@ -40,12 +40,12 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/asynckit/-/asynckit-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { "version": "1.6.8", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/axios/-/axios-1.6.8.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { "follow-redirects": "^1.15.6", @@ -55,7 +55,7 @@ }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/combined-stream/-/combined-stream-1.0.8.tgz", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { "delayed-stream": "~1.0.0" @@ -66,7 +66,7 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { "node": ">=0.4.0" @@ -74,8 +74,14 @@ }, "node_modules/follow-redirects": { "version": "1.15.6", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/follow-redirects/-/follow-redirects-1.15.6.tgz", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { "node": ">=4.0" }, @@ -87,7 +93,7 @@ }, "node_modules/form-data": { "version": "4.0.0", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/form-data/-/form-data-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { "asynckit": "^0.4.0", @@ -100,7 +106,7 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/mime-db/-/mime-db-1.52.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" @@ -108,7 +114,7 @@ }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/mime-types/-/mime-types-2.1.35.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { "mime-db": "1.52.0" @@ -119,18 +125,21 @@ }, "node_modules/mkdirp": { "version": "3.0.1", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/mkdirp/-/mkdirp-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "bin": { "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/prettier": { "version": "3.2.5", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/prettier/-/prettier-3.2.5.tgz", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { @@ -138,16 +147,19 @@ }, "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/typescript": { "version": "5.4.3", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/typescript/-/typescript-5.4.3.tgz", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, "bin": { @@ -160,7 +172,7 @@ }, "node_modules/undici-types": { "version": "5.26.5", - "resolved": "https://repox.jfrog.io/artifactory/api/npm/npm/undici-types/-/undici-types-5.26.5.tgz", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }