Skip to content
Ferroscope
Menu

11Method

Every number here
has a derivation.

The exposure score is four multiplications and one exponential saturation. The planner is a division. Nothing is learned, sampled, or guessed at request time.

pure functions54 unit testsno LLM in the loop

01 · Matching

An incident is matched when at least one stack item it declares as affected is present in the profile. If the incident has no stack surface by design — a regional bank breach, a state-actor campaign — it is matched by region and account type instead.

present    = affected ∩ declared
confidence = 0.55 + 0.45 × (|present| / |affected|)

// no stack surface:
banking     → matched if region matches and profile.bank
data-breach → matched if region matches
state-actor → matched if region matches

02 · Mitigation

Hygiene controls reduce impact, but they are capped at 80% and applied per vector. Ticking every box cannot produce a zero.

ControlApplies toReduction
Phishing-resistant 2FAidentity, banking, supply chain, ai-agent, network, state actor+0.22
Password manageridentity, banking, supply chain+0.18
Automatic updatesendpoint, runtime, browser, web server, network, certificate authority+0.25
Capall0.80

03 · Score

raw_i = severityWeight_i × avgRiskWeight_i × confidence_i × (1 − mitigation_i)

risk  = round(100 × (1 − e^(−0.55 × Σ raw_i)))
score = 100 − risk

The exponential saturation is deliberate. Adding a tenth incident should not add ten points to a score that already reads 90. It also guarantees the score cannot leave 0–100 no matter how large the dataset grows.

04 · Planner

relevantRisk = Σ contribution of exposed incidents the action touches
riskReduced  = min(totalRisk, round(relevantRisk × action.reduces))
efficiency   = riskReduced / max(action.minutes, 1)

residual = fold(risk, completed, (r, a) => r − r × a.reduces × 0.72)

Residual risk is multiplicative, not additive, which is why the planner can never claim to remove 130% of your risk. The 0.72 factor is a deliberate haircut: doing the action is not the same as doing it perfectly.

05 · Bands

fortified0 – 19Nothing declared intersects the dataset, or the impact is fully mitigated.
watchful20 – 44One meaningful intersection with strong hygiene, or several weak ones.
exposed45 – 69Multiple intersections, at least one with a high-risk component.
critical70 – 100Broad intersection with critical-severity incidents and no mitigating controls.

06 · What the model refuses to claim

It is not a vulnerability scan

Ferroscope never inspects your machine. It compares what you declare against a public dataset. A clean report means “no declared intersection”, not “no vulnerabilities”.

Patch state is coarse

There is a single auto-updates flag. The real world has per-package patch levels, and modelling them honestly would require a scanner the project deliberately does not ship.

The dataset is a snapshot

27 incidents from a specific window, each with sources. It ages. The version string is in every report because the numbers only mean something relative to that version.

Risk is not probability

A 58 does not mean a 58% chance of compromise. It is a bounded index of surface intersection weighted by severity. Treat it as a ranking device, not a forecast.