@@ -110,7 +110,8 @@ func (p *Parser) Parse(_ context.Context, r xio.ReadSeekerAt) ([]ftypes.Package,
110110 // See https://github.com/aquasecurity/trivy/issues/1837#issuecomment-1832523477.
111111 version := p .checkVersion (info .Main .Path , info .Main .Version )
112112 ldflagsVersion := p .ParseLDFlags (info .Main .Path , ldflags )
113- version = p .chooseMainVersion (version , ldflagsVersion )
113+ elfVersion := p .elfSymbolVersion (r , info .Main .Path )
114+ version = p .chooseMainVersion (version , ldflagsVersion , elfVersion )
114115
115116 root := ftypes.Package {
116117 ID : dependency .ID (ftypes .GoBinary , info .Main .Path , version ),
@@ -148,15 +149,29 @@ func (p *Parser) checkVersion(name, version string) string {
148149}
149150
150151// chooseMainVersion determines which version to use for the main module.
151- // It prefers the ldflags version when:
152- // - The build info version is empty, OR
153- // - The build info version is a pseudo-version AND ldflags version is available
154- // This handles cases where actual release versions are injected via -ldflags.
155- func (p * Parser ) chooseMainVersion (version , ldflagsVersion string ) string {
156- if version == "" || (module .IsPseudoVersion (version ) && ldflagsVersion != "" ) {
152+ // The priority order is:
153+ // 1. Build info version (if it is a real semver, e.g. "v1.2.3" from `go install`)
154+ // 2. ldflags version (e.g. `-ldflags "-X main.version=v1.0.0"`)
155+ // 3. ELF symbol table version (fallback when `-trimpath` hides `-ldflags`)
156+ // 4. Original version as-is (may be empty or a pseudo-version)
157+ //
158+ // Examples:
159+ //
160+ // chooseMainVersion("v1.2.3", "v1.0.0", "v1.0.0") => "v1.2.3" (real semver wins)
161+ // chooseMainVersion("v0.0.0-2024...", "v1.0.0", "") => "v1.0.0" (ldflags over pseudo)
162+ // chooseMainVersion("v0.0.0-2024...", "", "v2.0.0") => "v2.0.0" (ELF over pseudo)
163+ // chooseMainVersion("", "", "") => "" (nothing available)
164+ func (p * Parser ) chooseMainVersion (version , ldflagsVersion , elfVersion string ) string {
165+ switch {
166+ case version != "" && ! module .IsPseudoVersion (version ):
167+ return version
168+ case ldflagsVersion != "" :
157169 return ldflagsVersion
170+ case elfVersion != "" :
171+ return elfVersion
172+ default :
173+ return version
158174 }
159- return version
160175}
161176
162177func (p * Parser ) ldFlags (settings []debug.BuildSetting ) []string {
@@ -216,14 +231,7 @@ func (p *Parser) ParseLDFlags(name string, flags []string) string {
216231 key = strings .TrimLeft (key , `'` )
217232 val = strings .TrimRight (val , `'` )
218233 if isVersionXKey (key ) && isValidSemVer (val ) {
219- switch {
220- case strings .HasPrefix (key , name + "/cmd/" ):
221- foundVersions [0 ] = append (foundVersions [0 ], val )
222- case defaultVersionPrefixes .Contains (versionPrefix (key )):
223- foundVersions [1 ] = append (foundVersions [1 ], val )
224- default :
225- foundVersions [2 ] = append (foundVersions [2 ], val )
226- }
234+ classifyVersion (foundVersions , key , name , val )
227235 }
228236 }
229237
@@ -267,6 +275,23 @@ func isValidSemVer(ver string) bool {
267275 return semver .IsValid (ver ) || semver .IsValid ("v" + ver )
268276}
269277
278+ // classifyVersion categorizes a version value into one of three priority tiers
279+ // based on its key:
280+ //
281+ // [0]: <module_path>/cmd/**/*.version
282+ // [1]: defaultVersionPrefixes (main, common, version, cmd)
283+ // [2]: other
284+ func classifyVersion (foundVersions [][]string , key , moduleName , val string ) {
285+ switch {
286+ case strings .HasPrefix (key , moduleName + "/cmd/" ):
287+ foundVersions [0 ] = append (foundVersions [0 ], val )
288+ case defaultVersionPrefixes .Contains (versionPrefix (key )):
289+ foundVersions [1 ] = append (foundVersions [1 ], val )
290+ default :
291+ foundVersions [2 ] = append (foundVersions [2 ], val )
292+ }
293+ }
294+
270295// versionPrefix returns version prefix from `-ldflags` flag key
271296// e.g.
272297// - `github.com/aquasecurity/trivy/pkg/version/app.ver` => `version`
0 commit comments