github.com/google/osv-scalibr@v0.4.1/extractor/standalone/os/netports/netports.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package netports extracts open ports on the system and maps them to running processes when 16 // possible. 17 package netports 18 19 // This package is highly EXPERIMENTAL. Its behavior and output are subject to change without prior 20 // notice. It has also not been tested extensively. 21 // Use at your own risk. 22 23 import ( 24 "context" 25 "fmt" 26 "net" 27 28 "github.com/google/osv-scalibr/extractor" 29 "github.com/google/osv-scalibr/extractor/standalone" 30 "github.com/google/osv-scalibr/inventory" 31 "github.com/google/osv-scalibr/plugin" 32 psutilnet "github.com/shirou/gopsutil/net" 33 "github.com/shirou/gopsutil/process" 34 ) 35 36 const ( 37 // Name is the unique name of this extractor. 38 Name = "os/netports" 39 ) 40 41 // Extractor extracts open ports on the system. 42 type Extractor struct{} 43 44 // New creates a new Extractor. 45 func New() standalone.Extractor { 46 return &Extractor{} 47 } 48 49 // Name of the extractor. 50 func (e Extractor) Name() string { return Name } 51 52 // Version of the extractor. 53 func (e Extractor) Version() int { return 0 } 54 55 // Requirements of the extractor. 56 func (e Extractor) Requirements() *plugin.Capabilities { 57 return &plugin.Capabilities{ 58 RunningSystem: true, 59 } 60 } 61 62 // Extract extracts open ports on the system. 63 func (e Extractor) Extract(ctx context.Context, input *standalone.ScanInput) (inventory.Inventory, error) { 64 var packages []*extractor.Package 65 66 connections, err := psutilnet.ConnectionsWithContext(ctx, "tcp") 67 if err != nil { 68 return inventory.Inventory{}, err 69 } 70 71 for _, c := range connections { 72 // only consider listening TCP connections 73 if c.Status != "LISTEN" { 74 continue 75 } 76 77 // Skip loopback connections. 78 laddrIP := net.ParseIP(c.Laddr.IP) 79 if laddrIP.IsLoopback() { 80 continue 81 } 82 83 processInfo, err := process.NewProcess(c.Pid) 84 if err != nil { 85 continue 86 } 87 88 cmdline, err := processInfo.Cmdline() 89 if err != nil { 90 continue 91 } 92 93 packages = append(packages, e.newPackage(c.Laddr.Port, "tcp", cmdline)) 94 } 95 return inventory.Inventory{Packages: packages}, nil 96 } 97 98 func (e Extractor) newPackage(port uint32, protocol string, cmdline string) *extractor.Package { 99 return &extractor.Package{ 100 Name: fmt.Sprintf("network-port-%d", port), 101 Metadata: &Metadata{ 102 Port: port, 103 Protocol: protocol, 104 Cmdline: cmdline, 105 }, 106 } 107 }