github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/misc/chrome/extensions/extensions_test.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 extensions_test
    16  
    17  import (
    18  	"runtime"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/google/go-cmp/cmp/cmpopts"
    23  	"github.com/google/osv-scalibr/extractor"
    24  	"github.com/google/osv-scalibr/extractor/filesystem/misc/chrome/extensions"
    25  	"github.com/google/osv-scalibr/extractor/filesystem/simplefileapi"
    26  	"github.com/google/osv-scalibr/purl"
    27  	"github.com/google/osv-scalibr/testing/extracttest"
    28  )
    29  
    30  func TestExtractor_FileRequired(t *testing.T) {
    31  	tests := []struct {
    32  		inputPath string
    33  		want      bool
    34  		GOOS      string
    35  	}{
    36  		{inputPath: "", want: false},
    37  		{GOOS: "windows", inputPath: `%LOCALAPPDATA%\Google\Chrome\User Data\Default\Extensions\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\version\manifest.json`, want: true},
    38  		{GOOS: "windows", inputPath: `%LOCALAPPDATA%\Google\Chrome Beta\User Data\Default\Extensions\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\version\manifest.json`, want: true},
    39  		{GOOS: "windows", inputPath: `%LOCALAPPDATA%\Google\Chrome SxS\User Data\Default\Extensions\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\version\manifest.json`, want: true},
    40  		{GOOS: "windows", inputPath: `%LOCALAPPDATA%\Google\Chrome for Testing\User Data\Default\Extensions\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\version\manifest.json`, want: true},
    41  		{GOOS: "windows", inputPath: `%LOCALAPPDATA%\Chromium\User Data\Default\Extensions\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\version\manifest.json`, want: true},
    42  
    43  		{GOOS: "windows", inputPath: `%LOCALAPPDATA%\Chromium\User Data\Default\Extensions\invalid-id\version\manifest.json`, want: false},
    44  		{GOOS: "windows", inputPath: `%LOCALAPPDATA%\Chromium\User Data\Default\Extensions\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bad\path\manifest.json`, want: false},
    45  
    46  		{GOOS: "darwin", inputPath: `~/Library/Application Support/Google/Chrome/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    47  		{GOOS: "darwin", inputPath: `~/Library/Application Support/Google/Chrome Beta/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    48  		{GOOS: "darwin", inputPath: `~/Library/Application Support/Google/Chrome Canary/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    49  		{GOOS: "darwin", inputPath: `~/Library/Application Support/Google/Chrome for Testing/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    50  		{GOOS: "darwin", inputPath: `~/Library/Application Support/Chromium/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    51  		{GOOS: "darwin", inputPath: `/Users/username/Library/Application Support/Google/Chrome/Default/Extensions/aapbdbdomjkkjkaonfhkkikfgjllcleb/1.0.0.6_0/manifest.json`, want: true},
    52  
    53  		{GOOS: "darwin", inputPath: `~/Library/Application Support/Chromium/Default/Extensions/invalid-id/version/manifest.json`, want: false},
    54  		{GOOS: "darwin", inputPath: `~/Library/Application Support/Chromium/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bad/path/manifest.json`, want: false},
    55  
    56  		{GOOS: "linux", inputPath: `~/.config/google-chrome/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    57  		{GOOS: "linux", inputPath: `~/.config/google-chrome-beta/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    58  		{GOOS: "linux", inputPath: `~/.config/google-chrome-unstable/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    59  		{GOOS: "linux", inputPath: `~/.config/google-chrome-for-testing/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    60  		{GOOS: "linux", inputPath: `~/.config/chromium/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/version/manifest.json`, want: true},
    61  
    62  		{GOOS: "linux", inputPath: `~/.config/chromium/Default/Extensions/invalid-id/version/manifest.json`, want: false},
    63  		{GOOS: "linux", inputPath: `~/.config/chromium/Default/Extensions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bad/path/manifest.json`, want: false},
    64  	}
    65  	for _, tt := range tests {
    66  		t.Run(tt.inputPath, func(t *testing.T) {
    67  			if tt.GOOS != "" && tt.GOOS != runtime.GOOS {
    68  				t.Skipf("Skipping test for unsupported OS %q", runtime.GOOS)
    69  			}
    70  			e := extensions.Extractor{}
    71  			got := e.FileRequired(simplefileapi.New(tt.inputPath, nil))
    72  			if got != tt.want {
    73  				t.Errorf("FileRequired(%s) got = %v, want %v", tt.inputPath, got, tt.want)
    74  			}
    75  		})
    76  	}
    77  }
    78  
    79  func TestExtractor_Extract(t *testing.T) {
    80  	tests := []extracttest.TestTableEntry{
    81  		{
    82  			Name: "invalid manifest",
    83  			InputConfig: extracttest.ScanInputMockConfig{
    84  				Path: "testdata/invalid-manifest.json",
    85  			},
    86  			WantErr: extracttest.ContainsErrStr{Str: "bad format"},
    87  		},
    88  		{
    89  			Name: "no locale specified",
    90  			InputConfig: extracttest.ScanInputMockConfig{
    91  				Path: "testdata/caaadbdomjkkjkaonfhkkikfgjllcleb/1.2.85/manifest.json",
    92  			},
    93  			WantPackages: []*extractor.Package{
    94  				{
    95  					Name:     "caaadbdomjkkjkaonfhkkikfgjllcleb",
    96  					Version:  "1.2.85",
    97  					PURLType: purl.TypeGeneric,
    98  					Metadata: &extensions.Metadata{
    99  						Description:     "A decentralized wallet for blockchain transactions.",
   100  						HostPermissions: []string{"file://*/*", "http://*/*", "https://*/*"},
   101  						ManifestVersion: 3,
   102  						Name:            "CryptoX Blockchain Wallet",
   103  						Permissions:     []string{"storage", "tabs", "alarms"},
   104  						UpdateURL:       "https://clients2.google.com/service/update2/crx",
   105  					},
   106  				},
   107  			},
   108  		},
   109  		{
   110  			Name: "default locale specified but no message file provided",
   111  			InputConfig: extracttest.ScanInputMockConfig{
   112  				Path: "testdata/nolocaleoekpmoecnnnilnnbdlolhkhi/1.89.1/manifest.json",
   113  			},
   114  			WantErr: extracttest.ContainsErrStr{Str: "could not extract locale info"},
   115  		},
   116  		{
   117  			Name: "locale specified",
   118  			InputConfig: extracttest.ScanInputMockConfig{
   119  				Path: "testdata/ghbmnnjooekpmoecnnnilnnbdlolhkhi/1.89.1/manifest.json",
   120  			},
   121  			WantPackages: []*extractor.Package{
   122  				{
   123  					Name:     "ghbmnnjooekpmoecnnnilnnbdlolhkhi",
   124  					Version:  "1.89.1",
   125  					PURLType: purl.TypeGeneric,
   126  					Metadata: &extensions.Metadata{
   127  						AuthorEmail:          "docs-hosted-app-own@google.com",
   128  						Description:          "Edit, create, and view your documents, spreadsheets, and presentations — all without internet access.",
   129  						HostPermissions:      []string{"https://docs.google.com/*", "https://drive.google.com/*"},
   130  						ManifestVersion:      3,
   131  						MinimumChromeVersion: "88",
   132  						Name:                 "Google Docs Offline",
   133  						Permissions:          []string{"alarms", "storage", "unlimitedStorage", "offscreen"},
   134  						UpdateURL:            "https://clients2.google.com/service/update2/crx",
   135  					},
   136  				},
   137  			},
   138  		},
   139  	}
   140  
   141  	for _, tt := range tests {
   142  		t.Run(tt.Name, func(t *testing.T) {
   143  			extr := extensions.Extractor{}
   144  
   145  			scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig)
   146  			defer extracttest.CloseTestScanInput(t, scanInput)
   147  
   148  			got, err := extr.Extract(t.Context(), &scanInput)
   149  
   150  			if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" {
   151  				t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   152  				return
   153  			}
   154  
   155  			if diff := cmp.Diff(tt.WantPackages, got.Packages, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" {
   156  				t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   157  			}
   158  		})
   159  	}
   160  }