Posted 2026-05-20 · ~8 min read

The Resistance Score: a verifiable number for AI-resistance claims

Every commercial JavaScript obfuscator markets some flavour of "AI-resistant." Almost none of them ship a way for the customer to check. In 2026 that's a problem: CASCADE and adjacent LLM-augmented deobfuscators exist, customers know they exist, and "trust us" stops being an acceptable answer.

JSO's Resistance Score, shipping with JSO AI Corporate in 2026-Q4, is the artifact we're using to make the claim auditable: an adversarial LLM probe runs against your protected output and reports a concrete recovery percentage, build by build. This post explains the methodology, the honesty trade-offs we've made, and where the metric is going to fall short.

What problem we're trying to solve

The claim "obfuscator X resists AI deobfuscation" is true for almost every value of X — if the AI you have in mind is a 2019 deep-learning paper that nobody runs in production. It's false for almost every value of X if the AI is a CASCADE-class hybrid LLM + compiler IR pass. Without a published methodology, the marketing claim sits in this fuzzy middle where everyone's right and nobody can verify anything.

Three properties a useful resistance metric needs:

  1. Customer-specific. Generic "we protect against AI" testimonials describe an average protected file. Customers care about their bundle, their function names, their string literals.
  2. Repeatable. Two consecutive runs on the same input must produce comparable numbers. If the score swings wildly, it's not a number; it's noise.
  3. Attacker-disclosed. The score has to name the attacker. "Recovery percentage against CASCADE-equivalent" is a meaningful claim; "recovery percentage against AI" is not.

The methodology, concretely

For every protection run on an AI Corporate or AI Enterprise account, the Resistance Score does this:

  1. Captures the original input source (in-memory; never persisted) and the protected output.
  2. Runs a CASCADE-equivalent attacker against the protected output. This is an LLM (Claude primary, Gemini and GPT-class as cross-checks) prompted with the same prelude-identification + symbolic-inversion task CASCADE performs. The attacker has access to: the protected source, a representative obfuscator-output corpus for in-context learning, and a compiler-IR pass for the deterministic-inversion step.
  3. For each original identifier, string literal, and function boundary, the score measures whether the attacker recovered it. "Recovered" is a strict definition: exact match for strings; semantic match (a synonym the LLM produced for the original purpose) for identifiers; full bracket-matching for control flow.
  4. The score is published in four sub-categories: identifiers, string literals, control flow, API surface. A composite score is also published; the sub-scores are the actionable ones.
  5. Every score carries the attacker fingerprint — which LLM, which prompt version, which IR pass version. Two scores from different attacker generations are not directly comparable, and the dashboard says so.

The output that lands on your dashboard looks roughly like this:

Build:        rel-abcdef123
Fingerprint:  1234567890abcdef
Attacker:     cascade-equiv-v3 (Claude 3.7 / IR pass r12 / 2026-05)

Identifiers recovered:    8% (12 / 156)
String literals recovered: 0% (0 / 84)
Control flow recovered:    "partial" — dispatcher shape identified, opcode encoding not inverted
API surface recovered:     22% (4 / 18)
Composite Resistance:      84/100

Lowest-scoring transform:  RenameGlobals (Public-facing globals named clearly)
Recommended next step:     Add 'requestPayment' and 'verifySession' to VariableExclusion
                           to keep them readable AND mark them // @virtualize so the
                           bodies are protected by VM bytecode (cold path; trivial overhead).

The point of the score isn't the number; it's the breakdown plus the recommendation. A composite "84/100" only helps if you also know which 16 points were lost and how to recover them.

The honesty trade-offs we made

Three design decisions where the easy answer would have been more marketable than the honest answer:

1. The attacker is named, not anonymized

The dashboard says "Claude 3.7" instead of "the AI." That looks bad in a marketing screenshot — competitors can claim "we tested against three LLMs" and that sounds more thorough. But anonymizing the attacker means the customer cannot reproduce the result, and a metric they can't reproduce isn't a metric. Naming the attacker lets the customer run their own check; that's the higher-value trade.

2. The score can go down between releases of JSO

If we upgrade the in-house attacker from CASCADE-equiv-v3 to CASCADE-equiv-v4 and v4 is genuinely stronger, your existing builds' scores will drop. The dashboard will be honest about that — "this build was last scored against v3; your composite is now 79/100 against v4." The alternative is freezing the attacker at the version that produces flattering scores, which is what every "static security scanner" eventually devolves into. We won't do it.

3. The score is a probe, not a proof

A score of 100/100 doesn't mean nobody can deobfuscate your build. It means the specific attacker we ran today recovered nothing. A determined human reverse engineer with hours to spend on a single function will still get further than CASCADE-equiv-v3 will. The Resistance Score is a useful proxy for "is the protection holding up against the published research"; it is not, and we will not claim it is, a proof against the entire population of attackers.

What the score doesn't measure

  • Runtime tampering. The Resistance Score is purely a static-analysis-resistance number. Runtime Defense is the surface for tamper-detection; that score lives on a different dashboard.
  • Performance overhead. A 100/100 build that's 50× slower than the source isn't a win. The dashboard pairs the Resistance Score with the existing input/output size and the per-transform cost so customers see both axes.
  • Bundle size growth. Same point — Resistance and size live together.
  • License or domain-lock effectiveness. Those are runtime-enforcement properties, not deobfuscation-resistance ones. Runtime Defense docs.
  • Threats from inside the running environment. A customer-controlled JavaScript runtime can always see what's executing. Obfuscation buys time and effort against the offline-analysis attacker; the Resistance Score measures that offline attacker, not the in-runtime one.

How the score affects your protection choices

The selective-obfuscation suggester (also Phase 2) reads the Resistance Score breakdown and recommends targeted upgrades. The most common shape we expect:

  • Composite 90+ with all sub-scores high: nothing to do. Maximum mode is doing its job.
  • Composite 80–90, identifiers low: you probably have public-facing API names in VariableExclusion for compatibility reasons. The suggester will recommend keeping the names readable for compatibility but VM-virtualizing the function bodies, so the names leak but the implementations don't.
  • Composite 80–90, string literals low: almost always means strings are decoded by a too-clean prelude function. The suggester will recommend a RuntimeFingerprint binding so the decoder cannot be symbolically executed offline.
  • Composite below 80, control flow recovered: the prelude-interleaving research transform (when it ships in 2027) targets exactly this case. Until then the recommendation will be VM virtualization on the affected functions.

Why this matters more than another protection feature

The hardest sell for a paid obfuscator in 2026 isn't "is this stronger than the OSS package." It's "how do I know the protection is holding up as the attackers evolve." The Resistance Score answers that question with a number that's customer-specific, repeatable, attacker-disclosed, and updated as the attacker landscape moves. None of the existing competitor offerings have an equivalent.

Verifiability is the moat. Anyone can ship a polymorphic decoder. Few will publish the methodology by which they let customers check whether the polymorphism is holding up.

Status and how to be early

The Resistance Score ships with JSO AI Corporate in 2026-Q4. Customers on the AI Corporate waitlist get pre-release access for testing on real protected builds; the feedback directly shapes what makes the GA cut. The AI Enterprise pilot tier (Phase 3, 2027-Q1) adds the AI Deobfuscation Benchmark layer on top — same architecture, but the deobfuscators are real named tools (CASCADE / JSIMPLIFIER / GPT-class) and the report is the artifact customers cite when their own security teams ask for vendor evidence.

The pricing preview for both tiers is at premium-membership.aspx#ai-pricing; the threat-model framing this whole feature responds to is in the CASCADE post.

Related reading: CASCADE and the LLM-deobfuscator question · JSO AI overview · VM bytecode protection · AI strategic plan.