diff --git a/internal/resources/providers/azurelib/inventory/mock_security_client_wrapper.go b/internal/resources/providers/azurelib/inventory/mock_security_client_wrapper.go index e5c28d3d01..5c920db93d 100644 --- a/internal/resources/providers/azurelib/inventory/mock_security_client_wrapper.go +++ b/internal/resources/providers/azurelib/inventory/mock_security_client_wrapper.go @@ -96,18 +96,20 @@ func (_c *mockSecurityClientWrapper_ListAutoProvisioningSettings_Call) RunAndRet } // ListSecurityContacts provides a mock function with given fields: ctx, subID -func (_m *mockSecurityClientWrapper) ListSecurityContacts(ctx context.Context, subID string) (armsecurity.ContactsClientListResponse, error) { +func (_m *mockSecurityClientWrapper) ListSecurityContacts(ctx context.Context, subID string) ([]armsecurity.ContactsClientListResponse, error) { ret := _m.Called(ctx, subID) - var r0 armsecurity.ContactsClientListResponse + var r0 []armsecurity.ContactsClientListResponse var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (armsecurity.ContactsClientListResponse, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) ([]armsecurity.ContactsClientListResponse, error)); ok { return rf(ctx, subID) } - if rf, ok := ret.Get(0).(func(context.Context, string) armsecurity.ContactsClientListResponse); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) []armsecurity.ContactsClientListResponse); ok { r0 = rf(ctx, subID) } else { - r0 = ret.Get(0).(armsecurity.ContactsClientListResponse) + if ret.Get(0) != nil { + r0 = ret.Get(0).([]armsecurity.ContactsClientListResponse) + } } if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { @@ -138,12 +140,12 @@ func (_c *mockSecurityClientWrapper_ListSecurityContacts_Call) Run(run func(ctx return _c } -func (_c *mockSecurityClientWrapper_ListSecurityContacts_Call) Return(_a0 armsecurity.ContactsClientListResponse, _a1 error) *mockSecurityClientWrapper_ListSecurityContacts_Call { +func (_c *mockSecurityClientWrapper_ListSecurityContacts_Call) Return(_a0 []armsecurity.ContactsClientListResponse, _a1 error) *mockSecurityClientWrapper_ListSecurityContacts_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *mockSecurityClientWrapper_ListSecurityContacts_Call) RunAndReturn(run func(context.Context, string) (armsecurity.ContactsClientListResponse, error)) *mockSecurityClientWrapper_ListSecurityContacts_Call { +func (_c *mockSecurityClientWrapper_ListSecurityContacts_Call) RunAndReturn(run func(context.Context, string) ([]armsecurity.ContactsClientListResponse, error)) *mockSecurityClientWrapper_ListSecurityContacts_Call { _c.Call.Return(run) return _c } diff --git a/internal/resources/providers/azurelib/inventory/security_provider.go b/internal/resources/providers/azurelib/inventory/security_provider.go index 9fbc95b60f..ff31f9c7fe 100644 --- a/internal/resources/providers/azurelib/inventory/security_provider.go +++ b/internal/resources/providers/azurelib/inventory/security_provider.go @@ -19,16 +19,10 @@ package inventory import ( "context" - "errors" "fmt" - "net/http" - "net/url" "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity" "github.com/elastic/elastic-agent-libs/logp" "github.com/samber/lo" @@ -38,7 +32,7 @@ import ( ) type securityClientWrapper interface { - ListSecurityContacts(ctx context.Context, subID string) (armsecurity.ContactsClientListResponse, error) + ListSecurityContacts(ctx context.Context, subID string) ([]armsecurity.ContactsClientListResponse, error) ListAutoProvisioningSettings(ctx context.Context, subID string) ([]armsecurity.AutoProvisioningSettingsClientListResponse, error) } @@ -47,76 +41,17 @@ type SecurityContactsProviderAPI interface { ListAutoProvisioningSettings(ctx context.Context, subscriptionID string) ([]AzureAsset, error) } -const listSecurityContactsURI = "/subscriptions/%s/providers/Microsoft.Security/securityContacts" - -// azureSecurityContactsClient is needed till this issue - -// https://github.com/Azure/azure-sdk-for-go/issues/19740 is fixed. -// Implementation code is similar to armsecurity.ContactsClient but it attempts to decode - -// the List response to two possible structs. -type azureSecurityContactsClient struct { - armClient *arm.Client -} - -// ListSecurityContacts implements the Security Contacts List Azure REST API call. -// https://learn.microsoft.com/en-us/rest/api/defenderforcloud/security-contacts/list -func (c *azureSecurityContactsClient) ListSecurityContacts(ctx context.Context, subID string) (armsecurity.ContactsClientListResponse, error) { - req, err := c.listSecurityContactsRequest(ctx, c.armClient.Endpoint(), subID) - if err != nil { - return armsecurity.ContactsClientListResponse{}, err - } - - httpResp, err := c.armClient.Pipeline().Do(req) - if err != nil { - return armsecurity.ContactsClientListResponse{}, err - } - - if !runtime.HasStatusCode(httpResp, http.StatusOK) { - err = runtime.NewResponseError(httpResp) - return armsecurity.ContactsClientListResponse{}, err - } - - return c.listSecurityContactsResponseDecode(httpResp) +type defaultSecurityClientWrapper struct { + credential azcore.TokenCredential } -// listCreateRequest creates the List request. -func (*azureSecurityContactsClient) listSecurityContactsRequest(ctx context.Context, endpoint, subID string) (*policy.Request, error) { - if subID == "" { - return nil, errors.New("parameter subscription id cannot be empty") - } - - urlPath := fmt.Sprintf(listSecurityContactsURI, url.PathEscape(subID)) - - req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(endpoint, urlPath)) +func (w *defaultSecurityClientWrapper) ListSecurityContacts(ctx context.Context, subID string) ([]armsecurity.ContactsClientListResponse, error) { + cl, err := armsecurity.NewContactsClient(subID, w.credential, nil) if err != nil { return nil, err } - reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2020-01-01-preview") - req.Raw().URL.RawQuery = reqQP.Encode() - req.Raw().Header.Set("Accept", "application/json") - - return req, nil -} - -func (*azureSecurityContactsClient) listSecurityContactsResponseDecode(resp *http.Response) (armsecurity.ContactsClientListResponse, error) { - result := armsecurity.ContactsClientListResponse{} - if err := runtime.UnmarshalAsJSON(resp, &result.ContactList.Value); err != nil { - // fallback attempt to unmarshal - if secondErr := runtime.UnmarshalAsJSON(resp, &result); secondErr != nil { - return armsecurity.ContactsClientListResponse{}, err - } - } - return result, nil -} - -type defaultSecurityClientWrapper struct { - credential azcore.TokenCredential - azureSecurityContactsClient *azureSecurityContactsClient -} - -func (w *defaultSecurityClientWrapper) ListSecurityContacts(ctx context.Context, subID string) (armsecurity.ContactsClientListResponse, error) { - return w.azureSecurityContactsClient.ListSecurityContacts(ctx, subID) + return readPager(ctx, cl.NewListPager(nil)) } func (w *defaultSecurityClientWrapper) ListAutoProvisioningSettings(ctx context.Context, subID string) ([]armsecurity.AutoProvisioningSettingsClientListResponse, error) { @@ -132,14 +67,11 @@ type securityContactsProvider struct { log *logp.Logger //nolint:unused } -func NewSecurityContacts(log *logp.Logger, credential azcore.TokenCredential, armClient *arm.Client) SecurityContactsProviderAPI { +func NewSecurityContacts(log *logp.Logger, credential azcore.TokenCredential) SecurityContactsProviderAPI { return &securityContactsProvider{ log: log, client: &defaultSecurityClientWrapper{ credential: credential, - azureSecurityContactsClient: &azureSecurityContactsClient{ - armClient: armClient, - }, }, } } @@ -150,12 +82,16 @@ func (p *securityContactsProvider) ListSecurityContacts(ctx context.Context, sub return nil, fmt.Errorf("error while fetching security contacts: %w", err) } - return lo.FilterMap(res.Value, func(contract *armsecurity.Contact, _ int) (AzureAsset, bool) { - if contract == nil { - return AzureAsset{}, false - } - return p.transformSecurityContract(contract, subscriptionID), true - }), nil + return lo.Flatten( + lo.Map(res, func(listResponse armsecurity.ContactsClientListResponse, _ int) []AzureAsset { + return lo.FilterMap(listResponse.ContactList.Value, func(contact *armsecurity.Contact, _ int) (AzureAsset, bool) { + if contact == nil { + return AzureAsset{}, false + } + return p.transformSecurityContract(contact, subscriptionID), true + }) + }), + ), nil } func (p *securityContactsProvider) transformSecurityContract(contact *armsecurity.Contact, subscriptionID string) AzureAsset { diff --git a/internal/resources/providers/azurelib/inventory/security_provider_test.go b/internal/resources/providers/azurelib/inventory/security_provider_test.go index ff926c70eb..e7c8d1ae08 100644 --- a/internal/resources/providers/azurelib/inventory/security_provider_test.go +++ b/internal/resources/providers/azurelib/inventory/security_provider_test.go @@ -40,17 +40,19 @@ func TestListSecurityContacts(t *testing.T) { return n } - response := func(c ...*armsecurity.Contact) armsecurity.ContactsClientListResponse { - return armsecurity.ContactsClientListResponse{ - ContactList: armsecurity.ContactList{ - Value: c, + response := func(c ...*armsecurity.Contact) []armsecurity.ContactsClientListResponse { + return []armsecurity.ContactsClientListResponse{ + { + ContactList: armsecurity.ContactList{ + Value: c, + }, }, } } tests := map[string]struct { inputSubID string - mockReturn armsecurity.ContactsClientListResponse + mockReturn []armsecurity.ContactsClientListResponse mockReturnError error expected []AzureAsset expectError bool diff --git a/internal/resources/providers/azurelib/provider.go b/internal/resources/providers/azurelib/provider.go index f3872772ba..6e67287607 100644 --- a/internal/resources/providers/azurelib/provider.go +++ b/internal/resources/providers/azurelib/provider.go @@ -20,7 +20,6 @@ package azurelib import ( "fmt" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph" "github.com/elastic/elastic-agent-libs/logp" @@ -64,11 +63,6 @@ func (p *ProviderInitializer) Init(log *logp.Logger, azureConfig auth.AzureFacto return nil, fmt.Errorf("failed to init monitor client: %w", err) } - genericARMClient, err := arm.NewClient("armsecurity-custom", "v0.0.1", azureConfig.Credentials, nil) - if err != nil { - return nil, fmt.Errorf("failed to init generic arm client: %w", err) - } - resourceGraphProvider := inventory.NewResourceGraphProvider(log, resourceGraphClientFactory) return &provider{ AppServiceProviderAPI: inventory.NewAppServiceProvider(log, azureConfig.Credentials), @@ -78,7 +72,7 @@ func (p *ProviderInitializer) Init(log *logp.Logger, azureConfig auth.AzureFacto ProviderAPI: governance.NewProvider(log, resourceGraphProvider), ResourceGraphProviderAPI: resourceGraphProvider, SQLProviderAPI: inventory.NewSQLProvider(log, azureConfig.Credentials), - SecurityContactsProviderAPI: inventory.NewSecurityContacts(log, azureConfig.Credentials, genericARMClient), + SecurityContactsProviderAPI: inventory.NewSecurityContacts(log, azureConfig.Credentials), StorageAccountProviderAPI: inventory.NewStorageAccountProvider(log, diagnosticSettingsClient, azureConfig.Credentials), SubscriptionProviderAPI: inventory.NewSubscriptionProvider(log, azureConfig.Credentials), }, nil