Repository navigation
feat(javascript): report javascript-obfuscator-family signatures in recovery limitations - #8530
Raed2180416 wants to merge 1 commit into
Conversation
…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.
[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
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.SummaryAdds per-input
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
Reviews (1) · Last reviewed commit: "feat(javascript): report javascript-obfu..." · Reviewed by Greptile |
| const identifiers = (text.match(IDENTIFIER_PATTERN) ?? []).length; | ||
| const decoderCalls = (text.match(DECODER_PATTERN) ?? []).length; |
There was a problem hiding this comment.
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.| npx webcrack@2 inputs/bundle.js -o /tmp/deobfuscated | ||
| npx obfuscator-io-deobfuscator@1 inputs/bundle.js -o /tmp/deobfuscated/bundle.js |
There was a problem hiding this 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.
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.| expect.stringContaining( | ||
| "bundle.js matches javascript-obfuscator-family signatures (92 _0x-hex identifier and 26 string-array decoder-call occurrences)", | ||
| ), |
There was a problem hiding this 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)
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!
Summary
recover_javascript_sourcessilently returns poor results for inputs produced byjavascript-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=strictreturns 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
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.tsappends the per-input limitation after the existing limitation entries; publication behavior, digests, and manifest shape are otherwise unchanged.docs/javascript-recovery.mdgains an "Obfuscated inputs" section with the observed verification and the remediation commands.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
WakaruPublication) fingerprints the validated input snapshot before writing; consumers are the returnedlimitationsarray and the generated tool description.recover_javascript_sourcesdescription extended by one sentence;npm run mcp-catalog:checkandmetadata:checkpass.docs/public/product-catalog.jsonis CI-generated and ignored; checks pass on this branch.Expected effects:
Evidence and regression coverage
RecoveryObfuscatorDetection.test.ts, 7 cases): fixture identity (sha256 + byte count recorded inobfuscator-signature-expectations.json) and exact occurrence facts; negatives for ordinary minified bundle code, sparse_0xnames 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).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) alongsideRECOVERY_LIMITATIONS, with statuscompleteand cleanup intact.--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).limitationsentry, asserted verbatim in the boundary test.humanifyrenaming is not verified by this adapter and is marked as such in the docs.For evidence-bearing changes:
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.webcrack@2.16.0andobfuscator-io-deobfuscator@1.0.6help output.Compatibility, safety, and release
Author readiness
Human owner:
(AI-assisted contribution: these declarations are left to the human owner per CONTRIBUTING.md, so the PR opens as a draft.)
Before merge
type(scope): outcome.