Repository navigation
New-PfbObjectStoreAccessPolicyRule omits the required names query parameter; audit the 23 cmdlets that build an empty @{} body #106
Description
Activity
juemerson-at-purestorage commented
on Aug 20, 2026 CollaboratorAuthorMore actionsPart 2 audit complete
Audited the full 23-cmdlet population against all 29 published OpenAPI specs (
fb2.0–fb2.28) atorigin/main@415f05f19fea898086598cb5598299b2f819894d.The current exact empty-body convention appears in 22 cmdlets; the 23rd historical row,
New-PfbObjectStoreAccountExport, was repaired by #101.Results
Of the 21 new audit candidates:
- 1 confirmed impossible-through-public-interface defect
- 1 typed-but-not-enforced contract mismatch
- 11 satisfied required contracts
- 8 legal empty requests
- 0 unresolved
The two controls behaved as expected:
New-PfbObjectStoreAccessPolicyRule: reproduced the known Part 1 omission of required query parameternames.New-PfbObjectStoreAccountExport: verified the New-PfbObjectStoreAccountExport cannot create an export at all: deadnameskey, wrong identifying parameter, and the requiredserverbody field is never sent (Update- help examples also send an invalid field) #101 repair supplies required body propertyserverthrough typed-ServerName/-ServerIdpaths.
New findings
1.
New-PfbApiClientcannot supply required body fields through typed parametersPOST /api-clientsrequires:public_keyin REST 2.0–2.28max_rolein REST 2.0–2.18
Public/Admin/New-PfbApiClient.ps1:29-45exposes only-Name,-Attributes, and-Array. The required body fields can only be supplied by hand-building-Attributes, which this issue defines as an escape hatch rather than the intended typed interface.A minimal invocation binds successfully but sends
{}withoutpublic_key:New-PfbApiClient -Name 'automation-client'
Classification: confirmed impossible through the public typed interface.
2.
Update-PfbLegalHoldEntitydoes not enforce requiredreleasedPATCH /legal-holds/held-entitiesrequires query parameterreleasedin every supported version, REST 2.17–2.28.Public/Misc/Update-PfbLegalHoldEntity.ps1:63-64exposes typed-Released, but it is optional. Line 91 sendsreleasedonly when explicitly bound, so this valid PowerShell invocation reaches the API without the required key:Update-PfbLegalHoldEntity -Name 'fs1' -Recursive $true
The cmdlet’s own examples also include calls that omit
-Released.Classification: typed but not enforced.
Proposed follow-up split
I suggest separate follow-ups for:
- Add typed required-body coverage to
New-PfbApiClient. - Enforce required
-ReleasedonUpdate-PfbLegalHoldEntityand correct its examples. - Extend drift reporting to detect typed parameters that exist but do not enforce API requiredness.
- Separately investigate
New-PfbNlmReclamation -Name, which sends an undeclarednameskey for an API operation described as system-wide.
Optional body-property coverage found on otherwise valid cmdlets remains broader usability work for #57/#65 rather than a required-field defect.
No source changes, live calls, or GitHub mutations were made during this audit.
juemerson-at-purestorage commented
on Aug 21, 2026 CollaboratorAuthorMore actionsPart 2's audit list is short by three, and the reason is worth recording because it will bite the next sweep of this convention too.
The list of 23 was built by matching the exact literal:
$body = if ($Attributes) { $Attributes } else { @{} }
Three cmdlets use a
.Clone()variant, which that pattern does not match:$body = if ($Attributes) { $Attributes.Clone() } else { @{} }
Public/DirectoryService/New-PfbLocalDirectoryService.ps1Public/DirectoryService/New-PfbLocalGroup.ps1Public/FileSystem/New-PfbFileSystemExport.ps1
The arithmetic, since the numbers move: the body lists 23 paths, one of which (
New-PfbObjectStoreAccountExport.ps1) it already annotates as fixed by #101 and which no longer uses the one-liner at all — so 22 still match the literal today. Adding the three above puts the convention at 25 cmdlets in scope. Same semantics for audit purposes either way — an empty body plus whatever query keys the cmdlet chose to send.New-PfbLocalGroupis not a hypothetical: it is confirmed broken on the wire for exactly the failure mode this issue is about, a required query key the cmdlet never sends. Filed separately as #136 with live evidence, since the missing key there islocal_directory_service_namesrather thannamesand the fix spans three cmdlets in that family. Flagging it here so the audit does not re-derive it, and soNew-PfbLocalDirectoryServiceandNew-PfbFileSystemExportget checked rather than skipped.Worth suggesting for the audit itself: match on
else { @{} }alone, or on the AST, rather than on the whole assignment..Clone()is a semantically meaningful variation someone added deliberately (it avoids mutating the caller's hashtable — arguably the better form, and the other 22 could be argued to have a latent aliasing bug), so more such variants are likely as this convention gets touched. A literal-string sweep silently under-reports, and an under-reporting audit reads exactly like a complete one.juemerson-at-purestorage commented
on Aug 21, 2026 CollaboratorAuthorMore actionsPart 2 audit: complete
The Part 2 audit is done. Every cmdlet building the semantic empty-
@{}body was checked against every published REST version for required query parameters and required body properties, and both surviving findings were sent to a fresh reviewer instructed only to refute them. Both survived as CONFIRMED.Two real defects, both being fixed directly rather than filed as separate issues, since they are exactly what Part 2 was chartered to find:
Cmdlet Endpoint What the contract requires What the cmdlet did New-PfbApiClientPOST /api-clientsbody public_keyrequired 2.0-2.28;max_rolerequired 2.0-2.18Exposed only -Nameand-Attributes, soNew-PfbApiClient -Name 'x'bound cleanly and sent{}. No typed route to either required field existedUpdate-PfbLegalHoldEntityPATCH /legal-holds/held-entitiesquery releasedrequired: trueon 2.17-2.28-Releasedwas optional and the key was sent only when explicitly bound, so-Name 'fs1' -Recursive $trueomitted it. Two of the cmdlet's own examples documented that shapeThe first is the "impossible through the public interface" case this issue is named for. The second is a new category the issue did not anticipate — typed but not enforced.
-Releasedexisted, which is why nothing flagged it:Reports/PfbApiDriftReport.jsonscores typed-parameter presence, not whether the parameter is mandatory, so the endpoint read as fully covered. That blind spot is filed separately as a tooling follow-up.Neither shared helper closes either gap.
Invoke-PfbApiRequestcan centrally inject onlycontext_names;Assert-PfbApiCapabilityversion-checks keys already present rather than supplying missing ones.Everything else is clean
- 11 cmdlets satisfy their required contract already — a mandatory
-Namewriting a requirednameskey, in each case with the source line confirmed rather than assumed. - 8 cmdlets issue a legally empty request: no required body property and no unsatisfied required query parameter in any published version.
New-PfbOidcIdp,New-PfbCertificateGroup,New-PfbDirectoryServiceRole,New-PfbNlmReclamation,New-PfbLegalHoldEntity,New-PfbObjectStoreTrustPolicyRule,Update-PfbObjectStoreAccessPolicyRule,Update-PfbObjectStoreTrustPolicyRule.
Optional body properties reachable only through
-Attributesare a real usability gap on many of these, but they are not required-field defects and stay with #57/#65.Two controls, both behaved
- Part 1 positive control.
New-PfbObjectStoreAccessPolicyRulereproduced the original missing-namescondition on the audit's base commit, confirming the method detects the very defect this issue was opened for. That fix has since landed onmainin Fix P0 wire contracts and output shape #134, so Part 1 is closed out. - Fixed-New-PfbObjectStoreAccountExport cannot create an export at all: dead
nameskey, wrong identifying parameter, and the requiredserverbody field is never sent (Update- help examples also send an invalid field) #101 control.New-PfbObjectStoreAccountExportcame back clean, confirming the method does not report a repaired cmdlet as broken. Its typed-plus-raw parameter-set shape is the pattern theNew-PfbApiClientfix follows.
A correction to this issue's own arithmetic
The list of 23 was built by matching one exact literal,
$body = if ($Attributes) { $Attributes } else { @{} }. Three cmdlets use a.Clone()variant that the pattern does not match:Public/DirectoryService/New-PfbLocalDirectoryService.ps1Public/DirectoryService/New-PfbLocalGroup.ps1Public/FileSystem/New-PfbFileSystemExport.ps1
So the convention is 25 cmdlets, not 23 — of which 22 still match the original literal today,
New-PfbObjectStoreAccountExporthaving been rewritten by #101. All three were audited.New-PfbLocalGroupis not hypothetical: it is confirmed broken on the wire for exactly this failure mode, a required query key the cmdlet never sends. That is #136, where the missing key islocal_directory_service_namesand the fix spans three cmdlets. The other two came back clean.Worth noting for the next sweep of this convention: match on
else { @{} }or on the AST, not on the whole assignment..Clone()is a deliberate improvement — it avoids mutating the caller's hashtable, which arguably makes the other 22 the ones carrying a latent aliasing bug — so more such variants are likely as this convention gets touched. A literal-string sweep under-reports, and an under-reporting audit reads exactly like a complete one.Follow-ups
Three, all ancillary to the required-field question rather than part of it:
- Drift reporting cannot distinguish a typed optional parameter from a typed mandatory guarantee, which is why the
releaseddefect was invisible to it. New-PfbNlmReclamation -Nameis a mandatory dead selector — the endpoint declares nonamesparameter and the operation is array-wide.Update-PfbLegalHoldEntity -Nameis a mandatory but unusable selector:namesis declared on the endpoint, but a held entity has no name (LegalHoldHeldEntitycarries onlyfile_system,legal_hold,path,status), and a live array rejects a request selected only by it. Found while live-verifying thereleasedfix.
Method
Endpoint literals were derived with
Get-PfbModuleCalledEndpointsrather than read by eye; all resolved, none required guessing a dynamic endpoint. Requirements were resolved withGet-PfbSchemaPropertyDetails -MaxDepth 8, which walks$refandallOfand unions every visitedrequiredarray, and by resolving path-item and operation parameter arrays throughResolve-PfbRefand accepting only resolved query parameters withrequired: true. Versions were iterated as strings from file names — a numeric literal silently truncates2.20to2.2and reads the wrong spec.Data/PfbCapabilityMap.jsonandReports/PfbApiDriftReport.jsonwere cross-checked but never treated as the verdict.The audit itself changed nothing in the repository, made no live call, and touched no issue.
- 11 cmdlets satisfy their required contract already — a mandatory
- added a commit that references this issue
on Aug 21, 2026
Found while fixing #101. Two related things: one confirmed defect, and a systematic audit the defect implies.
Part 1 — confirmed defect:
New-PfbObjectStoreAccessPolicyRulecannot name a rulePOST /object-store-access-policies/rulesdeclares these query parameters (tools/specs/fb2.28.json):X-Request-ID,context_names,enforce_action_restrictions,names,policy_ids,policy_namesThe
namesparameter resolves to the shared componentNames_required, which is declaredrequired: true:Public/ObjectStore/New-PfbObjectStoreAccessPolicyRule.ps1:51sends onlypolicy_names:The rule's own name is never sent, and the API requires it. The cmdlet exposes no parameter that could supply it —
-PolicyNameis the containing policy, not the rule.Needs a
-Nameparameter mapping to thenamesquery key. Note this is one of the cases where-Nameis genuinely correct, unlike #64/#87/#99 wherenameswas the dead key — the distinction is whether the spec declares it, which here it does, as required.Related but not a defect here: the empty
@{}body is legal on this endpoint.PolicyRuleObjectAccessPosthas norequiredarray;actions,conditions,effect, andresourcesare all optional. A rule created with no body is presumably useless in practice, but it is not a schema violation. Exposing typed parameters for those four fields is worth considering as part of the usability work in #57 / #65, not as a wire fix.Part 2 — audit: 23 cmdlets build an empty body the same way
$body = if ($Attributes) { $Attributes } else { @{} }is a module-wide convention, not a one-off:The shape itself is harmless where the endpoint requires nothing. It becomes a broken-on-arrival create wherever the endpoint declares a required body field or a required query parameter that the cmdlet has no parameter to supply. That is exactly how #101 shipped:
POST /object-store-account-exportsdeclares"required": ["server"], andNew-PfbObjectStoreAccountExporthad no way to send it, so the cmdlet could never create anything.For each of the 23, check against the spec:
requiredarray? If so, can the cmdlet supply every required field without the caller hand-rolling-Attributes?required: truequery parameter (the*_requiredcomponent variants are the tell)? If so, does the cmdlet send it?-Attributes, treat that as broken by default —-Attributesis an escape hatch, not the intended interface.Watch for schemas nesting
allOftwo levels deep; a single-level walk silently returns an incomplete field list and will make an affected cmdlet look clean.Priority
Part 1 is P0 for that one cmdlet — it cannot create a rule at all. Part 2 is P1: the audit is likely to surface several more creates in the same state, and should be sized before being scheduled.
Related
nameskey, wrong identifying parameter, and the requiredserverbody field is never sent (Update- help examples also send an invalid field) #101 — the instance of this class that prompted the audit-Attributes