◇ OpenCyber Academy
Engineering · Chapter 31 · English

Chapter 31 — OT, IoT and cyber-physical security: implementation II

Open, free learning for anyone who wants to protect people and technology. No account is required to read this text or its worked answers.

Chapter 31 — OT, IoT and cyber-physical security: implementation II

Lesson map

Lesson 31 published. Map, example, EX091.

flowchart TD
  N0["Learning outcomes"]
  N1["31.1 Upsert"]
  N2["31.2 Validate on write"]
  N3["31.3 API versioning"]
  N4["Practice note"]
  N5["31.1b Partial updates and concurrency"]
  N6["Field note — bulk import incident"]
  N7["Synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
  N7 --> EX["Worked example"]
  EX --> Q["Exercise EX091"]

Logical drawing

flowchart LR
  N0["Learning outcomes"]
  N1["31.1 Upsert"]
  N2["31.2 Validate on write"]
  N3["31.3 API versioning"]
  N4["Practice note"]
  N5["31.1b Partial updates and concurrency"]
  N6["Field note — bulk import incident"]
  N7["Synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX091"]
[Learning outcomes]
    |
    v
[31.1 Upsert]
    |
    v
[31.2 Validate on write]
    |
    v
[31.3 API versioning]
    |
    v
[Practice note]
    |
    v
[31.1b Partial updates and concurrency]
    |
    v
[Field note — bulk import incident]
    |
    v
[Synthesis]
    |
    v
[Worked example] --> [Exercise EX091]

Learning outcomes

You will (1) add idempotent finding upserts; (2) enforce schema validation on write; (3) version API responses for CNHP tools.

31.1 Upsert

The CNHP finding API takes PUT /findings/{id} with the full body, so the same id sent twice yields one row.

At ABP, finding ids come from the client on create and are reused on every retry, so a network failure mid-request cannot create a duplicate. Fingerprints suggest when two findings may be the same problem, but a person confirms any merge, because two findings on PLC-A can share a register and still have different causes. Merges are logged with reasons.

31.2 Validate on write

Reject unknown fields and forbidden names; coerce severity through an enum. Validation errors return machine-readable codes.

Lena's finding store at ABP rejects any record with an unknown field or a forbidden name, such as payload or password, and returns a code saying which field failed. Severity accepts only the agreed values. The same validator runs for interactive creates and bulk imports, so no input path is trusted more than another, and a scanner import that sends risk_score is refused with the same code as the mobile client. Errors are documented with examples.

31.3 API versioning

Prefix /v1/; additive changes only within v1. Breaking changes require /v2/ and a migration note.

Synthetic case — Ashford Batch Plant. A mobile client sent risk_score; validator rejected it — empty scores stay out (Chapter 15).

Lena's API for ABP has stayed on v1 so far. Lena publishes the additive-change rule in the API documentation, and continuous integration compares each new schema with the last release to catch removed or renamed fields. If a breaking change ever becomes necessary, v1 will keep running alongside v2 for an announced period, so mobile clients and scanner imports can migrate without failing in the middle of a window.

31.4 Errors and limits

Idempotency keys must not be derived from wall-clock alone.

An idempotency key built from the current time changes between retries, so each retry looks like a new request and creates a duplicate. Lena's clients generate the key once per logical operation, store it with the pending request and reuse it until the server confirms success. The rule is documented with the API, and the client libraries enforce it, so individual developers cannot reintroduce the time-based shortcut. Reviews check this rule explicitly.

Practice note

Bulk import from scanners must pass the same schema validation as interactive create, including forbidden-field checks.

31.1b Partial updates and concurrency

PATCH that allows severity changes without evidence references invites silent downgrades. For CNHP findings, prefer a full PUT of the validated body, or a restricted PATCH that cannot clear the hashes of monitor captures. Use ETags so two remediators do not clobber each other.

A status change to closed is accepted only when the same request carries a re-test pointer. Bulk deletes are forbidden without dual control. Document upsert semantics in OpenAPI so mobile clients do not invent retry ids.

ETags are required on every finding update.

Field note — bulk import incident

A scanner import path skipped forbidden-field checks because it was trusted. Someone's notes field contained a password. After scrubbing and rotating, bulk import was rewired through the same validator as interactive create. Trusted import is how payloads enter sealed stores.

Clients that omit the finding id on create receive 400 with a link to the upsert documentation, so retries cannot invent duplicates by accident.

31.5 Worked example — idempotent upsert of asset records (synthetic ABP)

Two inventory sources report PLC-A: the engineering project export and passive network discovery. Lena's upsert keys on (site, asset tag), not on IP address, because the IP changes when a controller is replaced. Each write carries the source and an observed-at time; a later observation updates firmware version only if its source is authoritative for that field (the project export wins for firmware; discovery wins for "last seen"). Replaying the same import twice produces no change — the test that proves idempotency. Without these rules, a bulk import once overwrote PLC-A's firmware field with "unknown" and hid it from an advisory check.

Exercises

Synthesis

Idempotent writes, strict schema, explicit versions.

Continue studying — chapter 31 of 50

← Chapter 30 (draft) · Course chapter index · Chapter 32 (draft) →