Skip to content

feat(javascript): report javascript-obfuscator-family signatures in recovery limitations - #8530

Draft
Raed2180416 wants to merge 1 commit into
morluto:mainfrom
Raed2180416:recovery/obfuscator-signatures
Draft

Raed2180416 wants to merge 1 commit into
morluto:mainfrom
Raed2180416:recovery/obfuscator-signatures

Conversation

@Raed2180416

Copy link
Copy Markdown

Summary

recover_javascript_sources silently returns poor results for inputs produced by javascript-obfuscator (and its clones): the family's _0x-hex identifiers and rotated string-array decoders break structural bundle splitting, so a caller receives a single low-value module with no explanation. This PR fingerprints each selected input for that family's signatures before publication and reports one explicit limitation per detected input, naming a working external remediation pipeline and re-ingestion.

AI-assisted contribution; the Author readiness declarations below are left to the human owner, so this PR is opened as a draft.

Problem and expected behavior

Trigger: recover a bundle produced with javascript-obfuscator (for example the default configuration of 4.2.2). Observed before this change: Wakaru --unpack=strict returns one module where the same un-obfuscated bundle splits into per-source modules, and the result carries no signal about why.

Expected: the result carries a limitation that (a) names the family with the observed signature counts for that specific input, (b) states the structural-splitting consequence, and (c) names the remediation path: run external static deobfuscators on the published input copy under inputs/ and re-ingest that output. Detection is signature-based and intentionally conservative; REA still does not install or run those tools.

Change and scope

  • New src/javascript/recovery/RecoveryObfuscatorDetection.ts: two fixed patterns (_0x[0-9a-f]{4,8} identifiers; decoder calls _0x…(0x…), with minimum co-occurrence thresholds (4 identifiers, 2 decoder calls) so ordinary code that merely names a few bindings _0x… stays undetected. Counting runs on the already-validated UTF-8 snapshot inside the existing publication pass; no new limits, workers, or deadlines.
  • WakaruPublication.ts appends the per-input limitation after the existing limitation entries; publication behavior, digests, and manifest shape are otherwise unchanged.
  • Tool contract description gains one sentence naming the reported limitation (canonical contract text; generated catalog check passes).
  • docs/javascript-recovery.md gains an "Obfuscated inputs" section with the observed verification and the remediation commands.
  • Not included: running webcrack/obfuscator-io-deobfuscator/humanify inside REA, detection of mangled-identifier configurations, or classifier-based detection. Those are out of scope for this adapter.

Contribution scope: ordinary enhancement within the existing recovery boundary; no new provider or capability. Not applicable (no linked maintainer scope decision required).

Contract and boundary impact

  • Semantic owner and earliest changed stage: recovery publication (WakaruPublication) fingerprints the validated input snapshot before writing; consumers are the returned limitations array and the generated tool description.
  • Contribution scope fit and linked maintainer decision: ordinary enhancement within existing boundaries; not applicable.
  • CLI and MCP/tool-catalog contract: recover_javascript_sources description extended by one sentence; npm run mcp-catalog:check and metadata:check pass.
  • Provider, bridge, target-format, or platform compatibility: none changed; Wakaru release line and admission unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: limitations only; digests, byte ranges, provenance, and manifest identity unchanged. Default behavior when nothing is detected is byte-identical (no limitation appended).
  • Target selection, authority, upstream behavior, and cleanup details: not applicable (no target interaction).
  • Generated metadata: docs/public/product-catalog.json is CI-generated and ignored; checks pass on this branch.

Expected effects:

  • Read existing files or captured data.

Evidence and regression coverage

  • Tests added or updated:
    • Unit (RecoveryObfuscatorDetection.test.ts, 7 cases): fixture identity (sha256 + byte count recorded in obfuscator-signature-expectations.json) and exact occurrence facts; negatives for ordinary minified bundle code, sparse _0x names without decoder calls, and decoder calls below the identifier threshold; threshold co-occurrence; multi-byte UTF-8; limitation text content (counts, pipeline names, re-ingestion, no-install statement).
    • Boundary (wakaruIntegration.test.ts): end-to-end recovery of the committed obfuscated fixture asserts the exact appended limitation string (92 _0x-hex identifier and 26 string-array decoder-call occurrences) alongside RECOVERY_LIMITATIONS, with status complete and cleanup intact.
  • Base reproduction: with Wakaru 1.14.0 and --unpack=strict, the same esbuild-minified bundle yields two modules plain and one module when obfuscated with javascript-obfuscator 4.2.2 defaults (re-verified during preparation of this PR; the detection patterns scored 0/0 on the plain bundle and 144/34 on the obfuscated one).
  • User-visible CLI/MCP output: the new limitations entry, asserted verbatim in the boundary test.
  • Remaining proof gaps: the remediation commands are stated, not executed by this adapter; humanify renaming is not verified by this adapter and is marked as such in the docs.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable. (Counts are observed; the remediation pipeline is a documented suggestion, not an executed claim.)
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. (Mangled-identifier configurations and humanify are explicitly not covered.)

Validation performed

  • npx vitest run src/javascript/recovery/RecoveryObfuscatorDetection.test.ts tests/boundary/javascript-recovery/wakaruIntegration.test.ts — 47 passed (7 + 40).
  • npx tsc --noEmit -p . — clean.
  • npx oxlint <changed files> — 0 warnings, 0 errors; npm run verify:module-boundaries — clean.
  • npx oxfmt --check <changed files> — clean (the obfuscated fixture was formatted and its recorded digest/counts recomputed so the repo-wide format check holds).
  • npm run verify:test-discovery — 2945 test files discovered exactly once.
  • npm run metadata:check, npm run mcp-registry:check, npm run mcp-catalog:check — pass.
  • npx knip --cache — one pre-existing unused exported type (DarwinHelperCache) on this base revision, untouched by this PR.
  • CLI syntax of the documented remediation commands checked against installed webcrack@2.16.0 and obfuscator-io-deobfuscator@1.0.6 help output.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. When no signature matches, output is identical to the previous release.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable (static file reading only; the operation never executes selected or recovered code).
  • Package or release metadata impact: tool description text only; catalog checks pass.
  • Security, privacy, process, or containment review: detection reads only the validated in-memory snapshot; no new subprocess, network, or filesystem access. The docs restate that the operation never executes selected or recovered code and that REA does not install or run the external tools.

Author readiness

Human owner:

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

(AI-assisted contribution: these declarations are left to the human owner per CONTRIBUTING.md, so the PR opens as a draft.)

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above. (No existing issue for this gap; the problem statement above serves as the record.)
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.
…ecovery limitations

recover_javascript_sources now fingerprints each selected input for
javascript-obfuscator-family signatures (_0x-hex identifiers co-occurring
with string-array decoder calls) before publication and reports one
explicit limitation per detected input, naming a working external
remediation pipeline (webcrack, obfuscator-io-deobfuscator, humanify) and
re-ingestion. Detection is signature-based and intentionally conservative
(minimum co-occurrence thresholds); mangled identifier configurations are
not covered.

Adds a 2.8 KB CI fixture generated with javascript-obfuscator 4.2.2
defaults plus a recorded expectation snapshot (sha256, byte count,
occurrence counts) and unit/boundary coverage for detection, thresholds,
UTF-8 handling and published limitation text.
@greptile-apps

greptile-apps Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5 Tier: apex

[Medium impact] The change reaches callers of JavaScript source recovery.

Address the avoidable match arrays and wording-bound tests before merging to meet the repository requirements; also clarify the external-tool example.

Findings

  1. P2 Match counting wastes memory ▶
  2. P2 Deobfuscation steps do not connect ▶
  3. P2 Harmless wording changes fail tests ▶
Fix with agent prompt
### Issue 1
src/javascript/recovery/RecoveryObfuscatorDetection.ts:33-34
`detectRecoveryObfuscatorFacts` builds a full array of matches for each pattern, only to read `.length`. A selected file can be 64 MiB, so signature-heavy inputs can create millions of temporary strings during publication. Wakaru's memory limit does not cover this work in the main server.

Count matches incrementally instead. This must be addressed before merging to meet `AGENTS.md`'s requirement to avoid unnecessary work and copies.

### Issue 2
docs/javascript-recovery.md:149-150
Both commands read `inputs/bundle.js`, so the second command starts again from the original file instead of using `webcrack`'s output. Users who follow the example do not get both tools' changes in one result.

Either pass the first result into the next step, or label these as alternatives and give them separate output paths.

### Issue 3
tests/boundary/javascript-recovery/wakaruIntegration.test.ts:84-86
The new boundary test copies explanatory wording instead of comparing the returned entry with `recoveryObfuscatorLimitation`. A harmless rewording now breaks the test.

`docs/testing.md` requires guidance checks to use the owning formatter or constant, with separate checks for behavior and facts. Use that formatter here while retaining independent expectations for the input path and the `92`/`26` counts. This requirement must be satisfied before merging.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds per-input javascript-obfuscator signature counts to recovery limitations, with external recovery guidance and fixture coverage.

  • JavaScript recovery reports signature matches for obfuscated inputs.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Validated input snapshot] --> B[Check SHA-256]
  B --> C[Count signatures]
  C --> D{Both thresholds met?}
  D -->|Yes| E[Append input-specific limitation]
  D -->|No| F[Keep existing limitations]
  B --> G[Publish exact input copy]
  E --> H[Write manifest and return result]
  F --> H
  G --> H
Loading

Reviews (1) · Last reviewed commit: "feat(javascript): report javascript-obfu..." · Reviewed by Greptile

Comment on lines +33 to +34
const identifiers = (text.match(IDENTIFIER_PATTERN) ?? []).length;
const decoderCalls = (text.match(DECODER_PATTERN) ?? []).length;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Match counting wastes memory

detectRecoveryObfuscatorFacts builds a full array of matches for each pattern, only to read .length. A selected file can be 64 MiB, so signature-heavy inputs can create millions of temporary strings during publication. Wakaru's memory limit does not cover this work in the main server.

Count matches incrementally instead. This must be addressed before merging to meet AGENTS.md's requirement to avoid unnecessary work and copies.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/javascript/recovery/RecoveryObfuscatorDetection.ts
Line: 33-34

Comment:
**Match counting wastes memory**

`detectRecoveryObfuscatorFacts` builds a full array of matches for each pattern, only to read `.length`. A selected file can be 64 MiB, so signature-heavy inputs can create millions of temporary strings during publication. Wakaru's memory limit does not cover this work in the main server.

Count matches incrementally instead. This must be addressed before merging to meet `AGENTS.md`'s requirement to avoid unnecessary work and copies.

**Context Used:** AGENTS.md ([source](https://github.com/morluto/rea/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Comment on lines +149 to +150
npx webcrack@2 inputs/bundle.js -o /tmp/deobfuscated
npx obfuscator-io-deobfuscator@1 inputs/bundle.js -o /tmp/deobfuscated/bundle.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Deobfuscation steps do not connect

Both commands read inputs/bundle.js, so the second command starts again from the original file instead of using webcrack's output. Users who follow the example do not get both tools' changes in one result.

Either pass the first result into the next step, or label these as alternatives and give them separate output paths.

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/javascript-recovery.md
Line: 149-150

Comment:
**Deobfuscation steps do not connect**

Both commands read `inputs/bundle.js`, so the second command starts again from the original file instead of using `webcrack`'s output. Users who follow the example do not get both tools' changes in one result.

Either pass the first result into the next step, or label these as alternatives and give them separate output paths.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Comment on lines +84 to +86
expect.stringContaining(
"bundle.js matches javascript-obfuscator-family signatures (92 _0x-hex identifier and 26 string-array decoder-call occurrences)",
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Harmless wording changes fail tests

The new boundary test copies explanatory wording instead of comparing the returned entry with recoveryObfuscatorLimitation. A harmless rewording now breaks the test.

docs/testing.md requires guidance checks to use the owning formatter or constant, with separate checks for behavior and facts. Use that formatter here while retaining independent expectations for the input path and the 92/26 counts. This requirement must be satisfied before merging.

Context Used: Documentation (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/boundary/javascript-recovery/wakaruIntegration.test.ts
Line: 84-86

Comment:
**Harmless wording changes fail tests**

The new boundary test copies explanatory wording instead of comparing the returned entry with `recoveryObfuscatorLimitation`. A harmless rewording now breaks the test.

`docs/testing.md` requires guidance checks to use the owning formatter or constant, with separate checks for behavior and facts. Use that formatter here while retaining independent expectations for the input path and the `92`/`26` counts. This requirement must be satisfied before merging.

**Context Used:** Documentation ([source](https://github.com/morluto/rea/blob/main/docs))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant