Summary
A missing input validation in iobCalcBilinear() causes Insulin-On-Board (IOB) to be
silently reported as 0 when profile.dia = 0. No exception is thrown. No log entry
is written. The APS loop receives IOB=0 and may authorize a correction bolus on top
of already-active insulin.
Affected File
lib/iob/calculate.js — iobCalcBilinear(), lines 45–56
Root Cause
var timeScalar = default_dia / dia; // dia=0 → Infinity (IEEE 754)
var activityPeak = 2 / (dia * 60); // dia=0 → Infinity
var scaled_minsAgo = timeScalar * minsAgo; // Infinity * N → Infinity
if (scaled_minsAgo < peak) { ... } // Infinity < 75 → FALSE always
else if (scaled_minsAgo < end) { ... } // Infinity < 180 → FALSE always
// Neither branch executes → returns { activityContrib: 0, iobContrib: 0 }
JavaScript does not throw on division by zero — it returns Infinity. Both branch
conditions evaluate to false, so the function returns zero for all values of
minsAgo, regardless of how much insulin was dosed.
Reproduction
const iobCalc = require('./lib/iob/calculate');
const treatment = { insulin: 1.0, date: new Date(Date.now() - 30*60*1000) };
const profile = { curve: 'bilinear' };
// Normal dia=4: IOB correctly calculated
console.log(iobCalc(treatment, new Date(), 'bilinear', 4.0, 75, profile));
// → { activityContrib: ~0.011, iobContrib: ~0.95 }
// dia=0: IOB silently zeroed
console.log(iobCalc(treatment, new Date(), 'bilinear', 0, 75, profile));
// → { activityContrib: 0, iobContrib: 0 } ← WRONG, 1u still active
Secondary Issue
In iobCalcExponential(): if profile.curve is not "rapid-acting" or
"ultra-rapid", console.error() fires but execution continues with the
original peak parameter unchecked. If the caller passed peak=undefined,
NaN arithmetic follows silently.
Impact
If profile.dia is set to 0 (invalid, but not validated at function entry),
the IOB calculation returns 0 for the entire duration of any active bolus.
The loop's decision logic (if reportedIOB < correction → authorize new dose)
will interpret this as "no insulin active" and may authorize a repeat correction
bolus, increasing hypoglycemia risk.
Formal Verification
Four Z3 SMT theorems were verified against a mathematical model of this function:
- OH1 SAT: confirmed — IOB=0 when insulin>0, minsAgo∈(0,180), dia=0
- OH2 SAT: confirmed — repeat bolus authorized despite active IOB
- OH3 UNSAT: confirmed — dia > 0 guard at entry fully eliminates the bug
- OH4 SAT: confirmed — invalid profile.curve causes silent NaN propagation
Suggested Fix
Add an explicit guard at the top of both iobCalcBilinear() and iobCalcExponential():
if (!dia || dia <= 0) {
console.error('Invalid DIA value:', dia, '— must be > 0. Check profile settings.');
return { activityContrib: 0, iobContrib: 0, error: 'INVALID_DIA' };
}
This is an early-return with a logged error. The function already returns
{ activityContrib: 0, iobContrib: 0 } in the buggy path — this fix makes
that explicit and logged rather than silent.
Notes
- This requires dia=0 in the user profile, which is an invalid configuration.
- No external attacker trigger — this is a safety/reliability issue, not an
exploitable vulnerability in the traditional security sense.
- Recommending a CVE request under CWE-369 given the medical device context.
Summary
A missing input validation in
iobCalcBilinear()causes Insulin-On-Board (IOB) to besilently reported as 0 when
profile.dia = 0. No exception is thrown. No log entryis written. The APS loop receives IOB=0 and may authorize a correction bolus on top
of already-active insulin.
Affected File
lib/iob/calculate.js—iobCalcBilinear(), lines 45–56Root Cause