diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8dd5e43e6..3acde14da 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,7 +27,7 @@ jobs:
run: npm run lint
- name: Update version
- run: echo ${{ github.event.release.tag_name }} | cut -c 11- | xargs npm version
+ run: echo ${{ github.event.release.tag_name }} | xargs npm version
# Publish to marketplaces
- name: Publish to VS marketplace
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 6aae986eb..1388510aa 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -58,9 +58,6 @@ jobs:
with:
node-version: ${{ matrix.node }}
check-latest: true
-
- - name: Rebuild native modules
- run: npm rebuild
- name: Tests on macOS, Windows
env:
diff --git a/.vscodeignore b/.vscodeignore
index 1dc7b8d7f..14a175770 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -3,7 +3,6 @@
out/test/**
src/**
node_modules/**
-!node_modules/keytar
.gitignore
**/tsconfig.json
**/tslint.json
diff --git a/README.md b/README.md
index 4b9ae6fd4..82b7b6663 100644
--- a/README.md
+++ b/README.md
@@ -24,8 +24,10 @@
- [General](#general)
- [Software Composition Analysis (SCA)](#software-composition-analysis)
- [CVE Research and Enrichment](#cve-research-and-enrichment)
+ - [Static Application Security Testing (SAST)](#static-application-security-testing-sast)
- [Vulnerability Contextual Analysis](#vulnerability-contextual-analysis)
- [Secrets Detection](#secrets-detection)
+ - [Infrastructure as Code (IaC) Scan](#infrastructure-as-code-iac-scan)
- [The CI View](#the-ci-view)
- [How Does It Work?](#how-does-it-work)
- [Setting Up Your CI Pipeline](#setting-up-your-ci-pipeline)
@@ -80,10 +82,15 @@ Check out what our research team is up to and stay updated on newly discovered i
Vulnerability Contextual Analysis
-Uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code.
+Uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code.
Vulnerability Contextual Analysis is currently supported for Python, Java and JavaScript code.
+
+ Static Application Security Testing (SAST)
+Provides fast and accurate security-focused engines that detect zero-day security vulnerabilities on your source code sensitive operations, while minimizing false positives.
+
+
Secrets Detection
Prevents the exposure of keys or credentials that are stored in your source code.
@@ -107,12 +114,13 @@ The extension also applies [JFrog File Spec JSON schema](https://raw.githubuserc
#### 🛡️ Supported Packages
| Features | [Go](#go-projects) | [Maven](#maven-projects) | [npm](#npm-projects) | [Yarn v1](#yarn-v1-projects) | [Pypi](#pypi-projects) | [.NET](#net-projects) | [Terraform](#-infrastructure-as-code-(iac)-Scan) |
|---------------------------------------------------|:----:|:------:|:-------:|:----:|:--------:|:-------:|:-------:|
-| [SCA](#-software-composition-analysis-sca) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
-| [Upgrade vulnerable dependencies to fixed versions](#updating-dependencies) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
-| [Vulnerability Contextual Analysis](#-vulnerability-contextual-analysis) | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
-| [Secrets Detection](#-secrets-detection) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |✅ |
+| [SCA](#software-composition-analysis-sca) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
+| [Upgrade vulnerable dependencies to fixed versions](#upgrade-vulnerable-dependencies-to-fixed-versions) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
+| [Vulnerability Contextual Analysis](#vulnerability-contextual-analysis) | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
+| [Static Application Security Testing (SAST)](#static-application-security-testing-sast) | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
+| [Secrets Detection](#secrets-detection) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |✅ |
| [Exclude dev dependencies](#exclude-development-dependencies-during-scan) | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
-| [Infrastructure as Code (IaC) Scan](#-infrastructure-as-code-(iac)-Scan) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
+| [Infrastructure as Code (IaC) Scan](#infrastructure-as-code-iac-scan) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
## Getting Started
@@ -254,9 +262,6 @@ Clicking on a CVE in the list will open the location with the issue in the edito
![Impact_Graph](resources/readme/preview/impactGraph.png)
![Public_Resources](resources/readme/preview/publicDetails.png)
-Update a vulnerable direct dependency to a fixed version directly from the vulnerable location at the editor using quick fix
-![Set_Fixed_Version](resources/readme/preview/updateQuickFix.png)
-
When Xray watches are enabled and a vulnerability is detected, a closed eye icon will appear next to the vulnerability line in the JFrog extension. By clicking on this icon, you can initiate the process of creating an [Ignore Rule](https://www.jfrog.com/confluence/display/JFROG/Ignore+Rules) in Xray.
![Ignore_Rule](resources/readme/preview/ignoreRule.png)
@@ -282,6 +287,24 @@ Xray automatically validates some high and very high impact vulnerabilities, suc
![Contextual_Analysis](resources/readme/preview/contextualDetails.png)
+### Upgrade vulnerable dependencies to fixed versions
+Update a vulnerable direct dependency to a fixed version directly from the vulnerable location at the editor using quick fix
+![Set_Fixed_Version](resources/readme/preview/updateQuickFix.png)
+
+### Static Application Security Testing (SAST)
+> **_NOTE:_** Static Application Security Testing (SAST) requires Xray version 3.66.5 or above and Enterprise X / Enterprise+ subscription with Advanced DevSecOps.
+
+JFrog SAST scans mainly for specific sensitive operations (DB queries, OS commands, outgoing connection destinations, etc) that can be controlled by an external attacker without proper sanitation injections such as: SQL injections, Command injections, Code injections and SSRF.
+It also detects cases when certain APIs (encryption, cryptographic signing, file operations, etc.) are used with parameters or under circumstances that render the API use unsafe.
+
+SAST findings are presented in a way that will help you easily locate the vulnerable data flow in your code. The data is represented within an easy-to-use interface that enables you to track each vulnerability in the code and provides the following information per vulnerability:
+
+* **Data Flow Analysis**: Provides information on the overall code flow and the different entry points of the vulnerability up to the execution point of the vulnerability. At JFrog we understand the developers need to see the entire picture of their code, rather than just providing the specific vulnerability found in the code. With Data Analysis Flow you will be able to follow the entire lifecycle of the vulnerability.
+* **Fix Steps**: To help you fix the security issues, the JFrog security team provides you with detailed fixes and mitigation options for the vulnerabilities. Xray empowers you to make smart choices when creating the mitigation plan and choosing the paths with the highest return on investment.
+Along with the JFrog severity given, you can make informed decisions on what vulnerabilities are a priority to fix. For example, vulnerabilities with low JFrog security severity are considered less risky, as it would be very unlikely to exploit them in the real world, or the impact of the exploitation is low.
+
+![SAST](resources/readme/preview/sast.png)
+
### Secrets Detection
> **_NOTE:_** Secrets Detection requires Xray version 3.66.5 or above and Enterprise X / Enterprise+ subscription with Advanced DevSecOps.
diff --git a/package-lock.json b/package-lock.json
index 3c3ce4a91..016cd5d66 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "jfrog-vscode-extension",
- "version": "2.7.1",
+ "version": "2.8.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jfrog-vscode-extension",
- "version": "2.7.1",
+ "version": "2.8.2",
"license": "Apache-2.0",
"dependencies": {
"adm-zip": "~0.5.9",
@@ -15,7 +15,6 @@
"jfrog-ide-webview": "https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/jfrog-ide-webview-0.2.11.tgz",
"js-yaml": "^4.1.0",
"json2csv": "~5.0.7",
- "keytar": "~7.9.0",
"nuget-deps-tree": "^0.2.2",
"original-fs": "~1.1.0",
"p-queue": "~6.6.2",
@@ -23,7 +22,7 @@
"semver": "^7.5.4",
"typescript-collections": "~1.3.3",
"walkdir": "~0.4.1",
- "xmlbuilder2": "~3.0.2"
+ "xmlbuilder2": "~3.1.1"
},
"devDependencies": {
"@faker-js/faker": "^7.1.0",
@@ -40,25 +39,25 @@
"@types/vscode": "1.64.0",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
+ "@vscode/vsce": "^2.21.0",
"chai": "^4.3.6",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.5.0",
"glob": "^8.0.3",
"mocha": "^10.0.0",
"nock": "^13.2.7",
- "ovsx": "^0.5.0",
+ "ovsx": "^0.8.3",
"prettier": "^1.19.1",
"sinon": "^15.2.0",
"tmp": "^0.2.1",
"ts-loader": "^9.3.0",
"typescript": "^4.7.2",
- "vsce": "^2.9.0",
"vscode-test": "^1.6.1",
"webpack": "^5.76.0",
"webpack-cli": "^4.9.2"
},
"engines": {
- "vscode": "^1.64.2"
+ "vscode": "^1.80.0"
}
},
"node_modules/@babel/code-frame": {
@@ -1457,6 +1456,147 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@vscode/vsce": {
+ "version": "2.21.1",
+ "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.21.1.tgz",
+ "integrity": "sha512-f45/aT+HTubfCU2oC7IaWnH9NjOWp668ML002QiFObFRVUCoLtcwepp9mmql/ArFUy+HCHp54Xrq4koTcOD6TA==",
+ "dev": true,
+ "dependencies": {
+ "azure-devops-node-api": "^11.0.1",
+ "chalk": "^2.4.2",
+ "cheerio": "^1.0.0-rc.9",
+ "commander": "^6.2.1",
+ "glob": "^7.0.6",
+ "hosted-git-info": "^4.0.2",
+ "jsonc-parser": "^3.2.0",
+ "leven": "^3.1.0",
+ "markdown-it": "^12.3.2",
+ "mime": "^1.3.4",
+ "minimatch": "^3.0.3",
+ "parse-semver": "^1.1.1",
+ "read": "^1.0.7",
+ "semver": "^7.5.2",
+ "tmp": "^0.2.1",
+ "typed-rest-client": "^1.8.4",
+ "url-join": "^4.0.1",
+ "xml2js": "^0.5.0",
+ "yauzl": "^2.3.1",
+ "yazl": "^2.2.2"
+ },
+ "bin": {
+ "vsce": "vsce"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "optionalDependencies": {
+ "keytar": "^7.7.0"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/@vscode/vsce/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@vscode/vsce/node_modules/xml2js": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+ "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
+ "dev": true,
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
"node_modules/@webassemblyjs/ast": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz",
@@ -1947,6 +2087,8 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
@@ -1957,6 +2099,7 @@
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -1971,6 +2114,7 @@
"url": "https://feross.org/support"
}
],
+ "optional": true,
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
@@ -1980,6 +2124,8 @@
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -2424,7 +2570,9 @@
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true,
+ "optional": true
},
"node_modules/chrome-trace-event": {
"version": "1.0.3",
@@ -2756,6 +2904,8 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"mimic-response": "^3.1.0"
},
@@ -2782,6 +2932,8 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "optional": true,
"engines": {
"node": ">=4.0.0"
}
@@ -2836,6 +2988,8 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
+ "dev": true,
+ "optional": true,
"engines": {
"node": ">=8"
}
@@ -3035,6 +3189,8 @@
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"once": "^1.4.0"
}
@@ -3351,6 +3507,8 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "dev": true,
+ "optional": true,
"engines": {
"node": ">=6"
}
@@ -3591,7 +3749,9 @@
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true,
+ "optional": true
},
"node_modules/fs-extra": {
"version": "10.1.0",
@@ -3688,9 +3848,9 @@
}
},
"node_modules/get-func-name": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
- "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
"dev": true,
"engines": {
"node": "*"
@@ -3712,7 +3872,9 @@
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
- "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "dev": true,
+ "optional": true
},
"node_modules/glob": {
"version": "8.1.0",
@@ -4175,7 +4337,9 @@
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "optional": true
},
"node_modules/inline-style-parser": {
"version": "0.1.1",
@@ -4600,6 +4764,12 @@
"npm": ">= 6.13.0"
}
},
+ "node_modules/jsonc-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
+ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
+ "dev": true
+ },
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -4629,7 +4799,9 @@
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz",
"integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==",
+ "dev": true,
"hasInstallScript": true,
+ "optional": true,
"dependencies": {
"node-addon-api": "^4.3.0",
"prebuild-install": "^7.0.1"
@@ -5416,6 +5588,8 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "dev": true,
+ "optional": true,
"engines": {
"node": ">=10"
},
@@ -5449,6 +5623,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -5468,7 +5643,9 @@
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "dev": true,
+ "optional": true
},
"node_modules/mocha": {
"version": "10.2.0",
@@ -5618,7 +5795,9 @@
"node_modules/napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
- "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
+ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
+ "dev": true,
+ "optional": true
},
"node_modules/natural-compare": {
"version": "1.4.0",
@@ -5678,6 +5857,8 @@
"version": "3.40.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz",
"integrity": "sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"semver": "^7.3.5"
},
@@ -5688,7 +5869,9 @@
"node_modules/node-addon-api": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
- "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
+ "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==",
+ "dev": true,
+ "optional": true
},
"node_modules/node-polyfill-webpack-plugin": {
"version": "2.0.1",
@@ -5839,6 +6022,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
"dependencies": {
"wrappy": "1"
}
@@ -5871,17 +6055,18 @@
"integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A=="
},
"node_modules/ovsx": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.2.tgz",
- "integrity": "sha512-UbLultRCk46WddeA0Cly4hoRhzBJUiLgbIEViXlgOvV54LbsppClDkMLoCevUUBHoiNdMX2NuiSgURAEXgCZdw==",
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.8.3.tgz",
+ "integrity": "sha512-LG7wTzy4eYV/KolFeO4AwWPzQSARvCONzd5oHQlNvYOlji2r/zjbdK8pyObZN84uZlk6rQBWrJrAdJfh/SX0Hg==",
"dev": true,
"dependencies": {
+ "@vscode/vsce": "^2.19.0",
"commander": "^6.1.0",
"follow-redirects": "^1.14.6",
"is-ci": "^2.0.0",
"leven": "^3.1.0",
- "tmp": "^0.2.1",
- "vsce": "^2.6.3"
+ "semver": "^7.5.2",
+ "tmp": "^0.2.1"
},
"bin": {
"ovsx": "lib/ovsx"
@@ -6036,9 +6221,9 @@
}
},
"node_modules/parse-semver/node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"bin": {
"semver": "bin/semver"
@@ -6244,6 +6429,8 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
@@ -6363,6 +6550,8 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
@@ -6449,6 +6638,8 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@@ -6463,6 +6654,8 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "optional": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6968,6 +7161,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -6981,12 +7175,14 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "optional": true
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -7001,6 +7197,7 @@
"url": "https://feross.org/support"
}
],
+ "optional": true,
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
@@ -7219,6 +7416,8 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
@@ -7230,6 +7429,8 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
@@ -7245,6 +7446,8 @@
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -7452,6 +7655,8 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"safe-buffer": "^5.0.1"
},
@@ -7803,141 +8008,6 @@
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
},
- "node_modules/vsce": {
- "version": "2.15.0",
- "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.15.0.tgz",
- "integrity": "sha512-P8E9LAZvBCQnoGoizw65JfGvyMqNGlHdlUXD1VAuxtvYAaHBKLBdKPnpy60XKVDAkQCfmMu53g+gq9FM+ydepw==",
- "deprecated": "vsce has been renamed to @vscode/vsce. Install using @vscode/vsce instead.",
- "dev": true,
- "dependencies": {
- "azure-devops-node-api": "^11.0.1",
- "chalk": "^2.4.2",
- "cheerio": "^1.0.0-rc.9",
- "commander": "^6.1.0",
- "glob": "^7.0.6",
- "hosted-git-info": "^4.0.2",
- "keytar": "^7.7.0",
- "leven": "^3.1.0",
- "markdown-it": "^12.3.2",
- "mime": "^1.3.4",
- "minimatch": "^3.0.3",
- "parse-semver": "^1.1.1",
- "read": "^1.0.7",
- "semver": "^5.1.0",
- "tmp": "^0.2.1",
- "typed-rest-client": "^1.8.4",
- "url-join": "^4.0.1",
- "xml2js": "^0.4.23",
- "yauzl": "^2.3.1",
- "yazl": "^2.2.2"
- },
- "bin": {
- "vsce": "vsce"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/vsce/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/vsce/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/vsce/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/vsce/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "node_modules/vsce/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/vsce/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/vsce/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/vsce/node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true,
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/vsce/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/vscode-test": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.6.1.tgz",
@@ -8137,9 +8207,9 @@
"dev": true
},
"node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -8171,20 +8241,8 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
- },
- "node_modules/xml2js": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
- "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
- "dev": true,
- "dependencies": {
- "sax": ">=0.6.0",
- "xmlbuilder": "~11.0.0"
- },
- "engines": {
- "node": ">=4.0.0"
- }
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
},
"node_modules/xmlbuilder": {
"version": "11.0.1",
@@ -8196,15 +8254,14 @@
}
},
"node_modules/xmlbuilder2": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz",
- "integrity": "sha512-h4MUawGY21CTdhV4xm3DG9dgsqyhDkZvVJBx88beqX8wJs3VgyGQgAn5VreHuae6unTQxh115aMK5InCVmOIKw==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz",
+ "integrity": "sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw==",
"dependencies": {
"@oozcitak/dom": "1.15.10",
"@oozcitak/infra": "1.0.8",
"@oozcitak/util": "8.3.8",
- "@types/node": "*",
- "js-yaml": "3.14.0"
+ "js-yaml": "3.14.1"
},
"engines": {
"node": ">=12.0"
@@ -8219,9 +8276,9 @@
}
},
"node_modules/xmlbuilder2/node_modules/js-yaml": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
- "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
diff --git a/package.json b/package.json
index cb39108c9..21a30155a 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "jfrog-vscode-extension",
"displayName": "JFrog",
"description": "Security scanning for your Go, npm, Pypi, Maven and NuGet projects.",
- "version": "2.7.1",
+ "version": "2.8.2",
"license": "Apache-2.0",
"icon": "resources/extensionIcon.png",
"repository": {
@@ -15,7 +15,7 @@
},
"publisher": "JFrog",
"engines": {
- "vscode": "^1.64.2"
+ "vscode": "^1.80.0"
},
"categories": [
"Other"
@@ -28,13 +28,18 @@
"viewsWelcome": [
{
"view": "jfrog.issues",
- "contents": "Your project is ready to be scanned for security issues as described below.\nThe basic functionality includes Software Composition Analysis (SCA). We will scan your project dependencies for security issues and also show you enriched CVE data provided by the JFrog Security Research team.\n\nIf your JFrog subscription includes [Advanced DevSecOps](https://jfrog.com/xray/) enabled, we'll also run the following scans:\n\nVulnerability Contextual Analysis - This feature uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code. Vulnerability Contextual Analysis is currently supported for Python, JavaScript and Java code.\nSecrets Detection - Detect and prevent the inclusion of sensitive information, such as credentials and API keys, in your codebase.\nInfrastructure as Code (IaC) scans - Analyze Infrastructure as Code (IaC) files, such as Terraform, to identify security vulnerabilities and misconfigurations before deploying your cloud infrastructure. Get actionable insights and recommendations for securing your IaC configurations.",
+ "contents": "Your project is ready to be scanned for security issues as described below.\nThe basic functionality includes Software Composition Analysis (SCA). We will scan your project dependencies for security issues and also show you enriched CVE data provided by the JFrog Security Research team.\n\nIf your JFrog subscription includes [Advanced DevSecOps](https://jfrog.com/xray/) enabled, we'll also run the following scans:\n\n[Vulnerability Contextual Analysis](https://github.com/jfrog/jfrog-vscode-extension#vulnerability-contextual-analysis) - This feature uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code. Vulnerability Contextual Analysis is currently supported for Python, JavaScript and Java code.\n[SAST](https://github.com/jfrog/jfrog-vscode-extension#static-application-security-testing-sast) - Provides fast and accurate security-focused engines that detect zero-day security vulnerabilities on your source code sensitive operations, while minimizing false positives.\n[Secrets Detection](https://github.com/jfrog/jfrog-vscode-extension#secrets-detection) - Detect and prevent the inclusion of sensitive information, such as credentials and API keys, in your codebase.\n[Infrastructure as Code (IaC) scans](https://github.com/jfrog/jfrog-vscode-extension#infrastructure-as-code-iac-scan) - IaC files, such as Terraform, to identify security vulnerabilities and misconfigurations before deploying your cloud infrastructure. Get actionable insights and recommendations for securing your IaC configurations.",
"when": "jfrog.connection.status == signedIn && jfrog.firstScanInWorkspace"
},
{
"view": "jfrog.issues",
"contents": "Your project was scanned and we didn't find any security issues.\n[Rescan](command:jfrog.scan.refresh)",
"when": "jfrog.connection.status == signedIn && !jfrog.firstScanInWorkspace"
+ },
+ {
+ "view": "jfrog.issues",
+ "contents": "Couldn't connect to your JFrog Platform. \n[Reconnect](command:jfrog.xray.reConnect) \n [Reset your connection details](command:jfrog.xray.resetConnection)",
+ "when": "jfrog.connection.status == connectionLost"
}
],
"viewsContainers": {
@@ -137,6 +142,12 @@
"icon": "$(extensions-manage)",
"category": "JFrog"
},
+ {
+ "command": "jfrog.open.feedback",
+ "title": "Send us feedback",
+ "icon": "$(feedback)",
+ "category": "JFrog"
+ },
{
"command": "jfrog.scan.refresh",
"title": "Refresh",
@@ -244,6 +255,11 @@
"command": "jfrog.open.settings",
"when": "view == jfrog.view.ci.issues || view == jfrog.issues",
"group": "navigation@5"
+ },
+ {
+ "command": "jfrog.open.feedback",
+ "when": "view == jfrog.view.ci.issues || view == jfrog.issues",
+ "group": "navigation@6"
}
],
"view/item/context": [
@@ -287,7 +303,6 @@
"jfrog-ide-webview": "https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/jfrog-ide-webview-0.2.11.tgz",
"js-yaml": "^4.1.0",
"json2csv": "~5.0.7",
- "keytar": "~7.9.0",
"nuget-deps-tree": "^0.2.2",
"original-fs": "~1.1.0",
"p-queue": "~6.6.2",
@@ -295,7 +310,7 @@
"semver": "^7.5.4",
"typescript-collections": "~1.3.3",
"walkdir": "~0.4.1",
- "xmlbuilder2": "~3.0.2"
+ "xmlbuilder2": "~3.1.1"
},
"devDependencies": {
"@faker-js/faker": "^7.1.0",
@@ -318,13 +333,13 @@
"glob": "^8.0.3",
"mocha": "^10.0.0",
"nock": "^13.2.7",
- "ovsx": "^0.5.0",
+ "ovsx": "^0.8.3",
"prettier": "^1.19.1",
"sinon": "^15.2.0",
"tmp": "^0.2.1",
"ts-loader": "^9.3.0",
"typescript": "^4.7.2",
- "vsce": "^2.9.0",
+ "@vscode/vsce": "^2.21.0",
"vscode-test": "^1.6.1",
"webpack": "^5.76.0",
"webpack-cli": "^4.9.2"
diff --git a/resources/dark/build.png b/resources/dark/build.png
index 7bb748ec9..507753096 100644
Binary files a/resources/dark/build.png and b/resources/dark/build.png differ
diff --git a/resources/dark/ci.png b/resources/dark/ci.png
index 4042310e7..3fea361b3 100644
Binary files a/resources/dark/ci.png and b/resources/dark/ci.png differ
diff --git a/resources/dark/filter.png b/resources/dark/filter.png
index 3da54322f..6b3b41313 100644
Binary files a/resources/dark/filter.png and b/resources/dark/filter.png differ
diff --git a/resources/dark/refresh.png b/resources/dark/refresh.png
index 5b707f2d2..d73f56bc0 100644
Binary files a/resources/dark/refresh.png and b/resources/dark/refresh.png differ
diff --git a/resources/light/build.png b/resources/light/build.png
index 710ac6271..fa54419f0 100644
Binary files a/resources/light/build.png and b/resources/light/build.png differ
diff --git a/resources/light/ci.png b/resources/light/ci.png
index 927dafb03..9de75344b 100644
Binary files a/resources/light/ci.png and b/resources/light/ci.png differ
diff --git a/resources/light/filter.png b/resources/light/filter.png
index 8779b2711..8a6d7535d 100644
Binary files a/resources/light/filter.png and b/resources/light/filter.png differ
diff --git a/resources/light/refresh.png b/resources/light/refresh.png
index 0a4ebe51b..563569b53 100644
Binary files a/resources/light/refresh.png and b/resources/light/refresh.png differ
diff --git a/resources/readme/preview/sast.png b/resources/readme/preview/sast.png
new file mode 100644
index 000000000..28bcff6ca
Binary files /dev/null and b/resources/readme/preview/sast.png differ
diff --git a/src/main/commands/commandManager.ts b/src/main/commands/commandManager.ts
index 292b4fb0e..4fbb846a7 100644
--- a/src/main/commands/commandManager.ts
+++ b/src/main/commands/commandManager.ts
@@ -41,6 +41,7 @@ export class CommandManager implements ExtensionComponent {
this.registerCommand(context, 'jfrog.xray.reConnect', () => this.doReconnect());
// General
this.registerCommand(context, 'jfrog.open.settings', () => Utils.openSettings());
+ this.registerCommand(context, 'jfrog.open.feedback', () => Utils.openFeedback());
this.registerCommand(context, 'jfrog.xray.copyToClipboard', node => this.doCopyToClipboard(node));
this.registerCommand(context, 'jfrog.xray.showOutput', () => this.showOutput());
this.registerCommand(context, 'jfrog.scan.refresh', () => this.doRefresh());
diff --git a/src/main/connect/connectionManager.ts b/src/main/connect/connectionManager.ts
index b8d45afae..6eba82499 100644
--- a/src/main/connect/connectionManager.ts
+++ b/src/main/connect/connectionManager.ts
@@ -1,24 +1,23 @@
import { execSync } from 'child_process';
+import * as crypto from 'crypto';
import {
+ AccessTokenResponse,
+ ClientUtils,
IAqlSearchResult,
IDetailsResponse,
IGraphRequestModel,
IGraphResponse,
IUsageFeature,
JfrogClient,
- AccessTokenResponse,
- XrayScanProgress,
- ClientUtils
+ XrayScanProgress
} from 'jfrog-client-js';
-import * as crypto from 'crypto';
-import * as keytar from 'keytar';
import * as semver from 'semver';
import * as vscode from 'vscode';
+import { ContextKeys, SessionStatus } from '../constants/contextKeys';
import { ExtensionComponent } from '../extensionComponent';
import { LogManager } from '../log/logManager';
-import { ConnectionUtils } from './connectionUtils';
import { ScanUtils } from '../utils/scanUtils';
-import { ContextKeys, SessionStatus } from '../constants/contextKeys';
+import { ConnectionUtils } from './connectionUtils';
export enum LoginStatus {
Success = 'SUCCESS',
@@ -42,7 +41,7 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
// Service ID in the OS KeyStore to store and retrieve the password / access token
private static readonly SERVICE_ID: string = 'com.jfrog.xray.vscode';
- // Key used for uniqueness when storing access token in KeyStore.
+ // Key used for uniqueness when storing access token in SecretStorage.
private static readonly ACCESS_TOKEN_FS_KEY: string = 'vscode_jfrog_token';
// Store connection details in file system after reading connection details from env
@@ -82,10 +81,8 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
this._context = context;
switch (await this.getConnectionStatus()) {
case SessionStatus.SignedIn:
- await this.handledSignedIn();
- break;
case SessionStatus.connectionLost:
- await this.handledConnectionLost();
+ await this.handledSignedIn();
break;
case SessionStatus.SignedOut:
this.setConnectionView(SessionStatus.SignedOut);
@@ -112,16 +109,16 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
}
private async handledSignedIn() {
+ if (!(await this.loadCredential())) {
+ await this.disconnect();
+ return;
+ }
if (!(await this.connect())) {
this.setConnectionView(SessionStatus.connectionLost);
await this.setConnectionStatus(SessionStatus.connectionLost);
}
}
- private async handledConnectionLost() {
- return this.handledSignedIn();
- }
-
private async handelUnknownState() {
if (!(await this.connect())) {
this.setConnectionView(SessionStatus.SignedOut);
@@ -130,11 +127,8 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
}
public async connect(): Promise {
- this._logManager.logMessage('Trying to read credentials from KeyStore...', 'DEBUG');
- const credentialsSet: boolean =
- (await this.setUrlsFromFilesystem()) &&
- (((await this.setUsernameFromFilesystem()) && (await this.setPasswordFromKeyStore())) || (await this.setAccessTokenFromKeyStore()));
- if (!credentialsSet) {
+ this._logManager.logMessage('Trying to read credentials from Secret Storage...', 'DEBUG');
+ if (!(await this.pingCredential())) {
this.deleteCredentialsFromMemory();
return false;
}
@@ -143,6 +137,25 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
return true;
}
+ private async pingCredential(): Promise {
+ if (await this.loadCredential()) {
+ try {
+ return await ConnectionUtils.validateXrayConnection(this.xrayUrl, this._username, this._password, this._accessToken);
+ } catch (error) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public async loadCredential(): Promise {
+ return (
+ (await this.setUrlsFromFilesystem()) &&
+ (((await this.setUsernameFromFilesystem()) && (await this.getPasswordFromSecretStorage())) ||
+ (await this.getAccessTokenFromSecretStorage()))
+ );
+ }
+
public async onSuccessConnect() {
await this.setConnectionStatus(SessionStatus.SignedIn);
this.setConnectionView(SessionStatus.SignedIn);
@@ -562,80 +575,78 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
await this._context.globalState.update(ConnectionManager.XRAY_USERNAME_KEY, this._username);
}
- private async setPasswordFromKeyStore(): Promise {
- this._password = await this.getSecretFromKeyStore(this._username);
+ private async getPasswordFromSecretStorage(): Promise {
+ this._password = await this.getSecretFromSecretStorage(this._username);
return !!this._password;
}
- private async setAccessTokenFromKeyStore(): Promise {
- this._accessToken = await this.getSecretFromKeyStore(ConnectionManager.ACCESS_TOKEN_FS_KEY);
+ private async getAccessTokenFromSecretStorage(): Promise {
+ this._accessToken = await this.getSecretFromSecretStorage(ConnectionManager.ACCESS_TOKEN_FS_KEY);
return !!this._accessToken;
}
/**
- * Password and access token are saved in KeyStore with an account that is a hash of url and another string.
+ * Password and access token are saved in SecretStorage with an account that is a hash of url and another string.
* For password - username, access token - a constant.
* @param keyPair - The second string of the account as described above.
* @returns The secret if found.
*/
- private async getSecretFromKeyStore(keyPair: string): Promise {
- return (
- (await keytar.getPassword(ConnectionManager.SERVICE_ID, this.createAccountId(this._url, keyPair))) ||
- (await keytar.getPassword(ConnectionManager.SERVICE_ID, this.createAccountId(this._xrayUrl, keyPair))) ||
- ''
- );
+ private async getSecretFromSecretStorage(keyPair: string): Promise {
+ return (await this._context.secrets.get(this.createSecretStoreId(this._xrayUrl, keyPair))) || '';
}
- private async deletePasswordFromKeyStore(): Promise {
+ private async deletePasswordFromSecretStorage(): Promise {
if (!this._password) {
- return true;
+ return;
}
- return await this.deleteSecretFromKeyStore(this._username, 'password');
+ await this.deleteSecretFromSecretStorage(this._username);
}
- private async deleteAccessTokenFromKeyStore(): Promise {
+ private async deleteAccessTokenFromSecretStorage(): Promise {
if (!this._accessToken) {
- return true;
+ return;
}
- return await this.deleteSecretFromKeyStore(ConnectionManager.ACCESS_TOKEN_FS_KEY, 'access token');
+ await this.deleteSecretFromSecretStorage(ConnectionManager.ACCESS_TOKEN_FS_KEY);
}
- private async deleteSecretFromKeyStore(keyPair: string, secretName: string): Promise {
- let ok: boolean = await keytar.deletePassword(ConnectionManager.SERVICE_ID, this.createAccountId(this._xrayUrl, keyPair));
- if (!ok) {
- this._logManager.logMessage('Failed to delete the ' + secretName + ' from the system secrets manager', 'WARN');
- return false;
- }
- return true;
+ private async deleteSecretFromSecretStorage(keyPair: string): Promise {
+ await this._context.secrets.delete(this.createSecretStoreId(this._xrayUrl, keyPair));
}
- private async storePassword() {
+ private async storePassword(): Promise {
if (!this._password) {
return;
}
- await keytar.setPassword(ConnectionManager.SERVICE_ID, this.createAccountId(this._xrayUrl, this._username), this._password);
+ await this._context.secrets.store(this.createSecretStoreId(this._xrayUrl, this._username), this._password);
}
- private async storeAccessToken() {
+ private async storeAccessToken(): Promise {
if (!this._accessToken) {
return;
}
- await keytar.setPassword(
- ConnectionManager.SERVICE_ID,
- this.createAccountId(this._xrayUrl, ConnectionManager.ACCESS_TOKEN_FS_KEY),
- this._accessToken
- );
+ await this._context.secrets.store(this.createSecretStoreId(this._xrayUrl, ConnectionManager.ACCESS_TOKEN_FS_KEY), this._accessToken);
}
/**
* Create obscured account id to get extra security.
- * @param url Xray url
- * @param username Xray username
+ * @param url - Xray url
+ * @param keyPair - Unique key
* @returns hashed account id
*/
- private createAccountId(url: string, username: string): string {
- return ScanUtils.Hash('sha256', url + username);
+ private createAccountId(url: string, keyPair: string): string {
+ return ScanUtils.Hash('sha256', url + keyPair);
}
+
+ /**
+ * Return unique Secret Store ID to allow retrieving/storing/deleting a value from the Secret Store.
+ * @param url - Xray url
+ * @param keyPair - Unique key
+ * @returns Secret Store ID
+ */
+ private createSecretStoreId(url: string, keyPair: string): string {
+ return ConnectionManager.SERVICE_ID + '.' + this.createAccountId(url, keyPair);
+ }
+
/**
* By setting the global context, we can save a key/value pair.
* VS Code manages the storage and will restore it for each extension activation.
@@ -700,17 +711,15 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
this._xrayVersion = '';
}
- private async deleteCredentialsFromFileSystem(): Promise {
+ private async deleteCredentialsFromFileSystem(): Promise {
// Delete password / access token must be executed first.
- let passOk: boolean = await this.deletePasswordFromKeyStore();
- let tokenOk: boolean = await this.deleteAccessTokenFromKeyStore();
+ await Promise.all([await this.deletePasswordFromSecretStorage(), await this.deleteAccessTokenFromSecretStorage()]);
await Promise.all([
this._context.globalState.update(ConnectionManager.XRAY_URL_KEY, undefined),
this._context.globalState.update(ConnectionManager.RT_URL_KEY, undefined),
this._context.globalState.update(ConnectionManager.PLATFORM_URL_KEY, undefined),
this._context.globalState.update(ConnectionManager.XRAY_USERNAME_KEY, undefined)
]);
- return passOk && tokenOk;
}
/**
diff --git a/src/main/connect/connectionUtils.ts b/src/main/connect/connectionUtils.ts
index 6f1526c20..c7e5cf8a7 100644
--- a/src/main/connect/connectionUtils.ts
+++ b/src/main/connect/connectionUtils.ts
@@ -83,6 +83,21 @@ export class ConnectionUtils {
.ping();
}
+ /**
+ * Validate Xray connection.
+ * @param xray - xray URL
+ * @param username - Username
+ * @param password - Password
+ * @param accessToken - Access Token
+ */
+ public static async validateXrayConnection(xray: string, username: string, password: string, accessToken: string): Promise {
+ let jfrogClient: JfrogClient = this.createJfrogClient('', '', xray, username, password, accessToken);
+ return await jfrogClient
+ .xray()
+ .system()
+ .ping();
+ }
+
public static async isPlatformUrl(url: string, username: string, password: string, accessToken: string): Promise {
// If URL ends with '/xray', the URL is an Xray URL
if (!url || url.endsWith('/xray') || url.endsWith('/xray/')) {
diff --git a/src/main/dependencyUpdate/mavenDependencyUpdate.ts b/src/main/dependencyUpdate/mavenDependencyUpdate.ts
index 9a85e7404..8d499bf47 100644
--- a/src/main/dependencyUpdate/mavenDependencyUpdate.ts
+++ b/src/main/dependencyUpdate/mavenDependencyUpdate.ts
@@ -1,9 +1,14 @@
+import * as fs from 'fs';
import { MavenUtils } from '../utils/mavenUtils';
import { AbstractDependencyUpdate } from './abstractDependencyUpdate';
import { ScanUtils } from '../utils/scanUtils';
import { PackageType } from '../types/projectType';
import { DependencyIssuesTreeNode } from '../treeDataProviders/issuesTree/descriptorTree/dependencyIssuesTreeNode';
+import { FileTreeNode } from '../treeDataProviders/issuesTree/fileTreeNode';
+/**
+ * Represents a Maven dependency update implementation.
+ */
export class MavenDependencyUpdate extends AbstractDependencyUpdate {
constructor() {
super(PackageType.Maven);
@@ -16,11 +21,128 @@ export class MavenDependencyUpdate extends AbstractDependencyUpdate {
/** @override */
public update(dependency: DependencyIssuesTreeNode, version: string): void {
- const workspace: string = dependency.getDependencyProjectPath();
+ const [cmd, location] = this.buildUpdateCmd(dependency, version);
+ ScanUtils.executeCmd(cmd, location);
+ }
+
+ /**
+ * Builds the Maven update command based on the provided parameters.
+ * @param dependency The dependency to update.
+ * @param newVersion The new version to set.
+ * @returns The Maven update command.
+ **/
+ private buildUpdateCmd(dependency: DependencyIssuesTreeNode, newVersion: string): [string, string] {
+ const version: string | undefined = this.getDependencyVersionFromPom(dependency);
+ if (!version) {
+ throw new Error('Failed to find dependency version in pom.xml');
+ }
+ if (this.isPropertyVersion(version)) {
+ const prop: string = this.cleanPropertyVersion(version);
+ return [this.buildUpdatePropertyCmd(newVersion, prop), this.searchPropertyFilePath(dependency, prop)];
+ }
+ return [this.buildUpdateVersionCmd(dependency, newVersion), dependency.getDependencyProjectPath()];
+ }
+
+ private isPropertyVersion(version: string): boolean {
+ return version.trimStart().startsWith('${');
+ }
+
+ /**
+ * Cleans the version property from the evaluation tag.
+ * @param version The version property to clean.
+ * @returns The cleaned version property.
+ */
+ private cleanPropertyVersion(version: string) {
+ return version.substring(version.indexOf('${') + 2, version.indexOf('}'));
+ }
+
+ private getDependencyVersionFromPom(dependency: DependencyIssuesTreeNode): string | undefined {
const [groupId, artifactId] = MavenUtils.getGavArray(dependency);
- ScanUtils.executeCmd(
- 'mvn versions:use-dep-version -DgenerateBackupPoms=false -Dincludes=' + groupId + ':' + artifactId + ' -DdepVersion=' + version,
- workspace
+ const pomXmlContent: string = fs.readFileSync(dependency.getDependencyFilePath(), 'utf-8');
+ return this.getVersionProperty(MavenUtils.getDependencyXmlTag(pomXmlContent, groupId, artifactId));
+ }
+
+ /**
+ * Searches for the version property declaration in the POM files.
+ * @param dependency The dependency to search the version property for.
+ * @param propName The property name to search.
+ * @returns The path to the POM file where the property is declared.
+ **/
+ private searchPropertyFilePath(dependency: DependencyIssuesTreeNode, propName: string) {
+ return this.searchPropDeclareFile(
+ // If the there are multi pom project, search in all of them.
+ // If not, search in the current pom.
+ dependency.parent.parent ? dependency.parent.parent.children : [dependency.parent],
+ propName,
+ dependency.version
);
}
+
+ /**
+ * Searches for the property declaration in the POM files.
+ * @param files The POM files to search in.
+ * @param propName The property name to search.
+ * @param propVersion The property version to search.
+ * @returns The path to the POM file where the property is declared.
+ **/
+ private searchPropDeclareFile(files: FileTreeNode[], propName: string, propVersion: string): string {
+ for (let mavenProject of files) {
+ const pomXmlContent: string = fs.readFileSync(mavenProject.projectFilePath, 'utf-8');
+ if (this.matchProperty(pomXmlContent, propName, propVersion)) {
+ return mavenProject.getProjectPath();
+ }
+ }
+ throw Error('Failed to find property declaration in pom.xml');
+ }
+
+ /**
+ * Matches the version property from the XML tag.
+ * @param xmlTag The XML tag to match the version property from.
+ * @returns The matched version property, if found.
+ */
+ private getVersionProperty(xmlTag: string): string | undefined {
+ return xmlTag.match(/(.*)<\/version>/)?.[1];
+ }
+
+ /**
+ * Matches the property from the POM text.
+ * @param pomText The POM text to match the property from.
+ * @param propertyName The property name to match.
+ * @param propertyVersion The property version to match.
+ * @returns The matched property, if found.
+ **/
+ private matchProperty(pomText: string, propertyName: string, propertyVersion: string): string | undefined {
+ // Create a regular expression pattern to match the property
+ const pattern: RegExp = new RegExp(`<${propertyName}>\\s*(${propertyVersion})\\s*${propertyName}>`, 'i');
+
+ // Use the regular expression to find a match in the POM text
+ const match: RegExpMatchArray | null = pomText.match(pattern);
+ if (match && match[1]) {
+ return match[1];
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
+ * Builds the Maven update command based on the provided parameters.
+ * @param groupId The group ID of the dependency.
+ * @param artifactId The artifact ID of the dependency.
+ * @param newVersion The new version to set.
+ * @param currentProperty The version property to update if exists.
+ */
+ private buildUpdatePropertyCmd(newVersion: string, propertyName: string) {
+ return 'mvn versions:set-property -DgenerateBackupPoms=false -DnewVersion=' + newVersion + ' -Dproperty=' + propertyName;
+ }
+
+ /**
+ * Builds the Maven update command based on the provided parameters.
+ * @param dependency The dependency to update.
+ * @param newVersion The new version to set.
+ * @returns The Maven update command.
+ **/
+ private buildUpdateVersionCmd(dependency: DependencyIssuesTreeNode, newVersion: string) {
+ const [groupId, artifactId] = MavenUtils.getGavArray(dependency);
+ return 'mvn versions:use-dep-version -DgenerateBackupPoms=false -Dincludes=' + groupId + ':' + artifactId + ' -DdepVersion=' + newVersion;
+ }
}
diff --git a/src/main/diagnostics/codeFileActionProvider.ts b/src/main/diagnostics/codeFileActionProvider.ts
index 3e913ae32..186037ed8 100644
--- a/src/main/diagnostics/codeFileActionProvider.ts
+++ b/src/main/diagnostics/codeFileActionProvider.ts
@@ -4,7 +4,6 @@ import { CodeIssueTreeNode } from '../treeDataProviders/issuesTree/codeFileTree/
import { FileTreeNode } from '../treeDataProviders/issuesTree/fileTreeNode';
import { IssueTreeNode } from '../treeDataProviders/issuesTree/issueTreeNode';
import { Severity, SeverityUtils } from '../types/severity';
-
import { AbstractFileActionProvider } from './abstractFileActionProvider';
export class CodeFileActionProvider extends AbstractFileActionProvider implements vscode.CodeActionProvider {
diff --git a/src/main/scanLogic/scanGraphLogic.ts b/src/main/scanLogic/scanGraphLogic.ts
index adc8db1d3..5d8c720fc 100644
--- a/src/main/scanLogic/scanGraphLogic.ts
+++ b/src/main/scanLogic/scanGraphLogic.ts
@@ -23,7 +23,7 @@ export class GraphScanLogic {
*/
public async scan(graphRoot: RootNode, progress: XrayScanProgress, checkCanceled: () => void): Promise {
let graphRequest: IGraphRequestModel = {
- component_id: graphRoot.generalInfo.artifactId ?? graphRoot.dependencyId,
+ component_id: graphRoot.generalInfo.artifactId ?? graphRoot.xrayDependencyId,
nodes: this.getFlattenRequestModelNodes(graphRoot, new Set())
} as IGraphRequestModel;
if (!graphRequest.nodes || graphRequest.nodes.length === 0) {
@@ -48,10 +48,10 @@ export class GraphScanLogic {
public getFlattenRequestModelNodes(dependency: DependenciesTreeNode, components: Set): IGraphRequestModel[] | undefined {
let nodes: IGraphRequestModel[] = [];
for (let child of dependency.children) {
- if (child.dependencyId && !components.has(child.dependencyId)) {
- components.add(child.dependencyId);
+ if (child.xrayDependencyId && !components.has(child.xrayDependencyId)) {
+ components.add(child.xrayDependencyId);
nodes.push({
- component_id: child.dependencyId
+ component_id: child.xrayDependencyId
} as IGraphRequestModel);
}
let childNodes: IGraphRequestModel[] | undefined = this.getFlattenRequestModelNodes(child, components);
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/goTree.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/goTree.ts
index 966ff85dd..b033aee15 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/goTree.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/goTree.ts
@@ -153,8 +153,7 @@ export class GoTreeNode extends RootNode {
private addComponentToScan(dependenciesTreeNode: DependenciesTreeNode) {
let componentId: string = dependenciesTreeNode.generalInfo.artifactId + ':' + dependenciesTreeNode.generalInfo.version;
- this.projectDetails.addDependency(GoTreeNode.COMPONENT_PREFIX + componentId);
- dependenciesTreeNode.dependencyId = GoTreeNode.COMPONENT_PREFIX + componentId;
+ dependenciesTreeNode.xrayDependencyId = GoTreeNode.COMPONENT_PREFIX + componentId;
}
private getNameVersionTuple(value: string): string[] {
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/mavenTree.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/mavenTree.ts
index 162d8c129..e62f49409 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/mavenTree.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/mavenTree.ts
@@ -66,9 +66,8 @@ export class MavenTreeNode extends RootNode {
let child: DependenciesTreeNode = new DependenciesTreeNode(gavGeneralInfo, treeCollapsibleState, parent);
child.label = group + ':' + name;
let componentId: string = gavGeneralInfo.getComponentId();
- this.projectDetails.addDependency(MavenTreeNode.COMPONENT_PREFIX + componentId);
- child.dependencyId = MavenTreeNode.COMPONENT_PREFIX + componentId;
+ child.xrayDependencyId = MavenTreeNode.COMPONENT_PREFIX + componentId;
if (rawDependenciesPtr.index + 1 < rawDependenciesList.length) {
while (
rawDependenciesPtr.index + 1 < rawDependenciesList.length &&
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/npmTree.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/npmTree.ts
index 025dcca9d..3e74b5feb 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/npmTree.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/npmTree.ts
@@ -65,8 +65,7 @@ export class NpmTreeNode extends RootNode {
: vscode.TreeItemCollapsibleState.None;
let child: DependenciesTreeNode = new DependenciesTreeNode(generalInfo, treeCollapsibleState, dependenciesTreeNode);
let componentId: string = key + ':' + version;
- this.projectDetails.addDependency(NpmTreeNode.COMPONENT_PREFIX + componentId);
- child.dependencyId = NpmTreeNode.COMPONENT_PREFIX + componentId;
+ child.xrayDependencyId = NpmTreeNode.COMPONENT_PREFIX + componentId;
this.populateDependenciesTree(child, childDependencies);
}
}
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/nugetTree.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/nugetTree.ts
index 36eb7bc52..18fd1b97a 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/nugetTree.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/nugetTree.ts
@@ -37,8 +37,7 @@ export class NugetTreeNode extends RootNode {
childDependencies.length > 0 ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None;
let child: DependenciesTreeNode = new DependenciesTreeNode(generalInfo, treeCollapsibleState, dependenciesTreeNode, '');
let combined: string = id + ':' + version;
- this.projectDetails.addDependency(NugetTreeNode.COMPONENT_PREFIX + combined);
- child.dependencyId = NugetTreeNode.COMPONENT_PREFIX + combined;
+ child.xrayDependencyId = NugetTreeNode.COMPONENT_PREFIX + combined;
this.populateDependenciesTree(child, childDependencies);
}
}
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/pypiTree.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/pypiTree.ts
index e02292ea3..82dc5a1fb 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/pypiTree.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/pypiTree.ts
@@ -40,8 +40,7 @@ export class PypiTreeNode extends RootNode {
: vscode.TreeItemCollapsibleState.None;
let child: DependenciesTreeNode = new DependenciesTreeNode(generalInfo, treeCollapsibleState, dependenciesTreeNode);
let componentId: string = dependency.key + ':' + version;
- this.projectDetails.addDependency(PypiTreeNode.COMPONENT_PREFIX + componentId);
- child.dependencyId = PypiTreeNode.COMPONENT_PREFIX + componentId;
+ child.xrayDependencyId = PypiTreeNode.COMPONENT_PREFIX + componentId;
this.populateDependenciesTree(child, childDependencies);
}
}
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/rootTree.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/rootTree.ts
index 97b9df0f3..35e4e1e98 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/rootTree.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/rootTree.ts
@@ -6,6 +6,7 @@ import { PackageType } from '../../../types/projectType';
import { DependenciesTreeNode } from '../dependenciesTreeNode';
import { DependencyScanResults } from '../../../types/workspaceIssuesDetails';
import { Utils } from '../../../utils/utils';
+import { IImpactGraph, IImpactGraphNode } from 'jfrog-ide-webview';
export enum BuildTreeErrorType {
NotInstalled = '[Not Installed]',
@@ -13,6 +14,7 @@ export enum BuildTreeErrorType {
}
export class RootNode extends DependenciesTreeNode {
+ public static IMPACT_PATHS_LIMIT: number = 50;
private _projectDetails: ProjectDetails;
private _workspaceFolder: string;
@@ -73,4 +75,49 @@ export class RootNode extends DependenciesTreeNode {
}
return result;
}
+
+ /**
+ * Retrieves the impact paths of all child components, recursively from a given root,
+ * The number of impact paths collected may be limited by the '{@link RootNode.IMPACT_PATHS_LIMIT}'.
+ * @param vulnerableDependencyName - the name of the component used to build a path to the root.
+ * @param componentWithIssue - the version of the component used to build a path to the root.
+ */
+ public createImpactedGraph(vulnerableDependencyName: string, vulnerableDependencyVersion: string): IImpactGraph {
+ return RootNode.collectPaths(vulnerableDependencyName + ':' + vulnerableDependencyVersion, this.children, 0);
+ }
+
+ private static collectPaths(vulnerableDependencyId: string, children: DependenciesTreeNode[], size: number): IImpactGraph {
+ let impactPaths: IImpactGraphNode[] = [];
+ for (let child of children) {
+ if (impactPaths.find(node => node.name === child.componentId)) {
+ // Loop encountered
+ continue;
+ }
+
+ if (child.componentId === vulnerableDependencyId) {
+ if (size < RootNode.IMPACT_PATHS_LIMIT) {
+ RootNode.appendDirectImpact(impactPaths, child.componentId);
+ }
+ size++;
+ }
+
+ let indirectImpact: IImpactGraph = RootNode.collectPaths(vulnerableDependencyId, child.children, size);
+ RootNode.appendIndirectImpact(impactPaths, child.componentId, indirectImpact);
+ size = indirectImpact.pathsCount ?? size;
+ }
+ return { root: { children: impactPaths }, pathsCount: size } as IImpactGraph;
+ }
+
+ private static appendDirectImpact(impactPaths: IImpactGraphNode[], componentId: string): void {
+ impactPaths.push({ name: componentId } as IImpactGraphNode);
+ }
+
+ private static appendIndirectImpact(impactPaths: IImpactGraphNode[], componentId: string, indirectImpact: IImpactGraph): void {
+ if (!!indirectImpact.root.children?.length) {
+ impactPaths.push({
+ name: componentId,
+ children: indirectImpact.root.children
+ } as IImpactGraphNode);
+ }
+ }
}
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/yarnTree.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/yarnTree.ts
index ee099c998..b0503ef8d 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/yarnTree.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesRoot/yarnTree.ts
@@ -8,6 +8,8 @@ import { DependenciesTreeNode } from '../dependenciesTreeNode';
import { BuildTreeErrorType, RootNode } from './rootTree';
import { PackageType } from '../../../types/projectType';
import { LogManager } from '../../../log/logManager';
+import { IImpactGraph } from 'jfrog-ide-webview';
+import { YarnImpactGraphCreator } from '../../utils/yarnImpactGraph';
export class YarnTreeNode extends RootNode {
private static readonly COMPONENT_PREFIX: string = 'npm://';
@@ -16,11 +18,11 @@ export class YarnTreeNode extends RootNode {
super(workspaceFolder, PackageType.Yarn, parent);
}
- public refreshDependencies() {
- let listResults: any;
+ public loadYarnDependencies() {
+ let results: any;
try {
- listResults = this.runYarnList();
- this.populateDependencyTree(this, listResults?.data?.trees);
+ results = this.runYarnList();
+ this.loadYarnList(this, results?.data?.trees);
} catch (error) {
this._logManager.logError(error, false);
this._logManager.logMessageAndToastErr(
@@ -36,36 +38,48 @@ export class YarnTreeNode extends RootNode {
this.label = this.projectDetails.name;
}
- private populateDependencyTree(dependencyTreeNode: DependenciesTreeNode, nodes: any[]) {
- if (!nodes) {
+ /** @override */
+ public createImpactedGraph(name: string, version: string): IImpactGraph {
+ return new YarnImpactGraphCreator(name, version, this.generalInfo.getComponentId(), this.workspaceFolder, this._logManager).create();
+ }
+
+ /**
+ * Parse and load all yarn list's dependencies into concert object.
+ * @param parent - Parent Yarn dependency that is loaded into object from 'yarn list'.
+ * @param children - Child dependency of parent in Yarn list form
+ * @returns
+ */
+ private loadYarnList(parent: DependenciesTreeNode, children: any[]) {
+ if (!children) {
return;
}
- for (let node of nodes) {
+ for (let node of children) {
// Shadow dependencies does not always contain exact version, and therefore we should skip them.
if (node.shadow) {
continue;
}
- const scope: string = NpmUtils.getDependencyScope(node.name);
- let lastIndexOfAt: number = node.name.lastIndexOf('@');
- let dependencyName: string = node.name.substring(0, lastIndexOfAt);
- let dependencyVersion: string = node.name.substring(lastIndexOfAt + 1);
-
- let generalInfo: GeneralInfo = new GeneralInfo(dependencyName, dependencyVersion, scope !== '' ? [scope] : [], '', PackageType.Yarn);
- let hasRealChildren: boolean = this.hasRealChildren(node.children);
- let treeCollapsibleState: vscode.TreeItemCollapsibleState = hasRealChildren
- ? vscode.TreeItemCollapsibleState.Collapsed
- : vscode.TreeItemCollapsibleState.None;
- let componentId: string = dependencyName + ':' + dependencyVersion;
- this.projectDetails.addDependency(YarnTreeNode.COMPONENT_PREFIX + componentId);
-
- let child: DependenciesTreeNode = new DependenciesTreeNode(generalInfo, treeCollapsibleState, dependencyTreeNode);
- child.dependencyId = YarnTreeNode.COMPONENT_PREFIX + componentId;
- if (hasRealChildren) {
- this.populateDependencyTree(child, node.children);
+ this.addDependency(parent, node);
+ if (this.hasRealChildren(node.children)) {
+ this.loadYarnList(parent, node.children);
}
}
}
+ private extractDependencyInfo(node: any): string[] {
+ const scope: string = NpmUtils.getDependencyScope(node.name);
+ let lastIndexOfAt: number = node.name.lastIndexOf('@');
+ let name: string = node.name.substring(0, lastIndexOfAt);
+ let version: string = node.name.substring(lastIndexOfAt + 1);
+ return [name, version, scope];
+ }
+
+ private addDependency(parent: DependenciesTreeNode, node: any): void {
+ const [dependencyName, dependencyVersion, scope] = this.extractDependencyInfo(node);
+ const generalInfo: GeneralInfo = new GeneralInfo(dependencyName, dependencyVersion, scope !== '' ? [scope] : [], '', PackageType.Yarn);
+ new DependenciesTreeNode(generalInfo, vscode.TreeItemCollapsibleState.None, parent).xrayDependencyId =
+ YarnTreeNode.COMPONENT_PREFIX + dependencyName + ':' + dependencyVersion;
+ }
+
/**
* Return true if the child dependencies contain any non shadowed dependency.
* @param childDependencies - Child dependencies at 'yarn list' results
diff --git a/src/main/treeDataProviders/dependenciesTree/dependenciesTreeNode.ts b/src/main/treeDataProviders/dependenciesTree/dependenciesTreeNode.ts
index 596f05619..c61a41a63 100644
--- a/src/main/treeDataProviders/dependenciesTree/dependenciesTreeNode.ts
+++ b/src/main/treeDataProviders/dependenciesTree/dependenciesTreeNode.ts
@@ -12,7 +12,7 @@ export class DependenciesTreeNode extends vscode.TreeItem {
private _licenses: Set = new Set(license => license.licenseName);
private _issues: Set = new Set(issue => issue.issue_id);
private _topSeverity: Severity;
- private _dependencyId: string = this._generalInfo.artifactId;
+ private _xrayDependencyId: string = this._generalInfo.artifactId;
constructor(
protected _generalInfo: GeneralInfo,
@@ -34,12 +34,12 @@ export class DependenciesTreeNode extends vscode.TreeItem {
}
}
- public get dependencyId(): string {
- return this._dependencyId;
+ public get xrayDependencyId(): string {
+ return this._xrayDependencyId;
}
- public set dependencyId(value: string) {
- this._dependencyId = value;
+ public set xrayDependencyId(value: string) {
+ this._xrayDependencyId = value;
}
public get componentId(): string {
diff --git a/src/main/treeDataProviders/issuesTree/descriptorTree/dependencyIssuesTreeNode.ts b/src/main/treeDataProviders/issuesTree/descriptorTree/dependencyIssuesTreeNode.ts
index 6d3c88509..99dde0184 100644
--- a/src/main/treeDataProviders/issuesTree/descriptorTree/dependencyIssuesTreeNode.ts
+++ b/src/main/treeDataProviders/issuesTree/descriptorTree/dependencyIssuesTreeNode.ts
@@ -7,6 +7,7 @@ import { CveTreeNode } from './cveTreeNode';
import { LicenseIssueTreeNode } from './licenseIssueTreeNode';
import { ContextKeys } from '../../../constants/contextKeys';
import { ProjectDependencyTreeNode } from './projectDependencyTreeNode';
+import { IssuesRootTreeNode } from '../issuesRootTreeNode';
export class DependencyIssuesTreeNode extends vscode.TreeItem {
// Infer from data
@@ -127,6 +128,10 @@ export class DependencyIssuesTreeNode extends vscode.TreeItem {
return this.parent.getProjectPath();
}
+ public getDependencyRootProject(): IssuesRootTreeNode | undefined {
+ return this.parent.parent;
+ }
+
public getDependencyFilePath(): string {
return this.parent.getProjectFilePath();
}
diff --git a/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts b/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts
index 0d17654c3..c12e8a66e 100644
--- a/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts
+++ b/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts
@@ -5,7 +5,6 @@ import { PackageType } from '../../../types/projectType';
import { IssueTreeNode } from '../issueTreeNode';
import { CveTreeNode } from './cveTreeNode';
import { IComponent } from 'jfrog-client-js';
-import * as path from 'path';
import { Utils } from '../../../utils/utils';
import { CveApplicableDetails } from '../../../scanLogic/scanRunners/applicabilityScan';
@@ -24,12 +23,10 @@ export class ProjectDependencyTreeNode extends FileTreeNode {
protected _applicableScanTimeStamp?: number;
protected _packageType: PackageType;
- private projectPath: string;
constructor(filePath: string, packageType?: PackageType, parent?: IssuesRootTreeNode) {
super(filePath, parent);
this._packageType = packageType ?? PackageType.Unknown;
- this.projectPath = path.dirname(filePath);
}
/** @override */
@@ -148,10 +145,6 @@ export class ProjectDependencyTreeNode extends FileTreeNode {
return this._packageType;
}
- public getProjectPath() {
- return this.projectPath;
- }
-
public getProjectFilePath() {
return this.projectFilePath;
}
diff --git a/src/main/treeDataProviders/issuesTree/fileTreeNode.ts b/src/main/treeDataProviders/issuesTree/fileTreeNode.ts
index e4a99b419..fafe3854d 100644
--- a/src/main/treeDataProviders/issuesTree/fileTreeNode.ts
+++ b/src/main/treeDataProviders/issuesTree/fileTreeNode.ts
@@ -14,12 +14,13 @@ import { IssueTreeNode } from './issueTreeNode';
export abstract class FileTreeNode extends vscode.TreeItem {
protected _severity: Severity = Severity.Unknown;
private _name: string;
+ protected projectPath: string;
constructor(private _fullPath: string, private _parent?: IssuesRootTreeNode, private _timeStamp?: number) {
super(_fullPath);
this._name = Utils.getLastSegment(_fullPath);
this.label = this._name;
-
+ this.projectPath = path.dirname(_fullPath);
this.contextValue += ContextKeys.COPY_TO_CLIPBOARD_ENABLED;
}
@@ -140,4 +141,8 @@ export abstract class FileTreeNode extends vscode.TreeItem {
public set projectFilePath(value: string) {
this._fullPath = value;
}
+
+ public getProjectPath() {
+ return this.projectPath;
+ }
}
diff --git a/src/main/treeDataProviders/utils/dependencyUtils.ts b/src/main/treeDataProviders/utils/dependencyUtils.ts
index a247c3261..8c64eabd1 100644
--- a/src/main/treeDataProviders/utils/dependencyUtils.ts
+++ b/src/main/treeDataProviders/utils/dependencyUtils.ts
@@ -12,7 +12,7 @@ import { MavenUtils } from '../../utils/mavenUtils';
import { NpmUtils } from '../../utils/npmUtils';
import { PypiUtils } from '../../utils/pypiUtils';
import { YarnUtils } from '../../utils/yarnUtils';
-import { IImpactGraph, IImpactGraphNode, ILicense } from 'jfrog-ide-webview';
+import { IImpactGraph, ILicense } from 'jfrog-ide-webview';
import { IssueTreeNode } from '../issuesTree/issueTreeNode';
import { FocusType } from '../../constants/contextKeys';
import { DependencyScanResults, ScanResults } from '../../types/workspaceIssuesDetails';
@@ -33,7 +33,6 @@ import { ApplicabilityRunner } from '../../scanLogic/scanRunners/applicabilitySc
export class DependencyUtils {
public static readonly FAIL_TO_SCAN: string = '[Fail to scan]';
- public static IMPACT_PATHS_LIMIT: number = 50;
/**
* Scan all the dependencies of a given package for security issues and populate the given data and view objects with the information.
@@ -327,18 +326,21 @@ export class DependencyUtils {
if (!issues) {
return paths;
}
+
for (let i: number = 0; i < issues.length; i++) {
let issue: IVulnerability = issues[i];
for (let [componentId, component] of Object.entries(issue.components)) {
- const childGraph: IImpactGraph = this.getChildrenGraph(descriptorGraph, component, 0);
- paths.set(issue.issue_id + componentId, {
- root: {
- name: this.getGraphName(descriptorGraph),
- children: childGraph.root.children
- },
- pathsCount: childGraph.pathsCount,
- pathsLimit: DependencyUtils.IMPACT_PATHS_LIMIT
- } as IImpactGraph);
+ const impactedPaths: IImpactGraph = descriptorGraph.createImpactedGraph(component.package_name, component.package_version);
+ if (impactedPaths.root) {
+ paths.set(issue.issue_id + componentId, {
+ root: {
+ name: this.getGraphName(descriptorGraph),
+ children: impactedPaths.root?.children
+ },
+ pathsCount: impactedPaths.pathsCount,
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
+ } as IImpactGraph);
+ }
}
}
return paths;
@@ -350,41 +352,6 @@ export class DependencyUtils {
: descriptorGraph.componentId;
}
- /**
- * Retrieves the impact paths of all child components, recursively from a given root,
- * The number of impact paths collected may be limited by the '{@link DependencyUtils.IMPACT_PATHS_LIMIT}'.
- * @param root - the root to get it's children impact
- * @param componentWithIssue - the component to generate the impact path for it
- * @param size - the total size of the impacted path
- */
- private static getChildrenGraph(root: DependenciesTreeNode, componentWithIssue: IComponent, size: number): IImpactGraph {
- let impactPaths: IImpactGraphNode[] = [];
- for (let child of root.children) {
- let impactChild: IImpactGraphNode | undefined = impactPaths.find(p => p.name === child.componentId);
- if (!impactChild) {
- if (child.componentId === componentWithIssue.package_name + ':' + componentWithIssue.package_version) {
- // Direct impact
- if (size < DependencyUtils.IMPACT_PATHS_LIMIT) {
- impactPaths.push({
- name: child.componentId
- } as IImpactGraphNode);
- }
- size++;
- }
- // indirect impact
- let indirectImpact: IImpactGraph = this.getChildrenGraph(child, componentWithIssue, size);
- if (!!indirectImpact.root.children?.length) {
- impactPaths.push({
- name: child.componentId,
- children: indirectImpact.root.children
- } as IImpactGraphNode);
- }
- size = indirectImpact.pathsCount ?? size;
- }
- }
- return { root: { children: impactPaths }, pathsCount: size } as IImpactGraph;
- }
-
/**
* Populate the provided issues data to the project node (view element)
* @param projectNode - the project node that will be populated
diff --git a/src/main/treeDataProviders/utils/yarnImpactGraph.ts b/src/main/treeDataProviders/utils/yarnImpactGraph.ts
new file mode 100644
index 000000000..58e69a48a
--- /dev/null
+++ b/src/main/treeDataProviders/utils/yarnImpactGraph.ts
@@ -0,0 +1,272 @@
+import { IImpactGraph, IImpactGraphNode } from 'jfrog-ide-webview';
+import { RootNode } from '../dependenciesTree/dependenciesRoot/rootTree';
+import { ScanUtils } from '../../utils/scanUtils';
+import { LogManager } from '../../log/logManager';
+
+export type YarnWhyItem = StepItem | InfoItem;
+
+/**
+ * Represents a step item in the "yarn why" output.
+ */
+interface StepItem {
+ type: 'list';
+ data: ListData;
+}
+
+/**
+ * Represents an info item in the "yarn why" output.
+ */
+interface InfoItem {
+ type: 'info';
+ data: string;
+}
+
+/**
+ * Represents data within a step item, specifically a list of reasons.
+ */
+interface ListData {
+ type: 'reasons';
+ items: string[];
+}
+/**
+ * Utility class for creating an impact graph based on "yarn why" command output.
+ */
+export class YarnImpactGraphCreator {
+ /**
+ * Creates an instance of YarnImpactGraphUtil.
+ * @param _dependencyName - The name of the impact dependency.
+ * @param _dependencyVersion - The version of the impact dependency.
+ * @param _projectName - The name of the project.
+ * @param _workspaceFolder - The folder where the project is located.
+ */
+ constructor(
+ private _dependencyName: string,
+ private _dependencyVersion: string,
+ private _projectName: string,
+ private _workspaceFolder: string,
+ private _logManager: LogManager
+ ) {}
+
+ /**
+ * Creates and returns an impact graph based on "yarn why" command output.
+ * @returns An impact graph.
+ */
+ public create(): IImpactGraph {
+ const dependencyChain: string[] = this.findDependencyChain(this.runYarnWhy());
+ if (dependencyChain.length > 0) {
+ return this.createImpactGraphFromChains(dependencyChain);
+ }
+
+ return {} as IImpactGraph;
+ }
+
+ /**
+ * Finds the dependency chain, aka, the path from the dependency to the root, based on the supplied "yarn why" command output.
+ * The dependency chain may appear as a part of a text or in a list of reasons.
+ *
+ * Example 1 (Text):
+ * {"type":"info","data":"This module exists because \"jest-cli#istanbul-api#mkdirp\" depends on it."}
+ *
+ * Example 2 (List):
+ * {"type":"list","data":{"type":"reasons","items":["Specified in \"dependencies\"","Hoisted from \"jest-cli#node-notifier#minimist\"","Hoisted from \"jest-cli#sane#minimist\""]}}
+ *
+ * @param output - The "yarn why" command output to analyze.
+ * @returns A list of vulnerable dependency chains to the root.
+ */
+ private findDependencyChain(output: YarnWhyItem[]): string[] {
+ const startIndex: number | undefined = this.findDependencyPosition(this._dependencyVersion, output);
+ // Zero could be a valid index
+ if (startIndex === undefined) {
+ return [];
+ }
+ for (let i: number = startIndex + 1; i < output.length; i++) {
+ const item: YarnWhyItem = output[i];
+ switch (item.type) {
+ case 'list':
+ return this.extractMultipleChain(item.data.items);
+ case 'info':
+ if (item.data.startsWith('This module exists because')) {
+ return this.extractMultipleChain([item.data]);
+ }
+ }
+ }
+
+ return [];
+ }
+
+ /**
+ * Dependency may present in multiple versions in yarn why output, therefore, finds the position of the specified version in the "yarn why" command output.
+ * @param version - The version to search for.
+ * @param output - The "yarn why" command output to search within.
+ * @returns The index of the found version or undefined if not found.
+ */
+ private findDependencyPosition(version: string, output: YarnWhyItem[]): number | undefined {
+ for (let i: number = 0; i < output.length; i++) {
+ const item: YarnWhyItem = output[i];
+ if (item.type === 'info' && item.data.includes(version)) {
+ this._logManager.debug('found dependency version ' + version + " from 'yarn why' at: " + item.data);
+ return i;
+ }
+ }
+ return undefined;
+ }
+
+ /**
+ * Extracts multiple dependency chains from a list raw dependency string.
+ * @param list - An array of strings representing raw dependency chains.
+ * @returns An array of extracted dependency chains.
+ *
+ * Example input - ["Specified in \"dependencies\"","Hoisted from \"jest-cli#node-notifier#minimist\"","Hoisted from \"jest-cli#sane#minimist\""]
+ * Example output - ["minimist","jest-cli#node-notifier#minimist","jest-cli#sane#minimist"]
+ */
+ private extractMultipleChain(list: string[]): string[] {
+ const results: string[] = [];
+ list.forEach(item => {
+ const chain: string | undefined = this.extractChain(item);
+ if (chain) {
+ this._logManager.debug("found dependency chain'" + chain + "' from" + item);
+ results.push(chain);
+ }
+ });
+ return results;
+ }
+
+ /**
+ * Extracts a single dependency chain from a raw dependency string.
+ * @param rawDependencyChain - The raw dependency chain string.
+ * @returns The extracted dependency chain or undefined if not found.
+ */
+ private extractChain(rawDependencyChain: string): string | undefined {
+ if (rawDependencyChain.toLowerCase().includes('specified in')) {
+ return this._dependencyName;
+ }
+ // Extract the path from the dependency chain using quotes
+ const startIndex: number = rawDependencyChain.indexOf('"');
+ const endIndex: number = rawDependencyChain.indexOf('"', startIndex + 1);
+ if (startIndex !== -1 && endIndex !== -1) {
+ return rawDependencyChain.substring(startIndex + 1, endIndex);
+ }
+ return undefined;
+ }
+
+ /**
+ * Creates an impact graph based on a list of dependency chains.
+ * @param chains - An array of dependency chains as strings.
+ * @returns An impact graph object.
+ */
+ private createImpactGraphFromChains(chains: string[]): IImpactGraph {
+ const trees: IImpactGraphNode[] = [];
+ for (let index: number = 0; index < chains.length && index < RootNode.IMPACT_PATHS_LIMIT; index++) {
+ trees.push(this.createImpactGraphNodeFromChain(chains[index]));
+ }
+ return {
+ root: this.mergeAllTrees(trees),
+ pathsCount: Math.min(RootNode.IMPACT_PATHS_LIMIT, chains.length),
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
+ } as IImpactGraph;
+ }
+
+ /**
+ * Merges two impact graph trees into a single tree.
+ * @param root1 - The root of the first tree to be merged.
+ * @param root2 - The root of the second tree to be merged.
+ * @returns The merged impact graph tree.
+ */
+ public mergeTrees(root1: IImpactGraphNode | null, root2: IImpactGraphNode | null): IImpactGraphNode | null {
+ if (!root1 || !root2) {
+ return root1 || root2;
+ }
+ // Create a merged node with the same name
+ const mergedNode: IImpactGraphNode = { name: root1.name };
+
+ // Merge the children recursively
+ if (root1.children && root2.children) {
+ const mergedChildren: IImpactGraphNode[] = [];
+
+ for (const child1 of root1.children) {
+ const matchingChild2: IImpactGraphNode | undefined = root2.children.find(child2 => child2.name === child1.name);
+
+ if (matchingChild2) {
+ const tree: IImpactGraphNode | null = this.mergeTrees(child1, matchingChild2);
+ if (tree) {
+ mergedChildren.push(tree);
+ }
+ } else {
+ // If not found in root2, keep the child from root1
+ mergedChildren.push(child1);
+ }
+ }
+
+ for (const child2 of root2.children) {
+ const matchingChild1: IImpactGraphNode | undefined = root1.children.find(child1 => child1.name === child2.name);
+
+ if (!matchingChild1) {
+ // Add children from root2 that are not present in root1
+ mergedChildren.push(child2);
+ }
+ }
+
+ mergedNode.children = mergedChildren;
+ } else if (root1.children) {
+ mergedNode.children = root1.children;
+ } else {
+ mergedNode.children = root2.children;
+ }
+
+ return mergedNode;
+ }
+
+ /**
+ * Merges multiple impact graph trees into a single tree.
+ * @param trees - An array of impact graph trees.
+ * @returns The merged impact graph tree.
+ */
+ public mergeAllTrees(trees: IImpactGraphNode[]): IImpactGraphNode | null {
+ if (trees.length === 0) {
+ return null;
+ }
+
+ let mergedTree: IImpactGraphNode | null = trees[0];
+
+ for (let i: number = 1; i < trees.length; i++) {
+ mergedTree = this.mergeTrees(mergedTree, trees[i]);
+ }
+ return mergedTree;
+ }
+
+ /**
+ * Creates an impact graph node from a single dependency chain.
+ * @param chain - A single dependency chain as a string.
+ * @returns An impact graph node.
+ */
+ private createImpactGraphNodeFromChain(chain: string): IImpactGraphNode {
+ const splitted: string[] = chain.split('#');
+ let currentNode: IImpactGraphNode = { name: this._projectName };
+ const root: IImpactGraphNode = currentNode;
+ for (const item of splitted) {
+ const child: IImpactGraphNode = { name: item };
+ currentNode.children = [child];
+ currentNode = child;
+ }
+ if (currentNode.name !== this._dependencyName) {
+ currentNode.children = [{ name: this._dependencyName + ':' + this._dependencyVersion }];
+ } else {
+ currentNode.name = this._dependencyName + ':' + this._dependencyVersion;
+ }
+ return root;
+ }
+
+ /**
+ * Executes the "yarn why" command and parses its JSON output.
+ */
+ protected runYarnWhy(): YarnWhyItem[] {
+ const cmd: string = 'yarn why --json --no-progress ' + this._dependencyName;
+ this._logManager.debug('Running ' + cmd + ' at ' + this._workspaceFolder);
+ const output: string = ScanUtils.executeCmd(cmd, this._workspaceFolder).toString();
+ this._logManager.debug('yarn why output ' + output);
+ return output
+ .split('\n')
+ .filter(line => line.trim() !== '')
+ .map((line: string) => JSON.parse(line));
+ }
+}
diff --git a/src/main/types/projectDetails.ts b/src/main/types/projectDetails.ts
index 1bbf3bf09..d6dc1e962 100644
--- a/src/main/types/projectDetails.ts
+++ b/src/main/types/projectDetails.ts
@@ -1,10 +1,7 @@
-import { ComponentDetails } from 'jfrog-client-js';
import * as path from 'path';
import { PackageType } from './projectType';
-import Set from 'typescript-collections/dist/lib/Set';
export class ProjectDetails {
- private _dependencies: Set = new Set();
private _name: string;
constructor(private _path: string, private _type: PackageType) {
@@ -34,23 +31,4 @@ export class ProjectDetails {
public set path(value: string) {
this._path = value;
}
-
- public get dependencies(): Set {
- return this._dependencies;
- }
-
- public set dependencies(value: Set) {
- this._dependencies = value;
- }
-
- /**
- * @param dependencyId - component id of the dependency
- */
- public addDependency(dependencyId: string) {
- this._dependencies.add(new ComponentDetails(dependencyId));
- }
-
- public toArray(): ComponentDetails[] {
- return this._dependencies.toArray();
- }
}
diff --git a/src/main/utils/mavenUtils.ts b/src/main/utils/mavenUtils.ts
index 0e9e8702f..6c7190779 100644
--- a/src/main/utils/mavenUtils.ts
+++ b/src/main/utils/mavenUtils.ts
@@ -43,10 +43,10 @@ export class MavenUtils {
let res: vscode.Range[] = [];
let pomXmlContent: string = document.getText();
let [groupId, artifactId, version] = MavenUtils.getGavArrayFromId(dependencyId);
- let dependencyTag: string = MavenUtils.getDependencyTag(pomXmlContent, groupId, artifactId);
- if (dependencyTag) {
- let startIndex: vscode.Position = document.positionAt(pomXmlContent.indexOf(dependencyTag));
- let arr: string[] = dependencyTag.split(/\r?\n/).filter(line => line.trim() !== '');
+ let tag: string = MavenUtils.getDependencyXmlTag(pomXmlContent, groupId, artifactId);
+ if (tag) {
+ let startIndex: vscode.Position = document.positionAt(pomXmlContent.indexOf(tag));
+ let arr: string[] = tag.split(/\r?\n/).filter(line => line.trim() !== '');
for (let i: number = 0; i < arr.length; i++) {
let depInfo: string = arr[i].trim().toLowerCase();
if (this.isDependencyMatch(groupId, artifactId, version, depInfo, focusType)) {
@@ -82,7 +82,7 @@ export class MavenUtils {
* @param groupId - The dependency's group ID
* @param artifactId - The dependency's artifact ID
*/
- public static getDependencyTag(pomXmlContent: string, groupId: string, artifactId: string): string {
+ public static getDependencyXmlTag(pomXmlContent: string, groupId: string, artifactId: string): string {
let groupIdRegex: RegExp = new RegExp(`\\s*${groupId}\\s*`, 'gi');
let artifactIdRegex: RegExp = new RegExp(`\\s*${artifactId}\\s*`, 'gi');
let dependencyMatch: string[] | undefined = pomXmlContent
@@ -368,13 +368,6 @@ export class MavenUtils {
return pomTreeArray.findIndex(pomTree => pomTree.pomGav === pomGav);
}
- /**
- * @param rawDependency Raw dependency text
- */
- public static getProjectInfo(rawDependency: string): [string, string, string, string] {
- return MavenUtils.getDependencyInfo(rawDependency.replace(/\s/g, '') + ':dummyScope');
- }
-
/**
* @param rawDependency - e.g. "| | +- javax.mail:mail:jar:1.4:compile"
* @returns [groupId,ArtifactId,version]
diff --git a/src/main/utils/utils.ts b/src/main/utils/utils.ts
index 3eef687d0..0ec702c69 100644
--- a/src/main/utils/utils.ts
+++ b/src/main/utils/utils.ts
@@ -19,6 +19,10 @@ export class Utils {
await vscode.commands.executeCommand('workbench.action.openSettings', `@ext:${Utils.getExtensionId()}` + (id ? ` ${id}` : ''));
}
+ public static async openFeedback(): Promise {
+ await vscode.env.openExternal(vscode.Uri.parse('https://github.com/jfrog/jfrog-vscode-extension/discussions/new/choose'));
+ }
+
public static combineSets(sets: Set[]): Set {
return new Set(...sets);
}
diff --git a/src/main/utils/yarnUtils.ts b/src/main/utils/yarnUtils.ts
index 3ce513c2f..719b5f10c 100644
--- a/src/main/utils/yarnUtils.ts
+++ b/src/main/utils/yarnUtils.ts
@@ -74,7 +74,7 @@ export class YarnUtils {
}
checkCanceled();
- root.refreshDependencies();
+ root.loadYarnDependencies();
}
}
diff --git a/src/test/resources/maven/dependency/pom.xml b/src/test/resources/maven/treeTestsProjects/dependency/pom.xml
similarity index 100%
rename from src/test/resources/maven/dependency/pom.xml
rename to src/test/resources/maven/treeTestsProjects/dependency/pom.xml
diff --git a/src/test/resources/maven/empty/pom.xml b/src/test/resources/maven/treeTestsProjects/empty/pom.xml
similarity index 100%
rename from src/test/resources/maven/empty/pom.xml
rename to src/test/resources/maven/treeTestsProjects/empty/pom.xml
diff --git a/src/test/resources/maven/multiPomDependency/multi1/pom.xml b/src/test/resources/maven/treeTestsProjects/multiPomDependency/multi1/pom.xml
similarity index 100%
rename from src/test/resources/maven/multiPomDependency/multi1/pom.xml
rename to src/test/resources/maven/treeTestsProjects/multiPomDependency/multi1/pom.xml
diff --git a/src/test/resources/maven/multiPomDependency/multi2/pom.xml b/src/test/resources/maven/treeTestsProjects/multiPomDependency/multi2/pom.xml
similarity index 100%
rename from src/test/resources/maven/multiPomDependency/multi2/pom.xml
rename to src/test/resources/maven/treeTestsProjects/multiPomDependency/multi2/pom.xml
diff --git a/src/test/resources/maven/multiPomDependency/multi3/pom.xml b/src/test/resources/maven/treeTestsProjects/multiPomDependency/multi3/pom.xml
similarity index 100%
rename from src/test/resources/maven/multiPomDependency/multi3/pom.xml
rename to src/test/resources/maven/treeTestsProjects/multiPomDependency/multi3/pom.xml
diff --git a/src/test/resources/maven/multiPomDependency/pom.xml b/src/test/resources/maven/treeTestsProjects/multiPomDependency/pom.xml
similarity index 100%
rename from src/test/resources/maven/multiPomDependency/pom.xml
rename to src/test/resources/maven/treeTestsProjects/multiPomDependency/pom.xml
diff --git a/src/test/resources/maven/updateToFixVersionProjects/updateParentPomProperty/multi1/pom.xml b/src/test/resources/maven/updateToFixVersionProjects/updateParentPomProperty/multi1/pom.xml
new file mode 100644
index 000000000..2ffa7adfe
--- /dev/null
+++ b/src/test/resources/maven/updateToFixVersionProjects/updateParentPomProperty/multi1/pom.xml
@@ -0,0 +1,89 @@
+
+
+ 4.0.0
+
+ org.jfrog.test
+ multi
+ 3.7-SNAPSHOT
+
+
+ multi1
+ jar
+ Multi 1
+
+
+
+ apache
+ none
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+
+
+
+
+ org.apache.commons
+ commons-email
+ 1.1
+ compile
+
+
+ org.codehaus.plexus
+ plexus-utils
+ ${lets.go.and.fix.this}
+
+
+ javax.servlet.jsp
+ jsp-api
+ 2.1
+ compile
+
+
+ commons-io
+ commons-io
+ 2.7
+
+
+ org.springframework
+ spring-aop
+ 2.5.6
+
+
+
+
+ org.testng
+ testng
+ jdk15
+ 5.9
+ test
+
+
+
+
diff --git a/src/test/resources/maven/updateToFixVersionProjects/updateParentPomProperty/pom.xml b/src/test/resources/maven/updateToFixVersionProjects/updateParentPomProperty/pom.xml
new file mode 100644
index 000000000..96e9ddda9
--- /dev/null
+++ b/src/test/resources/maven/updateToFixVersionProjects/updateParentPomProperty/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+ org.jfrog.test
+ multi
+ 3.7-SNAPSHOT
+ pom
+ Simple Multi Modules Build
+
+
+ multi1
+
+
+
+ UTF-8
+ 1.8
+ 1.8
+ 1.5
+
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.4
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 2.4
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.1.2
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+ false
+
+
+
+
+
+
diff --git a/src/test/resources/maven/updateToFixVersionProjects/updatePom/pom.xml b/src/test/resources/maven/updateToFixVersionProjects/updatePom/pom.xml
new file mode 100644
index 000000000..1322b28f4
--- /dev/null
+++ b/src/test/resources/maven/updateToFixVersionProjects/updatePom/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+ multi1
+ jar
+ org.jfrog.test
+ 3.7-SNAPSHOT
+ Need your help here to fix this!
+
+
+ org.codehaus.plexus
+ plexus-utils
+ 1.5
+
+
+
diff --git a/src/test/resources/maven/updateToFixVersionProjects/updatePomProperty/pom.xml b/src/test/resources/maven/updateToFixVersionProjects/updatePomProperty/pom.xml
new file mode 100644
index 000000000..915a8ab8e
--- /dev/null
+++ b/src/test/resources/maven/updateToFixVersionProjects/updatePomProperty/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+ multi1
+ jar
+ org.jfrog.test
+ 3.7-SNAPSHOT
+ Need your help here to fix this!
+
+ 1.5
+
+
+
+ org.codehaus.plexus
+ plexus-utils
+ ${lets.go.and.fix.this}
+
+
+
+
diff --git a/src/test/tests/connectionManager.test.ts b/src/test/tests/connectionManager.test.ts
index 100351f93..5d600b27b 100644
--- a/src/test/tests/connectionManager.test.ts
+++ b/src/test/tests/connectionManager.test.ts
@@ -203,8 +203,8 @@ describe('Connection Manager Tests', () => {
let logMessageStub: sinon.SinonStub;
let setUrlsFromFilesystemStub: sinon.SinonStub>;
let setUsernameFromFilesystemStub: sinon.SinonStub>;
- let setPasswordFromKeyStoreStub: sinon.SinonStub>;
- let setAccessTokenFromKeyStoreStub: sinon.SinonStub>;
+ let getPasswordFromSecretStorageStub: sinon.SinonStub>;
+ let getAccessTokenFromSecretStorageStub: sinon.SinonStub>;
let deleteCredentialsFromMemoryStub: sinon.SinonStub;
let resolveUrlsStub: sinon.SinonStub>;
let onSuccessConnectStub: sinon.SinonStub>;
@@ -214,8 +214,8 @@ describe('Connection Manager Tests', () => {
//By casting mockConnectionManager to any, we can access and stub the private function.
setUrlsFromFilesystemStub = sinon.stub(mockConnectionManager as any, 'setUrlsFromFilesystem').resolves(true);
setUsernameFromFilesystemStub = sinon.stub(mockConnectionManager as any, 'setUsernameFromFilesystem').resolves(true);
- setPasswordFromKeyStoreStub = sinon.stub(mockConnectionManager as any, 'setPasswordFromKeyStore').resolves(true);
- setAccessTokenFromKeyStoreStub = sinon.stub(mockConnectionManager as any, 'setAccessTokenFromKeyStore').resolves(false);
+ getPasswordFromSecretStorageStub = sinon.stub(mockConnectionManager as any, 'getPasswordFromSecretStorage').resolves(true);
+ getAccessTokenFromSecretStorageStub = sinon.stub(mockConnectionManager as any, 'getAccessTokenFromSecretStorage').resolves(false);
deleteCredentialsFromMemoryStub = sinon.stub(mockConnectionManager as any, 'deleteCredentialsFromMemory').resolves(true);
resolveUrlsStub = sinon.stub(mockConnectionManager as any, 'resolveUrls').resolves();
onSuccessConnectStub = sinon.stub(mockConnectionManager, 'onSuccessConnect').resolves();
@@ -227,53 +227,47 @@ describe('Connection Manager Tests', () => {
it('Read username and password', async () => {
// Call the function
- const result: boolean = await mockConnectionManager.connect();
+ const result: boolean = await mockConnectionManager.loadCredential();
// Check the return value and ensure that necessary methods are called
assert.isTrue(result);
- sinon.assert.calledWith(logMessageStub, 'Trying to read credentials from KeyStore...', 'DEBUG');
sinon.assert.calledOnce(setUrlsFromFilesystemStub);
sinon.assert.calledOnce(setUsernameFromFilesystemStub);
- sinon.assert.calledOnce(setPasswordFromKeyStoreStub);
- sinon.assert.notCalled(setAccessTokenFromKeyStoreStub);
+ sinon.assert.calledOnce(getPasswordFromSecretStorageStub);
+ sinon.assert.notCalled(getAccessTokenFromSecretStorageStub);
sinon.assert.notCalled(deleteCredentialsFromMemoryStub);
- sinon.assert.calledOnce(resolveUrlsStub);
- sinon.assert.calledOnce(onSuccessConnectStub);
});
it('Read access token', async () => {
setUsernameFromFilesystemStub.resolves(false);
- setPasswordFromKeyStoreStub.resolves(false);
- setAccessTokenFromKeyStoreStub.resolves(true);
+ getPasswordFromSecretStorageStub.resolves(false);
+ getAccessTokenFromSecretStorageStub.resolves(true);
// Call the function
- const result: boolean = await mockConnectionManager.connect();
+ const result: boolean = await mockConnectionManager.loadCredential();
// Check the return value and ensure that necessary methods are called
assert.isTrue(result);
- sinon.assert.calledWith(logMessageStub, 'Trying to read credentials from KeyStore...', 'DEBUG');
sinon.assert.calledOnce(setUrlsFromFilesystemStub);
sinon.assert.calledOnce(setUsernameFromFilesystemStub);
- sinon.assert.notCalled(setPasswordFromKeyStoreStub);
- sinon.assert.calledOnce(setAccessTokenFromKeyStoreStub);
+ sinon.assert.notCalled(getPasswordFromSecretStorageStub);
+ sinon.assert.calledOnce(getAccessTokenFromSecretStorageStub);
sinon.assert.notCalled(deleteCredentialsFromMemoryStub);
- sinon.assert.calledOnce(resolveUrlsStub);
- sinon.assert.calledOnce(onSuccessConnectStub);
});
it('Empty KeyStore', async () => {
setUrlsFromFilesystemStub.resolves(false);
setUsernameFromFilesystemStub.resolves(false);
- setPasswordFromKeyStoreStub.resolves(false);
+ getPasswordFromSecretStorageStub.resolves(false);
// Call the function
const result: boolean = await mockConnectionManager.connect();
// Check the return value and ensure that necessary methods are called
assert.isFalse(result);
- sinon.assert.calledWith(logMessageStub, 'Trying to read credentials from KeyStore...', 'DEBUG');
+ sinon.assert.calledWith(logMessageStub, 'Trying to read credentials from Secret Storage...', 'DEBUG');
sinon.assert.calledOnce(setUrlsFromFilesystemStub);
sinon.assert.notCalled(setUsernameFromFilesystemStub);
- sinon.assert.notCalled(setPasswordFromKeyStoreStub);
- sinon.assert.notCalled(setAccessTokenFromKeyStoreStub);
+ sinon.assert.notCalled(getPasswordFromSecretStorageStub);
+ sinon.assert.notCalled(getAccessTokenFromSecretStorageStub);
sinon.assert.calledOnce(deleteCredentialsFromMemoryStub);
sinon.assert.notCalled(resolveUrlsStub);
sinon.assert.notCalled(onSuccessConnectStub);
diff --git a/src/test/tests/dependencyUtils.test.ts b/src/test/tests/dependencyUtils.test.ts
index 3f7d7c866..ff52c73eb 100644
--- a/src/test/tests/dependencyUtils.test.ts
+++ b/src/test/tests/dependencyUtils.test.ts
@@ -83,7 +83,7 @@ describe('Dependency Utils Tests', () => {
]
} as IImpactGraphNode,
pathsCount: 2,
- pathsLimit: DependencyUtils.IMPACT_PATHS_LIMIT
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
} as IImpactGraph);
map.set('XRAY-191882' + 'C:2.0.0', {
root: {
@@ -91,7 +91,7 @@ describe('Dependency Utils Tests', () => {
children: [{ name: 'C:2.0.0' } as IImpactGraphNode]
},
pathsCount: 1,
- pathsLimit: DependencyUtils.IMPACT_PATHS_LIMIT
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
} as IImpactGraph);
// issue XRAY-94201, for components B:1.0.0
map.set('XRAY-94201' + 'B:1.0.0', {
@@ -100,7 +100,7 @@ describe('Dependency Utils Tests', () => {
children: [{ name: 'B:1.0.0' } as IImpactGraphNode]
},
pathsCount: 1,
- pathsLimit: DependencyUtils.IMPACT_PATHS_LIMIT
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
} as IImpactGraph);
// issue XRAY-142007, for components [A:1.0.1, C:2.0.0]
map.set('XRAY-142007' + 'A:1.0.1', {
@@ -109,7 +109,7 @@ describe('Dependency Utils Tests', () => {
children: [{ name: 'B:1.0.0', children: [{ name: 'A:1.0.1' } as IImpactGraphNode] } as IImpactGraphNode]
},
pathsCount: 1,
- pathsLimit: DependencyUtils.IMPACT_PATHS_LIMIT
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
} as IImpactGraph);
map.set('XRAY-142007' + 'C:2.0.0', {
root: {
@@ -117,7 +117,7 @@ describe('Dependency Utils Tests', () => {
children: [{ name: 'C:2.0.0' } as IImpactGraphNode]
},
pathsCount: 1,
- pathsLimit: DependencyUtils.IMPACT_PATHS_LIMIT
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
} as IImpactGraph);
return map;
}
@@ -139,15 +139,15 @@ describe('Dependency Utils Tests', () => {
describe('Limit impacted graph', () => {
it('Set paths limit to 1', () => {
- const ORIGIN_IMPACT_PATHS_LIMIT: number = DependencyUtils.IMPACT_PATHS_LIMIT;
- DependencyUtils.IMPACT_PATHS_LIMIT = 1;
+ const ORIGIN_IMPACT_PATHS_LIMIT: number = RootNode.IMPACT_PATHS_LIMIT;
+ RootNode.IMPACT_PATHS_LIMIT = 1;
let impactedTree: Map = DependencyUtils.createImpactedGraph(root, getGraphResponse('scanGraphVulnerabilities'));
assert.equal(impactedTree.get('XRAY-191882A:1.0.0')?.pathsCount, 2);
assert.equal(impactedTree.get('XRAY-191882A:1.0.0')?.root.children?.length, 1);
- DependencyUtils.IMPACT_PATHS_LIMIT = ORIGIN_IMPACT_PATHS_LIMIT;
+ RootNode.IMPACT_PATHS_LIMIT = ORIGIN_IMPACT_PATHS_LIMIT;
});
});
diff --git a/src/test/tests/goUtils.test.ts b/src/test/tests/goUtils.test.ts
index ad622f084..fe8d7791d 100644
--- a/src/test/tests/goUtils.test.ts
+++ b/src/test/tests/goUtils.test.ts
@@ -97,14 +97,14 @@ describe('Go Utils Tests', async () => {
let dependenciesTreeNode: DependenciesTreeNode = new DependenciesTreeNode(
new GeneralInfo('github.com/jfrog/jfrog-cli-core', '1.9.1', [], '', PackageType.Go)
);
- let dependencyPos: vscode.Range[] = GoUtils.getDependencyPosition(textDocument, dependenciesTreeNode.dependencyId, FocusType.Dependency);
+ let dependencyPos: vscode.Range[] = GoUtils.getDependencyPosition(textDocument, dependenciesTreeNode.xrayDependencyId, FocusType.Dependency);
assert.deepEqual(dependencyPos[0].start, new vscode.Position(5, 1));
assert.deepEqual(dependencyPos[0].end, new vscode.Position(5, 39));
// Test 'resources/go/empty/go.mod'
goMod = vscode.Uri.file(path.join(commonProjDir.fsPath, 'empty', 'go.mod'));
textDocument = await vscode.workspace.openTextDocument(goMod);
- dependencyPos = GoUtils.getDependencyPosition(textDocument, dependenciesTreeNode.dependencyId, FocusType.Dependency);
+ dependencyPos = GoUtils.getDependencyPosition(textDocument, dependenciesTreeNode.xrayDependencyId, FocusType.Dependency);
assert.isEmpty(dependencyPos);
});
diff --git a/src/test/tests/maven.test.ts b/src/test/tests/maven.test.ts
index 9ac398c12..879f7352e 100644
--- a/src/test/tests/maven.test.ts
+++ b/src/test/tests/maven.test.ts
@@ -34,11 +34,9 @@ describe('Maven Tests', async () => {
{} as CacheManager,
logManager
);
- // let mavenExclusion: MavenExclusion = new MavenExclusion(treesManager);
- // let mavenDependencyUpdate: MavenDependencyUpdate = new MavenDependencyUpdate();
let projectDirs: string[] = ['dependency', 'empty', 'multiPomDependency'];
let workspaceFolders: vscode.WorkspaceFolder[];
- let tmpDir: vscode.Uri = vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven'));
+ let tmpDir: vscode.Uri = vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects'));
before(function() {
workspaceFolders = [
@@ -49,7 +47,7 @@ describe('Maven Tests', async () => {
} as vscode.WorkspaceFolder
];
// Install maven dependencies
- exec.execSync('mvn clean install', { cwd: path.join(__dirname, '..', 'resources', 'maven', 'multiPomDependency') });
+ exec.execSync('mvn clean install', { cwd: path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'multiPomDependency') });
MavenUtils.installMavenGavReader();
});
@@ -97,7 +95,7 @@ describe('Maven Tests', async () => {
// Single pom
let localWorkspaceFolders: vscode.WorkspaceFolder[] = [
{
- uri: vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven', 'dependency')),
+ uri: vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'dependency')),
name: '',
index: 0
} as vscode.WorkspaceFolder
@@ -110,7 +108,7 @@ describe('Maven Tests', async () => {
//Multi pom
localWorkspaceFolders = [
{
- uri: vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven', 'multiPomDependency')),
+ uri: vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'multiPomDependency')),
name: '',
index: 0
} as vscode.WorkspaceFolder
@@ -120,16 +118,6 @@ describe('Maven Tests', async () => {
assert.deepEqual(got, want[1]);
});
- /**
- * Test getProjectInfo.
- */
- it('Get Project Info', async () => {
- const [groupId, ArtifactId, version] = MavenUtils.getProjectInfo(' org.jfrog.test:multi2:jar:3.7-SNAPSHOT');
- assert.equal(groupId, 'org.jfrog.test');
- assert.equal(ArtifactId, 'multi2');
- assert.equal(version, '3.7-SNAPSHOT');
- });
-
/**
* Test getDependencyInfo.
*/
@@ -332,26 +320,26 @@ describe('Maven Tests', async () => {
function expectedBuildPrototypePomTree(): PomTree[][] {
return [
- [new PomTree('org.jfrog.test:multi2:3.7-SNAPSHOT', path.join(__dirname, '..', 'resources', 'maven', 'dependency', 'pom.xml'))],
+ [new PomTree('org.jfrog.test:multi2:3.7-SNAPSHOT', path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'dependency', 'pom.xml'))],
[
- new PomTree('org.jfrog.test:multi:3.7-SNAPSHOT', path.join(__dirname, '..', 'resources', 'maven', 'multiPomDependency', 'pom.xml'), [
+ new PomTree('org.jfrog.test:multi:3.7-SNAPSHOT', path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'multiPomDependency', 'pom.xml'), [
new PomTree(
'org.jfrog.test:multi1:3.7-SNAPSHOT',
- path.join(__dirname, '..', 'resources', 'maven', 'multiPomDependency', 'multi1', 'pom.xml'),
+ path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'multiPomDependency', 'multi1', 'pom.xml'),
[],
undefined,
'org.jfrog.test:multi:3.7-SNAPSHOT'
),
new PomTree(
'org.jfrog.test:multi2:3.7-SNAPSHOT',
- path.join(__dirname, '..', 'resources', 'maven', 'multiPomDependency', 'multi2', 'pom.xml'),
+ path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'multiPomDependency', 'multi2', 'pom.xml'),
[],
undefined,
'org.jfrog.test:multi:3.7-SNAPSHOT'
),
new PomTree(
'org.jfrog.test:multi3:3.7-SNAPSHOT',
- path.join(__dirname, '..', 'resources', 'maven', 'multiPomDependency', 'multi3', 'pom.xml'),
+ path.join(__dirname, '..', 'resources', 'maven', 'treeTestsProjects', 'multiPomDependency', 'multi3', 'pom.xml'),
[],
undefined,
'org.jfrog.test:multi:3.7-SNAPSHOT'
diff --git a/src/test/tests/mavenUpdate.test.ts b/src/test/tests/mavenUpdate.test.ts
new file mode 100644
index 000000000..ed344edb3
--- /dev/null
+++ b/src/test/tests/mavenUpdate.test.ts
@@ -0,0 +1,161 @@
+import { assert } from 'chai';
+import * as path from 'path';
+import * as vscode from 'vscode';
+// import * as exec from 'child_process';
+
+import * as fs from 'fs-extra';
+import { DependenciesTreeNode } from '../../main/treeDataProviders/dependenciesTree/dependenciesTreeNode';
+import { GeneralInfo } from '../../main/types/generalInfo';
+import { MavenUtils } from '../../main/utils/mavenUtils';
+import { PackageType } from '../../main/types/projectType';
+import { MavenDependencyUpdate } from '../../main/dependencyUpdate/mavenDependencyUpdate';
+import { DependencyIssuesTreeNode } from '../../main/treeDataProviders/issuesTree/descriptorTree/dependencyIssuesTreeNode';
+import { ProjectDependencyTreeNode } from '../../main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode';
+import { IComponent } from 'jfrog-client-js';
+import { IssuesRootTreeNode } from '../../main/treeDataProviders/issuesTree/issuesRootTreeNode';
+import { ScanUtils } from '../../main/utils/scanUtils';
+import { CacheManager } from '../../main/cache/cacheManager';
+import { ScanCacheManager } from '../../main/cache/scanCacheManager';
+import { ConnectionManager } from '../../main/connect/connectionManager';
+import { LogManager } from '../../main/log/logManager';
+import { ScanManager } from '../../main/scanLogic/scanManager';
+import { TreesManager } from '../../main/treeDataProviders/treesManager';
+import { createScanCacheManager } from './utils/utils.test';
+
+
+ describe('Maven - Update to fixed version', async () => {
+
+ let logManager: LogManager = new LogManager().activate();
+ let dummyScanCacheManager: ScanCacheManager = createScanCacheManager();
+ let treesManager: TreesManager = new TreesManager(
+ [],
+ new ConnectionManager(logManager),
+ dummyScanCacheManager,
+ {} as ScanManager,
+ {} as CacheManager,
+ logManager
+ );
+ const expectedVersion: string = '3.0.16';
+
+ before(function() {
+ // Install maven dependencies
+ // exec.execSync('mvn clean install', { cwd: path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', 'updateParentPomProperty') });
+ // MavenUtils.installMavenGavReader();
+ });
+ it('Without version property', async () => {
+ const [mavenDependencyUpdate, issueToUpdate, pomXmlPath] = await setupTestEnvironment('updatePom');
+
+ // Operate the test
+ mavenDependencyUpdate.update(issueToUpdate, expectedVersion);
+
+ // Check results
+ const fileContent: string = fs.readFileSync(pomXmlPath, 'utf-8');
+ assert.include(fileContent, `${expectedVersion}`);
+ assert.isFalse(fs.existsSync(path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', 'updatePom', 'pom.xml.versionsBackup')));
+ });
+
+ it('With property', async () => {
+ const [mavenDependencyUpdate, issueToUpdate, pomXmlPath] = await setupTestEnvironment('updatePomProperty');
+
+ // Operate the test
+ mavenDependencyUpdate.update(issueToUpdate, expectedVersion);
+
+ // Check results
+ const fileContent: string = fs.readFileSync(pomXmlPath, 'utf-8');
+ assert.include(fileContent, `${expectedVersion}`);
+ assert.isFalse(
+ fs.existsSync(path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', 'updatePomProperty', 'pom.xml.versionsBackup'))
+ );
+ });
+
+ it('Multi module with property', async () => {
+ const [mavenDependencyUpdate, issueToUpdate, pomXmlPath] = await setupMultiModuleTestEnvironment(
+ path.join('updateParentPomProperty', 'multi1'),
+ 'updateParentPomProperty'
+ );
+
+ // Operate the test
+ mavenDependencyUpdate.update(issueToUpdate, expectedVersion);
+
+ // Check results
+ const fileContent: string = fs.readFileSync(pomXmlPath, 'utf-8');
+ assert.include(fileContent, `${expectedVersion}`);
+ assert.isFalse(
+ fs.existsSync(path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', 'updateParentPomProperty', 'pom.xml.versionsBackup'))
+ );
+ });
+
+ async function setupTestEnvironment(projectDir: string): Promise<[MavenDependencyUpdate, DependencyIssuesTreeNode, string]> {
+ const mavenDependencyUpdate: MavenDependencyUpdate = new MavenDependencyUpdate();
+ const pomXmlPath: string = path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', projectDir, 'pom.xml');
+
+ const localWorkspaceFolders: vscode.WorkspaceFolder[] = [
+ {
+ uri: vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', projectDir)),
+ name: '',
+ index: 0
+ } as vscode.WorkspaceFolder
+ ];
+
+ const pomXmlsArray: vscode.Uri[] | undefined = await locatePomXmls(localWorkspaceFolders);
+
+ const parent: DependenciesTreeNode = new DependenciesTreeNode(new GeneralInfo('parent', '1.0.0', [], '', PackageType.Unknown));
+
+ await MavenUtils.createDependenciesTrees(pomXmlsArray, treesManager.logManager, () => undefined, parent);
+
+ const issueToUpdate: DependencyIssuesTreeNode = new DependencyIssuesTreeNode(
+ 'artifactId',
+ { package_type: 'MAVEN', package_name: 'org.codehaus.plexus:plexus-utils', package_version: '1.5' } as IComponent,
+ false,
+ new ProjectDependencyTreeNode(pomXmlPath)
+ );
+ return [mavenDependencyUpdate, issueToUpdate, pomXmlPath];
+ }
+
+ async function setupMultiModuleTestEnvironment(
+ pomDir: string,
+ parentPomDir: string
+ ): Promise<[MavenDependencyUpdate, DependencyIssuesTreeNode, string]> {
+ const mavenDependencyUpdate: MavenDependencyUpdate = new MavenDependencyUpdate();
+ const pomXmlPath: string = path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', pomDir, 'pom.xml');
+ const parentPomXmlPath: string = path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', parentPomDir, 'pom.xml');
+
+ const localWorkspaceFolders: vscode.WorkspaceFolder[] = [
+ {
+ uri: vscode.Uri.file(path.join(__dirname, '..', 'resources', 'maven','updateToFixVersionProjects', parentPomDir)),
+ name: '',
+ index: 0
+ } as vscode.WorkspaceFolder
+ ];
+
+ const pomXmlsArray: vscode.Uri[] | undefined = await locatePomXmls(localWorkspaceFolders);
+
+ const parent: DependenciesTreeNode = new DependenciesTreeNode(new GeneralInfo('parent', '1.0.0', [], '', PackageType.Unknown));
+
+ await MavenUtils.createDependenciesTrees(pomXmlsArray, treesManager.logManager, () => undefined, parent);
+
+ const projectRoot: IssuesRootTreeNode = new IssuesRootTreeNode({
+ uri: localWorkspaceFolders[0].uri
+ } as vscode.WorkspaceFolder);
+
+ const VulnerablePom: ProjectDependencyTreeNode = new ProjectDependencyTreeNode(pomXmlPath, PackageType.Maven);
+ const ParentPomOfVulnerablePom: ProjectDependencyTreeNode = new ProjectDependencyTreeNode(parentPomXmlPath, PackageType.Maven);
+ projectRoot.addChild(VulnerablePom);
+ projectRoot.addChild(ParentPomOfVulnerablePom);
+ const issueToUpdate: DependencyIssuesTreeNode = new DependencyIssuesTreeNode(
+ 'artifactId',
+ { package_type: 'MAVEN', package_name: 'org.codehaus.plexus:plexus-utils', package_version: '1.5' } as IComponent,
+ false,
+ VulnerablePom
+ );
+ return [mavenDependencyUpdate, issueToUpdate, parentPomXmlPath];
+
+ }
+ async function locatePomXmls(workspaceFolders: vscode.WorkspaceFolder[]): Promise {
+ let packageDescriptors: Map = await ScanUtils.locatePackageDescriptors(workspaceFolders, treesManager.logManager);
+ let pomXmlsArray: vscode.Uri[] | undefined = packageDescriptors.get(PackageType.Maven);
+ assert.isDefined(pomXmlsArray);
+ pomXmlsArray = pomXmlsArray?.sort((a: vscode.Uri, b: vscode.Uri) => a.fsPath.localeCompare(b.fsPath));
+ return pomXmlsArray;
+ }
+});
diff --git a/src/test/tests/utils/treeNodeUtils.test.ts b/src/test/tests/utils/treeNodeUtils.test.ts
index 1cace1ad2..7b090c477 100644
--- a/src/test/tests/utils/treeNodeUtils.test.ts
+++ b/src/test/tests/utils/treeNodeUtils.test.ts
@@ -59,7 +59,7 @@ export function createRootTestNode(pathOfWorkspace: string): IssuesRootTreeNode
export function createDependency(artifactId: string, version: string, parent?: DependenciesTreeNode): DependenciesTreeNode {
let dependenciesTreeNode: DependenciesTreeNode = new DependenciesTreeNode(new GeneralInfo(artifactId, version, [], '', PackageType.Unknown));
- dependenciesTreeNode.dependencyId = artifactId + ':' + version;
+ dependenciesTreeNode.xrayDependencyId = artifactId + ':' + version;
parent?.addChild(dependenciesTreeNode);
return dependenciesTreeNode;
}
diff --git a/src/test/tests/yarnImpactGraph.test.ts b/src/test/tests/yarnImpactGraph.test.ts
new file mode 100644
index 000000000..aab0fc515
--- /dev/null
+++ b/src/test/tests/yarnImpactGraph.test.ts
@@ -0,0 +1,145 @@
+import { IImpactGraph } from 'jfrog-ide-webview';
+import { YarnImpactGraphCreator, YarnWhyItem } from '../../main/treeDataProviders/utils/yarnImpactGraph';
+import { assert } from 'chai';
+import { RootNode } from '../../main/treeDataProviders/dependenciesTree/dependenciesRoot/rootTree';
+import { LogManager } from '../../main/log/logManager';
+
+describe('Yarn impact graph util', async () => {
+ it('Build single impact graph', async () => {
+ const results: IImpactGraph = new YarnImpactGraphUtilMock('minimist', '0.0.8', 'Mock-Project', '', new LogManager().activate()).create();
+ assert.deepEqual(results, generateExpectedSingleImpactGraph());
+ });
+
+ it('Build multiple impact graphs', async () => {
+ const results: IImpactGraph = new YarnImpactGraphUtilMock('minimist', '1.2.0', 'Mock-Project', '', new LogManager().activate()).create();
+ assert.deepEqual(results, generateExpectedMultipleImpactGraphs());
+ });
+});
+
+class YarnImpactGraphUtilMock extends YarnImpactGraphCreator {
+ protected runYarnWhy(): YarnWhyItem[] {
+ const yarnWhyOutput: YarnWhyItem[] = [
+ {
+ type: 'info',
+ data: '\r=> Found "minimist@1.2.0"'
+ },
+ {
+ type: 'info',
+ data: 'Has been hoisted to "minimist"'
+ },
+ {
+ type: 'info',
+ data: 'Reasons this module exists'
+ },
+ {
+ type: 'list',
+ data: {
+ type: 'reasons',
+ items: [
+ 'Specified in "dependencies"',
+ 'Hoisted from "jest-cli#node-notifier#minimist"',
+ 'Hoisted from "jest-cli#sane#minimist"',
+ 'Hoisted from "jest-cli#istanbul-lib-instrument#babel-generator#detect-indent#minimist"'
+ ]
+ }
+ },
+ {
+ type: 'info',
+ data: 'Disk size without dependencies: "96KB"'
+ },
+ {
+ type: 'info',
+ data: '\r=> Found "mkdirp#minimist@0.0.8"'
+ },
+ {
+ type: 'info',
+ data: 'This module exists because "jest-cli#istanbul-api#mkdirp" depends on it.'
+ }
+ ];
+ return yarnWhyOutput;
+ }
+}
+
+function generateExpectedSingleImpactGraph(): IImpactGraph {
+ return {
+ root: {
+ name: 'Mock-Project',
+ children: [
+ {
+ name: 'jest-cli',
+ children: [
+ {
+ name: 'istanbul-api',
+ children: [
+ {
+ name: 'mkdirp',
+ children: [
+ {
+ name: 'minimist:0.0.8'
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ pathsCount: 1,
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
+ };
+}
+
+function generateExpectedMultipleImpactGraphs(): IImpactGraph {
+ return {
+ root: {
+ name: 'Mock-Project',
+ children: [
+ {
+ name: 'minimist:1.2.0'
+ },
+ {
+ name: 'jest-cli',
+ children: [
+ {
+ name: 'node-notifier',
+ children: [
+ {
+ name: 'minimist:1.2.0'
+ }
+ ]
+ },
+ {
+ name: 'sane',
+ children: [
+ {
+ name: 'minimist:1.2.0'
+ }
+ ]
+ },
+ {
+ name: 'istanbul-lib-instrument',
+ children: [
+ {
+ name: 'babel-generator',
+ children: [
+ {
+ name: 'detect-indent',
+ children: [
+ {
+ name: 'minimist:1.2.0'
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ pathsCount: 4,
+ pathsLimit: RootNode.IMPACT_PATHS_LIMIT
+ };
+}
diff --git a/webpack.config.js b/webpack.config.js
index a79df9391..032d2592b 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -15,8 +15,7 @@ const extensionConfig = {
},
devtool: 'source-map',
externals: {
- vscode: 'commonjs vscode',
- keytar: 'keytar'
+ vscode: 'commonjs vscode'
},
resolve: {
extensions: ['.ts', '.js']