@guardbee/mcp-reverse-shield 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +117 -2
- package/TR.md +114 -0
- package/ZH.md +61 -0
- package/dist/__tests__/android.test.d.ts +2 -0
- package/dist/__tests__/android.test.d.ts.map +1 -0
- package/dist/__tests__/android.test.js +114 -0
- package/dist/__tests__/android.test.js.map +1 -0
- package/dist/__tests__/electron.test.d.ts +2 -0
- package/dist/__tests__/electron.test.d.ts.map +1 -0
- package/dist/__tests__/electron.test.js +128 -0
- package/dist/__tests__/electron.test.js.map +1 -0
- package/dist/__tests__/live.test.d.ts +2 -0
- package/dist/__tests__/live.test.d.ts.map +1 -0
- package/dist/__tests__/live.test.js +67 -0
- package/dist/__tests__/live.test.js.map +1 -0
- package/dist/__tests__/native.test.d.ts +2 -0
- package/dist/__tests__/native.test.d.ts.map +1 -0
- package/dist/__tests__/native.test.js +111 -0
- package/dist/__tests__/native.test.js.map +1 -0
- package/dist/__tests__/scanner.test.d.ts +2 -0
- package/dist/__tests__/scanner.test.d.ts.map +1 -0
- package/dist/__tests__/scanner.test.js +127 -0
- package/dist/__tests__/scanner.test.js.map +1 -0
- package/dist/__tests__/testFixtures.d.ts +61 -0
- package/dist/__tests__/testFixtures.d.ts.map +1 -0
- package/dist/__tests__/testFixtures.js +416 -0
- package/dist/__tests__/testFixtures.js.map +1 -0
- package/dist/__tests__/web.test.d.ts +2 -0
- package/dist/__tests__/web.test.d.ts.map +1 -0
- package/dist/__tests__/web.test.js +156 -0
- package/dist/__tests__/web.test.js.map +1 -0
- package/dist/apk.d.ts +9 -0
- package/dist/apk.d.ts.map +1 -0
- package/dist/apk.js +132 -0
- package/dist/apk.js.map +1 -0
- package/dist/asar.d.ts +10 -0
- package/dist/asar.d.ts.map +1 -0
- package/dist/asar.js +73 -0
- package/dist/asar.js.map +1 -0
- package/dist/axml.d.ts +20 -0
- package/dist/axml.d.ts.map +1 -0
- package/dist/axml.js +104 -0
- package/dist/axml.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +172 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +102 -0
- package/dist/config.js.map +1 -0
- package/dist/dex.d.ts +16 -0
- package/dist/dex.d.ts.map +1 -0
- package/dist/dex.js +63 -0
- package/dist/dex.js.map +1 -0
- package/dist/electron.d.ts +26 -0
- package/dist/electron.d.ts.map +1 -0
- package/dist/electron.js +146 -0
- package/dist/electron.js.map +1 -0
- package/dist/finding.d.ts +30 -0
- package/dist/finding.d.ts.map +1 -0
- package/dist/finding.js +38 -0
- package/dist/finding.js.map +1 -0
- package/dist/fsutil.d.ts +20 -0
- package/dist/fsutil.d.ts.map +1 -0
- package/dist/fsutil.js +80 -0
- package/dist/fsutil.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/live.d.ts +14 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +144 -0
- package/dist/live.js.map +1 -0
- package/dist/native.d.ts +18 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +411 -0
- package/dist/native.js.map +1 -0
- package/dist/rules.d.ts +22 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +354 -0
- package/dist/rules.js.map +1 -0
- package/dist/sarif.d.ts +3 -0
- package/dist/sarif.d.ts.map +1 -0
- package/dist/sarif.js +60 -0
- package/dist/sarif.js.map +1 -0
- package/dist/scanner.d.ts +27 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +169 -0
- package/dist/scanner.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +88 -0
- package/dist/server.js.map +1 -0
- package/dist/web.d.ts +56 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +301 -0
- package/dist/web.js.map +1 -0
- package/dist/zip.d.ts +21 -0
- package/dist/zip.d.ts.map +1 -0
- package/dist/zip.js +112 -0
- package/dist/zip.js.map +1 -0
- package/package.json +52 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GuardBee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,118 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @guardbee/mcp-reverse-shield
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**🇬🇧 English** | [🇹🇷 Türkçe](https://github.com/GuardBee/guardbee-mcp/blob/main/packages/reverse-shield/TR.md) | [🇨🇳 中文](https://github.com/GuardBee/guardbee-mcp/blob/main/packages/reverse-shield/ZH.md)
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@guardbee/mcp-reverse-shield)
|
|
6
|
+
[](https://www.npmjs.com/package/@guardbee/mcp-reverse-shield)
|
|
7
|
+
[](https://github.com/GuardBee/guardbee-mcp/blob/main/packages/reverse-shield/LICENSE)
|
|
8
|
+
|
|
9
|
+
An MCP server that tells you **how much your shipped app gives a reverse engineer for free**.
|
|
10
|
+
|
|
11
|
+
Reverse-engineering MCPs now let an agent take an app it has no source for — a web bundle, an Electron app, an APK, a native or .NET binary — and explain how a feature works, down to the binary. This package is the other side of that: point it at *your own* build artifact and it reports what such a tool (or a person with jadx, ILSpy, Hopper or the browser's devtools) would get without any effort, with the fix for each.
|
|
12
|
+
|
|
13
|
+
> Telemetry is **off** by default. With `GUARDBEE_TELEMETRY=1` it sends the server, tool, success, duration and short parameter values such as a file path; longer strings and fields like `content`, `token` or `password` are replaced with a placeholder. See [`@guardbee/mcp-telemetry`](https://github.com/GuardBee/guardbee-mcp/blob/main/packages/telemetry/README.md).
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Claude ──► reverse-shield ──► your build artifact / live site
|
|
17
|
+
│
|
|
18
|
+
├─ web source maps (shipped, inline, publicly reachable), unminified / dev bundles, keys, internal hosts
|
|
19
|
+
├─ electron fuses (RunAsNode, --inspect, NODE_OPTIONS, ASAR integrity), unpacked app code, code inside app.asar
|
|
20
|
+
├─ android debuggable, R8/ProGuard missing, signing scheme, backup, plain React Native bundle, unstripped .so
|
|
21
|
+
├─ native debug info, symbols, get-task-allow, hardened runtime, signing, PIE/NX/RELRO/canary (ELF, Mach-O, PE)
|
|
22
|
+
└─ .NET obfuscation missing, embedded PDB, leaked build paths
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Everything runs locally and offline (except `scan_url`), with its own parsers for ASAR, the Electron fuse wire, binary AndroidManifest (AXML), DEX, ELF, Mach-O (including universal binaries and code signatures) and PE/.NET metadata — no apktool, jadx or binutils needed.
|
|
26
|
+
|
|
27
|
+
## What it flags
|
|
28
|
+
|
|
29
|
+
| Check | Severity | Target | What it gives away |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `sourcemap_embeds_source` | critical | web | Full original source via `sourcesContent` |
|
|
32
|
+
| `sourcemap_shipped` / `sourcemap_inline` / `sourcemap_publicly_reachable` | high | web | Original file paths and structure |
|
|
33
|
+
| `eval_devtool_bundle` | high | web | Dev bundle with each module's path |
|
|
34
|
+
| `bundle_secret` | per key type | web | A working credential in client code |
|
|
35
|
+
| `unminified_bundle` / `dev_build_framework` | medium | web | Readable code / framework dev runtime |
|
|
36
|
+
| `sourcemap_reference` / `internal_endpoint` | low | web | Where the map lives / localhost, private IPs, staging hosts |
|
|
37
|
+
| `electron_fuse_run_as_node` / `electron_fuse_inspect_args` | high | electron | Node runtime or debugger inside the signed app |
|
|
38
|
+
| `electron_app_unpacked` | high | electron | App code as an editable folder |
|
|
39
|
+
| `electron_fuse_node_options` / `_asar_integrity_off` / `_load_outside_asar` | medium | electron | Code injection and ASAR tampering |
|
|
40
|
+
| `apk_debuggable` | critical | android | Debugger attach and `run-as` into app data |
|
|
41
|
+
| `apk_no_obfuscation` / `apk_js_bundle_plaintext` / `apk_unsigned` | high | android | jadx output with real names / readable RN bundle / no tamper evidence |
|
|
42
|
+
| `apk_allow_backup` / `apk_v1_only_signature` | medium | android | Data pulled via backup / Janus-style repackaging |
|
|
43
|
+
| `native_debug_info` / `native_debuggable_entitlement` | high | native | Source-level debug data / lldb attach |
|
|
44
|
+
| `native_symbols_not_stripped` / `native_no_hardened_runtime` / `native_injection_entitlement` / `native_unsigned` | medium | native | Named functions / dylib injection / patched copies run |
|
|
45
|
+
| `native_no_pie` / `native_no_nx` | medium | native | Exploit-friendly fixed addresses / executable stack |
|
|
46
|
+
| `native_no_relro` / `native_no_stack_canary` / `native_adhoc_signed` / `native_build_path_leak` | low | native | Hardening gaps and build-host details |
|
|
47
|
+
| `dotnet_not_obfuscated` / `dotnet_embedded_pdb` | high | .NET | ILSpy/dnSpy output close to the original source |
|
|
48
|
+
|
|
49
|
+
`list_checks` returns the full catalog with fixes. Every result carries an **A–F grade**: F when anything critical (or several high findings) is exposed, A when nothing is.
|
|
50
|
+
|
|
51
|
+
### How the trickier checks decide
|
|
52
|
+
|
|
53
|
+
- **R8/ProGuard**: reads class descriptors from every `classes*.dex` and looks at classes under your application id (and under the packages of components your manifest names, for `.debug`-style ids). Dozens of descriptive class names with almost no one- or two-letter names means R8 did not run.
|
|
54
|
+
- **.NET obfuscation**: reads the metadata `#Strings` heap. Known obfuscator markers (ConfuserEx, Dotfuscator, SmartAssembly, …) pass immediately; otherwise an assembly passes when at least 10% of its identifiers are one- or two-letter names, bare numbers or non-ASCII junk. Checked against an Obfuscar build (21%, passes) and 400 unobfuscated NuGet assemblies (383 flagged; the other 17 are resource-only assemblies with too few names to judge).
|
|
55
|
+
- **Private keys**: only reported when base64 key material follows the PEM header — crypto libraries bundled into apps carry the header as a string constant.
|
|
56
|
+
- **Repeats**: one key in 30 chunks is one finding (“also in 29 other files”), and long lists of unminified files collapse into one.
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"guardbee-reverse-shield": {
|
|
64
|
+
"command": "npx",
|
|
65
|
+
"args": ["-y", "@guardbee/mcp-reverse-shield"]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then ask your agent: *“How easy is it to reverse engineer `./release/MyApp.app`? Fix what you can in the build config.”*
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high
|
|
75
|
+
npx @guardbee/mcp-reverse-shield scan ./app/build/outputs/apk/release/app-release.apk
|
|
76
|
+
npx @guardbee/mcp-reverse-shield scan "/Applications/My App.app" --format=json
|
|
77
|
+
npx @guardbee/mcp-reverse-shield scan https://app.example.com --format=sarif > reverse-shield.sarif
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Tools
|
|
81
|
+
|
|
82
|
+
| Tool | Purpose |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `scan_artifact` | Scan a file or directory: APK, `.app`, Electron folder, `app.asar`, ELF/Mach-O/PE/.NET binary, JS bundle, source map, or a build output folder |
|
|
85
|
+
| `scan_url` | Fetch a live page, check its same-site bundles, follow `sourceMappingURL` and probe `<bundle>.map` (third-party scripts are not fetched) |
|
|
86
|
+
| `list_checks` | The check catalog with what each finding gives away and how to fix it |
|
|
87
|
+
|
|
88
|
+
Directories are walked including `dist/`, `build/` and `out/` (that is the point); `node_modules`, VCS and cache folders are skipped, and a macOS `.app` or Electron folder found inside is scanned as one unit — its fuses, its ASAR and its own executable, not the bundled third-party frameworks.
|
|
89
|
+
|
|
90
|
+
`guardbee.yml`:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
reverse-shield:
|
|
94
|
+
fail-on: high
|
|
95
|
+
max-files: 5000
|
|
96
|
+
exclude:
|
|
97
|
+
- "vendor/"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## CI
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
- run: npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high --format=sarif > reverse-shield.sarif
|
|
104
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
105
|
+
with:
|
|
106
|
+
sarif_file: reverse-shield.sarif
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Limits
|
|
110
|
+
|
|
111
|
+
- Obfuscation is judged from names, not control flow: a build that only renames passes, and string encryption or control-flow flattening is not measured.
|
|
112
|
+
- Web checks look at what is shipped; they cannot tell whether a key is meant to be public (some, like Firebase web keys, are restricted server-side). Rotate or restrict, then add the path to `exclude`.
|
|
113
|
+
- Native hardening checks (PIE/NX/RELRO/canary) are exploit-mitigation signals rather than reverse-engineering ones; they are reported low/medium for that reason.
|
|
114
|
+
- iOS `.ipa` and Android App Bundles (`.aab`) are not unpacked yet; scan the `.app` inside the `.ipa`, or a universal APK built from the bundle.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
package/TR.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# @guardbee/mcp-reverse-shield
|
|
2
|
+
|
|
3
|
+
[🇬🇧 English](README.md) | **🇹🇷 Türkçe** | [🇨🇳 中文](ZH.md)
|
|
4
|
+
|
|
5
|
+
Yayınladığın uygulamanın bir tersine mühendise **neyi bedavaya verdiğini** söyleyen bir MCP server.
|
|
6
|
+
|
|
7
|
+
Tersine mühendislik MCP'leri artık bir agent'ın kaynak kodu olmayan bir uygulamayı (web bundle, Electron uygulaması, APK, native ya da .NET binary) alıp bir özelliğin nasıl çalıştığını binary seviyesine kadar açıklamasına izin veriyor. Bu paket madalyonun öbür yüzü: onu *kendi* build artifact'ına yönelt; böyle bir aracın (ya da jadx, ILSpy, Hopper veya tarayıcı devtools'u kullanan birinin) hiç uğraşmadan neler elde edeceğini, her biri için düzeltmesiyle birlikte raporlar.
|
|
8
|
+
|
|
9
|
+
> Telemetri varsayılan olarak **kapalıdır**. `GUARDBEE_TELEMETRY=1` ile açılırsa sunucu, tool, başarı, süre ve dosya yolu gibi kısa parametre değerleri gönderilir; daha uzun metinler ve `content`, `token`, `password` gibi alanlar yer tutucuyla değiştirilir. Bkz. [`@guardbee/mcp-telemetry`](../telemetry/TR.md).
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Claude ──► reverse-shield ──► build artifact'ın / canlı site
|
|
13
|
+
│
|
|
14
|
+
├─ web source map'ler (paketlenmiş, inline, herkese açık), minify edilmemiş / dev bundle, anahtarlar, iç host'lar
|
|
15
|
+
├─ electron fuse'lar (RunAsNode, --inspect, NODE_OPTIONS, ASAR integrity), açık uygulama kodu, app.asar içindeki kod
|
|
16
|
+
├─ android debuggable, R8/ProGuard yok, imza şeması, backup, düz React Native bundle, strip edilmemiş .so
|
|
17
|
+
├─ native debug bilgisi, semboller, get-task-allow, hardened runtime, imza, PIE/NX/RELRO/canary (ELF, Mach-O, PE)
|
|
18
|
+
└─ .NET obfuscation yok, gömülü PDB, sızan build yolları
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`scan_url` dışında her şey yerelde ve çevrimdışı çalışır. ASAR, Electron fuse wire, ikili AndroidManifest (AXML), DEX, ELF, Mach-O (universal binary'ler ve kod imzası dahil) ve PE/.NET metadata için kendi ayrıştırıcıları var; apktool, jadx ya da binutils gerekmez.
|
|
22
|
+
|
|
23
|
+
## Neyi işaretler
|
|
24
|
+
|
|
25
|
+
| Kontrol | Önem | Hedef | Neyi ele verir |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| `sourcemap_embeds_source` | critical | web | `sourcesContent` ile orijinal kaynak kodun tamamı |
|
|
28
|
+
| `sourcemap_shipped` / `sourcemap_inline` / `sourcemap_publicly_reachable` | high | web | Orijinal dosya yolları ve yapı |
|
|
29
|
+
| `eval_devtool_bundle` | high | web | Her modülün yoluyla birlikte dev bundle |
|
|
30
|
+
| `bundle_secret` | anahtar türüne göre | web | İstemci kodunda çalışan bir kimlik bilgisi |
|
|
31
|
+
| `unminified_bundle` / `dev_build_framework` | medium | web | Okunabilir kod / framework'ün dev runtime'ı |
|
|
32
|
+
| `sourcemap_reference` / `internal_endpoint` | low | web | Map'in yeri / localhost, özel IP'ler, staging host'ları |
|
|
33
|
+
| `electron_fuse_run_as_node` / `electron_fuse_inspect_args` | high | electron | İmzalı uygulamanın içinde Node runtime'ı ya da debugger |
|
|
34
|
+
| `electron_app_unpacked` | high | electron | Düzenlenebilir klasör olarak uygulama kodu |
|
|
35
|
+
| `electron_fuse_node_options` / `_asar_integrity_off` / `_load_outside_asar` | medium | electron | Kod enjeksiyonu ve ASAR kurcalama |
|
|
36
|
+
| `apk_debuggable` | critical | android | Debugger bağlama ve `run-as` ile uygulama verisine erişim |
|
|
37
|
+
| `apk_no_obfuscation` / `apk_js_bundle_plaintext` / `apk_unsigned` | high | android | Gerçek isimlerle jadx çıktısı / okunabilir RN bundle / kurcalama izi yok |
|
|
38
|
+
| `apk_allow_backup` / `apk_v1_only_signature` | medium | android | Backup ile çekilen veri / Janus tarzı yeniden paketleme |
|
|
39
|
+
| `native_debug_info` / `native_debuggable_entitlement` | high | native | Kaynak seviyesinde debug verisi / lldb ile bağlanma |
|
|
40
|
+
| `native_symbols_not_stripped` / `native_no_hardened_runtime` / `native_injection_entitlement` / `native_unsigned` | medium | native | İsimli fonksiyonlar / dylib enjeksiyonu / yamalı kopyaların çalışması |
|
|
41
|
+
| `native_no_pie` / `native_no_nx` | medium | native | Exploit'e elverişli sabit adresler / çalıştırılabilir stack |
|
|
42
|
+
| `native_no_relro` / `native_no_stack_canary` / `native_adhoc_signed` / `native_build_path_leak` | low | native | Sertleştirme boşlukları ve build makinesi ayrıntıları |
|
|
43
|
+
| `dotnet_not_obfuscated` / `dotnet_embedded_pdb` | high | .NET | Orijinal kaynağa çok yakın ILSpy/dnSpy çıktısı |
|
|
44
|
+
|
|
45
|
+
`list_checks` tüm kataloğu düzeltmeleriyle döndürür. Her sonuç bir **A–F notu** taşır: kritik bir şey (ya da birden fazla high bulgu) açıktaysa F, hiçbir şey yoksa A.
|
|
46
|
+
|
|
47
|
+
### Zor kontroller nasıl karar veriyor
|
|
48
|
+
|
|
49
|
+
- **R8/ProGuard**: her `classes*.dex`'ten sınıf tanımlayıcılarını okur ve application id altındaki (`.debug` gibi son ekler için manifest'te adı geçen bileşenlerin paketleri altındaki) sınıflara bakar. Neredeyse hiç bir-iki harfli isim olmadan onlarca açıklayıcı sınıf adı, R8'in çalışmadığı anlamına gelir.
|
|
50
|
+
- **.NET obfuscation**: metadata `#Strings` heap'ini okur. Bilinen obfuscator işaretleri (ConfuserEx, Dotfuscator, SmartAssembly, …) doğrudan geçer; aksi halde tanımlayıcıların en az %10'u bir-iki harfli isim, sadece rakam ya da ASCII dışı çöp karakterse assembly geçer. Bir Obfuscar build'ine (%21, geçer) ve obfuscate edilmemiş 400 NuGet assembly'sine (383'ü işaretlendi; kalan 17'si karar verilemeyecek kadar az isim içeren kaynak assembly'leri) karşı doğrulandı.
|
|
51
|
+
- **Private key'ler**: sadece PEM başlığından sonra base64 anahtar verisi geliyorsa raporlanır; uygulamalara gömülen kripto kütüphaneleri bu başlığı string sabiti olarak taşır.
|
|
52
|
+
- **Tekrarlar**: 30 chunk'taki aynı anahtar tek bulgudur ("also in 29 other files"); uzun minify edilmemiş dosya listeleri tek bulguda toplanır.
|
|
53
|
+
|
|
54
|
+
## Hızlı başlangıç
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"guardbee-reverse-shield": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["-y", "@guardbee/mcp-reverse-shield"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Sonra agent'ına sor: *"`./release/MyApp.app`'i tersine mühendislikle açmak ne kadar kolay? Build ayarlarında düzeltebildiklerini düzelt."*
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high
|
|
71
|
+
npx @guardbee/mcp-reverse-shield scan ./app/build/outputs/apk/release/app-release.apk
|
|
72
|
+
npx @guardbee/mcp-reverse-shield scan "/Applications/My App.app" --format=json
|
|
73
|
+
npx @guardbee/mcp-reverse-shield scan https://app.example.com --format=sarif > reverse-shield.sarif
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Tool'lar
|
|
77
|
+
|
|
78
|
+
| Tool | Amaç |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `scan_artifact` | Bir dosya ya da dizini tarar: APK, `.app`, Electron klasörü, `app.asar`, ELF/Mach-O/PE/.NET binary, JS bundle, source map ya da build çıktı klasörü |
|
|
81
|
+
| `scan_url` | Canlı bir sayfayı çeker, aynı siteye ait bundle'ları kontrol eder, `sourceMappingURL`'i takip eder ve `<bundle>.map`'i dener (üçüncü taraf script'ler çekilmez) |
|
|
82
|
+
| `list_checks` | Her bulgunun neyi ele verdiği ve nasıl düzeltileceğiyle kontrol kataloğu |
|
|
83
|
+
|
|
84
|
+
Dizinler `dist/`, `build/` ve `out/` dahil yürünür (amaç zaten bu); `node_modules`, VCS ve cache klasörleri atlanır. İçeride bulunan bir macOS `.app` ya da Electron klasörü tek birim olarak taranır: fuse'ları, ASAR'ı ve kendi executable'ı; paketlenmiş üçüncü taraf framework'ler değil.
|
|
85
|
+
|
|
86
|
+
`guardbee.yml`:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
reverse-shield:
|
|
90
|
+
fail-on: high
|
|
91
|
+
max-files: 5000
|
|
92
|
+
exclude:
|
|
93
|
+
- "vendor/"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## CI
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
- run: npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high --format=sarif > reverse-shield.sarif
|
|
100
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
101
|
+
with:
|
|
102
|
+
sarif_file: reverse-shield.sarif
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Sınırlar
|
|
106
|
+
|
|
107
|
+
- Obfuscation kontrol akışından değil isimlerden değerlendirilir: sadece yeniden adlandıran bir build geçer; string şifreleme ya da control-flow flattening ölçülmez.
|
|
108
|
+
- Web kontrolleri gönderileni inceler; bir anahtarın herkese açık olmak üzere tasarlanıp tasarlanmadığını bilemez (Firebase web anahtarları gibi bazıları sunucu tarafında kısıtlanır). Döndür ya da kısıtla, sonra yolu `exclude`'a ekle.
|
|
109
|
+
- Native sertleştirme kontrolleri (PIE/NX/RELRO/canary) tersine mühendislikten çok exploit önleme sinyalidir; bu yüzden low/medium raporlanır.
|
|
110
|
+
- iOS `.ipa` ve Android App Bundle (`.aab`) henüz açılmıyor; `.ipa` içindeki `.app`'i ya da bundle'dan üretilmiş universal APK'yı tara.
|
|
111
|
+
|
|
112
|
+
## Lisans
|
|
113
|
+
|
|
114
|
+
MIT
|
package/ZH.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @guardbee/mcp-reverse-shield
|
|
2
|
+
|
|
3
|
+
[🇬🇧 English](README.md) | [🇹🇷 Türkçe](TR.md) | **🇨🇳 中文**
|
|
4
|
+
|
|
5
|
+
一个告诉你**已发布的应用会把什么白白送给逆向工程者**的 MCP 服务器。
|
|
6
|
+
|
|
7
|
+
逆向工程 MCP 如今可以让智能体拿到一个没有源码的应用(Web 打包文件、Electron 应用、APK、原生或 .NET 二进制),一直解释到二进制层面某个功能是如何实现的。本包是它的另一面:把它指向*你自己*的构建产物,它会报告这类工具(或使用 jadx、ILSpy、Hopper、浏览器开发者工具的人)不费力气就能拿到的东西,并给出每一项的修复方法。
|
|
8
|
+
|
|
9
|
+
> 遥测默认**关闭**。设置 `GUARDBEE_TELEMETRY=1` 后会发送服务器、工具、是否成功、耗时以及文件路径等简短参数;较长的字符串以及 `content`、`token`、`password` 等字段会被替换为占位符。参见 [`@guardbee/mcp-telemetry`](../telemetry/README.md)。
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Claude ──► reverse-shield ──► 你的构建产物 / 线上站点
|
|
13
|
+
│
|
|
14
|
+
├─ web source map(随包发布、内联、可公开下载)、未压缩 / 开发版打包、密钥、内部主机
|
|
15
|
+
├─ electron fuse(RunAsNode、--inspect、NODE_OPTIONS、ASAR 完整性)、未打包的应用代码、app.asar 内的代码
|
|
16
|
+
├─ android debuggable、未启用 R8/ProGuard、签名方案、备份、明文 React Native 包、未剥离符号的 .so
|
|
17
|
+
├─ native 调试信息、符号、get-task-allow、hardened runtime、签名、PIE/NX/RELRO/canary(ELF、Mach-O、PE)
|
|
18
|
+
└─ .NET 未混淆、内嵌 PDB、泄露的构建路径
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
除 `scan_url` 外全部在本地离线运行,自带 ASAR、Electron fuse wire、二进制 AndroidManifest(AXML)、DEX、ELF、Mach-O(含通用二进制与代码签名)以及 PE/.NET 元数据解析器,无需 apktool、jadx 或 binutils。
|
|
22
|
+
|
|
23
|
+
## 工具
|
|
24
|
+
|
|
25
|
+
| 工具 | 用途 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `scan_artifact` | 扫描文件或目录:APK、`.app`、Electron 目录、`app.asar`、ELF/Mach-O/PE/.NET 二进制、JS 打包文件、source map 或构建输出目录 |
|
|
28
|
+
| `scan_url` | 抓取线上页面,检查同站点的打包脚本,跟随 `sourceMappingURL` 并尝试 `<bundle>.map`(不抓取第三方脚本) |
|
|
29
|
+
| `list_checks` | 检查项目录,包括每项泄露了什么以及如何修复 |
|
|
30
|
+
|
|
31
|
+
每个结果都带有 **A–F 评级**:存在严重问题(或多个高危问题)时为 F,没有任何问题时为 A。
|
|
32
|
+
|
|
33
|
+
## 快速开始
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"guardbee-reverse-shield": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["-y", "@guardbee/mcp-reverse-shield"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx @guardbee/mcp-reverse-shield scan ./dist --fail-on=high
|
|
48
|
+
npx @guardbee/mcp-reverse-shield scan ./app/build/outputs/apk/release/app-release.apk
|
|
49
|
+
npx @guardbee/mcp-reverse-shield scan https://app.example.com --format=sarif > reverse-shield.sarif
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 限制
|
|
53
|
+
|
|
54
|
+
- 混淆仅根据名称判断,不分析控制流:只做重命名的构建会通过,字符串加密或控制流平坦化不在衡量范围内。
|
|
55
|
+
- Web 检查只看发布出去的内容,无法判断某个密钥是否本就应当公开(例如 Firebase Web 密钥在服务端受限)。
|
|
56
|
+
- 原生加固检查(PIE/NX/RELRO/canary)更多是漏洞利用缓解信号,因此以 low/medium 报告。
|
|
57
|
+
- 暂不解包 iOS `.ipa` 和 Android App Bundle(`.aab`)。
|
|
58
|
+
|
|
59
|
+
## 许可证
|
|
60
|
+
|
|
61
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/android.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
2
|
+
import { tmpdir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { analyzeApk } from "../apk.js";
|
|
6
|
+
import { ATTR, attribute, parseAxml } from "../axml.js";
|
|
7
|
+
import { assessDexObfuscation, readTypeDescriptors } from "../dex.js";
|
|
8
|
+
import { apkSigningBlock, buildAxml, buildDex, buildElf64, buildZip } from "./testFixtures.js";
|
|
9
|
+
const ids = (fs) => fs.map((f) => f.patternId).sort();
|
|
10
|
+
const dirs = [];
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
for (const d of dirs.splice(0))
|
|
13
|
+
rmSync(d, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
function writeApk(buf) {
|
|
16
|
+
const d = mkdtempSync(join(tmpdir(), "rs-apk-"));
|
|
17
|
+
dirs.push(d);
|
|
18
|
+
const p = join(d, "app.apk");
|
|
19
|
+
writeFileSync(p, buf);
|
|
20
|
+
return p;
|
|
21
|
+
}
|
|
22
|
+
const PKG = "com.example.shop";
|
|
23
|
+
const appClasses = (n, short = false) => Array.from({ length: n }, (_, i) => (short ? `L${PKG.replace(/\./g, "/")}/${String.fromCharCode(97 + (i % 26))};` : `L${PKG.replace(/\./g, "/")}/checkout/PaymentScreen${i}ViewModel;`));
|
|
24
|
+
function manifest(app) {
|
|
25
|
+
const attrs = [];
|
|
26
|
+
if (app.debuggable !== undefined)
|
|
27
|
+
attrs.push({ name: "debuggable", resourceId: ATTR.debuggable, bool: app.debuggable });
|
|
28
|
+
if (app.allowBackup !== undefined)
|
|
29
|
+
attrs.push({ name: "allowBackup", resourceId: ATTR.allowBackup, bool: app.allowBackup });
|
|
30
|
+
if (app.testOnly !== undefined)
|
|
31
|
+
attrs.push({ name: "testOnly", resourceId: ATTR.testOnly, bool: app.testOnly });
|
|
32
|
+
return buildAxml([
|
|
33
|
+
{ name: "manifest", attrs: [{ name: "package", string: PKG }] },
|
|
34
|
+
{ name: "application", attrs },
|
|
35
|
+
{ name: "activity", attrs: [{ name: "name", resourceId: 0x01010003, string: `${PKG}.MainActivity` }] },
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
describe("parseAxml", () => {
|
|
39
|
+
it("reads element names, typed booleans and strings, keyed by resource id", () => {
|
|
40
|
+
const elements = parseAxml(manifest({ debuggable: true, allowBackup: false }));
|
|
41
|
+
expect(elements.map((e) => e.name)).toEqual(["manifest", "application", "activity"]);
|
|
42
|
+
const app = elements[1];
|
|
43
|
+
expect(attribute(app, "debuggable")?.value).toBe(true);
|
|
44
|
+
expect(attribute(app, "allowBackup")?.value).toBe(false);
|
|
45
|
+
expect(attribute(app, "testOnly")).toBeUndefined();
|
|
46
|
+
expect(elements[0].attributes[0]).toMatchObject({ name: "package", value: PKG });
|
|
47
|
+
});
|
|
48
|
+
it("returns null for text XML", () => {
|
|
49
|
+
expect(parseAxml(Buffer.from('<?xml version="1.0"?><manifest/>'))).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("dex", () => {
|
|
53
|
+
it("reads type descriptors", () => {
|
|
54
|
+
expect(readTypeDescriptors(buildDex(["I", "Ljava/lang/Object;", "Lcom/example/A;"]))).toEqual(["I", "Ljava/lang/Object;", "Lcom/example/A;"]);
|
|
55
|
+
});
|
|
56
|
+
it("calls readable app classes unobfuscated and renamed ones obfuscated", () => {
|
|
57
|
+
expect(assessDexObfuscation(appClasses(40), PKG)).toMatchObject({ appClasses: 40, shortNamed: 0, obfuscated: false });
|
|
58
|
+
expect(assessDexObfuscation(appClasses(40, true), PKG).obfuscated).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it("does not judge an app whose classes R8 moved out of the package", () => {
|
|
61
|
+
expect(assessDexObfuscation([...appClasses(3), ...Array.from({ length: 200 }, (_, i) => `La${i}/b;`)], PKG).obfuscated).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
it("ignores generated R and BuildConfig classes", () => {
|
|
64
|
+
const generated = ["R", "R$string", "R$id", "BuildConfig"].map((c) => `L${PKG.replace(/\./g, "/")}/${c};`);
|
|
65
|
+
expect(assessDexObfuscation(generated, PKG).appClasses).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("analyzeApk", () => {
|
|
69
|
+
it("flags a debug build: debuggable, unobfuscated, backup allowed, v1-only signature", () => {
|
|
70
|
+
const apk = writeApk(buildZip([
|
|
71
|
+
{ name: "AndroidManifest.xml", data: manifest({ debuggable: true }) },
|
|
72
|
+
{ name: "classes.dex", data: buildDex(appClasses(60)), deflate: true },
|
|
73
|
+
{ name: "META-INF/CERT.RSA", data: Buffer.from("sig") },
|
|
74
|
+
]));
|
|
75
|
+
const result = analyzeApk(apk);
|
|
76
|
+
expect(ids(result.findings)).toEqual(["apk_allow_backup", "apk_debuggable", "apk_no_obfuscation", "apk_v1_only_signature"]);
|
|
77
|
+
expect(result.findings.find((f) => f.patternId === "apk_allow_backup").match).toContain("defaults to true");
|
|
78
|
+
expect(result.findings.find((f) => f.patternId === "apk_no_obfuscation").match).toContain("PaymentScreen0ViewModel");
|
|
79
|
+
});
|
|
80
|
+
it("passes a hardened release build", () => {
|
|
81
|
+
const apk = writeApk(buildZip([
|
|
82
|
+
{ name: "AndroidManifest.xml", data: manifest({ allowBackup: false }) },
|
|
83
|
+
{ name: "classes.dex", data: buildDex([...appClasses(2), ...appClasses(60, true)]) },
|
|
84
|
+
{ name: "META-INF/CERT.RSA", data: Buffer.from("sig") },
|
|
85
|
+
], apkSigningBlock()));
|
|
86
|
+
const result = analyzeApk(apk);
|
|
87
|
+
expect(result.findings).toEqual([]);
|
|
88
|
+
expect(result.notes.join("\n")).toContain("signed with APK Signature Scheme v2+ and v1");
|
|
89
|
+
});
|
|
90
|
+
it("reports an unsigned APK, testOnly, a plain React Native bundle and unstripped native libraries", () => {
|
|
91
|
+
const apk = writeApk(buildZip([
|
|
92
|
+
{ name: "AndroidManifest.xml", data: manifest({ allowBackup: false, testOnly: true }) },
|
|
93
|
+
{ name: "assets/index.android.bundle", data: Buffer.from('var x=1;fetch("http://192.168.1.20:8081/status")') },
|
|
94
|
+
{ name: "lib/arm64-v8a/libcore.so", data: buildElf64({ type: "shared", sections: [{ name: ".symtab", type: 2, size: 240, entsize: 24 }] }) },
|
|
95
|
+
{ name: "lib/armeabi-v7a/libcore.so", data: buildElf64({ type: "shared", sections: [{ name: ".symtab", type: 2, size: 240, entsize: 24 }] }) },
|
|
96
|
+
]));
|
|
97
|
+
const result = analyzeApk(apk);
|
|
98
|
+
expect(ids(result.findings)).toEqual(["apk_js_bundle_plaintext", "apk_test_only", "apk_unsigned", "internal_endpoint", "native_symbols_not_stripped"]);
|
|
99
|
+
});
|
|
100
|
+
it("accepts a Hermes bytecode bundle", () => {
|
|
101
|
+
const hermes = Buffer.concat([Buffer.from([0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f]), Buffer.alloc(64)]);
|
|
102
|
+
const apk = writeApk(buildZip([
|
|
103
|
+
{ name: "AndroidManifest.xml", data: manifest({ allowBackup: false }) },
|
|
104
|
+
{ name: "assets/index.android.bundle", data: hermes },
|
|
105
|
+
], apkSigningBlock()));
|
|
106
|
+
const result = analyzeApk(apk);
|
|
107
|
+
expect(result.findings).toEqual([]);
|
|
108
|
+
expect(result.notes).toContain("React Native bundle is Hermes bytecode");
|
|
109
|
+
});
|
|
110
|
+
it("returns null for a zip without a manifest", () => {
|
|
111
|
+
expect(analyzeApk(writeApk(buildZip([{ name: "readme.txt", data: Buffer.from("hi") }])))).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=android.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android.test.js","sourceRoot":"","sources":["../../src/__tests__/android.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"electron.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/electron.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
2
|
+
import { tmpdir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { readAsar } from "../asar.js";
|
|
6
|
+
import { analyzeElectron, describeFuses, detectElectron, fuseFindings, parseFuseWire, readFuseWire } from "../electron.js";
|
|
7
|
+
import { buildAsar, buildFuseBinary } from "./testFixtures.js";
|
|
8
|
+
const ids = (fs) => fs.map((f) => f.patternId).sort();
|
|
9
|
+
const dirs = [];
|
|
10
|
+
const tmp = () => {
|
|
11
|
+
const d = mkdtempSync(join(tmpdir(), "rs-electron-"));
|
|
12
|
+
dirs.push(d);
|
|
13
|
+
return d;
|
|
14
|
+
};
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
for (const d of dirs.splice(0))
|
|
17
|
+
rmSync(d, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
// Electron's defaults: RunAsNode on, cookie encryption off, NODE_OPTIONS on, inspect on, integrity off, only-asar off
|
|
20
|
+
const DEFAULT_FUSES = "10110000";
|
|
21
|
+
// What a hardened app (e.g. via @electron/fuses) ships
|
|
22
|
+
const HARDENED_FUSES = "01001100";
|
|
23
|
+
function macApp(fuses, asarFiles, appDir) {
|
|
24
|
+
const app = join(tmp(), "Demo.app");
|
|
25
|
+
const fw = join(app, "Contents", "Frameworks", "Electron Framework.framework");
|
|
26
|
+
mkdirSync(fw, { recursive: true });
|
|
27
|
+
writeFileSync(join(fw, "Electron Framework"), buildFuseBinary(fuses));
|
|
28
|
+
const resources = join(app, "Contents", "Resources");
|
|
29
|
+
mkdirSync(resources, { recursive: true });
|
|
30
|
+
if (asarFiles)
|
|
31
|
+
writeFileSync(join(resources, "app.asar"), buildAsar(asarFiles));
|
|
32
|
+
if (appDir) {
|
|
33
|
+
for (const [p, text] of Object.entries(appDir)) {
|
|
34
|
+
const full = join(resources, "app", ...p.split("/"));
|
|
35
|
+
mkdirSync(join(full, ".."), { recursive: true });
|
|
36
|
+
writeFileSync(full, text);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return app;
|
|
40
|
+
}
|
|
41
|
+
describe("fuse wire", () => {
|
|
42
|
+
it("parses version and states after the sentinel", () => {
|
|
43
|
+
const buf = buildFuseBinary(DEFAULT_FUSES, 10);
|
|
44
|
+
expect(parseFuseWire(buf, 10)).toEqual({ version: 1, states: DEFAULT_FUSES });
|
|
45
|
+
});
|
|
46
|
+
it("finds the wire inside a large file", () => {
|
|
47
|
+
const dir = tmp();
|
|
48
|
+
const bin = join(dir, "Electron Framework");
|
|
49
|
+
writeFileSync(bin, buildFuseBinary(HARDENED_FUSES, 17 * 1024 * 1024));
|
|
50
|
+
expect(readFuseWire(bin)?.states).toBe(HARDENED_FUSES);
|
|
51
|
+
});
|
|
52
|
+
it("returns null when there is no fuse wire", () => {
|
|
53
|
+
const dir = tmp();
|
|
54
|
+
writeFileSync(join(dir, "bin"), Buffer.alloc(4096));
|
|
55
|
+
expect(readFuseWire(join(dir, "bin"))).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
it("reports every unsafe default fuse", () => {
|
|
58
|
+
expect(ids(fuseFindings({ version: 1, states: DEFAULT_FUSES }, "bin"))).toEqual([
|
|
59
|
+
"electron_fuse_asar_integrity_off",
|
|
60
|
+
"electron_fuse_inspect_args",
|
|
61
|
+
"electron_fuse_load_outside_asar",
|
|
62
|
+
"electron_fuse_node_options",
|
|
63
|
+
"electron_fuse_run_as_node",
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
it("reports nothing for hardened fuses, and treats removed fuses as safe", () => {
|
|
67
|
+
expect(fuseFindings({ version: 1, states: HARDENED_FUSES }, "bin")).toEqual([]);
|
|
68
|
+
expect(fuseFindings({ version: 1, states: "r0rr11" }, "bin")).toEqual([]);
|
|
69
|
+
});
|
|
70
|
+
it("describes fuse states by name", () => {
|
|
71
|
+
expect(describeFuses({ version: 1, states: "1r" })).toBe("RunAsNode=on, EnableCookieEncryption=removed");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("readAsar", () => {
|
|
75
|
+
it("lists nested files and reads their contents", () => {
|
|
76
|
+
const dir = tmp();
|
|
77
|
+
const asar = join(dir, "app.asar");
|
|
78
|
+
writeFileSync(asar, buildAsar({ "package.json": '{"main":"main.js"}', "dist/main.js": "console.log(1)", "dist/deep/x.js": "y()" }));
|
|
79
|
+
const files = readAsar(asar);
|
|
80
|
+
expect(files.map((f) => f.key).sort()).toEqual(["dist/deep/x.js", "dist/main.js", "package.json"]);
|
|
81
|
+
expect(files.find((f) => f.key === "dist/deep/x.js").read().toString()).toBe("y()");
|
|
82
|
+
expect(files[0].display).toContain("app.asar!/");
|
|
83
|
+
});
|
|
84
|
+
it("returns null for something that is not an ASAR", () => {
|
|
85
|
+
const dir = tmp();
|
|
86
|
+
writeFileSync(join(dir, "x.asar"), Buffer.from("definitely not an archive"));
|
|
87
|
+
expect(readAsar(join(dir, "x.asar"))).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("analyzeElectron", () => {
|
|
91
|
+
it("combines fuse findings with the code inside app.asar", () => {
|
|
92
|
+
const map = JSON.stringify({ version: 3, mappings: "AAAA", sources: ["src/main.ts"], sourcesContent: ["x"] });
|
|
93
|
+
const app = macApp(DEFAULT_FUSES, {
|
|
94
|
+
"package.json": "{}",
|
|
95
|
+
"dist/main.js": "a()//# sourceMappingURL=main.js.map",
|
|
96
|
+
"dist/main.js.map": map,
|
|
97
|
+
"node_modules/dep/index.js": 'fetch("http://localhost:1")',
|
|
98
|
+
});
|
|
99
|
+
const layout = detectElectron(app);
|
|
100
|
+
expect(layout.fuseBinary).toContain("Electron Framework");
|
|
101
|
+
const result = analyzeElectron(layout, 100);
|
|
102
|
+
expect(ids(result.findings)).toEqual([
|
|
103
|
+
"electron_fuse_asar_integrity_off",
|
|
104
|
+
"electron_fuse_inspect_args",
|
|
105
|
+
"electron_fuse_load_outside_asar",
|
|
106
|
+
"electron_fuse_node_options",
|
|
107
|
+
"electron_fuse_run_as_node",
|
|
108
|
+
"sourcemap_embeds_source",
|
|
109
|
+
]);
|
|
110
|
+
expect(result.notes.join("\n")).toContain("RunAsNode=on");
|
|
111
|
+
});
|
|
112
|
+
it("flags app code shipped as a plain resources/app folder", () => {
|
|
113
|
+
const app = macApp(HARDENED_FUSES, null, { "package.json": "{}", "main.js": "start()" });
|
|
114
|
+
const result = analyzeElectron(detectElectron(app), 100);
|
|
115
|
+
expect(ids(result.findings)).toEqual(["electron_app_unpacked"]);
|
|
116
|
+
expect(result.findings[0].match).toBe("no app.asar");
|
|
117
|
+
});
|
|
118
|
+
it("detects a Windows/Linux layout by its resources folder", () => {
|
|
119
|
+
const dir = tmp();
|
|
120
|
+
mkdirSync(join(dir, "resources"));
|
|
121
|
+
writeFileSync(join(dir, "resources", "app.asar"), buildAsar({ "package.json": "{}" }));
|
|
122
|
+
const layout = detectElectron(dir);
|
|
123
|
+
expect(layout).not.toBeNull();
|
|
124
|
+
expect(layout.fuseBinary).toBeNull(); // no executable large enough to be Electron
|
|
125
|
+
expect(detectElectron(tmp())).toBeNull();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=electron.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"electron.test.js","sourceRoot":"","sources":["../../src/__tests__/electron.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/live.test.ts"],"names":[],"mappings":""}
|