diff --git a/pkg/fanal/analyzer/language/rust/cargo/cargo.go b/pkg/fanal/analyzer/language/rust/cargo/cargo.go index 03154d142b..4e75217a90 100644 --- a/pkg/fanal/analyzer/language/rust/cargo/cargo.go +++ b/pkg/fanal/analyzer/language/rust/cargo/cargo.go @@ -222,16 +222,32 @@ func (a cargoAnalyzer) parseRootCargoTOML(fsys fs.FS, filePath string) (string, var workspaces []string for _, member := range members { memberPath := path.Join(path.Dir(filePath), member, types.CargoToml) - memberPkg, memberDeps, _, err := a.parseCargoTOML(fsys, memberPath) + + // Cargo workspaces can be globs: + // https://github.com/rust-lang/cargo/pull/3979 + resolvedPaths, err := fs.Glob(fsys, memberPath) if err != nil { - a.logger.Warn("Unable to parse Cargo.toml", log.String("member_path", memberPath), log.Err(err)) + a.logger.Warn("Invalid glob pattern in Cargo.toml member paths", log.String("member_path", memberPath), log.Err(err)) continue } - workspaces = append(workspaces, memberPkg) - // Member dependencies shouldn't overwrite dependencies from root cargo.toml file - maps.Copy(memberDeps, dependencies) - dependencies = memberDeps + if len(resolvedPaths) == 0 { + a.logger.Warn("Unable to find matching Cargo.toml files", log.String("member_path", memberPath)) + continue + } + + for _, pkg := range resolvedPaths { + memberPkg, memberDeps, _, err := a.parseCargoTOML(fsys, pkg) + if err != nil { + a.logger.Warn("Unable to parse Cargo.toml", log.String("member_path", pkg), log.Err(err)) + continue + } + workspaces = append(workspaces, memberPkg) + + // Member dependencies shouldn't overwrite dependencies from root cargo.toml file + maps.Copy(memberDeps, dependencies) + dependencies = memberDeps + } } deps := make(map[string]string) diff --git a/pkg/fanal/analyzer/language/rust/cargo/cargo_test.go b/pkg/fanal/analyzer/language/rust/cargo/cargo_test.go index 11fa15bde9..b6e5252117 100644 --- a/pkg/fanal/analyzer/language/rust/cargo/cargo_test.go +++ b/pkg/fanal/analyzer/language/rust/cargo/cargo_test.go @@ -608,6 +608,178 @@ func Test_cargoAnalyzer_Analyze(t *testing.T) { }, }, }, + { + name: "workspace members", + dir: "testdata/toml-workspace-glob", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.Cargo, + FilePath: "Cargo.lock", + Packages: types.Packages{ + { + ID: "18164bd748b1f49e", + Relationship: types.RelationshipRoot, + DependsOn: []string{ + "member1@0.1.0", + "member2@0.1.0", + }, + }, + { + ID: "member1@0.1.0", + Name: "member1", + Version: "0.1.0", + Relationship: types.RelationshipWorkspace, + Locations: []types.Location{ + { + StartLine: 30, + EndLine: 35, + }, + }, + DependsOn: []string{ + "gdb-command@0.7.6", + }, + }, + { + ID: "member2@0.1.0", + Name: "member2", + Version: "0.1.0", + Relationship: types.RelationshipWorkspace, + Locations: []types.Location{ + { + StartLine: 37, + EndLine: 42, + }, + }, + DependsOn: []string{ + "regex@1.10.2", + }, + }, + { + ID: "gdb-command@0.7.6", + Name: "gdb-command", + Version: "0.7.6", + Indirect: false, + Relationship: types.RelationshipDirect, + Locations: []types.Location{ + { + StartLine: 14, + EndLine: 22, + }, + }, + DependsOn: []string{ + "regex@1.10.2", + "wait-timeout@0.2.0", + }, + }, + { + ID: "regex@1.10.2", + Name: "regex", + Version: "1.10.2", + Relationship: types.RelationshipDirect, + Locations: []types.Location{ + { + StartLine: 50, + EndLine: 60, + }, + }, + DependsOn: []string{ + "aho-corasick@1.1.2", + "memchr@2.6.4", + "regex-automata@0.4.3", + "regex-syntax@0.8.2", + }, + }, + { + ID: "aho-corasick@1.1.2", + Name: "aho-corasick", + Version: "1.1.2", + Indirect: true, + Relationship: types.RelationshipIndirect, + Locations: []types.Location{ + { + StartLine: 5, + EndLine: 12, + }, + }, + DependsOn: []string{"memchr@2.6.4"}, + }, + { + ID: "libc@0.2.150", + Name: "libc", + Version: "0.2.150", + Indirect: true, + Relationship: types.RelationshipIndirect, + Locations: []types.Location{ + { + StartLine: 24, + EndLine: 28, + }, + }, + }, + { + ID: "memchr@2.6.4", + Name: "memchr", + Version: "2.6.4", + Indirect: true, + Relationship: types.RelationshipIndirect, + Locations: []types.Location{ + { + StartLine: 44, + EndLine: 48, + }, + }, + }, + { + ID: "regex-automata@0.4.3", + Name: "regex-automata", + Version: "0.4.3", + Indirect: true, + Relationship: types.RelationshipIndirect, + Locations: []types.Location{ + { + StartLine: 62, + EndLine: 71, + }, + }, + DependsOn: []string{ + "aho-corasick@1.1.2", + "memchr@2.6.4", + "regex-syntax@0.8.2", + }, + }, + { + ID: "regex-syntax@0.8.2", + Name: "regex-syntax", + Version: "0.8.2", + Indirect: true, + Relationship: types.RelationshipIndirect, + Locations: []types.Location{ + { + StartLine: 73, + EndLine: 77, + }, + }, + }, + { + ID: "wait-timeout@0.2.0", + Name: "wait-timeout", + Version: "0.2.0", + Indirect: true, + Relationship: types.RelationshipIndirect, + Locations: []types.Location{ + { + StartLine: 79, + EndLine: 86, + }, + }, + DependsOn: []string{"libc@0.2.150"}, + }, + }, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/Cargo.lock b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/Cargo.lock new file mode 100644 index 0000000000..0dcf81c6d3 --- /dev/null +++ b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/Cargo.lock @@ -0,0 +1,86 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "gdb-command" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a067c49eb3dfc7c2357d3d37536d2f57c5250ac377672164286b8d5ea94c5d" +dependencies = [ + "regex", + "wait-timeout", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "member1" +version = "0.1.0" +dependencies = [ + "gdb-command", +] + +[[package]] +name = "member2" +version = "0.1.0" +dependencies = [ + "regex", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] diff --git a/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/Cargo.toml b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/Cargo.toml new file mode 100644 index 0000000000..efd576cc0a --- /dev/null +++ b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/Cargo.toml @@ -0,0 +1,10 @@ +[workspace.package] +name = "toml-workspace-members" +version = "0.1.0" + +[workspace] +resolver = "2" +members = ["member*"] + +[workspace.dependencies] +regex = "1" diff --git a/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/member1/Cargo.toml b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/member1/Cargo.toml new file mode 100644 index 0000000000..3491225246 --- /dev/null +++ b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/member1/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "member1" +version = "0.1.0" +edition = "2021" + +[dependencies] +gdb-command = "0.7" diff --git a/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/member2/Cargo.toml b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/member2/Cargo.toml new file mode 100644 index 0000000000..297d3a8659 --- /dev/null +++ b/pkg/fanal/analyzer/language/rust/cargo/testdata/toml-workspace-glob/member2/Cargo.toml @@ -0,0 +1,7 @@ +[package] +edition = "2021" +name = "member2" +version = "0.1.0" + +[dependencies] +regex = { workspace = true}