github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/secrets/mysqlmylogin/mysqlmylogin.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 mysqlmylogin provides an extractor for identifying secrets in .mylogin.cnf files.
    16  package mysqlmylogin
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"path/filepath"
    22  
    23  	"github.com/google/osv-scalibr/extractor/filesystem"
    24  	"github.com/google/osv-scalibr/inventory"
    25  	"github.com/google/osv-scalibr/plugin"
    26  
    27  	"gopkg.in/ini.v1"
    28  )
    29  
    30  // Section is a Veles Secret that holds relevant information for a [Mysql MyLogin](https://dev.mysql.com/doc/refman/8.4/en/option-files.html).
    31  type Section struct {
    32  	SectionName string
    33  	User        string
    34  	Password    string
    35  	Host        string
    36  	Port        string
    37  	Socket      string
    38  }
    39  
    40  const (
    41  	// Name is the unique name of this extractor.
    42  	Name = "secrets/mysqlmylogin"
    43  )
    44  
    45  // Extractor extracts mysql credentials from .mylogin.cnf files.
    46  type Extractor struct{}
    47  
    48  // New returns a .mylogin.cnf extractor.
    49  func New() filesystem.Extractor { return &Extractor{} }
    50  
    51  // Name of the extractor.
    52  func (e Extractor) Name() string { return Name }
    53  
    54  // Version of the extractor.
    55  func (e Extractor) Version() int { return 0 }
    56  
    57  // Requirements of the extractor.
    58  func (e Extractor) Requirements() *plugin.Capabilities {
    59  	return &plugin.Capabilities{}
    60  }
    61  
    62  // FileRequired returns true if the specified file is a .mylogin.cnf file.
    63  func (e Extractor) FileRequired(api filesystem.FileAPI) bool {
    64  	return filepath.Base(api.Path()) == ".mylogin.cnf"
    65  }
    66  
    67  // Extract extracts Mysql credentials from .mylogin.cnf file.
    68  func (e Extractor) Extract(ctx context.Context, input *filesystem.ScanInput) (inventory.Inventory, error) {
    69  	var secrets []*inventory.Secret
    70  
    71  	plaintext, err := decryptMyLoginCNF(input.Reader)
    72  	if err != nil {
    73  		return inventory.Inventory{}, fmt.Errorf("error decrypting file %w", err)
    74  	}
    75  
    76  	cfg, err := ini.Load(plaintext)
    77  	if err != nil {
    78  		return inventory.Inventory{}, fmt.Errorf("unable to Load ini structure from %s", input.Path)
    79  	}
    80  
    81  	// Get all sections in the mylogin file
    82  	sections := cfg.Sections()
    83  	for _, section := range sections {
    84  		// The ini library uses a "DEFAULT" section which is always empty
    85  		// skipping it
    86  		if section.Name() == ini.DefaultSection {
    87  			continue
    88  		}
    89  
    90  		// Get all key-value pairs
    91  		keysMap := section.KeysHash()
    92  		// Populate struct (keys that don't exist will just be "")
    93  		s := Section{
    94  			SectionName: section.Name(),
    95  			Host:        keysMap["host"],
    96  			User:        keysMap["user"],
    97  			Password:    keysMap["password"],
    98  			Port:        keysMap["port"],
    99  			Socket:      keysMap["socket"],
   100  		}
   101  
   102  		secrets = append(secrets, &inventory.Secret{
   103  			Secret:   s,
   104  			Location: input.Path,
   105  		})
   106  	}
   107  	return inventory.Inventory{Secrets: secrets}, nil
   108  }