> For the complete documentation index, see [llms.txt](https://engineering.umh.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://engineering.umh.app/engineering/development-process/how-to-build/coding-standards/architecture/security.md).

# Security

What we protect, against whom, and how every developer applies the approach to code that no rule covers.

Owner: Jeremy

UMH runs at the core of a factory. Customers build applications on it that run their production, such as an MES. Their [IT security](/product/who-we-build-for/it-security.md) decides whether UMH may run at all. More and more often, it asks for standards such as IEC 62443, or for proof that suppliers manage their security, as the EU's NIS2 directive requires. The EU Cyber Resilience Act sets security requirements for software sold in the EU. UMH has to meet these standards.

Our information security follows ISO 27001, as our [Trust Center](https://trust.umh.app) describes. This page is how engineering puts [our security policies](https://app.eu.vanta.com/c/umh/employee/onboarding) (Vanta, UMH only) into practice.

The approach behind individual attacks such as SQL injection is deciding what we protect and against whom, writing that down, and building every part to hold when another part fails. This page describes that approach, so that every developer can apply it to code that no rule covers.

## Rules

* We follow Google's [Building Secure and Reliable Systems](https://google.github.io/building-secure-and-reliable-systems/). Where this page says nothing, the book decides.
* Security protects three things: confidentiality, integrity and availability. A user who stops their own production by accident is a security problem, and so is [data that arrives wrong](/engineering/development-process/how-to-build/coding-standards/architecture/data-integrity.md).
* Every rollout ticket lists who could attack the new feature and how. This list, the [threat model](#threat-model), uses our standard actors and for now lives on the ticket.
* Every package checks its own input and who may call it, even when another layer already checks both. A check in another layer can be missing.
* The secure setting is the default. Where we can, there is no setting to turn it off.
* Before you request a review, you run /security-review on your pull request.
* Threat models go into our public docs over time.

## Confidentiality, integrity, availability

**Security protects** [**confidentiality, integrity and availability**](https://google.github.io/building-secure-and-reliable-systems/raw/ch01.html)**.**

* Confidentiality: only the right people read the data. For example, one company never sees another company's data.
* Integrity: nobody changes data or commands without permission. For example, a value from a machine arrives as the machine sent it.
* Availability: the system keeps running. For example, a user of one company must not be able to take down another company, and ideally not their own company either, by polling a PLC so often that it fails.

**For each actor, the threat model says which of the three we protect.** For example, an encrypted backup protects confidentiality against someone who steals it. It does not protect availability against them, because they can still delete it.

**Most security work is documentation and process.** It means writing down what we protect, keeping that current when the code changes, and filing every potential issue. [Chapter 21](https://google.github.io/building-secure-and-reliable-systems/raw/ch21.html) of the book describes this culture.

## Threat model

**A threat model says** [**who could attack**](https://google.github.io/building-secure-and-reliable-systems/raw/ch02.html) **a feature, what they could do, and whether we protect against it.** We follow [RFC 3552](https://www.rfc-editor.org/rfc/rfc3552): name every attack, and say whether we protect against it and why.

**For now, every rollout ticket gets a threat model as a Linear document.** The [rollout ticket](/engineering/development-process/how-to-build/tickets/rollout.md) links it. Code that was built without a threat model is hard to threat-model afterwards, so we add threat models feature by feature. Over time, we move them to a shared place and their public parts into our docs.

**Before you build, read the existing threat model and the** [**abstractions**](/engineering/development-process/how-to-build/coding-standards/code-quality/abstractions.md) **of the code you change.** If the code has no threat model yet, file `Threat model for <part> missing` under the [Security sub-ticket](/engineering/development-process/how-to-build/tickets/security.md). Every potential issue you find goes there too, until a threat model exists and the issues can be ranked.

**The threat model goes public, known vulnerabilities stay internal.** Our public [threat model](https://docs.umh.app/management-console/security/threat-model) shows the actors, what we assume and what we protect. Which rows are only partly protected or not protected, and the tickets for them, stay in Linear until they are fixed.

### Actors

**Every threat model picks from the same six actors.** Each actor is defined by what they already have. Add an actor only for a position the list doesn't cover.

| # | Actor                           | Already has                                                                                                                |
| - | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| 1 | Anyone on the internet          | nothing                                                                                                                    |
| 2 | User of another company         | a UMH account, in another company                                                                                          |
| 3 | User of the same company        | a role in this company, from viewer to admin                                                                               |
| 4 | Someone with UMH's access       | an employee acting alone, or someone who broke into our cloud                                                              |
| 5 | UMH as a whole                  | the one exclusion: we do not protect against UMH deliberately shipping malicious code through its reviewed release process |
| 6 | Someone in the customer network | access to a network zone, but no role in UMH                                                                               |

**An actor plus a device they control is a variant with its own row.** For example, "3, with a compromised laptop", or "6, controlling an instance's host": whoever has root on the machine that runs umh-core also has the instance's identity. It doesn't matter if that is an intruder or the customer's own IT admin.

### Template

```markdown
# Threat model: <feature>

Rollout ticket: <link> · Owner: <name>

## Actors
<pick from the six standard actors; add variants as rows below>

## What we assume
- <e.g. Communication between umh-core and the user is protected.
  See <security document> for its gaps.>

## What happens when an actor misbehaves
| Actor | What can they do, on purpose or by mistake? | Status | Decision |
|---|---|---|---|
| <e.g. 3> | <…> | Mitigated / Partially mitigated / Not mitigated | <only when not fully mitigated:> Accepted, fix in <ticket> / Accepted, no fix planned |

## Open points
- <each one is a ticket under the Security sub-ticket>
```

**Check the code before you write a row.** Whatever you can't show is protected counts as Not mitigated until someone shows otherwise.

**Every risk we don't protect against stays in the table, with a reason.** Actor 5 is the only exclusion, and it is made once, in the actor list.

**An assumption may name something the customer has to do, only if our public docs tell the customer.** The assumption links to that place, for example the [shared responsibility](https://docs.umh.app/management-console/security/shared-responsibility) page.

**A threat that applies to several actors is written once, under the actor with the least access.** For example, whoever holds a leaked instance token can act as the instance, no matter who they are. That row reads "1, holding a leaked instance token" and names how the token can leak.

## Package boundaries

**Good** [**abstractions**](/engineering/development-process/how-to-build/coding-standards/code-quality/abstractions.md) **are already security.** A package with a small API has few ways in, so you can see everything that enters it and check it there. The package checks once, where the input enters it, as [Simplicity](/engineering/development-process/how-to-build/coding-standards/code-quality/simplicity.md) describes, and works with the checked value inside. This might feel like overkill, but it is James Reason's [Swiss cheese model](https://pmc.ncbi.nlm.nih.gov/articles/PMC1117770/): another developer can change some other function and make a mistake there, and our code still catches it. [Chapter 6](https://google.github.io/building-secure-and-reliable-systems/raw/ch06.html) of the book calls such a boundary a security boundary.

**Every layer has holes.** Reason pictures each layer of defense as a slice of Swiss cheese. Damage happens only when the holes of several slices line up.

**A package's own check prevents an attack. Other layers only add to that protection.** ISO 27002 sorts security controls into three types:

* Preventive controls stop an attack, for example a package that checks who is asking. [Chapter 5](https://google.github.io/building-secure-and-reliable-systems/raw/ch05.html) and [chapter 8](https://google.github.io/building-secure-and-reliable-systems/raw/ch08.html) of the book describe how to design them.
* Detective controls notice an attack while it happens, for example logs and alerts. See [chapter 15](https://google.github.io/building-secure-and-reliable-systems/raw/ch15.html).
* Corrective controls repair the damage afterwards, for example backups or revoking a token. See [chapter 9](https://google.github.io/building-secure-and-reliable-systems/raw/ch09.html).

Rate limiting and Cloudflare only slow an attacker down. An id nobody can guess only protects until someone finds it. Both are useful extra layers, but neither replaces the check in the package.

## Secure by default

**Even the best engineers** [**make mistakes**](https://google.github.io/building-secure-and-reliable-systems/raw/ch12.html)**.** So every package assumes that other packages have mistakes in them.

**The security of the applications customers build on UMH is their responsibility.** The defaults are secure, and where we can, there is no setting to turn them off, as [opinionated simplicity](/product/product-standards/opinionated-simplicity.md) asks.

**The public** [**threat model**](https://docs.umh.app/management-console/security/threat-model) **tells customers what we protect, what we don't, and what they have to do themselves.** Every change to any of these goes into the threat model.

## Did it work?

**Before you request a review, run /security-review on your pull request**, as [Code review](/engineering/development-process/how-to-build/code-review.md) describes.

**You can also run a penetration test (pentest) against your own code. For now, pentests are optional.** A pentest always checks every row of the threat model marked Mitigated. A pentest without a threat model finds random bugs, but can't say whether our protections hold. A finding that fits no row of the threat model shows a gap in the model, so the threat model is updated. [Chapter 13](https://google.github.io/building-secure-and-reliable-systems/raw/ch13.html) of the book describes testing for security, and [Testing](/engineering/development-process/how-to-build/testing.md) what our tests cover.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://engineering.umh.app/engineering/development-process/how-to-build/coding-standards/architecture/security.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
