mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-06 17:36:53 +01:00
Driven by a parity comparison between BCAppsBCQuality PR #27 and BCAppsCampAIRHack PR #162 on byte-identical content: | | BCQuality | AIRHack | |--|--|--| | Total findings | 6 | 10 | | Performance | 0 | 4 | | Security | 0 | 5 | Standalone runs of al-security-review and al-performance-review against the SAME diff produced the expected matches (rimd-on-read-only via inherent-permissions-minimal-grant; redundant-Get via avoid-redundant-get-when-record-already-loaded). The miss in the live run is therefore not a knowledge-coverage gap and not a worklist filtering issue. It is attention dilution inside the al-code-review super-skill, which the model collapses into one rolled-up generation pass on real-size PRs. Changes: microsoft/skills/review/al-code-review.md - New 'Execution discipline (mandatory)' subsection in the Action step that explicitly forbids collapsing leaves into one shared reasoning pass and requires each sub-skill to walk its Source -> Relevance -> Worklist -> Action steps as its own iteration before the next leaf starts. - Self-review pass is now described as the final, mandatory iteration with a concrete candidate-category checklist (architecture-level smells, error-handling gaps, magic constants, privacy/telemetry, resource lifecycle). Returning zero agent findings on a real-size diff is explicitly defined as a defect. microsoft/skills/review/al-{security,performance,privacy,style, upgrade,ui}-review.md - Each leaf skill now states that when an unambiguous .good.al companion exists, findings[].suggested-code should carry the literal replacement for the source lines. Closes the one-click-suggestion gap created when BCQ#19 only updated al-code-review. microsoft/knowledge/security/case-must-handle-unknown-enum-values.{md, bad.al, good.al} - New article: case over a security-sensitive enum (Authentication Type, Authorization Mode, Identity Provider, Permission Scope, Encryption Algorithm) MUST have an else arm. Without it, an unknown enum value silently falls through and the security context never initialises. The bad sample is lifted from the SharePoint Graph helper that triggered the parity finding. microsoft/knowledge/performance/instream-length-unreliable-for-bc- streams.{md,bad,good} - New article: InStream.Length returns 0 / partial for HTTP-response streams and some file-API streams, breaking size-threshold branching in upload code. Bad sample is the simple-vs-chunked Graph upload pattern; good sample materialises into a Temp Blob first. Companion change: microsoft/BCAppsBCQuality#28 extends the orchestrator's bootstrap prompt with the same per-iteration execution discipline and adds a CI warning when a >5-file PR returns zero agent findings (regression signal). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
55 lines
3.5 KiB
Markdown
55 lines
3.5 KiB
Markdown
---
|
|
bc-version: [all]
|
|
domain: security
|
|
keywords: [case, else, enum, fallthrough, authentication, authorization, default, switch]
|
|
technologies: [al]
|
|
countries: [w1]
|
|
application-area: [all]
|
|
---
|
|
|
|
# `case` over an enum must handle unknown values via `else`
|
|
|
|
## Description
|
|
|
|
A `case` statement that branches on an enum value and lists only the values the author knows about silently falls through when the runtime value is one the code does not name. For business-logic enums, falling through usually means "do nothing"; for security-relevant enums — authentication type, authorization mode, identity provider, encryption strategy, permission scope — falling through means **the code path that was supposed to set up the security context never runs, and the operation proceeds with whatever state the variables had before the `case`**.
|
|
|
|
A canonical example, lifted from real review traffic:
|
|
|
|
```al
|
|
case SharePointAccount."Authentication Type" of
|
|
SharePointAccount."Authentication Type"::"Client Secret":
|
|
GraphAuthInterface := GraphAuthClientCredentials;
|
|
SharePointAccount."Authentication Type"::Certificate:
|
|
GraphAuthInterface := GraphAuthCertificate;
|
|
end;
|
|
GraphClient.Initialize(GraphAuthInterface);
|
|
```
|
|
|
|
If a new authentication type is added to the enum, or if a database row carries a value the deployed code does not yet handle, `GraphAuthInterface` is whatever the previous caller left in it (or default-initialised), and the client initialises against an unauthenticated or wrongly-authenticated context. The compiler does not warn — enums are not closed sets to the AL type system the way unions are in other languages.
|
|
|
|
The same shape shows up outside security: postings codeunits that handle two of three document types and silently skip the third; tax computation that branches on calculation method; report layouts that branch on output format. Wherever a `case` over an enum determines what code path executes, an `else` branch with a controlled error (or a deliberate documented no-op) is required.
|
|
|
|
## Best Practice
|
|
|
|
Add an `else` branch to every `case` statement that branches on an enum value when the code paths matter. For security-sensitive branches, raise a `Error` with a message that names the unsupported value:
|
|
|
|
```al
|
|
case SharePointAccount."Authentication Type" of
|
|
SharePointAccount."Authentication Type"::"Client Secret":
|
|
GraphAuthInterface := GraphAuthClientCredentials;
|
|
SharePointAccount."Authentication Type"::Certificate:
|
|
GraphAuthInterface := GraphAuthCertificate;
|
|
else
|
|
Error(UnsupportedAuthTypeErr, SharePointAccount."Authentication Type");
|
|
end;
|
|
```
|
|
|
|
For deliberate no-op fall-through, document it: `else // intentional: format X is a passthrough.` so reviewers see the choice was made rather than forgotten. Pair the `else` arm of a security branch with telemetry — an unsupported value reaching this point in production is a deployment signal worth surfacing.
|
|
|
|
See sample: `case-must-handle-unknown-enum-values.good.al`.
|
|
|
|
## Anti Pattern
|
|
|
|
`case` over an enum with no `else`, used to choose which authentication, authorization, or security-state-initialising code path runs. Detection signal: a `case` whose arms write to a single shared output (an interface variable, a credentials record, a permission token) with no `else` arm. The narrower signal — a `case` whose value type is a security-related enum (`Authentication Type`, `Authorization Mode`, `Identity Provider`, `Permission Scope`, `Encryption Algorithm`) — is the high-confidence anti-pattern.
|
|
|
|
See sample: `case-must-handle-unknown-enum-values.bad.al`.
|