Skip to content

refactor: audit-driven cleanup wave + zero out PR/issue backlog#111

Merged
mghabin merged 1 commit intomainfrom
refactor/audit-cleanup-wave
May 2, 2026
Merged

refactor: audit-driven cleanup wave + zero out PR/issue backlog#111
mghabin merged 1 commit intomainfrom
refactor/audit-cleanup-wave

Conversation

@mghabin
Copy link
Copy Markdown
Owner

@mghabin mghabin commented May 2, 2026

Audit-driven cleanup folding in all 6 open Dependabot PRs + closing the 4 high-impact findings from this session's parallel audit run + addressing all 3 open issues.

Headline change: 3 docs prescribed a forbidden pattern

docs/validation.md:54,57,156 and docs/decision-trees.md:76,82 showed [Authorize(Roles = "...")] — explicitly forbidden by DOCTRINE.md (added in PR #110): silently no-ops on Microsoft.Identity.Web JWT schemes (MapInboundClaims=false looks for ClaimTypes.Role, Entra emits short-name roles). Readers copying these snippets would deploy a privilege-escalation bug.

Six Dependabot PRs absorbed

Each as part of this single squash, with their breakage transitively fixed:

All 4 audit waves

  • A — Doc doctrine: validation.md + decision-trees.md fixed; operations.md env-rename runbook marked historical.
  • B — Test coverage (+21 new tests, 72 → 93):
    • new EntraAuthForwardedHeadersExtensionsTests (X-Forwarded-* + ForwardLimit cap)
    • rate-limiter long-form claim Theory (http://schemas.microsoft.com/identity/claims/{tid,oid,appid}) + Retry-After RFC 6585 + 2-user partition isolation
    • claims-challenge RFC 7235 edge-case Theory (case-insensitive params, comma-in-quoted, empty Bearer, multi-Bearer first-wins, non-Bearer scheme)
    • bug fix surfaced by Retry-After test: MetadataName.RetryAfter isn't always populated; falls back to opts.Window so 429 responses always carry the header.
  • C — Infra:
    • log-analytics.bicep retentionInDays parameterized (prod=90d audit floor, others=30d)
    • new modules/budget.bicep (Microsoft.Consumption/budgets, Forecasted+Actual at 80/100%, prod $50/mo, others $10/mo)
    • new drift-detection.yml workflow (weekly az deployment group what-if, matrix [ci, ppe], ::warning:: on drift)
    • container-app transport='https' for prod
  • D — Polish:
    • DownstreamApiClient.ProbeAsync ConfigureAwait(false) (library code)
    • cd-cleanup.yml PPE auth failure fixed via pre-check before azure/login

Issues closed

Verification

Gate Result
dotnet test EntraAuthPatterns.slnx 93/93
az bicep build (every .bicep + .bicepparam) clean
actionlint clean
shellcheck clean
markdownlint-cli2 (CI version) clean

Closes the post-PR110 audit loop addressing all 4 explore agents'
verified findings. Folds in all 6 open Dependabot bumps, fixes the
3 doc-doctrine drifts that contradict DOCTRINE.md, and adds a chunk
of real prod-readiness coverage.

## Wave A — DOCTRINE drift in 3 docs (CRITICAL)

* docs/validation.md (lines 54, 57, 156) — replaced
  '[Authorize(Roles = "Tasks.Process.All")]' (silently no-ops on
  MapInboundClaims=false JWT schemes) with the named-policy form
  '[Authorize(Policy = OrdersAuthorizationPolicies.App)]'. Switched
  generic 'Files.Read' / 'Tasks.Process.All' examples to the FTGO
  domain ('orders.read' / 'Orders.Process'). Added pointer to
  DOCTRINE.md § 'Authorization-policy doctrine'.
* docs/decision-trees.md Tree 4 — same fix on the mermaid + body.
* docs/operations.md — env-rename runbook (lines 184–) marked as a
  template / historical example (the dev->ci rename happened in PR
  #103; current main has no 'dev' tier).

## Wave B — Test coverage gaps (security-relevant)

* tests/EntraAuthForwardedHeadersExtensionsTests.cs (new) — 4 facts
  covering X-Forwarded-{For,Proto,Host} parsing + ForwardLimit cap.
* tests/EntraAuthRateLimiterTests.cs — added Theory cases for
  long-form claim fallbacks (http://schemas.microsoft.com/identity/
  claims/{tenantid,objectidentifier,appid}), unknown-* paths, plus
  partition isolation across 2 distinct users + Retry-After-on-429
  RFC 6585 §3 conformance.
* tests/DownstreamApiClientClaimsChallengeTests.cs — added Theory
  for RFC 7235 edge cases (case-insensitive params, comma in quoted
  value, empty Bearer, multi-Bearer first-wins, non-Bearer scheme).
* src/Ftgo.Auth/EntraAuthRateLimiterExtensions.cs — fixed RFC 6585
  Retry-After header bug surfaced by new test: lease metadata is
  not always populated by sliding-window limiters; fall back to the
  configured Window so 429 responses always carry Retry-After.

## Wave C — Infrastructure adds

* infra/bicep/modules/log-analytics.bicep — retentionInDays now
  parameterized (@minValue 30 @MaxValue 730); azure.bicep wires
  90 d for prod, 30 d for ci/ppe (audit compliance floor for prod).
* infra/bicep/modules/budget.bicep (new) — Microsoft.Consumption/
  budgets with Forecasted+Actual notifications at 80% and 100%.
* infra/bicep/azure.bicep — wires the budget module; defaults
  USD 50/mo for prod, USD 10/mo for ci/ppe.
* infra/bicep/modules/container-app.bicep — transport='https' for
  prod (was 'auto', allowing HTTP fallback).
* .github/workflows/drift-detection.yml (new) — weekly + manual
  dispatch, matrix [ci, ppe], 'az deployment group what-if' with
  ::warning:: annotation if drift detected. Skips tiers whose RG
  doesn't exist (PPE-not-yet-bootstrapped case).

## Wave D — Polish

* src/Ftgo.Auth.Client/DownstreamApiClient.cs — ConfigureAwait(false)
  on the 3 awaits in ProbeAsync (library-code best practice).
* .github/workflows/cd-cleanup.yml — pre-checks AZURE_CLIENT_ID
  before azure/login. Fixes the long-standing PPE auth failure
  (PPE env never bootstrapped, login fails, job fails). Now logs
  ::notice:: and exits 0.

## Wave E — All 6 open Dependabot bumps folded in

* PR #104 (actions group): codeql-action/upload-sarif, markdownlint-
  cli2-action v20->v23, lychee-action SHA refresh.
* PR #105: Microsoft.Identity.Web 4.8.0 -> 4.9.0.
* PR #106: Azure.Monitor.OpenTelemetry.Exporter 1.7.0 -> 1.8.0.
* PR #107: Meziantou.Analyzer + SonarAnalyzer.CSharp.
* PR #108: Microsoft.NET.Test.Sdk 18.4.0 -> 18.5.1.
* PR #109: Scalar.AspNetCore 2.14.4 -> 2.14.9.
* markdownlint v23 introduced new MD060 (table-column-style)
  enforcement; realigned 5 doc tables; disabled MD060 in
  .markdownlint.yaml because emoji-width breaks 'aligned' style
  detection (real-world tables look fine in render).

## Wave F — GitHub issues

* .github/dependabot.yml — added 'Microsoft.Testing.*' to the test
  group so MTP packages bump together (closes #80 follow-up).
* #80, #83, #85 — closed/updated separately (see PR description).

## Verification

* dotnet test EntraAuthPatterns.slnx -> 93/93 (was 72; +21 new).
* az bicep build on every .bicep + .bicepparam clean.
* actionlint .github/workflows/*.yml clean.
* shellcheck scripts/*.sh clean.
* markdownlint-cli2 (CI version) clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant