feat: handle Authorization_RequestDenied errors with informative message#254
feat: handle Authorization_RequestDenied errors with informative message#254bgdnext64 wants to merge 3 commits into
Conversation
Detect Authorization_RequestDenied errors from Microsoft Graph / Azure AD early in the authorization error parser and return a specific error message explaining these require admin consent or Global Administrator role and cannot be automatically discovered by MPF. Links to Entra ID docs. Closes #45
Fixes govulncheck failures for GO-2026-4947, GO-2026-4946, GO-2026-4870, GO-2026-4866, GO-2026-4865, GO-2026-4864, GO-2026-4869 in crypto/x509, crypto/tls, html/template, internal/syscall/unix, and archive/tar.
There was a problem hiding this comment.
Pull request overview
Adds early detection for Microsoft Graph / Entra Authorization_RequestDenied authorization failures so MPF can return a clearer, actionable error when required permissions can’t be inferred automatically (admin consent / Global Admin required), aligning with Issue #45.
Changes:
- Detect
Authorization_RequestDeniedin the authorization error parser and return a dedicated explanatory error message with a docs link. - Add a unit test covering the new
Authorization_RequestDeniedhandling path. - Update the Go version directive in
go.mod.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| pkg/domain/authorizationErrorParser.go | Adds early check for Authorization_RequestDenied and returns a more informative error. |
| pkg/domain/authorizationErrorParser_test.go | Adds a unit test validating Authorization_RequestDenied returns an error and nil permissions map. |
| go.mod | Bumps the go directive patch version. |
| log.Warnln("Authorization_RequestDenied error detected. This error originates from Microsoft Graph / Azure AD and cannot be resolved by MPF.") | ||
| return nil, fmt.Errorf("Authorization_RequestDenied error detected: %w", | ||
| errors.New("this error indicates insufficient Azure AD / Microsoft Graph API privileges. "+ | ||
| "These permissions require admin consent or Global Administrator role and cannot be automatically discovered by MPF. "+ | ||
| "See https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference for details")) |
There was a problem hiding this comment.
Using fmt.Errorf with %w to wrap a freshly-created errors.New value doesn't provide useful wrapping semantics (callers can't reliably errors.Is/As against it) and makes the error chain noisy. Prefer returning a single errors.New / fmt.Errorf without %w, or define a package-level sentinel error (or typed error) and wrap that so callers can detect Authorization_RequestDenied programmatically.
There was a problem hiding this comment.
Good catch — switched to a package-level sentinel (ErrAuthorizationRequestDenied) so callers can use errors.Is to detect this case. The double-wrap with a freshly-created errors.New is gone.
maniSbindra
left a comment
There was a problem hiding this comment.
Could you also add a minimal e2e test for this? The repo has a well-established pattern for Terraform e2e tests (e.g., e2eTerraformInvalid_test.go, e2eTerraformAuthPermissionMismatch_test.go). A Terraform sample that exercises the Authorization_RequestDenied path (e.g., an azuread_group resource) with a corresponding e2e test would validate the full flow end-to-end.
|
|
||
| // Check for Authorization_RequestDenied errors from Microsoft Graph / Azure AD. | ||
| // These errors do not contain specific permission details that MPF can parse. | ||
| // They typically require Azure AD admin consent or Global Administrator privileges. |
There was a problem hiding this comment.
The early return here could discard parseable permissions from mixed error messages. Terraform errors can contain multiple error types in one payload — e.g., both Authorization_RequestDenied and AuthorizationFailed entries. With this placement, MPF would bail out and lose all extractable scope/permission pairs.
Consider moving this check to after the existing parse attempts, and only returning the Authorization_RequestDenied guidance if no permissions were extracted (e.g., in the len(resMap) == 0 fallback path). That way MPF still extracts what it can and only surfaces this message when there's truly nothing parseable.
There was a problem hiding this comment.
Agreed, this was the right call. Moved the check out of the early-return path. Parsing now runs first and the Authorization_RequestDenied guidance is only returned in the len(resMap) == 0 fallback, so mixed payloads with parseable AuthorizationFailed entries still produce the scope/permission pairs.
| log.Warnln("Authorization_RequestDenied error detected. This error originates from Microsoft Graph / Azure AD and cannot be resolved by MPF.") | ||
| return nil, fmt.Errorf("Authorization_RequestDenied error detected: %w", | ||
| errors.New("this error indicates insufficient Azure AD / Microsoft Graph API privileges. "+ | ||
| "These permissions require admin consent or Global Administrator role and cannot be automatically discovered by MPF. "+ |
There was a problem hiding this comment.
The linked URL points to the Entra built-in roles reference, but this error is typically about Microsoft Graph API application permissions that require admin consent. More actionable links would be:
- Microsoft Graph permissions reference — explains the Graph API permissions model and admin consent requirements
- Terraform AzureAD provider service principal configuration — already referenced in this repo's own
docs/known-issues-and-workarounds.MD
Suggestion:
"See https://learn.microsoft.com/en-us/graph/permissions-reference for Microsoft Graph permissions "+
"and https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/guides/service_principal_configuration for Terraform AzureAD provider setup"))There was a problem hiding this comment.
Updated the message to point at the Microsoft Graph permissions reference and the Terraform AzureAD provider service-principal configuration guide instead of the Entra built-in roles page. Those are the actually-actionable links for this scenario.
| assert.Nil(t, spm) | ||
| assert.Contains(t, err.Error(), "Authorization_RequestDenied") | ||
| assert.Contains(t, err.Error(), "Azure AD / Microsoft Graph API privileges") | ||
| } |
There was a problem hiding this comment.
Good coverage for the pure Authorization_RequestDenied case. Could you also add a test for a mixed error message that contains both Authorization_RequestDenied and parseable AuthorizationFailed / Authorization failed entries? That would guard against the early-return regression where extractable permissions are silently lost.
There was a problem hiding this comment.
Added TestMixedAuthorizationRequestDeniedAndAuthorizationFailedError which covers exactly that — an error blob containing both an Authorization_RequestDenied line and a parseable AuthorizationFailed entry. It asserts the Microsoft.Storage/storageAccounts/write permission is still extracted, so the regression you flagged would be caught.
…s links, mixed-error unit test, terraform e2e test
|
One thing worth flagging on the CI side: the Lint and CI Build/Unit Test jobs are red on this push, but I'm fairly sure none of it is from the changes in this PR. Every failing job dies at the same step — the A few things that point at this being upstream/infra rather than the PR:
Locally If this keeps reproducing for others too, the cleanest fix is probably to pin |
|
Just a heads up on testing: in addition to the unit tests, I ran the new Result: PASS in about 145 seconds. The Terraform apply hit a real So the production path — not just the unit-test simulated string — is verified. |
Detect Authorization_RequestDenied errors from Microsoft Graph / Azure AD early in the authorization error parser and return a specific error message explaining these require admin consent or Global Administrator role and cannot be automatically discovered by MPF. Links to Entra ID docs.
Closes #45