WEB / WEB-16.S01
Validation, normalization, and interpretation boundaries
Follow bytes, JSON values, NFC domain records, and sink representations through an ordinary book preview.
1. Core · Mechanism
Read every scenario without motion
NFC preview
- Raw input supplied
The fixed ordinary title/quantity JSON is supplied; neither a parsed value nor an accepted preview exists.
- Raw bytes
- Bounded bytes pending
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- Input-budget decision
The JSON input first satisfies the 128-byte budget and UTF-8 rules. This step does not validate field types or business rules.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- JSON value ready
The ordinary JSON object parses; quantity can be string or integer, with schema admission still pending.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- No validated record
- Preview consumer
- No preview
- Closed-schema decision
title:string and quantity:integer are the two fields required by the closed schema. quantity=2 passes schema validation; the domain decision is still pending.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Schema valid
- Preview consumer
- No preview
- Domain and NFC boundary
The admitted title normalizes to Café with NFC. quantity=2 is within the allowed range 1–3, so an accepted record can now be prepared.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- NFC Café; quantity=2 valid
- Preview consumer
- No preview
- Preview-record outcome
The accepted Café/2 record produces plain preview data. This result is for public preview only and performs no other business mutation.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- NFC Café; quantity=2 valid
- Preview consumer
- Preview Café / 2
Wrong type
- Raw input supplied
The fixed ordinary title/quantity JSON is supplied; neither a parsed value nor an accepted preview exists.
- Raw bytes
- Bounded bytes pending
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- Input-budget decision
The JSON input first satisfies the 128-byte budget and UTF-8 rules. This step does not validate field types or business rules.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- JSON value ready
The ordinary JSON object parses; quantity can be string or integer, with schema admission still pending.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- No validated record
- Preview consumer
- No preview
- Closed-schema decision
quantity="two" parses as a string, but the schema requires an integer. Field-type rejection produces no domain record.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- field-type
- Preview consumer
- No preview
- Domain and NFC boundary
Schema rejection prevents domain evaluation and NFC normalization. The parsed object cannot substitute for an admitted preview record.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Domain not evaluated
- Preview consumer
- No preview
- Preview-record outcome
The view shows field-type rejection with previewRecord=null. The invalid type is not displayed as empty success or an admitted quantity.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Domain not evaluated
- Preview consumer
- No preview; field-type
Quantity limit
- Raw input supplied
The fixed ordinary title/quantity JSON is supplied; neither a parsed value nor an accepted preview exists.
- Raw bytes
- Bounded bytes pending
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- Input-budget decision
The JSON input first satisfies the 128-byte budget and UTF-8 rules. This step does not validate field types or business rules.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- JSON value ready
The ordinary JSON object parses; quantity can be string or integer, with schema admission still pending.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- No validated record
- Preview consumer
- No preview
- Closed-schema decision
quantity=5 is an integer and passes the schema. Correct field types do not yet establish the preview quantity rule.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Schema valid
- Preview consumer
- No preview
- Domain and NFC boundary
quantity=5 is outside the allowed range 1–3. The domain rejects the input; schema success is insufficient to produce a preview.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Domain range rejected
- Preview consumer
- No preview
- Preview-record outcome
The view shows domain-range rejection with previewRecord=null. A correctly typed quantity is not displayed as an accepted preview.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Domain range rejected
- Preview consumer
- No preview; domain-range
Extra field
- Raw input supplied
The fixed ordinary title/quantity JSON is supplied; neither a parsed value nor an accepted preview exists.
- Raw bytes
- Bounded bytes pending
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- Input-budget decision
The JSON input first satisfies the 128-byte budget and UTF-8 rules. This step does not validate field types or business rules.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- No JSON value
- Domain gate
- No validated record
- Preview consumer
- No preview
- JSON value ready
The ordinary JSON object parses; quantity can be string or integer, with schema admission still pending.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- No validated record
- Preview consumer
- No preview
- Closed-schema decision
reviewed:true is outside the two fields admitted by the closed schema. The extra field is rejected rather than silently discarded to admit the record.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- closed-shape
- Preview consumer
- No preview
- Domain and NFC boundary
Closed-shape rejection prevents domain evaluation and NFC normalization. No admitted record is extracted from the rejected object.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Domain not evaluated
- Preview consumer
- No preview
- Preview-record outcome
The view shows closed-shape rejection with previewRecord=null. Discarding the extra field does not produce a displayed success.
- Raw bytes
- Byte budget accepted; UTF-8
- JSON value
- Object parsed; types visible
- Domain gate
- Domain not evaluated
- Preview consumer
- No preview; closed-shape
ACTORS · STATE · DATA FLOW
Parsing is not an application decision
Atlas receives title and quantity for a public book preview. Use Cafe plus a combining acute accent and quantity=2 as ordinary input. A JSON parser turns text into values without establishing an allowed quantity or permission for a business action. Byte admission, parsing, schema, and domain decisions yield distinct outcomes.
The native profile admits at most 128 bytes, decodes UTF-8 strictly, and requires a JSON object. It does not portray Python json defaults as a general strict policy: selected hooks reject duplicate members and nonstandard numeric constants. Its tiny-object scope certifies neither parser-depth handling nor every resource-exhaustion condition.
Schema and domain rules ask different questions
The closed schema admits only title:string and quantity:integer. Missing, null, extra fields, and incorrect types fail structural rules. Quantity=5 passes the integer type check but fails Atlas’s 1..3 preview rule. The string “2” is never silently coerced, leaving the API’s admitted representation explicit.
In the wrong-type branch, quantity “two” is a parseable string rather than a JSON syntax error. The extra-field branch rejects ordinary reviewed:true and creates no preview record. Validity grants no user permission, so the demo produces only public preview data, with no order, payment, private note, or session mutation.
Which equivalence does normalization select?
The selected title policy uses NFC. Cafe plus U+0301 and Café can have different byte and code-point sequences; canonical normalization makes these ordinary examples equal. Normalization chooses the representation for comparison and storage. Here length means 1..40 Unicode code points after NFC, distinct from bytes, grapheme clusters, or visible glyph count.
NFKC also folds compatibility distinctions and is not silently applied to all free text. An ordinary fullwidth-A observation demonstrates differing NFC and NFKC outcomes. Decoding the normalized title again downstream changes the representation an earlier check inspected. This profile consumes the same normalized record; normalization is neither a sanitizer nor authorization.
Encoding and parameterization belong to a sink
The preview consumes a plain title. HTML text serialization, when needed, has its own context encoding. For ordinary Tea & Bread, the native observation serializes the ampersand as an entity while the domain title stays Tea & Bread. Keep the representations distinct. HTML text encoding is not a general safety transform for JavaScript source, URL destinations, or SQL structure.
Parameterization separates query/program structure from values through an API; validation checks whether a value fits an operation. The following database lesson explores fixed queries and bound titles. This lesson executes neither SQL nor commands. Building query text from an escaped title does not implement a named parameter.
Do not consume a partially checked record
The artifact distinguishes parsed, schemaValid, domainValid, normalizedTitle, and previewRecord. Wrong type can leave parsed=true while previewRecord stays null. A domain limit leaves schemaValid=true and domainValid=false. Extra fields are not silently discarded into an accepted record. These outcomes identify the rejecting owner instead of one generic invalid label.
Missing raw input yields not-computed, distinct from an empty title or malformed JSON. Scene owners are responsibilities within one local pipeline, with no packet sent to parse a value. Follow what changes from raw representation to an accepted record, and which values have not yet been consumed.
2. Red · Trust & Impact
Read every scenario without motion
Extra field
- The public-preview contract was selected
The profile accepts only title and quantity keys, with quantity range 1..3. Client form controls do not replace this local authoritative rule.
- Public JSON input
- case=extra-field
- Schema/domain gate
- Consumer gate not read
- Preview boundary
- No preview record
- The JSON parse result was read separately
Parsed=true means this input satisfies JSON grammar. Parse success alone establishes neither schema nor domain acceptance.
- Public JSON input
- case=extra-field
- Schema/domain gate
- Parsed=true
- Preview boundary
- No preview record
- The first failing consumer gate was read
Input has extra field reviewed=true, so closed-shape policy gives schemaValid=false. The field being a JSON boolean adds no authority under this profile.
- Public JSON input
- case=extra-field
- Schema/domain gate
- Closed shape rejected
- Preview boundary
- No preview record
- Normalization was not substituted as the cause
The extra-field row retains normalizedTitle=null; no downstream normalized record is assumed.
- Public JSON input
- case=extra-field
- Schema/domain gate
- No accepted normalized record
- Preview boundary
- No preview record
- Preview construction was withheld
decision=REJECT and previewRecord=null remain. The reason stays closed-shape; no private-resource permission outcome is added.
- Public JSON input
- case=extra-field
- Schema/domain gate
- Reason: closed-shape
- Preview boundary
- No preview construction
- Interpretation layers were kept distinct
This negative input parses but fails its consumer contract. JSON, schema, business rules and output contexts are different boundaries; punctuation blacklists do not establish their separation.
- Public JSON input
- case=extra-field
- Schema/domain gate
- Known: consumer denial
- Preview boundary
- No accepted preview record
Domain limit
- The public-preview contract was selected
The profile accepts only title and quantity keys, with quantity range 1..3. Client form controls do not replace this local authoritative rule.
- Public JSON input
- case=domain-limit
- Schema/domain gate
- Consumer gate not read
- Preview boundary
- No preview record
- The JSON parse result was read separately
Parsed=true means this input satisfies JSON grammar. Parse success alone establishes neither schema nor domain acceptance.
- Public JSON input
- case=domain-limit
- Schema/domain gate
- Parsed=true
- Preview boundary
- No preview record
- The first failing consumer gate was read
Quantity=5 has the required integer type with schemaValid=true. The operation range 1..3 gives domainValid=false.
- Public JSON input
- case=domain-limit
- Schema/domain gate
- Schema true; domain false
- Preview boundary
- No preview record
- Normalization was not substituted as the cause
normalizedTitle=Café is supplied. Correct NFC output does not repair the quantity-domain failure.
- Public JSON input
- case=domain-limit
- Schema/domain gate
- NFC title valid; quantity denied
- Preview boundary
- No preview record
- Preview construction was withheld
decision=REJECT and previewRecord=null remain. The reason stays domain-range; no private-resource permission outcome is added.
- Public JSON input
- case=domain-limit
- Schema/domain gate
- Reason: domain-range
- Preview boundary
- No preview construction
- Interpretation layers were kept distinct
This negative input parses but fails its consumer contract. JSON, schema, business rules and output contexts are different boundaries; punctuation blacklists do not establish their separation.
- Public JSON input
- case=domain-limit
- Schema/domain gate
- Known: consumer denial
- Preview boundary
- No accepted preview record
ASSUMPTION · BOUNDARY · CONTROL
What follows from valid JSON?
Reporting quantity “two” as a parse failure identifies the wrong owner. It fits the grammar but fails the schema. Quantity=5 fits the type but not the operation rule. Keep grammar, field/type, and domain success distinct in review; native evidence explains why these branches differ.
A quantity dropdown does not remove the server’s 1..3 rule. Form-control state and body value can differ. The public-preview example locates the final decision in the local application rule. A successful schema grants no right to a private record.
Similar-looking titles need not share bytes
Native composed and decomposed Café examples distinguish appearance from raw equality. Selected NFC unifies their comparison representation without equating all Unicode strings. NFC preserving the fullwidth-A distinction supplies another observation: normalization is a choice about application semantics.
A correctly normalized title can still meet an API that interprets source rather than a value. Literal titles, HTML text serialization, and query value slots therefore provide different capabilities. The html.escape observation concerns its HTML text context alone; it does not show a browser sanitizer, SQL engine, or command parser.
3. Blue · Secure development & defense
Read every scenario without motion
Unicode form
- The normalization representation was selected
representations carries decomposed Café and composed Café separately. Equal appearance is not raw-byte or code-point equality.
- Input/representation record
- Two Café representations
- Validation review
- NFC not compared
- Evidence conclusion
- Business equality not examined
- The selected NFC policy was read
The profile selects titleNormalization=NFC, giving title=Café in the accepted normal record. The selected policy supports downstream comparison using the same canonical representation.
- Input/representation record
- Decomposed title input
- Validation review
- NFC result=Café
- Evidence conclusion
- Selected normalization known
- The compatibility-form distinction was read
For fullwidth A, NFC remains A while NFKC gives A. NFC and NFKC do not carry identical business semantics for every input.
- Input/representation record
- Fullwidth A
- Validation review
- NFC=A; NFKC=A
- Evidence conclusion
- Forms not interchangeable
- Normalization was not treated as output encoding
Tea & Bread becomes Tea & Bread in HTML text serialization. This output-context transformation does not substitute for NFC or a record key.
- Input/representation record
- Unicode domain representation
- Validation review
- HTML text representation separate
- Evidence conclusion
- No universal encoder claim
- Consumer permission remained separate
A correctly normalized title is not private-record access or caller-identity evidence. This profile is for public preview, not an API-permission outcome.
- Input/representation record
- Known: selected representations
- Validation review
- Validation/normalization separate
- Evidence conclusion
- Authorization not established
- The representation conclusion stayed in scope
The artifact’s normalization and text-serialization outputs can be explained. These values do not verify browser display, sanitizer, SQL or command-interpreter behavior.
- Input/representation record
- Known: NFC/NFKC distinction
- Validation review
- Known: HTML text transformation
- Evidence conclusion
- Other consumers require evidence
Missing input
- The missing-evidence scope was selected
missingEvidence.raw=null is supplied. The normal raw input is not assumed to have been inserted into this gap.
- Input/representation record
- Raw=null
- Validation review
- No parse evidence
- Evidence conclusion
- Preview unknown
- The byte-budget precondition was separated
The body budget is evaluated before parsing; missing raw input does not establish that the byte limit passed. Field-type and domain predicates cannot fill that evidence gap.
- Input/representation record
- Raw=null
- Validation review
- Budget/parse not computed
- Evidence conclusion
- Preview unknown
- Unknown and parse rejection were separated
decision=NOT-COMPUTED preserves absent input. It is not relabelled as malformed JSON or a known schema-rejection reason.
- Input/representation record
- Raw=null
- Validation review
- Decision NOT-COMPUTED
- Evidence conclusion
- No rejection cause inferred
- Record construction did not occur
previewRecord=null remains. Without source evidence, title, quantity or reviewed state is not constructed as a guessed success.
- Input/representation record
- Raw=null
- Validation review
- No accepted field evidence
- Evidence conclusion
- PreviewRecord=null
- Input and accepted-projection evidence were requested
Raw bytes, parsed types and consumer decisions require their own sources. Displaying Café/2 in the UI alone does not complete this local decision chain.
- Input/representation record
- Missing input known
- Validation review
- Raw/type/decision evidence required
- Evidence conclusion
- No accepted preview
- The missing-input conclusion was bounded
It is known that this branch computed no result; input validity remains unknown. Event loss or malicious-caller attribution needs separate evidence.
- Input/representation record
- Known: input absent
- Validation review
- Known: not computed
- Evidence conclusion
- UNKNOWN: input validity
IMPLEMENTATION · SECURITY CONTROL · VERIFICATION
Define an accepted-representation contract
The public-preview contract defines two fields, exact types, quantity range, and NFC title length together. Missing, extra, and null values need explicit outcomes. Apply the byte limit before parsing and type/domain rules to values. Python bool/int inheritance does not define the API’s quantity type: the native profile rejects booleans. Unknown input produces no guessed record.
Normalize once for the accepted record and consume that same title downstream. Duplicate-member rejection is selected application policy, not universal JSON behavior. Legitimate punctuation remains admissible: O’Reilly stays a native title. A punctuation blacklist neither separates query structure from values nor preserves normal content.
Compare results against an independent artifact
Expect Café/2 for normal input, schema rejection for wrong type, domain rejection for quantity=5, and closed-shape rejection for reviewed. The last three retain a null record. Read parsed types, normalized titles, and HTML text representations from separate fields. Success at one stage is not evidence of success at another.
No preview record exists at scene start. Knowledge changes as budget, parse, schema, and domain decisions complete. Keep raw, domain, and sink representations separate instead of storing output encoding as a database key. The local demonstration verifies no HTTP handler, browser display, sanitizer, or user permission; their owner boundaries need separate observations.
NOW APPLY IT TO THE CASE
Where do grammar, types, domain and representations differ?
Harmless public-preview JSON must satisfy its consumer rule. Unicode normalization and output encoding serve different purposes; missing raw input does not become guessed success.
Reconstruct decision order for input, server processing and response in the case. Parsed or valid data is not a completed operation; identify each owner and its condition.
Open the related case file ↗READ THE EXAMPLE TOGETHER
Atlas preview representation ledger
Select a field and examine what it supports and where the evidence ends.
Read the raw artifact and field explanations
{
"profile": {
"rawByteLimit": 128,
"keys": [
"title",
"quantity"
],
"titleNormalization": "NFC",
"titleCodePoints": [
1,
40
],
"quantityRange": [
1,
3
],
"publicPreviewOnly": true
},
"raw": {
"normal": "{\"title\":\"Café\",\"quantity\":2}",
"wrong-type": "{\"title\":\"Café\",\"quantity\":\"two\"}",
"domain-limit": "{\"title\":\"Café\",\"quantity\":5}",
"extra-field": "{\"title\":\"Café\",\"quantity\":2,\"reviewed\":true}"
},
"expected": {
"normal": {
"decision": "ACCEPT",
"reason": "public-preview-valid",
"schemaValid": true,
"domainValid": true,
"previewRecord": {
"title": "Café",
"quantity": 2
},
"parsed": true,
"normalizedTitle": "Café"
},
"wrong-type": {
"decision": "REJECT",
"reason": "field-type",
"schemaValid": false,
"domainValid": false,
"previewRecord": null,
"parsed": true,
"normalizedTitle": null
},
"domain-limit": {
"decision": "REJECT",
"reason": "domain-range",
"schemaValid": true,
"domainValid": false,
"previewRecord": null,
"parsed": true,
"normalizedTitle": "Café"
},
"extra-field": {
"decision": "REJECT",
"reason": "closed-shape",
"schemaValid": false,
"domainValid": false,
"previewRecord": null,
"parsed": true,
"normalizedTitle": null
}
},
"representations": {
"decomposedTitle": "Café",
"composedTitle": "Café",
"htmlTextInput": "Tea & Bread",
"htmlTextSerialized": "Tea & Bread",
"fullwidthA": "A",
"NFCFullwidthA": "A",
"NFKCFullwidthA": "A"
},
"missingEvidence": {
"raw": null,
"decision": "NOT-COMPUTED",
"previewRecord": null
}
}- raw
- Fixed ordinary JSON text; byte admission is separate from parsing.
- schemaValid / domainValid
- Fields/types and operation range are separate gates.
- previewRecord
- Accepted NFC title and quantity; null on rejection.
- representations
- Raw, normalized, and HTML-text-serialized forms differ.
Actual Python fixed JSON/Unicode/HTML-text observations and shared pure reducer. No HTTP handler, browser/sanitizer, SQL/command interpreter, arbitrary input, exploit, or user authorization executes.
Two reality checks
Valid JSON establishes a valid business operation.
Grammar, schema, domain, and authorization need distinct decisions.
A normalized or escaped string is safe in every sink.
Normalization chooses equivalence; encoding is contextual, while parameterization separates structure and values.
YOUR TURN
Work through this story.
Predict the outcome, find the evidence boundary, and explain it in your own words.
quantity=5 is an integer and schemaValid=true, but the allowed range is 1–3. Is a public preview record created?
- No. domainValid=false and previewRecord=null.
- Yes. Every integer quantity is accepted.
Read the explained answer
No. domainValid=false and previewRecord=null. — The numeric type is correct, but the quantity exceeds the limit. Schema success does not satisfy the domain condition.
JSON was parsed, but reviewed is not part of the contract. Does this closed-shape policy silently drop the field and accept the preview?
Selected lesson artifact fields; teaching example
- profile.keys
- ["title","quantity"]
- raw.extra-field
- {"title":"Café","quantity":2,"reviewed":true}
- expected.extra-field
- {"decision":"REJECT","reason":"closed-shape","schemaValid":false,"domainValid":false,"previewRecord":null,"parsed":true,"normalizedTitle":null}
- Yes. Correct JSON syntax makes every extra field trusted metadata.
- No. closed-shape results in REJECT.
Read the explained answer
No. closed-shape results in REJECT. — The allowed keys are title and quantity. parsed=true does not mean the schema accepts the input; previewRecord remains null.
Café becomes Café under NFC; fullwidth A remains unchanged under NFC but becomes A under NFKC. Why is normalization neither HTML encoding nor permission?
Read the explained answer
NFC and NFKC apply different normalization rules, so the contract must choose one. Canonicalized character representation does not remove title type/range or authorization checks. HTML text encoding represents Tea & Bread as Tea & Bread; it is a separate context operation. Missing input leaves the decision NOT-COMPUTED.
□ I did not equate NFC and NFKC outcomes.
□ I separated normalization, encoding and permission.
Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples
Sources and scope
- OWASP — Input Validation Cheat SheetIntroduction · Input Validation Strategies · Parse Safely, Then Validate · Validate Structured Data · Validating Free-form Unicode Text
- RFC 8259 — JSON4 Objects · 9 Parsers
- Python — jsonjson.loads / object_pairs_hook / parse_constant · Repeated Names Within an Object
- Unicode Standard Annex #15 — Normalization Forms1.2 Normalization Forms; compatibility distinctions
- Python — unicodedataunicodedata.normalize
- Python — htmlhtml.escape
- OWASP — Injection Prevention Cheat SheetRemediation: Defense Option 1: Prepared Statements (with Parameterized Queries)