How we keep your tenant safe

Read-only by design, and verifiable by anyone.

Tenant Strike can see your Microsoft 365 and Azure settings. It cannot change them. And you don’t have to take our word for it — the proof, down to every single API call we make, is published on this page.

01

Read permissions only

When your admin grants access, the 12 Microsoft permissions we request are all read-only — they let us see configuration, not change it. We don't request any "write" permission, because we don't have any code that could use one.

02

No changes to your tenant — ever

Our scanner lists users, reads policies, and checks settings. It cannot create, modify, or delete anything in your Microsoft 365. Every API call we make is logged so you can see exactly what we did.

03

Auditable by anyone

Our build process automatically lists every API call in our code and fails the build if any "write" call sneaks in. Your security team can verify it themselves in five minutes — evidence below.

What we look at

Seven security surfaces — read-only, all of them.

Tenant Strike checks the parts of your Microsoft 365 tenant that attackers target. Here's what each surface covers.

  • Identity

    Sign-in policies, MFA enforcement, admin accounts, guest access. We see how people log in, not what they do once signed in.

  • Email

    DMARC, SPF, DKIM, Defender for Office policies, forwarding rules. We never read message content.

  • Sharing & collaboration

    SharePoint, OneDrive, and Teams external-sharing settings. We see the settings, not the documents being shared.

  • Apps & consent

    Enterprise apps and the permissions they were granted. Useful for spotting OAuth phishing and over-privileged add-ins.

  • Devices

    Intune compliance, BitLocker, Defender for Endpoint coverage. We see device status, not what's on the device.

  • Logging & alerting

    Whether your audit log is on, where security alerts go, and what gets recorded. The forensics-readiness surface.

  • External attack surface

    What your tenant looks like from the public internet — exposed ports, expired TLS, DNS health. Outside-in, not inside your tenant. We also make read-only HTTPS requests to the portals and domains you list, to read their product/version banners — only to hosts under a domain Microsoft has verified you own, and only when you opt in.

  • Configuration metadata only.
    Never file, mailbox, or chat content.

Permissions we request

The exact list — all read-only.

These are the 12 Microsoft Graph permissions your admin grants at consent time. Every one ends in .Read or .Read.All. There is no .ReadWrite in the list.

For your security team — technical evidence

Reproduce it yourself

Tenant Strike's GraphReadOnlyClient (src/auth/graph.ts) exposes only GET. This manifest is regenerated on every build by scripts/build-graph-endpoints.ts, which fails the build if any write verb (post/patch/put/delete) is detected on a receiver typed as GraphReadOnlyClient. The list below is the complete set of Microsoft Graph endpoints Tenant Strike calls, with source-file references for line-level verification.

TypeScript AST walker (src/auth/graph-endpoint-extractor.ts) — resolves the receiver's type to GraphReadOnlyClient regardless of variable name. Catches aliases and `as any` casts that the prior regex-based extractor could have missed.
Endpoints
47
Unique paths
37
Source files
28
Write verbs
0

Endpoint manifest · auto-extracted

src/auth/graph-cache.ts (3)
  • GET · v1.0<dynamic>L21
  • GET · v1.0<dynamic>L26
  • GET · v1.0<dynamic>L31
src/auth/verify-domain.ts (2)
  • GET · v1.0/domainsL68
  • GET · v1.0/domainsL177
src/branding/tenant-logo.ts (4)
  • GET · v1.0/organizationL52
  • GET · v1.0/organization/${orgId}/branding/localizations/default/squareLogoL61
  • GET · v1.0/organization/${orgId}/branding/localizations/default/bannerLogoL62
  • GET · v1.0/organization/${orgId}/branding/localizations/default/squareLogoDarkL64
src/catalog/licenses.ts (1)
  • GET · v1.0/subscribedSkusL65
src/checks/apps/admin-consent-workflow.ts (1)
  • GET · v1.0/policies/adminConsentRequestPolicyL28
src/checks/apps/expiring-app-secrets.ts (1)
  • GET · v1.0/applicationsL37
src/checks/apps/risky-app-permissions.ts (3)
  • GET · v1.0/servicePrincipals(appId='00000003-0000-0000-c000-000000000000')L59
  • GET · v1.0/servicePrincipalsL83
  • GET · v1.0/servicePrincipals/${sp.id}/appRoleAssignmentsL95
src/checks/apps/user-consent-policy.ts (1)
  • GET · v1.0/policies/authorizationPolicyL57
src/checks/devices/defender-onboarding-coverage.ts (1)
  • GET · v1.0/deviceManagement/managedDevices/${d.id}/windowsProtectionStateL93
src/checks/devices/defender-unresolved-alerts.ts (1)
  • GET · v1.0/security/alerts_v2L55
src/checks/devices/intune-shared.ts (4)
  • GET · v1.0/deviceManagement/managedDevicesL79
  • GET · v1.0/deviceManagement/deviceCompliancePoliciesL89
  • GET · v1.0/deviceAppManagement/iosManagedAppProtectionsL106
  • GET · v1.0/deviceAppManagement/androidManagedAppProtectionsL115
src/checks/email/inbox-forwarding-rules.ts (2)
  • GET · v1.0/usersL69
  • GET · v1.0/users/${u.id}/mailFolders/inbox/messageRulesL99
src/checks/identity/authentication-methods.ts (1)
  • GET · v1.0/policies/authenticationMethodsPolicyL43
src/checks/identity/ca-policies-shared.ts (1)
  • GET · v1.0/policies/conditionalAccessPoliciesL61
src/checks/identity/guest-invite-policy.ts (1)
  • GET · v1.0/policies/authorizationPolicyL35
src/checks/identity/privileged-roles-shared.ts (3)
  • GET · v1.0/directoryRolesL63
  • GET · v1.0/directoryRoles/${role.id}/membersL73
  • GET · v1.0/users/${m.id}L89
src/checks/identity/security-defaults-status.ts (1)
  • GET · v1.0/policies/identitySecurityDefaultsEnforcementPolicyL20
src/checks/identity/stale-guest-users.ts (1)
  • GET · v1.0/usersL29
src/checks/identity/too-many-global-admins.ts (2)
  • GET · v1.0/directoryRolesL35
  • GET · v1.0/directoryRoles/${gaRole.id}/membersL63
src/checks/identity/users-without-mfa.ts (1)
  • GET · v1.0/reports/authenticationMethods/userRegistrationDetailsL27
src/checks/logging/service-health-notifications.ts (1)
  • GET · v1.0/organizationL50
src/checks/logging/unified-audit-log.ts (2)
  • GET · v1.0/auditLogs/directoryAuditsL36
  • GET · v1.0/auditLogs/signInsL71
src/checks/sharing/cross-tenant-shared.ts (2)
  • GET · v1.0/policies/crossTenantAccessPolicy/defaultL52
  • GET · v1.0/policies/crossTenantAccessPolicy/partnersL61
src/checks/sharing/sharepoint-settings-shared.ts (1)
  • GET · v1.0/admin/sharepoint/settingsL71
src/offline/run.ts (1)
  • GET · v1.0/domainsL61
src/scan.ts (1)
  • GET · v1.0/domainsL207
src/secure-score/fetch.ts (2)
  • GET · v1.0/security/secureScoresL31
  • GET · v1.0/security/secureScoreControlProfilesL67
src/vulnerability-watch/inventory.ts (2)
  • GET · v1.0/deviceManagement/detectedAppsL46
  • GET · v1.0/deviceManagement/managedDevicesL49

Azure Resource Manager · machine-verified read-only

Every ARM endpoint we call — for Azure scanning.

The same automated build check covers our Azure code: every call we make to Azure is a read. We never change, create, or delete anything in your subscriptions.

Endpoints
6
Unique paths
6
Source files
66
Write verbs
0
Show all 6 ARM endpoints
  • GET/subscriptions/${subId}/providers/Microsoft.Security/pricings
  • POST/providers/Microsoft.ResourceGraph/resources
  • GET/subscriptions/${subId}/providers/Microsoft.Authorization/roleAssignments
  • GET/subscriptions/${subId}/providers/Microsoft.Insights/diagnosticSettings
  • GET/subscriptions/${subId}/providers/Microsoft.Security/autoProvisioningSettings
  • GET/subscriptions

The one POST in the list is Azure's Resource Graph query endpoint — Azure requires POST there only because the (read-only) query is too long to fit in a URL. Nothing is changed by it, and our build check treats it as the single documented exception.

Comfortable with how we work?

See what we'd find in your tenant.

No write scopes, no agents, and a setup that takes a few minutes. See what your tenant looks like to an attacker.