skip to content

In an attack-tree library, what is a reusable subtree and when do you re-attach one?

level: middleimportance: should knowfreq 38%

answer

  1. decompose the sub-goal once
  2. same sub-goal under many roots
  3. reference it, do not copy-paste
  4. shape is shared, numbers are not
  5. buys consistency, costs coupling

basics

~20 s

A reusable subtree is one sub-goal decomposed once - say 'gain write access to the build runner' - and re-attached under many attack trees by reference. Re-attach when the sub-goal genuinely recurs with the same shape.

solid answer

~50 s

An attack tree decomposes one attacker goal into AND branches (all children needed) and OR branches (any one suffices). Some sub-goals recur across many systems: obtaining a working operator credential, getting write access to a build runner, walking out with a bulk data extract. A reusable subtree is that sub-goal decomposed once, stored in a shared library, and re-attached under many roots by reference rather than copy-paste. You re-attach when the sub-goal's *structure* - its branches and its concrete leaf actions - really is the same for this system; you write fresh when only the label matches. The payoff is consistency and speed: six service deploy trees all inherit the same, reviewed decomposition of runner write-access instead of six people guessing. The price is coupling: one shared node now has several consumers, so its values must be set per consumer and someone has to own keeping it true.

go deeper

for a junior

Know the vocabulary: a subtree is the decomposition hanging under one sub-goal, and a reusable one is stored centrally and pointed at from several trees. Be able to name a sub-goal that plainly recurs, such as obtaining a valid credential.

for a middle

Explain the mechanics: what re-attachment shares (branch structure, leaf actions, AND/OR operators) versus what is set per attachment (values, pruned branches, existing controls), and why referencing beats copying.

for a senior

Show the judgment call in practice - you checked each branch is real for this system before attaching, you pruned what is not, and you can say which consumers a given library subtree currently has.

for a principal

Own the tradeoff: reuse buys consistency across a portfolio and buys coupling with it. Be ready to argue when a library is worth its upkeep and when six independent trees are cheaper than one shared node nobody maintains.

## The problem reuse solves An attack tree starts at a single attacker goal and decomposes it downward. Each internal node is a sub-goal; an **AND** node means every child must be achieved, an **OR** node means any one child suffices. Leaves are concrete actions. Build a dozen of these across a portfolio and you notice the same sub-goals appearing again and again - *obtain a working operator credential*, *gain write access to the build runner*, *get a bulk extract out of a datastore you can legitimately read*. Re-deriving that decomposition each time is wasted effort, and worse, it is **inconsistent**: two engineers modelling the same sub-goal a month apart produce different branches, so two trees disagree about what an attacker actually has to do. A **reusable subtree** is that recurring sub-goal decomposed once, reviewed once, kept in a shared library, and **re-attached** under many roots. Re-attached means referenced - the consuming tree points at the shared node, it does not paste a private copy. That distinction is the whole point: a copy silently forks the moment either side is edited, and nobody knows the two ever agreed. ## What re-attachment looks like Take a monorepo with six services, each with its own tree. One service's tree: ``` GOAL: ship attacker-controlled code into service-payments production [OR] |- get a malicious change merged [AND] | |- obtain a contributor account with push rights | +- get the change past required review +- <shared> gain write access to the build runner [OR] |- land a workflow change that executes on the runner pool |- compromise a dependency the build resolves at build time +- abuse a runner-pool credential whose role is broader than the job ``` The `<shared>` node is the library subtree. The same node hangs under the other five service trees. The adversary it models is a contributor who can land a workflow change, or a dependency that runs code during the build; the assets at stake are source-code IP and the deploy credential the job assumes. ## Shared structure, local values What travels with the subtree is its **shape**: the sub-goal wording, its branches, its leaf actions, and the AND/OR operators between them. What does **not** travel is anything system-specific: - **Leaf values** - cost, skill, time, detectability, or a simple feasible/infeasible flag. Two services on different runner pools do not have the same numbers. - **Applicability** - a branch that is simply unreachable for one consumer is pruned in that consumer's attachment. - **Existing controls** - a control one consumer already has raises that branch's cost there and nowhere else. Because values are local, the *propagated* result is local too. At an OR node scored by attacker cost the value is the **minimum** over the children, since the attacker picks the cheapest branch; at an AND node it is the **sum**, since they must do all of them. Identical structure plus different leaf values therefore yields a different cheapest path per consumer - which is exactly the information you re-attached the subtree to get, and exactly what a copy-paste reuse tends to destroy by carrying one system's numbers into another. ## When to re-attach, and when not to Re-attach when three things hold: the sub-goal is stated the same way (an attacker objective, not a vulnerability); the branches under it are genuinely the ones this system offers; and you are willing to accept that a future edit to the shared node reaches this tree. Write fresh - or fork the library copy - when the sub-goal's *shape* differs, not just its numbers. A payments company reusing a 'reach the signing-request path on the HSM' subtree across card issuance and settlement will find one consumer sits inside the audited cardholder-data segment with a different set of reachable branches; that is a structural difference and forking is the honest answer. Parameterisation handles different values; it cannot handle different branches. The common failure is name-matching. 'Steal a credential' appears in two trees, so someone re-attaches the same subtree, and now a tree claims an attack path its system does not have. A reused subtree is a claim about the system, and every attachment still needs a person to confirm each branch is real there. ## The cost side Reuse creates a dependency graph across your models. One shared node with six consumers means an edit made for one consumer's sake lands in five trees that nobody re-read, and a subtree written for an architecture that has since changed can sit in the library describing no consumer at all. So a library needs the same hygiene as shared code: a named owner per subtree, a record of which trees consume it, and a periodic check that each attachment still holds. Without that, reuse trades six inconsistent-but-honest trees for six consistent-and-wrong ones.

  • Why insist on referencing the shared subtree rather than copying it into each tree?
    A copy forks the instant either side is edited, and nothing records that they were ever the same, so the trees drift apart invisibly. A reference keeps one reviewed decomposition and makes the consumer list explicit, which is what lets you find every tree affected when the sub-goal's shape changes. The cost is real coupling, but it is coupling you can see and manage rather than divergence you cannot.
  • How do you decide a recurring sub-goal is worth promoting into the shared library at all?
    Promote it when the same sub-goal has already been derived independently in two or three trees and the derivations agree on structure, when it is deep enough that re-deriving it wastes real effort, and when the sub-goal is stable - it describes how an attacker reaches something, not a control that changes each quarter. A sub-goal with one consumer is not a library entry, it is just a branch.
  • A candidate says reuse means every consumer tree now shows the same risk for that sub-goal. What is wrong with that?
    Only the structure is shared. Leaf values - cost, skill, detectability, feasibility - are set per attachment, and unreachable branches are pruned per attachment. Since an OR node takes the minimum of its children and an AND node the sum, the same shape produces a different cheapest path and a different propagated value per system. Identical risk across consumers is a symptom that nobody parameterised the attachment.

It is a shared library function, not a snippet you paste. Callers pass their own arguments, and one edit to the function reaches every caller whether they read it or not.

saying these in an interview costs you the question

  • Copies the subtree into each tree and calls that reuse
  • Re-attaches on a matching sub-goal name without checking the branches
  • Assumes a shared subtree implies the same risk everywhere
  • Treats a shared subtree as reviewed once and never again
  • Parameterises a consumer whose branches are structurally different instead of forking

context