Posted 2026-05-20 · ~8 min read
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.
Status: the Resistance Score is planned methodology, not a live production score yet. JSO AI currently supports BYO provider keys, preview tools, usage tracking, and release-review guidance. For the current buyer checklist, use AI Resistance Evidence.
JSO's planned Resistance Score is the artifact we're designing to make the claim reviewable: a named adversarial probe runs against protected output and reports recovery categories, build by build. This post explains the methodology, the honesty trade-offs behind it, and where the metric will 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:
- Customer-specific. Generic "we protect against AI" testimonials describe an average protected file. Customers care about their bundle, their function names, their string literals.
- 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.
- 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 protected releases covered by the planned AI Corporate or AI Enterprise evidence workflow, the Resistance Score is designed to do this:
- Captures the original input source (in-memory; never persisted) and the protected output.
- 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.
- 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.
- 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.
- 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 source-free output is expected to look 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
When the planned score is live, a score of 100/100 will not mean nobody can deobfuscate your build. It will mean the specific attacker profile in that report recovered nothing. A determined human reverse engineer with hours to spend on a single function can still get further than a static probe. The Resistance Score is intended as a useful proxy for "is the protection holding up against the named research-style attacker"; 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 planned Resistance Score is purely a static-analysis-resistance number. Runtime Defense is the surface for tamper detection; runtime evidence lives on a different dashboard.
- Performance overhead. A 100/100 build that's 50× slower than the source isn't a win. The planned report design 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 planned Resistance Score measures that offline attacker, not the in-runtime one.
How the score affects your protection choices
The planned 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 planned Resistance Score is meant to answer that question with a number that's customer-specific, repeatable, attacker-disclosed, and updated as the attacker landscape moves.
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 is planned for JSO AI Corporate in late 2026. Corporate customers can contact support if they want to be considered for early protected-release review. The AI Enterprise pilot adds a broader deobfuscation-resistance benchmark for teams that need stronger evidence during security reviews.
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: AI resistance evidence · CASCADE and the LLM-deobfuscator question · JSO AI overview · VM bytecode protection.
Explore JSO AI Corporate
The Resistance Score is planned for late 2026, but Maximum-mode protection and VM bytecode protection are available today.
Try It Free
See Pricing