first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,742 @@
import { mockDeep } from 'jest-mock-extended';
import type { IExecuteFunctions, INode } from 'n8n-workflow';
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
import {
msGraphSecurityApiRequest,
tolerateDoubleQuotes,
throwOnEmptyUpdate,
} from '../GenericFunctions';
describe('Microsoft GraphSecurity GenericFunctions', () => {
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
let mockNode: INode;
let mockRequest: jest.Mock;
beforeEach(() => {
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
mockRequest = jest.fn();
mockExecuteFunctions.helpers.request = mockRequest;
mockNode = {
id: 'test-node',
name: 'Test GraphSecurity Node',
type: 'n8n-nodes-base.microsoftGraphSecurity',
typeVersion: 1,
position: [0, 0],
parameters: {},
};
mockExecuteFunctions.getNode.mockReturnValue(mockNode);
jest.clearAllMocks();
});
afterEach(() => {
jest.resetAllMocks();
});
describe('msGraphSecurityApiRequest', () => {
const mockCredentials = {
oauthTokenData: {
access_token: 'test-access-token',
},
};
beforeEach(() => {
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
});
describe('successful requests', () => {
it('should make a successful GET request with default parameters', async () => {
const mockResponse = { data: 'test data' };
mockRequest.mockResolvedValue(mockResponse);
const result = await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
expect(result).toEqual(mockResponse);
});
it('should make a POST request with body data', async () => {
const mockResponse = { id: '123', status: 'created' };
const requestBody = { name: 'Test Alert', status: 'active' };
mockRequest.mockResolvedValue(mockResponse);
const result = await msGraphSecurityApiRequest.call(
mockExecuteFunctions,
'POST',
'/alerts',
requestBody,
);
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'POST',
body: requestBody,
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
expect(result).toEqual(mockResponse);
});
it('should make a request with query string parameters', async () => {
const mockResponse = { alerts: [] };
const queryParams = { $filter: "status eq 'active'", $top: 10 };
mockRequest.mockResolvedValue(mockResponse);
const result = await msGraphSecurityApiRequest.call(
mockExecuteFunctions,
'GET',
'/alerts',
{},
queryParams,
);
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
qs: queryParams,
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
expect(result).toEqual(mockResponse);
});
it('should make a request with custom headers', async () => {
const mockResponse = { success: true };
const customHeaders = { 'Content-Type': 'application/json', 'X-Custom-Header': 'test' };
mockRequest.mockResolvedValue(mockResponse);
const result = await msGraphSecurityApiRequest.call(
mockExecuteFunctions,
'PUT',
'/secureScores',
{ data: 'test' },
{},
customHeaders,
);
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
'Content-Type': 'application/json',
'X-Custom-Header': 'test',
},
method: 'PUT',
body: { data: 'test' },
uri: 'https://graph.microsoft.com/v1.0/security/secureScores',
json: true,
});
expect(result).toEqual(mockResponse);
});
it('should handle all parameters together', async () => {
const mockResponse = { updated: true };
const body = { status: 'resolved' };
const qs = { $select: 'id,status' };
const headers = { 'If-Match': 'etag-value' };
mockRequest.mockResolvedValue(mockResponse);
const result = await msGraphSecurityApiRequest.call(
mockExecuteFunctions,
'PATCH',
'/alerts/123',
body,
qs,
headers,
);
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
'If-Match': 'etag-value',
},
method: 'PATCH',
body,
qs,
uri: 'https://graph.microsoft.com/v1.0/security/alerts/123',
json: true,
});
expect(result).toEqual(mockResponse);
});
it('should remove empty body when no body data is provided', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts', {});
const requestOptions = mockRequest.mock.calls[0][0];
expect(requestOptions.body).toBeUndefined();
});
it('should remove empty query string when no qs data is provided', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts', {}, {});
const requestOptions = mockRequest.mock.calls[0][0];
expect(requestOptions.qs).toBeUndefined();
});
});
describe('credential handling', () => {
it('should handle missing credentials', async () => {
mockExecuteFunctions.getCredentials.mockRejectedValue(new Error('Credentials not found'));
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts'),
).rejects.toThrow('Credentials not found');
});
it('should handle malformed credentials', async () => {
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {},
} as any);
mockRequest.mockResolvedValue({ data: 'test' });
const result = await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer undefined',
},
method: 'GET',
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
expect(result).toEqual({ data: 'test' });
});
});
describe('error handling', () => {
it('should handle basic API errors', async () => {
const apiError = {
error: {
error: {
message: 'Resource not found',
code: 'NotFound',
},
},
};
mockRequest.mockRejectedValue(apiError);
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts/invalid-id'),
).rejects.toThrow(NodeApiError);
});
it('should parse JSON error messages', async () => {
const jsonErrorMessage = '{"error":{"code":"InvalidRequest","message":"Invalid request"}}';
const apiError = {
error: {
error: {
message: jsonErrorMessage,
},
},
};
mockRequest.mockRejectedValue(apiError);
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts'),
).rejects.toThrow(NodeApiError);
});
it('should handle BadRequest errors', async () => {
const apiError = {
error: {
error: {
message: 'Http request failed with statusCode=BadRequest: Invalid filter',
},
},
};
mockRequest.mockRejectedValue(apiError);
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts'),
).rejects.toThrow(NodeApiError);
expect(apiError.error.error.message).toBe('Request failed with bad request');
});
it('should handle Http request failed errors with JSON content', async () => {
const jsonError = '{"error":{"code":"Forbidden","message":"Insufficient privileges"}}';
const apiError = {
error: {
error: {
message: `Http request failed with statusCode=403: ${jsonError}`,
},
},
};
mockRequest.mockRejectedValue(apiError);
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts'),
).rejects.toThrow(NodeApiError);
});
it('should handle Invalid filter clause errors', async () => {
const apiError = {
error: {
error: {
message: 'Invalid filter clause',
},
},
};
mockRequest.mockRejectedValue(apiError);
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts'),
).rejects.toThrow(NodeApiError);
expect(apiError.error.error.message).toContain(
'Please check that your query parameter syntax is correct',
);
});
it('should handle Invalid ODATA query filter errors', async () => {
const apiError = {
error: {
error: {
message: 'Invalid ODATA query filter',
},
},
};
mockRequest.mockRejectedValue(apiError);
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts'),
).rejects.toThrow(NodeApiError);
expect(apiError.error.error.message).toContain(
'Please check that your query parameter syntax is correct',
);
});
it('should handle errors without nested structure', async () => {
const simpleError = {
error: {
error: {
message: 'Simple network error',
},
},
};
mockRequest.mockRejectedValue(simpleError);
await expect(
msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts'),
).rejects.toThrow(NodeApiError);
});
});
describe('endpoint construction', () => {
it('should construct correct URL for different endpoints', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/secureScores');
expect(mockRequest).toHaveBeenCalledWith(
expect.objectContaining({
uri: 'https://graph.microsoft.com/v1.0/security/secureScores',
}),
);
});
it('should handle endpoints with parameters', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts/123/comments');
expect(mockRequest).toHaveBeenCalledWith(
expect.objectContaining({
uri: 'https://graph.microsoft.com/v1.0/security/alerts/123/comments',
}),
);
});
it('should handle endpoints without leading slash', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', 'alerts');
expect(mockRequest).toHaveBeenCalledWith(
expect.objectContaining({
uri: 'https://graph.microsoft.com/v1.0/securityalerts',
}),
);
});
});
describe('graphApiBaseUrl from credentials', () => {
it('should use base URL from credentials', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
graphApiBaseUrl: 'https://graph.microsoft.us',
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://graph.microsoft.us/v1.0/security/alerts',
json: true,
});
});
it('should fall back to default when credentials.graphApiBaseUrl is empty', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
graphApiBaseUrl: '',
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
});
it('should fall back to default when credentials.graphApiBaseUrl is undefined', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
});
it('should strip trailing slashes from base URL using regex', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
graphApiBaseUrl: 'https://graph.microsoft.com/',
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
});
it('should strip multiple trailing slashes from base URL', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
graphApiBaseUrl: 'https://graph.microsoft.com///',
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://graph.microsoft.com/v1.0/security/alerts',
json: true,
});
});
it('should use US Government cloud endpoint', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
graphApiBaseUrl: 'https://graph.microsoft.us',
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://graph.microsoft.us/v1.0/security/alerts',
json: true,
});
});
it('should use US Government DOD cloud endpoint', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
graphApiBaseUrl: 'https://dod-graph.microsoft.us',
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://dod-graph.microsoft.us/v1.0/security/alerts',
json: true,
});
});
it('should use China cloud endpoint', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
graphApiBaseUrl: 'https://microsoftgraph.chinacloudapi.cn',
});
await msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts');
expect(mockRequest).toHaveBeenCalledWith({
headers: {
Authorization: 'Bearer test-access-token',
},
method: 'GET',
uri: 'https://microsoftgraph.chinacloudapi.cn/v1.0/security/alerts',
json: true,
});
});
});
});
describe('tolerateDoubleQuotes', () => {
it('should replace double quotes with single quotes', () => {
const input = 'status eq "active" and severity eq "high"';
const expected = "status eq 'active' and severity eq 'high'";
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle multiple double quotes', () => {
const input = '"test" and "another" and "third"';
const expected = "'test' and 'another' and 'third'";
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle empty string', () => {
const input = '';
const expected = '';
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle string with no double quotes', () => {
const input = 'status eq active and severity eq high';
const expected = 'status eq active and severity eq high';
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle string with only single quotes', () => {
const input = "status eq 'active' and severity eq 'high'";
const expected = "status eq 'active' and severity eq 'high'";
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle mixed quotes', () => {
const input = 'status eq "active" and name eq \'test\' and type eq "alert"';
const expected = "status eq 'active' and name eq 'test' and type eq 'alert'";
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle escaped quotes', () => {
const input = 'description eq "He said \\"hello\\""';
const expected = "description eq 'He said \\'hello\\''";
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle special characters within quotes', () => {
const input = 'title eq "Alert: SQL Injection @#$%^&*()"';
const expected = "title eq 'Alert: SQL Injection @#$%^&*()'";
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
it('should handle very long strings', () => {
const longString = '"' + 'a'.repeat(1000) + '"';
const expectedString = "'" + 'a'.repeat(1000) + "'";
const result = tolerateDoubleQuotes(longString);
expect(result).toEqual(expectedString);
});
it('should handle unicode characters', () => {
const input = 'title eq "Alert: 测试 🚨 данные"';
const expected = "title eq 'Alert: 测试 🚨 данные'";
const result = tolerateDoubleQuotes(input);
expect(result).toEqual(expected);
});
});
describe('throwOnEmptyUpdate', () => {
it('should throw NodeOperationError with correct message', () => {
expect(() => {
throwOnEmptyUpdate.call(mockExecuteFunctions);
}).toThrow(NodeOperationError);
});
it('should throw with expected error message', () => {
expect(() => {
throwOnEmptyUpdate.call(mockExecuteFunctions);
}).toThrow('Please enter at least one field to update');
});
it('should use the correct node context', () => {
try {
throwOnEmptyUpdate.call(mockExecuteFunctions);
} catch (error) {
expect(mockExecuteFunctions.getNode).toHaveBeenCalled();
}
});
it('should always throw regardless of input', () => {
expect(() => {
throwOnEmptyUpdate.call(mockExecuteFunctions);
}).toThrow();
});
});
describe('Edge Cases and Integration', () => {
beforeEach(() => {
mockExecuteFunctions.getCredentials.mockResolvedValue({
oauthTokenData: {
access_token: 'test-access-token',
},
});
});
it('should handle concurrent requests', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
const promises: Array<Promise<any>> = [];
for (let i = 0; i < 5; i++) {
promises.push(msGraphSecurityApiRequest.call(mockExecuteFunctions, 'GET', '/alerts/' + i));
}
const results = await Promise.all(promises);
expect(results).toHaveLength(5);
expect(mockRequest).toHaveBeenCalledTimes(5);
});
it('should handle extremely large request bodies', async () => {
const mockResponse = { success: true };
const largeBody = { data: 'x'.repeat(10000) };
mockRequest.mockResolvedValue(mockResponse);
const result = await msGraphSecurityApiRequest.call(
mockExecuteFunctions,
'POST',
'/alerts',
largeBody,
);
expect(result).toEqual(mockResponse);
expect(mockRequest).toHaveBeenCalledWith(
expect.objectContaining({
body: largeBody,
}),
);
});
it('should handle empty parameters gracefully', async () => {
const mockResponse = { data: 'test' };
mockRequest.mockResolvedValue(mockResponse);
const result = await msGraphSecurityApiRequest.call(
mockExecuteFunctions,
'GET',
'/alerts',
{},
{},
{},
);
expect(result).toEqual(mockResponse);
});
});
});
@@ -0,0 +1,47 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('Test MicrosoftGraphSecurity, secureScore => get', () => {
const credentials = {
microsoftGraphSecurityOAuth2Api: {
oauthTokenData: {
access_token: 'test-access-token',
},
},
};
beforeAll(() => {
nock('https://graph.microsoft.com')
.get('/v1.0/security/secureScores/test-secure-score-id')
.matchHeader('Authorization', 'Bearer test-access-token')
.reply(200, {
'@odata.context':
'https://graph.microsoft.com/v1.0/$metadata#security/secureScores/$entity',
id: 'test-secure-score-id',
azureTenantId: 'tenant-123',
activeUserCount: 100,
createdDateTime: '2023-01-01T00:00:00Z',
currentScore: 85,
maxScore: 100,
averageComparativeScores: [
{
basis: 'AllTenants',
averageScore: 75.5,
},
],
controlScores: [
{
controlName: 'Enable MFA',
controlCategory: 'Identity',
score: 10,
maxScore: 10,
},
],
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['secureScore.get.workflow.json'],
});
});
@@ -0,0 +1,82 @@
{
"name": "Microsoft GraphSecurity SecureScore Get Test",
"nodes": [
{
"parameters": {},
"id": "trigger-id",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [820, 360]
},
{
"parameters": {
"resource": "secureScore",
"operation": "get",
"secureScoreId": "test-secure-score-id"
},
"id": "node-id",
"name": "Microsoft Graph Security",
"type": "n8n-nodes-base.microsoftGraphSecurity",
"typeVersion": 1,
"position": [1040, 360],
"credentials": {
"microsoftGraphSecurityOAuth2Api": {
"id": "credential-id",
"name": "Microsoft Graph Security OAuth2"
}
}
}
],
"pinData": {
"Microsoft Graph Security": [
{
"json": {
"id": "test-secure-score-id",
"azureTenantId": "tenant-123",
"activeUserCount": 100,
"createdDateTime": "2023-01-01T00:00:00Z",
"currentScore": 85,
"maxScore": 100,
"averageComparativeScores": [
{
"basis": "AllTenants",
"averageScore": 75.5
}
],
"controlScores": [
{
"controlName": "Enable MFA",
"controlCategory": "Identity",
"score": 10,
"maxScore": 10
}
]
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Microsoft Graph Security",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"id": "test-workflow-id",
"meta": {
"instanceId": "test-instance-id"
},
"tags": []
}
@@ -0,0 +1,72 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('Test MicrosoftGraphSecurity, secureScore => getAll', () => {
const credentials = {
microsoftGraphSecurityOAuth2Api: {
oauthTokenData: {
access_token: 'test-access-token',
},
},
};
beforeAll(() => {
nock('https://graph.microsoft.com')
.get('/v1.0/security/secureScores')
.matchHeader('Authorization', 'Bearer test-access-token')
.reply(200, {
'@odata.context': 'https://graph.microsoft.com/v1.0/$metadata#security/secureScores',
value: [
{
id: 'test-secure-score-1',
azureTenantId: 'tenant-123',
activeUserCount: 100,
createdDateTime: '2023-01-01T00:00:00Z',
currentScore: 85,
maxScore: 100,
averageComparativeScores: [
{
basis: 'AllTenants',
averageScore: 75.5,
},
],
controlScores: [
{
controlName: 'Enable MFA',
controlCategory: 'Identity',
score: 10,
maxScore: 10,
},
],
},
{
id: 'test-secure-score-2',
azureTenantId: 'tenant-456',
activeUserCount: 200,
createdDateTime: '2023-01-02T00:00:00Z',
currentScore: 90,
maxScore: 100,
averageComparativeScores: [
{
basis: 'AllTenants',
averageScore: 78.2,
},
],
controlScores: [
{
controlName: 'Enable Conditional Access',
controlCategory: 'Identity',
score: 15,
maxScore: 15,
},
],
},
],
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['secureScore.getAll.workflow.json'],
});
});
@@ -0,0 +1,90 @@
{
"name": "Microsoft GraphSecurity SecureScore GetAll Test",
"nodes": [
{
"parameters": {},
"id": "trigger-id",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [820, 360]
},
{
"parameters": {
"resource": "secureScore",
"operation": "getAll",
"returnAll": true
},
"id": "node-id",
"name": "Microsoft Graph Security",
"type": "n8n-nodes-base.microsoftGraphSecurity",
"typeVersion": 1,
"position": [1040, 360],
"credentials": {
"microsoftGraphSecurityOAuth2Api": {
"id": "credential-id",
"name": "Microsoft Graph Security OAuth2"
}
}
}
],
"pinData": {
"Microsoft Graph Security": [
{
"json": {
"id": "test-secure-score-1",
"azureTenantId": "tenant-123",
"activeUserCount": 100,
"createdDateTime": "2023-01-01T00:00:00Z",
"currentScore": 85,
"maxScore": 100,
"averageComparativeScores": [
{
"basis": "AllTenants",
"averageScore": 75.5
}
]
}
},
{
"json": {
"id": "test-secure-score-2",
"azureTenantId": "tenant-456",
"activeUserCount": 200,
"createdDateTime": "2023-01-02T00:00:00Z",
"currentScore": 90,
"maxScore": 100,
"averageComparativeScores": [
{
"basis": "AllTenants",
"averageScore": 78.2
}
]
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Microsoft Graph Security",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"id": "test-workflow-id",
"meta": {
"instanceId": "test-instance-id"
},
"tags": []
}
@@ -0,0 +1,48 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('Test MicrosoftGraphSecurity, secureScoreControlProfile => get', () => {
const credentials = {
microsoftGraphSecurityOAuth2Api: {
oauthTokenData: {
access_token: 'test-access-token',
},
},
};
beforeAll(() => {
nock('https://graph.microsoft.com')
.get('/v1.0/security/secureScoreControlProfiles/test-control-profile-id')
.matchHeader('Authorization', 'Bearer test-access-token')
.reply(200, {
'@odata.context':
'https://graph.microsoft.com/v1.0/$metadata#security/secureScoreControlProfiles/$entity',
id: 'test-control-profile-id',
azureTenantId: 'tenant-123',
controlName: 'Enable multifactor authentication',
controlCategory: 'Identity',
actionType: 'Config',
service: 'AAD',
maxScore: 10,
tier: 'Core',
userImpact: 'Low',
implementationCost: 'Low',
rank: 1,
threats: ['Account Breach', 'Credential Theft'],
deprecated: false,
remediation: 'Enable multi-factor authentication for all users',
remediationImpact: 'Users will need to use an additional authentication method',
actionUrl: 'https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/MFA',
controlStateUpdates: [],
vendorInformation: {
provider: 'Microsoft',
vendor: 'Microsoft',
},
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['secureScoreControlProfile.get.workflow.json'],
});
});
@@ -0,0 +1,83 @@
{
"name": "Microsoft GraphSecurity SecureScoreControlProfile Get Test",
"nodes": [
{
"parameters": {},
"id": "trigger-id",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [820, 360]
},
{
"parameters": {
"resource": "secureScoreControlProfile",
"operation": "get",
"secureScoreControlProfileId": "test-control-profile-id"
},
"id": "node-id",
"name": "Microsoft Graph Security",
"type": "n8n-nodes-base.microsoftGraphSecurity",
"typeVersion": 1,
"position": [1040, 360],
"credentials": {
"microsoftGraphSecurityOAuth2Api": {
"id": "credential-id",
"name": "Microsoft Graph Security OAuth2"
}
}
}
],
"pinData": {
"Microsoft Graph Security": [
{
"json": {
"id": "test-control-profile-id",
"azureTenantId": "tenant-123",
"controlName": "Enable multifactor authentication",
"controlCategory": "Identity",
"actionType": "Config",
"service": "AAD",
"maxScore": 10,
"tier": "Core",
"userImpact": "Low",
"implementationCost": "Low",
"rank": 1,
"threats": ["Account Breach", "Credential Theft"],
"deprecated": false,
"remediation": "Enable multi-factor authentication for all users",
"remediationImpact": "Users will need to use an additional authentication method",
"actionUrl": "https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/MFA",
"controlStateUpdates": [],
"vendorInformation": {
"provider": "Microsoft",
"vendor": "Microsoft"
}
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Microsoft Graph Security",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"id": "test-workflow-id",
"meta": {
"instanceId": "test-instance-id"
},
"tags": []
}
@@ -0,0 +1,77 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('Test MicrosoftGraphSecurity, secureScoreControlProfile => getAll', () => {
const credentials = {
microsoftGraphSecurityOAuth2Api: {
oauthTokenData: {
access_token: 'test-access-token',
},
},
};
beforeAll(() => {
nock('https://graph.microsoft.com')
.get('/v1.0/security/secureScoreControlProfiles')
.matchHeader('Authorization', 'Bearer test-access-token')
.reply(200, {
'@odata.context':
'https://graph.microsoft.com/v1.0/$metadata#security/secureScoreControlProfiles',
value: [
{
id: 'test-control-profile-1',
azureTenantId: 'tenant-123',
controlName: 'Enable multifactor authentication',
controlCategory: 'Identity',
actionType: 'Config',
service: 'AAD',
maxScore: 10,
tier: 'Core',
userImpact: 'Low',
implementationCost: 'Low',
rank: 1,
threats: ['Account Breach', 'Credential Theft'],
deprecated: false,
remediation: 'Enable multi-factor authentication for all users',
remediationImpact: 'Users will need to use an additional authentication method',
actionUrl:
'https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/MFA',
controlStateUpdates: [],
vendorInformation: {
provider: 'Microsoft',
vendor: 'Microsoft',
},
},
{
id: 'test-control-profile-2',
azureTenantId: 'tenant-456',
controlName: 'Enable conditional access',
controlCategory: 'Identity',
actionType: 'Config',
service: 'AAD',
maxScore: 15,
tier: 'Core',
userImpact: 'Medium',
implementationCost: 'Medium',
rank: 2,
threats: ['Account Breach', 'Data Exfiltration'],
deprecated: false,
remediation: 'Configure conditional access policies',
remediationImpact: 'Users may need to authenticate differently based on location',
actionUrl:
'https://portal.azure.com/#blade/Microsoft_AAD_ConditionalAccess/ConditionalAccessBlade',
controlStateUpdates: [],
vendorInformation: {
provider: 'Microsoft',
vendor: 'Microsoft',
},
},
],
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['secureScoreControlProfile.getAll.workflow.json'],
});
});
@@ -0,0 +1,108 @@
{
"name": "Microsoft GraphSecurity SecureScoreControlProfile GetAll Test",
"nodes": [
{
"parameters": {},
"id": "trigger-id",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [820, 360]
},
{
"parameters": {
"resource": "secureScoreControlProfile",
"operation": "getAll",
"returnAll": true
},
"id": "node-id",
"name": "Microsoft Graph Security",
"type": "n8n-nodes-base.microsoftGraphSecurity",
"typeVersion": 1,
"position": [1040, 360],
"credentials": {
"microsoftGraphSecurityOAuth2Api": {
"id": "credential-id",
"name": "Microsoft Graph Security OAuth2"
}
}
}
],
"pinData": {
"Microsoft Graph Security": [
{
"json": {
"id": "test-control-profile-1",
"azureTenantId": "tenant-123",
"controlName": "Enable multifactor authentication",
"controlCategory": "Identity",
"actionType": "Config",
"service": "AAD",
"maxScore": 10,
"tier": "Core",
"userImpact": "Low",
"implementationCost": "Low",
"rank": 1,
"threats": ["Account Breach", "Credential Theft"],
"deprecated": false,
"remediation": "Enable multi-factor authentication for all users",
"remediationImpact": "Users will need to use an additional authentication method",
"actionUrl": "https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/MFA",
"controlStateUpdates": [],
"vendorInformation": {
"provider": "Microsoft",
"vendor": "Microsoft"
}
}
},
{
"json": {
"id": "test-control-profile-2",
"azureTenantId": "tenant-456",
"controlName": "Enable conditional access",
"controlCategory": "Identity",
"actionType": "Config",
"service": "AAD",
"maxScore": 15,
"tier": "Core",
"userImpact": "Medium",
"implementationCost": "Medium",
"rank": 2,
"threats": ["Account Breach", "Data Exfiltration"],
"deprecated": false,
"remediation": "Configure conditional access policies",
"remediationImpact": "Users may need to authenticate differently based on location",
"actionUrl": "https://portal.azure.com/#blade/Microsoft_AAD_ConditionalAccess/ConditionalAccessBlade",
"controlStateUpdates": [],
"vendorInformation": {
"provider": "Microsoft",
"vendor": "Microsoft"
}
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Microsoft Graph Security",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"id": "test-workflow-id",
"meta": {
"instanceId": "test-instance-id"
},
"tags": []
}
@@ -0,0 +1,56 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('Test MicrosoftGraphSecurity, secureScoreControlProfile => update', () => {
const credentials = {
microsoftGraphSecurityOAuth2Api: {
oauthTokenData: {
access_token: 'test-access-token',
},
},
};
beforeAll(() => {
nock('https://graph.microsoft.com')
.patch('/v1.0/security/secureScoreControlProfiles/test-control-profile-id', {
vendorInformation: {
provider: 'Microsoft',
vendor: 'Microsoft',
},
state: 'Ignored',
})
.matchHeader('Authorization', 'Bearer test-access-token')
.matchHeader('Prefer', 'return=representation')
.reply(200, {
'@odata.context':
'https://graph.microsoft.com/v1.0/$metadata#security/secureScoreControlProfiles/$entity',
id: 'test-control-profile-id',
azureTenantId: 'tenant-123',
controlName: 'Enable multifactor authentication',
controlCategory: 'Identity',
actionType: 'Config',
service: 'AAD',
maxScore: 10,
tier: 'Core',
userImpact: 'Low',
implementationCost: 'Low',
rank: 1,
threats: ['Account Breach', 'Credential Theft'],
deprecated: false,
remediation: 'Enable multi-factor authentication for all users',
remediationImpact: 'Users will need to use an additional authentication method',
actionUrl: 'https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/MFA',
controlStateUpdates: [],
state: 'Ignored',
vendorInformation: {
provider: 'Microsoft',
vendor: 'Microsoft',
},
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['secureScoreControlProfile.update.workflow.json'],
});
});
@@ -0,0 +1,89 @@
{
"name": "Microsoft GraphSecurity SecureScoreControlProfile Update Test",
"nodes": [
{
"parameters": {},
"id": "trigger-id",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [820, 360]
},
{
"parameters": {
"resource": "secureScoreControlProfile",
"operation": "update",
"secureScoreControlProfileId": "test-control-profile-id",
"provider": "Microsoft",
"vendor": "Microsoft",
"updateFields": {
"state": "Ignored"
}
},
"id": "node-id",
"name": "Microsoft Graph Security",
"type": "n8n-nodes-base.microsoftGraphSecurity",
"typeVersion": 1,
"position": [1040, 360],
"credentials": {
"microsoftGraphSecurityOAuth2Api": {
"id": "credential-id",
"name": "Microsoft Graph Security OAuth2"
}
}
}
],
"pinData": {
"Microsoft Graph Security": [
{
"json": {
"id": "test-control-profile-id",
"azureTenantId": "tenant-123",
"controlName": "Enable multifactor authentication",
"controlCategory": "Identity",
"actionType": "Config",
"service": "AAD",
"maxScore": 10,
"tier": "Core",
"userImpact": "Low",
"implementationCost": "Low",
"rank": 1,
"threats": ["Account Breach", "Credential Theft"],
"deprecated": false,
"remediation": "Enable multi-factor authentication for all users",
"remediationImpact": "Users will need to use an additional authentication method",
"actionUrl": "https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/MFA",
"controlStateUpdates": [],
"state": "Ignored",
"vendorInformation": {
"provider": "Microsoft",
"vendor": "Microsoft"
}
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Microsoft Graph Security",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"id": "test-workflow-id",
"meta": {
"instanceId": "test-instance-id"
},
"tags": []
}