github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/binary/classifiers.go (about)

     1  package binary
     2  
     3  import (
     4  	"github.com/anchore/syft/syft/cpe"
     5  )
     6  
     7  //nolint:funlen
     8  func DefaultClassifiers() []Classifier {
     9  	return []Classifier{
    10  		{
    11  			Class:    "python-binary",
    12  			FileGlob: "**/python*",
    13  			EvidenceMatcher: evidenceMatchers(
    14  				// try to find version information from libpython shared libraries
    15  				sharedLibraryLookup(
    16  					`^libpython[0-9]+(?:\.[0-9]+)+[a-z]?\.so.*$`,
    17  					libpythonMatcher),
    18  				// check for version information in the binary
    19  				fileNameTemplateVersionMatcher(
    20  					`(?:.*/|^)python(?P<version>[0-9]+(?:\.[0-9]+)+)$`,
    21  					pythonVersionTemplate),
    22  			),
    23  			Package: "python",
    24  			PURL:    mustPURL("pkg:generic/python@version"),
    25  			CPEs: []cpe.CPE{
    26  				cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
    27  				cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
    28  			},
    29  		},
    30  		{
    31  			Class:           "python-binary-lib",
    32  			FileGlob:        "**/libpython*.so*",
    33  			EvidenceMatcher: libpythonMatcher,
    34  			Package:         "python",
    35  			PURL:            mustPURL("pkg:generic/python@version"),
    36  			CPEs: []cpe.CPE{
    37  				cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
    38  				cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
    39  			},
    40  		},
    41  		{
    42  			Class:    "pypy-binary-lib",
    43  			FileGlob: "**/libpypy*.so*",
    44  			EvidenceMatcher: FileContentsVersionMatcher(
    45  				`(?m)\[PyPy (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
    46  			Package: "pypy",
    47  			PURL:    mustPURL("pkg:generic/pypy@version"),
    48  		},
    49  		{
    50  			Class:    "go-binary",
    51  			FileGlob: "**/go",
    52  			EvidenceMatcher: FileContentsVersionMatcher(
    53  				`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)\x00`),
    54  			Package: "go",
    55  			PURL:    mustPURL("pkg:generic/go@version"),
    56  			CPEs:    singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"),
    57  		},
    58  		{
    59  			Class:    "julia-binary",
    60  			FileGlob: "**/libjulia-internal.so",
    61  			EvidenceMatcher: FileContentsVersionMatcher(
    62  				`(?m)__init__\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00verify`),
    63  			Package: "julia",
    64  			PURL:    mustPURL("pkg:generic/julia@version"),
    65  			CPEs:    singleCPE("cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*"),
    66  		},
    67  		{
    68  			Class:    "helm",
    69  			FileGlob: "**/helm",
    70  			EvidenceMatcher: FileContentsVersionMatcher(
    71  				`(?m)\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`),
    72  			Package: "helm",
    73  			PURL:    mustPURL("pkg:golang/helm.sh/helm@version"),
    74  			CPEs:    singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*"),
    75  		},
    76  		{
    77  			Class:    "redis-binary",
    78  			FileGlob: "**/redis-server",
    79  			EvidenceMatcher: evidenceMatchers(
    80  				FileContentsVersionMatcher(`(?s)payload %5.*?(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}`),
    81  				FileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12}-[0-9]{19}\x00.*?payload %5`),
    82  			),
    83  			Package: "redis",
    84  			PURL:    mustPURL("pkg:generic/redis@version"),
    85  			CPEs:    singleCPE("cpe:2.3:a:redislabs:redis:*:*:*:*:*:*:*:*"),
    86  		},
    87  		{
    88  			Class:    "java-binary-openjdk",
    89  			FileGlob: "**/java",
    90  			EvidenceMatcher: matchExcluding(
    91  				evidenceMatchers(
    92  					FileContentsVersionMatcher(
    93  						// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
    94  						// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
    95  						`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
    96  					FileContentsVersionMatcher(
    97  						// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
    98  						`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+openjdk\x00java`),
    99  				),
   100  				// don't match graalvm
   101  				"-jvmci-",
   102  			),
   103  			Package: "java/jre",
   104  			PURL:    mustPURL("pkg:generic/java/jre@version"),
   105  			// TODO the updates might need to be part of the CPE Attributes, like: 1.8.0:update152
   106  			CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*"),
   107  		},
   108  		{
   109  			Class:    "java-binary-ibm",
   110  			FileGlob: "**/java",
   111  			EvidenceMatcher: FileContentsVersionMatcher(
   112  				// [NUL]java[NUL]1.8[NUL][NUL][NUL][NUL]1.8.0-foreman_2022_09_22_15_30-b00[NUL]
   113  				`(?m)\x00java\x00(?P<release>[0-9]+[.0-9]+)\x00{4}(?P<version>[0-9]+[-._a-zA-Z0-9]+)\x00`),
   114  			Package: "java/jre",
   115  			PURL:    mustPURL("pkg:generic/java/jre@version"),
   116  			CPEs:    singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*"),
   117  		},
   118  		{
   119  			Class:    "java-binary-oracle",
   120  			FileGlob: "**/java",
   121  			EvidenceMatcher: matchExcluding(
   122  				FileContentsVersionMatcher(
   123  					// [NUL]19.0.1+10-21[NUL]
   124  					`(?m)\x00(?P<version>[0-9]+[.0-9]+[+][-0-9]+)\x00`),
   125  				// don't match openjdk
   126  				`\x00openjdk\x00`,
   127  			),
   128  			Package: "java/jre",
   129  			PURL:    mustPURL("pkg:generic/java/jre@version"),
   130  			CPEs:    singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*"),
   131  		},
   132  		{
   133  			Class:    "java-binary-graalvm",
   134  			FileGlob: "**/java",
   135  			EvidenceMatcher: FileContentsVersionMatcher(
   136  				`(?m)\x00(?P<version>[0-9]+[.0-9]+[.0-9]+\+[0-9]+-jvmci-[0-9]+[.0-9]+-b[0-9]+)\x00`),
   137  			Package: "java/graalvm",
   138  			PURL:    mustPURL("pkg:generic/java/graalvm@version"),
   139  			CPEs:    singleCPE("cpe:2.3:a:oracle:graalvm:*:*:*:*:*:*:*:*"),
   140  		},
   141  		{
   142  			Class:    "java-binary-jdk",
   143  			FileGlob: "**/jdb",
   144  			EvidenceMatcher: FileContentsVersionMatcher(
   145  				`(?m)\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\+[0-9]+)?([-._a-zA-Z0-9]+)?)\x00`),
   146  			Package: "java/jdk",
   147  			PURL:    mustPURL("pkg:generic/java/jdk@version"),
   148  			CPEs:    singleCPE("cpe:2.3:a:oracle:jdk:*:*:*:*:*:*:*:*"),
   149  		},
   150  		{
   151  			Class:    "nodejs-binary",
   152  			FileGlob: "**/node",
   153  			EvidenceMatcher: FileContentsVersionMatcher(
   154  				`(?m)node\.js\/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
   155  			Package: "node",
   156  			PURL:    mustPURL("pkg:generic/node@version"),
   157  			CPEs:    singleCPE("cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*"),
   158  		},
   159  		{
   160  			Class:    "go-binary-hint",
   161  			FileGlob: "**/VERSION",
   162  			EvidenceMatcher: FileContentsVersionMatcher(
   163  				`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)`),
   164  			Package: "go",
   165  			PURL:    mustPURL("pkg:generic/go@version"),
   166  		},
   167  		{
   168  			Class:    "busybox-binary",
   169  			FileGlob: "**/busybox",
   170  			EvidenceMatcher: FileContentsVersionMatcher(
   171  				`(?m)BusyBox\s+v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
   172  			Package: "busybox",
   173  			PURL:    mustPURL("pkg:generic/busybox@version"),
   174  			CPEs:    singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"),
   175  		},
   176  		{
   177  			Class:    "haproxy-binary",
   178  			FileGlob: "**/haproxy",
   179  			EvidenceMatcher: evidenceMatchers(
   180  				FileContentsVersionMatcher(`(?m)HA-Proxy version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
   181  				FileContentsVersionMatcher(`(?m)(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-[0-9a-zA-Z]{7}.+HAProxy version`),
   182  			),
   183  			Package: "haproxy",
   184  			PURL:    mustPURL("pkg:generic/haproxy@version"),
   185  			CPEs:    singleCPE("cpe:2.3:a:haproxy:haproxy:*:*:*:*:*:*:*:*"),
   186  		},
   187  		{
   188  			Class:    "perl-binary",
   189  			FileGlob: "**/perl",
   190  			EvidenceMatcher: FileContentsVersionMatcher(
   191  				`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
   192  			Package: "perl",
   193  			PURL:    mustPURL("pkg:generic/perl@version"),
   194  			CPEs:    singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*"),
   195  		},
   196  		{
   197  			Class:    "php-cli-binary",
   198  			FileGlob: "**/php*",
   199  			EvidenceMatcher: fileNameTemplateVersionMatcher(
   200  				`(.*/|^)php[0-9]*$`,
   201  				`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
   202  			Package: "php-cli",
   203  			PURL:    mustPURL("pkg:generic/php-cli@version"),
   204  			CPEs:    singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
   205  		},
   206  		{
   207  			Class:    "php-fpm-binary",
   208  			FileGlob: "**/php-fpm*",
   209  			EvidenceMatcher: FileContentsVersionMatcher(
   210  				`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
   211  			Package: "php-fpm",
   212  			PURL:    mustPURL("pkg:generic/php-fpm@version"),
   213  			CPEs:    singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
   214  		},
   215  		{
   216  			Class:    "php-apache-binary",
   217  			FileGlob: "**/libphp*.so",
   218  			EvidenceMatcher: FileContentsVersionMatcher(
   219  				`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
   220  			Package: "libphp",
   221  			PURL:    mustPURL("pkg:generic/php@version"),
   222  			CPEs:    singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
   223  		},
   224  		{
   225  			Class:    "php-composer-binary",
   226  			FileGlob: "**/composer*",
   227  			EvidenceMatcher: FileContentsVersionMatcher(
   228  				`(?m)'pretty_version'\s*=>\s*'(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)'`),
   229  			Package: "composer",
   230  			PURL:    mustPURL("pkg:generic/composer@version"),
   231  			CPEs:    singleCPE("cpe:2.3:a:getcomposer:composer:*:*:*:*:*:*:*:*"),
   232  		},
   233  		{
   234  			Class:    "httpd-binary",
   235  			FileGlob: "**/httpd",
   236  			EvidenceMatcher: FileContentsVersionMatcher(
   237  				`(?m)Apache\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
   238  			Package: "httpd",
   239  			PURL:    mustPURL("pkg:generic/httpd@version"),
   240  			CPEs:    singleCPE("cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*"),
   241  		},
   242  		{
   243  			Class:    "memcached-binary",
   244  			FileGlob: "**/memcached",
   245  			EvidenceMatcher: FileContentsVersionMatcher(
   246  				`(?m)memcached\s(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
   247  			Package: "memcached",
   248  			PURL:    mustPURL("pkg:generic/memcached@version"),
   249  			CPEs:    singleCPE("cpe:2.3:a:memcached:memcached:*:*:*:*:*:*:*:*"),
   250  		},
   251  		{
   252  			Class:    "traefik-binary",
   253  			FileGlob: "**/traefik",
   254  			EvidenceMatcher: FileContentsVersionMatcher(
   255  				// [NUL]v1.7.34[NUL]
   256  				// [NUL]2.9.6[NUL]
   257  				`(?m)(\x00|\x{FFFD})v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`),
   258  			Package: "traefik",
   259  			PURL:    mustPURL("pkg:generic/traefik@version"),
   260  			CPEs:    singleCPE("cpe:2.3:a:traefik:traefik:*:*:*:*:*:*:*:*"),
   261  		},
   262  		{
   263  			Class:    "arangodb-binary",
   264  			FileGlob: "**/arangosh",
   265  			EvidenceMatcher: FileContentsVersionMatcher(
   266  				`(?m)ArangoDB\s\x00*(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\s\[linux\]`),
   267  			Package: "arangodb",
   268  			PURL:    mustPURL("pkg:generic/arangodb@version"),
   269  			CPEs:    singleCPE("cpe:2.3:a:arangodb:arangodb:*:*:*:*:*:*:*:*"),
   270  		},
   271  		{
   272  			Class:    "postgresql-binary",
   273  			FileGlob: "**/postgres",
   274  			EvidenceMatcher: FileContentsVersionMatcher(
   275  				// [NUL]PostgreSQL 15beta4
   276  				// [NUL]PostgreSQL 15.1
   277  				// [NUL]PostgreSQL 9.6.24
   278  				// ?PostgreSQL 9.5alpha1
   279  				`(?m)(\x00|\?)PostgreSQL (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
   280  			Package: "postgresql",
   281  			PURL:    mustPURL("pkg:generic/postgresql@version"),
   282  			CPEs:    singleCPE("cpe:2.3:a:postgresql:postgresql:*:*:*:*:*:*:*:*"),
   283  		},
   284  		{
   285  			Class:    "mysql-binary",
   286  			FileGlob: "**/mysql",
   287  			EvidenceMatcher: FileContentsVersionMatcher(
   288  				`(?m).*/mysql-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
   289  			Package: "mysql",
   290  			PURL:    mustPURL("pkg:generic/mysql@version"),
   291  			CPEs:    singleCPE("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*"),
   292  		},
   293  		{
   294  			Class:    "mysql-binary",
   295  			FileGlob: "**/mysql",
   296  			EvidenceMatcher: FileContentsVersionMatcher(
   297  				`(?m).*/percona-server-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
   298  			Package: "percona-server",
   299  			PURL:    mustPURL("pkg:generic/percona-server@version"),
   300  			CPEs: []cpe.CPE{
   301  				cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
   302  				cpe.Must("cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
   303  			},
   304  		},
   305  		{
   306  			Class:    "mysql-binary",
   307  			FileGlob: "**/mysql",
   308  			EvidenceMatcher: FileContentsVersionMatcher(
   309  				`(?m).*/Percona-XtraDB-Cluster-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
   310  			Package: "percona-xtradb-cluster",
   311  			PURL:    mustPURL("pkg:generic/percona-xtradb-cluster@version"),
   312  			CPEs: []cpe.CPE{
   313  				cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
   314  				cpe.Must("cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
   315  				cpe.Must("cpe:2.3:a:percona:xtradb_cluster:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
   316  			},
   317  		},
   318  		{
   319  			Class:    "xtrabackup-binary",
   320  			FileGlob: "**/xtrabackup",
   321  			EvidenceMatcher: FileContentsVersionMatcher(
   322  				`(?m).*/percona-xtrabackup-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
   323  			Package: "percona-xtrabackup",
   324  			PURL:    mustPURL("pkg:generic/percona-xtrabackup@version"),
   325  			CPEs:    singleCPE("cpe:2.3:a:percona:xtrabackup:*:*:*:*:*:*:*:*"),
   326  		},
   327  		{
   328  			Class:    "mariadb-binary",
   329  			FileGlob: "**/mariadb",
   330  			EvidenceMatcher: FileContentsVersionMatcher(
   331  				// 10.6.15-MariaDB
   332  				`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
   333  			Package: "mariadb",
   334  			PURL:    mustPURL("pkg:generic/mariadb@version"),
   335  		},
   336  		{
   337  			Class:    "rust-standard-library-linux",
   338  			FileGlob: "**/libstd-????????????????.so",
   339  			EvidenceMatcher: FileContentsVersionMatcher(
   340  				// clang LLVM (rustc version 1.48.0 (7eac88abb 2020-11-16))
   341  				`(?m)(\x00)clang LLVM \(rustc version (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
   342  			Package: "rust",
   343  			PURL:    mustPURL("pkg:generic/rust@version"),
   344  			CPEs:    singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
   345  		},
   346  		{
   347  			Class:    "rust-standard-library-macos",
   348  			FileGlob: "**/libstd-????????????????.dylib",
   349  			EvidenceMatcher: FileContentsVersionMatcher(
   350  				// c 1.48.0 (7eac88abb 2020-11-16)
   351  				`(?m)c (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
   352  			Package: "rust",
   353  			PURL:    mustPURL("pkg:generic/rust@version"),
   354  			CPEs:    singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
   355  		},
   356  		{
   357  			Class:    "ruby-binary",
   358  			FileGlob: "**/ruby",
   359  			EvidenceMatcher: evidenceMatchers(
   360  				rubyMatcher,
   361  				sharedLibraryLookup(
   362  					// try to find version information from libruby shared libraries
   363  					`^libruby\.so.*$`,
   364  					rubyMatcher),
   365  			),
   366  			Package: "ruby",
   367  			PURL:    mustPURL("pkg:generic/ruby@version"),
   368  			CPEs:    singleCPE("cpe:2.3:a:ruby-lang:ruby:*:*:*:*:*:*:*:*"),
   369  		},
   370  		{
   371  			Class:    "erlang-binary",
   372  			FileGlob: "**/erlexec",
   373  			EvidenceMatcher: evidenceMatchers(
   374  				FileContentsVersionMatcher(
   375  					// <artificial>[NUL]/usr/src/otp_src_25.3.2.6/erts/
   376  					`(?m)/src/otp_src_(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
   377  				),
   378  				FileContentsVersionMatcher(
   379  					// <artificial>[NUL]/usr/local/src/otp-25.3.2.7/erts/
   380  					`(?m)/usr/local/src/otp-(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
   381  				),
   382  			),
   383  			Package: "erlang",
   384  			PURL:    mustPURL("pkg:generic/erlang@version"),
   385  			CPEs:    singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
   386  		},
   387  		{
   388  			Class:    "erlang-library",
   389  			FileGlob: "**/liberts_internal.a",
   390  			EvidenceMatcher: evidenceMatchers(
   391  				FileContentsVersionMatcher(
   392  					// <artificial>[NUL]/usr/src/otp_src_25.3.2.6/erts/
   393  					`(?m)/src/otp_src_(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
   394  				),
   395  				FileContentsVersionMatcher(
   396  					// <artificial>[NUL]/usr/local/src/otp-25.3.2.7/erts/
   397  					`(?m)/usr/local/src/otp-(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
   398  				),
   399  			),
   400  			Package: "erlang",
   401  			PURL:    mustPURL("pkg:generic/erlang@version"),
   402  			CPEs:    singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
   403  		},
   404  		{
   405  			Class:    "consul-binary",
   406  			FileGlob: "**/consul",
   407  			EvidenceMatcher: FileContentsVersionMatcher(
   408  				// NOTE: This is brittle and may not work for past or future versions
   409  				`CONSUL_VERSION: (?P<version>\d+\.\d+\.\d+)`,
   410  			),
   411  			Package: "consul",
   412  			PURL:    mustPURL("pkg:golang/github.com/hashicorp/consul@version"),
   413  			CPEs:    singleCPE("cpe:2.3:a:hashicorp:consul:*:*:*:*:*:*:*:*"),
   414  		},
   415  		{
   416  			Class:    "nginx-binary",
   417  			FileGlob: "**/nginx",
   418  			EvidenceMatcher: FileContentsVersionMatcher(
   419  				// [NUL]nginx version: nginx/1.25.1 - fetches '1.25.1'
   420  				// [NUL]nginx version: openresty/1.21.4.1 - fetches '1.21.4' as this is the nginx version part
   421  				`(?m)(\x00|\?)nginx version: [^\/]+\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:\+\d+)?(?:-\d+)?)`,
   422  			),
   423  			Package: "nginx",
   424  			PURL:    mustPURL("pkg:generic/nginx@version"),
   425  			CPEs: []cpe.CPE{
   426  				cpe.Must("cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
   427  				cpe.Must("cpe:2.3:a:nginx:nginx:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
   428  			},
   429  		},
   430  		{
   431  			Class:    "bash-binary",
   432  			FileGlob: "**/bash",
   433  			EvidenceMatcher: FileContentsVersionMatcher(
   434  				// @(#)Bash version 5.2.15(1) release GNU
   435  				// @(#)Bash version 5.2.0(1) alpha GNU
   436  				// @(#)Bash version 5.2.0(1) beta GNU
   437  				// @(#)Bash version 5.2.0(1) rc4 GNU
   438  				`(?m)@\(#\)Bash version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)\([0-9]\) [a-z0-9]+ GNU`,
   439  			),
   440  			Package: "bash",
   441  			PURL:    mustPURL("pkg:generic/bash@version"),
   442  			CPEs:    singleCPE("cpe:2.3:a:gnu:bash:*:*:*:*:*:*:*:*"),
   443  		},
   444  		{
   445  			Class:    "openssl-binary",
   446  			FileGlob: "**/openssl",
   447  			EvidenceMatcher: FileContentsVersionMatcher(
   448  				// [NUL]OpenSSL 3.1.4'
   449  				// [NUL]OpenSSL 1.1.1w'
   450  				`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+([a-z]|-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
   451  			),
   452  			Package: "openssl",
   453  			PURL:    mustPURL("pkg:generic/openssl@version"),
   454  			CPEs:    singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*"),
   455  		},
   456  		{
   457  			Class:    "gcc-binary",
   458  			FileGlob: "**/gcc",
   459  			EvidenceMatcher: FileContentsVersionMatcher(
   460  				// GCC: \(GNU\)  12.3.0'
   461  				`GCC: \(GNU\) (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
   462  			),
   463  			Package: "gcc",
   464  			PURL:    mustPURL("pkg:generic/gcc@version"),
   465  			CPEs:    singleCPE("cpe:2.3:a:gnu:gcc:*:*:*:*:*:*:*:*"),
   466  		},
   467  		{
   468  			Class:    "wordpress-cli-binary",
   469  			FileGlob: "**/wp",
   470  			EvidenceMatcher: FileContentsVersionMatcher(
   471  				// wp-cli/wp-cli 2.9.0'
   472  				`(?m)wp-cli/wp-cli (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
   473  			),
   474  			Package: "wp-cli",
   475  			PURL:    mustPURL("pkg:generic/wp-cli@version"),
   476  			CPEs:    singleCPE("cpe:2.3:a:wp-cli:wp-cli:*:*:*:*:*:*:*:*"),
   477  		},
   478  	}
   479  }
   480  
   481  // in both binaries and shared libraries, the version pattern is [NUL]3.11.2[NUL]
   482  var pythonVersionTemplate = `(?m)\x00(?P<version>{{ .version }}[-._a-zA-Z0-9]*)\x00`
   483  
   484  var libpythonMatcher = fileNameTemplateVersionMatcher(
   485  	`(?:.*/|^)libpython(?P<version>[0-9]+(?:\.[0-9]+)+)[a-z]?\.so.*$`,
   486  	pythonVersionTemplate,
   487  )
   488  
   489  var rubyMatcher = FileContentsVersionMatcher(
   490  	// ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
   491  	// ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
   492  	`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+(p[0-9]+)?) `)