Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,60 @@ private void handleVirtualWorkspace(
+ metadata.workspaceMembers());
}
for (String memberId : metadata.workspaceMembers()) {
Set<String> memberIgnoredDeps = getMemberIgnoredDeps(memberId, packageMap, ignoredDeps);
processWorkspaceMember(
sbom, root, memberId, nodeMap, packageMap, ignoredDeps, analysisType);
sbom, root, memberId, nodeMap, packageMap, memberIgnoredDeps, analysisType);
}
}
}
}

/**
* Builds the full set of ignored dependencies for a workspace member by reading the member's own
* Cargo.toml for ignore patterns and merging them with the workspace-level ignored deps.
*/
private Set<String> getMemberIgnoredDeps(
String memberId, Map<String, CargoPackage> packageMap, Set<String> workspaceIgnoredDeps) {
CargoPackage memberPkg = packageMap.get(memberId);
if (memberPkg == null || memberPkg.manifestPath() == null) {
return workspaceIgnoredDeps;
}
Path memberManifest = Path.of(memberPkg.manifestPath());
if (!Files.isRegularFile(memberManifest)) {
return workspaceIgnoredDeps;
}
try {
TomlParseResult memberToml = Toml.parse(memberManifest);
if (memberToml.hasErrors()) {
return workspaceIgnoredDeps;
}
String memberContent = Files.readString(memberManifest, StandardCharsets.UTF_8);
Set<String> memberIgnored = getIgnoredDependencies(memberToml, memberContent);
if (memberIgnored.isEmpty()) {
return workspaceIgnoredDeps;
}
if (debugLoggingIsNeeded()) {
log.info(
"Found "
+ memberIgnored.size()
+ " ignored dependencies in member "
+ memberPkg.name()
+ ": "
+ memberIgnored);
}
Set<String> merged = new HashSet<>(workspaceIgnoredDeps);
merged.addAll(memberIgnored);
return merged;
} catch (IOException e) {
log.warning(
"Failed to read member Cargo.toml for ignore patterns: "
+ memberManifest
+ ": "
+ e.getMessage());
return workspaceIgnoredDeps;
}
}

void processWorkspaceDependencies(
Sbom sbom,
PackageURL root,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ public record CargoPackage(
@JsonProperty("name") String name,
@JsonProperty("version") String version,
@JsonProperty("id") String id,
@JsonProperty("manifest_path") String manifestPath,
@JsonProperty("dependencies") List<CargoDependency> dependencies) {}
Original file line number Diff line number Diff line change
Expand Up @@ -766,4 +766,83 @@ public void testVirtualWorkspaceWithoutWorkspaceDepsDoesNotThrowNPE(@TempDir Pat
sbom, root, new HashMap<>(), new HashSet<>(), tomlResult),
"processWorkspaceDependencies should handle missing [workspace.dependencies] gracefully");
}

@Test
public void testMemberCargoTomlIgnorePatternsDetected(@TempDir Path tempDir) throws Exception {
// Simulate a member's Cargo.toml with exhortignore on a dependency
Path memberDir = tempDir.resolve("crate-a");
Files.createDirectories(memberDir);
Path memberCargoToml = memberDir.resolve("Cargo.toml");
String memberContent =
"""
[package]
name = "crate-a"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = "1.0" # exhortignore
tokio = "1.0"
reqwest = "0.11" # trustify-da-ignore
""";
Files.writeString(memberCargoToml, memberContent);

CargoProvider provider = new CargoProvider(memberCargoToml);

java.lang.reflect.Method method =
Comment thread
soul2zimate marked this conversation as resolved.
Outdated
CargoProvider.class.getDeclaredMethod(
"getIgnoredDependencies", TomlParseResult.class, String.class);
method.setAccessible(true);

TomlParseResult tomlResult = Toml.parse(memberCargoToml);
String content = Files.readString(memberCargoToml, StandardCharsets.UTF_8);

@SuppressWarnings("unchecked")
Set<String> ignoredDeps = (Set<String>) method.invoke(provider, tomlResult, content);

assertTrue(ignoredDeps.contains("serde"), "serde should be ignored (exhortignore)");
assertFalse(ignoredDeps.contains("tokio"), "tokio should NOT be ignored");
assertTrue(ignoredDeps.contains("reqwest"), "reqwest should be ignored (trustify-da-ignore)");
assertEquals(2, ignoredDeps.size(), "Should find exactly 2 ignored dependencies in member");
}

@Test
public void testMemberIgnorePatternsWithTableFormat(@TempDir Path tempDir) throws Exception {
Path memberDir = tempDir.resolve("crate-b");
Files.createDirectories(memberDir);
Path memberCargoToml = memberDir.resolve("Cargo.toml");
String memberContent =
"""
[package]
name = "crate-b"
version = "0.1.0"
edition = "2021"

[dependencies]
serde-json-wasm = "1.0"

[dependencies.aho-corasick] # trustify-da-ignore
version = "1.0.0"
""";
Files.writeString(memberCargoToml, memberContent);

CargoProvider provider = new CargoProvider(memberCargoToml);

java.lang.reflect.Method method =
CargoProvider.class.getDeclaredMethod(
"getIgnoredDependencies", TomlParseResult.class, String.class);
method.setAccessible(true);

TomlParseResult tomlResult = Toml.parse(memberCargoToml);
String content = Files.readString(memberCargoToml, StandardCharsets.UTF_8);

@SuppressWarnings("unchecked")
Set<String> ignoredDeps = (Set<String>) method.invoke(provider, tomlResult, content);

assertFalse(ignoredDeps.contains("serde-json-wasm"), "serde-json-wasm should NOT be ignored");
assertTrue(
ignoredDeps.contains("aho-corasick"),
"aho-corasick should be ignored (table format with trustify-da-ignore)");
assertEquals(1, ignoredDeps.size(), "Should find exactly 1 ignored dependency in member");
}
}
Loading