From ebac92a86ebae0002ec5bee50c2f5ebf6f32b26b Mon Sep 17 00:00:00 2001 From: Marcus Date: Mon, 6 Jul 2026 15:17:42 +0000 Subject: [PATCH] fix: conanfile comments in [requires] no longer stop dependency detection (#5020) The [requires] section parser set inRequirements=false when it encountered a comment line (#) inside the section, because the comment check was OR'd with the section-change check. Any dependency listed after a comment was silently skipped. Skip blank lines and comments before evaluating section state so they never toggle inRequirements. Add a regression test fixture with a comment inside [requires]. Fixes #5017 Signed-off-by: jeff --- syft/pkg/cataloger/cpp/parse_conanfile.go | 17 ++++++---- .../pkg/cataloger/cpp/parse_conanfile_test.go | 33 +++++++++++++++++++ .../conanfile-comment-in-requires.txt | 9 +++++ 3 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 syft/pkg/cataloger/cpp/testdata/conanfile-comment-in-requires.txt diff --git a/syft/pkg/cataloger/cpp/parse_conanfile.go b/syft/pkg/cataloger/cpp/parse_conanfile.go index 5ae124051..677a1b8cb 100644 --- a/syft/pkg/cataloger/cpp/parse_conanfile.go +++ b/syft/pkg/cataloger/cpp/parse_conanfile.go @@ -31,15 +31,20 @@ func parseConanfile(_ context.Context, _ file.Resolver, _ *generic.Environment, return nil, nil, fmt.Errorf("failed to parse conanfile.txt file: %w", err) } + trimmed := strings.TrimSpace(line) + + // skip blank lines and comments without affecting section state + if trimmed == "" || strings.HasPrefix(trimmed, "#") { + continue + } + switch { case strings.Contains(line, "[requires]"): inRequirements = true - case strings.ContainsAny(line, "[]") || strings.HasPrefix(strings.TrimSpace(line), "#"): + continue + case strings.ContainsAny(line, "[]"): inRequirements = false - } - - m := pkg.ConanfileEntry{ - Ref: strings.TrimSpace(line), + continue } if !inRequirements { @@ -47,7 +52,7 @@ func parseConanfile(_ context.Context, _ file.Resolver, _ *generic.Environment, } p := newConanfilePackage( - m, + pkg.ConanfileEntry{Ref: trimmed}, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ) if p == nil { diff --git a/syft/pkg/cataloger/cpp/parse_conanfile_test.go b/syft/pkg/cataloger/cpp/parse_conanfile_test.go index 53995493b..9fc6a9384 100644 --- a/syft/pkg/cataloger/cpp/parse_conanfile_test.go +++ b/syft/pkg/cataloger/cpp/parse_conanfile_test.go @@ -86,3 +86,36 @@ func TestParseConanfile(t *testing.T) { pkgtest.TestFileParser(t, fixture, parseConanfile, expected, expectedRelationships) } + +func TestParseConanfileCommentInRequires(t *testing.T) { + fixture := "testdata/conanfile-comment-in-requires.txt" + fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture)) + expected := []pkg.Package{ + { + Name: "catch2", + Version: "2.13.8", + PURL: "pkg:conan/catch2@2.13.8", + Locations: fixtureLocationSet, + Language: pkg.CPP, + Type: pkg.ConanPkg, + Metadata: pkg.ConanfileEntry{ + Ref: "catch2/2.13.8", + }, + }, + { + Name: "docopt.cpp", + Version: "0.6.3", + PURL: "pkg:conan/docopt.cpp@0.6.3", + Locations: fixtureLocationSet, + Language: pkg.CPP, + Type: pkg.ConanPkg, + Metadata: pkg.ConanfileEntry{ + Ref: "docopt.cpp/0.6.3", + }, + }, + } + + var expectedRelationships []artifact.Relationship + + pkgtest.TestFileParser(t, fixture, parseConanfile, expected, expectedRelationships) +} diff --git a/syft/pkg/cataloger/cpp/testdata/conanfile-comment-in-requires.txt b/syft/pkg/cataloger/cpp/testdata/conanfile-comment-in-requires.txt new file mode 100644 index 000000000..207158045 --- /dev/null +++ b/syft/pkg/cataloger/cpp/testdata/conanfile-comment-in-requires.txt @@ -0,0 +1,9 @@ +# Docs at https://docs.conan.io/en/latest/reference/conanfile_txt.html + +[requires] +# this is a comment inside requires +catch2/2.13.8 +docopt.cpp/0.6.3 + +[generators] +cmake_find_package_multi