Course outline

Secure Software Development / Intermediate: implementation boundaries and verification / Lesson 7

Build explicit input boundary contracts

Separate parsing, canonicalization, shape, domain rules, and authority before accepting a value.

About 4 min readContent reviewed
On this page

Lesson goals

  • Validate documented types and ranges without accidental coercion.
  • Explain why valid input still needs permission and safe output handling.

Assign each question to its boundary

A workshop capacity update crosses several decisions. Parsing establishes a representation, structural validation checks required fields, domain validation checks the allowed capacity range, and authorization checks who may change this workshop. Passing one layer does not satisfy the others. A string containing digits may be valid JSON while violating an integer-only contract. A valid integer may still request a forbidden reduction below current attendance. Write these rules separately so failures explain what the caller must correct and so implementation review can locate the actual enforcement boundary.

Reject coercion that the contract does not permit

A validator should reflect the public value domain rather than a convenient runtime conversion. In Python, bool is a subtype of int, so an integer-only classroom rule uses an exact type check. The first program accepts bounded capacities and rejects booleans, digit strings, and out-of-range values. The chosen range is fictional and deliberately small. A real product must select its own bounds and units. This program does not parse HTTP bytes, enforce request-size limits, or handle every possible numeric representation in other languages.

python A strict fictional capacity domain
def valid_capacity(value):
    return type(value) is int and 1 <= value <= 80

for value in [1, 80]:
    assert valid_capacity(value) is True
for value in [0, 81, True, "20", None]:
    assert valid_capacity(value) is False
print("seven capacity boundary cases checked")
Expected output
seven capacity boundary cases checked

The complete local program checks seven explicit inputs without coercing them. Eighty is a classroom maximum, not a recommended system limit. Application parsing, persistence, and permission are outside this test.

Normalize only with a documented semantic rule

Canonicalization can reduce equivalent forms, but it can also destroy distinctions. Trimming a category label under a documented policy differs from lowercasing every identifier, password, or user name. Decide whether normalization occurs before validation and ensure all later uses rely on the same accepted representation. The second program maps a small case-insensitive workshop category to an allowlisted internal value. It does not claim general Unicode equivalence or a safe identifier policy. Keep normalization policy field-specific and test that a forbidden value is still rejected rather than silently mapped to a default.

python A bounded category normalization policy
def category(value):
    if not isinstance(value, str):
        return "INVALID_CATEGORY"
    normalized = value.strip().lower()
    return normalized if normalized in {"art", "science"} else "INVALID_CATEGORY"

assert category(" ART ") == "art"
assert category("science") == "science"
assert category("unknown") == "INVALID_CATEGORY"
assert category(None) == "INVALID_CATEGORY"
print("field-specific normalization checked")
Expected output
field-specific normalization checked

The complete program checks four local cases and returns a stable classroom error marker. It creates no request or storage record. The literal error string is preserved across both language versions as machine-facing technical data.

Validate relationships against trusted state

A capacity of thirty may be within the numeric range yet invalid when thirty-five active registrations already exist. Cross-field and state-dependent rules need trusted current facts, not values supplied by the same request. Define the mutation and refusal invariants and consider concurrency at the actual storage boundary. Client-side validation helps usability but cannot establish trusted state or enforce server permission. Avoid a growing collection of denylisted strings as a substitute for a positive domain model. Use explicit supported values, meaningful bounds, and documented relationships that reviewers can explain.

Preserve interpretation boundaries after validation

A valid workshop title can contain ordinary punctuation and still need context-aware HTML encoding or structured serialization at output. A validated resource identifier still needs authorization before lookup or mutation. Avoid calling a value sanitized without naming the property and destination context. The validation result should be a well-defined typed value or a documented rejection, with raw exceptions kept out of public responses. Test boundary values, absent fields, null, empty text, and supported multilingual content while keeping examples synthetic and avoiding unnecessary large-input experiments.

Common errors and fixes

True is accepted as workshop capacity

Why it happens
A runtime subtype check widens the intended domain.
How to fix it
Implement the explicit documented type and range.
Prevent it next time
Include boolean, string, null, and boundary cases.

A valid id is treated as permission

Why it happens
Syntactic validity is confused with authority.
How to fix it
Check subject-resource-action policy at the trusted boundary.
Prevent it next time
Keep validation and authorization cases separate.

Normalization silently accepts an unknown category

Why it happens
The fallback broadens the supported domain.
How to fix it
Return a documented rejection for unsupported normalized values.
Prevent it next time
Test forbidden values after normalization.

Check your understanding

1. The request sends capacity=30 and currentAttendance=20. Can the handler trust both?

Show worked solution

It may validate capacity from the request, but current attendance must come from the reviewed state boundary. Otherwise the caller can supply a convenient count. Test the rule with a trusted fixture of thirty-five active registrations and require refusal without mutation.

2. Why is an accepted category not automatically safe in a generated command?

Show worked solution

Domain acceptance and command interpretation are different properties. Prefer a library API and keep external values as data through a reviewed structured interface. An allowlisted category does not justify string-concatenating an operating-system command or ignoring argument semantics.

Key takeaways

  • Validation has multiple independent layers.
  • Normalization is field-specific and must preserve meaning.
  • Valid data still needs authority and safe interpretation.

Official references