ownerlens 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +209 -0
  3. package/bin/ownerlens.js +92 -0
  4. package/dist/assets/index-B9aAYpVl.css +1 -0
  5. package/dist/assets/index-BcwLk2bx.js +10 -0
  6. package/dist/index.html +13 -0
  7. package/package.json +73 -0
  8. package/src/App.tsx +18 -0
  9. package/src/components/azure/AzureComponent.test.tsx +625 -0
  10. package/src/components/azure/AzureComponent.tsx +189 -0
  11. package/src/components/azure/AzureRbacComponent.tsx +104 -0
  12. package/src/components/azure/ClosableAzureTab.tsx +42 -0
  13. package/src/components/azure/EntraPermissionsComponent.tsx +194 -0
  14. package/src/components/azure/ManagedIdentityComponent.test.tsx +324 -0
  15. package/src/components/azure/ManagedIdentityComponent.tsx +141 -0
  16. package/src/components/azure/ResourceGroupComponent.tsx +157 -0
  17. package/src/components/azure/ServicePrincipalComponent.test.tsx +457 -0
  18. package/src/components/azure/ServicePrincipalComponent.tsx +155 -0
  19. package/src/components/azure/ServicePrincipalFieldRenderers.tsx +140 -0
  20. package/src/components/azure/ZtaComponent.test.tsx +267 -0
  21. package/src/components/azure/ZtaComponent.tsx +276 -0
  22. package/src/components/azure/ZtaRemediationBadge.tsx +70 -0
  23. package/src/components/azure/api.ts +216 -0
  24. package/src/components/azure/azureReportConfig.ts +247 -0
  25. package/src/core/azure/azureRbac.ts +70 -0
  26. package/src/core/azure/entra/index.ts +1 -0
  27. package/src/core/azure/entra/managedIdentity.ts +21 -0
  28. package/src/core/azure/entra/servicePrincipal.ts +34 -0
  29. package/src/core/azure/entra/types.ts +56 -0
  30. package/src/core/azure/identityEnrichment.ts +65 -0
  31. package/src/core/azure/resources.ts +141 -0
  32. package/src/core/azure/ztaReport.ts +58 -0
  33. package/src/core/config.ts +39 -0
  34. package/src/core/ownership/OwnershipTarget.ts +32 -0
  35. package/src/core/ownership/resolveOwner.ts +5 -0
  36. package/src/core/ownership/types.ts +14 -0
  37. package/src/core/risk/types.ts +1 -0
  38. package/src/core/runtime/index.ts +1 -0
  39. package/src/core/runtime/localSnapshotFiles.ts +74 -0
  40. package/src/core/runtime/rest.ts +61 -0
  41. package/src/lib/searchFilterUtils.ts +17 -0
  42. package/src/lib/utils.ts +48 -0
  43. package/src/main.tsx +10 -0
  44. package/src/providers/azure/identities/azureIdentityTypes.ts +1 -0
  45. package/src/providers/azure/identities/buildAzureManagedIdentityAssignmentIndex.test.ts +32 -0
  46. package/src/providers/azure/identities/buildAzureManagedIdentityAssignmentIndex.ts +35 -0
  47. package/src/providers/azure/identities/userAssignedIdentityAssignments.ts +52 -0
  48. package/src/providers/azure/inputTransferObject/entra/EntraAppRoleAssignment.ts +10 -0
  49. package/src/providers/azure/inputTransferObject/entra/EntraApplication.ts +27 -0
  50. package/src/providers/azure/inputTransferObject/entra/EntraOAuth2PermissionGrant.ts +8 -0
  51. package/src/providers/azure/inputTransferObject/entra/EntraServicePrincipal.ts +43 -0
  52. package/src/providers/azure/inputTransferObject/entra/EntraSnapshot.ts +13 -0
  53. package/src/providers/azure/inputTransferObject/entra/EntraSnapshotMeta.ts +12 -0
  54. package/src/providers/azure/inputTransferObject/resources/AzureActivityLog.ts +1 -0
  55. package/src/providers/azure/inputTransferObject/resources/AzureResource.ts +1 -0
  56. package/src/providers/azure/inputTransferObject/resources/AzureResourceGroup.ts +1 -0
  57. package/src/providers/azure/inputTransferObject/resources/AzureRoleAssignment.ts +1 -0
  58. package/src/providers/azure/inputTransferObject/resources/AzureSnapshot.ts +1 -0
  59. package/src/providers/azure/inputTransferObject/resources/AzureSnapshotMeta.ts +1 -0
  60. package/src/providers/azure/inputTransferObject/resources/AzureSubscription.ts +1 -0
  61. package/src/providers/azure/inputTransferObject/resources/AzureUserAssignedManagedIdentity.ts +1 -0
  62. package/src/providers/azure/ownership/azureActivityOwnershipEvidence.ts +60 -0
  63. package/src/providers/azure/ownership/azureOwnerReportTypes.ts +13 -0
  64. package/src/providers/azure/ownership/azureOwnershipConfig.ts +21 -0
  65. package/src/providers/azure/ownership/azureOwnershipTypes.ts +46 -0
  66. package/src/providers/azure/ownership/buildAzureOwnershipReport.test.ts +99 -0
  67. package/src/providers/azure/ownership/buildAzureOwnershipReport.ts +90 -0
  68. package/src/providers/azure/ownership/buildAzureOwnershipTargets.test.ts +87 -0
  69. package/src/providers/azure/ownership/buildAzureOwnershipTargets.ts +42 -0
  70. package/src/providers/azure/ownership/resolveAzureOwner.ts +146 -0
  71. package/src/providers/azure/runtime/DisabledEvidenceStore.ts +34 -0
  72. package/src/providers/azure/runtime/EnrichmentService.ts +35 -0
  73. package/src/providers/azure/runtime/LocalReportRuntime.test.ts +2318 -0
  74. package/src/providers/azure/runtime/LocalReportRuntime.ts +302 -0
  75. package/src/providers/azure/runtime/RuntimeHost.ts +60 -0
  76. package/src/providers/azure/runtime/SnapshotImporter.ts +44 -0
  77. package/src/providers/azure/runtime/enrichment/azureIdentityEnrichment.ts +523 -0
  78. package/src/providers/azure/runtime/enrichment/azureScopeClassifier.ts +30 -0
  79. package/src/providers/azure/runtime/enrichment/evaluateAzureRoleAssignmentRisk.ts +88 -0
  80. package/src/providers/azure/runtime/entra/EntraCollectionQueryService.ts +307 -0
  81. package/src/providers/azure/runtime/entra/LocalEntraReportRuntime.ts +227 -0
  82. package/src/providers/azure/runtime/entra/appRoleAssignmentsTable.ts +52 -0
  83. package/src/providers/azure/runtime/entra/applicationsTable.ts +175 -0
  84. package/src/providers/azure/runtime/entra/entraServicePrincipalMapper.ts +63 -0
  85. package/src/providers/azure/runtime/entra/localReportRuntimeRest.ts +41 -0
  86. package/src/providers/azure/runtime/entra/oauth2PermissionGrantsTable.ts +48 -0
  87. package/src/providers/azure/runtime/entra/principalProjection.ts +173 -0
  88. package/src/providers/azure/runtime/entra/servicePrincipalsTable.ts +149 -0
  89. package/src/providers/azure/runtime/entra/snapshotMetadataTable.ts +18 -0
  90. package/src/providers/azure/runtime/entra/snapshotStore.ts +102 -0
  91. package/src/providers/azure/runtime/localReportCollections.ts +101 -0
  92. package/src/providers/azure/runtime/localReportRuntimeRest.ts +71 -0
  93. package/src/providers/azure/runtime/resources/AzureResourcesCollectionQueryService.ts +145 -0
  94. package/src/providers/azure/runtime/resources/LocalAzureResourcesReportRuntime.ts +114 -0
  95. package/src/providers/azure/runtime/resources/disabledOwnerEvidenceTable.ts +60 -0
  96. package/src/providers/azure/runtime/resources/localReportRuntimeRest.ts +81 -0
  97. package/src/providers/azure/runtime/resources/resourceGroupOwnership.ts +90 -0
  98. package/src/providers/azure/runtime/resources/snapshotMetadataTable.ts +19 -0
  99. package/src/providers/azure/runtime/resources/snapshotStore.ts +128 -0
  100. package/src/providers/azure/runtime/resources/tables.ts +441 -0
  101. package/src/providers/azure/runtime/runtimeRestQuery.ts +46 -0
  102. package/src/providers/azure/runtime/runtimeSqlSchema.ts +357 -0
  103. package/src/providers/azure/runtime/zta/Discovery.ts +141 -0
  104. package/src/providers/azure/runtime/zta/LocalZeroTrustAssessmentReportRuntime.ts +86 -0
  105. package/src/providers/azure/runtime/zta/ZeroTrustAssessmentQueryService.ts +124 -0
  106. package/src/providers/azure/runtime/zta/localReportRuntimeRest.ts +15 -0
  107. package/src/providers/azure/runtime/zta/snapshotMetadataTable.ts +77 -0
  108. package/src/providers/azure/runtime/zta/snapshotStore.ts +112 -0
  109. package/src/providers/azure/runtime/zta/tables.ts +361 -0
  110. package/src/providers/azure/runtime/zta/types.ts +7 -0
  111. package/src/providers/azure/runtime/zta/ztaReportMapper.ts +12 -0
  112. package/src/report/applyCollectionControls.ts +289 -0
  113. package/src/report/buildCollectionColumns.tsx +38 -0
  114. package/src/report/components/ConfidenceBadge.tsx +10 -0
  115. package/src/report/components/EvidenceList.test.ts +25 -0
  116. package/src/report/components/EvidenceList.tsx +52 -0
  117. package/src/report/components/GenericTable.tsx +373 -0
  118. package/src/report/components/PermissionRiskBadge.tsx +19 -0
  119. package/src/report/components/reportTableControls.test.ts +175 -0
  120. package/src/report/components/reportTableControls.tsx +483 -0
  121. package/src/report/components/ui/badge.tsx +35 -0
  122. package/src/report/components/ui/button.tsx +38 -0
  123. package/src/report/components/ui/card.tsx +23 -0
  124. package/src/report/components/ui/input.tsx +15 -0
  125. package/src/report/components/ui/table.tsx +44 -0
  126. package/src/report/components/ui/tabs.tsx +29 -0
  127. package/src/report/export/csv.ts +34 -0
  128. package/src/report/ownerManualPrecheck.test.ts +137 -0
  129. package/src/report/ownerManualPrecheck.ts +132 -0
  130. package/src/report/reportArchitecture.test.ts +125 -0
  131. package/src/report/reportTypes.ts +54 -0
  132. package/src/report/reportValueRenderers.tsx +54 -0
  133. package/src/report/runtimeCollectionQuery.ts +23 -0
  134. package/src/report/types.ts +14 -0
  135. package/src/styles.css +43 -0
  136. package/tools/README.md +108 -0
  137. package/tools/azure-activity-check.ps1 +164 -0
  138. package/tools/collect-azure.ps1 +54 -0
  139. package/tools/collect-entra.ps1 +47 -0
  140. package/tools/collect-scripts.test.ts +22 -0
  141. package/tools/prepare-entra-snapshot.ps1 +403 -0
  142. package/tools/prepare-entra-snapshot.test.ts +14 -0
  143. package/tools/prepare-resource-snapshot.ps1 +345 -0
  144. package/vite.config.ts +23 -0
@@ -0,0 +1,42 @@
1
+ import type { OwnershipTarget, OwnershipTargetRef } from "../../../core/ownership/OwnershipTarget";
2
+ import type { EntraServicePrincipal } from "../inputTransferObject/entra/EntraServicePrincipal";
3
+ import type { AzureUserAssignedManagedIdentity } from "../../../core/azure/resources";
4
+
5
+ export function buildAzureManagedIdentityOwnershipTargets(
6
+ managedIdentities: AzureUserAssignedManagedIdentity[]
7
+ ): OwnershipTarget[] {
8
+ return managedIdentities.map((identity) => ({
9
+ id: identity.resourceId,
10
+ kind: "azure.managedIdentity",
11
+ displayName: identity.name,
12
+ sourceProvider: "azure",
13
+ technicalId: identity.principalId,
14
+ refs: compactRefs([
15
+ { type: "azure.subscription", id: identity.subscriptionId, label: identity.subscriptionName },
16
+ { type: "azure.resourceGroup", id: identity.resourceGroup },
17
+ { type: "entra.servicePrincipal", id: identity.principalId },
18
+ { type: "entra.application", id: identity.clientId },
19
+ { type: "entra.tenant", id: identity.tenantId }
20
+ ])
21
+ }));
22
+ }
23
+
24
+ export function buildEntraServicePrincipalOwnershipTargets(
25
+ servicePrincipals: EntraServicePrincipal[]
26
+ ): OwnershipTarget[] {
27
+ return servicePrincipals.map((servicePrincipal) => ({
28
+ id: servicePrincipal.id,
29
+ kind: "entra.servicePrincipal",
30
+ displayName: servicePrincipal.displayName,
31
+ sourceProvider: "entra",
32
+ technicalId: servicePrincipal.appId,
33
+ refs: compactRefs([
34
+ { type: "entra.application", id: servicePrincipal.appId, label: servicePrincipal.appDisplayName ?? undefined },
35
+ { type: "entra.tenant", id: servicePrincipal.appOwnerOrganizationId ?? "" }
36
+ ])
37
+ }));
38
+ }
39
+
40
+ function compactRefs(refs: OwnershipTargetRef[]): OwnershipTargetRef[] {
41
+ return refs.filter((ref) => ref.id.trim().length > 0);
42
+ }
@@ -0,0 +1,146 @@
1
+ import type { AzureActivityLog, AzureResourceGroup, AzureResourceTags } from "../../../core/azure/resources";
2
+ import type { OwnerEvidence, OwnerResolution } from "../../../core/ownership/types";
3
+ import {
4
+ compareLogsNewestFirst,
5
+ describeIdentity,
6
+ getActivityIndexKey,
7
+ getTagValue,
8
+ isOwnerActivity,
9
+ normalizeOwner
10
+ } from "./azureActivityOwnershipEvidence";
11
+ import type { ActivityLogIndex, OwnerResolverAdapter, OwnerResolverContext } from "./azureOwnershipTypes";
12
+
13
+ type AzureResourceWithTags = {
14
+ tags: AzureResourceTags | null;
15
+ };
16
+
17
+ export const azureOwnerAdapter: OwnerResolverAdapter = {
18
+ resolveOwner(target, context) {
19
+ if (target.kind === "resourceGroup") {
20
+ const owner = resolveOwnerFromTags(target.resourceGroup, context);
21
+ if (owner === null) {
22
+ return resolveResourceGroupOwnerFromActivity(target.resourceGroup, context);
23
+ }
24
+
25
+ return owner;
26
+ }
27
+
28
+ const owner = resolveOwnerFromTags(target.subscription, context);
29
+ if (owner === null) {
30
+ return resolveSubscriptionOwner(target.subscription.subscriptionId, context);
31
+ }
32
+
33
+ return owner;
34
+ }
35
+ };
36
+
37
+ function resolveOwnerFromTags(resource: AzureResourceWithTags, context: OwnerResolverContext): OwnerResolution | null {
38
+ for (const [tagName, tagConfig] of Object.entries(context.tags)) {
39
+ const tagValue = getTagValue(resource.tags, tagName);
40
+ if (!tagValue) {
41
+ continue;
42
+ }
43
+
44
+ return {
45
+ ...tagConfig,
46
+ owner: normalizeOwner(tagValue),
47
+ source: `tag.${tagName}`,
48
+ evidence: [buildTagEvidence(tagName, tagValue)]
49
+ };
50
+ }
51
+
52
+ return null;
53
+ }
54
+
55
+ function buildTagEvidence(tagName: string, tagValue: string): OwnerEvidence {
56
+ return {
57
+ user: `${tagName}=${tagValue}`,
58
+ date: null
59
+ };
60
+ }
61
+
62
+ function resolveResourceGroupOwnerFromActivity(
63
+ resourceGroup: AzureResourceGroup,
64
+ context: OwnerResolverContext
65
+ ): OwnerResolution {
66
+ const logs = context.activityLogIndex.get(getActivityIndexKey(resourceGroup.subscriptionId, resourceGroup.resourceGroup)) ?? [];
67
+ return resolveFromActivity(logs, context, "activity.lastModifier");
68
+ }
69
+
70
+ function resolveSubscriptionOwner(subscriptionId: string, context: OwnerResolverContext): OwnerResolution {
71
+ const logs = context.resourceSnapshot.activityLogs.filter(
72
+ (log) => log.subscriptionId.toLowerCase() === subscriptionId.toLowerCase()
73
+ );
74
+ return resolveFromActivity(logs, context, "activity.subscriptionLastModifier");
75
+ }
76
+
77
+ function resolveFromActivity(
78
+ logs: AzureActivityLog[],
79
+ context: OwnerResolverContext,
80
+ source: string
81
+ ): OwnerResolution {
82
+ const lastWrite = logs.filter(isOwnerActivity).sort(compareLogsNewestFirst)[0];
83
+
84
+ if (!lastWrite?.caller) {
85
+ return {
86
+ owner: null,
87
+ confidence: "none",
88
+ source: "none",
89
+ evidence: []
90
+ };
91
+ }
92
+
93
+ const owner = describeIdentity(lastWrite.caller, context.servicePrincipalIndex);
94
+ const evidence = getLastActionByCallerEvidence(logs, context.servicePrincipalIndex);
95
+
96
+ return {
97
+ owner,
98
+ confidence: "low",
99
+ source,
100
+ evidence
101
+ };
102
+ }
103
+
104
+ function getLastActionByCallerEvidence(
105
+ logs: AzureActivityLog[],
106
+ servicePrincipalIndex: OwnerResolverContext["servicePrincipalIndex"]
107
+ ): OwnerEvidence[] {
108
+ const lastActionByCaller = new Map<string, AzureActivityLog>();
109
+
110
+ for (const log of logs.filter(isOwnerActivity)) {
111
+ const caller = normalizeOwner(log.caller ?? "");
112
+ const previous = lastActionByCaller.get(caller);
113
+
114
+ if (!previous || compareLogsNewestFirst(log, previous) < 0) {
115
+ lastActionByCaller.set(caller, log);
116
+ }
117
+ }
118
+
119
+ return [...lastActionByCaller.values()]
120
+ .sort(compareLogsNewestFirst)
121
+ .map((log) => ({
122
+ user: getEvidenceCallerName(log.caller ?? "", servicePrincipalIndex),
123
+ date: log.eventTimestamp
124
+ }));
125
+ }
126
+
127
+ function getEvidenceCallerName(
128
+ caller: string,
129
+ servicePrincipalIndex: OwnerResolverContext["servicePrincipalIndex"]
130
+ ): string {
131
+ const normalizedCaller = normalizeOwner(caller);
132
+ return servicePrincipalIndex.get(normalizedCaller)?.displayName ?? normalizedCaller;
133
+ }
134
+
135
+ export function buildActivityIndex(activityLogs: AzureActivityLog[]): ActivityLogIndex {
136
+ const index: ActivityLogIndex = new Map();
137
+
138
+ for (const log of activityLogs) {
139
+ const key = getActivityIndexKey(log.subscriptionId, log.resourceGroupName);
140
+ const logs = index.get(key) ?? [];
141
+ logs.push(log);
142
+ index.set(key, logs);
143
+ }
144
+
145
+ return index;
146
+ }
@@ -0,0 +1,34 @@
1
+ import type { DuckDBConnection } from "@duckdb/node-api";
2
+
3
+ import {
4
+ countDisabledOwnerEvidenceKeys,
5
+ type DisabledOwnerKey,
6
+ disableOwnerEvidenceKey,
7
+ enableOwnerEvidenceKey,
8
+ readDisabledOwnerEvidenceKeys
9
+ } from "./resources/disabledOwnerEvidenceTable";
10
+
11
+ export { type DisabledOwnerKey };
12
+
13
+ export class DisabledEvidenceStore {
14
+ private readonly getConnection: () => DuckDBConnection;
15
+
16
+ constructor(getConnection: () => DuckDBConnection) {
17
+ this.getConnection = getConnection;
18
+ }
19
+
20
+ readKeys(): Promise<ReadonlySet<DisabledOwnerKey>> {
21
+ return readDisabledOwnerEvidenceKeys(this.getConnection());
22
+ }
23
+
24
+ async setDisabled(key: DisabledOwnerKey, disabled: boolean): Promise<number> {
25
+ const connection = this.getConnection();
26
+ if (disabled) {
27
+ await disableOwnerEvidenceKey(connection, key);
28
+ } else {
29
+ await enableOwnerEvidenceKey(connection, key);
30
+ }
31
+
32
+ return countDisabledOwnerEvidenceKeys(connection);
33
+ }
34
+ }
@@ -0,0 +1,35 @@
1
+ import type { DuckDBConnection } from "@duckdb/node-api";
2
+
3
+ import {
4
+ readAzureIdentityEnrichmentStatus,
5
+ recalculateAzureIdentityEnrichment,
6
+ type AzureIdentityEnrichmentStatus
7
+ } from "./enrichment/azureIdentityEnrichment";
8
+
9
+ export class EnrichmentService {
10
+ private readonly getConnection: () => DuckDBConnection;
11
+ private status: AzureIdentityEnrichmentStatus = {
12
+ calculated: false,
13
+ latestRunId: null,
14
+ identityRoleAssignmentCount: 0,
15
+ accessRiskIdentityCount: 0,
16
+ managedIdentityAssignmentCount: 0,
17
+ calculatedAt: null
18
+ };
19
+
20
+ constructor(getConnection: () => DuckDBConnection) {
21
+ this.getConnection = getConnection;
22
+ }
23
+
24
+ getStatus(): AzureIdentityEnrichmentStatus {
25
+ return this.status;
26
+ }
27
+
28
+ async recalculate(): Promise<void> {
29
+ this.status = await recalculateAzureIdentityEnrichment(this.getConnection());
30
+ }
31
+
32
+ async readStatus(): Promise<void> {
33
+ this.status = await readAzureIdentityEnrichmentStatus(this.getConnection());
34
+ }
35
+ }