Why Safe Systems Start by Refusing to Trust Their Own State

Honyee Chua

Hatched by Honyee Chua

Jul 19, 2026

9 min read

87%

0

What do a tic-tac-toe board and a malware scanner have in common?

At first glance, almost nothing. One is a toy game about Xs and Os. The other is a security tool that inspects Python Pickle files for suspicious behavior. But both are built around the same uncomfortable truth: the most dangerous bugs are not the ones that look obviously wrong, but the ones that quietly trust the wrong thing.

A game board that lets each square keep its own private version of reality becomes impossible to reason about. A file loader that assumes serialized data is harmless can become a gateway for arbitrary code execution. In both cases, the system fails not because it lacks power, but because it places trust in the wrong layer.

That is the deeper connection: good systems do not merely store state, they govern it. They decide where truth lives, who is allowed to change it, and how to detect when something is trying to smuggle in a false version of reality.


The hidden risk inside convenience

The first instinct in software is often to make things local. Put the value in the component. Let the object remember its own state. Let the file carry everything it needs. This feels clean, intuitive, and efficient. Until it does not.

A tic-tac-toe square with its own independent state seems fine at first. Each button can remember whether it contains an X or an O. But once the game begins, the illusion of simplicity collapses. The squares need to agree about whose turn it is, whether the game is over, and what the full board looks like. If every square acts as its own authority, the board fractures into disconnected opinions.

That is why the state gets lifted into the parent board. The board becomes the source of truth, and the squares become projections of that truth. The squares do not own reality anymore, they merely display it. This is not just a React pattern. It is a design principle: the more a system needs coordination, the less truth should be scattered across its parts.

Pickle files expose the same temptation in a harsher form. Serialization promises convenience: save an object, load it later, and recover its structure. But if a system treats serialized data as trusted state, it may unknowingly execute actions hidden inside that data. The file is no longer just data, it can be a vehicle for behavior. A scanner that detects suspicious Pickle files exists because the boundary between data and action is not automatically safe.

Convenience is often just deferred complexity. In secure systems, deferred complexity becomes deferred vulnerability.

The real question is not whether something is easy to use. The question is whether it can be safely trusted after it has left the place where it was created.


Why state must be centralized before it can be controlled

React’s useState and lifting state up are often taught as implementation details, but they reveal something deeper about system design. A component is not interesting because it stores information. It is interesting because it participates in a larger agreement about information.

A square cannot update the board directly because that would let a child rewrite the rules of the game. Instead, the square sends an event upward, and the board decides what the new state should be. The square is allowed to request change, but not to redefine reality. That separation matters. It creates a chain of responsibility: events flow up, state flows down.

This pattern mirrors how secure systems should work. Inputs should be treated as requests, not as commands. External data should be interpreted, validated, and constrained before it can affect the system’s internal state. A scanner for malicious Pickle files is, in spirit, a stricter version of the parent board. It refuses to let untrusted content become authority without inspection.

There is a practical lesson here about architecture. When state is fragmented, every part of the system must defend itself. When state is centralized, defense becomes possible. You can validate once at the boundary, manage consistency in one place, and reason about updates as a controlled sequence rather than a swarm of local exceptions.

The parent component in a UI and the trust boundary in security are both forms of governance. They are where the system says:

  1. This is the current truth.
  2. These are the only valid ways it can change.
  3. Anything else must be rejected or transformed.

If you cannot identify that layer in your design, your system is probably already trusting too much.


Immutability is not a style choice, it is a security posture

One of the most underrated ideas in the tic-tac-toe tutorial is immutability. Instead of mutating the squares array directly, a copy is made with slice(), and the new version replaces the old one. At first, this looks like a small JavaScript habit. In reality, it is a way of preserving history and preventing silent corruption.

Mutation is seductive because it feels efficient. Change the thing in place, move on. But in interactive systems, in-place mutation hides the past. Once you overwrite a state object, you lose the ability to compare before and after, trace a mistake, or rewind an action. That is why undo and redo become difficult when state is mutated casually.

Now look at security. Malicious Pickle files exploit the fact that a system may accept a serialized object as if it were a faithful copy of state. But trust without verification is just mutation at a distance. You are not merely reading data. You are allowing foreign structure to change your world model.

Immutability helps because it makes changes legible. A new board state can be inspected against the previous one. A new file payload can be compared against known patterns. A suspicious transition stands out when the old state still exists as a reference point.

Think of immutability as a forensic advantage. If every update leaves behind an intact prior version, then debugging becomes the study of transitions rather than the archaeology of overwritten evidence.

Systems that preserve history are easier to trust because they make change visible.

This is why immutability is more than a React convention. It is a discipline of accountability. A system that can show what changed, when it changed, and what depended on it is harder to manipulate and easier to repair.


The real enemy is not change, it is uncontrolled change

It would be a mistake to conclude that all mutation is bad or all dynamic behavior is dangerous. Tic-tac-toe obviously needs change. Pickle scanners exist because files do change, and some of those changes are malicious. The issue is never change itself. The issue is change without a trusted interpreter.

In React, a click on a square does not directly rewrite the UI. Instead, the click triggers a callback, the callback updates the parent state, and the next render recomputes the visible board. The button does not become reality. It becomes a signal that reality should be reconsidered.

That distinction is profound. A system stays stable not by freezing itself, but by routing change through a controlled pipeline.

The same logic applies to untrusted inputs. A Pickle file may contain instructions, object graphs, and seemingly ordinary data. But if those instructions are allowed to run unchecked, the system has turned input into authority. A scanner exists because the correct response to external change is not panic, but inspection.

This leads to a useful mental model: every system has a small number of legitimate mutation points. These are the places where the system is allowed to change state. Everything else should be declarative, observational, or read-only. If you multiply mutation points, you multiply failure modes.

A well designed board has one place where the move is decided. A well designed security boundary has one place where trust is granted. A well designed architecture makes it boring to ask, “Who changed this?” because the answer is always obvious.


A mental model: the board, the border, and the scanner

Here is a framework that connects these ideas in a way that is useful beyond these specific examples.

1. The board: a single source of truth

The board is the authoritative model of reality. In a UI, this means state belongs where coordination happens. In a broader system, it means some component must be able to answer, unambiguously, “What is true right now?”

2. The border: a controlled interface for change

The border is where outside influence meets internal state. It can be a click handler, an API endpoint, a file parser, or a deserialization layer. The border should never blindly accept input as truth. It should translate, validate, and limit.

3. The scanner: a guard against disguised action

The scanner looks for cases where something that appears to be data is actually behavior in disguise. That is what makes Pickle dangerous and why security tools exist to detect suspicious actions. The scanner is not there because all input is evil. It is there because the system must distinguish between harmless representation and executable intent.

These three roles are easy to mix up. When the board becomes fragmented, the border becomes porous. When the border is porous, the scanner has too much to catch. When the scanner is overloaded, the entire system becomes a guessing game.

A robust architecture gives each role a distinct job:

  • The board knows the truth.
  • The border decides what may change.
  • The scanner watches for deception.

This model applies to application code, data pipelines, distributed systems, and even organizational workflows. The details differ, but the discipline is the same.


Key Takeaways

  1. Centralize authority where coordination matters. If multiple parts of a system need the same truth, store it in one place and pass it down deliberately.

  2. Treat inputs as requests, not facts. Whether it is a click, a file, or an API payload, external input should be validated before it becomes state.

  3. Prefer immutable updates when you need traceability. Creating a new version instead of mutating in place makes debugging, undo, and auditability far easier.

  4. Make mutation points explicit. A system is safer when it is obvious where state can change and who has permission to change it.

  5. Assume some data may hide behavior. Especially in serialization and deserialization, inspect what looks like data before trusting it to be inert.


The deeper lesson: trust should be earned at the boundary

The surprising connection between a game board and a malware scanner is not that both deal with computer code. It is that both confront the same design problem: how to keep a system coherent in the presence of change.

A child component should not be allowed to rewrite the parent’s truth. A serialized file should not be allowed to smuggle in behavior just because it arrived wrapped as data. In both cases, the system survives by refusing to trust local appearances. It demands that change pass through a place of judgment.

That is the discipline modern software often forgets. We optimize for convenience, flexibility, and composability, then act surprised when state becomes inconsistent or security fails. But the best systems do not simply move fast. They know where truth lives.

If you remember one thing, remember this: trust is not a property of the input. It is a property of the boundary. The board, the parent, and the scanner all exist to make that boundary visible.

And once you see that, even a toy game and a pickle scanner become the same story: a story about who gets to define reality, and how to stop anyone else from pretending they can.

Sources

← Back to Library

Hatch New Ideas with Glasp AI 🐣

Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)

Start Hatching 🐣