dsh-js-reverse-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/cordis.patch.yml +14 -0
  2. package/index.js +61 -0
  3. package/package.json +20 -0
  4. package/skills/iv8-env-runtime/SKILL.md +159 -0
  5. package/skills/iv8-env-runtime/assets/entry-report-template.txt +23 -0
  6. package/skills/iv8-env-runtime/assets/env_starter.py +100 -0
  7. package/skills/iv8-env-runtime/references/case-function-iwencai.md +96 -0
  8. package/skills/iv8-env-runtime/references/case-intercept-douyin.md +133 -0
  9. package/skills/iv8-env-runtime/references/config-defaults-cheatsheet.md +75 -0
  10. package/skills/iv8-env-runtime/references/cross-platform-install.md +108 -0
  11. package/skills/iv8-env-runtime/references/iv8-api-contract.md +97 -0
  12. package/skills/iv8-env-runtime/references/network-and-eventloop.md +113 -0
  13. package/skills/iv8-env-runtime/references/part1-entry-location.md +138 -0
  14. package/skills/iv8-env-runtime/references/part1-mcp-tool-map.md +77 -0
  15. package/skills/iv8-env-runtime/references/part2-discovery.md +80 -0
  16. package/skills/iv8-env-runtime/references/patching-principles.md +91 -0
  17. package/skills/iv8-env-runtime/references/trap-blacklist.md +60 -0
  18. package/skills/iv8-env-runtime/references/version-gotchas.md +128 -0
  19. package/skills/iv8-env-runtime/scripts/consistency_lint.py +211 -0
  20. package/skills/iv8-env-runtime/scripts/defaults_audit.py +143 -0
  21. package/skills/iv8-env-runtime/scripts/monitor_env.py +185 -0
  22. package/skills/iv8-env-runtime/scripts/network_bridge.py +147 -0
  23. package/skills/iv8-env-runtime/scripts/parity_check.py +175 -0
  24. package/skills/iv8-env-runtime/scripts/preflight.py +144 -0
  25. package/skills/iv8-env-runtime/scripts/reproduce_stub_error.py +165 -0
  26. package/skills/iv8-env-runtime/scripts/run_target.py +150 -0
  27. package/skills/iv8-jsvmp-recovery/SKILL.md +129 -0
  28. package/skills/iv8-jsvmp-recovery/agents/openai.yaml +4 -0
  29. package/skills/iv8-jsvmp-recovery/references/case-study-douyin-a-bogus.md +320 -0
  30. package/skills/iv8-jsvmp-recovery/references/controlled-oracle-and-equivalence.md +247 -0
  31. package/skills/iv8-jsvmp-recovery/references/static-trace-evidence.md +289 -0
  32. package/skills/iv8-jsvmp-recovery/references/vmp-domain-model.md +212 -0
  33. package/skills/iv8-jsvmp-recovery/scripts/verify-equivalence.py +281 -0
  34. package/skills/js-ast-deobfuscation/SKILL.md +140 -0
  35. package/skills/js-ast-deobfuscation/agents/openai.yaml +4 -0
  36. package/skills/js-ast-deobfuscation/references/anti-patterns.md +325 -0
  37. package/skills/js-ast-deobfuscation/references/first-pass.md +255 -0
  38. package/skills/js-ast-deobfuscation/references/interaction-and-stopping.md +758 -0
  39. package/skills/js-ast-deobfuscation/references/iv8-runtime-observation.md +773 -0
  40. package/skills/js-ast-deobfuscation/references/jsvmp-identification.md +328 -0
  41. package/skills/js-ast-deobfuscation/references/methods-and-evidence.md +460 -0
  42. package/skills/js-ast-deobfuscation/references/patterns-and-priority.md +516 -0
  43. package/skills/js-ast-deobfuscation/references/validation-contract.md +604 -0
  44. package/skills/js-ast-deobfuscation/scripts/prepare-source.mjs +921 -0
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env python3
2
+ """Verify exact output equality without normalizing either output.
3
+
4
+ This script proves one mechanical fact only: whether the supplied oracle and
5
+ recovered output files contain identical bytes. It records hashes for the raw
6
+ input and source artifacts so the result remains tied to a declared baseline.
7
+ It does not prove that the input is truly raw, that the implementation is
8
+ independent, or that one matching sample generalizes to other inputs.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import hashlib
15
+ import json
16
+ import os
17
+ import sys
18
+ from dataclasses import dataclass
19
+ from pathlib import Path
20
+ from typing import Any, Sequence
21
+
22
+
23
+ SCHEMA_VERSION = 1
24
+ CONTEXT_BYTES = 16
25
+
26
+
27
+ class VerificationError(Exception):
28
+ """Raised for invalid files or unsafe report destinations."""
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class Artifact:
33
+ requested_path: str
34
+ path: Path
35
+ data: bytes
36
+
37
+ @property
38
+ def sha256(self) -> str:
39
+ return hashlib.sha256(self.data).hexdigest()
40
+
41
+ def as_report(self) -> dict[str, Any]:
42
+ return {
43
+ "requested_path": self.requested_path,
44
+ "path": str(self.path),
45
+ "size": len(self.data),
46
+ "sha256": self.sha256,
47
+ }
48
+
49
+
50
+ def absolute_path(path_text: str) -> Path:
51
+ return Path(os.path.abspath(Path(path_text).expanduser()))
52
+
53
+
54
+ def parse_args(argv: Sequence[str]) -> argparse.Namespace:
55
+ parser = argparse.ArgumentParser(
56
+ description=(
57
+ "Compare oracle and recovered outputs byte-for-byte while hashing "
58
+ "the declared raw-input and source artifacts."
59
+ ),
60
+ epilog=(
61
+ "Exit status: 0 exact match; 1 output mismatch; 2 invalid input or "
62
+ "report error. No whitespace, Unicode, Base64, JSON, or newline "
63
+ "normalization is performed."
64
+ ),
65
+ )
66
+ parser.add_argument(
67
+ "--input",
68
+ action="append",
69
+ required=True,
70
+ metavar="FILE",
71
+ help=(
72
+ "Raw-input artifact to hash. Repeat for split request/environment/"
73
+ "behavior/time/config artifacts."
74
+ ),
75
+ )
76
+ parser.add_argument(
77
+ "--source",
78
+ action="append",
79
+ default=[],
80
+ metavar="FILE",
81
+ help="Original source, bytecode, or runtime manifest to hash; repeatable.",
82
+ )
83
+ parser.add_argument(
84
+ "--oracle",
85
+ required=True,
86
+ metavar="FILE",
87
+ help="Output captured from the controlled original program.",
88
+ )
89
+ parser.add_argument(
90
+ "--recovered",
91
+ required=True,
92
+ metavar="FILE",
93
+ help="Output produced by the independent recovered implementation.",
94
+ )
95
+ parser.add_argument(
96
+ "--label",
97
+ default="baseline",
98
+ help="Human-readable experiment label stored in the report.",
99
+ )
100
+ parser.add_argument(
101
+ "--text-encoding",
102
+ default="utf-8",
103
+ help=(
104
+ "Encoding used only for diagnostic previews near the first "
105
+ "difference. It never changes comparison bytes. Default: utf-8."
106
+ ),
107
+ )
108
+ parser.add_argument(
109
+ "--report",
110
+ metavar="FILE",
111
+ help="Write JSON to this path instead of stdout.",
112
+ )
113
+ parser.add_argument(
114
+ "--force",
115
+ action="store_true",
116
+ help="Allow replacing an explicitly named existing report file.",
117
+ )
118
+ return parser.parse_args(argv)
119
+
120
+
121
+ def read_artifact(requested_path: str, role: str) -> Artifact:
122
+ path = absolute_path(requested_path)
123
+ try:
124
+ metadata = path.lstat()
125
+ except FileNotFoundError as error:
126
+ raise VerificationError(f"{role} does not exist: {path}") from error
127
+
128
+ if path.is_symlink():
129
+ raise VerificationError(f"{role} must not be a symbolic link: {path}")
130
+ if not path.is_file():
131
+ raise VerificationError(f"{role} is not a regular file: {path}")
132
+ if not metadata.st_size == path.stat().st_size:
133
+ raise VerificationError(f"{role} changed while being inspected: {path}")
134
+
135
+ try:
136
+ data = path.read_bytes()
137
+ except OSError as error:
138
+ raise VerificationError(f"cannot read {role}: {path}: {error}") from error
139
+
140
+ return Artifact(requested_path=requested_path, path=path, data=data)
141
+
142
+
143
+ def decode_preview(data: bytes, encoding: str) -> str:
144
+ try:
145
+ return data.decode(encoding, errors="replace")
146
+ except LookupError as error:
147
+ raise VerificationError(f"unknown text encoding: {encoding}") from error
148
+
149
+
150
+ def first_difference(
151
+ oracle: bytes,
152
+ recovered: bytes,
153
+ encoding: str,
154
+ ) -> dict[str, Any] | None:
155
+ common_length = min(len(oracle), len(recovered))
156
+ offset = next(
157
+ (index for index in range(common_length) if oracle[index] != recovered[index]),
158
+ None,
159
+ )
160
+
161
+ if offset is None:
162
+ if len(oracle) == len(recovered):
163
+ return None
164
+ offset = common_length
165
+
166
+ start = max(0, offset - CONTEXT_BYTES)
167
+ stop = min(max(len(oracle), len(recovered)), offset + CONTEXT_BYTES + 1)
168
+ oracle_context = oracle[start:min(stop, len(oracle))]
169
+ recovered_context = recovered[start:min(stop, len(recovered))]
170
+
171
+ return {
172
+ "offset": offset,
173
+ "oracle_byte": oracle[offset] if offset < len(oracle) else None,
174
+ "recovered_byte": recovered[offset] if offset < len(recovered) else None,
175
+ "context_start": start,
176
+ "context_stop": stop,
177
+ "oracle_context_hex": oracle_context.hex(),
178
+ "recovered_context_hex": recovered_context.hex(),
179
+ "oracle_context_text": decode_preview(oracle_context, encoding),
180
+ "recovered_context_text": decode_preview(recovered_context, encoding),
181
+ }
182
+
183
+
184
+ def build_report(args: argparse.Namespace) -> dict[str, Any]:
185
+ inputs = [
186
+ read_artifact(path, f"raw input #{index}")
187
+ for index, path in enumerate(args.input, start=1)
188
+ ]
189
+ sources = [
190
+ read_artifact(path, f"source artifact #{index}")
191
+ for index, path in enumerate(args.source, start=1)
192
+ ]
193
+ oracle = read_artifact(args.oracle, "oracle output")
194
+ recovered = read_artifact(args.recovered, "recovered output")
195
+ difference = first_difference(oracle.data, recovered.data, args.text_encoding)
196
+ bundle_digest = hashlib.sha256()
197
+ for artifact in inputs:
198
+ bundle_digest.update(len(artifact.data).to_bytes(8, byteorder="big"))
199
+ bundle_digest.update(artifact.data)
200
+
201
+ return {
202
+ "schema_version": SCHEMA_VERSION,
203
+ "label": args.label,
204
+ "comparison": "exact-bytes",
205
+ "normalization": "none",
206
+ "raw_inputs": [artifact.as_report() for artifact in inputs],
207
+ "raw_input_bundle_sha256": bundle_digest.hexdigest(),
208
+ "source_artifacts": [artifact.as_report() for artifact in sources],
209
+ "oracle": oracle.as_report(),
210
+ "recovered": recovered.as_report(),
211
+ "exact": difference is None,
212
+ "first_difference": difference,
213
+ "claim_boundary": (
214
+ "This report proves exact equality only for the supplied artifacts. "
215
+ "It does not prove raw-input integrity, implementation independence, "
216
+ "or equivalence outside the declared experiment."
217
+ ),
218
+ }
219
+
220
+
221
+ def serialize_report(report: dict[str, Any]) -> str:
222
+ return json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
223
+
224
+
225
+ def write_report(
226
+ path_text: str,
227
+ content: str,
228
+ force: bool,
229
+ protected_paths: set[Path],
230
+ ) -> None:
231
+ path = absolute_path(path_text)
232
+ if path in protected_paths:
233
+ raise VerificationError(f"report path would overwrite an input artifact: {path}")
234
+ if path.exists():
235
+ if path.is_symlink():
236
+ raise VerificationError(f"report path must not be a symbolic link: {path}")
237
+ if not path.is_file():
238
+ raise VerificationError(f"report path is not a regular file: {path}")
239
+ if not force:
240
+ raise VerificationError(
241
+ f"report already exists: {path}; pass --force to replace it"
242
+ )
243
+ if not path.parent.is_dir():
244
+ raise VerificationError(f"report parent directory does not exist: {path.parent}")
245
+
246
+ flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
247
+ if not force:
248
+ flags |= os.O_EXCL
249
+ try:
250
+ descriptor = os.open(path, flags, 0o644)
251
+ with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as handle:
252
+ handle.write(content)
253
+ except FileExistsError as error:
254
+ raise VerificationError(
255
+ f"report already exists: {path}; pass --force to replace it"
256
+ ) from error
257
+ except OSError as error:
258
+ raise VerificationError(f"cannot write report: {path}: {error}") from error
259
+
260
+
261
+ def main(argv: Sequence[str] | None = None) -> int:
262
+ args = parse_args(sys.argv[1:] if argv is None else argv)
263
+ try:
264
+ report = build_report(args)
265
+ content = serialize_report(report)
266
+ if args.report:
267
+ protected_paths = {
268
+ absolute_path(path)
269
+ for path in [*args.input, *args.source, args.oracle, args.recovered]
270
+ }
271
+ write_report(args.report, content, args.force, protected_paths)
272
+ else:
273
+ sys.stdout.write(content)
274
+ return 0 if report["exact"] else 1
275
+ except VerificationError as error:
276
+ print(f"verify-equivalence: {error}", file=sys.stderr)
277
+ return 2
278
+
279
+
280
+ if __name__ == "__main__":
281
+ raise SystemExit(main())
@@ -0,0 +1,140 @@
1
+ ---
2
+ name: js-ast-deobfuscation
3
+ description: >-
4
+ 对混淆 JavaScript 建立证据驱动、用户逐步决策的 AST 解混淆工作流。用于用户要求解混淆、提高可读性、识别混淆结构、恢复字符串或名称、分析打包器与动态载荷、为算法分析或 JSVMP/VM 分析准备宿主边界时;先保护原始输入并格式化派生副本,直接阅读源码和建立画像,再为每个候选选择静态、受控局部执行、动态、混合、阻塞或交接路线。可使用 iv8 等运行能力取得最小动态证据,但不要求先补环境,也不反编译或插桩 VM 内部。
5
+ ---
6
+
7
+ # JS AST Deobfuscation
8
+
9
+ ## 目的
10
+
11
+ 生成方便阅读、搜索、定位、动态观测和后续算法分析的派生视图。解混淆服务于分析,不以“清理干净”、恢复作者原名或生成可独立运行的源码为目标。
12
+
13
+ 分析视图可以不可执行,只要明确产物类型、保留原始运行目标,并且不妨碍当前与下一阶段分析。
14
+
15
+ ## 不可跳过的边界
16
+
17
+ 1. 固定原始输入和 SHA-256;不修改原始文件。
18
+ 2. 使用本地 Node Prettier 格式化派生副本,再从连续源码片段直接阅读完整文件。AST、搜索和统计只用于复核,不代替阅读。
19
+ 3. 从真实源码建立画像,区分观察事实、推断和未知;不根据文件名、课程主题或旧案例预判。
20
+ 4. 区分真正混淆、压缩/转译、打包器、polyfill、第三方代码、环境检测、动态载荷与 VM 外围。
21
+ 5. 检查字符串隐藏。真实字符串隐藏确实阻碍主要消费者时,把字符串恢复作为第一个语义推荐;普通字面量、第三方数据、压缩资源和 VM 字节数据不因此改写。
22
+ 6. 首轮画像完成后再询问用户分析方向。一次批准默认只覆盖一个语义步骤。
23
+ 7. 不执行未经授权的完整未知代码,不把 `node:vm` 当安全沙箱;动态执行、真实网络、会话、Hook、插桩、删除和高风险控制流必须单独说明并取得批准。
24
+ 8. 不在本 Skill 中反编译、解释或插桩 VM 的 Opcode、PC、值栈、帧栈和虚拟控制流。
25
+
26
+ 不使用固定 transform 流水线。字符串门之后的顺序由最新源码、用户方向、依赖、证据、收益和风险决定。
27
+
28
+ ## 准备与首轮画像
29
+
30
+ 1. 确定唯一输入文件;目录中有多个合理候选且无法从入口关系判断时再询问。
31
+ 2. 执行 `scripts/prepare-source.mjs --help`,再用它创建隔离工作区、原始副本、格式化视图和准备报告。直接执行脚本,不需要先把脚本读入上下文。
32
+ 3. 脚本找不到本地 Prettier 时,在案例工作区的隔离 `tooling/` 中安装并锁定版本;不修改项目原有 `package.json`。联网或权限策略要求批准时先申请,再把本地可执行文件路径传给脚本。
33
+ 4. 先不读取任何 reference,按连续区间阅读完整的 `steps/00_formatted.js`,只记录已读范围、真实片段和原始锚点,不先套模式清单。文件很大时跨轮继续,不能用抽样或脚本摘要代替未读区间。
34
+ 5. 读到 EOF 后才读取 [first-pass.md](references/first-pass.md),用它复核阅读覆盖并建立画像。在第一次用户决策前不读取其他 reference;只有首轮无法继续的异常情况可以提前读取对应文件,并说明原因。
35
+ 6. 生成 `ANALYSIS.md` 和首轮决策卡,状态进入 `READY_FOR_USER_DECISION`。
36
+
37
+ 准备失败时保留原始输入和错误证据,进入 `FORMAT_BLOCKED`;不要通过删除语句、补括号或静默更换 formatter 绕过失败。
38
+
39
+ ## 核心循环
40
+
41
+ 每次只处理一个明确问题:
42
+
43
+ ```text
44
+ 读取最新分析视图
45
+ → 更新候选问题与依赖
46
+ → 判断 static / local-exec / dynamic / hybrid / blocked / handoff
47
+ → 比较收益、风险和证据缺口
48
+ → 推荐一个步骤并等待用户决定
49
+ → 写出范围、前提、验证和停止条件
50
+ → 执行一个 transform 或最小取证实验
51
+ → 验证并生成新的派生视图
52
+ → 重新直接阅读受影响区域和消费者
53
+ → 重新画像、排序、汇报
54
+ ```
55
+
56
+ 不要在一步中混合字符串替换、代理内联、死代码删除、控制流重写和语义命名。样本专用 transform 必须可重放、有前提、有失败条件;按 binding 和作用域改写,不按名称文本全局替换。
57
+
58
+ ## 静态与动态分流
59
+
60
+ - `static`:AST、作用域、引用、常量、def-use 和调用关系足以证明,直接静态处理。
61
+ - `local-exec`:decoder 闭合、输入可冻结、无宿主依赖,只执行最小闭合切片,不运行完整目标。
62
+ - `dynamic`:必须运行才能取得当前事实,且不需要 AST 写回才成立。
63
+ - `hybrid`:静态定位,动态取证,再把已经证明的结果映射回 AST。
64
+ - `blocked`:静态不足,也没有可信、已授权或可复现的执行条件;只阻塞当前候选。
65
+ - `handoff`:需要系统补环境、真实浏览器、纯算法阶段或 VM 内部能力。
66
+
67
+ 动态不等于补环境。环境已经存在不是选择动态的理由;环境未完成也不阻塞证据已经充分的静态步骤。
68
+
69
+ 已批准的静态步骤发现必须动态时立即停止,说明静态证明到哪里、只缺哪个运行事实、最小实验及风险,等待新的批准。不得把静态授权扩大为完整执行、联网、Hook 或插桩。
70
+
71
+ 动态观测选择能回答问题的最低侵入层:
72
+
73
+ ```text
74
+ L0 运行时旁路日志
75
+ L1 外部调试器、watch、scope 和表达式求值
76
+ L2 独立 harness / trigger
77
+ L3 独立 probe 或 Hook,保存无 probe / 有 probe A/B
78
+ L4 派生插桩副本,降低证据等级并保留位置映射
79
+ ```
80
+
81
+ 不在原始 JavaScript 中插入 `console.log`、`debugger` 或日志代码。任何 debug、wrapper、Hook 和环境补丁都记录为运行条件。
82
+
83
+ ## 用户决策
84
+
85
+ 默认采用 `interactive`:
86
+
87
+ - 自动完成准备、完整阅读、首轮画像、字符串门和候选排序。
88
+ - 用结果语言说明候选,例如“恢复隐藏文本和属性名”或“建立 VM 宿主边界”,不让用户选择 AST 术语或工具。
89
+ - 提供推荐项、真正不同的替代项和暂停/交接,共 2~3 个互斥选项。
90
+ - 用户回复“继续”“按推荐做”或“你来判断”,只批准当前一个低风险推荐步骤。
91
+ - 用户不回应时停止,不自行执行后续语义步骤。
92
+
93
+ 只有用户明确给出范围才进入批量模式;遇到动态升级、删除、高风险控制流、语义命名、验证失败或阶段切换时退出批次。
94
+
95
+ ## 验证、产物与停止
96
+
97
+ 每一步至少记录输入与输出 hash、匹配/修改/跳过/残留数量、parse 结果、binding/reference/shadow/alias 检查、确定性重跑和原始到派生映射。动态步骤再记录环境、输入、补丁、观测层级、日志、覆盖范围和 A/B。
98
+
99
+ 声明产物类型:
100
+
101
+ - `analysis-js`:默认分析视图,可以不可执行。
102
+ - `annotated-view`:带证据注释的阅读视图。
103
+ - `executable-js`:只有明确要求并完成行为验收时使用。
104
+ - `pseudocode`:必须明确不是 JavaScript。
105
+
106
+ 主要隐藏层已经处理、阻塞或交接,没有未经评审的高收益 AST 候选,而且最新视图足以支持当前或下一阶段时,提出关闭。第三方短名、分析视图不可执行或 VM 未还原都不是拒绝停止的理由。
107
+
108
+ 整体只有在用户确认后进入 `COMPLETE`;否则使用:
109
+
110
+ ```text
111
+ FORMAT_BLOCKED
112
+ READY_FOR_USER_DECISION
113
+ STEP_STOPPED
114
+ BLOCKED_CURRENT
115
+ READY_FOR_HANDOFF
116
+ READY_FOR_USER_CLOSURE
117
+ ```
118
+
119
+ ## VM 边界
120
+
121
+ 允许整理 VM 加载器、解释器外围和宿主 JavaScript,恢复外层字符串与确定性载荷,标记入口、数据区、宿主 binding、slot 和公开 API,并建立原始位置、派生位置和下一阶段问题清单。
122
+
123
+ 大数组、数字 ID、`while` 或 dispatcher 单独出现都不足以确认 JSVMP。只有“程序作为数据 → 重复取指分派 → 持续虚拟状态 → 抽象指令语义”在同一数据流闭环中成立,才确认解释器结构;再连到目标公开或业务路径,才确认目标业务使用 JSVMP。
124
+
125
+ 使用 `entry_12`、`slot_7`、`opcode_38` 等中性坐标。证据不足时不填入业务语义;需要 Opcode、字节码、虚拟栈或虚拟控制流时交接 VM Skill。
126
+
127
+ ## Reference 路由
128
+
129
+ 只有本文件决定何时读取 reference;reference 不再继续路由其他 reference。
130
+
131
+ - 普通首轮准备、完整阅读、画像、字符串门、排序和第一次决策:只读 [first-pass.md](references/first-pass.md)。
132
+ - 首轮后遇到难以分类的模式、误判风险或需要重新排序:读 [patterns-and-priority.md](references/patterns-and-priority.md)。
133
+ - 首轮后或用户明确询问时,需要判断候选是否真的构成 JSVMP、区分数字模块或确定 AST/VM 边界:读 [jsvmp-identification.md](references/jsvmp-identification.md)。
134
+ - 用户选择候选后,需要设计静态、局部执行、动态或 hybrid 方法:读 [methods-and-evidence.md](references/methods-and-evidence.md)。
135
+ - 执行 transform 或接受动态结论前:读 [validation-contract.md](references/validation-contract.md)。
136
+ - 用户批准动态取证且选择 iv8 后:读 [iv8-runtime-observation.md](references/iv8-runtime-observation.md)。
137
+ - 风险升级、失败、批量模式、停止或交接需要详细模板:读 [interaction-and-stopping.md](references/interaction-and-stopping.md)。
138
+ - 发现流程异常或专门审计设计时:读 [anti-patterns.md](references/anti-patterns.md)。
139
+
140
+ 只读取当前阶段需要的一份 reference。案例只能帮助提出假设,不能覆盖当前源码证据。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "JS AST Deobfuscation"
3
+ short_description: "证据驱动、用户逐步决策的 JavaScript AST 解混淆"
4
+ default_prompt: "Use $js-ast-deobfuscation to profile and deobfuscate this JavaScript one evidence-backed step at a time."