Skip to content

Commit aa4c391

Browse files
committed
fix: read ignore patterns from workspace member Cargo.toml files
1 parent 7f8eff5 commit aa4c391

3 files changed

Lines changed: 128 additions & 1 deletion

File tree

src/main/java/io/github/guacsec/trustifyda/providers/CargoProvider.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,60 @@ private void handleVirtualWorkspace(
202202
+ metadata.workspaceMembers());
203203
}
204204
for (String memberId : metadata.workspaceMembers()) {
205+
Set<String> memberIgnoredDeps = getMemberIgnoredDeps(memberId, packageMap, ignoredDeps);
205206
processWorkspaceMember(
206-
sbom, root, memberId, nodeMap, packageMap, ignoredDeps, analysisType);
207+
sbom, root, memberId, nodeMap, packageMap, memberIgnoredDeps, analysisType);
207208
}
208209
}
209210
}
210211
}
211212

213+
/**
214+
* Builds the full set of ignored dependencies for a workspace member by reading the member's own
215+
* Cargo.toml for ignore patterns and merging them with the workspace-level ignored deps.
216+
*/
217+
private Set<String> getMemberIgnoredDeps(
218+
String memberId, Map<String, CargoPackage> packageMap, Set<String> workspaceIgnoredDeps) {
219+
CargoPackage memberPkg = packageMap.get(memberId);
220+
if (memberPkg == null || memberPkg.manifestPath() == null) {
221+
return workspaceIgnoredDeps;
222+
}
223+
Path memberManifest = Path.of(memberPkg.manifestPath());
224+
if (!Files.isRegularFile(memberManifest)) {
225+
return workspaceIgnoredDeps;
226+
}
227+
try {
228+
TomlParseResult memberToml = Toml.parse(memberManifest);
229+
if (memberToml.hasErrors()) {
230+
return workspaceIgnoredDeps;
231+
}
232+
String memberContent = Files.readString(memberManifest, StandardCharsets.UTF_8);
233+
Set<String> memberIgnored = getIgnoredDependencies(memberToml, memberContent);
234+
if (memberIgnored.isEmpty()) {
235+
return workspaceIgnoredDeps;
236+
}
237+
if (debugLoggingIsNeeded()) {
238+
log.info(
239+
"Found "
240+
+ memberIgnored.size()
241+
+ " ignored dependencies in member "
242+
+ memberPkg.name()
243+
+ ": "
244+
+ memberIgnored);
245+
}
246+
Set<String> merged = new HashSet<>(workspaceIgnoredDeps);
247+
merged.addAll(memberIgnored);
248+
return merged;
249+
} catch (IOException e) {
250+
log.warning(
251+
"Failed to read member Cargo.toml for ignore patterns: "
252+
+ memberManifest
253+
+ ": "
254+
+ e.getMessage());
255+
return workspaceIgnoredDeps;
256+
}
257+
}
258+
212259
void processWorkspaceDependencies(
213260
Sbom sbom,
214261
PackageURL root,

src/main/java/io/github/guacsec/trustifyda/providers/rust/model/CargoPackage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ public record CargoPackage(
2424
@JsonProperty("name") String name,
2525
@JsonProperty("version") String version,
2626
@JsonProperty("id") String id,
27+
@JsonProperty("manifest_path") String manifestPath,
2728
@JsonProperty("dependencies") List<CargoDependency> dependencies) {}

src/test/java/io/github/guacsec/trustifyda/providers/CargoProviderCargoParsingTest.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,4 +766,83 @@ public void testVirtualWorkspaceWithoutWorkspaceDepsDoesNotThrowNPE(@TempDir Pat
766766
sbom, root, new HashMap<>(), new HashSet<>(), tomlResult),
767767
"processWorkspaceDependencies should handle missing [workspace.dependencies] gracefully");
768768
}
769+
770+
@Test
771+
public void testMemberCargoTomlIgnorePatternsDetected(@TempDir Path tempDir) throws Exception {
772+
// Simulate a member's Cargo.toml with exhortignore on a dependency
773+
Path memberDir = tempDir.resolve("crate-a");
774+
Files.createDirectories(memberDir);
775+
Path memberCargoToml = memberDir.resolve("Cargo.toml");
776+
String memberContent =
777+
"""
778+
[package]
779+
name = "crate-a"
780+
version = "0.1.0"
781+
edition = "2021"
782+
783+
[dependencies]
784+
serde = "1.0" # exhortignore
785+
tokio = "1.0"
786+
reqwest = "0.11" # trustify-da-ignore
787+
""";
788+
Files.writeString(memberCargoToml, memberContent);
789+
790+
CargoProvider provider = new CargoProvider(memberCargoToml);
791+
792+
java.lang.reflect.Method method =
793+
CargoProvider.class.getDeclaredMethod(
794+
"getIgnoredDependencies", TomlParseResult.class, String.class);
795+
method.setAccessible(true);
796+
797+
TomlParseResult tomlResult = Toml.parse(memberCargoToml);
798+
String content = Files.readString(memberCargoToml, StandardCharsets.UTF_8);
799+
800+
@SuppressWarnings("unchecked")
801+
Set<String> ignoredDeps = (Set<String>) method.invoke(provider, tomlResult, content);
802+
803+
assertTrue(ignoredDeps.contains("serde"), "serde should be ignored (exhortignore)");
804+
assertFalse(ignoredDeps.contains("tokio"), "tokio should NOT be ignored");
805+
assertTrue(ignoredDeps.contains("reqwest"), "reqwest should be ignored (trustify-da-ignore)");
806+
assertEquals(2, ignoredDeps.size(), "Should find exactly 2 ignored dependencies in member");
807+
}
808+
809+
@Test
810+
public void testMemberIgnorePatternsWithTableFormat(@TempDir Path tempDir) throws Exception {
811+
Path memberDir = tempDir.resolve("crate-b");
812+
Files.createDirectories(memberDir);
813+
Path memberCargoToml = memberDir.resolve("Cargo.toml");
814+
String memberContent =
815+
"""
816+
[package]
817+
name = "crate-b"
818+
version = "0.1.0"
819+
edition = "2021"
820+
821+
[dependencies]
822+
serde-json-wasm = "1.0"
823+
824+
[dependencies.aho-corasick] # trustify-da-ignore
825+
version = "1.0.0"
826+
""";
827+
Files.writeString(memberCargoToml, memberContent);
828+
829+
CargoProvider provider = new CargoProvider(memberCargoToml);
830+
831+
java.lang.reflect.Method method =
832+
CargoProvider.class.getDeclaredMethod(
833+
"getIgnoredDependencies", TomlParseResult.class, String.class);
834+
method.setAccessible(true);
835+
836+
TomlParseResult tomlResult = Toml.parse(memberCargoToml);
837+
String content = Files.readString(memberCargoToml, StandardCharsets.UTF_8);
838+
839+
@SuppressWarnings("unchecked")
840+
Set<String> ignoredDeps = (Set<String>) method.invoke(provider, tomlResult, content);
841+
842+
assertFalse(ignoredDeps.contains("serde-json-wasm"), "serde-json-wasm should NOT be ignored");
843+
assertTrue(
844+
ignoredDeps.contains("aho-corasick"),
845+
"aho-corasick should be ignored (table format with trustify-da-ignore)");
846+
assertEquals(1, ignoredDeps.size(), "Should find exactly 1 ignored dependency in member");
847+
}
769848
}

0 commit comments

Comments
 (0)