From 7073008f4b80fa72990597f1d341a8692c7bf004 Mon Sep 17 00:00:00 2001 From: MaineK00n Date: Wed, 15 Mar 2023 07:59:25 +0900 Subject: [PATCH] feat(contrib): add snmp2cpe --- .gitignore | 2 + .goreleaser.yml | 34 +++ GNUmakefile | 4 + contrib/Dockerfile | 5 +- contrib/snmp2cpe/README.md | 50 ++++ contrib/snmp2cpe/cmd/main.go | 15 ++ contrib/snmp2cpe/pkg/cmd/convert/convert.go | 52 +++++ contrib/snmp2cpe/pkg/cmd/root/root.go | 30 +++ contrib/snmp2cpe/pkg/cmd/v1/v1.go | 47 ++++ contrib/snmp2cpe/pkg/cmd/v2c/v2c.go | 47 ++++ contrib/snmp2cpe/pkg/cmd/v3/v3.go | 39 ++++ contrib/snmp2cpe/pkg/cmd/version/version.go | 23 ++ contrib/snmp2cpe/pkg/cpe/cpe.go | 212 +++++++++++++++++ contrib/snmp2cpe/pkg/cpe/cpe_test.go | 244 ++++++++++++++++++++ contrib/snmp2cpe/pkg/snmp/snmp.go | 131 +++++++++++ contrib/snmp2cpe/pkg/snmp/types.go | 14 ++ contrib/snmp2cpe/pkg/util/util.go | 12 + go.mod | 3 +- go.sum | 2 + 19 files changed, 963 insertions(+), 3 deletions(-) create mode 100644 contrib/snmp2cpe/README.md create mode 100644 contrib/snmp2cpe/cmd/main.go create mode 100644 contrib/snmp2cpe/pkg/cmd/convert/convert.go create mode 100644 contrib/snmp2cpe/pkg/cmd/root/root.go create mode 100644 contrib/snmp2cpe/pkg/cmd/v1/v1.go create mode 100644 contrib/snmp2cpe/pkg/cmd/v2c/v2c.go create mode 100644 contrib/snmp2cpe/pkg/cmd/v3/v3.go create mode 100644 contrib/snmp2cpe/pkg/cmd/version/version.go create mode 100644 contrib/snmp2cpe/pkg/cpe/cpe.go create mode 100644 contrib/snmp2cpe/pkg/cpe/cpe_test.go create mode 100644 contrib/snmp2cpe/pkg/snmp/snmp.go create mode 100644 contrib/snmp2cpe/pkg/snmp/types.go create mode 100644 contrib/snmp2cpe/pkg/util/util.go diff --git a/.gitignore b/.gitignore index 38684da871..7e7b73254f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ vuls !cmd/vuls future-vuls trivy-to-vuls +snmp2cpe +!snmp2cpe/ \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml index 91bdf2d9d8..e789f2f309 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -68,6 +68,8 @@ builds: tags: - scanner main: ./contrib/trivy/cmd/main.go + ldflags: + - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }} binary: trivy-to-vuls - id: future-vuls @@ -84,9 +86,30 @@ builds: - -a tags: - scanner + ldflags: + - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }} main: ./contrib/future-vuls/cmd/main.go binary: future-vuls +- id: snmp2cpe + env: + - CGO_ENABLED=0 + goos: + - linux + goarch: + - 386 + - amd64 + - arm + - arm64 + flags: + - -a + tags: + - scanner + ldflags: + - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }} + main: ./contrib/snmp2cpe/cmd/main.go + binary: snmp2cpe + archives: - id: vuls @@ -129,5 +152,16 @@ archives: - LICENSE - README* - CHANGELOG.md + +- id: snmp2cpe + name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' + builds: + - snmp2cpe + format: tar.gz + files: + - LICENSE + - README* + - CHANGELOG.md + snapshot: name_template: SNAPSHOT-{{ .Commit }} diff --git a/GNUmakefile b/GNUmakefile index 04ebf65eae..1f96adecb9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -81,6 +81,10 @@ build-trivy-to-vuls: ./contrib/trivy/cmd/main.go build-future-vuls: ./contrib/future-vuls/cmd/main.go $(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls ./contrib/future-vuls/cmd +# snmp2cpe +build-snmp2cpe: ./contrib/snmp2cpe/cmd/main.go + $(GO) build -a -ldflags "$(LDFLAGS)" -o snmp2cpe ./contrib/snmp2cpe/cmd + # integration-test BASE_DIR := '${PWD}/integration/results' # $(shell mkdir -p ${BASE_DIR}) diff --git a/contrib/Dockerfile b/contrib/Dockerfile index 667c8d208e..e29b980db4 100644 --- a/contrib/Dockerfile +++ b/contrib/Dockerfile @@ -11,7 +11,8 @@ COPY . $GOPATH/src/$REPOSITORY RUN cd $GOPATH/src/$REPOSITORY && \ make build-scanner && mv vuls $GOPATH/bin && \ make build-trivy-to-vuls && mv trivy-to-vuls $GOPATH/bin && \ - make build-future-vuls && mv future-vuls $GOPATH/bin + make build-future-vuls && mv future-vuls $GOPATH/bin && \ + make build-snmp2cpe && mv snmp2cpe $GOPATH/bin FROM alpine:3.15 @@ -25,7 +26,7 @@ RUN apk add --no-cache \ nmap \ && mkdir -p $WORKDIR $LOGDIR -COPY --from=builder /go/bin/vuls /go/bin/trivy-to-vuls /go/bin/future-vuls /usr/local/bin/ +COPY --from=builder /go/bin/vuls /go/bin/trivy-to-vuls /go/bin/future-vuls /go/bin/snmp2cpe /usr/local/bin/ COPY --from=aquasec/trivy:latest /usr/local/bin/trivy /usr/local/bin/trivy VOLUME ["$WORKDIR", "$LOGDIR"] diff --git a/contrib/snmp2cpe/README.md b/contrib/snmp2cpe/README.md new file mode 100644 index 0000000000..8a80478410 --- /dev/null +++ b/contrib/snmp2cpe/README.md @@ -0,0 +1,50 @@ +# snmp2cpe + +## Main Features + +- Estimate hardware and OS CPE from SNMP reply of network devices + +## Installation + +```console +$ git clone https://github.com/future-architect/vuls.git +$ make build-snmp2cpe +``` + +## Command Reference + +```console +$ snmp2cpe help +snmp2cpe: SNMP reply To CPE + +Usage: + snmp2cpe [command] + +Available Commands: + completion Generate the autocompletion script for the specified shell + convert snmpget reply to CPE + help Help about any command + v1 snmpget with SNMPv1 + v2c snmpget with SNMPv2c + v3 snmpget with SNMPv3 + version Print the version + +Flags: + -h, --help help for snmp2cpe + +Use "snmp2cpe [command] --help" for more information about a command. +``` + +## Usage + +```console +$ snmp2cpe v2c --debug 192.168.1.99 public +2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.1.1.0 -> +2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.47.1.1.1.1.12.1 -> Fortinet +2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.47.1.1.1.1.7.1 -> FGT_50E +2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.47.1.1.1.1.10.1 -> FortiGate-50E v5.4.6,build1165b1165,171018 (GA) +{"192.168.1.99":{"entPhysicalTables":{"1":{"entPhysicalMfgName":"Fortinet","entPhysicalName":"FGT_50E","entPhysicalSoftwareRev":"FortiGate-50E v5.4.6,build1165b1165,171018 (GA)"}}}} + +$ snmp2cpe v2c 192.168.1.99 public | snmp2cpe convert +{"192.168.1.99":["cpe:2.3:h:fortinet:fortigate-50e:-:*:*:*:*:*:*:*","cpe:2.3:o:fortinet:fortios:5.4.6:*:*:*:*:*:*:*"]} +``` diff --git a/contrib/snmp2cpe/cmd/main.go b/contrib/snmp2cpe/cmd/main.go new file mode 100644 index 0000000000..2189a9c014 --- /dev/null +++ b/contrib/snmp2cpe/cmd/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "os" + + rootCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/root" +) + +func main() { + if err := rootCmd.NewCmdRoot().Execute(); err != nil { + fmt.Fprintf(os.Stderr, "failed to exec snmp2cpe: %s\n", fmt.Sprintf("%+v", err)) + os.Exit(1) + } +} diff --git a/contrib/snmp2cpe/pkg/cmd/convert/convert.go b/contrib/snmp2cpe/pkg/cmd/convert/convert.go new file mode 100644 index 0000000000..eb1c324fde --- /dev/null +++ b/contrib/snmp2cpe/pkg/cmd/convert/convert.go @@ -0,0 +1,52 @@ +package convert + +import ( + "encoding/json" + "os" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cpe" + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp" +) + +// NewCmdConvert ... +func NewCmdConvert() *cobra.Command { + cmd := &cobra.Command{ + Use: "convert", + Short: "snmpget reply to CPE", + Args: cobra.MaximumNArgs(1), + Example: `$ snmp2cpe v2c 192.168.11.11 public | snmp2cpe convert +$ snmp2cpe v2c 192.168.11.11 public | snmp2cpe convert - +$ snmp2cpe v2c 192.168.11.11 public > v2c.json && snmp2cpe convert v2c.json`, + RunE: func(_ *cobra.Command, args []string) error { + r := os.Stdin + if len(args) == 1 && args[0] != "-" { + f, err := os.Open(args[0]) + if err != nil { + return errors.Wrapf(err, "failed to open %s", args[0]) + } + defer f.Close() + r = f + } + + var reply map[string]snmp.Result + if err := json.NewDecoder(r).Decode(&reply); err != nil { + return errors.Wrap(err, "failed to decode") + } + + converted := map[string][]string{} + for ipaddr, res := range reply { + converted[ipaddr] = cpe.Convert(res) + } + + if err := json.NewEncoder(os.Stdout).Encode(converted); err != nil { + return errors.Wrap(err, "failed to encode") + } + + return nil + }, + } + return cmd +} diff --git a/contrib/snmp2cpe/pkg/cmd/root/root.go b/contrib/snmp2cpe/pkg/cmd/root/root.go new file mode 100644 index 0000000000..89c0f882a9 --- /dev/null +++ b/contrib/snmp2cpe/pkg/cmd/root/root.go @@ -0,0 +1,30 @@ +package root + +import ( + "github.com/spf13/cobra" + + convertCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/convert" + v1Cmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/v1" + v2cCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/v2c" + v3Cmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/v3" + versionCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/version" +) + +// NewCmdRoot ... +func NewCmdRoot() *cobra.Command { + cmd := &cobra.Command{ + Use: "snmp2cpe ", + Short: "snmp2cpe", + Long: "snmp2cpe: SNMP reply To CPE", + SilenceErrors: true, + SilenceUsage: true, + } + + cmd.AddCommand(v1Cmd.NewCmdV1()) + cmd.AddCommand(v2cCmd.NewCmdV2c()) + cmd.AddCommand(v3Cmd.NewCmdV3()) + cmd.AddCommand(convertCmd.NewCmdConvert()) + cmd.AddCommand(versionCmd.NewCmdVersion()) + + return cmd +} diff --git a/contrib/snmp2cpe/pkg/cmd/v1/v1.go b/contrib/snmp2cpe/pkg/cmd/v1/v1.go new file mode 100644 index 0000000000..6f791b9f2e --- /dev/null +++ b/contrib/snmp2cpe/pkg/cmd/v1/v1.go @@ -0,0 +1,47 @@ +package v1 + +import ( + "encoding/json" + "os" + + "github.com/gosnmp/gosnmp" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp" +) + +// SNMPv1Options ... +type SNMPv1Options struct { + Debug bool +} + +// NewCmdV1 ... +func NewCmdV1() *cobra.Command { + opts := &SNMPv1Options{ + Debug: false, + } + + cmd := &cobra.Command{ + Use: "v1 ", + Short: "snmpget with SNMPv1", + Example: "$ snmp2cpe v1 192.168.100.1 public", + Args: cobra.ExactArgs(2), + RunE: func(_ *cobra.Command, args []string) error { + r, err := snmp.Get(gosnmp.Version1, args[0], snmp.WithCommunity(args[1]), snmp.WithDebug(opts.Debug)) + if err != nil { + return errors.Wrap(err, "failed to snmpget") + } + + if err := json.NewEncoder(os.Stdout).Encode(map[string]snmp.Result{args[0]: r}); err != nil { + return errors.Wrap(err, "failed to encode") + } + + return nil + }, + } + + cmd.Flags().BoolVarP(&opts.Debug, "debug", "", false, "debug mode") + + return cmd +} diff --git a/contrib/snmp2cpe/pkg/cmd/v2c/v2c.go b/contrib/snmp2cpe/pkg/cmd/v2c/v2c.go new file mode 100644 index 0000000000..ec94b7defb --- /dev/null +++ b/contrib/snmp2cpe/pkg/cmd/v2c/v2c.go @@ -0,0 +1,47 @@ +package v2c + +import ( + "encoding/json" + "os" + + "github.com/gosnmp/gosnmp" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp" +) + +// SNMPv2cOptions ... +type SNMPv2cOptions struct { + Debug bool +} + +// NewCmdV2c ... +func NewCmdV2c() *cobra.Command { + opts := &SNMPv2cOptions{ + Debug: false, + } + + cmd := &cobra.Command{ + Use: "v2c ", + Short: "snmpget with SNMPv2c", + Example: "$ snmp2cpe v2c 192.168.100.1 public", + Args: cobra.ExactArgs(2), + RunE: func(_ *cobra.Command, args []string) error { + r, err := snmp.Get(gosnmp.Version2c, args[0], snmp.WithCommunity(args[1]), snmp.WithDebug(opts.Debug)) + if err != nil { + return errors.Wrap(err, "failed to snmpget") + } + + if err := json.NewEncoder(os.Stdout).Encode(map[string]snmp.Result{args[0]: r}); err != nil { + return errors.Wrap(err, "failed to encode") + } + + return nil + }, + } + + cmd.Flags().BoolVarP(&opts.Debug, "debug", "", false, "debug mode") + + return cmd +} diff --git a/contrib/snmp2cpe/pkg/cmd/v3/v3.go b/contrib/snmp2cpe/pkg/cmd/v3/v3.go new file mode 100644 index 0000000000..3dd0a540c9 --- /dev/null +++ b/contrib/snmp2cpe/pkg/cmd/v3/v3.go @@ -0,0 +1,39 @@ +package v3 + +import ( + "github.com/gosnmp/gosnmp" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp" +) + +// SNMPv3Options ... +type SNMPv3Options struct { + Debug bool +} + +// NewCmdV3 ... +func NewCmdV3() *cobra.Command { + opts := &SNMPv3Options{ + Debug: false, + } + + cmd := &cobra.Command{ + Use: "v3 ", + Short: "snmpget with SNMPv3", + Example: "$ snmp2cpe v3", + RunE: func(_ *cobra.Command, _ []string) error { + _, err := snmp.Get(gosnmp.Version3, "", snmp.WithDebug(opts.Debug)) + if err != nil { + return errors.Wrap(err, "failed to snmpget") + } + + return nil + }, + } + + cmd.Flags().BoolVarP(&opts.Debug, "debug", "", false, "debug mode") + + return cmd +} diff --git a/contrib/snmp2cpe/pkg/cmd/version/version.go b/contrib/snmp2cpe/pkg/cmd/version/version.go new file mode 100644 index 0000000000..26b1fd2691 --- /dev/null +++ b/contrib/snmp2cpe/pkg/cmd/version/version.go @@ -0,0 +1,23 @@ +package version + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/future-architect/vuls/config" +) + +// NewCmdVersion ... +func NewCmdVersion() *cobra.Command { + cmd := &cobra.Command{ + Use: "version", + Short: "Print the version", + Args: cobra.NoArgs, + Run: func(_ *cobra.Command, _ []string) { + fmt.Fprintf(os.Stdout, "snmp2cpe %s %s\n", config.Version, config.Revision) + }, + } + return cmd +} diff --git a/contrib/snmp2cpe/pkg/cpe/cpe.go b/contrib/snmp2cpe/pkg/cpe/cpe.go new file mode 100644 index 0000000000..0454162300 --- /dev/null +++ b/contrib/snmp2cpe/pkg/cpe/cpe.go @@ -0,0 +1,212 @@ +package cpe + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-version" + + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp" + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/util" +) + +// Convert ... +func Convert(result snmp.Result) []string { + var cpes []string + + switch detectVendor(result) { + case "Cisco": + var p, v string + lhs, _, _ := strings.Cut(result.SysDescr0, " RELEASE SOFTWARE") + for _, s := range strings.Split(lhs, ",") { + s = strings.TrimSpace(s) + switch { + case strings.Contains(s, "Cisco NX-OS"): + p = "nx-os" + case strings.Contains(s, "Cisco IOS Software"), strings.Contains(s, "Cisco Internetwork Operating System Software IOS"): + p = "ios" + if strings.Contains(lhs, "IOSXE") || strings.Contains(lhs, "IOS-XE") { + p = "ios_xe" + } + case strings.HasPrefix(s, "Version "): + v = strings.ToLower(strings.TrimPrefix(s, "Version ")) + } + } + if p != "" && v != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:cisco:%s:%s:*:*:*:*:*:*:*", p, v)) + } + + if t, ok := result.EntPhysicalTables[1]; ok { + if t.EntPhysicalName != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:cisco:%s:-:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalName))) + } + if p != "" && t.EntPhysicalSoftwareRev != "" { + s, _, _ := strings.Cut(t.EntPhysicalSoftwareRev, " RELEASE SOFTWARE") + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:cisco:%s:%s:*:*:*:*:*:*:*", p, strings.ToLower(strings.TrimSuffix(s, ",")))) + } + } + case "Juniper Networks": + if strings.HasPrefix(result.SysDescr0, "Juniper Networks, Inc.") { + for _, s := range strings.Split(strings.TrimPrefix(result.SysDescr0, "Juniper Networks, Inc. "), ",") { + s = strings.TrimSpace(s) + switch { + case strings.HasPrefix(s, "qfx"), strings.HasPrefix(s, "ex"), strings.HasPrefix(s, "mx"), strings.HasPrefix(s, "ptx"), strings.HasPrefix(s, "acx"), strings.HasPrefix(s, "bti"), strings.HasPrefix(s, "srx"): + cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:juniper:%s:-:*:*:*:*:*:*:*", strings.Fields(s)[0])) + case strings.HasPrefix(s, "kernel JUNOS "): + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:juniper:junos:%s:*:*:*:*:*:*:*", strings.ToLower(strings.Fields(strings.TrimPrefix(s, "kernel JUNOS "))[0]))) + } + } + + if t, ok := result.EntPhysicalTables[1]; ok { + if t.EntPhysicalSoftwareRev != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:juniper:junos:%s:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalSoftwareRev))) + } + } + } else { + h, v, ok := strings.Cut(result.SysDescr0, " version ") + if ok { + cpes = append(cpes, + fmt.Sprintf("cpe:2.3:h:juniper:%s:-:*:*:*:*:*:*:*", strings.ToLower(h)), + fmt.Sprintf("cpe:2.3:o:juniper:screenos:%s:*:*:*:*:*:*:*", strings.ToLower(strings.Fields(v)[0])), + ) + } + } + case "Arista Networks": + v, h, ok := strings.Cut(result.SysDescr0, " running on an ") + if ok { + if strings.HasPrefix(v, "Arista Networks EOS version ") { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:arista:eos:%s:*:*:*:*:*:*:*", strings.ToLower(strings.TrimPrefix(v, "Arista Networks EOS version ")))) + } + cpes = append(cpes, fmt.Sprintf("cpe:/h:arista:%s:-:*:*:*:*:*:*:*", strings.ToLower(strings.TrimPrefix(h, "Arista Networks ")))) + } + if t, ok := result.EntPhysicalTables[1]; ok { + if t.EntPhysicalSoftwareRev != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:arista:eos:%s:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalSoftwareRev))) + } + } + case "Fortinet": + if t, ok := result.EntPhysicalTables[1]; ok { + if strings.HasPrefix(t.EntPhysicalName, "FGT_") { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:fortinet:fortigate-%s:-:*:*:*:*:*:*:*", strings.ToLower(strings.TrimPrefix(t.EntPhysicalName, "FGT_")))) + } + for _, s := range strings.Fields(t.EntPhysicalSoftwareRev) { + switch { + case strings.HasPrefix(s, "FortiGate-"): + cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:fortinet:%s:-:*:*:*:*:*:*:*", strings.ToLower(s))) + case strings.HasPrefix(s, "v") && strings.Contains(s, "build"): + if v, _, found := strings.Cut(strings.TrimPrefix(s, "v"), ",build"); found { + if _, err := version.NewVersion(v); err == nil { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:fortinet:fortios:%s:*:*:*:*:*:*:*", v)) + } + } + } + } + } + case "YAMAHA": + var h, v string + for _, s := range strings.Fields(result.SysDescr0) { + switch { + case strings.HasPrefix(s, "RTX"), strings.HasPrefix(s, "NVR"), strings.HasPrefix(s, "RTV"), strings.HasPrefix(s, "RT"), + strings.HasPrefix(s, "SRT"), strings.HasPrefix(s, "FWX"), strings.HasPrefix(s, "YSL-V810"): + h = strings.ToLower(s) + case strings.HasPrefix(s, "Rev."): + if _, err := version.NewVersion(strings.TrimPrefix(s, "Rev.")); err == nil { + v = strings.TrimPrefix(s, "Rev.") + } + } + } + if h != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:yamaha:%s:-:*:*:*:*:*:*:*", h)) + if v != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:yamaha:%s:%s:*:*:*:*:*:*:*", h, v)) + } + } + case "NEC": + var h, v string + for _, s := range strings.Split(result.SysDescr0, ",") { + s = strings.TrimSpace(s) + switch { + case strings.HasPrefix(s, "IX Series "): + h = strings.ToLower(strings.TrimSuffix(strings.TrimPrefix(s, "IX Series "), " (magellan-sec) Software")) + case strings.HasPrefix(s, "Version "): + if _, err := version.NewVersion(strings.TrimSpace(strings.TrimPrefix(s, "Version "))); err == nil { + v = strings.TrimSpace(strings.TrimPrefix(s, "Version ")) + } + } + } + if h != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:nec:%s:-:*:*:*:*:*:*:*", h)) + if v != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:nec:%s:%s:*:*:*:*:*:*:*", h, v)) + } + } + case "Palo Alto Networks": + if t, ok := result.EntPhysicalTables[1]; ok { + if t.EntPhysicalName != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:paloaltonetworks:%s:-:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalName))) + } + if t.EntPhysicalSoftwareRev != "" { + cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:paloaltonetworks:pan-os:%s:*:*:*:*:*:*:*", t.EntPhysicalSoftwareRev)) + } + } + default: + return []string{} + } + + return util.Unique(cpes) +} + +func detectVendor(r snmp.Result) string { + if t, ok := r.EntPhysicalTables[1]; ok { + switch t.EntPhysicalMfgName { + case "Cisco": + return "Cisco" + case "Juniper Networks": + return "Juniper Networks" + case "Arista Networks": + return "Arista Networks" + case "Fortinet": + return "Fortinet" + case "YAMAHA": + return "YAMAHA" + case "NEC": + return "NEC" + case "Palo Alto Networks": + return "Palo Alto Networks" + } + } + + switch { + case strings.Contains(r.SysDescr0, "Cisco"): + return "Cisco" + case strings.Contains(r.SysDescr0, "Juniper Networks"), + strings.Contains(r.SysDescr0, "SSG5"), strings.Contains(r.SysDescr0, "SSG20"), strings.Contains(r.SysDescr0, "SSG140"), + strings.Contains(r.SysDescr0, "SSG320"), strings.Contains(r.SysDescr0, "SSG350"), strings.Contains(r.SysDescr0, "SSG520"), + strings.Contains(r.SysDescr0, "SSG550"): + return "Juniper Networks" + case strings.Contains(r.SysDescr0, "Arista Networks"): + return "Arista Networks" + case strings.Contains(r.SysDescr0, "Fortinet"), strings.Contains(r.SysDescr0, "FortiGate"): + return "Fortinet" + case strings.Contains(r.SysDescr0, "YAMAHA"), + strings.Contains(r.SysDescr0, "RTX810"), strings.Contains(r.SysDescr0, "RTX830"), + strings.Contains(r.SysDescr0, "RTX1000"), strings.Contains(r.SysDescr0, "RTX1100"), + strings.Contains(r.SysDescr0, "RTX1200"), strings.Contains(r.SysDescr0, "RTX1210"), strings.Contains(r.SysDescr0, "RTX1220"), + strings.Contains(r.SysDescr0, "RTX1300"), strings.Contains(r.SysDescr0, "RTX1500"), strings.Contains(r.SysDescr0, "RTX2000"), + strings.Contains(r.SysDescr0, "RTX3000"), strings.Contains(r.SysDescr0, "RTX3500"), strings.Contains(r.SysDescr0, "RTX5000"), + strings.Contains(r.SysDescr0, "NVR500"), strings.Contains(r.SysDescr0, "NVR510"), strings.Contains(r.SysDescr0, "NVR700W"), + strings.Contains(r.SysDescr0, "RTV01"), strings.Contains(r.SysDescr0, "RTV700"), + strings.Contains(r.SysDescr0, "RT105i"), strings.Contains(r.SysDescr0, "RT105p"), strings.Contains(r.SysDescr0, "RT105e"), + strings.Contains(r.SysDescr0, "RT107e"), strings.Contains(r.SysDescr0, "RT250i"), strings.Contains(r.SysDescr0, "RT300i"), + strings.Contains(r.SysDescr0, "SRT100"), + strings.Contains(r.SysDescr0, "FWX100"), + strings.Contains(r.SysDescr0, "YSL-V810"): + return "YAMAHA" + case strings.Contains(r.SysDescr0, "NEC"): + return "NEC" + case strings.Contains(r.SysDescr0, "Palo Alto Networks"): + return "Palo Alto Networks" + default: + return "" + } +} diff --git a/contrib/snmp2cpe/pkg/cpe/cpe_test.go b/contrib/snmp2cpe/pkg/cpe/cpe_test.go new file mode 100644 index 0000000000..01d061c60a --- /dev/null +++ b/contrib/snmp2cpe/pkg/cpe/cpe_test.go @@ -0,0 +1,244 @@ +package cpe_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cpe" + "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp" +) + +func TestConvert(t *testing.T) { + tests := []struct { + name string + args snmp.Result + want []string + }{ + { + name: "Cisco NX-OS Version 7.1(4)N1(1)", + args: snmp.Result{ + SysDescr0: "Cisco NX-OS(tm) n6000, Software (n6000-uk9), Version 7.1(4)N1(1), RELEASE SOFTWARE Copyright (c) 2002-2012 by Cisco Systems, Inc. Device Manager Version 6.0(2)N1(1),Compiled 9/2/2016 10:00:00", + }, + want: []string{"cpe:2.3:o:cisco:nx-os:7.1(4)n1(1):*:*:*:*:*:*:*"}, + }, + { + name: "Cisco IOS Version 15.1(4)M3", + args: snmp.Result{ + SysDescr0: `Cisco IOS Software, 2800 Software (C2800NM-ADVENTERPRISEK9-M), Version 15.1(4)M3, RELEASE SOFTWARE (fc1) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2011 by Cisco Systems, Inc. + Compiled Tue 06-Dec-11 16:21 by prod_rel_team`, + }, + want: []string{"cpe:2.3:o:cisco:ios:15.1(4)m3:*:*:*:*:*:*:*"}, + }, + { + name: "Cisco IOS Version 15.1(4)M4", + args: snmp.Result{ + SysDescr0: `Cisco IOS Software, C181X Software (C181X-ADVENTERPRISEK9-M), Version 15.1(4)M4, RELEASE SOFTWARE (fc1) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2012 by Cisco Systems, Inc. + Compiled Tue 20-Mar-12 23:34 by prod_rel_team`, + }, + want: []string{"cpe:2.3:o:cisco:ios:15.1(4)m4:*:*:*:*:*:*:*"}, + }, + { + name: "Cisco IOS Vresion 15.5(3)M on Cisco 892J-K9-V02", + args: snmp.Result{ + SysDescr0: `Cisco IOS Software, C890 Software (C890-UNIVERSALK9-M), Version 15.5(3)M, RELEASE SOFTWARE (fc1) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2015 by Cisco Systems, Inc. + Compiled Thu 23-Jul-15 03:08 by prod_rel_team`, + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Cisco", + EntPhysicalName: "892", + EntPhysicalSoftwareRev: "15.5(3)M, RELEASE SOFTWARE (fc1)", + }}, + }, + want: []string{"cpe:2.3:h:cisco:892:-:*:*:*:*:*:*:*", "cpe:2.3:o:cisco:ios:15.5(3)m:*:*:*:*:*:*:*"}, + }, + { + name: "Cisco IOS Version 15.4(3)M5 on Cisco C892FSP-K9-V02", + args: snmp.Result{ + SysDescr0: `Cisco IOS Software, C800 Software (C800-UNIVERSALK9-M), Version 15.4(3)M5, RELEASE SOFTWARE (fc1) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2016 by Cisco Systems, Inc. + Compiled Tue 09-Feb-16 06:15 by prod_rel_team`, + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Cisco", + EntPhysicalName: "C892FSP-K9", + EntPhysicalSoftwareRev: "15.4(3)M5, RELEASE SOFTWARE (fc1)", + }}, + }, + want: []string{"cpe:2.3:h:cisco:c892fsp-k9:-:*:*:*:*:*:*:*", "cpe:2.3:o:cisco:ios:15.4(3)m5:*:*:*:*:*:*:*"}, + }, + { + name: "Cisco IOS Version 12.2(17d)SXB11", + args: snmp.Result{ + SysDescr0: `Cisco Internetwork Operating System Software IOS (tm) s72033_rp Software (s72033_rp-JK9SV-M), Version 12.2(17d)SXB11, RELEASE SOFTWARE (fc1) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2005 by cisco Systems, Inc.`, + }, + want: []string{"cpe:2.3:o:cisco:ios:12.2(17d)sxb11:*:*:*:*:*:*:*"}, + }, + { + name: "Cisco IOX-XE Version 16.12.4", + args: snmp.Result{ + SysDescr0: `Cisco IOS Software [Gibraltar], Catalyst L3 Switch Software (CAT9K_LITE_IOSXE), Version 16.12.4, RELEASE SOFTWARE (fc5) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2020 by Cisco Systems, Inc. + Compiled Thu 09-Jul-20 19:31 by m`, + }, + want: []string{"cpe:2.3:o:cisco:ios_xe:16.12.4:*:*:*:*:*:*:*"}, + }, + { + name: "Cisco IOX-XE Version 03.06.07.E", + args: snmp.Result{ + SysDescr0: `Cisco IOS Software, IOS-XE Software, Catalyst 4500 L3 Switch Software (cat4500es8-UNIVERSALK9-M), Version 03.06.07.E RELEASE SOFTWARE (fc3) + Technical Support: http://www.cisco.com/techsupport + Copyright (c) 1986-2017 by Cisco Systems, Inc. + Compiled Wed`, + }, + want: []string{"cpe:2.3:o:cisco:ios_xe:03.06.07.e:*:*:*:*:*:*:*"}, + }, + { + name: "Juniper SSG-5-SH-BT", + args: snmp.Result{ + SysDescr0: "SSG5-ISDN version 6.3.0r14.0 (SN: 0000000000000001, Firewall+VPN)", + }, + want: []string{"cpe:2.3:h:juniper:ssg5-isdn:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:screenos:6.3.0r14.0:*:*:*:*:*:*:*"}, + }, + { + name: "JUNOS 20.4R3-S4.8 on Juniper MX240", + args: snmp.Result{ + SysDescr0: "Juniper Networks, Inc. mx240 internet router, kernel JUNOS 20.4R3-S4.8, Build date: 2022-08-16 20:42:11 UTC Copyright (c) 1996-2022 Juniper Networks, Inc.", + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Juniper Networks", + EntPhysicalName: "CHAS-BP3-MX240-S", + EntPhysicalSoftwareRev: "20.4R3-S4.8", + }}, + }, + want: []string{"cpe:2.3:h:juniper:mx240:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:20.4r3-s4.8:*:*:*:*:*:*:*"}, + }, + { + name: "JUNOS 12.1X46-D65.4 on Juniper SRX220H", + args: snmp.Result{ + SysDescr0: "Juniper Networks, Inc. srx220h internet router, kernel JUNOS 12.1X46-D65.4 #0: 2016-12-30 01:34:30 UTC builder@quoarth.juniper.net:/volume/build/junos/12.1/service/12.1X46-D65.4/obj-octeon/junos/bsd/kernels/JSRXNLE/kernel Build date: 2016-12-30 02:59", + }, + want: []string{"cpe:2.3:h:juniper:srx220h:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:12.1x46-d65.4:*:*:*:*:*:*:*"}, + }, + { + name: "JUNOS 12.3X48-D30.7 on Juniper SRX220H2", + args: snmp.Result{ + SysDescr0: "Juniper Networks, Inc. srx220h2 internet router, kernel JUNOS 12.3X48-D30.7, Build date: 2016-04-29 00:01:04 UTC Copyright (c) 1996-2016 Juniper Networks, Inc.", + }, + want: []string{"cpe:2.3:h:juniper:srx220h2:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:12.3x48-d30.7:*:*:*:*:*:*:*"}, + }, + { + name: "JUNOS 20.4R3-S4.8 on Juniper SRX4600", + args: snmp.Result{ + SysDescr0: "Juniper Networks, Inc. srx4600 internet router, kernel JUNOS 20.4R3-S4.8, Build date: 2022-08-16 20:42:11 UTC Copyright (c) 1996-2022 Juniper Networks, Inc.", + }, + want: []string{"cpe:2.3:h:juniper:srx4600:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:20.4r3-s4.8:*:*:*:*:*:*:*"}, + }, + { + name: "cpe:2.3:o:juniper:junos:20.4:r2-s2.2:*:*:*:*:*:*", + args: snmp.Result{ + SysDescr0: "Juniper Networks, Inc. ex4300-32f Ethernet Switch, kernel JUNOS 20.4R3-S4.8, Build date: 2022-08-16 21:10:45 UTC Copyright (c) 1996-2022 Juniper Networks, Inc.", + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Juniper Networks", + EntPhysicalName: "", + EntPhysicalSoftwareRev: "20.4R3-S4.8", + }}, + }, + want: []string{"cpe:2.3:h:juniper:ex4300-32f:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:20.4r3-s4.8:*:*:*:*:*:*:*"}, + }, + { + name: "Arista Networks EOS version 4.28.4M on DCS-7050TX-64", + args: snmp.Result{ + SysDescr0: "Arista Networks EOS version 4.28.4M running on an Arista Networks DCS-7050TX-64", + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Arista Networks", + EntPhysicalName: "", + EntPhysicalSoftwareRev: "4.28.4M", + }}, + }, + want: []string{"cpe:/h:arista:dcs-7050tx-64:-:*:*:*:*:*:*:*", "cpe:2.3:o:arista:eos:4.28.4m:*:*:*:*:*:*:*"}, + }, + { + name: "FortiGate-50E", + args: snmp.Result{ + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Fortinet", + EntPhysicalName: "FGT_50E", + EntPhysicalSoftwareRev: "FortiGate-50E v5.4.6,build1165b1165,171018 (GA)", + }}, + }, + want: []string{"cpe:2.3:h:fortinet:fortigate-50e:-:*:*:*:*:*:*:*", "cpe:2.3:o:fortinet:fortios:5.4.6:*:*:*:*:*:*:*"}, + }, + { + name: "FortiGate-60F", + args: snmp.Result{ + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Fortinet", + EntPhysicalName: "FGT_60F", + EntPhysicalSoftwareRev: "FortiGate-60F v6.4.11,build2030,221031 (GA.M)", + }}, + }, + want: []string{"cpe:2.3:h:fortinet:fortigate-60f:-:*:*:*:*:*:*:*", "cpe:2.3:o:fortinet:fortios:6.4.11:*:*:*:*:*:*:*"}, + }, + { + name: "YAMAHA RTX1000", + args: snmp.Result{ + SysDescr0: "RTX1000 Rev.8.01.29 (Fri Apr 15 11:50:44 2011)", + }, + want: []string{"cpe:2.3:h:yamaha:rtx1000:-:*:*:*:*:*:*:*", "cpe:2.3:o:yamaha:rtx1000:8.01.29:*:*:*:*:*:*:*"}, + }, + { + name: "YAMAHA RTX810", + args: snmp.Result{ + SysDescr0: "RTX810 Rev.11.01.34 (Tue Nov 26 18:39:12 2019)", + }, + want: []string{"cpe:2.3:h:yamaha:rtx810:-:*:*:*:*:*:*:*", "cpe:2.3:o:yamaha:rtx810:11.01.34:*:*:*:*:*:*:*"}, + }, + { + name: "NEC IX2105", + args: snmp.Result{ + SysDescr0: "NEC Portable Internetwork Core Operating System Software, IX Series IX2105 (magellan-sec) Software, Version 8.8.22, RELEASE SOFTWARE, Compiled Jul 04-Wed-2012 14:18:46 JST #2, IX2105", + }, + want: []string{"cpe:2.3:h:nec:ix2105:-:*:*:*:*:*:*:*", "cpe:2.3:o:nec:ix2105:8.8.22:*:*:*:*:*:*:*"}, + }, + { + name: "NEC IX2235", + args: snmp.Result{ + SysDescr0: "NEC Portable Internetwork Core Operating System Software, IX Series IX2235 (magellan-sec) Software, Version 10.6.21, RELEASE SOFTWARE, Compiled Dec 15-Fri-YYYY HH:MM:SS JST #2, IX2235", + }, + want: []string{"cpe:2.3:h:nec:ix2235:-:*:*:*:*:*:*:*", "cpe:2.3:o:nec:ix2235:10.6.21:*:*:*:*:*:*:*"}, + }, + { + name: "Palo Alto Networks PAN-OS 10.0.0 on PA-220", + args: snmp.Result{ + SysDescr0: "Palo Alto Networks PA-220 series firewall", + EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: { + EntPhysicalMfgName: "Palo Alto Networks", + EntPhysicalName: "PA-220", + EntPhysicalSoftwareRev: "10.0.0", + }}, + }, + want: []string{"cpe:2.3:h:paloaltonetworks:pa-220:-:*:*:*:*:*:*:*", "cpe:2.3:o:paloaltonetworks:pan-os:10.0.0:*:*:*:*:*:*:*"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + opts := []cmp.Option{ + cmpopts.SortSlices(func(i, j string) bool { + return i < j + }), + } + if diff := cmp.Diff(cpe.Convert(tt.args), tt.want, opts...); diff != "" { + t.Errorf("Convert() value is mismatch (-got +want):%s\n", diff) + } + }) + } +} diff --git a/contrib/snmp2cpe/pkg/snmp/snmp.go b/contrib/snmp2cpe/pkg/snmp/snmp.go new file mode 100644 index 0000000000..6a561c9d7d --- /dev/null +++ b/contrib/snmp2cpe/pkg/snmp/snmp.go @@ -0,0 +1,131 @@ +package snmp + +import ( + "log" + "strconv" + "strings" + "time" + + "github.com/gosnmp/gosnmp" + "github.com/pkg/errors" +) + +type options struct { + community string + debug bool +} + +// Option ... +type Option interface { + apply(*options) +} + +type communityOption string + +func (c communityOption) apply(opts *options) { + opts.community = string(c) +} + +// WithCommunity ... +func WithCommunity(c string) Option { + return communityOption(c) +} + +type debugOption bool + +func (d debugOption) apply(opts *options) { + opts.debug = bool(d) +} + +// WithDebug ... +func WithDebug(d bool) Option { + return debugOption(d) +} + +// Get ... +func Get(version gosnmp.SnmpVersion, ipaddr string, opts ...Option) (Result, error) { + var options options + for _, o := range opts { + o.apply(&options) + } + + r := Result{SysDescr0: "", EntPhysicalTables: map[int]EntPhysicalTable{}} + + params := &gosnmp.GoSNMP{ + Target: ipaddr, + Port: 161, + Version: version, + Timeout: time.Duration(2) * time.Second, + Retries: 3, + ExponentialTimeout: true, + MaxOids: gosnmp.MaxOids, + } + + switch version { + case gosnmp.Version1, gosnmp.Version2c: + params.Community = options.community + case gosnmp.Version3: + return Result{}, errors.New("not implemented") + } + + if err := params.Connect(); err != nil { + return Result{}, errors.Wrap(err, "failed to connect") + } + defer params.Conn.Close() + + for _, oid := range []string{"1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.47.1.1.1.1.12.1", "1.3.6.1.2.1.47.1.1.1.1.7.1", "1.3.6.1.2.1.47.1.1.1.1.10.1"} { + resp, err := params.Get([]string{oid}) + if err != nil { + return Result{}, errors.Wrap(err, "send SNMP GET request") + } + for _, v := range resp.Variables { + if options.debug { + switch v.Type { + case gosnmp.OctetString: + log.Printf("DEBUG: %s -> %s", v.Name, string(v.Value.([]byte))) + default: + log.Printf("DEBUG: %s -> %v", v.Name, v.Value) + } + } + + switch { + case v.Name == ".1.3.6.1.2.1.1.1.0": + if v.Type == gosnmp.OctetString { + r.SysDescr0 = string(v.Value.([]byte)) + } + case strings.HasPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.12."): + i, err := strconv.Atoi(strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.12.")) + if err != nil { + return Result{}, errors.Wrap(err, "failed to get index") + } + if v.Type == gosnmp.OctetString { + b := r.EntPhysicalTables[i] + b.EntPhysicalMfgName = string(v.Value.([]byte)) + r.EntPhysicalTables[i] = b + } + case strings.HasPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.7."): + i, err := strconv.Atoi(strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.7.")) + if err != nil { + return Result{}, errors.Wrap(err, "failed to get index") + } + if v.Type == gosnmp.OctetString { + b := r.EntPhysicalTables[i] + b.EntPhysicalName = string(v.Value.([]byte)) + r.EntPhysicalTables[i] = b + } + case strings.HasPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.10."): + i, err := strconv.Atoi(strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.10.")) + if err != nil { + return Result{}, errors.Wrap(err, "failed to get index") + } + if v.Type == gosnmp.OctetString { + b := r.EntPhysicalTables[i] + b.EntPhysicalSoftwareRev = string(v.Value.([]byte)) + r.EntPhysicalTables[i] = b + } + } + } + } + + return r, nil +} diff --git a/contrib/snmp2cpe/pkg/snmp/types.go b/contrib/snmp2cpe/pkg/snmp/types.go new file mode 100644 index 0000000000..c8fede04fd --- /dev/null +++ b/contrib/snmp2cpe/pkg/snmp/types.go @@ -0,0 +1,14 @@ +package snmp + +// Result ... +type Result struct { + SysDescr0 string `json:"sysDescr0,omitempty"` + EntPhysicalTables map[int]EntPhysicalTable `json:"entPhysicalTables,omitempty"` +} + +// EntPhysicalTable ... +type EntPhysicalTable struct { + EntPhysicalMfgName string `json:"entPhysicalMfgName,omitempty"` + EntPhysicalName string `json:"entPhysicalName,omitempty"` + EntPhysicalSoftwareRev string `json:"entPhysicalSoftwareRev,omitempty"` +} diff --git a/contrib/snmp2cpe/pkg/util/util.go b/contrib/snmp2cpe/pkg/util/util.go new file mode 100644 index 0000000000..78f9d4f16b --- /dev/null +++ b/contrib/snmp2cpe/pkg/util/util.go @@ -0,0 +1,12 @@ +package util + +import "golang.org/x/exp/maps" + +// Unique return unique elements +func Unique[T comparable](s []T) []T { + m := map[T]struct{}{} + for _, v := range s { + m[v] = struct{}{} + } + return maps.Keys(m) +} diff --git a/go.mod b/go.mod index d0fb4cd03f..5079653509 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/emersion/go-smtp v0.14.0 github.com/google/subcommands v1.2.0 github.com/google/uuid v1.3.0 + github.com/gosnmp/gosnmp v1.35.0 github.com/gosuri/uitable v0.0.4 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 @@ -35,6 +36,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/package-url/packageurl-go v0.1.1-0.20220203205134-d70459300c8a github.com/parnurzeal/gorequest v0.2.16 + github.com/pkg/errors v0.9.1 github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 @@ -134,7 +136,6 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect diff --git a/go.sum b/go.sum index 4963e29f03..861fd07ffb 100644 --- a/go.sum +++ b/go.sum @@ -333,6 +333,8 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosnmp/gosnmp v1.35.0 h1:EuWWNPxTCdAUx2/NbQcSa3WdNxjzpy4Phv57b4MWpJM= +github.com/gosnmp/gosnmp v1.35.0/go.mod h1:2AvKZ3n9aEl5TJEo/fFmf/FGO4Nj4cVeEc5yuk88CYc= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=