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  }