blob: ad2e3b8d0b361465c2e9fdc4186d3e327151bf5d [file] [log] [blame]
package checks
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/mockhttpclient"
"go.skia.org/infra/go/testutils/unittest"
"go.skia.org/infra/npm-audit-mirror/go/config"
"go.skia.org/infra/npm-audit-mirror/go/types"
"go.skia.org/infra/npm-audit-mirror/go/types/mocks"
)
func TestGetPackageDetails(t *testing.T) {
unittest.SmallTest(t)
tests := []struct {
packageRequestURL string
expectedNameWithScope string
expectedScopeName string
expectedTarballName string
expectedVersion string
name string
}{
{
packageRequestURL: "/import-fresh/-/import-fresh-3.3.0.tgz",
expectedNameWithScope: "import-fresh",
expectedScopeName: "",
expectedTarballName: "import-fresh-3.3.0.tgz",
expectedVersion: "3.3.0",
name: "GetPackageDetails test with /import-fresh/-/import-fresh-3.3.0.tgz",
},
{
packageRequestURL: "/@types/web/-/web-0.0.58.tgz",
expectedNameWithScope: "@types/web",
expectedScopeName: "@types/",
expectedTarballName: "web-0.0.58.tgz",
expectedVersion: "0.0.58",
name: "GetPackageDetails test with /@types/web/-/web-0.0.58.tgz",
},
{
packageRequestURL: "/@google-web-components/google-chart/-/google-chart-4.0.2.tgz",
expectedNameWithScope: "@google-web-components/google-chart",
expectedScopeName: "@google-web-components/",
expectedTarballName: "google-chart-4.0.2.tgz",
expectedVersion: "4.0.2",
name: "GetPackageDetails test with /@google-web-components/google-chart/-/google-chart-4.0.2.tgz",
},
{
packageRequestURL: "/gensync/-/gensync-1.0.0-beta.2.tgz",
expectedNameWithScope: "gensync",
expectedScopeName: "",
expectedTarballName: "gensync-1.0.0-beta.2.tgz",
expectedVersion: "1.0.0-beta.2",
name: "GetPackageDetails test with /gensync/-/gensync-1.0.0-beta.2.tgz",
},
{
packageRequestURL: "/@some-scope/gensync/-/gensync-1.0.0-beta.2.tgz",
expectedNameWithScope: "@some-scope/gensync",
expectedScopeName: "@some-scope/",
expectedTarballName: "gensync-1.0.0-beta.2.tgz",
expectedVersion: "1.0.0-beta.2",
name: "GetPackageDetails test with /@some-scope/gensync/-/gensync-1.0.0-beta.2.tgz",
},
}
for _, test := range tests {
details := getPackageDetails(test.packageRequestURL)
require.Equal(t, test.expectedNameWithScope, details.NameWithScope, test.name)
require.Equal(t, test.expectedScopeName, details.ScopeName, test.name)
require.Equal(t, test.expectedTarballName, details.TarballName, test.name)
require.Equal(t, test.expectedVersion, details.Version, test.name)
}
}
func TestPerformChecks_NonTarballRequest_ReturnImmediately(t *testing.T) {
unittest.SmallTest(t)
cm := &NpmChecksManager{}
checkPassed, result, err := cm.PerformChecks("gensync/-/gensync-1.0.0-beta")
require.NoError(t, err)
require.True(t, checkPassed)
require.Empty(t, result)
}
func TestPerformChecks_PackageAlreadyDownloaded_ReturnAfterDownloadCheck(t *testing.T) {
unittest.SmallTest(t)
// Mock ProjectMirror.
mockProjectMirror := &mocks.ProjectMirror{}
defer mockProjectMirror.AssertExpectations(t)
mockProjectMirror.On("IsPackageTarballDownloaded", "gensync-1.0.0-beta.tgz").Return(true).Once()
mockProjectMirror.On("GetProjectName").Return("test-project").Once()
cm := &NpmChecksManager{projectMirror: mockProjectMirror}
checkPassed, result, err := cm.PerformChecks("/gensync/-/gensync-1.0.0-beta.tgz")
require.NoError(t, err)
require.True(t, checkPassed)
require.Empty(t, result)
}
func TestPerformChecks_PackageWithTrustedScope_ReturnAfterTrustedScopeCheck(t *testing.T) {
unittest.SmallTest(t)
// Mock ProjectMirror.
mockProjectMirror := &mocks.ProjectMirror{}
defer mockProjectMirror.AssertExpectations(t)
mockProjectMirror.On("IsPackageTarballDownloaded", "gensync-1.0.0-beta.tgz").Return(false).Once()
cm := &NpmChecksManager{
projectMirror: mockProjectMirror,
trustedScopes: []string{"@some-scope/"},
}
checkPassed, result, err := cm.PerformChecks("/@some-scope/gensync/-/gensync-1.0.0-beta.tgz")
require.NoError(t, err)
require.True(t, checkPassed)
require.Empty(t, result)
}
func TestPerformChecks_PackageInAllowlist_ReturnAfterAllowlistCheck(t *testing.T) {
unittest.SmallTest(t)
// Mock ProjectMirror.
mockProjectMirror := &mocks.ProjectMirror{}
defer mockProjectMirror.AssertExpectations(t)
mockProjectMirror.On("IsPackageTarballDownloaded", "gensync-1.0.0-beta.tgz").Return(false).Once()
// Add allowed package with full package version specified.
allowedPackages := []*config.PackagesAllowList{
{Name: "gensync", Version: "1.0.0-beta"},
}
cm := &NpmChecksManager{
projectMirror: mockProjectMirror,
trustedScopes: []string{},
allowedPackages: allowedPackages,
}
checkPassed, result, err := cm.PerformChecks("/gensync/-/gensync-1.0.0-beta.tgz")
require.NoError(t, err)
require.True(t, checkPassed)
require.Empty(t, result)
// Add allowedPackage with semver.
allowedPackages = []*config.PackagesAllowList{
{Name: "gensync", Version: "^1.0"},
}
mockProjectMirror.On("IsPackageTarballDownloaded", "gensync-1.1.tgz").Return(false).Once()
cm.allowedPackages = allowedPackages
checkPassed, result, err = cm.PerformChecks("/gensync/-/gensync-1.1.tgz")
require.NoError(t, err)
require.True(t, checkPassed)
require.Empty(t, result)
}
func TestPerformChecks_RunSecurityChecks(t *testing.T) {
unittest.SmallTest(t)
// Mock ProjectMirror.
mockProjectMirror := &mocks.ProjectMirror{}
defer mockProjectMirror.AssertExpectations(t)
mockProjectMirror.On("IsPackageTarballDownloaded", "gensync-1.0.0-beta.tgz").Return(false)
mockProjectMirror.On("AddToDownloadedPackageTarballs", "gensync-1.0.0-beta.tgz").Return()
mockProjectMirror.On("GetProjectName").Return("test-project")
// Mock HTTP client.
testPackageResp, err := json.Marshal(&types.NpmPackage{
Versions: map[string]types.NpmVersion{
"1.0.0": {},
},
})
mockHttpClient := mockhttpclient.NewURLMock()
mockHttpClient.Mock("https://registry.npmjs.org/gensync", mockhttpclient.MockGetDialogue(testPackageResp))
// Run with no security checks specified should pass.
cm := &NpmChecksManager{
projectMirror: mockProjectMirror,
trustedScopes: []string{},
allowedPackages: []*config.PackagesAllowList{},
httpClient: mockHttpClient.Client(),
checks: []types.Check{},
}
checkPassed, result, err := cm.PerformChecks("/gensync/-/gensync-1.0.0-beta.tgz")
require.NoError(t, err)
require.True(t, checkPassed)
require.Empty(t, result)
// Add a mock security check that passes.
mockCheck := &mocks.Check{}
mockCheck.On("PerformCheck", "gensync", "1.0.0-beta", mock.Anything).Return(true, "", nil).Once()
mockCheck.On("Name").Return("Mock-Check")
defer mockCheck.AssertExpectations(t)
cm.checks = []types.Check{mockCheck}
checkPassed, result, err = cm.PerformChecks("/gensync/-/gensync-1.0.0-beta.tgz")
require.NoError(t, err)
require.True(t, checkPassed)
require.Empty(t, result)
// Add a security check that fails.
mockCheck.On("PerformCheck", "gensync", "1.0.0-beta", mock.Anything).Return(false, "some reason", nil).Once()
checkPassed, result, err = cm.PerformChecks("/gensync/-/gensync-1.0.0-beta.tgz")
require.NoError(t, err)
require.False(t, checkPassed)
require.Equal(t, "some reason", result)
}