This commit is contained in:
Abhisek Datta 2025-07-21 09:14:36 +05:30 committed by GitHub
commit b4976630da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 308 additions and 34 deletions

View File

@ -0,0 +1,15 @@
[
{
"identifier": {
"id": "golang.go",
"uuid": "36c8e36e-4981-4810-b4db-2fba5441957c"
},
"version": "0.39.1",
"location": {
"path": "/home/user/.cursor/extensions/golang.go-0.39.1",
"scheme": "file"
},
"relativeLocation": "golang.go-0.39.1"
}
]

View File

@ -0,0 +1,15 @@
[
{
"identifier": {
"id": "redhat.java",
"uuid": "198a707e-28af-4e84-8610-6e2f628dd12d"
},
"version": "1.20.0",
"location": {
"path": "/home/user/.vscode-oss/extensions/redhat.java-1.20.0",
"scheme": "file"
},
"relativeLocation": "redhat.java-1.20.0"
}
]

View File

@ -0,0 +1,14 @@
[
{
"identifier": {
"id": "ms-python.python",
"uuid": "f1f59ae4-9318-4f3c-a9b5-81b2eaa5f8a3"
},
"version": "2023.20.0",
"location": {
"path": "/home/user/.vscode/extensions/ms-python.python-2023.20.0",
"scheme": "file"
},
"relativeLocation": "ms-python.python-2023.20.0"
}
]

View File

@ -0,0 +1,15 @@
[
{
"identifier": {
"id": "rust-lang.rust",
"uuid": "3f899f2a-4d01-4718-ac18-4e5832cf5e6b"
},
"version": "0.7.9",
"location": {
"path": "/home/user/.windsurf/extensions/rust-lang.rust-0.7.9",
"scheme": "file"
},
"relativeLocation": "rust-lang.rust-0.7.9"
}
]

View File

@ -1,26 +0,0 @@
[
{
"identifier": {
"id": "castwide.solargraph",
"uuid": "349e83e2-207c-4309-a8d2-dfa43f7ee0c9"
},
"version": "0.24.1",
"location": {
"$mid": 1,
"path": "/Users/dev/.vscode/extensions/castwide.solargraph-0.24.1",
"scheme": "file"
},
"relativeLocation": "castwide.solargraph-0.24.1",
"metadata": {
"id": "349e83e2-207c-4309-a8d2-dfa43f7ee0c9",
"publisherId": "8244762e-597b-434d-b303-f780ff47b36c",
"publisherDisplayName": "Castwide",
"targetPlatform": "undefined",
"updated": false,
"isPreReleaseVersion": false,
"installedTimestamp": 1703166823676,
"pinned": false,
"preRelease": false
}
}
]

View File

@ -68,15 +68,24 @@ var _ PackageManifestReader = (*vsixExtReader)(nil)
func NewVSIXExtReader(distributions []string) (*vsixExtReader, error) {
customDistributions := make(map[string]distributionInfo)
ecosystem := models.EcosystemVSCodeExtensions
for i, distribution := range distributions {
// Check if the path matches any supported editor
foundMatch := false
ecosystem := models.EcosystemVSCodeExtensions
for _, eco := range editors {
if strings.Contains(distribution, eco.FilePath) {
ecosystem = eco.Ecosystem
foundMatch = true
break
}
}
if !foundMatch {
return nil, fmt.Errorf("unsupported editor path: %s", distribution)
}
customDistributions[fmt.Sprintf("custom-%d", i)] = distributionInfo{
FilePath: distribution,
Ecosystem: ecosystem,

View File

@ -1,8 +1,10 @@
package readers
import (
"path/filepath"
"testing"
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
"github.com/safedep/vet/pkg/models"
"github.com/stretchr/testify/assert"
)
@ -13,21 +15,116 @@ func TestVSCodeExtReaderInit(t *testing.T) {
assert.NotNil(t, reader)
}
func TestVSCodeExtReaderEnumManifests(t *testing.T) {
reader, err := NewVSIXExtReader([]string{"./fixtures/vsx"})
// TestVSCodeExtReaderForAllEditors tests each editor individually
func TestVSCodeExtReaderForAllEditors(t *testing.T) {
tests := []struct {
name string
path string
expectedPkg string
expectedVersion string
expectedEco packagev1.Ecosystem
}{
{
name: "VSCode Editor",
path: ".vscode/extensions/",
expectedPkg: "ms-python.python",
expectedVersion: "2023.20.0",
expectedEco: packagev1.Ecosystem_ECOSYSTEM_VSCODE,
},
{
name: "VSCodium Editor",
path: ".vscode-oss/extensions/",
expectedPkg: "redhat.java",
expectedVersion: "1.20.0",
expectedEco: packagev1.Ecosystem_ECOSYSTEM_OPENVSX,
},
{
name: "Cursor Editor",
path: ".cursor/extensions/",
expectedPkg: "golang.go",
expectedVersion: "0.39.1",
expectedEco: packagev1.Ecosystem_ECOSYSTEM_OPENVSX,
},
{
name: "Windsurf Editor",
path: ".windsurf/extensions/",
expectedPkg: "rust-lang.rust",
expectedVersion: "0.7.9",
expectedEco: packagev1.Ecosystem_ECOSYSTEM_OPENVSX,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
path := filepath.Join("./fixtures/vsix", tt.path)
reader, err := NewVSIXExtReader([]string{path})
assert.NoError(t, err)
assert.NotNil(t, reader)
err = reader.EnumManifests(func(manifest *models.PackageManifest, reader PackageReader) error {
assert.NotNil(t, manifest)
assert.NotNil(t, reader)
packages := manifest.GetPackages()
assert.Equal(t, 1, len(packages))
assert.Equal(t, tt.expectedPkg, packages[0].GetName())
assert.Equal(t, tt.expectedVersion, packages[0].GetVersion())
assert.Equal(t, tt.expectedEco, packages[0].GetControlTowerSpecEcosystem())
return nil
})
assert.NoError(t, err)
})
}
}
// TestVSCodeExtReaderWithMultipleEditors tests reading from multiple editors simultaneously
func TestVSCodeExtReaderWithMultipleEditors(t *testing.T) {
paths := []string{
"./fixtures/vsix/.vscode/extensions",
"./fixtures/vsix/.vscode-oss/extensions",
"./fixtures/vsix/.cursor/extensions",
"./fixtures/vsix/.windsurf/extensions",
}
reader, err := NewVSIXExtReader(paths)
assert.NoError(t, err)
assert.NotNil(t, reader)
foundPackages := make(map[string]bool)
packageCount := 0
err = reader.EnumManifests(func(manifest *models.PackageManifest, reader PackageReader) error {
assert.NotNil(t, manifest)
assert.NotNil(t, reader)
packages := manifest.GetPackages()
packageCount += len(packages)
assert.Equal(t, 1, manifest.GetPackagesCount())
assert.Equal(t, "castwide.solargraph", manifest.GetPackages()[0].GetName())
assert.Equal(t, "0.24.1", manifest.GetPackages()[0].GetVersion())
for _, pkg := range packages {
foundPackages[pkg.GetName()] = true
// Verify ecosystem based on package source
switch pkg.GetName() {
case "ms-python.python":
assert.Equal(t, packagev1.Ecosystem_ECOSYSTEM_VSCODE, pkg.GetControlTowerSpecEcosystem())
case "redhat.java", "golang.go", "rust-lang.rust":
assert.Equal(t, packagev1.Ecosystem_ECOSYSTEM_OPENVSX, pkg.GetControlTowerSpecEcosystem())
}
}
return nil
})
assert.NoError(t, err)
assert.Equal(t, 4, packageCount) // Should find all four extensions
assert.True(t, foundPackages["ms-python.python"])
assert.True(t, foundPackages["redhat.java"])
assert.True(t, foundPackages["golang.go"])
assert.True(t, foundPackages["rust-lang.rust"])
}
func TestVSCodeExtReaderWithInvalidPath(t *testing.T) {
reader, err := NewVSIXExtReader([]string{"./fixtures/vsix/nonexistent"})
assert.Error(t, err)
assert.Nil(t, reader)
}

View File

@ -0,0 +1,53 @@
package reporter
import (
"os"
controltowerv1pb "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/controltower/v1"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
)
type githubActionResolver struct{}
func GithubActionsSyncReporterResolver() SyncReporterEnvResolver {
return &githubActionResolver{}
}
var _ SyncReporterEnvResolver = &githubActionResolver{}
func (g *githubActionResolver) GetProjectSource() controltowerv1pb.Project_Source {
return controltowerv1pb.Project_SOURCE_GITHUB
}
func (g *githubActionResolver) GetProjectURL() string {
return os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY")
}
func (g *githubActionResolver) GitRef() string {
return os.Getenv("GITHUB_REF")
}
func (g *githubActionResolver) GitSha() string {
return os.Getenv("GITHUB_SHA")
}
func (g *githubActionResolver) Trigger() controltowerv1.ToolTrigger {
switch eventName := os.Getenv("GITHUB_EVENT_NAME"); eventName {
case "push":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_PUSH
case "pull_request", "pull_request_target":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_PULL_REQUEST
case "create":
// In GitHub Actions, 'create' event with ref_type=tag indicates a tag was created
if os.Getenv("GITHUB_REF_TYPE") == "tag" {
return controltowerv1.ToolTrigger_TOOL_TRIGGER_TAG
}
return controltowerv1.ToolTrigger_TOOL_TRIGGER_UNSPECIFIED
case "schedule":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_SCHEDULED
case "workflow_dispatch", "repository_dispatch":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_MANUAL
default:
return controltowerv1.ToolTrigger_TOOL_TRIGGER_UNSPECIFIED
}
}

View File

@ -3,6 +3,7 @@ package reporter
import (
"context"
"fmt"
"os"
"strings"
"sync"
@ -187,6 +188,15 @@ type syncReporter struct {
// Verify syncReporter implements the Reporter interface
var _ Reporter = (*syncReporter)(nil)
func NewSyncReporterEnvironmentResolver() SyncReporterEnvResolver {
// The `GITHUB_ACTIONS` environment variable is always set to true when GitHub Actions is running the workflow
if _, exists := os.LookupEnv("GITHUB_ACTIONS"); exists {
return GithubActionsSyncReporterResolver()
}
return DefaultSyncReporterEnvResolver()
}
// NewSyncReporter creates a new sync reporter.
func NewSyncReporter(config SyncReporterConfig, envResolver SyncReporterEnvResolver, callbacks SyncReporterCallbacks) (*syncReporter, error) {
if config.ClientConnection == nil {

View File

@ -3,6 +3,7 @@ package reporter
import (
"context"
"errors"
"os"
"testing"
controltowerv1pb "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/controltower/v1"
@ -111,6 +112,77 @@ func mockDependencyGraph(pkg *models.Package) {
pkg.Manifest.DependencyGraph = dg
}
func TestNewSyncReporterEnvironmentResolver(t *testing.T) {
tests := []struct {
name string
setupEnv func(t *testing.T)
expectedProjectSource controltowerv1pb.Project_Source
expectedProjectUrl string
expectedTrigger controltowerv1.ToolTrigger
expectedGitRef string
expectedGitSha string
}{
{
name: "should return GitHub Actions values when GITHUB_ACTIONS is set",
setupEnv: func(t *testing.T) {
t.Setenv("GITHUB_ACTIONS", "true")
t.Setenv("GITHUB_SERVER_URL", "https://github.com")
t.Setenv("GITHUB_REPOSITORY", "safedep/vet")
t.Setenv("GITHUB_EVENT_NAME", "push")
t.Setenv("GITHUB_REF", "refs/heads/main")
t.Setenv("GITHUB_SHA", "abc123")
},
expectedProjectSource: controltowerv1pb.Project_SOURCE_GITHUB,
expectedTrigger: controltowerv1.ToolTrigger_TOOL_TRIGGER_PUSH,
expectedProjectUrl: "https://github.com/safedep/vet",
expectedGitRef: "refs/heads/main",
expectedGitSha: "abc123",
},
{
name: "should return default values when GITHUB_ACTIONS is not set",
setupEnv: func(t *testing.T) {
// No environment setup needed
os.Unsetenv("GITHUB_ACTIONS")
os.Unsetenv("GITHUB_REPOSITORY")
os.Unsetenv("GITHUB_SERVER_URL")
os.Unsetenv("GITHUB_REF")
os.Unsetenv("GITHUB_SHA")
os.Unsetenv("GITHUB_EVENT_NAME")
},
expectedProjectSource: controltowerv1pb.Project_SOURCE_UNSPECIFIED,
expectedTrigger: controltowerv1.ToolTrigger_TOOL_TRIGGER_MANUAL,
expectedProjectUrl: "",
expectedGitRef: "",
expectedGitSha: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup test environment
tt.setupEnv(t)
// Get the resolver
resolver := NewSyncReporterEnvironmentResolver()
source := resolver.GetProjectSource()
assert.Equal(t, tt.expectedProjectSource, source, "unexpected project source")
url := resolver.GetProjectURL()
assert.Equal(t, tt.expectedProjectUrl, url, "unexpected project URL")
trigger := resolver.Trigger()
assert.Equal(t, tt.expectedTrigger, trigger, "unexpected trigger")
gitRef := resolver.GitRef()
assert.Equal(t, tt.expectedGitRef, gitRef, "unexpected git ref")
gitSha := resolver.GitSha()
assert.Equal(t, tt.expectedGitSha, gitSha, "unexpected git sha")
})
}
}
func TestSyncPackage(t *testing.T) {
tests := []struct {
name string

View File

@ -679,7 +679,7 @@ func internalStartScan() error {
ProjectVersion: syncReportStream,
EnableMultiProjectSync: syncEnableMultiProject,
ClientConnection: clientConn,
}, reporter.DefaultSyncReporterEnvResolver(), reporter.SyncReporterCallbacks{
}, reporter.NewSyncReporterEnvironmentResolver(), reporter.SyncReporterCallbacks{
OnSyncStart: func() {
ui.PrintMsg("🌐 Syncing data to SafeDep Cloud...")
},