Skip to content
This repository has been archived by the owner on Jun 25, 2024. It is now read-only.

Commit

Permalink
Merge pull request #579 from JupiterOne/INT-7619-fix-duplicated-key
Browse files Browse the repository at this point in the history
INT-7619: fix duplicated Audit Config IAM Policy relationship key
  • Loading branch information
ndowmon authored Apr 6, 2023
2 parents 2ab8a4a + 4a83a10 commit 88c805c
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 26 deletions.
43 changes: 43 additions & 0 deletions src/steps/resource-manager/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
buildOrgFolderProjectMappedRelationships,
fetchIamPolicyAuditConfig,
AUDIT_CONFIG_ENTITY_TYPE,
flattenAuditLogConfigs,
} from '.';
import { integrationConfig } from '../../../test/config';
import {
Expand All @@ -31,6 +32,48 @@ import { fetchApiServices } from '../service-usage';
import { fetchIamManagedRoles, fetchIamServiceAccounts } from '../iam';
import { separateDirectMappedRelationships } from '../../../test/helpers/separateDirectMappedRelationships';

describe('flattenAuditLogConfigs', () => {
test('should flatten multiple log types onto the same exempted member', () => {
expect(
flattenAuditLogConfigs([
{ exemptedMembers: ['[email protected]', '[email protected]'], logType: 'type1' },
{ exemptedMembers: ['[email protected]', '[email protected]'], logType: 'type2' },
]),
).toEqual([
{ exemptedMember: '[email protected]', logTypes: ['type1', 'type2'] },
{ exemptedMember: '[email protected]', logTypes: ['type1', 'type2'] },
]);
});

test('should return empty array for `null` logType', () => {
expect(
flattenAuditLogConfigs([
{ exemptedMembers: ['[email protected]'], logType: null },
]),
).toEqual([{ exemptedMember: '[email protected]', logTypes: [] }]);
});

test('should return empty array for `undefined` logType', () => {
expect(
flattenAuditLogConfigs([
{ exemptedMembers: ['[email protected]'], logType: undefined },
]),
).toEqual([{ exemptedMember: '[email protected]', logTypes: [] }]);
});

test('should skip `null` exemptedMembers', () => {
expect(
flattenAuditLogConfigs([{ exemptedMembers: null, logType: 'type1' }]),
).toEqual([]);
});

test('should skip `undefined` exemptedMembers', () => {
expect(
flattenAuditLogConfigs([{ exemptedMembers: null, logType: 'type1' }]),
).toEqual([]);
});
});

describe('#fetchIamPolicyAuditConfig', () => {
let recording: Recording;

Expand Down
91 changes: 65 additions & 26 deletions src/steps/resource-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
} from '../service-usage/constants';
import { getServiceApiEntityKey } from '../service-usage/converters';
import { buildIamTargetRelationship } from '../cloud-asset';
import { cloudresourcemanager_v3 } from 'googleapis';

export * from './constants';

Expand Down Expand Up @@ -317,36 +318,74 @@ export async function fetchIamPolicyAuditConfig(
}
}

for (const auditLogConfig of auditConfig.auditLogConfigs || []) {
const exemptedMembers = auditLogConfig.exemptedMembers;
const logType = auditLogConfig.logType;
if (exemptedMembers) {
for (const exemptedMember of exemptedMembers) {
const parsedMember = parseIamMember(exemptedMember);
const { identifier: parsedIdentifier, type: parsedMemberType } =
parsedMember;
let principalEntity: Entity | null = null;
if (parsedIdentifier && parsedMemberType === 'serviceAccount') {
principalEntity = await jobState.findEntity(parsedIdentifier);
}
for (const { exemptedMember, logTypes } of flattenAuditLogConfigs(
auditConfig.auditLogConfigs || [],
)) {
const parsedMember = parseIamMember(exemptedMember);
const { identifier: parsedIdentifier, type: parsedMemberType } =
parsedMember;
let principalEntity: Entity | null = null;
if (parsedIdentifier && parsedMemberType === 'serviceAccount') {
principalEntity = await jobState.findEntity(parsedIdentifier);
}
const relationship = buildIamTargetRelationship({
fromEntity: auditConfigEntity,
principalEntity,
parsedMember,
logger,
projectId: client.projectId,
additionalProperties: { logTypes },
relationshipClass: RelationshipClass.ALLOWS,
});

if (relationship) {
await jobState.addRelationship(relationship);
}
}
});
}

const relationship = buildIamTargetRelationship({
fromEntity: auditConfigEntity,
principalEntity,
parsedMember,
logger,
projectId: client.projectId,
additionalProperties: { logType },
relationshipClass: RelationshipClass.ALLOWS,
});

if (relationship) {
await jobState.addRelationship(relationship);
}
/**
* Reorganizes from objects with an array of exemptedMembers and a single
* logType to a single exemptedMember and an array of logTypes
*
* [
* { exemptedMembers: ['dev@j1.io'], logType: 'type1' }
* { exemptedMembers: ['dev@j1.io'], logType: 'type2' },
* ]
*
* =>
*
* [
* { exemptedMember: ['dev@j1.io'], logTypes: ['type1', 'type2' ] }
* ]
*
*/
export function flattenAuditLogConfigs(
auditLogConfigs: cloudresourcemanager_v3.Schema$AuditLogConfig[],
): {
exemptedMember: string;
logTypes: string[];
}[] {
const exemptedMemberToLogTypesMap: { [exemptedMember: string]: string[] } =
{};
for (const { exemptedMembers, logType } of auditLogConfigs) {
for (const exemptedMember of exemptedMembers || []) {
if (exemptedMember) {
if (!exemptedMemberToLogTypesMap[exemptedMember]) {
exemptedMemberToLogTypesMap[exemptedMember] = [];
}

if (logType) {
exemptedMemberToLogTypesMap[exemptedMember].push(logType);
}
}
}
});
}

return Object.entries(exemptedMemberToLogTypesMap).map(
([exemptedMember, logTypes]) => ({ exemptedMember, logTypes }),
);
}

export const resourceManagerSteps: GoogleCloudIntegrationStep[] = [
Expand Down

0 comments on commit 88c805c

Please sign in to comment.