Found in the 2026-09-17 pre-release sweep (G2, PR #6034 / #5818 W06), on a stack at 026c268d4.
Repro (verified)
/reports → Templates → Identity & Access Review → Create report (defaults: adminDetail: true, dormantDays: 45).
- Generate now.
POST /reports/:id/generate → 200 {"status":"completed"} — correct, an empty-data run completes.
- Download the PDF (staff path) or
GET /api/v1/portal/reports/runs/:id/pdf (server path). Both render the designed identity arm.
The rendered PDF contains:
Administrator sign-in detail — Administrator sign-in detail was switched off for this report, so no individual sign-ins are listed. That is a setting, not a finding.
The report was created with adminDetail: true (select config->>'adminDetail' on the report row = true). The artifact makes a false statement about the reader's own configuration — on exactly the data-gap path this wave exists to keep honest ("unmeasured ≠ zero", "a gap is a gap").
Cause (verified)
apps/api/src/services/identityAccessReport.ts — both early gap returns use emptySummary(), which hardcodes adminSignins: null regardless of cfg.adminDetail:
- L257 restricted-authority refusal (OD-8 = A)
- L270
M365_TENANT_SYNC_ENABLED off
packages/shared/src/reportPdf/identityAccessPdf.ts:291 treats adminSignins === null as "switched off". The happy path at L576 (adminSignins: cfg.adminDetail ? adminSigninRows : null) is the only place null really means "off", so the two meanings are conflated.
The renderer already has the correct branch one else if below — adminRows.length === 0 && signins.total == null → "No interactive sign-in data was available for this period… This is an absence of measurement, not an absence of activity."
On the restricted-scope path it is worse still: the real reason is a deliberate scope refusal, and the PDF blames a setting.
Second defect, same function
emptySummary also sets orgName: null (L161), and both gap returns happen before the organizations lookup at L285. So the data-gap PDF carries no customer name, while the sibling evidence types (threat_detection_review, endpoint_management_review, vulnerability_management) all print Default Organization in their equivalent gap PDFs. A PII-bearing evidence artifact that does not name the customer it is about is a distribution hazard.
Suggested fix
- Pass
adminDetail into emptySummary and distinguish "off" from "not measured" (e.g. adminSignins: cfg.adminDetail ? [] : null, which routes to the existing honest branch), or give the renderer a coverage-derived signal.
- Move the org lookup above both early returns so
orgName is populated on the gap paths.
Everything else in W06 verified fine: the run completes on empty data, the title block reads "Interactive sign-ins for 2026-08-18 to 2026-09-17", "VPN policy review" is absent, identity/signins/dormant are all null (never 0), the limits section prints, the type has no portal generate button (POST /portal/reports/generate with identity_access_review → 400), and OD-12 gating works in both directions.
Found in the 2026-09-17 pre-release sweep (G2, PR #6034 / #5818 W06), on a stack at
026c268d4.Repro (verified)
/reports→ Templates → Identity & Access Review → Create report (defaults:adminDetail: true,dormantDays: 45).POST /reports/:id/generate→200 {"status":"completed"}— correct, an empty-data run completes.GET /api/v1/portal/reports/runs/:id/pdf(server path). Both render the designed identity arm.The rendered PDF contains:
The report was created with
adminDetail: true(select config->>'adminDetail'on the report row =true). The artifact makes a false statement about the reader's own configuration — on exactly the data-gap path this wave exists to keep honest ("unmeasured ≠ zero", "a gap is a gap").Cause (verified)
apps/api/src/services/identityAccessReport.ts— both early gap returns useemptySummary(), which hardcodesadminSignins: nullregardless ofcfg.adminDetail:M365_TENANT_SYNC_ENABLEDoffpackages/shared/src/reportPdf/identityAccessPdf.ts:291treatsadminSignins === nullas "switched off". The happy path at L576 (adminSignins: cfg.adminDetail ? adminSigninRows : null) is the only placenullreally means "off", so the two meanings are conflated.The renderer already has the correct branch one
else ifbelow —adminRows.length === 0 && signins.total == null→ "No interactive sign-in data was available for this period… This is an absence of measurement, not an absence of activity."On the restricted-scope path it is worse still: the real reason is a deliberate scope refusal, and the PDF blames a setting.
Second defect, same function
emptySummaryalso setsorgName: null(L161), and both gap returns happen before theorganizationslookup at L285. So the data-gap PDF carries no customer name, while the sibling evidence types (threat_detection_review,endpoint_management_review,vulnerability_management) all printDefault Organizationin their equivalent gap PDFs. A PII-bearing evidence artifact that does not name the customer it is about is a distribution hazard.Suggested fix
adminDetailintoemptySummaryand distinguish "off" from "not measured" (e.g.adminSignins: cfg.adminDetail ? [] : null, which routes to the existing honest branch), or give the renderer acoverage-derived signal.orgNameis populated on the gap paths.Everything else in W06 verified fine: the run completes on empty data, the title block reads "Interactive sign-ins for 2026-08-18 to 2026-09-17", "VPN policy review" is absent,
identity/signins/dormantare allnull(never0), the limits section prints, the type has no portal generate button (POST /portal/reports/generatewithidentity_access_review→ 400), and OD-12 gating works in both directions.