Twenty threat models all assume CI runners cannot reach production databases, and nobody has tested it. What do you do?
answer
- an assumption repeated is an assumption unowned
- belief versus control
- one wrong belief falsifies twenty models
- put the proof where the control lives
- actively attempt the forbidden path
basics
~20 sTurn the repeated assumption into a test the platform owns and runs continuously. An unverified inherited control is a shared belief, not a control, and twenty models depend on it, so one wrong belief invalidates all twenty at once.
solid answer
~50 sThe recurrence is what makes it dangerous. Each model wrote 'CI runners cannot reach production databases' and moved on, so nobody owns proving it — and a network path that quietly opened last quarter falsifies twenty models simultaneously. The attacker who benefits is whoever compromises a build runner (a poisoned dependency, a pull request that runs code, a stolen runner credential) and the asset is customer data. My fix is to give the assumption an owner and a regression test that lives in the platform's own suite rather than in twenty product repositories: from a real runner, in the real network position, attempt the connection on every change to the platform and fail when it succeeds. After that, models can inherit the control by reference, because there is evidence behind it instead of a sentence.
go deeper
Know the difference between an assumption and a control. Writing that build runners cannot reach production is a belief about the network, and a belief on its own stops no attacker.
Explain how you would actually prove such a claim: an active attempt from the same network position a runner occupies, asserting that the connection fails, repeated on every change rather than once at design time.
Show that you place the test where the control lives and route its failures to the team that owns the path, and that you can name who benefits when the assumption is wrong — a compromised build runner reaching customer data in bulk.
Own the systemic point. Inheritance is what keeps a portfolio of models small enough to maintain, and that leverage is only safe when inheritance is verified and its dependants are known. Be ready to argue for funding that verification against teams who would rather ship.
## Assumption, control, and the gap between them Threat modelling depends on inheritance. A product team does not re-derive network segmentation, key storage or runner isolation for every service; it states what it is relying on and models what it owns. That is correct and it is how the practice stays affordable. The failure mode is that the inherited item is written down and never verified, so the model rests on a **belief** while reading like a **control**. *CI runners cannot reach production databases* is a good example because it sounds like a fact about the network. In twenty models it is doing enormous work: it is why a build compromise is scoped to the build, why credentials in the pipeline are treated as lower value, why database-level threats are ranked below others. If it is wrong — a peering route added for a migration, a temporary rule that outlived its ticket, a new runner pool placed in a subnet nobody re-checked — then twenty models are wrong at the same moment, and none of them will notice, because the sentence stays true on the page. ## Who benefits when the assumption is false Name the attacker and the asset, because they set how hard to work on this. The attacker is whoever controls what a build runner executes: a compromised transitive dependency pulled during the build, a pull request from a fork that triggers a job, a stolen runner registration credential, an insider with commit rights. That attacker starts with code execution in a machine that already holds credentials and network position. The asset is customer data sitting in the production database, reachable in bulk and without a login. That is why the assumption is worth a test rather than a footnote: it is the boundary between a bad day and a breach notification. ## Turning the assumption into a test The assumption states a **negative** property — this path does not exist — and negative properties cannot be verified by reading configuration. Configuration tells you what the configuration says; a route added elsewhere, a proxy, a service mesh egress, or a second network interface all falsify it without changing the file you read. Verify it by attempting the thing: - From a **real runner**, in the same network position a build actually occupies, including each runner pool. - **Attempt the connection** to a production database endpoint — resolve, connect, complete the transport handshake. - **Fail the run when it succeeds.** The test passes only when the connection is refused, dropped or unroutable. - **Run it on every change to the control**, not once at design time, because the property degrades through ordinary infrastructure work rather than through anything anyone would flag as security-relevant. Where the test lives matters as much as what it does. Put it in the platform's own suite, not in twenty product repositories. The product teams do not own the network path, cannot change it, and cannot see it change; twenty copies of the same test drift, get skipped, and still leave a twenty-first service arriving with no test at all. One test, sitting with the control, runs whenever the control changes and routes its failure to the team that can actually fix it. ## Inheritance becomes safe once it is evidenced Once the test exists, something changes in every one of the twenty models. Where they used to say *we assume runners cannot reach production*, they can say *we inherit runner-to-production isolation, verified by a test owned by the platform team*. The sentence is the same length; what sits underneath it is different. Inheritance is now the transfer of a verified control instead of the transfer of optimism. This also creates an obligation in the other direction. Something has to record **who depends on the assumption**, because the first question when the test goes red is not how the path opened — it is which services just lost a control they were counting on. If the dependency is only implicit in twenty documents, you discover the blast radius one team at a time, usually after an incident. ## The general rule Read the portfolio for assumptions the way you read it for threats. An assumption repeated in many models is not stronger for being repeated — it is more dangerous, because its consequences fan out and its ownership is diffuse. The practical rule: **every inherited control that many models rest on needs a named owner, an active test from the right vantage point, and a list of dependants.** Anything short of that is a sentence protecting nothing, replicated twenty times.
- Why put the test in the platform's own suite rather than asking each of the twenty teams to test it?Because the control is not theirs. They cannot change the network path and cannot see it change, so twenty copies of the test drift, get skipped or go stale, and the twenty-first service arrives with none at all. A single test living with the control runs on every change to that control, and its failure lands on the team that can actually fix the path.
- How do you verify a negative property like 'this path does not exist'?By attempting it, not by reading configuration. Configuration proves what the configuration says, while a route added elsewhere, an egress proxy or a second interface can open the path without touching it. From the real runner in the real network position, try to reach the production database endpoint and fail the run if the connection completes. Cover every runner pool, since position differs between them.
- The test is green for a year, then a platform change turns it red. What is the first question you ask?Which models depended on it. Twenty services just lost a control they were counting on, and each now carries an unmitigated path from a build compromise to customer data until it is closed. That is why the dependency should be recorded somewhere you can query rather than implied inside twenty documents — otherwise you learn the blast radius one team at a time.
A sign on a door reading 'this door is locked'. Twenty people have read the sign and planned around it. Nobody has pulled the handle.
saying these in an interview costs you the question
- Treats a written assumption as if it were a control
- Asks twenty product teams to each test the same path
- Verifies by reading configuration instead of probing
- Checks once at design time and never again
- Cannot say which models break when it fails
- Says repetition across models makes it more trustworthy