Claims and benefits

Does obfuscation break underwriting and rating rules?

No, not on its own. We protected an underwriting module on five presets and every risk quoted identically. Then we renamed the members it reads, and a wildfire-exposed property that rates at $4,836 was quoted $1,560 - by a rating table that was still there, still populated, and looking up the wrong key.

What we actually measured

A quoting screen decides whether a risk is written and at what price. The module we built has the shape these really have: a base rate, a table of rating factors by risk class, a list of classes the carrier does not write, a cap on the claims surcharge, and an inspection rule the carrier wired in because the underwriting guidelines are theirs.

The engine consuming it is a separate installed library, copied in unprotected. Baseline: five presets, twenty-five comparisons, twenty-five identical quotes. Protection alone changed nothing.

A missing rating factor is not an error - it is 1.0

This is the whole story of the module, and it is a specific instance of a general problem with lookups. When a rating factor cannot be found, nothing throws and nothing becomes NaN. The engine falls back to a factor of 1.0, which is a completely ordinary factor that many risks legitimately carry.

Rename the option holding the table and every risk rates at 1.0. The coastal property that should quote $3,312 quotes $1,380. The wildfire-exposed property that should quote $4,836 quotes $1,560 - under a third of the correct premium. Both quotes are finite, positive, plausible and bindable. Neither is flagged.

The direction is what makes this expensive: the fallback under-prices precisely the risks that carry the highest factors. The standard suburban house at factor 1.0 is unaffected, because it was already at the fallback. Your book is mispriced exactly where the exposure is.

The factor is still in the configuration, under a new name

We then renamed a single key inside the table rather than the table itself.

factors=standard:1 _0x1:2.4 wildfire:3.1

The 2.4 coastal factor is visibly still there. The wildfire factor beside it is untouched and works perfectly. But the coastal risk quoted $1,380 instead of $3,312, because the lookup asks for coastal and the table now offers something else.

One name, one segment of the book, under-priced by 58%, with a configuration screen that shows a populated rating table. This is the strongest argument in the article against treating a configuration dump as evidence: it tells you entries exist, not that anything finds them.

One rename reached a pricing table and a refusal list at once

The record field naming the risk class is used twice: to look up the rating factor, and to check the decline list. Renaming it reaches both.

Every factor lookup missed, so the coastal and wildfire risks were under-priced exactly as above. And the brushfire-zone risk the carrier does not write at all was quoted and bound:

declined-class-not-written BOUND=true bound(rsk-6 class=undefined factor=1 ... premium $1200.00 annual ...)

A class that appears nowhere in the carrier's appetite, on the books at the base rate. Two controls that are genuinely separate in design fell together because they are properties of the same field on the same record, and one pattern reached both. Separation in a design document is not separation in a build.

What the rest of the module did

The surcharge cap failed open in the ordinary way: a risk with six prior claims took a 90% load instead of the capped 45%, which over-prices rather than under-prices and is the safer direction. Renaming the prior-claims count produced NaN and the engine's finite check refused to bind at all - loud, and exactly what that one-line mitigation is for. The inspection rule's permission flag failed closed and refused every risk including acceptable ones. The enabling flag in front of it produced a union behaviourally identical to the flag alone, while still reporting the rule as caller-supplied and correct.

So the numeric fields fail loudly and the lookup keys fail silently, which is the same split this pass measured on claim records and payer records. The value is protected by a finite check. The name that decides which value to use is not protected by anything.

What actually protects you

Make a missing lookup an error. A rating factor that cannot be found should refuse to quote, not default to 1.0. That is a one-line change in your own code, it is worth having regardless of obfuscation because a mistyped class in a config file produces the same miss, and it converts every silent result in this article into a loud one.

Validate the table against the classes you actually write. If the set of keys in the rating table does not equal the set of classes in your appetite, that is a startup assertion, and it catches a renamed key before a single quote goes out.

And keep lookup keys out of any renaming pattern. A name that describes something is a candidate for obscuring. A name that is used to find something - a table key, a record identifier, a grouping key, an ordering key - is a name your own code has to agree with itself about across a boundary, and renaming reaches only one side of it.

Frequently asked questions

Does protecting an underwriting module change what a risk is quoted?

Not in what we measured. Five presets covering the ES5 and modern targets plus the string transforms, every risk through every build: twenty-five comparisons, twenty-five identical quotes.

What happens when a rating factor cannot be found?

Nothing throws and nothing becomes NaN. The engine falls back to a factor of 1.0, which is an ordinary factor many risks legitimately carry. In our measurement a wildfire-exposed property that rates at $4,836 was quoted $1,560, and the quote was finite, plausible and bindable.

Why is the direction of that failure particularly expensive?

Because the fallback under-prices exactly the risks carrying the highest factors. The standard suburban house at factor 1.0 was unaffected, since it was already at the fallback value. The mispricing concentrates precisely where the exposure is.

Can we see the problem on the configuration screen?

Not reliably. When we renamed a single key inside the rating table, the 2.4 factor was still visible in the dump under a new name, and the factor beside it still worked. A configuration screen shows that entries exist, not that anything finds them.

How did one rename reach both the pricing table and the decline list?

Because the field naming the risk class is used for both lookups. Renaming it under-priced the high-factor risks and bound a class the carrier does not write at all, at base rate. Two controls that are separate in the design were properties of one field on one record.

Did anything fail in a safe direction?

Yes. The surcharge cap failing open over-prices rather than under-prices. Renaming the prior-claims count produced NaN and the engine refused to bind, which is loud and correct. The inspection rule's permission flag refused every risk, which is costly but not dangerous.

What is the cheapest safeguard to add?

Refuse to quote when a rating factor lookup misses, instead of defaulting to 1.0. It is one line, it converts every silent result in this article into a loud one, and it protects you against a mistyped class in a configuration file as well.

Related reading