Skip to content

Commit daa1b3f

Browse files
committed
✨ Detect binaries
Signed-off-by: Christian Zunker <christian@mondoo.com>
1 parent 4af8746 commit daa1b3f

6 files changed

Lines changed: 193 additions & 6 deletions

File tree

.vscode/launch.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,13 @@
4444
"cwd": "${workspaceRoot}/",
4545
"args": [
4646
"run",
47-
// "local",
47+
"container",
48+
"image",
49+
// "fluent/fluent-bit:3.0.0",
50+
"prom/alertmanager:v0.27.0",
4851
"-c",
49-
"asset.eol"
52+
"packages",
53+
"--log-level=debug"
5054
],
5155
},
5256
{

providers.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# review a PR where someone changed the `os` provider,
1111
# you'll want to set it as builtin for fast testing.
1212
# In this case it will be pulled from your local repo
13-
# instead of using the pre-installed provider in
13+
# instead of using the pre-installed provider in
1414
# your OS (like ~/.mondoo/providers/os).
1515
#
1616
# If you want to test a provider that is not part of
@@ -19,4 +19,4 @@
1919
# - name: my-awesome-provider
2020
# remote: ~/path/to/my-awesome-provider
2121
# goPackage: github.com/me/my-awesome-provider
22-
builtin: []
22+
builtin: ["os"]

providers/builtin_dev.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@ import (
1010
_ "embed"
1111
// osconf "go.mondoo.com/cnquery/v12/providers/os/config"
1212
// os "go.mondoo.com/cnquery/v12/providers/os/provider"
13+
networkconf "go.mondoo.com/cnquery/v12/providers/network/config"
14+
network "go.mondoo.com/cnquery/v12/providers/network/provider"
15+
osconf "go.mondoo.com/cnquery/v12/providers/os/config"
16+
os "go.mondoo.com/cnquery/v12/providers/os/provider"
1317
)
1418

1519
// //go:embed os/resources/os.resources.json
1620
// var osInfo []byte
1721

22+
//go:embed os.resources.json
23+
var osInfo []byte
24+
25+
//go:embed network.resources.json
26+
var networkInfo []byte
27+
1828
func init() {
1929
// builtinProviders[osconf.Config.ID] = &builtinProvider{
2030
// Runtime: &RunningProvider{
@@ -27,4 +37,26 @@ func init() {
2737
// Config: &osconf.Config,
2838
// }
2939

40+
builtinProviders[osconf.Config.ID] = &builtinProvider{
41+
Runtime: &RunningProvider{
42+
Name: osconf.Config.Name,
43+
ID: osconf.Config.ID,
44+
Plugin: os.Init(),
45+
Schema: MustLoadSchema("os", osInfo),
46+
isClosed: false,
47+
},
48+
Config: &osconf.Config,
49+
}
50+
51+
builtinProviders[networkconf.Config.ID] = &builtinProvider{
52+
Runtime: &RunningProvider{
53+
Name: networkconf.Config.Name,
54+
ID: networkconf.Config.ID,
55+
Plugin: network.Init(),
56+
Schema: MustLoadSchema("network", networkInfo),
57+
isClosed: false,
58+
},
59+
Config: &networkconf.Config,
60+
}
61+
3062
}

providers/os/connection/tar/file.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func (f *File) Read(b []byte) (n int, err error) {
4747
}
4848

4949
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
50+
// FIXME: implement this to get aroudn the creation of a temp file for binaries
5051
return 0, errors.New("not implemented yet")
5152
}
5253

providers/os/connection/tar/fs.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,10 @@ func (fs *FS) Find(from string, r *regexp.Regexp, typ string, perm *uint32, dept
209209
log.Trace().Str("path", k).Str("from", from).Str("prefix", from).Bool("prefix", p).Bool("m", m).Msg("check if matches")
210210
if p && m {
211211
entry := fs.FileMap[k]
212+
// FIXME: Add check for permissions
212213
if (typ == "directory" && entry.Typeflag == tar.TypeDir) || (typ == "file" && entry.Typeflag == tar.TypeReg) || typ == "" {
213214
list = append(list, k)
214-
log.Debug().Msg("matches")
215+
// log.Debug().Msg("matches")
215216
continue
216217
}
217218
}

providers/os/resources/packages.go

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,30 @@
44
package resources
55

66
import (
7+
"debug/elf"
78
"errors"
9+
"io"
10+
"os"
11+
"path/filepath"
812
"regexp"
13+
"strings"
914
"sync"
1015

1116
"github.com/rs/zerolog/log"
1217
"go.mondoo.com/cnquery/v12/llx"
1318
"go.mondoo.com/cnquery/v12/providers-sdk/v1/plugin"
1419
"go.mondoo.com/cnquery/v12/providers/os/connection/shared"
20+
"go.mondoo.com/cnquery/v12/providers/os/connection/tar"
1521
"go.mondoo.com/cnquery/v12/providers/os/resources/packages"
1622
"go.mondoo.com/cnquery/v12/types"
1723
"go.mondoo.com/cnquery/v12/utils/multierr"
1824
)
1925

20-
var PKG_IDENTIFIER = regexp.MustCompile(`^(.*):\/\/(.*)\/(.*)\/(.*)$`)
26+
var (
27+
PKG_IDENTIFIER = regexp.MustCompile(`^(.*):\/\/(.*)\/(.*)\/(.*)$`)
28+
// semverRegEx = regexp.MustCompile(`[^\d\w](v?\d+\.\d+\.\d+)[^\d\w]`)
29+
semverRegEx = regexp.MustCompile(`\x{0000}((:?[\w]+/)?v?\d+\.\d+\.\d+)\x{0000}`)
30+
)
2131

2232
// A system package cannot be installed twice but there are edge cases:
2333
// - the same package name could be installed for multiple archs
@@ -139,11 +149,148 @@ type mqlPackagesInternal struct {
139149
packagesByName map[string]*mqlPackage
140150
}
141151

152+
type elfBinaryPackageNotes struct {
153+
Name string `json:"name"`
154+
Version string `json:"version"`
155+
PURL string `json:"purl"`
156+
CPE string `json:"cpe"`
157+
License string `json:"license"`
158+
}
159+
142160
func (x *mqlPackages) list() ([]any, error) {
143161
x.lock.Lock()
144162
defer x.lock.Unlock()
145163

146164
conn := x.MqlRuntime.Connection.(shared.Connection)
165+
166+
binaryPkgs := []packages.Package{}
167+
tarConn := conn.(*tar.Connection)
168+
tarFs := tarConn.FileSystem().(*tar.FS)
169+
perm := uint32(0o777)
170+
depth := int(1)
171+
pathsToScan := []string{
172+
"/fluent-bit/bin",
173+
"/bin",
174+
"/sbin",
175+
"/usr/bin",
176+
"/usr/sbin",
177+
"/usr/local/bin",
178+
"/usr/local/sbin",
179+
"/",
180+
}
181+
supportedFiles := []string{
182+
"fluent-bit",
183+
"gitlab-runner",
184+
"nginx",
185+
}
186+
for _, path := range pathsToScan {
187+
files, err := tarFs.Find(path, nil, "file", &perm, &depth)
188+
if err != nil {
189+
return nil, err
190+
}
191+
192+
for _, file := range files {
193+
log.Debug().Str("file", file).Msg("found file")
194+
// TODO: we need a reject list here, well known OS binaries are not interesting
195+
supported := false
196+
for _, supportedFile := range supportedFiles {
197+
if strings.HasSuffix(file, supportedFile) {
198+
supported = true
199+
}
200+
}
201+
if !supported {
202+
continue
203+
}
204+
f, err := tarFs.Open(file)
205+
if err != nil {
206+
return nil, err
207+
}
208+
defer f.Close()
209+
210+
tmpFile, err := os.CreateTemp("", "mondoo-binary-package-notes-")
211+
if err != nil {
212+
log.Error().Err(err).Msg("could not copy file to temp file")
213+
continue
214+
}
215+
defer os.Remove(tmpFile.Name())
216+
217+
_, err = io.Copy(tmpFile, f)
218+
if err != nil {
219+
log.Error().Err(err).Msg("could not create temp file")
220+
continue
221+
}
222+
tmpFile.Close()
223+
f, err = os.Open(tmpFile.Name())
224+
if err != nil {
225+
log.Error().Err(err).Msg("could not open temp file")
226+
continue
227+
}
228+
defer f.Close()
229+
230+
// chunk := make([]byte, 1024*1024*3)
231+
// start := int64(0)
232+
// for {
233+
// numBytes, err := f.ReadAt(chunk, start)
234+
// if err != nil {
235+
// log.Error().Err(err).Msg("could not read first block")
236+
// break
237+
// }
238+
// m := semverRegEx.FindAllStringSubmatch(string(chunk), -1)
239+
// log.Debug().Interface("versions", m).Msg("found version")
240+
// if numBytes < 1024*1024*3 {
241+
// break
242+
// }
243+
244+
// start += int64(len(chunk) - 100)
245+
// }
246+
247+
elfBinary, err := elf.NewFile(f)
248+
if err != nil {
249+
log.Error().Err(err).Msg("could not read elf file")
250+
continue
251+
}
252+
for _, section := range elfBinary.Sections {
253+
// if section.Name == ".text" || section.Name == ".dynstr" || section.Name == ".tbss" || section.Name == ".bss" {
254+
if section.Name != ".rodata" {
255+
continue
256+
}
257+
notes, err := section.Data()
258+
if err != nil {
259+
log.Error().Err(err).Msg("could not read section data")
260+
continue
261+
}
262+
log.Debug().Interface("section", section.Name).Msg("found section")
263+
notesStr := string(notes)
264+
m := semverRegEx.FindStringSubmatch(notesStr)
265+
if len(m) > 0 {
266+
log.Debug().Str("version", m[1]).Msg("found version")
267+
pkgName := filepath.Base(file)
268+
binPkg := packages.Package{
269+
Name: pkgName,
270+
Version: m[1],
271+
Arch: "amd64",
272+
Format: "binary",
273+
PUrl: "pkg:binary/" + pkgName,
274+
}
275+
binaryPkgs = append(binaryPkgs, binPkg)
276+
log.Debug().Str("package", binPkg.Name).Str("version", binPkg.Version).Str("arch", binPkg.Arch).Str("format", binPkg.Format).Str("purl", binPkg.PUrl).Msg("found binary package")
277+
}
278+
279+
}
280+
281+
// var metadata elfBinaryPackageNotes
282+
// if err := json.Unmarshal(notes, &metadata); err == nil {
283+
// binaryPkgs = append(binaryPkgs, packages.Package{
284+
// Name: metadata.Name,
285+
// Version: metadata.Version,
286+
// Arch: "amd64",
287+
// Format: "binary",
288+
// PUrl: metadata.PURL,
289+
// })
290+
// }
291+
}
292+
}
293+
147294
pms, err := packages.ResolveSystemPkgManagers(conn)
148295
if len(pms) == 0 || err != nil {
149296
return nil, errors.New("could not detect suitable package manager for platform")
@@ -178,6 +325,8 @@ func (x *mqlPackages) list() ([]any, error) {
178325
availableMap[a.Name+"/"+a.Arch] = a
179326
}
180327

328+
osPkgs = append(osPkgs, binaryPkgs...)
329+
181330
// create MQL package os for each package
182331
pkgs := make([]any, len(osPkgs))
183332
for i, osPkg := range osPkgs {

0 commit comments

Comments
 (0)