---
title: 'Asset, Threat and Vulnerability'
source: 'https://academia.sh/en/courses/introduction-to-cybersecurity/asset-threat-and-vulnerability'
course: 'Introduction to Cybersecurity'
language: en
updated: '2026-10-21T05:02:56+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Models the asset, threat, and vulnerability terms as separate lists in the same inventory and counts each one'
    - 'Shows step by step, through narrowing, why multiplying the three lists does not give the number of reachable paths'
    - 'Separates vulnerability count from triple count: how many triples closing a given number of vulnerabilities actually closes'
---

# Asset, Threat and Vulnerability

Separating three terms into the same inventory: the cartesian product of 8 assets, 6 threats, and 12 vulnerabilities is 576, while the valid-triple count drops to 19, or 3.3%; two assets each carrying two vulnerabilities produce 1 and 4 triples; a vulnerability no threat can reach carries zero triples; two threats carrying the same five triples touch 4 and 5 separate assets; closing five of twelve vulnerabilities closes 12 of the 19 triples; and narrowing an asset's zone closes 3, 2, 1, or no triples at all.

The previous lesson scored eight items and counted which direction six decisions moved those
scores. The whole computation ran on a single list, and the names of that list's parts were left
unclear: the thing being protected was called "asset," but what was the thing that would lower a
score, what was the thing that let it drop. This lesson splits the same inventory into three
separate lists and counts how the three combine.

**Asset** is the thing being protected: data, a service, a device, a record. **Threat** is an
agent capable of causing harm; the party capable of doing so is called a **threat actor**.
**Vulnerability** is the weakness in the asset that lets that harm happen. The three are separate
lists and are counted separately. In everyday speech they get used interchangeably; once counted,
it becomes clear they are not interchangeable.

## Three Lists Answer Three Separate Questions

The example is again the fictional **regional measurement network**, and the asset list is the same
eight items as the previous lesson. The inventory below is a **model**: there is no real network,
no real threat record, no real vulnerability list.

**SC6:** every asset is named by the widest zone it can be reached from — outer, field, inner.
**SC7:** every vulnerability lives in exactly one asset and carries a type. **SC8:** every threat
is defined by the set of zones it can reach and the vulnerability types it can use. **SC9:** an
`(asset, vulnerability, threat)` triple is valid only if all three conditions hold together — the
vulnerability lives in that asset, the threat reaches that zone, the threat can use that
vulnerability type.

```js
// security/inventory.mjs — fictional measurement network's asset, threat, and vulnerability
// inventory (model): there is no real network, threat record, or vulnerability list. No randomness.

// SC6: every asset is named by the widest zone it can be reached from (outer, field, inner).
export const ASSET = `
  reading-data         inner
  billing-record       outer
  work-order-queue     field
  identity-store       inner
  configuration        inner
  audit-log            inner
  meter-firmware       field
  field-app            outer`
  .trim().split("\n").map((r) => { const [name, zone] = r.trim().split(/\s+/); return { name, zone }; });

// SC7: every vulnerability lives in exactly one asset and carries a type.
export const VULN = `
  outdated-library-version    billing-record       version
  default-password             configuration        credential
  broad-read-permission        reading-data          permission
  unvalidated-input            field-app             input
  open-management-endpoint     configuration         configuration
  unsigned-update               meter-firmware        integrity
  shared-account                identity-store        credential
  unencrypted-transfer          work-order-queue      transfer
  unretained-log                audit-log             visibility
  outdated-agent-version        field-app             version
  physical-access-point         meter-firmware         physical
  unvalidated-field             audit-log             input`
  .trim().split("\n").map((r) => {
    const [name, asset, type] = r.trim().split(/\s+/);
    return { name, asset, type, zone: ASSET.find((v) => v.name === asset).zone };
  });

// SC8: every threat is defined by the zones it can reach and the vulnerability types it can use.
export const THREAT = [
  { name: "external-opportunist", zone: ["outer"], type: ["version", "credential", "input", "configuration"] },
  { name: "targeted-external", zone: ["outer", "field"],
    type: ["version", "credential", "input", "configuration", "transfer", "integrity"] },
  { name: "field-proximate", zone: ["field"], type: ["physical", "integrity", "transfer"] },
  { name: "internal-user-error", zone: ["inner", "field"], type: ["configuration", "permission"] },
  { name: "malicious-insider", zone: ["inner", "field", "outer"],
    type: ["permission", "credential", "visibility", "transfer"] },
  { name: "supply-chain", zone: ["field", "inner"], type: ["integrity", "version"] },
];

// SC9: a triple is valid only if the vulnerability reaches that zone and the threat can use
// that vulnerability type.
export const valid = (v, t) => t.zone.includes(v.zone) && t.type.includes(v.type);
export const TRIPLE = VULN.flatMap((v) => THREAT.filter((t) => valid(v, t))
  .map((t) => ({ asset: v.asset, vuln: v.name, threat: t.name })));

export const vulnCount = (a) => VULN.filter((v) => v.asset === a).length;
export const tripleCount = (field, name) => TRIPLE.filter((u) => u[field] === name).length;

// SC10: an asset's zone can be narrowed by one step (outer -> field -> inner); narrowing
// changes which vulnerabilities in that asset are reachable, it does not close them.
export const NARROW = { outer: "field", field: "inner", inner: "inner" };
export function narrowedTriple(asset) {
  return VULN.reduce((n, v) => n + THREAT.filter((t) =>
    t.zone.includes(v.asset === asset ? NARROW[v.zone] : v.zone) && t.type.includes(v.type)).length, 0);
}
```

```js
// security/count.mjs — three lists in one inventory: how many assets, threats, vulns, and valid triples.
import { ASSET, VULN, THREAT, TRIPLE, NARROW, vulnCount, tripleCount, narrowedTriple } from "./inventory.mjs";

const write = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));
const cartesian = ASSET.length * THREAT.length * VULN.length;

console.log(`${ASSET.length} assets, ${THREAT.length} threats, ${VULN.length} vulns; ` +
  `cartesian product ${cartesian}, since each vuln is already tied to one asset the ` +
  `candidates are ${VULN.length * THREAT.length}, valid triples ${TRIPLE.length}`);

const A = [-21, 8, 8, 9, 14];
console.log("\n1. vulns and valid triples per asset");
write(A, "asset", "zone", "vulns", "triples", "triples/vuln");
for (const v of ASSET)
  write(A, v.name, v.zone, vulnCount(v.name), tripleCount("asset", v.name),
    vulnCount(v.name) ? (tripleCount("asset", v.name) / vulnCount(v.name)).toFixed(1) : "-");
write(A, "total", "-", VULN.length, TRIPLE.length, (TRIPLE.length / VULN.length).toFixed(1));

const B = [-26, 15, 8, 9];
console.log("\n2. how many threats can use each vuln");
write(B, "vuln", "type", "zone", "triples");
for (const v of [...VULN].sort((x, y) => tripleCount("vuln", y.name) - tripleCount("vuln", x.name)))
  write(B, v.name, v.type, v.zone, tripleCount("vuln", v.name));

const C = [-23, 8, 7, 9, 10];
console.log("\n3. reach and use per threat");
write(C, "threat", "zones", "types", "triples", "assets");
for (const t of THREAT)
  write(C, t.name, t.zone.length, t.type.length, tripleCount("threat", t.name),
    new Set(TRIPLE.filter((u) => u.threat === t.name).map((u) => u.asset)).size);

const D = [-48, 12, 9];
console.log("\n4. where the product narrows");
write(D, "step", "candidates", "left %");
const pct = (n) => ((n / cartesian) * 100).toFixed(1);
for (const [name, n] of [["asset x threat x vuln", cartesian],
  ["vuln already tied to one asset (vuln x threat)", VULN.length * THREAT.length],
  ["threat can reach that zone", VULN.reduce((a, x) =>
    a + THREAT.filter((t) => t.zone.includes(x.zone)).length, 0)],
  ["threat can use that vuln type", TRIPLE.length]]) write(D, name, n, pct(n));

const sorted = [...VULN].map((v) => tripleCount("vuln", v.name)).sort((x, y) => y - x);
const running = sorted.map(((s) => (n) => (s += n))(0));
console.log(`\nvuln order by triples carried: ${sorted.join(" ")}`);
console.log(`closing sum: ${running.join(" ")} (5 of 12 vulns close ${running[4]}/${TRIPLE.length}` +
  ` triples); vuln carrying zero triples: ` +
  VULN.filter((v) => tripleCount("vuln", v.name) === 0).map((v) => v.name).join(", "));

const E = [-21, 8, 9, 9, 9];
console.log("\n5. narrowing an asset's zone by one step (vuln count unchanged)");
write(E, "asset", "zone", "new", "triples", "closed");
for (const v of ASSET)
  write(E, v.name, v.zone, NARROW[v.zone], narrowedTriple(v.name), TRIPLE.length - narrowedTriple(v.name));
```

```
8 assets, 6 threats, 12 vulns; cartesian product 576, since each vuln is already tied to one asset the candidates are 72, valid triples 19

1. vulns and valid triples per asset
asset                    zone   vulns  triples  triples/vuln
reading-data            inner       1        2           2.0
billing-record          outer       1        2           2.0
work-order-queue        field       1        3           3.0
identity-store          inner       1        1           1.0
configuration           inner       2        2           1.0
audit-log               inner       2        1           0.5
meter-firmware          field       2        4           2.0
field-app               outer       2        4           2.0
total                       -      12       19           1.6

2. how many threats can use each vuln
vuln                                 type    zone  triples
unsigned-update                 integrity   field        3
unencrypted-transfer             transfer   field        3
outdated-library-version          version   outer        2
broad-read-permission          permission   inner        2
unvalidated-input                   input   outer        2
outdated-agent-version            version   outer        2
default-password               credential   inner        1
open-management-endpoint    configuration   inner        1
shared-account                 credential   inner        1
unretained-log                 visibility   inner        1
physical-access-point            physical   field        1
unvalidated-field                   input   inner        0

3. reach and use per threat
threat                    zones  types  triples    assets
external-opportunist          1      4        3         2
targeted-external             2      6        5         4
field-proximate               1      3        3         2
internal-user-error           2      2        2         2
malicious-insider             3      4        5         5
supply-chain                  2      2        1         1

4. where the product narrows
step                                              candidates   left %
asset x threat x vuln                                    576    100.0
vuln already tied to one asset (vuln x threat)            72     12.5
threat can reach that zone                                42      7.3
threat can use that vuln type                             19      3.3

vuln order by triples carried: 3 3 2 2 2 2 1 1 1 1 1 0
closing sum: 3 6 8 10 12 14 15 16 17 18 19 19 (5 of 12 vulns close 12/19 triples); vuln carrying zero triples: unvalidated-field

5. narrowing an asset's zone by one step (vuln count unchanged)
asset                    zone      new  triples   closed
reading-data            inner    inner       19        0
billing-record          outer    field       19        0
work-order-queue        field    inner       17        2
identity-store          inner    inner       19        0
configuration           inner    inner       19        0
audit-log               inner    inner       19        0
meter-firmware          field    inner       16        3
field-app               outer    field       18        1
```
Numbers belong to the **measurement** class; their inputs are SC6–SC9.

## The Product Is Not a Path Count

The fourth table is this lesson's central finding. Multiplying the lengths of the three lists
gives 576. That number measures nothing, because the lists are not independent. The first
narrowing is definitional: every vulnerability already lives in exactly one asset, so the asset
dimension is not a separate factor — 72 candidates remain, **12.5%** of the product. The second
narrowing comes from reach: the threat has to reach the zone the vulnerability lives in, and the
candidates drop to 42. The third comes from capability: the threat has to be able to use that
vulnerability's type, leaving **19 triples** — **3.3%** of the starting product.

The gap is not a difference of scale but of kind. The sentence "we have eight assets, six threat
types, and twelve vulnerabilities" is an inventory; it says nothing about what could happen from
where. Multiplying the three produces a scare number. What is actually countable is where the
three **intersect**, and that intersection narrows under two conditions: reach and capability.

Still, the number 19 is not yet a path count. Every triple assumes the threat can touch the asset
directly; the zone is given as a label, not counted as a gate to pass through. Reaching an asset
in the inner zone actually requires passing over other assets first, and each of those crossings
is a separate step. What this lesson leaves behind is an **intersection count**; the path count
will be done in this topic's last lesson.

## Same Number of Vulnerabilities, a Fourfold Difference

The first table shows why vulnerability count alone is not an indicator. `audit-log` and
`field-app` both carry **two vulnerabilities each**. The first produces 1 triple, the second 4.
The ratio per vulnerability is 0.5 against 2.0 — a **fourfold difference** at the same
vulnerability count.

The source of the difference shows up in the second table. `field-app` is in the outer zone and
its vulnerabilities are of type input and version; two of the three threats that reach the outer
zone can use those types. `audit-log` is in the inner zone and its vulnerabilities are of type
visibility and input; of the three threats that reach the inner zone, only one uses visibility,
and none use the input type.

The last row is this lesson's sharpest item: the `unvalidated-field` vulnerability carries **zero
triples**. The vulnerability is real, it sits in the list, it gets flagged in an audit — but no
threat can reach it and use it. That does not mean the vulnerability should be left unpatched; if
the zone boundary changes, the triple count stops being zero. What it means is this: **vulnerability
count is not an exposure measure.** One of twelve vulnerabilities is exposed to nothing, and
another is exposed to three at once.

## A Threat's Two Separate Widths

The third table measures threats on two axes: how many zones they reach and how many
vulnerability types they can use. `targeted-external` reaches two zones but uses six types;
`malicious-insider` reaches three zones and uses four types. Both produce **five triples** — the
same number, by two different routes.

The difference is in the last column: `targeted-external` touches four separate assets,
`malicious-insider` touches five. Reach width and capability width give the same triple total, but
their spread differs. Capability width makes multiple vulnerabilities on the same asset usable;
reach width touches more assets. A defense decision cannot be written without knowing **which of
the two it is narrowing**.

`supply-chain` sits at the opposite end: it reaches two zones, uses two types, produces a single
triple, and touches a single asset. This row records that the threat list's length is not an
indicator either — one of six threats carries **one nineteenth** of the inventory.

The last two lines rank the return on closing. When the twelve vulnerabilities are ordered by
triple count, the sequence `3 3 2 2 2 2 1 1 1 1 1 0` comes out. Closing the five vulnerabilities
that carry the most closes **12 of the 19 triples**; the remaining seven triples need seven more
separate vulnerabilities closed, and the twelfth adds nothing. Closing order is set not by
vulnerability count but by **triple count carried**.

## Which of the Three Lists Is Yours to Move

The fifth table tries a third lever. Closing a vulnerability shortens one list; the threat list
cannot be shortened, because who is capable of what is not the organization's decision to make.
What remains is reach: narrowing an asset's zone by one step changes the set of threats that can
reach its vulnerabilities **without closing any of them**.

The results show four separate behaviors. When `meter-firmware` is pulled from field to inner,
**3 triples close** — equal to the highest return of closing a single vulnerability, and both of
its vulnerabilities stay exactly where they are. `work-order-queue` closes 2, `field-app` closes
1. The four assets already in the inner zone have no further step left to narrow, and the triples
they close are zero.

The second row is the table's most instructive item: when `billing-record` is pulled from outer to
field, the triples closed are **zero**. The item has moved to a narrower zone, but its
version-type vulnerability can be used both by a threat that reaches the outer zone and by another
that reaches the field zone; a new triple opens exactly where one closed. **The return on
narrowing does not depend on how hidden the item now is, but on which set of threats is left
outside.**

The three levers are therefore not the same thing: closing a vulnerability deletes a row from the
list, narrowing a zone leaves the same row in place and changes the set of threats that reach it,
and the threat list is not yours to move at all. This is the last thing this lesson can count; the
second lever's real measure cannot be given without knowing how many steps sit between zones.

## Summary

- Asset is the thing protected, threat is the agent capable of causing harm, and vulnerability is
  the weakness in the asset; the three are separate lists and are counted separately.
- The product of eight assets, six threats, and twelve vulnerabilities is 576; the valid-triple
  count is 19, or 3.3% of the product. The narrowing comes from two conditions: does the threat
  reach that zone, can it use that vulnerability type.
- Nineteen triples are still not a path count; every triple assumes the threat touches the asset
  directly and does not count zone-to-zone crossings as steps.
- Two assets carrying two vulnerabilities each produce 1 and 4 triples; one vulnerability carries
  zero triples, another carries three. Vulnerability count alone is not an exposure measure.
- Of two threats that produce five triples each, one touches four assets and the other five; reach
  width and capability width give the same total with a different spread.
- Narrowing an asset's zone by one step, without closing any vulnerability, closes 3, 2, 1, or 0
  triples; when other threats already reach the narrower zone, the return stays zero.

## Next Step

This lesson counted nineteen triples and wrote every one of them at equal weight. But the single
triple carried through `physical-access-point` does not occur at the same frequency as the three
carried through `unencrypted-transfer`, nor does it cost the same when it does occur. In this
lesson, closing order was set purely by triple count carried; yet an item that carries few triples
can still be far heavier. The next lesson combines these two dimensions — **probability** and
**impact** — over a set of items, and shows how two different scoring schemes rank the same list
differently.
