Course outline

Secure Software Development / Basic: outcomes, models, and decisions / Lesson 5

Design safe defaults and enforceable invariants

Choose explicit behavior for absent policy, dependency uncertainty, and state transitions.

About 4 min readContent reviewed
On this page

Lesson goals

  • Separate a safe default from a universal deny-everything prescription.
  • Place a business invariant at a boundary that can enforce it consistently.

Make the unspecified case explicit

An unspecified permission must not quietly become an authorization grant. For the workshop export, missing assignment information should prevent contact disclosure while producing a documented operational outcome. That does not mean every subsystem should always stop in the same way: safety, integrity, confidentiality, and availability can create different product requirements. Decide the behavior for each protected action and record the tradeoff. An implementation that treats an absent policy object as โ€œallowโ€ has made a consequential design decision whether or not anyone intended it. Defaults deserve deliberate review.

Represent unknown separately from false

A dependency can report assigned, not assigned, or unable to determine. The protected disclosure may be refused in the latter two cases, but diagnostics and recovery should distinguish them. The first program models those three outcomes. It avoids a broad exception handler that converts every error into an allowed result. It also avoids calling unknown equivalent to a permanent policy denial. The model contains declared facts only; it does not query an assignment service, prove availability, or specify the public HTTP mapping. Those decisions depend on the approved contract.

python A local decision with explicit uncertainty
def export_decision(assignment_state):
    if assignment_state == "assigned":
        return {"allow": True, "reason": "policy-satisfied"}
    if assignment_state == "not-assigned":
        return {"allow": False, "reason": "policy-denial"}
    return {"allow": False, "reason": "policy-unavailable"}

assert export_decision("assigned")["allow"] is True
assert export_decision("not-assigned")["reason"] == "policy-denial"
assert export_decision("unknown")["reason"] == "policy-unavailable"
print("three policy states distinguished")
Expected output
three policy states distinguished

The complete program checks the fictional policyโ€™s decision categories. The fallback prevents this modeled disclosure, but it is not a universal failure-mode recommendation. Actual identity, dependency, and response behavior are not exercised.

Locate the invariant where state changes

A registration invariant may say that cancellation cannot increase active attendance and that an already cancelled registration produces no second notification. The transition boundary should enforce the rule even when the visible interface is bypassed by another legitimate client. The second program makes cancellation an explicit state transition and checks repeated application. It is a local sequential model; it has no durable event store or atomic transaction. In a real implementation, the record change and contractually relevant event must be coordinated under the selected persistence design to avoid partial or duplicate effects.

python Check a fictional repeat-safe cancellation policy
def cancel(state):
    if state["status"] == "cancelled":
        return dict(state), 0
    return {**state, "status": "cancelled"}, 1

first, notices = cancel({"id":"registration-1", "status":"active"})
second, repeated_notices = cancel(first)
assert first == second
assert (notices, repeated_notices) == (1, 0)
print("cancellation invariant checked sequentially")
Expected output
cancellation invariant checked sequentially

The complete program returns an effect count instead of sending a notification. It verifies sequential policy behavior only. It does not establish cross-process deduplication, transactional delivery, or the correctness of all registration states.

Reduce authority before adding filters

When a component only needs public workshop titles, do not give it an unrestricted contact export interface and rely on callers to remember a flag. Prefer a narrow operation or response projection whose authority matches the task. This reduces the number of places that must correctly suppress private fields. Narrow interfaces still need validation and permission checks; they do not remove every risk. Review how defaults, optional parameters, and administrative exceptions interact. A convenience API that silently broadens scope when a parameter is absent can defeat an otherwise careful authorization design.

Test default behavior and recovery

Include absent configuration, unknown enum values, dependency refusal, malformed state, and legitimate recovery in the design review. Each case should state the protected outcome and what the caller can do next. Do not use universal catch-and-ignore logic that hides corrupted state or repeated processing. Preserve useful diagnostic distinctions without exposing internals in the public response. A safe default is strongest when it is explicit, observable, and verified at the actual enforcement boundary, with availability tradeoffs reviewed by the responsible product and engineering owners.

Common errors and fixes

Missing policy silently allows export

Why it happens
An implicit fallback grants authority.
How to fix it
Define and implement the documented no-disclosure outcome.
Prevent it next time
Test absent and unknown policy states explicitly.

Unknown is reported as permanent denial

Why it happens
Dependency uncertainty is collapsed into policy meaning.
How to fix it
Preserve separate internal categories and recovery guidance.
Prevent it next time
Model more than a single allow boolean.

The interface blocks duplicates but a background path does not

Why it happens
The invariant lives only in the visible client.
How to fix it
Enforce the transition at the trusted shared boundary.
Prevent it next time
Review every legitimate entry point to protected mutation.

Check your understanding

1. Should policy-unavailable and not-assigned produce identical internal records?

Show worked solution

No. Both can prevent contact disclosure while representing different causes and recovery paths. Preserve a nonsecret diagnostic category for the unavailable dependency and keep the public response consistent with the approved disclosure policy. Test the distinction without exposing infrastructure details.

2. A repeated cancellation returns the same state but sends another notice. What failed?

Show worked solution

The state invariant held while the side-effect invariant failed. Include the notification effect in the transition contract and coordinate it with the state change under the reviewed design. A sequential local model clarifies the policy; durable and concurrent enforcement require separate evidence.

Key takeaways

  • Defaults are security decisions even when implicit.
  • Unknown and denied can share protection while needing different diagnostics.
  • Invariants belong at trusted transition boundaries.

Official references