@knowttl/mg-axi 0.1.1 → 0.1.3

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 (48) hide show
  1. package/README.md +251 -77
  2. package/dist/api.js +253 -8
  3. package/dist/bin/mg-axi-hook.js +31 -0
  4. package/dist/catalogue.js +693 -2
  5. package/dist/cli.js +155 -17
  6. package/dist/docs.js +175 -46
  7. package/dist/doctor.js +2 -2
  8. package/dist/entra-access-reviews.js +11 -4
  9. package/dist/entra-apps.js +171 -14
  10. package/dist/entra-audit-logs.js +101 -16
  11. package/dist/entra-auth-methods.js +8 -4
  12. package/dist/entra-certificate-auth.js +12 -8
  13. package/dist/entra-conditional-access.js +425 -15
  14. package/dist/entra-contacts.js +19 -12
  15. package/dist/entra-contracts.js +7 -4
  16. package/dist/entra-custom-security-attributes.js +19 -12
  17. package/dist/entra-data-policy-operations.js +7 -4
  18. package/dist/entra-delegated-admin.js +55 -6
  19. package/dist/entra-deleted-items.js +6 -4
  20. package/dist/entra-directory-objects.js +6 -4
  21. package/dist/entra-directory.js +19 -12
  22. package/dist/entra-domains.js +13 -8
  23. package/dist/entra-entitlement-management.js +12 -4
  24. package/dist/entra-federation-configurations.js +7 -4
  25. package/dist/entra-grants.js +8 -4
  26. package/dist/entra-group-lifecycle.js +13 -8
  27. package/dist/entra-group-pim.js +431 -0
  28. package/dist/entra-groups.js +13 -8
  29. package/dist/entra-identity-providers.js +7 -4
  30. package/dist/entra-lifecycle-workflows.js +152 -13
  31. package/dist/entra-multi-tenant-organization.js +7 -4
  32. package/dist/entra-on-premises-synchronization.js +6 -4
  33. package/dist/entra-organization.js +13 -8
  34. package/dist/entra-risk-prevention.js +442 -0
  35. package/dist/entra-risk.js +224 -9
  36. package/dist/entra-roles.js +204 -12
  37. package/dist/entra-subscriptions.js +6 -4
  38. package/dist/entra-terms-of-use.js +13 -8
  39. package/dist/entra-users.js +7 -4
  40. package/dist/graph-session.js +30 -5
  41. package/dist/hook.js +84 -0
  42. package/dist/list-totals.js +52 -0
  43. package/dist/setup.js +9 -1
  44. package/docs/coverage.md +743 -610
  45. package/inventory/operations.json +2 -2
  46. package/inventory/schema.json +1 -1
  47. package/package.json +4 -3
  48. package/skills/mg-axi/SKILL.md +86 -1
package/dist/api.js CHANGED
@@ -1,16 +1,20 @@
1
1
  import { AxiError } from "axi-sdk-js";
2
2
  import { KNOWN_CONTACTED_REVIEWER_FIELDS, KNOWN_DECISION_FIELDS, KNOWN_DEFINITION_FIELDS, KNOWN_INSTANCE_FIELDS, KNOWN_STAGE_FIELDS } from "./entra-access-reviews.js";
3
3
  import { KNOWN_ACCESS_PACKAGE_FIELDS, KNOWN_ASSIGNMENT_POLICY_FIELDS, KNOWN_CATALOG_FIELDS, KNOWN_RESOURCE_ROLE_SCOPE_FIELDS } from "./entra-entitlement-management.js";
4
- import { KNOWN_LIFECYCLE_SETTINGS_FIELDS, KNOWN_RUN_FIELDS, KNOWN_SUBJECT_PROCESSING_FIELDS, KNOWN_TASK_DEFINITION_FIELDS, KNOWN_TASK_FIELDS, KNOWN_TASK_REPORT_FIELDS, KNOWN_USER_PROCESSING_FIELDS, KNOWN_WORKFLOW_FIELDS, KNOWN_WORKFLOW_TEMPLATE_FIELDS } from "./entra-lifecycle-workflows.js";
4
+ import { KNOWN_LIFECYCLE_SETTINGS_FIELDS, KNOWN_RUN_FIELDS, KNOWN_SUBJECT_PROCESSING_FIELDS, KNOWN_TASK_DEFINITION_FIELDS, KNOWN_TASK_FIELDS, KNOWN_TASK_PROCESSING_FIELDS, KNOWN_TASK_REPORT_FIELDS, KNOWN_USER_PROCESSING_FIELDS, KNOWN_WORKFLOW_FIELDS, KNOWN_WORKFLOW_TEMPLATE_FIELDS } from "./entra-lifecycle-workflows.js";
5
5
  import { KNOWN_BRANDING_FIELDS, KNOWN_ORGANIZATION_FIELDS } from "./entra-organization.js";
6
6
  import { KNOWN_CONTRACT_FIELDS } from "./entra-contracts.js";
7
7
  import { KNOWN_DELEGATED_ADMIN_CUSTOMER_FIELDS, KNOWN_DELEGATED_ADMIN_RELATIONSHIP_FIELDS, KNOWN_DELEGATED_ADMIN_ACCESS_ASSIGNMENT_FIELDS, KNOWN_DELEGATED_ADMIN_OPERATION_FIELDS, KNOWN_DELEGATED_ADMIN_REQUEST_FIELDS } from "./entra-delegated-admin.js";
8
8
  import { KNOWN_TENANT_INFORMATION_FIELDS } from "./entra-tenant-information.js";
9
9
  import { KNOWN_MTO_FIELDS, KNOWN_MTO_JOIN_REQUEST_FIELDS, KNOWN_MTO_TENANT_FIELDS } from "./entra-multi-tenant-organization.js";
10
10
  import { KNOWN_DATA_POLICY_FIELDS } from "./entra-data-policy-operations.js";
11
+ import { KNOWN_FRAUD_FIELDS, KNOWN_WAF_FIELDS, KNOWN_VERIFICATION_FIELDS } from "./entra-risk-prevention.js";
11
12
  import { KNOWN_FEDERATION_FIELDS } from "./entra-federation-configurations.js";
12
13
  import { KNOWN_CA_FIELDS, KNOWN_PKI_FIELDS } from "./entra-certificate-auth.js";
14
+ import { KNOWN_AUTH_CONTEXT_FIELDS, KNOWN_COMBO_FIELDS, KNOWN_DELETED_LOCATION_FIELDS, KNOWN_DELETED_POLICY_FIELDS, KNOWN_MODE_FIELDS, KNOWN_STRENGTH_FIELDS, KNOWN_TEMPLATE_FIELDS as KNOWN_CA_TEMPLATE_FIELDS } from "./entra-conditional-access.js";
13
15
  import { KNOWN_SUBSCRIPTION_FIELDS } from "./entra-subscriptions.js";
16
+ import { KNOWN_TEMPLATE_FIELDS as KNOWN_ROLE_TEMPLATE_FIELDS, KNOWN_ROLE_MEMBER_FIELDS, KNOWN_SCOPED_MEMBER_FIELDS } from "./entra-roles.js";
17
+ import { KNOWN_ASSIGNMENT_INSTANCE_FIELDS, KNOWN_ASSIGNMENT_SCHEDULE_FIELDS, KNOWN_ELIGIBILITY_INSTANCE_FIELDS, KNOWN_ELIGIBILITY_REQUEST_FIELDS, KNOWN_ELIGIBILITY_SCHEDULE_FIELDS } from "./entra-group-pim.js";
14
18
  import { KNOWN_SYNC_FIELDS } from "./entra-on-premises-synchronization.js";
15
19
  import { KNOWN_ACCEPTANCE_FIELDS, KNOWN_AGREEMENT_FIELDS } from "./entra-terms-of-use.js";
16
20
  import { KNOWN_DIRECTORY_OBJECT_FIELDS } from "./entra-directory-objects.js";
@@ -41,7 +45,7 @@ import { encodeGraphPathSegment, functionBindingFor, GraphSession, resolveSessio
41
45
  // preview, /me and sensitive-area policy still runs inside the shared session,
42
46
  // so raw reads cannot bypass named-command gating.
43
47
  export const REVIEWED_ON = "2026-10-04";
44
- const TRUNCATE_AT = 4000;
48
+ const TRUNCATE_AT = 500;
45
49
  // Collections support server filtering, ordering and paging hints. Advanced
46
50
  // queries ($search, $count=true) need per-endpoint ConsistencyLevel review and
47
51
  // stay unsupported here; $skiptoken is server paging state, paged via --all.
@@ -57,12 +61,17 @@ const AUTH_METHOD_FIELDS = ["id", "createdDateTime", "displayName", "phoneNumber
57
61
  const REGISTRATION_FIELDS = ["id", "userPrincipalName", "userDisplayName", "userType", "isAdmin", "isMfaRegistered", "isMfaCapable", "isPasswordlessCapable", "isSsprRegistered", "isSsprEnabled", "isSsprCapable", "userPreferredMethodForSecondaryAuthentication", "lastUpdatedDateTime"];
58
62
  const SIGNIN_FIELDS = ["id", "createdDateTime", "userId", "userPrincipalName", "userDisplayName", "appDisplayName", "appId", "ipAddress", "location", "status", "conditionalAccessStatus", "riskDetail", "riskLevelAggregated", "riskLevelDuringSignIn", "riskState", "resourceDisplayName", "resourceId", "clientAppUsed"];
59
63
  const AUDIT_FIELDS = ["id", "activityDateTime", "activityDisplayName", "category", "loggedByService", "operationType", "result", "resultReason", "correlationId", "initiatedBy", "targetResources"];
64
+ const PROVISIONING_FIELDS = ["id", "activityDateTime", "tenantId", "jobId", "cycleId", "changeId", "action", "durationInMilliseconds", "sourceSystem", "sourceIdentity", "targetSystem", "targetIdentity", "provisioningStatusInfo", "provisioningSteps", "modifiedProperties", "servicePrincipal", "initiatedBy"];
60
65
  const RISKY_USER_FIELDS = ["id", "userPrincipalName", "userDisplayName", "riskDetail", "riskLastUpdatedDateTime", "riskLevel", "riskState"];
61
66
  const RISK_DETECTION_FIELDS = ["id", "detectedDateTime", "activityDateTime", "userId", "userPrincipalName", "userDisplayName", "ipAddress", "location", "riskDetail", "riskLevel", "riskState", "riskEventType", "detectionTimingType", "lastUpdatedDateTime", "source"];
67
+ const RISKY_SP_FIELDS = ["id", "displayName", "appId", "servicePrincipalType", "riskDetail", "riskLastUpdatedDateTime", "riskLevel", "riskState", "isEnabled", "isProcessing"];
68
+ const RISKY_SP_HISTORY_FIELDS = ["id", "displayName", "appId", "servicePrincipalType", "servicePrincipalId", "riskDetail", "riskLastUpdatedDateTime", "riskLevel", "riskState", "isEnabled", "isProcessing", "initiatedBy", "activity"];
69
+ const SP_DETECTION_FIELDS = ["id", "detectedDateTime", "activityDateTime", "servicePrincipalId", "servicePrincipalDisplayName", "appId", "keyIds", "ipAddress", "location", "riskDetail", "riskLevel", "riskState", "riskEventType", "detectionTimingType", "lastUpdatedDateTime", "source", "additionalInfo"];
62
70
  const APP_FIELDS = ["id", "appId", "displayName", "createdDateTime", "signInAudience", "publisherDomain", "keyCredentials", "passwordCredentials", "requiredResourceAccess", "web", "spa", "publicClient"];
63
71
  const SP_FIELDS = ["id", "appId", "displayName", "servicePrincipalType", "accountEnabled", "appOwnerOrganizationId", "appRoleAssignmentRequired", "preferredSingleSignOnMode", "loginUrl"];
64
72
  const GRANT_FIELDS = ["id", "clientId", "consentType", "principalId", "resourceId", "scope", "startTime", "expiryTime"];
65
73
  const APP_ROLE_FIELDS = ["id", "appRoleId", "principalId", "principalDisplayName", "principalType", "resourceId", "resourceDisplayName", "createdDateTime"];
74
+ const FEDERATED_FIELDS = ["id", "name", "issuer", "subject", "description", "audiences"];
66
75
  const ROLE_ASSIGNMENT_FIELDS = ["id", "principalId", "roleDefinitionId", "directoryScopeId", "appScopeId", "createdDateTime"];
67
76
  const PIM_INSTANCE_FIELDS = ["id", "roleDefinitionId", "principalId", "assignmentType", "memberType", "startDateTime", "endDateTime", "activatedUsing"];
68
77
  const DEVICE_FIELDS = ["id", "deviceId", "displayName", "operatingSystem", "operatingSystemVersion", "trustType", "isCompliant", "isManaged", "accountEnabled", "createdDateTime", "approximateLastSignInDateTime", "manufacturer", "model"];
@@ -104,6 +113,62 @@ export const REVIEWED_ROUTES = [
104
113
  { id: "v1.0:GET:/identity/conditionalAccess/namedLocations/{namedLocation-id}", kind: "single", query: SINGLE_QUERY, fields: NAMED_LOCATION_FIELDS,
105
114
  access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (for example Security Reader) is also required.",
106
115
  sources: ["https://learn.microsoft.com/graph/api/countrynamedlocation-get?view=graph-rest-1.0"] },
116
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/policies", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_STRENGTH_FIELDS,
117
+ access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
118
+ note: "Conditional Access needs P1.",
119
+ sources: ["https://learn.microsoft.com/en-us/graph/api/authenticationstrengthroot-list-policies?view=graph-rest-1.0"] },
120
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/policies/{authenticationStrengthPolicy-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_STRENGTH_FIELDS,
121
+ access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
122
+ note: "Conditional Access needs P1.",
123
+ sources: ["https://learn.microsoft.com/en-us/graph/api/authenticationstrengthpolicy-get?view=graph-rest-1.0"] },
124
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/policies/{authenticationStrengthPolicy-id}/combinationConfigurations", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_COMBO_FIELDS, keepODataType: true,
125
+ access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
126
+ note: "Conditional Access needs P1. Rows are polymorphic: the @odata.type discriminator rides along automatically to name the configuration kind.",
127
+ sources: ["https://learn.microsoft.com/graph/api/authenticationstrengthpolicy-list-combinationconfigurations?view=graph-rest-1.0"] },
128
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/policies/{authenticationStrengthPolicy-id}/combinationConfigurations/{authenticationCombinationConfiguration-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_COMBO_FIELDS, keepODataType: true,
129
+ access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
130
+ note: "Conditional Access needs P1. Rows are polymorphic: the @odata.type discriminator rides along automatically to name the configuration kind.",
131
+ sources: ["https://learn.microsoft.com/graph/api/authenticationcombinationconfiguration-get?view=graph-rest-1.0"] },
132
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/authenticationMethodModes", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_MODE_FIELDS,
133
+ access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
134
+ note: "Conditional Access needs P1.",
135
+ sources: ["https://learn.microsoft.com/graph/api/authenticationstrengthroot-list-authenticationmethodmodes?view=graph-rest-1.0"] },
136
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/authenticationMethodModes/{authenticationMethodModeDetail-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_MODE_FIELDS,
137
+ access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
138
+ note: "Conditional Access needs P1.",
139
+ sources: ["https://learn.microsoft.com/en-us/graph/api/authenticationmethodmodedetail-get?view=graph-rest-1.0"] },
140
+ { id: "v1.0:GET:/identity/conditionalAccess/templates", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_CA_TEMPLATE_FIELDS,
141
+ access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (for example Security Reader) is also required.",
142
+ note: "Conditional Access needs P1.",
143
+ sources: ["https://learn.microsoft.com/en-us/graph/api/conditionalaccessroot-list-templates?view=graph-rest-1.0"] },
144
+ { id: "v1.0:GET:/identity/conditionalAccess/templates/{conditionalAccessTemplate-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_CA_TEMPLATE_FIELDS,
145
+ access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (for example Security Reader) is also required.",
146
+ note: "Conditional Access needs P1.",
147
+ sources: ["https://learn.microsoft.com/en-us/graph/api/conditionalaccesstemplate-get?view=graph-rest-1.0"] },
148
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationContextClassReferences", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_AUTH_CONTEXT_FIELDS,
149
+ access: "D/A AuthenticationContext.Read.All. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader) is also required.",
150
+ note: "Conditional Access needs P1.",
151
+ sources: ["https://learn.microsoft.com/graph/api/conditionalaccessroot-list-authenticationcontextclassreferences?view=graph-rest-1.0"] },
152
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationContextClassReferences/{authenticationContextClassReference-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_AUTH_CONTEXT_FIELDS,
153
+ access: "D/A AuthenticationContext.Read.All. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader) is also required.",
154
+ note: "The get page documents no optional query parameters; $select travels as the standard single-object projection. Conditional Access needs P1.",
155
+ sources: ["https://learn.microsoft.com/graph/api/authenticationcontextclassreference-get?view=graph-rest-1.0"] },
156
+ { id: "v1.0:GET:/identity/conditionalAccess/deletedItems/policies", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_DELETED_POLICY_FIELDS,
157
+ access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader) is also required.",
158
+ note: "No v1.0 operation-level documentation page; access follows the beta policyDeletableItem contract for conditional-access objects with the conditionalAccessPolicy resource reference. Soft-deleted policies restore within 30 days; restore is beta-only and out of scope. Conditional Access needs P1.",
159
+ sources: ["https://learn.microsoft.com/en-us/graph/api/policydeletableitem-list?view=graph-rest-beta", "https://learn.microsoft.com/en-us/graph/api/resources/policydeletableitem?view=graph-rest-beta"] },
160
+ { id: "v1.0:GET:/identity/conditionalAccess/deletedItems/policies/{conditionalAccessPolicy-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_DELETED_POLICY_FIELDS,
161
+ access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader) is also required.",
162
+ note: "No v1.0 operation-level documentation page; access follows the beta policyDeletableItem contract for conditional-access objects with the conditionalAccessPolicy resource reference. Soft-deleted policies restore within 30 days; restore is beta-only and out of scope. Conditional Access needs P1.",
163
+ sources: ["https://learn.microsoft.com/en-us/graph/api/policydeletableitem-get?view=graph-rest-beta", "https://learn.microsoft.com/en-us/graph/api/resources/policydeletableitem?view=graph-rest-beta"] },
164
+ { id: "v1.0:GET:/identity/conditionalAccess/deletedItems/namedLocations", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_DELETED_LOCATION_FIELDS, keepODataType: true,
165
+ access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader) is also required.",
166
+ note: "No v1.0 operation-level documentation page; access follows the beta policyDeletableItem contract for conditional-access objects with the namedLocation resource reference. Rows are polymorphic: the @odata.type discriminator rides along automatically to name the location kind. Soft-deleted locations restore within 30 days; restore is beta-only and out of scope. Conditional Access needs P1.",
167
+ sources: ["https://learn.microsoft.com/en-us/graph/api/policydeletableitem-list?view=graph-rest-beta", "https://learn.microsoft.com/en-us/graph/api/resources/policydeletableitem?view=graph-rest-beta"] },
168
+ { id: "v1.0:GET:/identity/conditionalAccess/deletedItems/namedLocations/{namedLocation-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_DELETED_LOCATION_FIELDS, keepODataType: true,
169
+ access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader) is also required.",
170
+ note: "No v1.0 operation-level documentation page; access follows the beta policyDeletableItem contract for conditional-access objects with the namedLocation resource reference. Soft-deleted locations restore within 30 days; restore is beta-only and out of scope. Conditional Access needs P1.",
171
+ sources: ["https://learn.microsoft.com/en-us/graph/api/policydeletableitem-get?view=graph-rest-beta", "https://learn.microsoft.com/en-us/graph/api/resources/policydeletableitem?view=graph-rest-beta"] },
107
172
  { id: "v1.0:GET:/users/{user-id}/authentication/methods", kind: "collection", query: ["$select"], fields: AUTH_METHOD_FIELDS,
108
173
  access: "D/A UserAuthenticationMethod.Read.All for other users; delegated self UserAuthenticationMethod.Read. Dedicated administrator roles also apply.",
109
174
  note: "Targeted per-user inspection only; aggregate coverage belongs to the registration report, never to a scan across users.",
@@ -122,6 +187,10 @@ export const REVIEWED_ROUTES = [
122
187
  { id: "v1.0:GET:/auditLogs/directoryAudits", kind: "collection", query: COLLECTION_QUERY, fields: AUDIT_FIELDS,
123
188
  access: "D/A AuditLog.Read.All. Delegated callers pass it as --scopes.",
124
189
  sources: ["https://learn.microsoft.com/graph/api/directoryaudit-list?view=graph-rest-1.0"] },
190
+ { id: "v1.0:GET:/auditLogs/provisioning", kind: "collection", query: COLLECTION_QUERY, fields: PROVISIONING_FIELDS,
191
+ access: "D/A AuditLog.Read.All and Directory.Read.All. Delegated callers pass both as --scopes; Reports Reader is the least privileged directory role.",
192
+ note: "Entra ID P1/P2 licence prerequisite; retention is 30 days on premium and 7 days on free.",
193
+ sources: ["https://learn.microsoft.com/graph/api/provisioningobjectsummary-list?view=graph-rest-1.0"] },
125
194
  { id: "v1.0:GET:/identityProtection/riskyUsers", kind: "collection", query: COLLECTION_QUERY, fields: RISKY_USER_FIELDS,
126
195
  access: "D/A IdentityRiskyUser.Read.All. Delegated callers pass it as --scopes.",
127
196
  note: "Full investigation needs P2/Suite; limited results stay limited, never reinterpreted as empty.",
@@ -136,6 +205,28 @@ export const REVIEWED_ROUTES = [
136
205
  { id: "v1.0:GET:/identityProtection/riskDetections/{riskDetection-id}", kind: "single", query: SINGLE_QUERY, fields: RISK_DETECTION_FIELDS,
137
206
  access: "D/A IdentityRiskEvent.Read.All. Delegated callers pass it as --scopes.",
138
207
  sources: ["https://learn.microsoft.com/graph/api/riskdetection-get?view=graph-rest-1.0"] },
208
+ { id: "v1.0:GET:/identityProtection/riskyServicePrincipals", kind: "collection", query: COLLECTION_QUERY, fields: RISKY_SP_FIELDS,
209
+ access: "D/A IdentityRiskyServicePrincipal.Read.All. Delegated callers pass it as --scopes.",
210
+ note: "The riskyServicePrincipals API requires a Microsoft Entra Workload Identities Premium licence; limited results stay limited, never reinterpreted as empty.",
211
+ sources: ["https://learn.microsoft.com/graph/api/identityprotectionroot-list-riskyserviceprincipals?view=graph-rest-1.0"] },
212
+ { id: "v1.0:GET:/identityProtection/riskyServicePrincipals/{riskyServicePrincipal-id}", kind: "single", query: SINGLE_QUERY, fields: RISKY_SP_FIELDS,
213
+ access: "D/A IdentityRiskyServicePrincipal.Read.All. Delegated callers pass it as --scopes.",
214
+ sources: ["https://learn.microsoft.com/graph/api/riskyserviceprincipal-get?view=graph-rest-1.0"] },
215
+ { id: "v1.0:GET:/identityProtection/riskyServicePrincipals/{riskyServicePrincipal-id}/history", kind: "collection", query: COLLECTION_QUERY, fields: RISKY_SP_HISTORY_FIELDS,
216
+ access: "D/A IdentityRiskyServicePrincipal.Read.All. Delegated callers pass it as --scopes.",
217
+ note: "Per-principal risk history; history items inherit the principal properties and add the service-principal identifier with the actor/activity of the change.",
218
+ sources: ["https://learn.microsoft.com/graph/api/riskyserviceprincipal-list-history?view=graph-rest-1.0"] },
219
+ { id: "v1.0:GET:/identityProtection/riskyServicePrincipals/{riskyServicePrincipal-id}/history/{riskyServicePrincipalHistoryItem-id}", kind: "single", query: SINGLE_QUERY, fields: RISKY_SP_HISTORY_FIELDS,
220
+ access: "D/A IdentityRiskyServicePrincipal.Read.All. Delegated callers pass it as --scopes.",
221
+ note: "No operation-level documentation page; access follows the parent history-list contract and the history-item resource reference.",
222
+ sources: ["https://learn.microsoft.com/graph/api/riskyserviceprincipal-list-history?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/riskyserviceprincipalhistoryitem?view=graph-rest-1.0"] },
223
+ { id: "v1.0:GET:/identityProtection/servicePrincipalRiskDetections", kind: "collection", query: COLLECTION_QUERY, fields: SP_DETECTION_FIELDS,
224
+ access: "D/A IdentityRiskEvent.Read.All. Delegated callers pass it as --scopes.",
225
+ note: "The servicePrincipalRiskDetection API requires a Microsoft Entra Workload Identities Premium licence; riskDetail and riskLevel report hidden without it.",
226
+ sources: ["https://learn.microsoft.com/graph/api/identityprotectionroot-list-serviceprincipalriskdetections?view=graph-rest-1.0"] },
227
+ { id: "v1.0:GET:/identityProtection/servicePrincipalRiskDetections/{servicePrincipalRiskDetection-id}", kind: "single", query: SINGLE_QUERY, fields: SP_DETECTION_FIELDS,
228
+ access: "D/A IdentityRiskEvent.Read.All. Delegated callers pass it as --scopes.",
229
+ sources: ["https://learn.microsoft.com/graph/api/serviceprincipalriskdetection-get?view=graph-rest-1.0"] },
139
230
  { id: "v1.0:GET:/applications", kind: "collection", query: COLLECTION_QUERY, fields: APP_FIELDS,
140
231
  access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
141
232
  note: "appId (client ID) is distinct from the object id. Credential fields carry expiry metadata only; GET never returns secret values and success redaction still applies.",
@@ -156,6 +247,34 @@ export const REVIEWED_ROUTES = [
156
247
  access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
157
248
  note: "Actual granted assignments; requested permissions are a separate contract.",
158
249
  sources: ["https://learn.microsoft.com/graph/api/serviceprincipal-list-approleassignments?view=graph-rest-1.0"] },
250
+ { id: "v1.0:GET:/servicePrincipals/{servicePrincipal-id}/appRoleAssignments/{appRoleAssignment-id}", kind: "single", query: SINGLE_QUERY, fields: APP_ROLE_FIELDS,
251
+ access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
252
+ note: "One granted assignment on the client service principal; requested permissions are a separate contract.",
253
+ sources: ["https://learn.microsoft.com/graph/api/approleassignment-get?view=graph-rest-1.0"] },
254
+ { id: "v1.0:GET:/servicePrincipals/{servicePrincipal-id}/appRoleAssignedTo", kind: "collection", query: COLLECTION_QUERY, fields: APP_ROLE_FIELDS,
255
+ access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
256
+ note: "Principals assigned to this resource service principal; the reverse view of appRoleAssignments.",
257
+ sources: ["https://learn.microsoft.com/graph/api/serviceprincipal-list-approleassignedto?view=graph-rest-1.0"] },
258
+ { id: "v1.0:GET:/servicePrincipals/{servicePrincipal-id}/appRoleAssignedTo/{appRoleAssignment-id}", kind: "single", query: SINGLE_QUERY, fields: APP_ROLE_FIELDS,
259
+ access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
260
+ note: "One principal assignment on the resource service principal; the reverse view of appRoleAssignments.",
261
+ sources: ["https://learn.microsoft.com/graph/api/approleassignment-get?view=graph-rest-1.0"] },
262
+ { id: "v1.0:GET:/applications/{application-id}/federatedIdentityCredentials", kind: "collection", query: COLLECTION_QUERY, fields: FEDERATED_FIELDS,
263
+ access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
264
+ note: "Workload-identity trust metadata only; GET never returns secret values and success redaction still applies.",
265
+ sources: ["https://learn.microsoft.com/graph/api/federatedidentitycredential-list?view=graph-rest-1.0"] },
266
+ { id: "v1.0:GET:/applications/{application-id}/federatedIdentityCredentials/{federatedIdentityCredential-id}", kind: "single", query: SINGLE_QUERY, fields: FEDERATED_FIELDS,
267
+ access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
268
+ note: "One workload-identity trust entry; metadata only, never secret values.",
269
+ sources: ["https://learn.microsoft.com/graph/api/federatedidentitycredential-get?view=graph-rest-1.0"] },
270
+ { id: "v1.0:GET:/servicePrincipals/{servicePrincipal-id}/federatedIdentityCredentials", kind: "collection", query: COLLECTION_QUERY, fields: FEDERATED_FIELDS,
271
+ access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
272
+ note: "Same federatedIdentityCredential resource contract as the application navigation; metadata only, never secret values.",
273
+ sources: ["https://learn.microsoft.com/graph/api/federatedidentitycredential-list?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/federatedidentitycredential?view=graph-rest-1.0"] },
274
+ { id: "v1.0:GET:/servicePrincipals/{servicePrincipal-id}/federatedIdentityCredentials/{federatedIdentityCredential-id}", kind: "single", query: SINGLE_QUERY, fields: FEDERATED_FIELDS,
275
+ access: "D/A Application.Read.All. Delegated callers pass it as --scopes.",
276
+ note: "Same federatedIdentityCredential resource contract as the application navigation; metadata only, never secret values.",
277
+ sources: ["https://learn.microsoft.com/graph/api/federatedidentitycredential-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/federatedidentitycredential?view=graph-rest-1.0"] },
159
278
  { id: "v1.0:GET:/oauth2PermissionGrants", kind: "collection", query: COLLECTION_QUERY, fields: GRANT_FIELDS,
160
279
  access: "D/A Directory.Read.All as the read choice. Delegated callers pass it as --scopes.",
161
280
  note: "Actual granted consent; requested permissions are a separate contract.",
@@ -175,6 +294,73 @@ export const REVIEWED_ROUTES = [
175
294
  access: "D/A RoleAssignmentSchedule.Read.Directory. Delegated callers pass it as --scopes.",
176
295
  note: "Active (including activated eligible) assignments; PIM needs P2 or ID Governance.",
177
296
  sources: ["https://learn.microsoft.com/graph/api/rbacapplication-list-roleassignmentscheduleinstances?view=graph-rest-1.0"] },
297
+ { id: "v1.0:GET:/directoryRoleTemplates", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ROLE_TEMPLATE_FIELDS,
298
+ access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
299
+ note: "Templates describe every role; activation state lives on directoryRoles.",
300
+ sources: ["https://learn.microsoft.com/graph/api/directoryroletemplate-list?view=graph-rest-1.0"] },
301
+ { id: "v1.0:GET:/directoryRoleTemplates/{directoryRoleTemplate-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_ROLE_TEMPLATE_FIELDS,
302
+ access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
303
+ sources: ["https://learn.microsoft.com/graph/api/directoryroletemplate-get?view=graph-rest-1.0"] },
304
+ { id: "v1.0:GET:/directoryRoles/{directoryRole-id}/members", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ROLE_MEMBER_FIELDS,
305
+ access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
306
+ note: "Members are the assigned principals; @odata.type names each member kind.",
307
+ sources: ["https://learn.microsoft.com/graph/api/directoryrole-list-members?view=graph-rest-1.0"] },
308
+ { id: "v1.0:GET:/directoryRoles/{directoryRole-id}/scopedMembers", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_SCOPED_MEMBER_FIELDS,
309
+ access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
310
+ note: "Scoped members bind one role to one principal within one directory scope; administrative-unit scopes need P1.",
311
+ sources: ["https://learn.microsoft.com/graph/api/directoryrole-list-scopedmembers?view=graph-rest-1.0"] },
312
+ { id: "v1.0:GET:/directoryRoles/{directoryRole-id}/scopedMembers/{scopedRoleMembership-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_SCOPED_MEMBER_FIELDS,
313
+ access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
314
+ note: "Administrative-unit scopes need P1.",
315
+ sources: ["https://learn.microsoft.com/graph/api/scopedrolemembership-get?view=graph-rest-1.0"] },
316
+ { id: "v1.0:GET:/scopedRoleMemberships", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_SCOPED_MEMBER_FIELDS,
317
+ access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
318
+ note: "Administrative-unit-scoped admin assignments; directory-wide assignments live on roleAssignments. Administrative-unit scopes need P1.",
319
+ sources: ["https://learn.microsoft.com/graph/api/scopedrolemembership-list?view=graph-rest-1.0"] },
320
+ { id: "v1.0:GET:/scopedRoleMemberships/{scopedRoleMembership-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_SCOPED_MEMBER_FIELDS,
321
+ access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
322
+ note: "Administrative-unit scopes need P1.",
323
+ sources: ["https://learn.microsoft.com/graph/api/scopedrolemembership-get?view=graph-rest-1.0"] },
324
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/assignmentSchedules", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ASSIGNMENT_SCHEDULE_FIELDS,
325
+ access: "D/A PrivilegedAssignmentSchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
326
+ note: "Assignment schedules govern group membership or ownership over time; PIM for Groups needs P2 or ID Governance; lists require $filter on groupId or principalId. AssignmentScheduleRequests stay out: their least privilege is the write scope PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup.",
327
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroup-list-assignmentschedules?view=graph-rest-1.0"] },
328
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/assignmentSchedules/{privilegedAccessGroupAssignmentSchedule-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_ASSIGNMENT_SCHEDULE_FIELDS,
329
+ access: "D/A PrivilegedAssignmentSchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
330
+ note: "PIM for Groups needs P2 or ID Governance.",
331
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroupassignmentschedule-get?view=graph-rest-1.0"] },
332
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/assignmentScheduleInstances", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ASSIGNMENT_INSTANCE_FIELDS,
333
+ access: "D/A PrivilegedAssignmentSchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
334
+ note: "Provisioned assignment windows; PIM for Groups needs P2 or ID Governance; lists require $filter on groupId or principalId.",
335
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroup-list-assignmentscheduleinstances?view=graph-rest-1.0"] },
336
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/assignmentScheduleInstances/{privilegedAccessGroupAssignmentScheduleInstance-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_ASSIGNMENT_INSTANCE_FIELDS,
337
+ access: "D/A PrivilegedAssignmentSchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
338
+ note: "PIM for Groups needs P2 or ID Governance.",
339
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroupassignmentscheduleinstance-get?view=graph-rest-1.0"] },
340
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/eligibilitySchedules", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ELIGIBILITY_SCHEDULE_FIELDS,
341
+ access: "D/A PrivilegedEligibilitySchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
342
+ note: "Eligibility schedules govern who may activate; PIM for Groups needs P2 or ID Governance; lists require $filter on groupId or principalId.",
343
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroup-list-eligibilityschedules?view=graph-rest-1.0"] },
344
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/eligibilitySchedules/{privilegedAccessGroupEligibilitySchedule-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_ELIGIBILITY_SCHEDULE_FIELDS,
345
+ access: "D/A PrivilegedEligibilitySchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
346
+ note: "PIM for Groups needs P2 or ID Governance.",
347
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroupeligibilityschedule-get?view=graph-rest-1.0"] },
348
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/eligibilityScheduleInstances", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ELIGIBILITY_INSTANCE_FIELDS,
349
+ access: "D/A PrivilegedEligibilitySchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
350
+ note: "Provisioned eligibility windows; PIM for Groups needs P2 or ID Governance; lists require $filter on groupId or principalId.",
351
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroup-list-eligibilityscheduleinstances?view=graph-rest-1.0"] },
352
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/eligibilityScheduleInstances/{privilegedAccessGroupEligibilityScheduleInstance-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_ELIGIBILITY_INSTANCE_FIELDS,
353
+ access: "D/A PrivilegedEligibilitySchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
354
+ note: "PIM for Groups needs P2 or ID Governance.",
355
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroupeligibilityscheduleinstance-get?view=graph-rest-1.0"] },
356
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/eligibilityScheduleRequests", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ELIGIBILITY_REQUEST_FIELDS,
357
+ access: "D/A PrivilegedEligibilitySchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
358
+ note: "Eligibility asks plus outcomes; justification text rides only behind an explicit $select; PIM for Groups needs P2 or ID Governance; lists require $filter on groupId or principalId.",
359
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroup-list-eligibilityschedulerequests?view=graph-rest-1.0"] },
360
+ { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/eligibilityScheduleRequests/{privilegedAccessGroupEligibilityScheduleRequest-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_ELIGIBILITY_REQUEST_FIELDS,
361
+ access: "D/A PrivilegedEligibilitySchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
362
+ note: "PIM for Groups needs P2 or ID Governance.",
363
+ sources: ["https://learn.microsoft.com/graph/api/privilegedaccessgroupeligibilityschedulerequest-get?view=graph-rest-1.0"] },
178
364
  { id: "v1.0:GET:/devices", kind: "collection", query: COLLECTION_QUERY, fields: DEVICE_FIELDS,
179
365
  access: "D/A Device.Read.All. Delegated callers pass it as --scopes.",
180
366
  note: "Directory-device scope only; Intune device management is a separately authorized surface.",
@@ -551,6 +737,41 @@ export const REVIEWED_ROUTES = [
551
737
  access: "D/A User.Export.All plus User.Read.All. Delegated callers pass both as --scopes; delegated access additionally needs Company Administrator, the privileged role documented for export reads; personal Microsoft accounts are not supported. No P1/P2 prerequisite is stated for this read.",
552
738
  note: "storageLocation always renders as the redaction marker: export blob URLs and signed links never reach output. Workforce context only; export submission belongs to no read slice.",
553
739
  sources: ["https://learn.microsoft.com/graph/api/datapolicyoperation-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/datapolicyoperation?view=graph-rest-1.0"] },
740
+ { id: "v1.0:GET:/identity/riskPrevention/fraudProtectionProviders", kind: "collection", query: ["$select"], fields: KNOWN_FRAUD_FIELDS, keepODataType: true,
741
+ access: "D RiskPreventionProviders.Read.All (RiskPreventionProviders.ReadWrite.All is the documented higher-privileged alternative). Delegated callers pass it as --scopes; delegated access additionally needs a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported and Graph documents no supported application permission, so application profiles are refused before credentials. No P1/P2 prerequisite is stated for these reads.",
742
+ delegatedOnly: true,
743
+ note: "Graph documents $select only on the fraud-protection-provider list; $filter is not reviewed here. Arkose privateKey/publicKey and HUMAN serverToken are never projected: the first two are key material and the server token authenticates HUMAN API calls. Workforce context only; external-customer user flows and provisioning stay scheduled.",
744
+ sources: ["https://learn.microsoft.com/graph/api/riskpreventioncontainer-list-fraudprotectionproviders?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/fraudprotectionprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/arkosefraudprotectionprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/humansecurityfraudprotectionprovider?view=graph-rest-1.0"] },
745
+ { id: "v1.0:GET:/identity/riskPrevention/fraudProtectionProviders/{fraudProtectionProvider-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_FRAUD_FIELDS, keepODataType: true,
746
+ access: "D RiskPreventionProviders.Read.All (RiskPreventionProviders.ReadWrite.All is the documented higher-privileged alternative). Delegated callers pass it as --scopes; delegated access additionally needs a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported and Graph documents no supported application permission, so application profiles are refused before credentials. No P1/P2 prerequisite is stated for this read.",
747
+ delegatedOnly: true,
748
+ note: "Arkose privateKey/publicKey and HUMAN serverToken are never projected: the first two are key material and the server token authenticates HUMAN API calls. Workforce context only; external-customer user flows and provisioning stay scheduled.",
749
+ sources: ["https://learn.microsoft.com/graph/api/fraudprotectionprovider-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/fraudprotectionprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/arkosefraudprotectionprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/humansecurityfraudprotectionprovider?view=graph-rest-1.0"] },
750
+ { id: "v1.0:GET:/identity/riskPrevention/webApplicationFirewallProviders", kind: "collection", query: ["$select", "$filter"], fields: KNOWN_WAF_FIELDS, keepODataType: true,
751
+ access: "D RiskPreventionProviders.Read.All (RiskPreventionProviders.ReadWrite.All is the documented higher-privileged alternative). Delegated callers pass it as --scopes; delegated access additionally needs a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported and Graph documents no supported application permission, so application profiles are refused before credentials. No P1/P2 prerequisite is stated for these reads.",
752
+ delegatedOnly: true,
753
+ note: "Filtering passes through as plain $filter with no $count or ConsistencyLevel contract. Akamai clientSecret/clientToken/accessToken and Cloudflare apiToken are never projected: all four authenticate provider APIs. Workforce context only; verification submission and external-customer user flows stay scheduled.",
754
+ sources: ["https://learn.microsoft.com/graph/api/riskpreventioncontainer-list-webapplicationfirewallproviders?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/webapplicationfirewallprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/akamaiwebapplicationfirewallprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/cloudflarewebapplicationfirewallprovider?view=graph-rest-1.0"] },
755
+ { id: "v1.0:GET:/identity/riskPrevention/webApplicationFirewallProviders/{webApplicationFirewallProvider-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_WAF_FIELDS, keepODataType: true,
756
+ access: "D RiskPreventionProviders.Read.All (RiskPreventionProviders.ReadWrite.All is the documented higher-privileged alternative). Delegated callers pass it as --scopes; delegated access additionally needs a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported and Graph documents no supported application permission, so application profiles are refused before credentials. No P1/P2 prerequisite is stated for this read.",
757
+ delegatedOnly: true,
758
+ note: "Akamai clientSecret/clientToken/accessToken and Cloudflare apiToken are never projected: all four authenticate provider APIs. Workforce context only; verification submission and external-customer user flows stay scheduled.",
759
+ sources: ["https://learn.microsoft.com/graph/api/webapplicationfirewallprovider-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/webapplicationfirewallprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/akamaiwebapplicationfirewallprovider?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/cloudflarewebapplicationfirewallprovider?view=graph-rest-1.0"] },
760
+ { id: "v1.0:GET:/identity/riskPrevention/webApplicationFirewallVerifications", kind: "collection", query: ["$select", "$filter"], fields: KNOWN_VERIFICATION_FIELDS,
761
+ access: "D RiskPreventionProviders.Read.All. Delegated callers pass it as --scopes; delegated access additionally needs a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported and Graph documents no supported application permission, so application profiles are refused before credentials. No P1/P2 prerequisite is stated for these reads.",
762
+ delegatedOnly: true,
763
+ note: "Filtering passes through as plain $filter with no $count or ConsistencyLevel contract ($expand stays unreviewed). verificationResult and verifiedDetails ride whole as complex objects. Rows never carry provider detail: the provider navigation has its own sub-read. Workforce context only; verification submission stays scheduled.",
764
+ sources: ["https://learn.microsoft.com/graph/api/riskpreventioncontainer-list-webapplicationfirewallverifications?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/webapplicationfirewallverificationmodel?view=graph-rest-1.0"] },
765
+ { id: "v1.0:GET:/identity/riskPrevention/webApplicationFirewallVerifications/{webApplicationFirewallVerificationModel-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_VERIFICATION_FIELDS,
766
+ access: "D RiskPreventionProviders.Read.All (RiskPreventionProviders.ReadWrite.All is the documented higher-privileged alternative). Delegated callers pass it as --scopes; delegated access additionally needs a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported and Graph documents no supported application permission, so application profiles are refused before credentials. No P1/P2 prerequisite is stated for this read.",
767
+ delegatedOnly: true,
768
+ note: "The get documents $expand only; $select is reviewed here as baseline single-resource support and $expand stays unreviewed. verificationResult and verifiedDetails ride whole as complex objects. The row never carries provider detail: the provider navigation has its own sub-read. Workforce context only; verification submission stays scheduled.",
769
+ sources: ["https://learn.microsoft.com/graph/api/webapplicationfirewallverificationmodel-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/webapplicationfirewallverificationmodel?view=graph-rest-1.0"] },
770
+ { id: "v1.0:GET:/identity/riskPrevention/webApplicationFirewallVerifications/{webApplicationFirewallVerificationModel-id}/provider", kind: "single", query: SINGLE_QUERY, fields: KNOWN_WAF_FIELDS, keepODataType: true,
771
+ access: "D RiskPreventionProviders.Read.All (RiskPreventionProviders.ReadWrite.All is the documented higher-privileged alternative). Delegated callers pass it as --scopes; delegated access additionally needs a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported and Graph documents no supported application permission, so application profiles are refused before credentials. No P1/P2 prerequisite is stated for this read.",
772
+ delegatedOnly: true,
773
+ note: "No provider-navigation operation page exists; access follows the WAF provider-get contract and the verificationModel resource reference. Akamai clientSecret/clientToken/accessToken and Cloudflare apiToken are never projected: all four authenticate provider APIs. Workforce context only.",
774
+ sources: ["https://learn.microsoft.com/graph/api/webapplicationfirewallprovider-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/webapplicationfirewallverificationmodel?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/webapplicationfirewallprovider?view=graph-rest-1.0"] },
554
775
  { id: "v1.0:GET:/identityGovernance/accessReviews/definitions", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_DEFINITION_FIELDS,
555
776
  access: "D/A AccessReview.Read.All. Delegated callers pass it as --scopes; delegated access additionally needs a supported Entra role (group/app reviews: review creator, Global Reader, Security Reader, User Administrator, Identity Governance Administrator or Security Administrator; Entra-role reviews: Security Reader, Identity Governance Administrator, Privileged Role Administrator or Security Administrator); personal Microsoft accounts are not supported.",
556
777
  note: "Definitions are review schedules (a series), never their occurrences; access reviews need P2 or ID Governance depending on capability.",
@@ -690,6 +911,30 @@ export const REVIEWED_ROUTES = [
690
911
  access: "D/A LifecycleWorkflows-Reports.Read.All (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail). Delegated callers pass one as --scopes; delegated access additionally needs Global Reader or Lifecycle Workflows Administrator; personal Microsoft accounts are not supported.",
691
912
  note: "No taskReport-nested operation page exists; access follows the taskReports list contract with the top-level taskdefinition-get contract (which documents LifecycleWorkflows.Read.All for its own route) and the taskDefinition resource reference. No query parameters are reviewed here, so whole rows arrive and are projected locally. Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite.",
692
913
  sources: ["https://learn.microsoft.com/graph/api/identitygovernance-workflow-list-taskreports?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/identitygovernance-taskdefinition-get?view=graph-rest-1.0"] },
914
+ { id: "v1.0:GET:/identityGovernance/lifecycleWorkflows/workflows/{workflow-id}/runs/{run-id}/userProcessingResults", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_USER_PROCESSING_FIELDS,
915
+ access: "D/A LifecycleWorkflows-Reports.Read.All (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail). Delegated callers pass one as --scopes; delegated access additionally needs Global Reader or Lifecycle Workflows Administrator; personal Microsoft accounts are not supported.",
916
+ note: "Filtering passes through as plain $filter with no $count or ConsistencyLevel contract ($search/$orderby/$expand stay unreviewed). Run user processing results are personal data: defaults carry status and counts only and the subject link needs $expand and stays out. Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite.",
917
+ sources: ["https://learn.microsoft.com/graph/api/identitygovernance-run-list-userprocessingresults?view=graph-rest-1.0"] },
918
+ { id: "v1.0:GET:/identityGovernance/lifecycleWorkflows/workflows/{workflow-id}/runs/{run-id}/userProcessingResults/{userProcessingResult-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_USER_PROCESSING_FIELDS,
919
+ access: "D/A LifecycleWorkflows-Reports.Read.All (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail). Delegated callers pass one as --scopes; delegated access additionally needs Global Reader or Lifecycle Workflows Administrator; personal Microsoft accounts are not supported.",
920
+ note: "The userprocessingresult-get page covers this run-nested single directly. Run user processing results are personal data: defaults carry status and counts only and the subject link stays out. Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite.",
921
+ sources: ["https://learn.microsoft.com/graph/api/identitygovernance-userprocessingresult-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/identitygovernance-userprocessingresult?view=graph-rest-1.0"] },
922
+ { id: "v1.0:GET:/identityGovernance/lifecycleWorkflows/workflows/{workflow-id}/runs/{run-id}/subjectProcessingResults", kind: "collection", query: ["$filter"], fields: KNOWN_SUBJECT_PROCESSING_FIELDS,
923
+ access: "D/A LifecycleWorkflows-Reports.Read.All (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail). Delegated callers pass one as --scopes; delegated access additionally needs Global Reader or Lifecycle Workflows Administrator; personal Microsoft accounts are not supported.",
924
+ note: "Graph documents $count/$filter/$orderby/$expand but not $select here, so only $filter is reviewed and rows arrive whole. Run subject processing results are personal data: defaults carry subject type, status and counts only and the subject navigation stays out. Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite.",
925
+ sources: ["https://learn.microsoft.com/graph/api/identitygovernance-run-list-subjectprocessingresults?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/identitygovernance-subjectprocessingresult?view=graph-rest-1.0"] },
926
+ { id: "v1.0:GET:/identityGovernance/lifecycleWorkflows/workflows/{workflow-id}/runs/{run-id}/subjectProcessingResults/{subjectProcessingResult-id}", kind: "single", query: [], fields: KNOWN_SUBJECT_PROCESSING_FIELDS,
927
+ access: "D/A LifecycleWorkflows-Reports.Read.All (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail). Delegated callers pass one as --scopes; delegated access additionally needs Global Reader or Lifecycle Workflows Administrator; personal Microsoft accounts are not supported.",
928
+ note: "The subjectprocessingresult-get page covers this run-nested single directly and documents $expand only, so whole rows are reviewed here and the subject navigation stays out. Run subject processing results are personal data: defaults carry subject type, status and counts only. Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite.",
929
+ sources: ["https://learn.microsoft.com/graph/api/identitygovernance-subjectprocessingresult-get?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/identitygovernance-subjectprocessingresult?view=graph-rest-1.0"] },
930
+ { id: "v1.0:GET:/identityGovernance/lifecycleWorkflows/workflows/{workflow-id}/runs/{run-id}/taskProcessingResults", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_TASK_PROCESSING_FIELDS,
931
+ access: "D/A LifecycleWorkflows-Reports.Read.All (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail). Delegated callers pass one as --scopes; delegated access additionally needs Global Reader or Lifecycle Workflows Administrator; personal Microsoft accounts are not supported.",
932
+ note: "Filtering passes through as plain $filter with no $count or ConsistencyLevel contract ($search/$orderby/$expand stay unreviewed). Run task processing results carry one task's outcome with failure detail; the subject, task and workflowSubject navigations need $expand and stay out. Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite.",
933
+ sources: ["https://learn.microsoft.com/graph/api/identitygovernance-run-list-taskprocessingresults?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/identitygovernance-taskprocessingresult?view=graph-rest-1.0"] },
934
+ { id: "v1.0:GET:/identityGovernance/lifecycleWorkflows/workflows/{workflow-id}/runs/{run-id}/taskProcessingResults/{taskProcessingResult-id}", kind: "single", query: [], fields: KNOWN_TASK_PROCESSING_FIELDS,
935
+ access: "D/A LifecycleWorkflows-Reports.Read.All (LifecycleWorkflows.Read.All or LifecycleWorkflows.ReadWrite.All for richer detail). Delegated callers pass one as --scopes; delegated access additionally needs Global Reader or Lifecycle Workflows Administrator; personal Microsoft accounts are not supported.",
936
+ note: "No operation-level get page exists; access follows the run task-list contract and the taskProcessingResult resource reference. No query parameters are reviewed here, so whole rows arrive and are projected locally; the subject, task and workflowSubject navigations stay out. Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite.",
937
+ sources: ["https://learn.microsoft.com/graph/api/identitygovernance-run-list-taskprocessingresults?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/identitygovernance-taskprocessingresult?view=graph-rest-1.0"] },
693
938
  ];
694
939
  function splitPath(path) {
695
940
  return path.split("/").filter(segment => segment.length > 0);
@@ -814,9 +1059,9 @@ function reviewedFields(route, value) {
814
1059
  kept["@odata.type"] = value["@odata.type"];
815
1060
  return kept;
816
1061
  }
817
- // String truncation at the output boundary, mirroring az-axi's 4000-char
818
- // convention. --full disables truncation; redaction already ran in the
819
- // session and row caps are unaffected. Plain objects and arrays only.
1062
+ // String truncation at the output boundary, matching the 500-char rule used
1063
+ // by every Entra command. --full disables truncation; redaction already ran
1064
+ // in the session and row caps are unaffected. Plain objects and arrays only.
820
1065
  function truncateForOutput(value, full) {
821
1066
  if (typeof value === "string") {
822
1067
  if (full || value.length <= TRUNCATE_AT)
@@ -924,7 +1169,7 @@ export async function runApiGet(args, deps) {
924
1169
  const body = await session.execute({ profile: args.profile, operation, params, query, scopes });
925
1170
  const shaped = truncateForOutput(reviewedFields(outputRoute, body), full);
926
1171
  const record = isRecord(shaped.value) ? shaped.value : { value: shaped.value };
927
- return shaped.truncated ? { ...record, help: ["Strings truncated at 4000 chars; re-run with --full"] } : record;
1172
+ return shaped.truncated ? { ...record, help: ["Strings truncated at 500 chars; re-run with --full"] } : record;
928
1173
  }
929
1174
  const collected = await session.collect({ profile: args.profile, operation, params, query, scopes, limit: args.limit, cursor: args.cursor });
930
1175
  const shaped = truncateForOutput(collected.value.map(row => reviewedFields(outputRoute, row)), full);
@@ -932,11 +1177,11 @@ export async function runApiGet(args, deps) {
932
1177
  const warnings = route.warning ? { warnings: [route.warning] } : {};
933
1178
  if (collected.complete) {
934
1179
  return shaped.truncated
935
- ? { returned: value.length, complete: true, value, ...warnings, help: ["Strings truncated at 4000 chars; re-run with --full"] }
1180
+ ? { returned: value.length, complete: true, value, ...warnings, help: ["Strings truncated at 500 chars; re-run with --full"] }
936
1181
  : { returned: value.length, complete: true, value, ...warnings };
937
1182
  }
938
1183
  const hint = "Resume the same path, profile and scopes with --cursor - and supply the cursor token on stdin; use --limit <rows> or --all";
939
- const help = shaped.truncated ? [hint, "Strings truncated at 4000 chars; re-run with --full"] : [hint];
1184
+ const help = shaped.truncated ? [hint, "Strings truncated at 500 chars; re-run with --full"] : [hint];
940
1185
  return { returned: value.length, complete: false, reason: collected.reason, value, ...warnings, cursor: collected.cursor, help };
941
1186
  }
942
1187
  // Minimal HTTPS transport: sends only what the session authorized (GET on the
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ // Session-start hook entry point: print the local-only ambient summary and
3
+ // always exit 0 on config state, never loudly, so agent startup never breaks.
4
+ // Unknown argv fails loud with exit 2 instead of a silent wrong summary.
5
+ // Static imports are avoided so a broken install still reaches the fallback.
6
+ const args = process.argv.slice(2);
7
+ if (args.length === 1 && args[0] === "--help") {
8
+ process.stdout.write("mg-axi-hook: print the local-only mg-axi session-start summary (configured profiles, auth-cache presence, version)\nusage: mg-axi-hook [--help]\n");
9
+ }
10
+ else if (args.length > 0) {
11
+ try {
12
+ const { encode } = await import("@toon-format/toon");
13
+ process.stdout.write(`${encode({ error: `unexpected argument \`${args[0]}\` for \`mg-axi-hook\``, code: "VALIDATION_ERROR", help: ["Run `mg-axi-hook` with no arguments for the session summary"] })}\n`);
14
+ }
15
+ catch {
16
+ process.stdout.write(`error: unexpected argument \`${args[0]}\` for \`mg-axi-hook\`\n`);
17
+ }
18
+ process.exitCode = 2;
19
+ }
20
+ else {
21
+ try {
22
+ const { VERSION } = await import("../version.js").catch(() => ({ VERSION: "unknown" }));
23
+ const { hookSummary } = await import("../hook.js");
24
+ process.stdout.write(await hookSummary({ version: VERSION }));
25
+ }
26
+ catch {
27
+ process.stdout.write("mg: status unavailable\n");
28
+ }
29
+ process.exitCode = 0;
30
+ }
31
+ export {};