Skip to content

Commit f0ffd4d

Browse files
authored
feat: Rust Cargo support (#241)
* feat: Rust Cargo support * fix: add missing Kotlin-based DSL script name build.gradle.kts for Gradle * docs: centralize manifest file references in Configuration section * feat: implement version update quick fix for all Cargo.toml dependency formats
1 parent 16436b0 commit f0ffd4d

20 files changed

Lines changed: 1063 additions & 83 deletions

README.md

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ while you build your application.
2020
- Yarn Berry and Classic (yarn)
2121
- Gradle Kotlin and Groovy (gradle)
2222
- Golang (go mod)
23+
- Rust (cargo)
2324
- Python (pip) ecosystems, and base images in Dockerfile.
2425

2526
In future releases, Red Hat plans to support other package managers.
@@ -45,14 +46,8 @@ vulnerability report.
4546

4647
**Prerequisites**
4748

48-
- For Maven projects, analyzing a `pom.xml` file, you must have the `mvn` binary in your IDE's `PATH` environment.
49-
- For Node projects, analyzing a `package.json` file, you must have one of the corresponding package manager `npm`, `pnpm` or `yarn`, `node` binaries in your IDE's `PATH`
50-
environment.
51-
- For Golang projects, analyzing a `go.mod` file, you must have the `go` binary in your IDE's `PATH` environment.
52-
- For Python projects, analyzing a `requirements.txt` file, you must have the `python3` and `pip3` binaries in your
53-
IDE's `PATH` environment.
54-
- For Gradle projects, analyzing a `build.gradle` file or `build.gradle.kts` file, you must have the `gradle` binary in your system's `PATH` environment.
55-
- For base images, analyzing a `Dockerfile`, you must have the [`syft`](https://github.com/anchore/syft?tab=readme-ov-file#installation) and [`skopeo`](https://github.com/containers/skopeo/blob/main/install.md) binaries in your IDE's `PATH` environment.
49+
- You must have the package manager binary for your ecosystem available in your IDE's `PATH` environment.
50+
See the [Configuration](#configuration) section for the full list of supported package managers, their manifest files, and binary path settings.
5651

5752
**Procedure**
5853

@@ -82,7 +77,7 @@ according to your preferences.
8277

8378
**Configurable parameters**
8479

85-
- **Maven** :
80+
- **Maven** (`pom.xml`) :
8681
<br >Set the full path of the Maven executable, which allows Exhort to locate and run the `mvn` command to resolve
8782
dependencies for Maven projects.
8883
<br >Path of the `JAVA_HOME` directory is required by the `mvn` executable.
@@ -95,20 +90,25 @@ according to your preferences.
9590
<br >`false`: Never use the wrapper regardless of `Build,Execution,Deployment › Build Tools > Maven: Maven home path` setting
9691
<br >`fallback`: Use IntelliJ's `Build,Execution,Deployment › Build Tools > Maven: Maven home path` setting (default behavior)
9792

98-
- **Node** :
93+
- **Node** (`package.json`) :
9994
<br >Set the full path of the Node executable, which allows Exhort to locate and run one of the corresponding `npm`, `pnpm` or `yarn` command to resolve
10095
dependencies for Node projects.
10196
<br >Path of the directory containing the `node` executable is required by one of the corresponding `npm`, `pnpm` or `yarn` executable.
10297
<br >If the paths are not provided, your IDE's `PATH` environment will be used to locate the executables.
10398

104-
- **Golang** :
99+
- **Golang** (`go.mod`) :
105100
<br >Set the full path of the Go executable, which allows Exhort to locate and run the `go` command to resolve
106101
dependencies for Go projects.
107102
<br >If the path is not provided, your IDE's `PATH` environment will be used to locate the executable.
108103
<br >When option `Strictly match package version` is selected, the resolved dependency versions will be compared to
109104
the versions specified in the manifest file, and users will be alerted if any mismatch is detected.
110105

111-
- **Python** :
106+
- **Rust** (`Cargo.toml`) :
107+
<br >Set the full path of the Cargo executable, which allows Exhort to locate and run the `cargo` command to resolve
108+
dependencies for Rust projects.
109+
<br >If the path is not provided, your IDE's `PATH` environment will be used to locate the executable.
110+
111+
- **Python** (`requirements.txt`) :
112112
<br >Set the full paths of the Python and the package installer for Python executables, which allows Exhort to locate
113113
and run the `pip3` commands to resolve dependencies for Python projects.
114114
<br >Python 2 executables `python` and `pip` can be used instead, if the `Use python 2.x` option is selected.
@@ -120,13 +120,13 @@ according to your preferences.
120120
specified in the manifest file will be ignored, and dependency versions will be resolved dynamically instead (this
121121
feature cannot be enabled when `Strictly match package version` is selected).
122122

123-
- **Gradle** :
123+
- **Gradle** (`build.gradle`, `build.gradle.kts`) :
124124
<br >Set the full path of the Gradle executable, which allows Exhort to locate and run the `gradle` command to resolve
125125
dependencies for Gradle projects.
126126
<br >By not setting a path to the gradle binary, IntelliJ IDEA uses its default path environment to locate the file.
127127

128128

129-
- **Image** :
129+
- **Image** (`Dockerfile`) :
130130
<br >Set the full path of the Syft executable, which allows Exhort to locate and run the `syft` command to
131131
generate Software Bill of Materials for the base images.
132132
<br >Optionally, set the full path of the Docker or Podman executable. Syft will attempt to find the images in the
@@ -167,7 +167,7 @@ according to your preferences.
167167
## Features
168168

169169
- **Component analysis**
170-
<br >Upon opening a manifest file, such as a `pom.xml`, `package.json`, `go.mod` or `requirements.txt` file, a scan
170+
<br >Upon opening a supported manifest file (see [Configuration](#configuration) for the full list), a scan
171171
starts the analysis process.
172172
The scan provides immediate inline feedback on detected security vulnerabilities for your application's dependencies.
173173
Such dependencies are appropriately underlined in red, and hovering over it gives you a short summary of the security
@@ -301,14 +301,23 @@ When modifying the grammar or lexer files, you need to regenerate the parser cla
301301
```text
302302
requests==2.28.1 # trustify-da-ignore
303303
```
304-
If you want to ignore vulnerabilities for a dependency in a `build.gradle` file, you must add `trustify-da-ignore` as a
304+
If you want to ignore vulnerabilities for a dependency in a `build.gradle` or `build.gradle.kts` file, you must add `trustify-da-ignore` as a
305305
comment against the dependency in the manifest file.
306306
For example:
307307
```text
308308
implementation "log4j:log4j:1.2.17" // trustify-da-ignore
309309
implementation group: 'log4j', name: 'log4j', version: '1.2.17' // trustify-da-ignore
310310
```
311311

312+
If you want to ignore vulnerabilities for a dependency in a `Cargo.toml` file, you must add `trustify-da-ignore` as a comment
313+
against the dependency in the manifest file.
314+
For example:
315+
```toml
316+
[dependencies]
317+
serde = "1.0" # trustify-da-ignore
318+
tokio = { version = "1.0", features = ["full"] } # trustify-da-ignore
319+
```
320+
312321
- **Excluding developmental or test dependencies**
313322
<br >Red Hat Dependency Analytics does not analyze dependencies marked as `dev` or `test`, these dependencies are
314323
ignored.
@@ -373,6 +382,49 @@ When modifying the grammar or lexer files, you need to regenerate the parser cla
373382
Report** tab remains open.
374383
Closing the tab removes the temporary HTML file.
375384

385+
## Rust/Cargo Component Analysis Limitations
386+
387+
The plugin provides vulnerability analysis for Rust projects using `Cargo.toml` manifest files. While stack analysis (full dependency report) works completely for all Cargo dependency formats, the component analysis feature (inline vulnerability detection) has some current limitations in version resolution and automatic fixes.
388+
389+
### Component Analysis Version Resolution
390+
391+
During inline component analysis, some dependency types cannot have their versions resolved directly from the manifest file and are marked as `UNRESOLVED_VERSION`. These dependencies will still appear in the full stack analysis report with correct version information.
392+
393+
Dependencies that result in unresolved versions in component analysis:
394+
- **Workspace dependencies**: `workspace = true` (version inherited from workspace root)
395+
- **Git dependencies**: `git = "https://github.com/user/repo"` (version resolved at build time)
396+
- **Path dependencies**: `path = "../local"` (version from local Cargo.toml)
397+
- **Registry dependencies without explicit version**: Dependencies that rely on registry resolution
398+
399+
Example:
400+
```toml
401+
[dependencies]
402+
# These will show UNRESOLVED_VERSION in inline component analysis
403+
workspace-dep = { workspace = true }
404+
git-dep = { git = "https://github.com/user/repo" }
405+
local-dep = { path = "../local" }
406+
registry-dep = { registry = "my-registry" }
407+
```
408+
409+
### Component Analysis Quick-Fix Support
410+
411+
The automatic version update feature (quick-fix suggestions) has varying support across different dependency declaration formats:
412+
413+
**Supported formats:**
414+
- Simple string versions: `serde = "1.0"`
415+
416+
**Unsupported formats:**
417+
- Complex inline tables: `tokio = { version = "1.0", features = ["full"] }`
418+
- Complex inline tables without version field: `tokio = { features = ["full"] }`
419+
- Standard table format:
420+
```toml
421+
[dependencies.cratename]
422+
version = "1.0"
423+
features = ["derive"]
424+
```
425+
426+
For unsupported formats, vulnerability detection still works and the full stack analysis report will contain complete information and recommendations. Manual updates to the manifest file are required for these cases.
427+
376428
## Know more about the Red Hat Dependency Analytics platform
377429

378430
The goal of this project is to significantly enhance a developer's experience by providing helpful vulnerability

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ideaVersion=2025.1
99
gradleVersion=8.5
1010

1111
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
12-
platformBundledPlugins=org.jetbrains.plugins.yaml,com.intellij.java,org.jetbrains.idea.maven
12+
platformBundledPlugins=org.jetbrains.plugins.yaml,com.intellij.java,org.jetbrains.idea.maven,org.toml.lang
1313
platformPlugins=com.redhat.devtools.intellij.telemetry:1.1.0.52
1414

1515
# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib

src/main/java/org/jboss/tools/intellij/componentanalysis/CAAnnotator.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -270,20 +270,15 @@ private InspectionProfileEntry getInspection(@NotNull PsiElement context, @NotNu
270270
}
271271

272272
public static String getPackageManager(String file) {
273-
switch (file) {
274-
case "pom.xml":
275-
return "maven";
276-
case "package.json":
277-
return "npm";
278-
case "go.mod":
279-
return "go";
280-
case "requirements.txt":
281-
return "python";
282-
case "build.gradle":
283-
return "gradle";
284-
default:
285-
return null;
286-
}
273+
return switch (file) {
274+
case "pom.xml" -> "maven";
275+
case "package.json" -> "npm";
276+
case "go.mod" -> "go";
277+
case "requirements.txt" -> "python";
278+
case "build.gradle", "build.gradle.kts" -> "gradle";
279+
case "Cargo.toml" -> "cargo";
280+
default -> null;
281+
};
287282
}
288283

289284
public static class Info {

src/main/java/org/jboss/tools/intellij/componentanalysis/ExcludeManifestIntentionAction.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file
4242
if (file == null || file.getVirtualFile() == null) {
4343
return false;
4444
}
45-
45+
4646
String fileName = file.getName();
47-
return "pom.xml".equals(fileName) ||
48-
"package.json".equals(fileName) ||
49-
"go.mod".equals(fileName) ||
50-
"requirements.txt".equals(fileName) ||
51-
"build.gradle".equals(fileName);
47+
return "pom.xml".equals(fileName) ||
48+
"package.json".equals(fileName) ||
49+
"go.mod".equals(fileName) ||
50+
"requirements.txt".equals(fileName) ||
51+
"build.gradle".equals(fileName) ||
52+
"build.gradle.kts".equals(fileName) ||
53+
"Cargo.toml".equals(fileName);
5254
}
5355

5456
@Override

src/main/java/org/jboss/tools/intellij/componentanalysis/SAIntentionAction.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file
4646
return "pom.xml".equals(file.getName())
4747
|| "package.json".equals(file.getName())
4848
|| "go.mod".equals(file.getName())
49-
|| "requirements.txt".equals(file.getName());
49+
|| "requirements.txt".equals(file.getName())
50+
|| "build.gradle".equals(file.getName())
51+
|| "build.gradle.kts".equals(file.getName())
52+
|| "Cargo.toml".equals(file.getName());
5053
}
5154

5255
@Override

0 commit comments

Comments
 (0)